Initial migrate GalleryListScene to New SearchBar, #397

This commit is contained in:
tarsin committed 2022-12-07 13:47:10 +08:00
1 parent 39e080cdca
commit 7ce273ec67
5 files changed
+408 -198

No files matched your search

@@ -32,6 +32,7 @@ import android.text.TextUtils;
import android.text.style.ImageSpan;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
@@ -92,7 +93,6 @@ import com.hippo.ehviewer.ui.GalleryActivity;
import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.ehviewer.ui.dialog.SelectItemWithIconAdapter;
import com.hippo.ehviewer.widget.GalleryInfoContentHelper;
import com.hippo.ehviewer.widget.SearchBar;
import com.hippo.ehviewer.widget.SearchLayout;
import com.hippo.scene.Announcer;
import com.hippo.scene.SceneFragment;
@@ -102,7 +102,6 @@ import com.hippo.view.BringOutTransition;
import com.hippo.view.ViewTransition;
import com.hippo.widget.ContentLayout;
import com.hippo.widget.FabLayout;
import com.hippo.widget.SearchBarMover;
import com.hippo.yorozuya.AnimationUtils;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.MathUtils;
@@ -123,9 +122,9 @@ import java.util.List;
import rikka.core.res.ResourcesKt;
@SuppressLint("RtlHardcoded")
public final class GalleryListScene extends BaseScene
implements SearchBar.Helper, SearchBar.OnStateChangeListener, FastScroller.OnDragHandlerListener,
SearchLayout.Helper, SearchBarMover.Helper, View.OnClickListener, FabLayout.OnClickFabListener,
public final class GalleryListScene extends SearchBarScene
implements FastScroller.OnDragHandlerListener,
SearchLayout.Helper, FabLayout.OnClickFabListener,
FabLayout.OnExpandListener {
public final static String KEY_ACTION = "action";
@@ -137,7 +136,6 @@ public final class GalleryListScene extends BaseScene
public final static String KEY_LIST_URL_BUILDER = "list_url_builder";
public final static String KEY_HAS_FIRST_REFRESH = "has_first_refresh";
public final static String KEY_STATE = "state";
private static final int BACK_PRESSED_INTERVAL = 2000;
private final static int STATE_NORMAL = 0;
private final static int STATE_SIMPLE_SEARCH = 1;
private final static int STATE_SEARCH = 2;
@@ -165,8 +163,6 @@ public final class GalleryListScene extends BaseScene
new ActivityResultContracts.PickVisualMedia(),
result -> mSearchLayout.setImageUri(result));
@Nullable
private SearchBar mSearchBar;
@Nullable
private View mSearchFab;
@Nullable
private final Animator.AnimatorListener mSearchFabAnimatorListener = new SimpleAnimatorListener() {
@@ -199,10 +195,6 @@ public final class GalleryListScene extends BaseScene
@Nullable
private DrawerArrowDrawable mLeftDrawable;
@Nullable
private AddDeleteDrawable mRightDrawable;
@Nullable
private SearchBarMover mSearchBarMover;
@Nullable
private AddDeleteDrawable mActionFabDrawable;
@Nullable
private List<QuickSearch> mQuickSearchList;
@@ -278,6 +270,11 @@ public final class GalleryListScene extends BaseScene
}
}
@Override
public int getMenuResId() {
return R.menu.scene_gallery_list_searchbar_menu;
}
public static void startScene(SceneFragment scene, ListUrlBuilder lub) {
scene.startScene(getStartAnnouncer(lub));
}
@@ -328,9 +325,7 @@ public final class GalleryListScene extends BaseScene
mHelper.refresh();
}
setState(STATE_NORMAL);
if (null != mSearchBarMover) {
mSearchBarMover.showSearchBar();
}
showSearchBar();
}
@Override
@@ -441,14 +436,14 @@ public final class GalleryListScene extends BaseScene
mFavouriteStatusRouter.removeListener(mFavouriteStatusRouterListener);
}
private void setSearchBarHint(SearchBar searchBar) {
searchBar.setEditTextHint(getString(EhUrl.SITE_EX == Settings.getGallerySite() ?
private void setSearchBarHint() {
setEditTextHint(getString(EhUrl.SITE_EX == Settings.getGallerySite() ?
R.string.gallery_list_search_bar_hint_exhentai :
R.string.gallery_list_search_bar_hint_e_hentai));
}
private void setSearchBarSuggestionProvider(SearchBar searchBar) {
searchBar.setSuggestionProvider(text -> {
private void setSearchBarSuggestionProvider() {
setSuggestionProvider(text -> {
GalleryDetailUrlParser.Result result1 = GalleryDetailUrlParser.parse(text, false);
if (result1 != null) {
return Collections.singletonList(new GalleryDetailUrlSuggestion(result1.gid, result1.token));
@@ -508,12 +503,11 @@ public final class GalleryListScene extends BaseScene
: R.id.search_normal_search);
// Update search edit text
if (!TextUtils.isEmpty(keyword) && null != mSearchBar && !mIsTopList) {
if (!TextUtils.isEmpty(keyword) && !mIsTopList) {
if (builder.getMode() == ListUrlBuilder.MODE_TAG) {
keyword = wrapTagKeyword(keyword);
}
mSearchBar.setText(keyword);
mSearchBar.cursorToEnd();
setSearchBarText(keyword);
}
// Update title
@@ -521,9 +515,7 @@ public final class GalleryListScene extends BaseScene
if (null == title) {
title = resources.getString(R.string.search);
}
if (null != mSearchBar) {
mSearchBar.setTitle(title);
}
setSearchBarHint(title);
// Update nav checked item
int checkedItemId;
@@ -546,8 +538,8 @@ public final class GalleryListScene extends BaseScene
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
public View onCreateViewWithToolbar(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.scene_gallery_list, container, false);
Context context = getContext();
@@ -562,7 +554,6 @@ public final class GalleryListScene extends BaseScene
mRecyclerView = mContentLayout.getRecyclerView();
FastScroller fastScroller = mContentLayout.getFastScroller();
mSearchLayout = (SearchLayout) ViewUtils.$$(mainLayout, R.id.search_layout);
mSearchBar = (SearchBar) ViewUtils.$$(mainLayout, R.id.search_bar);
mFabLayout = (FabLayout) ViewUtils.$$(mainLayout, R.id.fab_layout);
mSearchFab = ViewUtils.$$(mainLayout, R.id.search_fab);
ViewCompat.setWindowInsetsAnimationCallback(view, new WindowInsetsAnimationHelper(
@@ -570,6 +561,8 @@ public final class GalleryListScene extends BaseScene
mFabLayout,
(View) mSearchFab.getParent()
));
((ViewGroup)mFabLayout.getParent()).removeView(mFabLayout);
container.addView(mFabLayout);
int paddingTopSB = resources.getDimensionPixelOffset(R.dimen.gallery_padding_top_search_bar);
int paddingBottomFab = resources.getDimensionPixelOffset(R.dimen.gallery_padding_bottom_fab);
@@ -591,13 +584,13 @@ public final class GalleryListScene extends BaseScene
fastScroller.getPaddingRight(), fastScroller.getPaddingBottom());
mLeftDrawable = new DrawerArrowDrawable(context, ResourcesKt.resolveColor(getTheme(), android.R.attr.colorControlNormal));
mRightDrawable = new AddDeleteDrawable(context, ResourcesKt.resolveColor(getTheme(), android.R.attr.colorControlNormal));
mSearchBar.setLeftDrawable(mLeftDrawable);
mSearchBar.setRightDrawable(mRightDrawable);
mSearchBar.setHelper(this);
mSearchBar.setOnStateChangeListener(this);
setSearchBarHint(mSearchBar);
setSearchBarSuggestionProvider(mSearchBar);
setOnApplySearch((query) -> {
onApplySearch(query);
return null;
});
setNavigationDrawable(mLeftDrawable);
setSearchBarHint();
setSearchBarSuggestionProvider();
mSearchLayout.setHelper(this);
mSearchLayout.setPadding(mSearchLayout.getPaddingLeft(), mSearchLayout.getPaddingTop() + paddingTopSB,
@@ -614,10 +607,6 @@ public final class GalleryListScene extends BaseScene
mActionFabDrawable = new AddDeleteDrawable(context, colorID);
mFabLayout.getPrimaryFab().setImageDrawable(mActionFabDrawable);
mSearchFab.setOnClickListener(this);
mSearchBarMover = new SearchBarMover(this, mSearchBar, mRecyclerView, mSearchLayout);
// Update list url builder
onUpdateUrlBuilder();
@@ -639,10 +628,6 @@ public final class GalleryListScene extends BaseScene
public void onDestroyView() {
super.onDestroyView();
if (null != mSearchBarMover) {
mSearchBarMover.cancelAnimation();
mSearchBarMover = null;
}
if (null != mHelper) {
mHelper.destroy();
if (1 == mHelper.getShownViewIndex()) {
@@ -654,17 +639,15 @@ public final class GalleryListScene extends BaseScene
mRecyclerView = null;
}
if (null != mFabLayout) {
((ViewGroup)mFabLayout.getParent()).removeView(mFabLayout);
removeAboveSnackView(mFabLayout);
mFabLayout = null;
}
mAdapter = null;
mSearchLayout = null;
mSearchBar = null;
mSearchFab = null;
mViewTransition = null;
mLeftDrawable = null;
mRightDrawable = null;
mActionFabDrawable = null;
}
@@ -793,6 +776,8 @@ public final class GalleryListScene extends BaseScene
if (null != mFabLayout && mFabLayout.isExpanded()) {
return false;
}
if (isSearchViewShown())
return false;
switch (mState) {
case STATE_NORMAL:
return getStackIndex() == 0;
@@ -806,6 +791,10 @@ public final class GalleryListScene extends BaseScene
@Override
public void onBackPressed() {
if (isSearchViewShown()) {
hideSearchView();
return;
}
if (null != mFabLayout && mFabLayout.isExpanded()) {
mFabLayout.setExpanded(false);
return;
@@ -846,14 +835,6 @@ public final class GalleryListScene extends BaseScene
return true;
}
@Override
public void onClick(View v) {
if (STATE_NORMAL != mState && null != mSearchBar) {
mSearchBar.applySearch();
hideSoftInput();
}
}
@Override
public void onClickPrimaryFab(FabLayout view, FloatingActionButton fab) {
if (STATE_NORMAL == mState) {
@@ -1157,8 +1138,7 @@ public final class GalleryListScene extends BaseScene
}
private void setState(@State int state, boolean animation) {
if (null == mSearchBar || null == mSearchBarMover ||
null == mViewTransition || null == mSearchLayout) {
if (null == mViewTransition || null == mSearchLayout) {
return;
}
@@ -1166,71 +1146,49 @@ public final class GalleryListScene extends BaseScene
int oldState = mState;
mState = state;
showSearchBar();
onStateChange(state);
switch (oldState) {
case STATE_NORMAL:
if (state == STATE_SIMPLE_SEARCH) {
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMover.returnSearchBarPosition();
selectSearchFab(animation);
} else if (state == STATE_SEARCH) {
mViewTransition.showView(1, animation);
mSearchLayout.scrollSearchContainerToTop();
mSearchBar.setState(SearchBar.STATE_SEARCH, animation);
mSearchBarMover.returnSearchBarPosition();
selectSearchFab(animation);
} else if (state == STATE_SEARCH_SHOW_LIST) {
mViewTransition.showView(1, animation);
mSearchLayout.scrollSearchContainerToTop();
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMover.returnSearchBarPosition();
selectSearchFab(animation);
}
break;
case STATE_SIMPLE_SEARCH:
if (state == STATE_NORMAL) {
mSearchBar.setState(SearchBar.STATE_NORMAL, animation);
mSearchBarMover.returnSearchBarPosition();
selectActionFab(animation);
} else if (state == STATE_SEARCH) {
mViewTransition.showView(1, animation);
mSearchLayout.scrollSearchContainerToTop();
mSearchBar.setState(SearchBar.STATE_SEARCH, animation);
mSearchBarMover.returnSearchBarPosition();
} else if (state == STATE_SEARCH_SHOW_LIST) {
mViewTransition.showView(1, animation);
mSearchLayout.scrollSearchContainerToTop();
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMover.returnSearchBarPosition();
}
break;
case STATE_SEARCH:
if (state == STATE_NORMAL) {
mViewTransition.showView(0, animation);
mSearchBar.setState(SearchBar.STATE_NORMAL, animation);
mSearchBarMover.returnSearchBarPosition();
selectActionFab(animation);
} else if (state == STATE_SIMPLE_SEARCH) {
mViewTransition.showView(0, animation);
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMover.returnSearchBarPosition();
} else if (state == STATE_SEARCH_SHOW_LIST) {
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMover.returnSearchBarPosition();
}
break;
case STATE_SEARCH_SHOW_LIST:
if (state == STATE_NORMAL) {
mViewTransition.showView(0, animation);
mSearchBar.setState(SearchBar.STATE_NORMAL, animation);
mSearchBarMover.returnSearchBarPosition();
selectActionFab(animation);
} else if (state == STATE_SIMPLE_SEARCH) {
mViewTransition.showView(0, animation);
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMover.returnSearchBarPosition();
} else if (state == STATE_SEARCH) {
mSearchBar.setState(SearchBar.STATE_SEARCH, animation);
mSearchBarMover.returnSearchBarPosition();
}
break;
}
@@ -1238,7 +1196,6 @@ public final class GalleryListScene extends BaseScene
((StageActivity) requireActivity()).updateBackPressCallBackStatus();
}
@Override
public void onClickTitle() {
if (mState == STATE_NORMAL) {
setState(STATE_SIMPLE_SEARCH);
@@ -1246,44 +1203,15 @@ public final class GalleryListScene extends BaseScene
}
@Override
public void onClickLeftIcon() {
if (null == mSearchBar) {
return;
}
if (mSearchBar.getState() == SearchBar.STATE_NORMAL) {
toggleDrawer(Gravity.LEFT);
public boolean onMenuItemClick(@NonNull MenuItem item) {
if (mState == STATE_NORMAL) {
setState(STATE_SEARCH);
} else {
setState(STATE_NORMAL);
}
return true;
}
@Override
public void onClickRightIcon() {
if (null == mSearchBar) {
return;
}
if (mSearchBar.getState() == SearchBar.STATE_NORMAL) {
setState(STATE_SEARCH);
} else {
if (mSearchBar.getEditText().length() == 0) {
setState(STATE_NORMAL);
} else {
// Clear
mSearchBar.setText("");
}
}
}
@Override
public void onSearchEditTextClick() {
if (mState == STATE_SEARCH) {
setState(STATE_SEARCH_SHOW_LIST);
}
}
@Override
public void onApplySearch(String query) {
if (null == mUrlBuilder || null == mHelper || null == mSearchLayout) {
return;
@@ -1311,12 +1239,6 @@ public final class GalleryListScene extends BaseScene
setState(STATE_NORMAL);
}
@Override
public void onSearchEditTextBackPressed() {
onBackPressed();
}
@Override
public void onReceiveContent(Uri uri) {
if (mSearchLayout == null || uri == null) {
return;
@@ -1336,38 +1258,10 @@ public final class GalleryListScene extends BaseScene
public void onEndDragHandler() {
// Restore right drawer
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
if (null != mSearchBarMover) {
mSearchBarMover.returnSearchBarPosition();
}
showSearchBar();
}
@Override
public void onStateChange(SearchBar searchBar, int newState, int oldState, boolean animation) {
if (null == mLeftDrawable || null == mRightDrawable) {
return;
}
switch (oldState) {
default:
case SearchBar.STATE_NORMAL:
mLeftDrawable.setArrow(animation ? ANIMATE_TIME : 0);
mRightDrawable.setDelete(animation ? ANIMATE_TIME : 0);
break;
case SearchBar.STATE_SEARCH:
if (newState == SearchBar.STATE_NORMAL) {
mLeftDrawable.setMenu(animation ? ANIMATE_TIME : 0);
mRightDrawable.setAdd(animation ? ANIMATE_TIME : 0);
}
break;
case SearchBar.STATE_SEARCH_LIST:
if (newState == STATE_NORMAL) {
mLeftDrawable.setMenu(animation ? ANIMATE_TIME : 0);
mRightDrawable.setAdd(animation ? ANIMATE_TIME : 0);
}
break;
}
public void onStateChange(int newState) {
if (newState == STATE_NORMAL || newState == STATE_SIMPLE_SEARCH) {
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.LEFT);
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
@@ -1379,9 +1273,7 @@ public final class GalleryListScene extends BaseScene
@Override
public void onChangeSearchMode() {
if (null != mSearchBarMover) {
mSearchBarMover.showSearchBar();
}
showSearchBar();
}
@Override
@@ -1396,32 +1288,9 @@ public final class GalleryListScene extends BaseScene
}
}
// SearchBarMover.Helper
@Override
public boolean isValidView(RecyclerView recyclerView) {
return (mState == STATE_NORMAL && recyclerView == mRecyclerView) ||
(mState == STATE_SEARCH && recyclerView == mSearchLayout);
}
// SearchBarMover.Helper
@Override
public RecyclerView getValidRecyclerView() {
if (mState == STATE_NORMAL || mState == STATE_SIMPLE_SEARCH) {
return mRecyclerView;
} else {
return mSearchLayout;
}
}
// SearchBarMover.Helper
@Override
public boolean forceShowSearchBar() {
return mState == STATE_SIMPLE_SEARCH || mState == STATE_SEARCH_SHOW_LIST;
}
private void onGetGalleryListSuccess(GalleryListParser.Result result, int taskId) {
if (mHelper != null && mSearchBarMover != null &&
mHelper.isCurrentTask(taskId)) {
if (mHelper != null && mHelper.isCurrentTask(taskId)) {
String emptyString = getString(mUrlBuilder.getMode() == ListUrlBuilder.MODE_SUBSCRIPTION && result.noWatchedTags
? R.string.gallery_list_empty_hit_subscription
: R.string.gallery_list_empty_hit);
@@ -1440,8 +1309,7 @@ public final class GalleryListScene extends BaseScene
}
private void onGetGalleryListFailure(Exception e, int taskId) {
if (mHelper != null && mSearchBarMover != null &&
mHelper.isCurrentTask(taskId)) {
if (mHelper != null && mHelper.isCurrentTask(taskId)) {
mHelper.onGetException(taskId, e);
}
}
@@ -1651,7 +1519,7 @@ public final class GalleryListScene extends BaseScene
}
}
private abstract class UrlSuggestion extends SearchBar.Suggestion {
private abstract class UrlSuggestion extends Suggestion {
@Override
public CharSequence getText(TextView textView) {
if (textView.getId() == android.R.id.text1) {
@@ -1835,9 +1703,7 @@ public final class GalleryListScene extends BaseScene
@Override
public void onShowView(View hiddenView, View shownView) {
if (null != mSearchBarMover) {
mSearchBarMover.showSearchBar();
}
showSearchBar();
showActionFab();
}
@@ -1847,11 +1713,9 @@ public final class GalleryListScene extends BaseScene
}
@Override
protected void onScrollToPosition(int postion) {
if (0 == postion) {
if (null != mSearchBarMover) {
mSearchBarMover.showSearchBar();
}
protected void onScrollToPosition(int position) {
if (0 == position) {
showSearchBar();
showActionFab();
}
}
@@ -0,0 +1,294 @@
package com.hippo.ehviewer.ui.scene
import android.annotation.SuppressLint
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.text.TextUtils
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.search.SearchBar
import com.google.android.material.search.SearchView
import com.hippo.app.BaseDialogBuilder
import com.hippo.ehviewer.R
import com.hippo.ehviewer.client.EhTagDatabase
import com.hippo.ehviewer.widget.SearchDatabase
import com.hippo.scene.StageActivity
abstract class SearchBarScene : ToolbarScene() {
private var mSearchView: SearchView? = null
private var mRecyclerView: RecyclerView? = null
private var mSuggestionList: List<Suggestion>? = null
private var mSuggestionAdapter: SuggestionAdapter? = null
private var mSuggestionProvider: SuggestionProvider? = null
private var mAllowEmptySearch = true
private val mSearchDatabase by lazy { SearchDatabase.getInstance(context) }
private var onApplySearch: (String) -> Unit = {}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
val view =
inflater.inflate(R.layout.scene_searchbar, container, false) as ViewGroup
mToolbar = view.findViewById(R.id.toolbar)
mAppBarLayout = view.findViewById(R.id.appbar)
mSearchView = view.findViewById(R.id.searchview)
mRecyclerView = view.findViewById(R.id.search_bar_list)
mSearchView?.editText?.addTextChangedListener {
updateSuggestions()
}
mSearchView?.editText?.setOnEditorActionListener { _, _, _ ->
(mToolbar as SearchBar).text = mSearchView!!.text
mSearchView?.hide()
onApplySearch()
false
}
mSuggestionList = ArrayList()
mSuggestionAdapter = SuggestionAdapter(LayoutInflater.from(context))
mRecyclerView?.adapter = mSuggestionAdapter
val layoutManager = LinearLayoutManager(context)
mRecyclerView?.layoutManager = layoutManager
mSearchView?.addTransitionListener { _, _, _ ->
(requireActivity() as StageActivity).updateBackPressCallBackStatus()
updateSuggestions()
}
val contentView = onCreateViewWithToolbar(inflater, view, savedInstanceState)
return view.apply { addView(contentView, 0) }
}
fun setSearchBarHint(hint: String?) {
(mToolbar as? SearchBar)?.hint = hint
}
fun setSearchBarText(text: String?) {
(mToolbar as? SearchBar)?.text = text
}
fun setEditTextHint(hint: String?) {
mSearchView?.editText?.hint = hint
}
fun setEditTextText(text: String?) {
mSearchView?.setText(text)
}
fun setNavigationDrawable(drawable: Drawable?) {
(mToolbar as? SearchBar)?.navigationIcon = drawable
}
override fun onNavigationClick() {
toggleDrawer(Gravity.START)
}
fun setOnApplySearch(lambda: (String) -> Unit) {
onApplySearch = lambda
}
private fun onApplySearch() {
val query = mSearchView?.text.toString().trim()
if (!mAllowEmptySearch && query.isEmpty()) {
return
}
mSearchDatabase.addQuery(query)
onApplySearch(query)
}
fun setAllowEmptySearch(allowEmptySearch: Boolean) {
mAllowEmptySearch = allowEmptySearch
}
fun isSearchViewShown(): Boolean {
return mSearchView?.isShowing ?: false
}
fun hideSearchView() {
mSearchView?.hide()
}
private fun wrapTagKeyword(keyword: String): String {
var keyword = keyword
keyword = keyword.trim { it <= ' ' }
val index1 = keyword.indexOf(':')
if (index1 == -1 || index1 >= keyword.length - 1) {
// Can't find :, or : is the last char
return keyword
}
if (keyword[index1 + 1] == '"') {
// The char after : is ", the word must be quoted
return keyword
}
val index2 = keyword.indexOf(' ')
return if (index2 <= index1) {
// Can't find space, or space is before :
keyword
} else keyword.substring(0, index1 + 1) + "\"" + keyword.substring(index1 + 1) + "$\""
}
interface SuggestionProvider {
fun providerSuggestions(text: String): List<Suggestion>?
}
abstract class Suggestion {
abstract fun getText(textView: TextView): CharSequence?
abstract fun onClick()
open fun onLongClick(): Boolean {
return false
}
}
private class SuggestionHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val text1 = itemView.findViewById(android.R.id.text1) as TextView
val text2 = itemView.findViewById(android.R.id.text2) as TextView
}
private inner class SuggestionAdapter constructor(private val mInflater: LayoutInflater) :
RecyclerView.Adapter<SuggestionHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SuggestionHolder {
return SuggestionHolder(mInflater.inflate(R.layout.item_simple_list_2, parent, false))
}
override fun onBindViewHolder(holder: SuggestionHolder, position: Int) {
val suggestion = mSuggestionList?.get(position)
val text1 = suggestion?.getText(holder.text1)
val text2 = suggestion?.getText(holder.text2)
holder.text1.text = text1
if (text2 == null) {
holder.text2.visibility = View.GONE
holder.text2.text = ""
} else {
holder.text2.visibility = View.VISIBLE
holder.text2.text = text2
}
holder.itemView.setOnClickListener {
mSuggestionList?.run {
if (position < size) {
this[position].onClick()
}
}
}
holder.itemView.setOnLongClickListener {
mSuggestionList?.run {
if (position < size) {
return@setOnLongClickListener this[position].onLongClick()
}
}
return@setOnLongClickListener false
}
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemCount(): Int {
return mSuggestionList?.size ?: 0
}
}
inner class TagSuggestion constructor(var mHint: String, var mKeyword: String) : Suggestion() {
override fun getText(textView: TextView): CharSequence {
return if (textView.id == android.R.id.text1) {
mKeyword
} else {
mHint
}
}
override fun onClick() {
val edittext = mSearchView?.editText
edittext?.let {
val text = it.text.toString()
var temp = wrapTagKeyword(mKeyword) + " "
if (text.contains(" ")) {
temp = text.substring(0, text.lastIndexOf(" ")) + " " + temp
}
it.setText(temp)
it.setSelection(text.length)
}
}
}
inner class KeywordSuggestion constructor(private val mKeyword: String) : Suggestion() {
override fun getText(textView: TextView): CharSequence? {
return if (textView.id == android.R.id.text1) {
mKeyword
} else {
null
}
}
override fun onClick() {
mSearchView?.editText?.run {
setText(mKeyword)
setSelection(length())
}
}
override fun onLongClick(): Boolean {
BaseDialogBuilder(requireContext())
.setMessage(requireContext().getString(R.string.delete_search_history, mKeyword))
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.delete) { _, _ ->
mSearchDatabase.deleteQuery(mKeyword)
updateSuggestions(false)
}
.show()
return true
}
}
@SuppressLint("NotifyDataSetChanged")
private fun updateSuggestions(scrollToTop: Boolean = true) {
val suggestions = arrayListOf<Suggestion>()
val text = mSearchView?.editText?.text?.toString() ?: return
mSuggestionProvider?.run {
providerSuggestions(text)?.let { suggestions.addAll(it) }
}
mSearchDatabase.getSuggestions(text, 128).forEach { suggestions.add(KeywordSuggestion(it)) }
val ehTagDatabase = EhTagDatabase.getInstance(context)
if (!TextUtils.isEmpty(text) && ehTagDatabase != null && !text.endsWith(" ")) {
val s = text.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (s.isNotEmpty()) {
val keyword = s[s.size - 1]
val searchHints = ehTagDatabase.suggest(keyword)
for (searchHint in searchHints) {
suggestions.add(TagSuggestion(searchHint.first, searchHint.second))
}
}
}
mSuggestionList = suggestions
mSuggestionAdapter?.notifyDataSetChanged()
if (scrollToTop) {
mRecyclerView?.scrollToPosition(0)
}
}
fun setSuggestionProvider(suggestionProvider: SuggestionProvider) {
mSuggestionProvider = suggestionProvider
}
fun showSearchBar() {
mAppBarLayout?.setExpanded(true)
}
}
+2 -10
View File
@@ -19,7 +19,8 @@
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
android:paddingTop="16dp"
app:layout_behavior="@string/searchbar_scrolling_view_behavior">
<com.hippo.widget.ContentLayout
android:id="@+id/content_layout"
@@ -33,15 +34,6 @@
android:layout_height="match_parent"
app:layout_fitsSystemWindowsInsets="top|bottom" />
<com.hippo.ehviewer.widget.SearchBar
android:id="@+id/search_bar"
style="@style/Widget.Material3.CardView.Filled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/gallery_search_bar_margin_h"
android:layout_marginVertical="@dimen/gallery_search_bar_margin_v"
app:layout_fitsSystemWindowsInsets="top|bottom" />
<com.hippo.widget.FabLayout
android:id="@+id/fab_layout"
android:layout_width="match_parent"
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.search.SearchBar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.search.SearchView
android:id="@+id/searchview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_anchor="@id/toolbar"
app:useDrawerArrowDrawable="true">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_bar_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.search.SearchView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -0,0 +1,11 @@
<?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_search_more"
android:icon="@drawable/v_plus_dark_x24"
android:title=""
app:showAsAction="always"/>
</menu>