Change mangas from categories (needs testing)

This commit is contained in:
inorichi 2015-12-29 01:00:05 +01:00
parent 3f1f9ea9f2
commit 1360a90bf9
15 changed files with 245 additions and 69 deletions

View File

@ -2,15 +2,18 @@ package eu.kanade.mangafeed.data.database;
import android.content.Context;
import com.pushtorefresh.storio.Queries;
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping;
import com.pushtorefresh.storio.sqlite.StorIOSQLite;
import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite;
import com.pushtorefresh.storio.sqlite.operations.delete.PreparedDeleteByQuery;
import com.pushtorefresh.storio.sqlite.operations.delete.PreparedDeleteCollectionOfObjects;
import com.pushtorefresh.storio.sqlite.operations.delete.PreparedDeleteObject;
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetListOfObjects;
import com.pushtorefresh.storio.sqlite.operations.put.PreparedPutCollectionOfObjects;
import com.pushtorefresh.storio.sqlite.operations.put.PreparedPutObject;
import com.pushtorefresh.storio.sqlite.operations.put.PutResults;
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery;
import com.pushtorefresh.storio.sqlite.queries.Query;
import com.pushtorefresh.storio.sqlite.queries.RawQuery;
@ -386,9 +389,36 @@ public class DatabaseHelper {
.prepare();
}
public PreparedPutCollectionOfObjects<MangaCategory> insertMangasCategory(List<MangaCategory> mangasCategory) {
public PreparedPutCollectionOfObjects<MangaCategory> insertMangasCategories(List<MangaCategory> mangasCategories) {
return db.put()
.objects(mangasCategory)
.objects(mangasCategories)
.prepare();
}
public PreparedDeleteByQuery deleteOldMangasCategories(List<Manga> mangas) {
List<Long> mangaIds = Observable.from(mangas)
.map(manga -> manga.id)
.toList().toBlocking().single();
return db.delete()
.byQuery(DeleteQuery.builder()
.table(MangaCategoryTable.TABLE)
.where(MangaCategoryTable.COLUMN_MANGA_ID + " IN ("
+ Queries.placeholders(mangas.size()) + ")")
.whereArgs(mangaIds.toArray())
.build())
.prepare();
}
public void setMangaCategories(List<MangaCategory> mangasCategories, List<Manga> mangas) {
db.internal().beginTransaction();
try {
deleteOldMangasCategories(mangas).executeAsBlocking();
insertMangasCategories(mangasCategories).executeAsBlocking();
db.internal().setTransactionSuccessful();
} finally {
db.internal().endTransaction();
}
}
}

View File

@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.reader.viewer.common;
package eu.kanade.mangafeed.ui.base.adapter;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
@ -6,6 +6,9 @@ import android.support.v4.app.FragmentStatePagerAdapter;
import android.util.SparseArray;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
public abstract class SmartFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
// Sparse array to keep track of registered fragments in memory
private SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
@ -33,4 +36,13 @@ public abstract class SmartFragmentStatePagerAdapter extends FragmentStatePagerA
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
public List<Fragment> getRegisteredFragments() {
ArrayList<Fragment> fragments = new ArrayList<>();
for (int i = 0; i < registeredFragments.size(); i++) {
fragments.add(registeredFragments.valueAt(i));
}
return fragments;
}
}

View File

