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 86895d699..8b6b236e5 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 @@ -70,7 +70,9 @@ import com.hippo.ehviewer.client.EhUtils; import com.hippo.ehviewer.client.data.GalleryInfo; import com.hippo.ehviewer.client.data.ListUrlBuilder; import com.hippo.ehviewer.client.exception.EhException; +import com.hippo.ehviewer.client.parser.GalleryDetailUrlParser; import com.hippo.ehviewer.client.parser.GalleryListParser; +import com.hippo.ehviewer.client.parser.GalleryPageUrlParser; import com.hippo.ehviewer.dao.QuickSearch; import com.hippo.ehviewer.ui.CommonOperations; import com.hippo.ehviewer.ui.MainActivity; @@ -95,6 +97,7 @@ import com.hippo.yorozuya.ViewUtils; import java.io.File; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collections; import java.util.List; import junit.framework.Assert; @@ -315,6 +318,92 @@ public final class GalleryListScene extends BaseScene searchBar.setEditTextHint(ssb); } + private void setSearchBarSuggestionProvider(SearchBar searchBar) { + searchBar.setSuggestionProvider(text -> { + GalleryDetailUrlParser.Result result1 = GalleryDetailUrlParser.parse(text, false); + if (result1 != null) { + return Collections.singletonList(new GalleryDetailUrlSuggestion(result1.gid, result1.token)); + } + GalleryPageUrlParser.Result result2 = GalleryPageUrlParser.parse(text, false); + if (result2 != null) { + return Collections.singletonList(new GalleryPageUrlSuggestion(result2.gid, result2.pToken, result2.page)); + } + return null; + }); + } + + private abstract class UrlSuggestion extends SearchBar.Suggestion { + @Override + public CharSequence getText(float textSize) { + Drawable bookImage = DrawableManager.getDrawable(getContext2(), R.drawable.v_book_open_x24); + SpannableStringBuilder ssb = new SpannableStringBuilder(" "); + ssb.append(getResources2().getString(R.string.gallery_list_search_bar_open_gallery)); + int imageSize = (int) (textSize * 1.25); + if (bookImage != null) { + bookImage.setBounds(0, 0, imageSize, imageSize); + ssb.setSpan(new ImageSpan(bookImage), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + return ssb; + } + + @Override + public void onClick() { + startScene(createAnnouncer()); + + if (mState == STATE_SIMPLE_SEARCH) { + setState(STATE_NORMAL); + } else if (mState == STATE_SEARCH_SHOW_LIST) { + setState(STATE_SEARCH); + } + } + + public abstract Announcer createAnnouncer(); + + @Override + public void onLongClick() { } + } + + private class GalleryDetailUrlSuggestion extends UrlSuggestion { + private long mGid; + private String mToken; + + private GalleryDetailUrlSuggestion(long gid, String token) { + mGid = gid; + mToken = token; + } + + @Override + public Announcer createAnnouncer() { + Bundle args = new Bundle(); + args.putString(GalleryDetailScene.KEY_ACTION, GalleryDetailScene.ACTION_GID_TOKEN); + args.putLong(GalleryDetailScene.KEY_GID, mGid); + args.putString(GalleryDetailScene.KEY_TOKEN, mToken); + return new Announcer(GalleryDetailScene.class).setArgs(args); + } + } + + private class GalleryPageUrlSuggestion extends UrlSuggestion { + private long mGid; + private String mPToken; + private int mPage; + + private GalleryPageUrlSuggestion(long gid, String pToken, int page) { + mGid = gid; + mPToken = pToken; + mPage = page; + } + + @Override + public Announcer createAnnouncer() { + Bundle args = new Bundle(); + args.putString(ProgressScene.KEY_ACTION, ProgressScene.ACTION_GALLERY_TOKEN); + args.putLong(ProgressScene.KEY_GID, mGid); + args.putString(ProgressScene.KEY_PTOKEN, mPToken); + args.putInt(ProgressScene.KEY_PAGE, mPage); + return new Announcer(ProgressScene.class).setArgs(args); + } + } + @Nullable private static String getSuitableTitleForUrlBuilder( Resources resources, ListUrlBuilder urlBuilder, boolean appName) { @@ -432,6 +521,7 @@ public final class GalleryListScene extends BaseScene mSearchBar.setHelper(this); mSearchBar.setOnStateChangeListener(this); setSearchBarHint(context, mSearchBar); + setSearchBarSuggestionProvider(mSearchBar); mSearchLayout.setHelper(this); mSearchLayout.setPadding(mSearchLayout.getPaddingLeft(), mSearchLayout.getPaddingTop() + paddingTopSB, diff --git a/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java b/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java index 6162fcdca..32d0023a4 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java @@ -38,7 +38,7 @@ import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; -import android.widget.ArrayAdapter; +import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; @@ -49,7 +49,6 @@ import com.hippo.yorozuya.MathUtils; import com.hippo.yorozuya.SimpleAnimatorListener; import com.hippo.yorozuya.ViewUtils; import java.util.ArrayList; -import java.util.Collections; import java.util.List; public class SearchBar extends CardView implements View.OnClickListener, @@ -84,11 +83,12 @@ public class SearchBar extends CardView implements View.OnClickListener, private ViewTransition mViewTransition; private SearchDatabase mSearchDatabase; - private List mSuggestionList; - private ArrayAdapter mSuggestionAdapter; + private List mSuggestionList; + private SuggestionAdapter mSuggestionAdapter; private Helper mHelper; private OnStateChangeListener mOnStateChangeListener; + private SuggestionProvider mSuggestionProvider; private boolean mAllowEmptySearch = true; @@ -136,22 +136,18 @@ public class SearchBar extends CardView implements View.OnClickListener, mBaseHeight = getMeasuredHeight(); mSuggestionList = new ArrayList<>(); - mSuggestionAdapter = new ArrayAdapter<>(getContext(), R.layout.item_simple_list, mSuggestionList); + mSuggestionAdapter = new SuggestionAdapter(LayoutInflater.from(getContext())); mListView.setAdapter(mSuggestionAdapter); mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - String suggestion = mSuggestionList.get(MathUtils.clamp(position, 0, mSuggestionList.size() - 1)); - mEditText.setText(suggestion); - mEditText.setSelection(mEditText.getText().length()); + mSuggestionList.get(position).onClick(); } }); mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { - String suggestion = mSuggestionList.get(MathUtils.clamp(position, 0, mSuggestionList.size() - 1)); - mSearchDatabase.deleteQuery(suggestion); - updateSuggestions(); + mSuggestionList.get(position).onLongClick(); return true; } }); @@ -166,10 +162,22 @@ public class SearchBar extends CardView implements View.OnClickListener, } private void updateSuggestions() { - String prefix = mEditText.getText().toString(); - String[] suggestions = mSearchDatabase.getSuggestions(prefix, 128); mSuggestionList.clear(); - Collections.addAll(mSuggestionList, suggestions); + + String text = mEditText.getText().toString(); + + if (mSuggestionProvider != null) { + List suggestions = mSuggestionProvider.providerSuggestions(text); + if (suggestions != null && !suggestions.isEmpty()) { + mSuggestionList.addAll(suggestions); + } + } + + String[] keywords = mSearchDatabase.getSuggestions(text, 128); + for (String keyword : keywords) { + mSuggestionList.add(new KeywordSuggestion(keyword)); + } + if (mSuggestionList.size() == 0) { removeListHeader(); } else { @@ -199,6 +207,10 @@ public class SearchBar extends CardView implements View.OnClickListener, mOnStateChangeListener = listener; } + public void setSuggestionProvider(SuggestionProvider suggestionProvider) { + mSuggestionProvider = suggestionProvider; + } + public void setText(String text) { mEditText.setText(text); } @@ -488,4 +500,82 @@ public class SearchBar extends CardView implements View.OnClickListener, void onStateChange(SearchBar searchBar, int newState, int oldState, boolean animation); } + + public interface SuggestionProvider { + + List providerSuggestions(String text); + } + + private class SuggestionAdapter extends BaseAdapter { + + private LayoutInflater mInflater; + + private SuggestionAdapter(LayoutInflater inflater) { + mInflater = inflater; + } + + @Override + public int getCount() { + return mSuggestionList.size(); + } + + @Override + public Object getItem(int position) { + return mSuggestionList.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TextView textView; + if (convertView == null) { + textView = (TextView) mInflater.inflate(R.layout.item_simple_list, parent, false); + } else { + textView = (TextView) convertView; + } + + textView.setText(mSuggestionList.get(position).getText(textView.getTextSize())); + + return textView; + } + } + + public abstract static class Suggestion { + + public abstract CharSequence getText(float textSize); + + public abstract void onClick(); + + public abstract void onLongClick(); + } + + public class KeywordSuggestion extends Suggestion { + + private String mKeyword; + + private KeywordSuggestion(String keyword) { + mKeyword = keyword; + } + + @Override + public CharSequence getText(float textSize) { + return mKeyword; + } + + @Override + public void onClick() { + mEditText.setText(mKeyword); + mEditText.setSelection(mEditText.getText().length()); + } + + @Override + public void onLongClick() { + mSearchDatabase.deleteQuery(mKeyword); + updateSuggestions(); + } + } } diff --git a/app/src/main/res/drawable/v_book_open_x24.xml b/app/src/main/res/drawable/v_book_open_x24.xml new file mode 100644 index 000000000..9fa1fdaed --- /dev/null +++ b/app/src/main/res/drawable/v_book_open_x24.xml @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 2fa404ab4..c5045fd3b 100755 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -125,6 +125,7 @@ 搜索 ExHentai 搜索 E-Hentai + 打开画廊 什么都没有找到 搜索 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 511f1303a..648be971b 100755 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -125,6 +125,7 @@ 搜索 ExHentai 搜索 E-Hentai + 打開畫廊 什麼都沒有找到 搜索 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 880a76aeb..379869d44 100755 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -125,6 +125,7 @@ 在 ExHentai 中搜尋 在 E-Hentai 搜尋 + 開啟圖庫 什麼都沒找到 搜尋 diff --git a/app/src/main/res/values/pathdata.xml b/app/src/main/res/values/pathdata.xml index 80daad068..ad1ffc187 100644 --- a/app/src/main/res/values/pathdata.xml +++ b/app/src/main/res/values/pathdata.xml @@ -30,7 +30,7 @@ M11.71,19C9.93,19 8.5,17.59 8.5,15.86C8.5,14.24 9.53,13.1 11.3,12.74C13.07,12.38 14.9,11.53 15.92,10.16C16.31,11.45 16.5,12.81 16.5,14.2C16.5,16.84 14.36,19 11.71,19M13.5,0.67C13.5,0.67 14.24,3.32 14.24,5.47C14.24,7.53 12.89,9.2 10.83,9.2C8.76,9.2 7.2,7.53 7.2,5.47L7.23,5.1C5.21,7.5 4,10.61 4,14A8,8 0 0,0 12,22A8,8 0 0,0 20,14C20,8.6 17.41,3.8 13.5,0.67Z M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z M11,7V12.11L15.71,14.9L16.5,13.62L12.5,11.25V7M12.5,2C8.97,2 5.91,3.92 4.27,6.77L2,4.5V11H8.5L5.75,8.25C6.96,5.73 9.5,4 12.5,4A7.5,7.5 0 0,1 20,11.5A7.5,7.5 0 0,1 12.5,19C9.23,19 6.47,16.91 5.44,14H3.34C4.44,18.03 8.11,21 12.5,21C17.74,21 22,16.75 22,11.5A9.5,9.5 0 0,0 12.5,2Z - M21,5C19.89,4.65 18.67,4.5 17.5,4.5C15.55,4.5 13.45,4.9 12,6C10.55,4.9 8.45,4.5 6.5,4.5C4.55,4.5 2.45,4.9 1,6V20.65C1,20.9 1.25,21.15 1.5,21.15C1.6,21.15 1.65,21.1 1.75,21.1C3.1,20.45 5.05,20 6.5,20C8.45,20 10.55,20.4 12,21.5C13.35,20.65 15.8,20 17.5,20C19.15,20 20.85,20.3 22.25,21.05C22.35,21.1 22.4,21.1 22.5,21.1C22.75,21.1 23,20.85 23,20.6V6C22.4,5.55 21.75,5.25 21,5M21,18.5C19.9,18.15 18.7,18 17.5,18C15.8,18 13.35,18.65 12,19.5V8C13.35,7.15 15.8,6.5 17.5,6.5C18.7,6.5 19.9,6.65 21,7V18.5Z + M21,4C19.9,3.7,18.7,3.5,17.5,3.5C15.6,3.5,13.4,3.9,12,5C10.6,3.9,8.4,3.5,6.5,3.5S2.5,3.9,1,5V19.6C1,19.9,1.3,20.1,1.5,20.1C1.6,20.1,1.6,20.1,1.8,20.1C3.1,19.5,5.1,19,6.5,19C8.4,19,10.6,19.4,12,20.5C13.4,19.6,15.8,19,17.5,19C19.1,19,20.9,19.3,22.3,20C22.4,20.1,22.4,20.1,22.6,20.1C22.9,20.1,23.1,19.8,23.1,19.6V5C22.4,4.6,21.8,4.3,21,4M21,17.5C19.9,17.1,18.7,17,17.5,17C15.8,17,13.4,17.6,12,18.5V7C13.4,6.2,15.8,5.5,17.5,5.5C18.7,5.5,19.9,5.7,21,6V17.5Z M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z M13,13L17,17L21,13H18V10C18,6.1,14.9,3,11,3S4,6.1,4,10V17H6V10C6,7.2,8.2,5,11,5S16,7.2,16,10V13H13M,16,19H18V21H16V19Z M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cdce4e925..9c76896a6 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -167,6 +167,7 @@ Search ExHentai Search E-Hentai + Open the gallery The World is Big and the panda sit alone Search