diff --git a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java index 272248e3a..fe6a3b77b 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java @@ -71,7 +71,6 @@ import com.hippo.ehviewer.ui.scene.GalleryListScene; import com.hippo.ehviewer.ui.scene.GalleryPreviewsScene; import com.hippo.ehviewer.ui.scene.HistoryScene; import com.hippo.ehviewer.ui.scene.ProgressScene; -import com.hippo.ehviewer.ui.scene.QuickSearchScene; import com.hippo.ehviewer.ui.scene.SecurityScene; import com.hippo.ehviewer.ui.scene.SelectSiteScene; import com.hippo.ehviewer.ui.scene.SignInScene; @@ -115,7 +114,6 @@ public final class MainActivity extends StageActivity registerLaunchMode(CookieSignInScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK); registerLaunchMode(SelectSiteScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK); registerLaunchMode(GalleryListScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TOP); - registerLaunchMode(QuickSearchScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK); registerLaunchMode(GalleryDetailScene.class, SceneFragment.LAUNCH_MODE_STANDARD); registerLaunchMode(GalleryInfoScene.class, SceneFragment.LAUNCH_MODE_STANDARD); registerLaunchMode(GalleryCommentsScene.class, SceneFragment.LAUNCH_MODE_STANDARD); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java index df4116e16..ed8beb21b 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java @@ -37,17 +37,18 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ListView; +import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.PopupMenu; import androidx.appcompat.widget.Toolbar; import androidx.drawerlayout.widget.DrawerLayout; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.github.amlcurran.showcaseview.ShowcaseView; @@ -55,6 +56,12 @@ import com.github.amlcurran.showcaseview.SimpleShowcaseEventListener; import com.github.amlcurran.showcaseview.targets.PointTarget; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator; +import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; +import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter; +import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange; +import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager; +import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableItemViewHolder; import com.hippo.android.resource.AttrResources; import com.hippo.app.CheckBoxDialogBuilder; import com.hippo.app.EditTextDialogBuilder; @@ -181,6 +188,8 @@ public final class GalleryListScene extends BaseScene private SearchBarMover mSearchBarMover; @Nullable private AddDeleteDrawable mActionFabDrawable; + @Nullable + private List mQuickSearchList; private int mHideActionFabSlop; private boolean mShowActionFab = true; @State @@ -662,8 +671,8 @@ public final class GalleryListScene extends BaseScene mActionFabDrawable = null; } - private void showQuickSearchTipDialog(final List list, - final ArrayAdapter adapter, final ListView listView, final TextView tip) { + private void showQuickSearchTipDialog(final QsDrawerAdapter adapter, + final EasyRecyclerView recyclerView, final TextView tip) { Context context = getContext2(); if (null == context) { return; @@ -677,13 +686,14 @@ public final class GalleryListScene extends BaseScene if (builder.isChecked()) { Settings.putQuickSearchTip(false); } - showAddQuickSearchDialog(list, adapter, listView, tip); + showAddQuickSearchDialog(adapter, recyclerView, tip); } }).show(); } - private void showAddQuickSearchDialog(final List list, - final ArrayAdapter adapter, final ListView listView, final TextView tip) { + + private void showAddQuickSearchDialog(final QsDrawerAdapter adapter, + final EasyRecyclerView recyclerView, final TextView tip) { Context context = getContext2(); final ListUrlBuilder urlBuilder = mUrlBuilder; if (null == context || null == urlBuilder) { @@ -697,7 +707,7 @@ public final class GalleryListScene extends BaseScene } // Check duplicate - for (QuickSearch q : list) { + for (QuickSearch q : mQuickSearchList) { if (urlBuilder.equalsQuickSearch(q)) { showTip(getString(R.string.duplicate_quick_search, q.name), LENGTH_LONG); return; @@ -721,7 +731,7 @@ public final class GalleryListScene extends BaseScene } // Check name duplicate - for (QuickSearch q : list) { + for (QuickSearch q : mQuickSearchList) { if (text.equals(q.name)) { builder.setError(getString(R.string.duplicate_name)); return; @@ -733,15 +743,15 @@ public final class GalleryListScene extends BaseScene QuickSearch quickSearch = urlBuilder.toQuickSearch(); quickSearch.name = text; EhDB.insertQuickSearch(quickSearch); - list.add(quickSearch); + mQuickSearchList.add(quickSearch); adapter.notifyDataSetChanged(); - if (0 == list.size()) { + if (0 == mQuickSearchList.size()) { tip.setVisibility(View.VISIBLE); - listView.setVisibility(View.GONE); + recyclerView.setVisibility(View.GONE); } else { tip.setVisibility(View.GONE); - listView.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.VISIBLE); } } }); @@ -750,62 +760,51 @@ public final class GalleryListScene extends BaseScene @Override public View onCreateDrawerView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.drawer_list, container, false); + View view = inflater.inflate(R.layout.drawer_list_rv, container, false); Toolbar toolbar = (Toolbar) ViewUtils.$$(view, R.id.toolbar); final TextView tip = (TextView) ViewUtils.$$(view, R.id.tip); - final ListView listView = (ListView) ViewUtils.$$(view, R.id.list_view); Context context = getContext2(); AssertUtils.assertNotNull(context); - final List list = EhDB.getAllQuickSearch(); - final ArrayAdapter adapter = new ArrayAdapter<>(context, R.layout.item_simple_list, list); - listView.setAdapter(adapter); - listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - if (null == mHelper || null == mUrlBuilder) { - return; - } - - mUrlBuilder.set(list.get(position)); - mUrlBuilder.setPageIndex(0); - onUpdateUrlBuilder(); - mHelper.refresh(); - setState(STATE_NORMAL); - closeDrawer(Gravity.RIGHT); - } - }); + RecyclerViewDragDropManager dragDropManager = new RecyclerViewDragDropManager(); + dragDropManager.setInitiateOnLongPress(true); + dragDropManager.setInitiateOnTouch(false); + dragDropManager.setDraggingItemAlpha(0.8f); + final EasyRecyclerView recyclerView = view.findViewById(R.id.recycler_view_drawer); + recyclerView.setLayoutManager(new LinearLayoutManager(context)); + recyclerView.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL)); + final QsDrawerAdapter qsDrawerAdapter = new QsDrawerAdapter(inflater); + qsDrawerAdapter.setHasStableIds(true); + final GeneralItemAnimator animator = new DraggableItemAnimator(); + mRecyclerView.setItemAnimator(animator); + recyclerView.setAdapter(dragDropManager.createWrappedAdapter(qsDrawerAdapter)); + dragDropManager.attachRecyclerView(recyclerView); + mQuickSearchList = EhDB.getAllQuickSearch(); tip.setText(R.string.quick_search_tip); toolbar.setTitle(R.string.quick_search); toolbar.inflateMenu(R.menu.drawer_gallery_list); - toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - int id = item.getItemId(); - switch (id) { - case R.id.action_add: - if (Settings.getQuickSearchTip()) { - showQuickSearchTipDialog(list, adapter, listView, tip); - } else { - showAddQuickSearchDialog(list, adapter, listView, tip); - } - break; - case R.id.action_settings: - startScene(new Announcer(QuickSearchScene.class)); - break; - } - return true; + toolbar.setOnMenuItemClickListener(item -> { + int id = item.getItemId(); + switch (id) { + case R.id.action_add: + if (Settings.getQuickSearchTip()) { + showQuickSearchTipDialog(qsDrawerAdapter, recyclerView, tip); + } else { + showAddQuickSearchDialog(qsDrawerAdapter, recyclerView, tip); + } + break; } + return true; }); - if (0 == list.size()) { + if (0 == mQuickSearchList.size()) { tip.setVisibility(View.VISIBLE); - listView.setVisibility(View.GONE); + recyclerView.setVisibility(View.GONE); } else { tip.setVisibility(View.GONE); - listView.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.VISIBLE); } return view; @@ -1502,6 +1501,121 @@ public final class GalleryListScene extends BaseScene } } + private class QsDrawerHolder extends AbstractDraggableItemViewHolder { + + private final TextView key; + private final ImageView option; + + private QsDrawerHolder(View itemView) { + super(itemView); + key = (TextView) ViewUtils.$$(itemView, R.id.tv_key); + option = (ImageView) ViewUtils.$$(itemView, R.id.iv_option); + } + } + + private class QsDrawerAdapter extends RecyclerView.Adapter implements DraggableItemAdapter { + + private final LayoutInflater mInflater; + + private QsDrawerAdapter(LayoutInflater inflater) { + + this.mInflater = inflater; + } + + @NonNull + @Override + public QsDrawerHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new QsDrawerHolder(mInflater.inflate(R.layout.item_quicksearch_list, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull QsDrawerHolder holder, int position) { + if (mQuickSearchList != null) { + holder.key.setText(mQuickSearchList.get(position).getName()); + holder.itemView.setOnClickListener(v -> { + if (null == mHelper || null == mUrlBuilder) { + return; + } + + mUrlBuilder.set(mQuickSearchList.get(position)); + mUrlBuilder.setPageIndex(0); + onUpdateUrlBuilder(); + mHelper.refresh(); + setState(STATE_NORMAL); + closeDrawer(Gravity.RIGHT); + }); + holder.option.setOnClickListener(v -> { + PopupMenu popupMenu = new PopupMenu(getContext2(), holder.option); + popupMenu.inflate(R.menu.quicksearch_option); + popupMenu.show(); + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + + final QuickSearch quickSearch = mQuickSearchList.get(position); + + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_qs_remove: + EhDB.deleteQuickSearch(quickSearch); + mQuickSearchList.remove(position); + notifyDataSetChanged(); + break; + } + return false; + } + }); + }); + } + } + + @Override + public long getItemId(int position) { + return mQuickSearchList != null ? mQuickSearchList.get(position).getId() : 0; + } + + @Override + public int getItemCount() { + return mQuickSearchList != null ? mQuickSearchList.size() : 0; + } + + @Override + public boolean onCheckCanStartDrag(@NonNull QsDrawerHolder holder, int position, int x, int y) { + return true; + } + + @Override + public ItemDraggableRange onGetItemDraggableRange(QsDrawerHolder holder, int position) { + return null; + } + + @Override + public void onMoveItem(int fromPosition, int toPosition) { + if (fromPosition == toPosition) { + return; + } + if (null == mQuickSearchList) { + return; + } + + EhDB.moveQuickSearch(fromPosition, toPosition); + final QuickSearch item = mQuickSearchList.remove(fromPosition); + mQuickSearchList.add(toPosition, item); + } + + @Override + public boolean onCheckCanDrop(int draggingPosition, int dropPosition) { + return true; + } + + @Override + public void onItemDragStarted(int position) { + } + + @Override + public void onItemDragFinished(int fromPosition, int toPosition, boolean result) { + } + } + private abstract class UrlSuggestion extends SearchBar.Suggestion { @Override public CharSequence getText(float textSize) { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/QuickSearchScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/QuickSearchScene.java deleted file mode 100644 index f9e0df975..000000000 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/QuickSearchScene.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright 2016 Hippo Seven - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hippo.ehviewer.ui.scene; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.NinePatchDrawable; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter; -import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange; -import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager; -import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableItemViewHolder; -import com.hippo.easyrecyclerview.EasyRecyclerView; -import com.hippo.ehviewer.EhDB; -import com.hippo.ehviewer.R; -import com.hippo.ehviewer.dao.QuickSearch; -import com.hippo.util.DrawableManager; -import com.hippo.view.ViewTransition; -import com.hippo.yorozuya.AssertUtils; -import com.hippo.yorozuya.ViewUtils; - -import java.util.List; - -public final class QuickSearchScene extends ToolbarScene { - - /*--------------- - Whole life cycle - ---------------*/ - @Nullable - private List mQuickSearchList; - - /*--------------- - View life cycle - ---------------*/ - @Nullable - private EasyRecyclerView mRecyclerView; - @Nullable - private ViewTransition mViewTransition; - @Nullable - private RecyclerView.Adapter mAdapter; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mQuickSearchList = EhDB.getAllQuickSearch(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - mQuickSearchList = null; - } - - @Override - public View onCreateView3(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.scene_label_list, container, false); - - mRecyclerView = (EasyRecyclerView) ViewUtils.$$(view, R.id.recycler_view); - TextView tip = (TextView) ViewUtils.$$(view, R.id.tip); - mViewTransition = new ViewTransition(mRecyclerView, tip); - - Context context = getContext2(); - AssertUtils.assertNotNull(context); - - Drawable drawable = DrawableManager.getVectorDrawable(context, R.drawable.big_search); - drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - tip.setCompoundDrawables(null, drawable, null, null); - tip.setText(R.string.no_quick_search); - - // drag & drop manager - RecyclerViewDragDropManager dragDropManager = new RecyclerViewDragDropManager(); - dragDropManager.setDraggingItemShadowDrawable( - (NinePatchDrawable) context.getResources().getDrawable(R.drawable.shadow_8dp)); - - RecyclerView.Adapter adapter = new QuickSearchAdapter(); - adapter.setHasStableIds(true); - adapter = dragDropManager.createWrappedAdapter(adapter); // wrap for dragging - mAdapter = adapter; - - final GeneralItemAnimator animator = new DraggableItemAnimator(); - mRecyclerView.setLayoutManager(new LinearLayoutManager(context)); - mRecyclerView.setAdapter(adapter); - mRecyclerView.setItemAnimator(animator); - - dragDropManager.attachRecyclerView(mRecyclerView); - - updateView(); - - return view; - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setTitle(R.string.quick_search); - setNavigationIcon(R.drawable.v_arrow_left_dark_x24); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - - if (null != mRecyclerView) { - mRecyclerView.stopScroll(); - mRecyclerView = null; - } - - mViewTransition = null; - } - - @Override - public void onNavigationClick() { - onBackPressed(); - } - - private void updateView() { - if (mViewTransition != null) { - if (mQuickSearchList != null && mQuickSearchList.size() > 0) { - mViewTransition.showView(0); - } else { - mViewTransition.showView(1); - } - } - } - - private class QuickSearchHolder extends AbstractDraggableItemViewHolder implements View.OnClickListener { - - public final TextView label; - public final View dragHandler; - public final View delete; - - public QuickSearchHolder(View itemView) { - super(itemView); - - label = (TextView) ViewUtils.$$(itemView, R.id.label); - dragHandler = ViewUtils.$$(itemView, R.id.drag_handler); - delete = ViewUtils.$$(itemView, R.id.delete); - - delete.setOnClickListener(this); - } - - @Override - public void onClick(View v) { - int position = getAdapterPosition(); - Context context = getContext2(); - if (position == RecyclerView.NO_POSITION || mQuickSearchList == null) { - return; - } - - final QuickSearch quickSearch = mQuickSearchList.get(position); - new MaterialAlertDialogBuilder(context) - .setTitle(R.string.delete_quick_search_title) - .setMessage(getString(R.string.delete_quick_search_message, quickSearch.name)) - .setPositiveButton(android.R.string.ok, (dialog, which) -> { - EhDB.deleteQuickSearch(quickSearch); - mQuickSearchList.remove(position); - }) - .setOnDismissListener(dialog -> { - if (null != mAdapter) { - mAdapter.notifyDataSetChanged(); - } - updateView(); - }).show(); - } - } - - private class QuickSearchAdapter extends RecyclerView.Adapter - implements DraggableItemAdapter { - - private final LayoutInflater mInflater; - - public QuickSearchAdapter() { - mInflater = getLayoutInflater2(); - AssertUtils.assertNotNull(mInflater); - } - - @Override - public QuickSearchHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return new QuickSearchHolder(mInflater.inflate(R.layout.item_quick_search, parent, false)); - } - - @Override - public void onBindViewHolder(QuickSearchHolder holder, int position) { - if (mQuickSearchList != null) { - holder.label.setText(mQuickSearchList.get(position).name); - } - } - - @Override - public long getItemId(int position) { - return mQuickSearchList != null ? mQuickSearchList.get(position).getId() : 0; - } - - @Override - public int getItemCount() { - return mQuickSearchList != null ? mQuickSearchList.size() : 0; - } - - @Override - public boolean onCheckCanStartDrag(QuickSearchHolder holder, int position, int x, int y) { - return ViewUtils.isViewUnder(holder.dragHandler, x, y, 0); - } - - @Override - public ItemDraggableRange onGetItemDraggableRange(QuickSearchHolder holder, int position) { - return null; - } - - @Override - public void onMoveItem(int fromPosition, int toPosition) { - if (fromPosition == toPosition) { - return; - } - if (null == mQuickSearchList) { - return; - } - - EhDB.moveQuickSearch(fromPosition, toPosition); - final QuickSearch item = mQuickSearchList.remove(fromPosition); - mQuickSearchList.add(toPosition, item); - } - - @Override - public boolean onCheckCanDrop(int draggingPosition, int dropPosition) { - return true; - } - - @Override - public void onItemDragStarted(int position) { - } - - @Override - public void onItemDragFinished(int fromPosition, int toPosition, boolean result) { - } - } -} diff --git a/app/src/main/res/drawable/ic_more_vert_black_24dp.xml b/app/src/main/res/drawable/ic_more_vert_black_24dp.xml new file mode 100644 index 000000000..64e2f23ee --- /dev/null +++ b/app/src/main/res/drawable/ic_more_vert_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/item_quicksearch_list.xml b/app/src/main/res/layout/item_quicksearch_list.xml new file mode 100644 index 000000000..6aba23e6d --- /dev/null +++ b/app/src/main/res/layout/item_quicksearch_list.xml @@ -0,0 +1,32 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/drawer_gallery_list.xml b/app/src/main/res/menu/drawer_gallery_list.xml index e9fa38aae..f701b5a05 100644 --- a/app/src/main/res/menu/drawer_gallery_list.xml +++ b/app/src/main/res/menu/drawer_gallery_list.xml @@ -23,10 +23,4 @@ android:title="@string/add" app:showAsAction="always" /> - - diff --git a/app/src/main/res/menu/quicksearch_option.xml b/app/src/main/res/menu/quicksearch_option.xml new file mode 100644 index 000000000..16a76ef4b --- /dev/null +++ b/app/src/main/res/menu/quicksearch_option.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file