@ -2,15 +2,15 @@ package eu.kanade.mangafeed.ui.library;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import java.util.List;
import eu.kanade.mangafeed.data.database.models.Category;
import eu.kanade.mangafeed.ui.base.adapter.SmartFragmentStatePagerAdapter;
class LibraryAdapter extends FragmentStatePagerAdapter {
public class LibraryAdapter extends SmartFragmentStatePagerAdapter {
private List<Category> categories;
protected List<Category> categories;
public LibraryAdapter(FragmentManager fm) {
super(fm);
@ -18,8 +18,7 @@ class LibraryAdapter extends FragmentStatePagerAdapter {
@Override
public Fragment getItem(int position) {
Category category = categories.get(position);
return LibraryCategoryFragment.newInstance(category);
return LibraryCategoryFragment.newInstance(position);
}
@Override
@ -37,4 +36,10 @@ class LibraryAdapter extends FragmentStatePagerAdapter {
notifyDataSetChanged();
}
public void setSelectionMode(int mode) {
for (Fragment fragment : getRegisteredFragments()) {
((LibraryCategoryFragment) fragment).setMode(mode);
}
}
}

View File

@ -3,24 +3,22 @@ package eu.kanade.mangafeed.ui.library;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v7.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import com.f2prateek.rx.preferences.Preference;
import java.util.ArrayList;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.models.Category;
import eu.kanade.mangafeed.data.database.models.Manga;
import eu.kanade.mangafeed.event.LibraryMangasEvent;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
import eu.kanade.mangafeed.ui.base.adapter.FlexibleViewHolder;
import eu.kanade.mangafeed.ui.base.fragment.BaseFragment;
import eu.kanade.mangafeed.ui.manga.MangaActivity;
@ -30,20 +28,19 @@ import icepick.Icepick;
import icepick.State;
import rx.Subscription;
public class LibraryCategoryFragment extends BaseFragment implements
ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener {
public class LibraryCategoryFragment extends BaseFragment
implements FlexibleViewHolder.OnListItemClickListener {
@Bind(R.id.library_mangas) AutofitRecyclerView recycler;
@State Category category;
@State int position;
private LibraryCategoryAdapter adapter;
private ActionMode actionMode;
private Subscription numColumnsSubscription;
public static LibraryCategoryFragment newInstance(Category category) {
public static LibraryCategoryFragment newInstance(int position) {
LibraryCategoryFragment fragment = new LibraryCategoryFragment();
fragment.category = category;
fragment.position = position;
return fragment;
}
@ -54,11 +51,14 @@ public class LibraryCategoryFragment extends BaseFragment implements
ButterKnife.bind(this, view);
Icepick.restoreInstanceState(this, savedState);
recycler.setHasFixedSize(true);
adapter = new LibraryCategoryAdapter(this);
recycler.setHasFixedSize(true);
recycler.setAdapter(adapter);
if (getLibraryFragment().getActionMode() != null) {
setMode(FlexibleAdapter.MODE_MULTI);
}
Preference<Integer> columnsPref = getResources().getConfiguration()
.orientation == Configuration.ORIENTATION_PORTRAIT ?
getLibraryPresenter().preferences.portraitColumns() :
@ -67,6 +67,14 @@ public class LibraryCategoryFragment extends BaseFragment implements
numColumnsSubscription = columnsPref.asObservable()
.subscribe(recycler::setSpanCount);
if (savedState != null) {
adapter.onRestoreInstanceState(savedState);
if (adapter.getMode() == FlexibleAdapter.MODE_SINGLE) {
adapter.clearSelection();
}
}
return view;
}
@ -91,13 +99,23 @@ public class LibraryCategoryFragment extends BaseFragment implements
@Override
public void onSaveInstanceState(Bundle outState) {
Icepick.saveInstanceState(this, outState);
adapter.onSaveInstanceState(outState);
super.onSaveInstanceState(outState);
}
@EventBusHook
public void onEventMainThread(LibraryMangasEvent event) {
destroyActionModeIfNeeded();
setMangas(event.getMangas().get(category.id));
List<Category> categories = getLibraryFragment().getAdapter().categories;
// When a category is deleted, the index can be greater than the number of categories
if (position >= categories.size())
return;
Category category = categories.get(position);
List<Manga> mangas = event.getMangas().get(category.id);
if (mangas == null) {
mangas = new ArrayList<>();
}
setMangas(mangas);
}
protected void openManga(Manga manga) {
@ -115,7 +133,7 @@ public class LibraryCategoryFragment extends BaseFragment implements
@Override
public boolean onListItemClick(int position) {
if (actionMode != null && position != -1) {
if (getLibraryFragment().getActionMode() != null && position != -1) {
toggleSelection(position);
return true;
} else {
@ -126,55 +144,29 @@ public class LibraryCategoryFragment extends BaseFragment implements
@Override
public void onListItemLongClick(int position) {
if (actionMode == null)
actionMode = ((BaseActivity) getActivity()).startSupportActionMode(this);
getLibraryFragment().createActionModeIfNeeded();
toggleSelection(position);
}
private void toggleSelection(int position) {
LibraryFragment f = getLibraryFragment();
adapter.toggleSelection(position, false);
f.getPresenter().setSelection(adapter.getItem(position), adapter.isSelected(position));
int count = adapter.getSelectedItemCount();
int count = f.getPresenter().selectedMangas.size();
if (count == 0) {
actionMode.finish();
f.destroyActionModeIfNeeded();
} else {
setContextTitle(count);
actionMode.invalidate();
f.setContextTitle(count);
f.invalidateActionMode();
}
}
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.library_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) {
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
adapter.setMode(LibraryCategoryAdapter.MODE_SINGLE);
public void setMode(int mode) {
adapter.setMode(mode);
if (mode == FlexibleAdapter.MODE_SINGLE) {
adapter.clearSelection();
actionMode = null;
}
public void destroyActionModeIfNeeded() {
if (actionMode != null) {
actionMode.finish();
}
}

View File

@ -2,10 +2,12 @@ package eu.kanade.mangafeed.ui.library;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.support.v7.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -13,21 +15,27 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import com.afollestad.materialdialogs.MaterialDialog;
import java.util.ArrayList;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.models.Category;
import eu.kanade.mangafeed.data.database.models.Manga;
import eu.kanade.mangafeed.data.sync.LibraryUpdateService;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
import eu.kanade.mangafeed.ui.library.category.CategoryFragment;
import eu.kanade.mangafeed.ui.main.MainActivity;
import nucleus.factory.RequiresPresenter;
@RequiresPresenter(LibraryPresenter.class)
public class LibraryFragment extends BaseRxFragment<LibraryPresenter> {
public class LibraryFragment extends BaseRxFragment<LibraryPresenter>
implements ActionMode.Callback {
TabLayout tabs;
AppBarLayout appBar;
@ -35,6 +43,8 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> {
@Bind(R.id.view_pager) ViewPager categoriesPager;
protected LibraryAdapter adapter;
private ActionMode actionMode;
public static LibraryFragment newInstance() {
return new LibraryFragment();
}
@ -83,7 +93,6 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> {
Intent intent = LibraryUpdateService.getStartIntent(getActivity());
getActivity().startService(intent);
}
return true;
case R.id.action_edit_categories:
onEditCategories();
@ -112,4 +121,77 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> {
tabs.setVisibility(actualCategories.size() == 1 ? View.GONE : View.VISIBLE);
}
public 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.library_selection, menu);
adapter.setSelectionMode(FlexibleAdapter.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_move_to_category:
moveMangasToCategories(getPresenter().selectedMangas);
return true;
}
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
adapter.setSelectionMode(FlexibleAdapter.MODE_SINGLE);
getPresenter().selectedMangas.clear();
actionMode = null;
}
public void destroyActionModeIfNeeded() {
if (actionMode != null) {
actionMode.finish();
}
}
private void moveMangasToCategories(List<Manga> mangas) {
new MaterialDialog.Builder(getActivity())
.title(R.string.action_move_category)
.items(getPresenter().getCategoriesNames())
.itemsCallbackMultiChoice(null, (dialog, which, text) -> {
getPresenter().moveMangasToCategories(which, mangas);
destroyActionModeIfNeeded();
return true;
})
.positiveText(R.string.button_ok)
.negativeText(R.string.button_cancel)
.show();
}
@Nullable
public ActionMode getActionMode() {
return actionMode;
}
public LibraryAdapter getAdapter() {
return adapter;
}
public void createActionModeIfNeeded() {
if (actionMode == null) {
actionMode = ((BaseActivity) getActivity()).startSupportActionMode(this);
}
}
public void invalidateActionMode() {
actionMode.invalidate();
}
}

View File

@ -3,6 +3,7 @@ package eu.kanade.mangafeed.ui.library;
import android.os.Bundle;
import android.util.Pair;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -13,6 +14,7 @@ import eu.kanade.mangafeed.data.cache.CoverCache;
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.MangaCategory;
import eu.kanade.mangafeed.data.preference.PreferencesHelper;
import eu.kanade.mangafeed.data.source.SourceManager;
import eu.kanade.mangafeed.event.LibraryMangasEvent;
@ -29,6 +31,7 @@ public class LibraryPresenter extends BasePresenter<LibraryFragment> {
@Inject SourceManager sourceManager;
protected List<Category> categories;
protected List<Manga> selectedMangas;
private static final int GET_CATEGORIES = 1;
@ -36,6 +39,8 @@ public class LibraryPresenter extends BasePresenter<LibraryFragment> {
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
selectedMangas = new ArrayList<>();
restartableLatestCache(GET_CATEGORIES,
this::getCategoriesObservable,
LibraryFragment::onNextCategories);
@ -48,6 +53,12 @@ public class LibraryPresenter extends BasePresenter<LibraryFragment> {
}
@Override
protected void onDestroy() {
EventBus.getDefault().removeStickyEvent(LibraryMangasEvent.class);
super.onDestroy();
}
public Observable<List<Category>> getCategoriesObservable() {
return db.getCategories().createObservable()
.doOnNext(categories -> this.categories = categories)
@ -72,5 +83,43 @@ public class LibraryPresenter extends BasePresenter<LibraryFragment> {
.subscribe());
}
public void setSelection(Manga manga, boolean selected) {
if (selected) {
selectedMangas.add(manga);
} else {
selectedMangas.remove(manga);
}
}
public String[] getCategoriesNames() {
int count = categories.size();
String[] names = new String[count];
for (int i = 0; i < count; i++) {
names[i] = categories.get(i).name;
}
return names;
}
public void moveMangasToCategories(Integer[] positions, List<Manga> mangas) {
List<Category> categoriesToAdd = new ArrayList<>();
for (Integer index : positions) {
categoriesToAdd.add(categories.get(index));
}
moveMangasToCategories(categoriesToAdd, mangas);
}
public void moveMangasToCategories(List<Category> categories, List<Manga> mangas) {
List<MangaCategory> mc = new ArrayList<>();
for (Manga manga : mangas) {
for (Category cat : categories) {
mc.add(MangaCategory.create(manga, cat));
}
}
db.setMangaCategories(mc, mangas);
}
}

View File

@ -6,6 +6,7 @@ import android.support.v4.app.FragmentManager;
import java.util.List;
import eu.kanade.mangafeed.data.source.model.Page;
import eu.kanade.mangafeed.ui.base.adapter.SmartFragmentStatePagerAdapter;
public class ViewPagerReaderAdapter extends SmartFragmentStatePagerAdapter {

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

View File

@ -3,10 +3,14 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_move_to_category"
android:title="@string/action_move_category"
android:icon="@drawable/ic_label"
app:showAsAction="ifRoom"/>
<item android:id="@+id/action_delete"
android:title="@string/action_delete"
android:icon="@drawable/ic_action_delete"
android:orderInCategory="1"
app:showAsAction="always"/>
app:showAsAction="ifRoom"/>
</menu>

View File

@ -25,6 +25,7 @@
<string name="action_add_category">Add category</string>
<string name="action_edit_categories">Edit categories</string>
<string name="action_rename_category">Rename category</string>
<string name="action_move_category">Move to categories</string>
<string name="action_sort_up">Sort up</string>
<string name="action_sort_down">Sort down</string>
<string name="action_show_unread">Unread</string>