Allow to create/remove categories. Some refactoring.

This commit is contained in:
inorichi 2015-12-28 01:13:52 +01:00
parent 3dff7f90e7
commit e548cbf171
31 changed files with 539 additions and 75 deletions

View file

@ -113,6 +113,7 @@ dependencies {
compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4' compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4'
compile 'eu.davidea:flexible-adapter:4.2.0@aar' compile 'eu.davidea:flexible-adapter:4.2.0@aar'
compile 'com.nononsenseapps:filepicker:2.5.0' compile 'com.nononsenseapps:filepicker:2.5.0'
compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
compile "com.google.dagger:dagger:$DAGGER_VERSION" compile "com.google.dagger:dagger:$DAGGER_VERSION"
apt "com.google.dagger:dagger-compiler:$DAGGER_VERSION" apt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"

View file

@ -367,6 +367,12 @@ public class DatabaseHelper {
.prepare(); .prepare();
} }
public PreparedDeleteCollectionOfObjects<Category> deleteCategories(List<Category> categories) {
return db.delete()
.objects(categories)
.prepare();
}
public PreparedPutObject<MangaCategory> insertMangaCategory(MangaCategory mangaCategory) { public PreparedPutObject<MangaCategory> insertMangaCategory(MangaCategory mangaCategory) {
return db.put() return db.put()
.object(mangaCategory) .object(mangaCategory)

View file

@ -71,7 +71,7 @@ public class DownloadManager {
if (threadsNumberSubscription != null && !threadsNumberSubscription.isUnsubscribed()) if (threadsNumberSubscription != null && !threadsNumberSubscription.isUnsubscribed())
threadsNumberSubscription.unsubscribe(); threadsNumberSubscription.unsubscribe();
threadsNumberSubscription = preferences.getDownloadTheadsObservable() threadsNumberSubscription = preferences.downloadThreads().asObservable()
.subscribe(threadsNumber::onNext); .subscribe(threadsNumber::onNext);
downloadsSubscription = downloadsQueueSubject downloadsSubscription = downloadsQueueSubject

View file

@ -13,7 +13,6 @@ import java.io.File;
import eu.kanade.mangafeed.R; import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.mangasync.base.MangaSyncService; import eu.kanade.mangafeed.data.mangasync.base.MangaSyncService;
import eu.kanade.mangafeed.data.source.base.Source; import eu.kanade.mangafeed.data.source.base.Source;
import rx.Observable;
public class PreferencesHelper { public class PreferencesHelper {
@ -138,12 +137,8 @@ public class PreferencesHelper {
prefs.edit().putString(getKey(R.string.pref_download_directory_key), path).apply(); prefs.edit().putString(getKey(R.string.pref_download_directory_key), path).apply();
} }
public int getDownloadThreads() { public Preference<Integer> downloadThreads() {
return prefs.getInt(getKey(R.string.pref_download_slots_key), 1); return rxPrefs.getInteger(getKey(R.string.pref_download_slots_key), 1);
}
public Observable<Integer> getDownloadTheadsObservable() {
return rxPrefs.getInteger(getKey(R.string.pref_download_slots_key), 1).asObservable();
} }
} }

View file

@ -14,7 +14,7 @@ import eu.kanade.mangafeed.injection.module.AppModule;
import eu.kanade.mangafeed.injection.module.DataModule; import eu.kanade.mangafeed.injection.module.DataModule;
import eu.kanade.mangafeed.ui.catalogue.CataloguePresenter; import eu.kanade.mangafeed.ui.catalogue.CataloguePresenter;
import eu.kanade.mangafeed.ui.download.DownloadPresenter; import eu.kanade.mangafeed.ui.download.DownloadPresenter;
import eu.kanade.mangafeed.ui.library.LibraryCategoryFragment; import eu.kanade.mangafeed.ui.library.category.CategoryPresenter;
import eu.kanade.mangafeed.ui.library.LibraryPresenter; import eu.kanade.mangafeed.ui.library.LibraryPresenter;
import eu.kanade.mangafeed.ui.manga.MangaActivity; import eu.kanade.mangafeed.ui.manga.MangaActivity;
import eu.kanade.mangafeed.ui.manga.MangaPresenter; import eu.kanade.mangafeed.ui.manga.MangaPresenter;
@ -43,14 +43,13 @@ public interface AppComponent {
void inject(ReaderPresenter readerPresenter); void inject(ReaderPresenter readerPresenter);
void inject(DownloadPresenter downloadPresenter); void inject(DownloadPresenter downloadPresenter);
void inject(MyAnimeListPresenter myAnimeListPresenter); void inject(MyAnimeListPresenter myAnimeListPresenter);
void inject(CategoryPresenter categoryPresenter);
void inject(ReaderActivity readerActivity); void inject(ReaderActivity readerActivity);
void inject(MangaActivity mangaActivity); void inject(MangaActivity mangaActivity);
void inject(SettingsAccountsFragment settingsAccountsFragment); void inject(SettingsAccountsFragment settingsAccountsFragment);
void inject(SettingsActivity settingsActivity); void inject(SettingsActivity settingsActivity);
void inject(LibraryCategoryFragment libraryCategoryFragment);
void inject(Source source); void inject(Source source);
void inject(MyAnimeList myAnimeList); void inject(MyAnimeList myAnimeList);

View file

@ -1,14 +1,27 @@
package eu.kanade.mangafeed.ui.base.activity; package eu.kanade.mangafeed.ui.base.activity;
import android.content.Context; import android.os.Bundle;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.view.MenuItem; import android.view.MenuItem;
import de.greenrobot.event.EventBus; import de.greenrobot.event.EventBus;
import icepick.Icepick;
public class BaseActivity extends AppCompatActivity { public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
Icepick.restoreInstanceState(this, savedState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Icepick.saveInstanceState(this, outState);
}
protected void setupToolbar(Toolbar toolbar) { protected void setupToolbar(Toolbar toolbar) {
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
if (getSupportActionBar() != null) if (getSupportActionBar() != null)
@ -35,10 +48,6 @@ public class BaseActivity extends AppCompatActivity {
getSupportActionBar().setSubtitle(getString(titleResource)); getSupportActionBar().setSubtitle(getString(titleResource));
} }
public Context getActivity() {
return this;
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {

View file

@ -33,8 +33,8 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration {
} }
@Override @Override
public void onDrawOver(Canvas c, RecyclerView parent) { public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mDivider == null) { super.onDrawOver(c, parent); return; } if (mDivider == null) { super.onDrawOver(c, parent, state); return; }
if (getOrientation(parent) == LinearLayoutManager.VERTICAL) { if (getOrientation(parent) == LinearLayoutManager.VERTICAL) {
final int left = parent.getPaddingLeft(); final int left = parent.getPaddingLeft();

View file

@ -14,15 +14,11 @@ import com.f2prateek.rx.preferences.Preference;
import java.util.List; import java.util.List;
import javax.inject.Inject;
import butterknife.Bind; import butterknife.Bind;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import eu.kanade.mangafeed.App;
import eu.kanade.mangafeed.R; import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.models.Category; import eu.kanade.mangafeed.data.database.models.Category;
import eu.kanade.mangafeed.data.database.models.Manga; import eu.kanade.mangafeed.data.database.models.Manga;
import eu.kanade.mangafeed.data.preference.PreferencesHelper;
import eu.kanade.mangafeed.event.LibraryMangasEvent; import eu.kanade.mangafeed.event.LibraryMangasEvent;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity; import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
import eu.kanade.mangafeed.ui.base.adapter.FlexibleViewHolder; import eu.kanade.mangafeed.ui.base.adapter.FlexibleViewHolder;
@ -37,8 +33,6 @@ import rx.Subscription;
public class LibraryCategoryFragment extends BaseFragment implements public class LibraryCategoryFragment extends BaseFragment implements
ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener { ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener {
@Inject PreferencesHelper preferences;
@Bind(R.id.library_mangas) AutofitRecyclerView recycler; @Bind(R.id.library_mangas) AutofitRecyclerView recycler;
@State Category category; @State Category category;
@ -47,20 +41,12 @@ public class LibraryCategoryFragment extends BaseFragment implements
private Subscription numColumnsSubscription; private Subscription numColumnsSubscription;
private static final int INVALID_POSITION = -1;
public static LibraryCategoryFragment newInstance(Category category) { public static LibraryCategoryFragment newInstance(Category category) {
LibraryCategoryFragment fragment = new LibraryCategoryFragment(); LibraryCategoryFragment fragment = new LibraryCategoryFragment();
fragment.category = category; fragment.category = category;
return fragment; return fragment;
} }
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
App.get(getActivity()).getComponent().inject(this);
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
// Inflate the layout for this fragment // Inflate the layout for this fragment
@ -75,8 +61,8 @@ public class LibraryCategoryFragment extends BaseFragment implements
Preference<Integer> columnsPref = getResources().getConfiguration() Preference<Integer> columnsPref = getResources().getConfiguration()
.orientation == Configuration.ORIENTATION_PORTRAIT ? .orientation == Configuration.ORIENTATION_PORTRAIT ?
preferences.portraitColumns() : getLibraryPresenter().preferences.portraitColumns() :
preferences.landscapeColumns(); getLibraryPresenter().preferences.landscapeColumns();
numColumnsSubscription = columnsPref.asObservable() numColumnsSubscription = columnsPref.asObservable()
.subscribe(recycler::setSpanCount); .subscribe(recycler::setSpanCount);
@ -110,6 +96,7 @@ public class LibraryCategoryFragment extends BaseFragment implements
@EventBusHook @EventBusHook
public void onEventMainThread(LibraryMangasEvent event) { public void onEventMainThread(LibraryMangasEvent event) {
destroyActionModeIfNeeded();
setMangas(event.getMangas().get(category.id)); setMangas(event.getMangas().get(category.id));
} }
@ -128,7 +115,7 @@ public class LibraryCategoryFragment extends BaseFragment implements
@Override @Override
public boolean onListItemClick(int position) { public boolean onListItemClick(int position) {
if (actionMode != null && position != INVALID_POSITION) { if (actionMode != null && position != -1) {
toggleSelection(position); toggleSelection(position);
return true; return true;
} else { } else {
@ -145,6 +132,22 @@ public class LibraryCategoryFragment extends BaseFragment implements
toggleSelection(position); toggleSelection(position);
} }
private void toggleSelection(int position) {
adapter.toggleSelection(position, false);
int count = adapter.getSelectedItemCount();
if (count == 0) {
actionMode.finish();
} else {
setContextTitle(count);
actionMode.invalidate();
}
}
private void setContextTitle(int count) {
actionMode.setTitle(getString(R.string.label_selected, count));
}
@Override @Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) { public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.library_selection, menu); mode.getMenuInflater().inflate(R.menu.library_selection, menu);
@ -169,19 +172,17 @@ public class LibraryCategoryFragment extends BaseFragment implements
actionMode = null; actionMode = null;
} }
private void toggleSelection(int position) { public void destroyActionModeIfNeeded() {
adapter.toggleSelection(position, false); if (actionMode != null) {
int count = adapter.getSelectedItemCount();
if (count == 0) {
actionMode.finish(); actionMode.finish();
} else {
setContextTitle(count);
actionMode.invalidate();
} }
} }
private void setContextTitle(int count) { private LibraryFragment getLibraryFragment() {
actionMode.setTitle(getString(R.string.selected_chapters_title, count)); return (LibraryFragment) getParentFragment();
}
private LibraryPresenter getLibraryPresenter() {
return getLibraryFragment().getPresenter();
} }
} }

View file

@ -4,6 +4,7 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.AppBarLayout; import android.support.design.widget.AppBarLayout;
import android.support.design.widget.TabLayout; import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -21,6 +22,7 @@ import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.models.Category; import eu.kanade.mangafeed.data.database.models.Category;
import eu.kanade.mangafeed.data.sync.LibraryUpdateService; import eu.kanade.mangafeed.data.sync.LibraryUpdateService;
import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment; import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
import eu.kanade.mangafeed.ui.library.category.CategoryFragment;
import eu.kanade.mangafeed.ui.main.MainActivity; import eu.kanade.mangafeed.ui.main.MainActivity;
import nucleus.factory.RequiresPresenter; import nucleus.factory.RequiresPresenter;
@ -82,12 +84,20 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> {
getActivity().startService(intent); getActivity().startService(intent);
} }
return true;
case R.id.action_edit_categories:
onEditCategories();
return true; return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
private void onEditCategories() {
Fragment fragment = CategoryFragment.newInstance();
((MainActivity) getActivity()).pushFragment(fragment);
}
public void onNextCategories(List<Category> categories) { public void onNextCategories(List<Category> categories) {
List<Category> actualCategories = new ArrayList<>(); List<Category> actualCategories = new ArrayList<>();

View file

@ -11,21 +11,25 @@ import javax.inject.Inject;
import de.greenrobot.event.EventBus; import de.greenrobot.event.EventBus;
import eu.kanade.mangafeed.data.cache.CoverCache; import eu.kanade.mangafeed.data.cache.CoverCache;
import eu.kanade.mangafeed.data.database.DatabaseHelper; import eu.kanade.mangafeed.data.database.DatabaseHelper;
import eu.kanade.mangafeed.data.database.models.Category;
import eu.kanade.mangafeed.data.database.models.Manga; import eu.kanade.mangafeed.data.database.models.Manga;
import eu.kanade.mangafeed.data.preference.PreferencesHelper; import eu.kanade.mangafeed.data.preference.PreferencesHelper;
import eu.kanade.mangafeed.data.source.SourceManager; import eu.kanade.mangafeed.data.source.SourceManager;
import eu.kanade.mangafeed.event.LibraryMangasEvent; import eu.kanade.mangafeed.event.LibraryMangasEvent;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter; import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import rx.Observable; import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers; import rx.schedulers.Schedulers;
public class LibraryPresenter extends BasePresenter<LibraryFragment> { public class LibraryPresenter extends BasePresenter<LibraryFragment> {
@Inject DatabaseHelper db; @Inject DatabaseHelper db;
@Inject PreferencesHelper prefs; @Inject PreferencesHelper preferences;
@Inject CoverCache coverCache; @Inject CoverCache coverCache;
@Inject SourceManager sourceManager; @Inject SourceManager sourceManager;
protected List<Category> categories;
private static final int GET_CATEGORIES = 1; private static final int GET_CATEGORIES = 1;
@Override @Override
@ -33,7 +37,7 @@ public class LibraryPresenter extends BasePresenter<LibraryFragment> {
super.onCreate(savedState); super.onCreate(savedState);
restartableLatestCache(GET_CATEGORIES, restartableLatestCache(GET_CATEGORIES,
() -> db.getCategories().createObservable(), this::getCategoriesObservable,
LibraryFragment::onNextCategories); LibraryFragment::onNextCategories);
start(GET_CATEGORIES); start(GET_CATEGORIES);
@ -41,9 +45,16 @@ public class LibraryPresenter extends BasePresenter<LibraryFragment> {
add(getLibraryMangasObservable() add(getLibraryMangasObservable()
.subscribe(mangas -> .subscribe(mangas ->
EventBus.getDefault().postSticky(new LibraryMangasEvent(mangas)))); EventBus.getDefault().postSticky(new LibraryMangasEvent(mangas))));
} }
public Observable<Map<Integer, List<Manga>>> getLibraryMangasObservable() { public Observable<List<Category>> getCategoriesObservable() {
return db.getCategories().createObservable()
.doOnNext(categories -> this.categories = categories)
.observeOn(AndroidSchedulers.mainThread());
}
private Observable<Map<Integer, List<Manga>>> getLibraryMangasObservable() {
return db.getLibraryMangas().createObservable() return db.getLibraryMangas().createObservable()
.flatMap(mangas -> Observable.from(mangas) .flatMap(mangas -> Observable.from(mangas)
.groupBy(manga -> manga.category) .groupBy(manga -> manga.category)

View file

@ -0,0 +1,60 @@
package eu.kanade.mangafeed.ui.library.category;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.amulyakhare.textdrawable.util.ColorGenerator;
import java.util.List;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.models.Category;
public class CategoryAdapter extends FlexibleAdapter<CategoryHolder, Category> {
private CategoryFragment fragment;
private ColorGenerator generator;
public CategoryAdapter(CategoryFragment fragment) {
this.fragment = fragment;
setHasStableIds(true);
generator = ColorGenerator.DEFAULT;
}
public void setItems(List<Category> items) {
mItems = items;
notifyDataSetChanged();
}
@Override
public long getItemId(int position) {
return mItems.get(position).id;
}
@Override
public void updateDataSet(String param) {
}
@Override
public CategoryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(fragment.getActivity());
View v = inflater.inflate(R.layout.item_edit_categories, parent, false);
return new CategoryHolder(v, this, fragment);
}
@Override
public void onBindViewHolder(CategoryHolder holder, int position) {
final Category category = getItem(position);
holder.onSetValues(category, generator);
//When user scrolls this bind the correct selection status
holder.itemView.setActivated(isSelected(position));
}
public ColorGenerator getColorGenerator() {
return generator;
}
}

View file

@ -0,0 +1,150 @@
package eu.kanade.mangafeed.ui.library.category;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import com.afollestad.materialdialogs.MaterialDialog;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.models.Category;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
import eu.kanade.mangafeed.ui.base.adapter.FlexibleViewHolder;
import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
import eu.kanade.mangafeed.ui.decoration.DividerItemDecoration;
import eu.kanade.mangafeed.ui.library.LibraryCategoryAdapter;
import nucleus.factory.RequiresPresenter;
import rx.Observable;
@RequiresPresenter(CategoryPresenter.class)
public class CategoryFragment extends BaseRxFragment<CategoryPresenter>
implements ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener {
@Bind(R.id.categories_list) RecyclerView recycler;
@Bind(R.id.fab) FloatingActionButton fab;
private CategoryAdapter adapter;
private ActionMode actionMode;
public static CategoryFragment newInstance() {
return new CategoryFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
View view = inflater.inflate(R.layout.fragment_edit_categories, container, false);
ButterKnife.bind(this, view);
setToolbarTitle(R.string.action_edit_categories);
adapter = new CategoryAdapter(this);
recycler.setLayoutManager(new LinearLayoutManager(getActivity()));
recycler.setHasFixedSize(true);
recycler.setAdapter(adapter);
recycler.addItemDecoration(new DividerItemDecoration(
ResourcesCompat.getDrawable(getResources(), R.drawable.line_divider, null)));
fab.setOnClickListener(v -> {
new MaterialDialog.Builder(getActivity())
.title(R.string.action_add_category)
.input(R.string.name, 0, false, (dialog, input) -> {
getPresenter().createCategory(input.toString());
}).show();
});
return view;
}
public void setCategories(List<Category> categories) {
destroyActionModeIfNeeded();
adapter.setItems(categories);
}
private List<Category> getSelectedCategories() {
// Create a blocking copy of the selected categories
return Observable.from(adapter.getSelectedItems())
.map(adapter::getItem).toList().toBlocking().single();
}
@Override
public boolean onListItemClick(int position) {
if (actionMode != null && position != -1) {
toggleSelection(position);
return true;
} else {
return false;
}
}
@Override
public void onListItemLongClick(int position) {
if (actionMode == null)
actionMode = ((BaseActivity) getActivity()).startSupportActionMode(this);
toggleSelection(position);
}
private void toggleSelection(int position) {
adapter.toggleSelection(position, false);
int count = adapter.getSelectedItemCount();
if (count == 0) {
actionMode.finish();
} else {
setContextTitle(count);
actionMode.invalidate();
}
}
private void setContextTitle(int count) {
actionMode.setTitle(getString(R.string.label_selected, count));
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.category_selection, menu);
adapter.setMode(LibraryCategoryAdapter.MODE_MULTI);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete:
getPresenter().deleteCategories(getSelectedCategories());
return true;
}
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
adapter.setMode(LibraryCategoryAdapter.MODE_SINGLE);
adapter.clearSelection();
actionMode = null;
}
public void destroyActionModeIfNeeded() {
if (actionMode != null) {
actionMode.finish();
}
}
}

View file

@ -0,0 +1,45 @@
package eu.kanade.mangafeed.ui.library.category;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.amulyakhare.textdrawable.util.ColorGenerator;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.models.Category;
import eu.kanade.mangafeed.ui.base.adapter.FlexibleViewHolder;
public class CategoryHolder extends FlexibleViewHolder {
private View view;
@Bind(R.id.image) ImageView image;
@Bind(R.id.title) TextView title;
public CategoryHolder(View view, CategoryAdapter adapter, OnListItemClickListener listener) {
super(view, adapter, listener);
ButterKnife.bind(this, view);
this.view = view;
}
public void onSetValues(Category category, ColorGenerator generator) {
title.setText(category.name);
image.setImageDrawable(getRound(category.name.substring(0, 1), generator));
}
private TextDrawable getRound(String text, ColorGenerator generator) {
return TextDrawable.builder().buildRound(text, generator.getColor(text));
}
@OnClick(R.id.image)
void onImageClick() {
// Simulate long click on this view to enter selection mode
onLongClick(view);
}
}

View file

@ -0,0 +1,39 @@
package eu.kanade.mangafeed.ui.library.category;
import android.os.Bundle;
import java.util.List;
import javax.inject.Inject;
import eu.kanade.mangafeed.data.database.DatabaseHelper;
import eu.kanade.mangafeed.data.database.models.Category;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import rx.android.schedulers.AndroidSchedulers;
public class CategoryPresenter extends BasePresenter<CategoryFragment> {
@Inject DatabaseHelper db;
private static final int GET_CATEGORIES = 1;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
restartableLatestCache(GET_CATEGORIES,
() -> db.getCategories().createObservable()
.observeOn(AndroidSchedulers.mainThread()),
CategoryFragment::setCategories);
start(GET_CATEGORIES);
}
public void createCategory(String name) {
db.insertCategory(Category.create(name)).createObservable().subscribe();
}
public void deleteCategories(List<Category> categories) {
db.deleteCategories(categories).createObservable().subscribe();
}
}

View file

@ -4,6 +4,7 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.AppBarLayout; import android.support.design.widget.AppBarLayout;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.widget.FrameLayout; import android.widget.FrameLayout;
@ -19,23 +20,23 @@ import eu.kanade.mangafeed.ui.catalogue.CatalogueFragment;
import eu.kanade.mangafeed.ui.download.DownloadFragment; import eu.kanade.mangafeed.ui.download.DownloadFragment;
import eu.kanade.mangafeed.ui.library.LibraryFragment; import eu.kanade.mangafeed.ui.library.LibraryFragment;
import eu.kanade.mangafeed.ui.setting.SettingsActivity; import eu.kanade.mangafeed.ui.setting.SettingsActivity;
import icepick.State;
import nucleus.view.ViewWithPresenter; import nucleus.view.ViewWithPresenter;
public class MainActivity extends BaseActivity { public class MainActivity extends BaseActivity {
@Bind(R.id.appbar) AppBarLayout appBar; @Bind(R.id.appbar) AppBarLayout appBar;
@Bind(R.id.toolbar) Toolbar toolbar; @Bind(R.id.toolbar) Toolbar toolbar;
@Bind(R.id.drawer_container) FrameLayout container; @Bind(R.id.drawer_container) FrameLayout container;
private Drawer drawer; private Drawer drawer;
private FragmentStack fragmentStack; private FragmentStack fragmentStack;
private final static String SELECTED_ITEM = "selected_item"; @State int selectedItem;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedState) {
super.onCreate(savedInstanceState); super.onCreate(savedState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
ButterKnife.bind(this); ButterKnife.bind(this);
@ -52,6 +53,13 @@ public class MainActivity extends BaseActivity {
.withRootView(container) .withRootView(container)
.withToolbar(toolbar) .withToolbar(toolbar)
.withActionBarDrawerToggleAnimated(true) .withActionBarDrawerToggleAnimated(true)
.withOnDrawerNavigationListener(view -> {
if (fragmentStack.size() > 1) {
onBackPressed();
return true;
}
return false;
})
.addDrawerItems( .addDrawerItems(
new PrimaryDrawerItem() new PrimaryDrawerItem()
.withName(R.string.label_library) .withName(R.string.label_library)
@ -70,7 +78,7 @@ public class MainActivity extends BaseActivity {
.withIdentifier(R.id.nav_drawer_settings) .withIdentifier(R.id.nav_drawer_settings)
.withSelectable(false) .withSelectable(false)
) )
.withSavedInstance(savedInstanceState) .withSavedInstance(savedState)
.withOnDrawerItemClickListener( .withOnDrawerItemClickListener(
(view, position, drawerItem) -> { (view, position, drawerItem) -> {
if (drawerItem != null) { if (drawerItem != null) {
@ -97,15 +105,23 @@ public class MainActivity extends BaseActivity {
) )
.build(); .build();
if (savedInstanceState == null) if (savedState != null) {
// Recover icon state after rotation
if (fragmentStack.size() > 1) {
showBackArrow();
}
// Set saved selection
drawer.setSelection(selectedItem, false);
} else {
// Set default selection
drawer.setSelection(R.id.nav_drawer_library); drawer.setSelection(R.id.nav_drawer_library);
else }
drawer.setSelection(savedInstanceState.getInt(SELECTED_ITEM), false);
} }
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
outState.putInt(SELECTED_ITEM, drawer.getCurrentSelection()); selectedItem = drawer.getCurrentSelection();
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
} }
@ -113,8 +129,37 @@ public class MainActivity extends BaseActivity {
fragmentStack.replace(fragment); fragmentStack.replace(fragment);
} }
public Fragment getActiveFragment() { public void pushFragment(Fragment fragment) {
return fragmentStack.peek(); fragmentStack.push(fragment);
if (fragmentStack.size() > 1) {
showBackArrow();
}
}
@Override
public void onBackPressed() {
if (!fragmentStack.pop()) {
super.onBackPressed();
} else if (fragmentStack.size() == 1) {
showHamburgerIcon();
drawer.getActionBarDrawerToggle().syncState();
}
}
private void showHamburgerIcon() {
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
drawer.getActionBarDrawerToggle().setDrawerIndicatorEnabled(true);
drawer.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
}
}
private void showBackArrow() {
if (getSupportActionBar() != null) {
drawer.getActionBarDrawerToggle().setDrawerIndicatorEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
drawer.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
} }
public Toolbar getToolbar() { public Toolbar getToolbar() {

View file

@ -49,8 +49,8 @@ public class MangaActivity extends BaseRxActivity<MangaPresenter> {
} }
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedState) {
super.onCreate(savedInstanceState); super.onCreate(savedState);
App.get(this).getComponent().inject(this); App.get(this).getComponent().inject(this);
setContentView(R.layout.activity_manga); setContentView(R.layout.activity_manga);
ButterKnife.bind(this); ButterKnife.bind(this);
@ -64,14 +64,12 @@ public class MangaActivity extends BaseRxActivity<MangaPresenter> {
setupViewPager(); setupViewPager();
if (savedInstanceState == null) if (savedState == null)
getPresenter().queryManga(manga_id); getPresenter().queryManga(manga_id);
} }
private void setupViewPager() { private void setupViewPager() {
adapter = new MangaDetailAdapter( adapter = new MangaDetailAdapter(getSupportFragmentManager(), this);
getSupportFragmentManager(),
getActivity());
view_pager.setAdapter(adapter); view_pager.setAdapter(adapter);
tabs.setupWithViewPager(view_pager); tabs.setupWithViewPager(view_pager);

View file

@ -145,7 +145,7 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
if (getPresenter().getChapters().isEmpty()) if (getPresenter().getChapters().isEmpty())
initialFetchChapters(); initialFetchChapters();
closeActionMode(); destroyActionModeIfNeeded();
adapter.setItems(chapters); adapter.setItems(chapters);
} }
@ -254,7 +254,7 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
return Observable.from(chapters); return Observable.from(chapters);
} }
public void closeActionMode() { public void destroyActionModeIfNeeded() {
if (actionMode != null) { if (actionMode != null) {
actionMode.finish(); actionMode.finish();
} }
@ -283,7 +283,7 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
.doOnCompleted(adapter::notifyDataSetChanged); .doOnCompleted(adapter::notifyDataSetChanged);
getPresenter().downloadChapters(observable); getPresenter().downloadChapters(observable);
closeActionMode(); destroyActionModeIfNeeded();
return true; return true;
} }
@ -311,7 +311,7 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
.finallyDo(dialog::dismiss); .finallyDo(dialog::dismiss);
getPresenter().deleteChapters(observable); getPresenter().deleteChapters(observable);
closeActionMode(); destroyActionModeIfNeeded();
return true; return true;
} }
@ -347,7 +347,7 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
} }
private void setContextTitle(int count) { private void setContextTitle(int count) {
actionMode.setTitle(getString(R.string.selected_chapters_title, count)); actionMode.setTitle(getString(R.string.label_selected, count));
} }
public void setSortIcon() { public void setSortIcon() {

View file

@ -22,15 +22,15 @@ public class SettingsActivity extends BaseActivity {
@Bind(R.id.toolbar) Toolbar toolbar; @Bind(R.id.toolbar) Toolbar toolbar;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedState) {
super.onCreate(savedInstanceState); super.onCreate(savedState);
App.get(this).getComponent().inject(this); App.get(this).getComponent().inject(this);
setContentView(R.layout.activity_preferences); setContentView(R.layout.activity_preferences);
ButterKnife.bind(this); ButterKnife.bind(this);
setupToolbar(toolbar); setupToolbar(toolbar);
if (savedInstanceState == null) if (savedState == null)
getFragmentManager().beginTransaction().replace(R.id.settings_content, getFragmentManager().beginTransaction().replace(R.id.settings_content,
new SettingsMainFragment()) new SettingsMainFragment())
.commit(); .commit();

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/categories_list"
android:choiceMode="multipleChoice"
android:listSelector="@color/list_choice_pressed_bg_light" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
android:clickable="true"
android:src="@drawable/ic_action_add_18dp"
app:borderWidth="0dp"/>
</android.support.design.widget.CoordinatorLayout>

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightLarge"
android:paddingTop="@dimen/margin_top"
android:paddingBottom="@dimen/margin_bottom"
android:background="@drawable/selector_chapter_light">
<ImageView
android:id="@+id/image"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerInParent="true"
android:elevation="4dp"
android:clickable="true"
android:layout_marginLeft="@dimen/margin_left"
android:layout_marginStart="@dimen/margin_left"
android:layout_marginRight="@dimen/margin_right"
android:layout_marginEnd="@dimen/margin_right"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/margin_right"
android:layout_marginEnd="@dimen/margin_right"
android:layout_toRightOf="@id/image"
android:layout_toEndOf="@id/image"
android:layout_centerInParent="true"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:textColor="@color/primary_text"
tools:text="Title"/>
</RelativeLayout>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_delete"
android:title="@string/action_delete"
android:icon="@drawable/ic_action_delete"
android:orderInCategory="1"
app:showAsAction="always"/>
</menu>

View file

@ -1,17 +1,25 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity"> xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
<item <item
android:id="@+id/action_search" android:id="@+id/action_search"
android:title="@string/action_search" android:title="@string/action_search"
android:icon="@drawable/ic_action_search" android:icon="@drawable/ic_action_search"
android:orderInCategory="100" android:orderInCategory="100"
app:showAsAction="collapseActionView|ifRoom" app:showAsAction="collapseActionView|ifRoom"
app:actionViewClass="android.support.v7.widget.SearchView"/> app:actionViewClass="android.support.v7.widget.SearchView" />
<item <item
android:id="@+id/action_refresh" android:id="@+id/action_refresh"
android:title="@string/action_refresh" android:title="@string/action_refresh"
android:icon="@drawable/ic_action_refresh" android:icon="@drawable/ic_action_refresh"
android:orderInCategory="1" android:orderInCategory="1"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item
android:id="@+id/action_edit_categories"
android:title="@string/action_edit_categories"
app:showAsAction="never" />
</menu> </menu>

View file

@ -2,6 +2,11 @@
<!-- Default screen margins, per the Android Design guidelines. --> <!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="margin_top">16dp</dimen>
<dimen name="margin_bottom">16dp</dimen>
<dimen name="margin_left">16dp</dimen>
<dimen name="margin_right">16dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="text_headline">24sp</dimen> <dimen name="text_headline">24sp</dimen>
<dimen name="text_large_title">22sp</dimen> <dimen name="text_large_title">22sp</dimen>

View file

@ -1,12 +1,15 @@
<resources> <resources>
<string name="app_name">Mangafeed</string> <string name="app_name">Mangafeed</string>
<string name="name">Name</string>
<!-- Activities and fragments labels (toolbar title) --> <!-- Activities and fragments labels (toolbar title) -->
<string name="label_settings">Settings</string> <string name="label_settings">Settings</string>
<string name="label_download_queue">Download queue</string> <string name="label_download_queue">Download queue</string>
<string name="label_library">My library</string> <string name="label_library">My library</string>
<string name="label_recent_updates">Recent updates</string> <string name="label_recent_updates">Recent updates</string>
<string name="label_catalogues">Catalogues</string> <string name="label_catalogues">Catalogues</string>
<string name="label_selected">Selected: %1$d</string>
<!-- Actions --> <!-- Actions -->
<string name="action_settings">Settings</string> <string name="action_settings">Settings</string>
@ -19,6 +22,8 @@
<string name="action_delete">Delete</string> <string name="action_delete">Delete</string>
<string name="action_update">Update</string> <string name="action_update">Update</string>
<string name="action_edit">Edit</string> <string name="action_edit">Edit</string>
<string name="action_add_category">Add category</string>
<string name="action_edit_categories">Edit categories</string>
<string name="action_sort_up">Sort up</string> <string name="action_sort_up">Sort up</string>
<string name="action_sort_down">Sort down</string> <string name="action_sort_down">Sort down</string>
<string name="action_show_unread">Unread</string> <string name="action_show_unread">Unread</string>
@ -118,7 +123,6 @@
<!-- Manga chapters fragment --> <!-- Manga chapters fragment -->
<string name="manga_chapters_tab">Chapters</string> <string name="manga_chapters_tab">Chapters</string>
<string name="selected_chapters_title">Selected: %1$d</string>
<string name="manga_chapter_no_title">No title</string> <string name="manga_chapter_no_title">No title</string>
<string name="chapter_downloaded">Downloaded</string> <string name="chapter_downloaded">Downloaded</string>
<string name="chapter_queued">Queued</string> <string name="chapter_queued">Queued</string>

View file

@ -22,5 +22,6 @@ allprojects {
maven { url "https://clojars.org/repo/" } maven { url "https://clojars.org/repo/" }
maven { url "http://dl.bintray.com/davideas/maven" } maven { url "http://dl.bintray.com/davideas/maven" }
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
maven { url 'http://dl.bintray.com/amulyakhare/maven' }
} }
} }