diff --git a/app/build.gradle b/app/build.gradle index eb52dbd0d5..65c8eb77cf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -88,6 +88,7 @@ dependencies { compile "frankiesardo:icepick:$ICEPICK_VERSION" provided "frankiesardo:icepick-processor:$ICEPICK_VERSION" compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4' + compile 'eu.davidea:flexible-adapter:4.0.1@aar' compile "com.google.dagger:dagger:$DAGGER_VERSION" apt "com.google.dagger:dagger-compiler:$DAGGER_VERSION" diff --git a/app/src/main/java/eu/kanade/mangafeed/presenter/MangaChaptersPresenter.java b/app/src/main/java/eu/kanade/mangafeed/presenter/MangaChaptersPresenter.java index 3e676d5607..ad83d3d0b6 100644 --- a/app/src/main/java/eu/kanade/mangafeed/presenter/MangaChaptersPresenter.java +++ b/app/src/main/java/eu/kanade/mangafeed/presenter/MangaChaptersPresenter.java @@ -18,6 +18,7 @@ import eu.kanade.mangafeed.ui.fragment.MangaChaptersFragment; import eu.kanade.mangafeed.util.EventBusHook; import eu.kanade.mangafeed.util.PostResult; import rx.Observable; +import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; @@ -32,6 +33,8 @@ public class MangaChaptersPresenter extends BasePresenter private static final int DB_CHAPTERS = 1; private static final int ONLINE_CHAPTERS = 2; + private Subscription menuOperationSubscription; + @Override protected void onCreate(Bundle savedState) { super.onCreate(savedState); @@ -106,4 +109,22 @@ public class MangaChaptersPresenter extends BasePresenter public void onChapterClicked(Chapter chapter) { EventBus.getDefault().postSticky(new SourceChapterEvent(source, chapter)); } + + public void markChaptersRead(Observable selectedChapters, boolean read) { + if (menuOperationSubscription != null) + remove(menuOperationSubscription); + + add(menuOperationSubscription = selectedChapters + .subscribeOn(Schedulers.io()) + .map(chapter -> { + chapter.read = read; + return chapter; + }) + .toList() + .flatMap(db::insertChapters) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + + })); + } } diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/adapter/ChaptersAdapter.java b/app/src/main/java/eu/kanade/mangafeed/ui/adapter/ChaptersAdapter.java new file mode 100644 index 0000000000..d98e1b86a2 --- /dev/null +++ b/app/src/main/java/eu/kanade/mangafeed/ui/adapter/ChaptersAdapter.java @@ -0,0 +1,52 @@ +package eu.kanade.mangafeed.ui.adapter; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.List; + +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.kanade.mangafeed.R; +import eu.kanade.mangafeed.data.models.Chapter; +import eu.kanade.mangafeed.ui.fragment.base.BaseFragment; +import eu.kanade.mangafeed.ui.holder.ChaptersHolder; + +public class ChaptersAdapter extends FlexibleAdapter { + + private Context context; + public OnItemClickListener clickListener; + + public ChaptersAdapter(BaseFragment fragment) { + this.context = fragment.getActivity(); + mItems = new ArrayList<>(); + clickListener = (OnItemClickListener) fragment; + } + + @Override + public void updateDataSet(String param) {} + + @Override + public ChaptersHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View v = LayoutInflater.from(context).inflate(R.layout.item_chapter, parent, false); + return new ChaptersHolder(v, this); + } + + @Override + public void onBindViewHolder(ChaptersHolder holder, int position) { + final Chapter chapter = getItem(position); + holder.onSetValues(context, chapter); + } + + public void setItems(List chapters) { + mItems = chapters; + notifyDataSetChanged(); + } + + public interface OnItemClickListener { + boolean onListItemClick(int position); + void onListItemLongClick(int position); + } +} diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/fragment/MangaChaptersFragment.java b/app/src/main/java/eu/kanade/mangafeed/ui/fragment/MangaChaptersFragment.java index b58153cde8..f2d94f7b91 100644 --- a/app/src/main/java/eu/kanade/mangafeed/ui/fragment/MangaChaptersFragment.java +++ b/app/src/main/java/eu/kanade/mangafeed/ui/fragment/MangaChaptersFragment.java @@ -4,6 +4,7 @@ import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.view.ActionMode; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -22,18 +23,22 @@ import eu.kanade.mangafeed.data.models.Chapter; import eu.kanade.mangafeed.presenter.MangaChaptersPresenter; import eu.kanade.mangafeed.ui.activity.MangaDetailActivity; import eu.kanade.mangafeed.ui.activity.ReaderActivity; -import eu.kanade.mangafeed.ui.holder.ChapterListHolder; +import eu.kanade.mangafeed.ui.activity.base.BaseActivity; +import eu.kanade.mangafeed.ui.adapter.ChaptersAdapter; import eu.kanade.mangafeed.ui.fragment.base.BaseRxFragment; import nucleus.factory.RequiresPresenter; -import uk.co.ribot.easyadapter.EasyRecyclerAdapter; +import rx.Observable; @RequiresPresenter(MangaChaptersPresenter.class) -public class MangaChaptersFragment extends BaseRxFragment { +public class MangaChaptersFragment extends BaseRxFragment implements + ActionMode.Callback, ChaptersAdapter.OnItemClickListener { @Bind(R.id.chapter_list) RecyclerView chapters; @Bind(R.id.swipe_refresh) SwipeRefreshLayout swipeRefresh; - private EasyRecyclerAdapter adapter; + private ChaptersAdapter adapter; + + private ActionMode actionMode; public static Fragment newInstance() { return new MangaChaptersFragment(); @@ -76,13 +81,7 @@ public class MangaChaptersFragment extends BaseRxFragment { - getPresenter().onChapterClicked(chapter); - Intent intent = ReaderActivity.newInstance(getActivity()); - startActivity(intent); - }; - - adapter = new EasyRecyclerAdapter<>(getActivity(), ChapterListHolder.class, listener); + adapter = new ChaptersAdapter(this); chapters.setAdapter(adapter); } @@ -92,6 +91,7 @@ public class MangaChaptersFragment extends BaseRxFragment chapters) { adapter.setItems(chapters); + closeActionMode(); } public void onNextOnlineChapters() { @@ -105,4 +105,87 @@ public class MangaChaptersFragment extends BaseRxFragment getSelectedChapters() { + return Observable.from(adapter.getSelectedItems()) + .map(adapter::getItem); + } + + public void closeActionMode() { + if (actionMode != null) + actionMode.finish(); + } + + @Override + public boolean onListItemClick(int position) { + if (actionMode != null && adapter.getMode() == ChaptersAdapter.MODE_MULTI) { + toggleSelection(position); + return true; + } else { + getPresenter().onChapterClicked(adapter.getItem(position)); + Intent intent = ReaderActivity.newInstance(getActivity()); + startActivity(intent); + 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.selected_chapters_title, count)); + } } diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/holder/ChapterListHolder.java b/app/src/main/java/eu/kanade/mangafeed/ui/holder/ChapterListHolder.java deleted file mode 100644 index 1b273d311f..0000000000 --- a/app/src/main/java/eu/kanade/mangafeed/ui/holder/ChapterListHolder.java +++ /dev/null @@ -1,60 +0,0 @@ -package eu.kanade.mangafeed.ui.holder; - -import android.graphics.Color; -import android.support.v4.content.ContextCompat; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import eu.kanade.mangafeed.R; -import eu.kanade.mangafeed.data.models.Chapter; -import uk.co.ribot.easyadapter.ItemViewHolder; -import uk.co.ribot.easyadapter.PositionInfo; -import uk.co.ribot.easyadapter.annotations.LayoutId; -import uk.co.ribot.easyadapter.annotations.ViewId; - -@LayoutId(R.layout.item_chapter) -public class ChapterListHolder extends ItemViewHolder { - - @ViewId(R.id.chapter_title) TextView title; - @ViewId(R.id.chapter_download_image) ImageView download_icon; - @ViewId(R.id.chapter_pages) TextView pages; - - View view; - - public ChapterListHolder(View view) { - super(view); - this.view = view; - } - - public void onSetValues(Chapter chapter, PositionInfo positionInfo) { - title.setText(chapter.name); - download_icon.setImageResource(R.drawable.ic_file_download_black_48dp); - - if (chapter.read) { - title.setTextColor(ContextCompat.getColor(getContext(), R.color.chapter_read_text)); - } else { - title.setTextColor(Color.BLACK); - } - - if (chapter.last_page_read > 0 && !chapter.read) { - pages.setText(getContext().getString(R.string.chapter_progress, chapter.last_page_read+1)); - } else { - pages.setText(""); - } - } - - @Override - public void onSetListeners() { - view.setOnClickListener(view -> { - ChapterListener listener = getListener(ChapterListener.class); - if (listener != null) { - listener.onRowClicked(getItem()); - } - }); - } - - public interface ChapterListener { - void onRowClicked(Chapter chapter); - } -} diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/holder/ChaptersHolder.java b/app/src/main/java/eu/kanade/mangafeed/ui/holder/ChaptersHolder.java new file mode 100644 index 0000000000..a904e74e78 --- /dev/null +++ b/app/src/main/java/eu/kanade/mangafeed/ui/holder/ChaptersHolder.java @@ -0,0 +1,74 @@ +package eu.kanade.mangafeed.ui.holder; + +import android.content.Context; +import android.graphics.Color; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import butterknife.Bind; +import butterknife.ButterKnife; +import eu.kanade.mangafeed.R; +import eu.kanade.mangafeed.data.models.Chapter; +import eu.kanade.mangafeed.ui.adapter.ChaptersAdapter; + +public class ChaptersHolder extends RecyclerView.ViewHolder implements + View.OnClickListener, View.OnLongClickListener { + + private ChaptersAdapter adapter; + + @Bind(R.id.chapter_title) TextView title; + @Bind(R.id.chapter_download_image) ImageView download_icon; + @Bind(R.id.chapter_pages) TextView pages; + + public ChaptersHolder(View view) { + super(view); + ButterKnife.bind(this, view); + } + + public ChaptersHolder(View view, final ChaptersAdapter adapter) { + this(view); + + this.adapter = adapter; + itemView.setOnClickListener(this); + itemView.setOnLongClickListener(this); + } + + public void onSetValues(Context context, Chapter chapter) { + title.setText(chapter.name); + download_icon.setImageResource(R.drawable.ic_file_download_black_48dp); + + if (chapter.read) { + title.setTextColor(ContextCompat.getColor(context, R.color.chapter_read_text)); + } else { + title.setTextColor(Color.BLACK); + } + + if (chapter.last_page_read > 0 && !chapter.read) { + pages.setText(context.getString(R.string.chapter_progress, chapter.last_page_read + 1)); + } else { + pages.setText(""); + } + + toggleActivation(); + } + + private void toggleActivation() { + itemView.setActivated(adapter.isSelected(getAdapterPosition())); + } + + @Override + public void onClick(View v) { + if (adapter.clickListener.onListItemClick(getAdapterPosition())) + toggleActivation(); + } + + @Override + public boolean onLongClick(View v) { + adapter.clickListener.onListItemLongClick(getAdapterPosition()); + toggleActivation(); + return true; + } +} diff --git a/app/src/main/res/drawable-hdpi/ic_action_done_all.png b/app/src/main/res/drawable-hdpi/ic_action_done_all.png new file mode 100644 index 0000000000..90c28786fc Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_done_all.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_select_all.png b/app/src/main/res/drawable-hdpi/ic_action_select_all.png new file mode 100644 index 0000000000..20f0ee821b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_select_all.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_undone_all.png b/app/src/main/res/drawable-hdpi/ic_action_undone_all.png new file mode 100644 index 0000000000..a5c8b2bfda Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_undone_all.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_done_all.png b/app/src/main/res/drawable-mdpi/ic_action_done_all.png new file mode 100644 index 0000000000..c4c3717b66 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_done_all.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_select_all.png b/app/src/main/res/drawable-mdpi/ic_action_select_all.png new file mode 100644 index 0000000000..8e1c873507 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_select_all.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_undone_all.png b/app/src/main/res/drawable-mdpi/ic_action_undone_all.png new file mode 100644 index 0000000000..093b1af9ef Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_undone_all.png differ diff --git a/app/src/main/res/drawable-v21/selector_chapter_light.xml b/app/src/main/res/drawable-v21/selector_chapter_light.xml new file mode 100644 index 0000000000..f92e970b2a --- /dev/null +++ b/app/src/main/res/drawable-v21/selector_chapter_light.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/ic_action_done_all.png b/app/src/main/res/drawable-xhdpi/ic_action_done_all.png new file mode 100644 index 0000000000..a592962597 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_done_all.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_select_all.png b/app/src/main/res/drawable-xhdpi/ic_action_select_all.png new file mode 100644 index 0000000000..aade8fa891 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_select_all.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_undone_all.png b/app/src/main/res/drawable-xhdpi/ic_action_undone_all.png new file mode 100644 index 0000000000..3d5a26b501 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_undone_all.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_done_all.png b/app/src/main/res/drawable-xxhdpi/ic_action_done_all.png new file mode 100644 index 0000000000..97b9be1235 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_done_all.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_select_all.png b/app/src/main/res/drawable-xxhdpi/ic_action_select_all.png new file mode 100644 index 0000000000..0b14667a1c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_select_all.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_undone_all.png b/app/src/main/res/drawable-xxhdpi/ic_action_undone_all.png new file mode 100644 index 0000000000..7e698e4f7e Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_undone_all.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_done_all.png b/app/src/main/res/drawable-xxxhdpi/ic_action_done_all.png new file mode 100644 index 0000000000..49545afcfd Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_action_done_all.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_select_all.png b/app/src/main/res/drawable-xxxhdpi/ic_action_select_all.png new file mode 100644 index 0000000000..a8ba084363 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_action_select_all.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_undone_all.png b/app/src/main/res/drawable-xxxhdpi/ic_action_undone_all.png new file mode 100644 index 0000000000..e042cc2baa Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_action_undone_all.png differ diff --git a/app/src/main/res/drawable/selector_chapter_light.xml b/app/src/main/res/drawable/selector_chapter_light.xml new file mode 100644 index 0000000000..58df6ebf87 --- /dev/null +++ b/app/src/main/res/drawable/selector_chapter_light.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_chapter.xml b/app/src/main/res/layout/item_chapter.xml index 9c33e3f703..f652d2f40f 100644 --- a/app/src/main/res/layout/item_chapter.xml +++ b/app/src/main/res/layout/item_chapter.xml @@ -3,12 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" android:orientation="horizontal" android:layout_width="match_parent" - android:layout_height="40dp"> - - + android:layout_height="40dp" + android:background="?attr/selectableItemBackground"> + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 7aa1362cc8..0f731d7e34 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -16,4 +16,5 @@ #DD000000 #E8E8E8 #909090 + #607D8B \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1d18e040ed..ba03e13781 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,7 +32,7 @@ Settings Search Refresh - Title or author... + Title or author… Delete Selected CatalogueList @@ -77,4 +77,9 @@ Page: %1$d This source requires login + Select all + Mark as read + Mark as unread + Selected chapters: %1$d + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 9e911bf999..a539c2f4bd 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -11,6 +11,7 @@ @color/white true @style/Widget.ActionMode + @drawable/selector_chapter_light