diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 10bc5c51f..3031b4f2a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -87,6 +87,12 @@
android:theme="@style/AppTheme.NoActionBar"
android:label="@string/license"/>
+
+
) params[0]);
case METHOD_GET_GALLERY_DETAIL:
diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java
index bef4e8666..65b269020 100644
--- a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java
+++ b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java
@@ -81,6 +81,12 @@ public class EhEngine {
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+ public static EhFilter sEhFilter;
+
+ public static void initialize() {
+ sEhFilter = EhFilter.getInstance();
+ }
+
private static void saveParseErrorBody(ParseException e) {
File dir = AppConfig.getExternalParseErrorDir();
if (null == dir) {
@@ -171,7 +177,7 @@ public class EhEngine {
}
public static GalleryListParser.Result getGalleryList(EhClient.Task task, OkHttpClient okHttpClient,
- String url, boolean callApi) throws Exception {
+ String url) throws Exception {
Log.d(TAG, url);
Request request = new EhRequestBuilder(url, task.getEhConfig()).build();
Call call = okHttpClient.newCall(request);
@@ -194,8 +200,30 @@ public class EhEngine {
throw e;
}
- if (callApi && result.galleryInfos.size() > 0) {
- fillGalleryListByApi(task, okHttpClient, result.galleryInfos);
+ // Filter title and uploader
+ List list = result.galleryInfos;
+ for (int i = 0, n = list.size(); i < n; i++) {
+ GalleryInfo info = list.get(i);
+ if (!sEhFilter.filterTitle(info) || !sEhFilter.filterUploader(info)) {
+ list.remove(i);
+ i--;
+ n--;
+ }
+ }
+
+ if (list.size() > 0 && (Settings.getShowJpnTitle() || sEhFilter.needCallApi())) {
+ // Fill by api
+ fillGalleryListByApi(task, okHttpClient, list);
+
+ // Filter tag
+ for (int i = 0, n = list.size(); i < n; i++) {
+ GalleryInfo info = list.get(i);
+ if (!sEhFilter.filterTag(info)) {
+ list.remove(i);
+ i--;
+ n--;
+ }
+ }
}
return result;
@@ -576,19 +604,45 @@ public class EhEngine {
String body = null;
Headers headers = null;
+ List list;
int code = -1;
try {
Response response = call.execute();
code = response.code();
headers = response.headers();
body = response.body().string();
- List list = WhatsHotParser.parse(body);
- fillGalleryListByApi(task, okHttpClient, list);
- return list;
+ list = WhatsHotParser.parse(body);
} catch (Exception e) {
throwException(call, code, headers, body, e);
throw e;
}
+
+ // Filter title
+ for (int i = 0, n = list.size(); i < n; i++) {
+ GalleryInfo info = list.get(i);
+ if (!sEhFilter.filterTitle(info)) {
+ list.remove(i);
+ i--;
+ n--;
+ }
+ }
+
+ if (list.size() > 0) {
+ // Fill by api
+ fillGalleryListByApi(task, okHttpClient, list);
+
+ // Filter uploader and tag
+ for (int i = 0, n = list.size(); i < n; i++) {
+ GalleryInfo info = list.get(i);
+ if (!sEhFilter.filterUploader(info) || !sEhFilter.filterTag(info)) {
+ list.remove(i);
+ i--;
+ n--;
+ }
+ }
+ }
+
+ return list;
}
private static ProfileParser.Result getProfileInternal(EhClient.Task task,
diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhFilter.java b/app/src/main/java/com/hippo/ehviewer/client/EhFilter.java
new file mode 100644
index 000000000..3b5a73da3
--- /dev/null
+++ b/app/src/main/java/com/hippo/ehviewer/client/EhFilter.java
@@ -0,0 +1,223 @@
+/*
+ * 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.client;
+
+import android.util.Log;
+
+import com.hippo.ehviewer.EhDB;
+import com.hippo.ehviewer.client.data.GalleryInfo;
+import com.hippo.ehviewer.dao.Filter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class EhFilter {
+
+ private static final String TAG = EhFilter.class.getSimpleName();
+
+ public static final int MODE_TITLE = 0;
+ public static final int MODE_UPLOADER = 1;
+ public static final int MODE_TAG = 2;
+
+ private final List mTitleFilterList = new ArrayList<>();
+ private final List mUploaderFilterList = new ArrayList<>();
+ private final List mTagFilterList = new ArrayList<>();
+
+ private static EhFilter sInstance;
+
+ public static EhFilter getInstance() {
+ if (sInstance == null) {
+ sInstance = new EhFilter();
+ }
+ return sInstance;
+ }
+
+ private EhFilter() {
+ List list = EhDB.getAllFilter();
+ for (int i = 0, n = list.size(); i < n; i++) {
+ Filter filter = list.get(i);
+ switch (filter.mode) {
+ case MODE_TITLE:
+ filter.text = filter.text.toLowerCase();
+ mTitleFilterList.add(filter);
+ break;
+ case MODE_UPLOADER:
+ mUploaderFilterList.add(filter);
+ break;
+ case MODE_TAG:
+ filter.text = filter.text.toLowerCase();
+ mTagFilterList.add(filter);
+ break;
+ default:
+ Log.d(TAG, "Unknown mode: " + filter.mode);
+ break;
+ }
+ }
+ }
+
+ public List getTitleFilterList() {
+ return mTitleFilterList;
+ }
+
+ public List getUploaderFilterList() {
+ return mUploaderFilterList;
+ }
+
+ public List getTagFilterList() {
+ return mTagFilterList;
+ }
+
+ public synchronized void addFilter(Filter filter) {
+ EhDB.addFilter(filter);
+
+ switch (filter.mode) {
+ case MODE_TITLE:
+ filter.text = filter.text.toLowerCase();
+ mTitleFilterList.add(filter);
+ break;
+ case MODE_UPLOADER:
+ mUploaderFilterList.add(filter);
+ break;
+ case MODE_TAG:
+ mTagFilterList.add(filter);
+ break;
+ default:
+ Log.d(TAG, "Unknown mode: " + filter.mode);
+ break;
+ }
+ }
+
+ public synchronized void deleteFilter(Filter filter) {
+ EhDB.deleteFilter(filter);
+
+ switch (filter.mode) {
+ case MODE_TITLE:
+ mTitleFilterList.remove(filter);
+ break;
+ case MODE_UPLOADER:
+ mUploaderFilterList.remove(filter);
+ break;
+ case MODE_TAG:
+ mTagFilterList.remove(filter);
+ break;
+ default:
+ Log.d(TAG, "Unknown mode: " + filter.mode);
+ break;
+ }
+ }
+
+ public synchronized boolean needCallApi() {
+ return 0 != mTagFilterList.size();
+ }
+
+ public synchronized boolean filterTitle(GalleryInfo info) {
+ if (null == info) {
+ return false;
+ }
+
+ // Title
+ String title = info.title;
+ List filters = mTitleFilterList;
+ if (null != title && filters.size() > 0) {
+ title = title.toLowerCase();
+ for (int i = 0, n = filters.size(); i < n; i++) {
+ if (title.contains(filters.get(i).text)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public synchronized boolean filterUploader(GalleryInfo info) {
+ if (null == info) {
+ return false;
+ }
+
+ // Uploader
+ String uploader = info.uploader;
+ List filters = mUploaderFilterList;
+ if (null != uploader && filters.size() > 0) {
+ for (int i = 0, n = filters.size(); i < n; i++) {
+ if (uploader.equals(filters.get(i).text)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private boolean matchTag(String tag, String filter) {
+ if (null == tag || null == filter) {
+ return false;
+ }
+
+ String tagNamespace;
+ String tagName;
+ String filterNamespace;
+ String filterName;
+ int index = tag.indexOf(':');
+ if (index < 0) {
+ tagNamespace = null;
+ tagName = tag;
+ } else {
+ tagNamespace = tag.substring(0, index);
+ tagName = tag.substring(index + 1);
+ }
+ index = filter.indexOf(':');
+ if (index < 0) {
+ filterNamespace = null;
+ filterName = filter;
+ } else {
+ filterNamespace = filter.substring(0, index);
+ filterName = filter.substring(index + 1);
+ }
+
+ if (null != tagNamespace && null != filterNamespace &&
+ !tagNamespace.equals(filterNamespace)) {
+ return false;
+ }
+ if (!tagName.equals(filterName)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public synchronized boolean filterTag(GalleryInfo info) {
+ if (null == info) {
+ return false;
+ }
+
+ // Tag
+ String[] tags = info.simpleTags;
+ List filters = mTagFilterList;
+ if (null != tags && filters.size() > 0) {
+ for (String tag: tags) {
+ for (int i = 0, n = filters.size(); i < n; i++) {
+ if (matchTag(tag, filters.get(i).text)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.java
index c3a24e621..9a321ea06 100644
--- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.java
+++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.java
@@ -20,11 +20,11 @@ import android.support.annotation.NonNull;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.client.data.GalleryInfo;
-import com.hippo.ehviewer.client.exception.NotFoundException;
import com.hippo.ehviewer.client.exception.ParseException;
import com.hippo.yorozuya.NumberUtils;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -52,6 +52,7 @@ public class GalleryListParser {
public List galleryInfos;
}
+ @SuppressWarnings("unchecked")
public static Result parse(@NonNull String body) throws Exception {
Result result = new Result();
Matcher m;
@@ -61,7 +62,8 @@ public class GalleryListParser {
if (m.find()) {
result.pages = ParserUtils.parseInt(m.group(1));
} else if (body.contains("No hits found
")) {
- throw new NotFoundException();
+ result.pages = 0;
+ result.galleryInfos = Collections.EMPTY_LIST;
} else {
// Can not get page number
throw new ParseException("Can't parse gallery list", body);
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/FilterActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/FilterActivity.java
new file mode 100644
index 000000000..bbec39c51
--- /dev/null
+++ b/app/src/main/java/com/hippo/ehviewer/ui/FilterActivity.java
@@ -0,0 +1,426 @@
+/*
+ * 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;
+
+import android.content.DialogInterface;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.design.widget.TextInputLayout;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.hippo.easyrecyclerview.EasyRecyclerView;
+import com.hippo.ehviewer.R;
+import com.hippo.ehviewer.client.EhFilter;
+import com.hippo.ehviewer.dao.Filter;
+import com.hippo.util.DrawableManager;
+import com.hippo.view.ViewTransition;
+import com.hippo.yorozuya.ViewUtils;
+
+import java.util.List;
+
+public class FilterActivity extends ToolbarActivity {
+
+ @Nullable
+ private EasyRecyclerView mRecyclerView;
+ @Nullable
+ private ViewTransition mViewTransition;
+ @Nullable
+ private FilterAdapter mAdapter;
+ @Nullable
+ private FilterList mFilterList;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_filter);
+ setNavigationIcon(R.drawable.v_arrow_left_dark_x24);
+
+ mFilterList = new FilterList();
+
+ mRecyclerView = (EasyRecyclerView) ViewUtils.$$(this, R.id.recycler_view);
+ TextView tip = (TextView) ViewUtils.$$(this, R.id.tip);
+ mViewTransition = new ViewTransition(mRecyclerView, tip);
+
+ Drawable drawable = DrawableManager.getDrawable(this, R.drawable.big_filter);
+ drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ tip.setCompoundDrawables(null, drawable, null, null);
+
+ mAdapter = new FilterAdapter();
+ mRecyclerView.setAdapter(mAdapter);
+ mRecyclerView.setClipToPadding(false);
+ mRecyclerView.setClipChildren(false);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mRecyclerView.hasFixedSize();
+
+ updateView();
+ }
+
+ private void updateView() {
+ if (null == mViewTransition) {
+ return;
+ }
+
+ if (null == mFilterList || 0 == mFilterList.size()) {
+ mViewTransition.showView(1);
+ } else {
+ mViewTransition.showView(0);
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ mRecyclerView = null;
+ mViewTransition = null;
+ mAdapter = null;
+ mFilterList = null;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.activity_filter, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ case R.id.action_add:
+ showAddFilterDialog();
+ return true;
+ case R.id.action_tip:
+ showTipDialog();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void showTipDialog() {
+
+ }
+
+ private void showAddFilterDialog() {
+ AlertDialog dialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.add_filter)
+ .setView(R.layout.dialog_add_filter)
+ .setPositiveButton(R.string.add, null)
+ .show();
+ AddFilterDialogHelper helper = new AddFilterDialogHelper();
+ helper.setDialog(dialog);
+ }
+
+ private void showDeleteFilterDialog(final Filter filter) {
+ int messageId;
+ switch (filter.mode) {
+ case EhFilter.MODE_TITLE:
+ messageId = R.string.delete_title_filter;
+ break;
+ case EhFilter.MODE_UPLOADER:
+ messageId = R.string.delete_uploader_filter;
+ break;
+ case EhFilter.MODE_TAG:
+ messageId = R.string.delete_tag_filter;
+ break;
+ default:
+ messageId = R.string.delete_filter;
+ }
+ String message = getString(messageId, filter.text);
+
+ new AlertDialog.Builder(this)
+ .setMessage(message)
+ .setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (DialogInterface.BUTTON_POSITIVE != which || null == mFilterList) {
+ return;
+ }
+ mFilterList.delete(filter);
+ if (null != mAdapter) {
+ mAdapter.notifyDataSetChanged();
+ }
+ updateView();
+ }
+ }).show();
+ }
+
+ private class AddFilterDialogHelper implements View.OnClickListener {
+
+ @Nullable
+ private AlertDialog mDialog;
+ @Nullable
+ private Spinner mSpinner;
+ @Nullable
+ private TextInputLayout mInputLayout;
+ @Nullable
+ private EditText mEditText;
+
+ public void setDialog(AlertDialog dialog) {
+ mDialog = dialog;
+ mSpinner = (Spinner) ViewUtils.$$(dialog, R.id.spinner);
+ mInputLayout = (TextInputLayout) ViewUtils.$$(dialog, R.id.text_input_layout);
+ mEditText = mInputLayout.getEditText();
+ View button = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
+ if (null != button) {
+ button.setOnClickListener(this);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (null == mFilterList || null == mDialog || null == mSpinner ||
+ null == mInputLayout || null == mEditText) {
+ return;
+ }
+
+ String text = mEditText.getText().toString().trim();
+ if (TextUtils.isEmpty(text)) {
+ mInputLayout.setError(getString(R.string.text_is_empty));
+ return;
+ } else {
+ mInputLayout.setError(null);
+ }
+ int mode = mSpinner.getSelectedItemPosition();
+
+ Filter filter = new Filter();
+ filter.mode = mode;
+ filter.text = text;
+ mFilterList.add(filter);
+
+ if (null != mAdapter) {
+ mAdapter.notifyDataSetChanged();
+ }
+ updateView();
+
+ mDialog.dismiss();
+ mDialog = null;
+ mSpinner = null;
+ mInputLayout = null;
+ mEditText = null;
+ }
+ }
+
+ private class FilterHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+
+ private final TextView text;
+ private final ImageView icon;
+
+ public FilterHolder(View itemView) {
+ super(itemView);
+ text = (TextView) ViewUtils.$$(itemView, R.id.text);
+ icon = (ImageView) itemView.findViewById(R.id.icon);
+
+ if (null != icon) {
+ icon.setOnClickListener(this);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ int position = getAdapterPosition();
+ if (position < 0 || null == mFilterList) {
+ return;
+ }
+ Filter filter = mFilterList.get(position);
+ if (FilterList.MODE_HEADER != filter.mode) {
+ showDeleteFilterDialog(filter);
+ }
+ }
+ }
+
+ private class FilterAdapter extends RecyclerView.Adapter {
+
+ private static final int TYPE_ITEM = 0;
+ private static final int TYPE_HEADER = 1;
+
+ @Override
+ public int getItemViewType(int position) {
+ if (null == mFilterList) {
+ return TYPE_ITEM;
+ }
+
+ if (mFilterList.get(position).mode == FilterList.MODE_HEADER) {
+ return TYPE_HEADER;
+ } else {
+ return TYPE_ITEM;
+ }
+ }
+
+ @Override
+ public FilterHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ int layoutId;
+ switch (viewType) {
+ default:
+ case TYPE_ITEM:
+ layoutId = R.layout.item_filter;
+ break;
+ case TYPE_HEADER:
+ layoutId = R.layout.item_filter_header;
+ break;
+ }
+
+ FilterHolder holder = new FilterHolder(getLayoutInflater().inflate(layoutId, parent, false));
+
+ if (R.layout.item_filter == layoutId) {
+ holder.icon.setImageDrawable(
+ DrawableManager.getDrawable(FilterActivity.this, R.drawable.v_delete_x24));
+ }
+
+ return holder;
+ }
+
+ @Override
+ public void onBindViewHolder(FilterHolder holder, int position) {
+ if (null == mFilterList) {
+ return;
+ }
+
+ Filter filter = mFilterList.get(position);
+ if (FilterList.MODE_HEADER == filter.mode) {
+ holder.text.setText(filter.text);
+ } else {
+ holder.text.setText(filter.text);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return null != mFilterList ? mFilterList.size(): 0;
+ }
+ }
+
+ private class FilterList {
+
+ public static final int MODE_HEADER = -1;
+
+ private final EhFilter mEhFilter;
+ private final List mTitleFilterList;
+ private final List mUploaderFilterList;
+ private final List mTagFilterList;
+
+ private Filter mTitleHeader;
+ private Filter mUploaderHeader;
+ private Filter mTagHeader;
+
+ public FilterList() {
+ mEhFilter = EhFilter.getInstance();
+ mTitleFilterList = mEhFilter.getTitleFilterList();
+ mUploaderFilterList = mEhFilter.getUploaderFilterList();
+ mTagFilterList = mEhFilter.getTagFilterList();
+ }
+
+ public int size() {
+ int count = 0;
+ int size = mTitleFilterList.size();
+ count += 0 == size ? 0 : size + 1;
+ size = mUploaderFilterList.size();
+ count += 0 == size ? 0 : size + 1;
+ size = mTagFilterList.size();
+ count += 0 == size ? 0 : size + 1;
+ return count;
+ }
+
+ private Filter getTitleHeader() {
+ if (null == mTitleHeader) {
+ mTitleHeader = new Filter();
+ mTitleHeader.mode = MODE_HEADER;
+ mTitleHeader.text = getString(R.string.filter_title);
+ }
+ return mTitleHeader;
+ }
+
+ private Filter getUploaderHeader() {
+ if (null == mUploaderHeader) {
+ mUploaderHeader = new Filter();
+ mUploaderHeader.mode = MODE_HEADER;
+ mUploaderHeader.text = getString(R.string.filter_uploader);
+ }
+ return mUploaderHeader;
+ }
+
+ private Filter getTagHeader() {
+ if (null == mTagHeader) {
+ mTagHeader = new Filter();
+ mTagHeader.mode = MODE_HEADER;
+ mTagHeader.text = getString(R.string.filter_tag);
+ }
+ return mTagHeader;
+ }
+
+ public Filter get(int index) {
+ int size = mTitleFilterList.size();
+ if (0 != size) {
+ if (index == 0) {
+ return getTitleHeader();
+ } else if (index <= size) {
+ return mTitleFilterList.get(index - 1);
+ } else {
+ index -= size + 1;
+ }
+ }
+
+ size = mUploaderFilterList.size();
+ if (0 != size) {
+ if (index == 0) {
+ return getUploaderHeader();
+ } else if (index <= size) {
+ return mUploaderFilterList.get(index - 1);
+ } else {
+ index -= size + 1;
+ }
+ }
+
+ size = mTagFilterList.size();
+ if (0 != size) {
+ if (index == 0) {
+ return getTagHeader();
+ } else if (index <= size) {
+ return mTagFilterList.get(index - 1);
+ } else {
+ index -= size + 1;
+ }
+ }
+
+ throw new IndexOutOfBoundsException();
+ }
+
+ public void add(Filter filter) {
+ mEhFilter.addFilter(filter);
+ }
+
+ public void delete(Filter filter) {
+ mEhFilter.deleteFilter(filter);
+ }
+ }
+}
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 79d326521..84a17d479 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
@@ -61,6 +61,7 @@ import com.hippo.ehviewer.R;
import com.hippo.ehviewer.UrlOpener;
import com.hippo.ehviewer.client.EhCacheKeyFactory;
import com.hippo.ehviewer.client.EhClient;
+import com.hippo.ehviewer.client.EhFilter;
import com.hippo.ehviewer.client.EhRequest;
import com.hippo.ehviewer.client.EhUrl;
import com.hippo.ehviewer.client.EhUtils;
@@ -72,6 +73,7 @@ import com.hippo.ehviewer.client.data.LargePreviewSet;
import com.hippo.ehviewer.client.data.ListUrlBuilder;
import com.hippo.ehviewer.client.parser.RateGalleryParser;
import com.hippo.ehviewer.dao.DownloadInfo;
+import com.hippo.ehviewer.dao.Filter;
import com.hippo.ehviewer.ui.CommonOperations;
import com.hippo.ehviewer.ui.GalleryActivity;
import com.hippo.ehviewer.ui.MainActivity;
@@ -421,6 +423,8 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
mDownload.setOnLongClickListener(this);
mRead.setOnClickListener(this);
+ mUploader.setOnLongClickListener(this);
+
mBelowHeader = mainView.findViewById(R.id.below_header);
View belowHeader = mBelowHeader;
@@ -794,6 +798,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
tag.setBackgroundDrawable(new RoundSideRectDrawable(colorTag));
tag.setTag(R.id.tag, tg.groupName + ":" + tagStr);
tag.setOnClickListener(this);
+ tag.setOnLongClickListener(this);
}
}
}
@@ -1096,14 +1101,67 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
}
}
+ private void showBlockUploaderDialog() {
+ final String uploader = getUploader();
+ if (null == uploader) {
+ return;
+ }
+
+ new AlertDialog.Builder(getContext())
+ .setMessage(getString(R.string.block_the_uploader, uploader))
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (DialogInterface.BUTTON_POSITIVE != which) {
+ return;
+ }
+
+ Filter filter = new Filter();
+ filter.mode = EhFilter.MODE_UPLOADER;
+ filter.text = uploader;
+ EhFilter.getInstance().addFilter(filter);
+
+ showTip(getString(R.string.uploader_blocked, uploader), LENGTH_SHORT);
+ }
+ }).show();
+ }
+
+ private void showBlockTagDialog(final String tag) {
+ new AlertDialog.Builder(getContext())
+ .setMessage(getString(R.string.block_the_tag, tag))
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (DialogInterface.BUTTON_POSITIVE != which) {
+ return;
+ }
+
+ Filter filter = new Filter();
+ filter.mode = EhFilter.MODE_TAG;
+ filter.text = tag;
+ EhFilter.getInstance().addFilter(filter);
+
+ showTip(getString(R.string.tag_blocked, tag), LENGTH_SHORT);
+ }
+ }).show();
+ }
+
@Override
public boolean onLongClick(View v) {
- if (mDownload == v) {
+ if (mUploader == v) {
+ showBlockUploaderDialog();
+ } else if (mDownload == v) {
GalleryInfo galleryInfo = getGalleryInfo();
if (galleryInfo != null) {
CommonOperations.startDownload((MainActivity) getActivity(), galleryInfo, true);
}
return true;
+ } else {
+ String tag = (String) v.getTag(R.id.tag);
+ if (null != tag) {
+ showBlockTagDialog(tag);
+ return true;
+ }
}
return false;
}
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 1903d324e..4f6ff05b8 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
@@ -984,7 +984,7 @@ public final class GalleryListScene extends BaseScene
request.setMethod(EhClient.METHOD_GET_GALLERY_LIST);
request.setCallback(new GetGalleryListListener(getContext(),
((StageActivity) getActivity()).getStageId(), getTag(), taskId));
- request.setArgs(url, Settings.getShowJpnTitle());
+ request.setArgs(url);
mClient.execute(request);
}
}
diff --git a/app/src/main/java/com/hippo/widget/ContentLayout.java b/app/src/main/java/com/hippo/widget/ContentLayout.java
index ffce5b09b..d473b7e47 100644
--- a/app/src/main/java/com/hippo/widget/ContentLayout.java
+++ b/app/src/main/java/com/hippo/widget/ContentLayout.java
@@ -412,36 +412,10 @@ public class ContentLayout extends FrameLayout {
notifyItemRangeRemoved(index, 1);
}
- public void onGetEmptyData(int taskId) {
- if (mCurrentTaskId != taskId) {
- return;
- }
-
- switch (mCurrentTaskType) {
- case TYPE_REFRESH:
- case TYPE_SOMEWHERE:
- showText(mEmptyString);
- break;
- case TYPE_NEXT_PAGE:
- case TYPE_NEXT_PAGE_KEEP_POS:
- // Last page ?
- break;
- case TYPE_PRE_PAGE:
- case TYPE_PRE_PAGE_KEEP_POS:
- case TYPE_REFRESH_PAGE:
- // TODO
- break;
- }
-
- mRefreshLayout.setHeaderRefreshing(false);
- mRefreshLayout.setFooterRefreshing(false);
- }
-
public void onGetPageData(int taskId, List data) {
if (mCurrentTaskId == taskId) {
- showContent();
-
int dataSize;
+
switch (mCurrentTaskType) {
case TYPE_REFRESH:
mStartPage = 0;
@@ -449,71 +423,201 @@ public class ContentLayout extends FrameLayout {
mPageDivider.clear();
mPageDivider.add(data.size());
- mData.clear();
- mData.addAll(data);
- notifyDataSetChanged();
+ if (data.isEmpty()) {
+ mData.clear();
+ notifyDataSetChanged();
- mRecyclerView.stopScroll();
- LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), 0, 0);
- onScrollToPosition();
+ if (true || mEndPage >= mPages) { // Not found
+ // Ui change, show empty string
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showEmptyString();
+ } else {
+ // Ui change, show progress bar
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showProgressBar();
+
+ // Get next page
+ mCurrentTaskId = mIdGenerator.nextId();
+ mCurrentTaskType = TYPE_NEXT_PAGE_KEEP_POS;
+ mCurrentTaskPage = mEndPage;
+ getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
+ }
+ } else {
+ mData.clear();
+ mData.addAll(data);
+ notifyDataSetChanged();
+
+ // Ui change, show content
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showContent();
+
+ // RecyclerView scroll
+ mRecyclerView.stopScroll();
+ LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), 0, 0);
+ onScrollToPosition();
+ }
break;
case TYPE_PRE_PAGE:
case TYPE_PRE_PAGE_KEEP_POS:
- mData.addAll(0, data);
- notifyItemRangeInserted(0, data.size());
-
dataSize = data.size();
for (int i = 0, n = mPageDivider.size(); i < n; i++) {
mPageDivider.set(i, mPageDivider.get(i) + dataSize);
}
mPageDivider.add(0, dataSize);
-
mStartPage--;
// assert mStartPage >= 0
- if (mCurrentTaskType == TYPE_PRE_PAGE_KEEP_POS) {
- mRecyclerView.stopScroll();
- LayoutManagerUtils.scrollToPositionProperly(mRecyclerView.getLayoutManager(), getContext(),
- dataSize - 1, mOnScrollToPositionListener);
+
+ if (data.isEmpty()) {
+ if (true || mStartPage <= 0) { // OK, that's all
+ if (mData.isEmpty()) {
+ // Ui change, show empty string
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showEmptyString();
+ } else {
+ // Ui change, show content
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showContent();
+
+ if (mCurrentTaskType == TYPE_PRE_PAGE) {
+ // RecyclerView scroll, to top
+ mRecyclerView.stopScroll();
+ LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), 0, 0);
+ onScrollToPosition();
+ }
+ }
+ } else {
+ // Keep UI
+
+ // Get previous
+ mCurrentTaskId = mIdGenerator.nextId();
+ // Keep mCurrentTaskType
+ mCurrentTaskPage = mStartPage - 1;
+ getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
+ }
} else {
- mRecyclerView.stopScroll();
- LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), 0, 0);
- onScrollToPosition();
+ mData.addAll(0, data);
+ notifyItemRangeInserted(0, data.size());
+
+ // Ui change, show content
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showContent();
+
+ // RecyclerView scroll
+ if (mCurrentTaskType == TYPE_PRE_PAGE_KEEP_POS) {
+ mRecyclerView.stopScroll();
+ LayoutManagerUtils.scrollToPositionProperly(mRecyclerView.getLayoutManager(), getContext(),
+ dataSize - 1, mOnScrollToPositionListener);
+ } else {
+ mRecyclerView.stopScroll();
+ LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), 0, 0);
+ onScrollToPosition();
+ }
}
break;
case TYPE_NEXT_PAGE:
case TYPE_NEXT_PAGE_KEEP_POS:
dataSize = data.size();
int oldDataSize = mData.size();
- mData.addAll(data);
- notifyItemRangeInserted(oldDataSize, dataSize);
-
mPageDivider.add(oldDataSize + dataSize);
-
mEndPage++;
- if (mCurrentTaskType == TYPE_NEXT_PAGE_KEEP_POS) {
- mRecyclerView.stopScroll();
- mRecyclerView.smoothScrollBy(0, mNextPageScrollSize);
- onScrollToPosition();
+
+ if (data.isEmpty()) {
+ if (true || mEndPage >= mPages) { // OK, that's all
+ if (mData.isEmpty()) {
+ // Ui change, show empty string
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showEmptyString();
+ } else {
+ // Ui change, show content
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showContent();
+
+ if (mCurrentTaskType == TYPE_NEXT_PAGE) {
+ // RecyclerView scroll
+ mRecyclerView.stopScroll();
+ LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), oldDataSize, 0);
+ onScrollToPosition();
+ }
+ }
+ } else {
+ // Keep UI
+
+ // Get next page
+ mCurrentTaskId = mIdGenerator.nextId();
+ // Keep mCurrentTaskType
+ mCurrentTaskPage = mEndPage;
+ getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
+ }
} else {
- mRecyclerView.stopScroll();
- LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), oldDataSize, 0);
- onScrollToPosition();
+ mData.addAll(data);
+ notifyItemRangeInserted(oldDataSize, dataSize);
+
+ // Ui change, show content
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showContent();
+
+ if (mCurrentTaskType == TYPE_NEXT_PAGE_KEEP_POS) {
+ mRecyclerView.stopScroll();
+ mRecyclerView.smoothScrollBy(0, mNextPageScrollSize);
+ onScrollToPosition();
+ } else {
+ mRecyclerView.stopScroll();
+ LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), oldDataSize, 0);
+ onScrollToPosition();
+ }
}
break;
case TYPE_SOMEWHERE:
- mData.clear();
- mData.addAll(data);
- notifyDataSetChanged();
-
mStartPage = mCurrentTaskPage;
mEndPage = mCurrentTaskPage + 1;
-
mPageDivider.clear();
mPageDivider.add(data.size());
- mRecyclerView.stopScroll();
- LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), 0, 0);
- onScrollToPosition();
+ if (data.isEmpty()) {
+ mData.clear();
+ notifyDataSetChanged();
+
+ if (true || mEndPage >= mPages) { // Not found
+ // Ui change, show empty string
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showEmptyString();
+ } else {
+ // Ui change, show progress bar
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showProgressBar();
+
+ // Get next page
+ mCurrentTaskId = mIdGenerator.nextId();
+ mCurrentTaskType = TYPE_NEXT_PAGE_KEEP_POS;
+ mCurrentTaskPage = mEndPage;
+ getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
+ }
+ } else {
+ mData.clear();
+ mData.addAll(data);
+ notifyDataSetChanged();
+
+ // Ui change, show content
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showContent();
+
+ // RecyclerView scroll
+ mRecyclerView.stopScroll();
+ LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), 0, 0);
+ onScrollToPosition();
+ }
break;
case TYPE_REFRESH_PAGE:
if (mCurrentTaskPage < mStartPage || mCurrentTaskPage >= mEndPage) {
@@ -534,17 +638,27 @@ public class ContentLayout extends FrameLayout {
mPageDivider.set(i, mPageDivider.get(i) - oldIndexEnd + newIndexEnd);
}
- if (newIndexEnd > oldIndexEnd && newIndexEnd > 0) {
- mRecyclerView.stopScroll();
- LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), newIndexEnd - 1, 0);
- onScrollToPosition();
+ if (mData.isEmpty()) {
+ // Ui change, show empty string
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showEmptyString();
+ } else {
+ // Ui change, show content
+ mRefreshLayout.setHeaderRefreshing(false);
+ mRefreshLayout.setFooterRefreshing(false);
+ showContent();
+
+ // RecyclerView scroll
+ if (newIndexEnd > oldIndexEnd && newIndexEnd > 0) {
+ mRecyclerView.stopScroll();
+ LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), newIndexEnd - 1, 0);
+ onScrollToPosition();
+ }
}
break;
}
}
-
- mRefreshLayout.setHeaderRefreshing(false);
- mRefreshLayout.setFooterRefreshing(false);
}
public void onGetException(int taskId, Exception e) {
diff --git a/app/src/main/res/drawable/big_filter.xml b/app/src/main/res/drawable/big_filter.xml
new file mode 100644
index 000000000..87dd72398
--- /dev/null
+++ b/app/src/main/res/drawable/big_filter.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/v_delete_x24.xml b/app/src/main/res/drawable/v_delete_x24.xml
new file mode 100644
index 000000000..e8544990b
--- /dev/null
+++ b/app/src/main/res/drawable/v_delete_x24.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/v_info_outline_dark_x24.xml b/app/src/main/res/drawable/v_info_outline_dark_x24.xml
new file mode 100644
index 000000000..4b6d0876f
--- /dev/null
+++ b/app/src/main/res/drawable/v_info_outline_dark_x24.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_filter.xml b/app/src/main/res/layout/activity_filter.xml
new file mode 100644
index 000000000..6c24e2451
--- /dev/null
+++ b/app/src/main/res/layout/activity_filter.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_add_filter.xml b/app/src/main/res/layout/dialog_add_filter.xml
new file mode 100644
index 000000000..1ab987bba
--- /dev/null
+++ b/app/src/main/res/layout/dialog_add_filter.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_filter.xml b/app/src/main/res/layout/item_filter.xml
new file mode 100644
index 000000000..4c6ba2635
--- /dev/null
+++ b/app/src/main/res/layout/item_filter.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_filter_header.xml b/app/src/main/res/layout/item_filter_header.xml
new file mode 100644
index 000000000..2701463e4
--- /dev/null
+++ b/app/src/main/res/layout/item_filter_header.xml
@@ -0,0 +1,26 @@
+
+
+
+
diff --git a/app/src/main/res/menu/activity_filter.xml b/app/src/main/res/menu/activity_filter.xml
new file mode 100644
index 000000000..62e09095d
--- /dev/null
+++ b/app/src/main/res/menu/activity_filter.xml
@@ -0,0 +1,34 @@
+
+
+
+
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 787983368..bda65eb5e 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -170,6 +170,10 @@
移除收藏
添加收藏失败
移除收藏失败
+ 屏蔽上传者:%s?
+ 屏蔽标签:%s?
+ 已屏蔽上传者 \"%s\"
+ 已屏蔽标签 \"%s\"
根本把持不住
@@ -267,10 +271,24 @@
重命名标签
删除标签
删除“%s”?
+ 提示
+ 添加过滤项
+ 过滤项文本
这里是阅读历史
+
+ 过滤
+ 这里是过滤列表
+ 标题
+ 上传者
+ 标签
+ 删除标题过滤项:%s?
+ 删除上传者过滤项:%s?
+ 删除标签过滤项:%s?
+ 删除过滤项:%s?
+
第 %d 页
@string/refresh
@@ -301,6 +319,8 @@
显示日文标题
尝试显示日文标题。这会使获取画廊列表花费更多时间
显示默认标题。一般是英文标题
+ 过滤
+ 过滤标题,上传者和标签
阅读
阅读方向
diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml
index c31681eb9..7890d0b7f 100644
--- a/app/src/main/res/values-zh-rHK/strings.xml
+++ b/app/src/main/res/values-zh-rHK/strings.xml
@@ -170,6 +170,10 @@
移除收藏
添加收藏失敗
移除收藏失敗
+ 屏蔽上傳者:%s?
+ 屏蔽標籤:%s?
+ 已屏蔽上傳者 \"%s\"
+ 已屏蔽標籤 \"%s\"
根本把持不住
@@ -267,10 +271,24 @@
重命名標籤
刪除標籤
刪除“%s”?
+ 提示
+ 添加過濾項
+ 過濾項文本
這裏是閲讀歷史
+
+ 過濾
+ 這裏是過濾列表
+ 標題
+ 上傳者
+ 標籤
+ 刪除標題過濾項:%s?
+ 刪除上傳者過濾項:%s?
+ 刪除標籤過濾項:%s?
+ 刪除過濾項:%s?
+
第 %d 頁
@string/refresh
@@ -301,6 +319,8 @@
顯示日文標題
嘗試顯示日文標題。這會使獲取畫廊列表花費更多時間
顯示默認標題。一般是英文標題
+ 過濾
+ 過濾標題,上傳者和標籤
閲讀
閲讀方向
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 67fd43e9e..761ae57a1 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -170,6 +170,10 @@
移除收藏
新增收藏失敗
移除收藏失敗
+ 遮蔽上傳者:%s?
+ 遮蔽標籤:%s?
+ 已遮蔽上傳者 \"%s\"
+ 已遮蔽標籤 \"%s\"
根本把持不住
@@ -267,10 +271,24 @@
重新命名標籤
刪除標籤
刪除“%s”?
+ 提示
+ 新增過濾項
+ 過濾項文字
這裡是閱讀歷史
+
+ 過濾
+ 這裡是過濾列表
+ 標題
+ 上傳者
+ 標籤
+ 刪除標題過濾項:%s?
+ 刪除上傳者過濾項:%s?
+ 刪除標籤過濾項:%s?
+ 刪除過濾項:%s?
+
第 %d 頁
@string/refresh
@@ -301,6 +319,8 @@
顯示日文標題
嘗試顯示日文標題。這會使獲取畫廊列表花費更多時間
顯示預設標題。一般是英文標題
+ 過濾
+ 過濾標題,上傳者和標籤
閱讀
閱讀方向
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 370094fcd..1e4fc64f7 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -132,4 +132,10 @@
- @string/add_to_favourites
+
+ - @string/filter_title
+ - @string/filter_uploader
+ - @string/filter_tag
+
+
diff --git a/app/src/main/res/values/pathdata.xml b/app/src/main/res/values/pathdata.xml
index 1b9c8733e..2e604a72a 100644
--- a/app/src/main/res/values/pathdata.xml
+++ b/app/src/main/res/values/pathdata.xml
@@ -55,12 +55,14 @@
M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A9,9 0 0,0 21,12C21,11.5 20.96,11 20.87,10.5C20.6,10 20,10 20,10H18V9C18,8 17,8 17,8H15V7C15,6 14,6 14,6H13V4C13,3 12,3 12,3M9.5,6A1.5,1.5 0 0,1 11,7.5A1.5,1.5 0 0,1 9.5,9A1.5,1.5 0 0,1 8,7.5A1.5,1.5 0 0,1 9.5,6M6.5,10A1.5,1.5 0 0,1 8,11.5A1.5,1.5 0 0,1 6.5,13A1.5,1.5 0 0,1 5,11.5A1.5,1.5 0 0,1 6.5,10M11.5,11A1.5,1.5 0 0,1 13,12.5A1.5,1.5 0 0,1 11.5,14A1.5,1.5 0 0,1 10,12.5A1.5,1.5 0 0,1 11.5,11M16.5,13A1.5,1.5 0 0,1 18,14.5A1.5,1.5 0 0,1 16.5,16H16.5A1.5,1.5 0 0,1 15,14.5H15A1.5,1.5 0 0,1 16.5,13M11,16A1.5,1.5 0 0,1 12.5,17.5A1.5,1.5 0 0,1 11,19A1.5,1.5 0 0,1 9.5,17.5A1.5,1.5 0 0,1 11,16Z
M14,12H10V10H14M14,16H10V14H14M20,8H17.19C16.74,7.22 16.12,6.55 15.37,6.04L17,4.41L15.59,3L13.42,5.17C12.96,5.06 12.5,5 12,5C11.5,5 11.04,5.06 10.59,5.17L8.41,3L7,4.41L8.62,6.04C7.88,6.55 7.26,7.22 6.81,8H4V10H6.09C6.04,10.33 6,10.66 6,11V12H4V14H6V15C6,15.34 6.04,15.67 6.09,16H4V18H6.81C7.85,19.79 9.78,21 12,21C14.22,21 16.15,19.79 17.19,18H20V16H17.91C17.96,15.67 18,15.34 18,15V14H20V12H18V11C18,10.66 17.96,10.33 17.91,10H20V8Z
M15,9A1,1 0 0,1 14,8A1,1 0 0,1 15,7A1,1 0 0,1 16,8A1,1 0 0,1 15,9M9,9A1,1 0 0,1 8,8A1,1 0 0,1 9,7A1,1 0 0,1 10,8A1,1 0 0,1 9,9M16.12,4.37L18.22,2.27L17.4,1.44L15.09,3.75C14.16,3.28 13.11,3 12,3C10.88,3 9.84,3.28 8.91,3.75L6.6,1.44L5.78,2.27L7.88,4.37C6.14,5.64 5,7.68 5,10V11H19V10C19,7.68 17.86,5.64 16.12,4.37M5,16C5,19.86 8.13,23 12,23A7,7 0 0,0 19,16V12H5V16Z
+ M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z
M12.88,2H4V11L14.97,22.14L24,13.1L12.88,2ZM7.26,5.26C7.96,4.58,9.06,4.58,9.74,5.26C10.42,5.96,10.42,7.06,9.74,7.74S7.94,8.42,7.26,7.74S6.58,5.94,7.26,5.26ZM14.98,19.3L8.76,12.95L14.88,6.83L21.18,13.11L14.98,19.31ZM11.6,21.56L10.23,23L0,12.74V5H2V11.9L11.6,21.56Z
M20.46,14.65C21.86,16.15,20.31,18.6,18.32,17.87C19.78,17.57,20.79,16.37,20.46,14.65ZM4.32,17.87C6.32,18.6,7.86,16.17,6.46,14.65C6.79,16.38,5.78,17.55,4.32,17.87ZM16.87,4.3C16.6,3.56,15.87,3,15,3C13.7,3,12.7,4.24,13.1,5.58C14.2,5.18,15.44,4.48,16.87,4.31ZM24,16C24,18.76,21.76,21,19,21C14.96,21,14.84,16.88,12,16.88S9.04,21,5,21C2.24,21,0,18.76,0,16C0,13.87,1.53,11.43,3.94,7.18C5.84,3.81,8.69,5.92,10.76,6.58C11.54,6.85,12.41,6.88,13.24,6.58C15.3,5.92,18.14,3.81,20.06,7.18C22.46,11.43,24,13.88,24,16ZM8,16C8,14.35,6.65,13,5,13S2,14.35,2,16S3.35,19,5,19S8,17.65,8,16ZM13,14C13,13.45,12.55,13,12,13S11,13.45,11,14S11.45,15,12,15S13,14.55,13,14ZM22,16C22,14.35,20.65,13,19,13S16,14.35,16,16S17.35,19,19,19S22,17.65,22,16ZM9,3C8.14,3,7.4,3.55,7.13,4.3C8.56,4.48,9.79,5.2,10.9,5.58C11.3,4.24,10.3,3,9,3Z
M5.5,2H22V0H5C3.3,0,2,1.3,2,3V21C2,22.7,3.3,24,5,24H22V4H5.5C4.1,4,4.1,2,5.5,2ZM20,6V22H6V6H20M12.5,11.8V14.4L14.9,15.8L15.3,15.2L13.3,14V11.9M13.3,9.3C11.5,9.3,10,10.3,9.2,11.7L8,10.5V13.8H11.3L9.9,12.4C10.5,11.1,11.8,10.3,13.3,10.3C15.4,10.3,17.1,12,17.1,14.1S15.4,17.9,13.3,17.9C11.7,17.9,10.3,16.9,9.8,15.4H8.7C9.3,17.4,11.1,18.9,13.3,18.9C15.9,18.9,18.1,16.8,18.1,14.1C18,11.4,15.9,9.3,13.3,9.3Z
M0,19V24H24V19H0ZM16,22H15V21H16V22ZM19,22H18V21H19V22ZM22,22H21V21H22V22M15,11L15,0L9,0L9,11L5,11L12,18L19,11Z
M12.9,21.3C11.3,19.4,10.5,18.3,8.6,17.1C7.5,16.4,6.6,15.4,5.9,14.4L6.4,13.9L6.4,13.9C8.9,15.9,11.2,16.2,15.8,16.2C15.8,16.2,17,17.4,18.2,18.8C21.1,22,16.1,25,12.9,21.3ZM10.9,21.9C5.9,21.4,2,17.1,2,12C2,6.5,6.5,2,12,2S22,6.5,22,12C22,14.2,21.3,16.2,20.1,17.8C20.6,18.4,20.9,19.1,21.1,19.8C22.9,17.7,24,15,24,12C24,5.4,18.6,0,12,0S0,5.4,0,12S5.4,24,12,24C12.3,24,12.6,24,12.9,24C12,23.3,11.6,22.8,10.9,21.9M8.5,10.7L7.2,12L6,10.8L7.3,9.5L6,8.2L7.2,7L8.5,8.3L9.8,7L11,8.2L9.7,9.5L11,10.8L9.8,12C9.8,12,8.5,10.7,8.5,10.7M15.5,6C13.6,6,12,7.6,12,9.5S13.6,13,15.5,13S19,11.4,19,9.5S17.4,6,15.5,6ZM15.5,11C14.7,11,14,10.3,14,9.5S14.7,8,15.5,8S17,8.7,17,9.5S16.3,11,15.5,11Z
+ M22.9,2.84C24,0.44,15.08,0,12,0C9.06,0,0,0.43,1.1,2.84C1.6,3.94,10,15.76,10,15.76V21L14,24V15.76S22.4,3.96,22.9,2.84ZM12,1.94C16.25,1.94,20.15,2.51,20.15,3.17S16.25,4.34,12,4.34S3.97,3.8,3.97,3.17S7.76,1.94,12,1.94Z
M13,0C20.2,0,26,5.9,26,13.2,26,16.9,24,20.8,18.7,26.1L13,32,7.2,26.2C2,20.8,0,16.9,0,13.3,0,5.9,5.8,0,13,0Z
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 92b04e254..a7d9f53df 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -134,7 +134,7 @@
Search ExHentai
- The World is Big and Pandas sit alone
+ The World is Big and the panda sit alone
Search
@@ -210,6 +210,10 @@
Removed from favorites
Failed to add to favorites
Failed to remove from favorites
+ Block the uploader: %s ?
+ Block the tag: %s ?
+ The uploader \"%s\" is blocked
+ The tag \"%s\" is blocked
MASTERPIECE
@@ -311,6 +315,20 @@
Viewed galleries will be shown here
+
+ Filter
+ Filters will be shown here
+ Title
+ Uploader
+ Tag
+ Delete title filter: %s ?
+ Delete uploader filter: %s ?
+ Delete tag filter: %s ?
+ Delete filter: %s ?
+ Tip
+ Add filter
+ Filter text
+
Page %d
@string/refresh
@@ -340,6 +358,8 @@
Show Japanese title
Try to show Japanese title. It takes more time to get gallery list
Show default title. It is usually English title
+ Filter
+ Filter by title, uploader and tags
Read
Reading direction
diff --git a/app/src/main/res/xml/eh_settings.xml b/app/src/main/res/xml/eh_settings.xml
index e78f19bef..5e27d84da 100644
--- a/app/src/main/res/xml/eh_settings.xml
+++ b/app/src/main/res/xml/eh_settings.xml
@@ -63,4 +63,10 @@
app:summaryOff="@string/settings_eh_show_jpn_title_summary_off"
android:defaultValue="false"/>
+
+
diff --git a/daogenerator/src/main/java/com/hippo/ehviewer/daogenerator/EhDaoGenerator.java b/daogenerator/src/main/java/com/hippo/ehviewer/daogenerator/EhDaoGenerator.java
index 57feef1df..3e4f28813 100644
--- a/daogenerator/src/main/java/com/hippo/ehviewer/daogenerator/EhDaoGenerator.java
+++ b/daogenerator/src/main/java/com/hippo/ehviewer/daogenerator/EhDaoGenerator.java
@@ -32,13 +32,14 @@ public class EhDaoGenerator {
private static final String OUT_DIR = "../app/src/main/java-gen";
private static final String DELETE_DIR = "../app/src/main/java-gen/com/hippo/ehviewer/dao";
- private static final int VERSION = 1;
+ private static final int VERSION = 2;
private static final String DOWNLOAD_INFO_PATH = "../app/src/main/java-gen/com/hippo/ehviewer/dao/DownloadInfo.java";
private static final String HISTORY_INFO_PATH = "../app/src/main/java-gen/com/hippo/ehviewer/dao/HistoryInfo.java";
private static final String QUICK_SEARCH_PATH = "../app/src/main/java-gen/com/hippo/ehviewer/dao/QuickSearch.java";
private static final String LOCAL_FAVORITE_INFO_PATH = "../app/src/main/java-gen/com/hippo/ehviewer/dao/LocalFavoriteInfo.java";
private static final String BOOKMARK_INFO_PATH = "../app/src/main/java-gen/com/hippo/ehviewer/dao/BookmarkInfo.java";
+ private static final String FILTER_PATH = "../app/src/main/java-gen/com/hippo/ehviewer/dao/Filter.java";
public static void generate() throws Exception {
Utilities.deleteContents(new File(DELETE_DIR));
@@ -54,6 +55,7 @@ public class EhDaoGenerator {
addQuickSearch(schema);
addLocalFavorites(schema);
addBookmarks(schema);
+ addFilter(schema);
new DaoGenerator().generateAll(schema, OUT_DIR);
adjustDownloadInfo();
@@ -61,6 +63,7 @@ public class EhDaoGenerator {
adjustQuickSearch();
adjustLocalFavoriteInfo();
adjustBookmarkInfo();
+ adjustFilter();
}
private static void addDownloads(Schema schema) {
@@ -179,6 +182,16 @@ public class EhDaoGenerator {
entity.addLongProperty("time").notNull();
}
+ // Since 2
+ private static void addFilter(Schema schema) {
+ Entity entity = schema.addEntity("Filter");
+ entity.setTableName("FILTER");
+ entity.setClassNameDao("FilterDao");
+ entity.addIdProperty();
+ entity.addIntProperty("mode").notNull();
+ entity.addStringProperty("text");
+ }
+
private static void adjustDownloadInfo() throws Exception {
JavaClassSource javaClass = Roaster.parse(JavaClassSource.class, new File(DOWNLOAD_INFO_PATH));
// Remove field from GalleryInfo
@@ -476,4 +489,16 @@ public class EhDaoGenerator {
fileWriter.write(javaClass.toString());
fileWriter.close();
}
+
+ // Since 2
+ private static void adjustFilter() throws Exception {
+ JavaClassSource javaClass = Roaster.parse(JavaClassSource.class, new File(FILTER_PATH));
+ // Set field public
+ javaClass.getField("mode").setPublic();
+ javaClass.getField("text").setPublic();
+
+ FileWriter fileWriter = new FileWriter(FILTER_PATH);
+ fileWriter.write(javaClass.toString());
+ fileWriter.close();
+ }
}