浏览代码

Change mangas from categories (needs testing)

inorichi 9 年之前
父节点
当前提交
1360a90bf9

+ 32 - 2
app/src/main/java/eu/kanade/mangafeed/data/database/DatabaseHelper.java

@@ -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();
+        }
+    }
+
 }

+ 13 - 1
app/src/main/java/eu/kanade/mangafeed/ui/reader/viewer/common/SmartFragmentStatePagerAdapter.java → app/src/main/java/eu/kanade/mangafeed/ui/base/adapter/SmartFragmentStatePagerAdapter.java

@@ -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;
+    }
+
 }

+ 10 - 5
app/src/main/java/eu/kanade/mangafeed/ui/library/LibraryAdapter.java

@@ -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);
+        }
+    }
+
 }

+ 45 - 53
app/src/main/java/eu/kanade/mangafeed/ui/library/LibraryCategoryFragment.java

@@ -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);
-        adapter.clearSelection();
-        actionMode = null;
-    }
-
-    public void destroyActionModeIfNeeded() {
-        if (actionMode != null) {
-            actionMode.finish();
+    public void setMode(int mode) {
+        adapter.setMode(mode);
+        if (mode == FlexibleAdapter.MODE_SINGLE) {
+            adapter.clearSelection();
         }
     }
 

+ 84 - 2
app/src/main/java/eu/kanade/mangafeed/ui/library/LibraryFragment.java

@@ -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();
+    }
+
 }

+ 49 - 0
app/src/main/java/eu/kanade/mangafeed/ui/library/LibraryPresenter.java

@@ -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);
+    }
 }

+ 1 - 0
app/src/main/java/eu/kanade/mangafeed/ui/reader/viewer/common/ViewPagerReaderAdapter.java

@@ -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 {
 

二进制
app/src/main/res/drawable-hdpi/ic_label.png


二进制
app/src/main/res/drawable-ldpi/ic_label.png


二进制
app/src/main/res/drawable-mdpi/ic_label.png


二进制
app/src/main/res/drawable-xhdpi/ic_label.png


二进制
app/src/main/res/drawable-xxhdpi/ic_label.png


二进制
app/src/main/res/drawable-xxxhdpi/ic_label.png


+ 9 - 5
app/src/main/res/menu/library_selection.xml

@@ -1,12 +1,16 @@
 <?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">
+      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"/>
+          android:title="@string/action_delete"
+          android:icon="@drawable/ic_action_delete"
+          app:showAsAction="ifRoom"/>
 
 </menu>

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -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>