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(); + } }