diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhUtils.java b/app/src/main/java/com/hippo/ehviewer/client/EhUtils.java index 7385b4681..9d25dc569 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhUtils.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhUtils.java @@ -127,16 +127,4 @@ public class EhUtils { return TextUtils.isEmpty(gi.title) ? gi.titleJpn : gi.title; } } - - public static String getSpecifyUploaderKeyword(String query) { - return "uploader:\"" + query + "\""; - } - - public static String getKeywordFromSpecifyUploader(String query) { - if (query.startsWith("uploader:\"") && query.endsWith("\"") && query.length() > 11) { - return query.substring(10, query.length() - 1); - } else { - return query; - } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java b/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java index cbeb8c860..7e419a881 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java @@ -294,8 +294,7 @@ public class ListUrlBuilder implements Cloneable, Parcelable { public String build() { switch (mMode) { default: - case MODE_NORMAL: - case MODE_UPLOADER: { + case MODE_NORMAL: { boolean filter = false; UrlBuilder ub = new UrlBuilder(EhUrl.HOST_EX); if (mCategory != EhUtils.NONE) { @@ -348,6 +347,19 @@ public class ListUrlBuilder implements Cloneable, Parcelable { } return ub.build(); } + case MODE_UPLOADER: { + StringBuilder sb = new StringBuilder(EhUrl.HOST_EX); + sb.append("uploader/"); + try { + sb.append(URLEncoder.encode(mKeyword, "UTF-8")); + } catch (UnsupportedEncodingException e) { + // Empty + } + if (mPageIndex != 0) { + sb.append('/').append(mPageIndex); + } + return sb.toString(); + } case MODE_TAG: { StringBuilder sb = new StringBuilder(EhUrl.HOST_EX); sb.append("tag/"); @@ -356,7 +368,9 @@ public class ListUrlBuilder implements Cloneable, Parcelable { } catch (UnsupportedEncodingException e) { // Empty } - sb.append('/').append(mPageIndex); + if (mPageIndex != 0) { + sb.append('/').append(mPageIndex); + } return sb.toString(); } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListUrlParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListUrlParser.java index 1d1ba7b42..70d4fc998 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListUrlParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListUrlParser.java @@ -19,7 +19,6 @@ package com.hippo.ehviewer.client.parser; import android.text.TextUtils; import com.hippo.ehviewer.client.EhUrl; -import com.hippo.ehviewer.client.EhUtils; import com.hippo.ehviewer.client.data.ListUrlBuilder; import com.hippo.yorozuya.Utilities; @@ -100,7 +99,7 @@ public final class GalleryListUrlParser { ListUrlBuilder builder = new ListUrlBuilder(); builder.setMode(ListUrlBuilder.MODE_UPLOADER); - builder.setKeyword(EhUtils.getSpecifyUploaderKeyword(uploader)); + builder.setKeyword(uploader); return builder; } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java index 7e421cb76..f75de8022 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java @@ -855,7 +855,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene } ListUrlBuilder lub = new ListUrlBuilder(); lub.setMode(ListUrlBuilder.MODE_UPLOADER); - lub.setKeyword(EhUtils.getSpecifyUploaderKeyword(uploader)); + lub.setKeyword(uploader); GalleryListScene.startScene(this, lub); } else if (mCategory == v) { int category = getCategory(); 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 c6bae7d14..6b5fdea59 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 @@ -609,10 +609,37 @@ public final class GalleryListScene extends BaseScene } } + + @Override public void onApplySearch(String query) { if (mState == STATE_SEARCH || mState == STATE_SEARCH_SHOW_LIST) { - mSearchLayout.formatListUrlBuilder(mUrlBuilder, query); + if (mSearchLayout.isSpecifyGallery()) { + int index = query.indexOf(' '); + if (index <= 0 || index >= query.length() - 1) { + Toast.makeText(getContext(), "Error", Toast.LENGTH_SHORT).show(); // TODO + return; + } + + int gid; + String token; + try { + gid = Integer.parseInt(query.substring(0, index)); + } catch (NumberFormatException e) { + Toast.makeText(getContext(), "Error", Toast.LENGTH_SHORT).show(); // TODO + return; + } + token = query.substring(index + 1); + + Bundle args = new Bundle(); + args.putString(GalleryDetailScene.KEY_ACTION, GalleryDetailScene.ACTION_GID_TOKEN); + args.putInt(GalleryDetailScene.KEY_GID, gid); + args.putString(GalleryDetailScene.KEY_TOKEN, token); + startScene(new Announcer(GalleryDetailScene.class).setArgs(args)); + return; + } else { + mSearchLayout.formatListUrlBuilder(mUrlBuilder, query); + } } else { mUrlBuilder.reset(); mUrlBuilder.setKeyword(query); diff --git a/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java b/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java index 4ce7c0247..72fbe8eec 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java @@ -34,14 +34,13 @@ import android.view.View; import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.FrameLayout; -import android.widget.RadioGroup; import android.widget.TextView; import com.hippo.easyrecyclerview.EasyRecyclerView; import com.hippo.ehviewer.R; -import com.hippo.ehviewer.client.EhUtils; import com.hippo.ehviewer.client.data.ListUrlBuilder; import com.hippo.rippleold.RippleSalon; +import com.hippo.widget.RadioGridGroup; import com.hippo.widget.SimpleImageView; import com.hippo.yorozuya.ViewUtils; @@ -86,7 +85,7 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC private View mNormalView; private CategoryTable mCategoryTable; - private RadioGroup mNormalSearchMode; + private RadioGridGroup mNormalSearchMode; private SimpleImageView mNormalSearchModeHelp; private View mSearchTagHelp; private SwitchCompat mEnableAdvanceSwitch; @@ -133,7 +132,7 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC View normalView = mInflater.inflate(R.layout.search_normal, frameLayout, false); mNormalView = normalView; mCategoryTable = (CategoryTable) normalView.findViewById(R.id.search_category_table); - mNormalSearchMode = (RadioGroup) normalView.findViewById(R.id.normal_search_mode); + mNormalSearchMode = (RadioGridGroup) normalView.findViewById(R.id.normal_search_mode); mNormalSearchModeHelp = (SimpleImageView) normalView.findViewById(R.id.normal_search_mode_help); mEnableAdvanceSwitch = (SwitchCompat) normalView.findViewById(R.id.search_enable_advance); Drawable helpDrawable = com.hippo.vector.VectorDrawable.create(context, R.xml.ic_help_circle_x24); @@ -214,6 +213,10 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC } } + public boolean isSpecifyGallery() { + return R.id.search_specify_gallery == mNormalSearchMode.getCheckedRadioButtonId(); + } + // TODO image search public void formatListUrlBuilder(ListUrlBuilder urlBuilder, String query) { urlBuilder.reset(); @@ -222,23 +225,22 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC switch (nsMode) { default: case R.id.search_normal_search: + urlBuilder.setMode(ListUrlBuilder.MODE_NORMAL); + break; case R.id.search_specify_uploader: - if (nsMode == R.id.search_specify_uploader) { - urlBuilder.setKeyword(EhUtils.getSpecifyUploaderKeyword(query)); - } else { - urlBuilder.setKeyword(query); - } - urlBuilder.setCategory(mCategoryTable.getCategory()); - if (mEnableAdvance) { - urlBuilder.setAdvanceSearch(mTableAdvanceSearch.getAdvanceSearch()); - urlBuilder.setMinRating(mTableAdvanceSearch.getMinRating()); - } + urlBuilder.setMode(ListUrlBuilder.MODE_UPLOADER); break; case R.id.search_specify_tag: urlBuilder.setMode(ListUrlBuilder.MODE_TAG); - urlBuilder.setKeyword(query); break; } + + urlBuilder.setKeyword(query); + urlBuilder.setCategory(mCategoryTable.getCategory()); + if (mEnableAdvance) { + urlBuilder.setAdvanceSearch(mTableAdvanceSearch.getAdvanceSearch()); + urlBuilder.setMinRating(mTableAdvanceSearch.getMinRating()); + } } public void setSearchMode(@SearchMode int searchMode, boolean animation) { diff --git a/app/src/main/java/com/hippo/widget/RadioGridGroup.java b/app/src/main/java/com/hippo/widget/RadioGridGroup.java new file mode 100644 index 000000000..7ca2d05a3 --- /dev/null +++ b/app/src/main/java/com/hippo/widget/RadioGridGroup.java @@ -0,0 +1,266 @@ +/* + * 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.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.IdRes; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.RadioButton; + +import com.hippo.yorozuya.ViewUtils; + +public class RadioGridGroup extends SimpleGridLayout { + + private static final int[] RADIO_ATTRS = new int[] { + android.R.attr.checkedButton + }; + + // holds the checked id; the selection is empty by default + private int mCheckedId = -1; + // tracks children radio buttons checked state + private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener; + // when true, mOnCheckedChangeListener discards events + private boolean mProtectFromCheckedChange = false; + private OnCheckedChangeListener mOnCheckedChangeListener; + private PassThroughHierarchyChangeListener mPassThroughListener; + + public RadioGridGroup(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public RadioGridGroup(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + final TypedArray a = context.obtainStyledAttributes(attrs, RADIO_ATTRS); + int value = a.getResourceId(0, View.NO_ID); + if (value != View.NO_ID) { + mCheckedId = value; + } + a.recycle(); + + mChildOnCheckedChangeListener = new CheckedStateTracker(); + mPassThroughListener = new PassThroughHierarchyChangeListener(); + super.setOnHierarchyChangeListener(mPassThroughListener); + } + + + /** + * {@inheritDoc} + */ + @Override + public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) { + // the user listener is delegated to our pass-through listener + mPassThroughListener.mOnHierarchyChangeListener = listener; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + // checks the appropriate radio button as requested in the XML file + if (mCheckedId != -1) { + mProtectFromCheckedChange = true; + setCheckedStateForView(mCheckedId, true); + mProtectFromCheckedChange = false; + setCheckedId(mCheckedId); + } + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (child instanceof RadioButton) { + final RadioButton button = (RadioButton) child; + if (button.isChecked()) { + mProtectFromCheckedChange = true; + if (mCheckedId != -1) { + setCheckedStateForView(mCheckedId, false); + } + mProtectFromCheckedChange = false; + setCheckedId(button.getId()); + } + } + + super.addView(child, index, params); + } + + /** + *

Sets the selection to the radio button whose identifier is passed in + * parameter. Using -1 as the selection identifier clears the selection; + * such an operation is equivalent to invoking {@link #clearCheck()}.

+ * + * @param id the unique id of the radio button to select in this group + * + * @see #getCheckedRadioButtonId() + * @see #clearCheck() + */ + public void check(@IdRes int id) { + // don't even bother + if (id != -1 && (id == mCheckedId)) { + return; + } + + if (mCheckedId != -1) { + setCheckedStateForView(mCheckedId, false); + } + + if (id != -1) { + setCheckedStateForView(id, true); + } + + setCheckedId(id); + } + + private void setCheckedId(@IdRes int id) { + mCheckedId = id; + if (mOnCheckedChangeListener != null) { + mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId); + } + } + + private void setCheckedStateForView(int viewId, boolean checked) { + View checkedView = findViewById(viewId); + if (checkedView != null && checkedView instanceof RadioButton) { + ((RadioButton) checkedView).setChecked(checked); + } + } + + /** + *

Returns the identifier of the selected radio button in this group. + * Upon empty selection, the returned value is -1.

+ * + * @return the unique id of the selected radio button in this group + * + * @see #check(int) + * @see #clearCheck() + */ + @IdRes + public int getCheckedRadioButtonId() { + return mCheckedId; + } + + /** + *

Clears the selection. When the selection is cleared, no radio button + * in this group is selected and {@link #getCheckedRadioButtonId()} returns + * null.

+ * + * @see #check(int) + * @see #getCheckedRadioButtonId() + */ + public void clearCheck() { + check(-1); + } + + /** + *

Register a callback to be invoked when the checked radio button + * changes in this group.

+ * + * @param listener the callback to call on checked state change + */ + public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { + mOnCheckedChangeListener = listener; + } + + /** + *

Interface definition for a callback to be invoked when the checked + * radio button changed in this group.

+ */ + public interface OnCheckedChangeListener { + /** + *

Called when the checked radio button has changed. When the + * selection is cleared, checkedId is -1.

+ * + * @param group the group in which the checked radio button has changed + * @param checkedId the unique identifier of the newly checked radio button + */ + void onCheckedChanged(RadioGridGroup group, @IdRes int checkedId); + } + + private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + // prevents from infinite recursion + if (mProtectFromCheckedChange) { + return; + } + + mProtectFromCheckedChange = true; + if (mCheckedId != -1) { + setCheckedStateForView(mCheckedId, false); + } + mProtectFromCheckedChange = false; + + int id = buttonView.getId(); + setCheckedId(id); + } + } + + /** + *

A pass-through listener acts upon the events and dispatches them + * to another listener. This allows the table layout to set its own internal + * hierarchy change listener without preventing the user to setup his.

+ */ + private class PassThroughHierarchyChangeListener implements + ViewGroup.OnHierarchyChangeListener { + private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener; + + /** + * {@inheritDoc} + */ + @Override + public void onChildViewAdded(View parent, View child) { + if (parent == RadioGridGroup.this && child instanceof RadioButton) { + int id = child.getId(); + // generates an id if it's missing + if (id == View.NO_ID) { + id = ViewUtils.generateViewId(); + child.setId(id); + } + ((RadioButton) child).setOnCheckedChangeListener( + mChildOnCheckedChangeListener); + } + + if (mOnHierarchyChangeListener != null) { + mOnHierarchyChangeListener.onChildViewAdded(parent, child); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onChildViewRemoved(View parent, View child) { + if (parent == RadioGridGroup.this && child instanceof RadioButton) { + ((RadioButton) child).setOnCheckedChangeListener(null); + } + + if (mOnHierarchyChangeListener != null) { + mOnHierarchyChangeListener.onChildViewRemoved(parent, child); + } + } + } +} diff --git a/app/src/main/java/com/hippo/widget/SimpleGridLayout.java b/app/src/main/java/com/hippo/widget/SimpleGridLayout.java index 849822448..490ea0b35 100644 --- a/app/src/main/java/com/hippo/widget/SimpleGridLayout.java +++ b/app/src/main/java/com/hippo/widget/SimpleGridLayout.java @@ -57,7 +57,7 @@ public class SimpleGridLayout extends ViewGroup { init(context, attrs); } - public void init(Context context, AttributeSet attrs) { + private void init(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SimpleGridLayout); mColumnCount = a.getInteger(R.styleable.SimpleGridLayout_columnCount, DEFAULT_COLUMN_COUNT); mItemMargin = a.getDimensionPixelOffset(R.styleable.SimpleGridLayout_itemMargin, 0); diff --git a/app/src/main/res/layout/search_normal.xml b/app/src/main/res/layout/search_normal.xml index f86fc4f22..f2eee6304 100644 --- a/app/src/main/res/layout/search_normal.xml +++ b/app/src/main/res/layout/search_normal.xml @@ -26,54 +26,64 @@ android:layout_height="wrap_content" android:layout_marginBottom="@dimen/search_item_interval"/> + + + + + + + + + + + + - - - - - - - - - - + android:layout_width="36dp" + android:layout_height="36dp" + android:padding="6dp"/> + + + + - - - diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index db83468b5..3b78c7742 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -65,6 +65,7 @@ 一般搜索 指定上传者 指定标签 + 指定画廊 启用高级选项 搜索画廊 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5ecdfed85..f404293e3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -104,6 +104,7 @@ Normal search Specify uploader Specify tag + Specify gallery Enable advance options Search Gallery diff --git a/yorozuya b/yorozuya index c1b5f4cb2..fe276a68f 160000 --- a/yorozuya +++ b/yorozuya @@ -1 +1 @@ -Subproject commit c1b5f4cb2e86282165843f0c180a5e07ac16cedf +Subproject commit fe276a68f183cb3639fdd77a0f46e5a5b6fafccd