diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhFilter.java b/app/src/main/java/com/hippo/ehviewer/client/EhFilter.java index 73942e1a9..5cdfc5458 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhFilter.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhFilter.java @@ -24,6 +24,8 @@ import com.hippo.ehviewer.dao.Filter; import java.util.ArrayList; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public final class EhFilter { @@ -31,36 +33,38 @@ public final class EhFilter { public static final int MODE_UPLOADER = 1; public static final int MODE_TAG = 2; public static final int MODE_TAG_NAMESPACE = 3; + public static final int MODE_COMMENTER = 4; + public static final int MODE_COMMENT = 5; private static final String TAG = EhFilter.class.getSimpleName(); private static EhFilter sInstance; private final List mTitleFilterList = new ArrayList<>(); private final List mUploaderFilterList = new ArrayList<>(); private final List mTagFilterList = new ArrayList<>(); private final List mTagNamespaceFilterList = new ArrayList<>(); + private final List mCommenterFilterList = new ArrayList<>(); + private final List mCommentFilterList = new ArrayList<>(); 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: + case MODE_TITLE -> { filter.text = filter.text.toLowerCase(); mTitleFilterList.add(filter); - break; - case MODE_UPLOADER: - mUploaderFilterList.add(filter); - break; - case MODE_TAG: + } + case MODE_TAG -> { filter.text = filter.text.toLowerCase(); mTagFilterList.add(filter); - break; - case MODE_TAG_NAMESPACE: + } + case MODE_TAG_NAMESPACE -> { filter.text = filter.text.toLowerCase(); mTagNamespaceFilterList.add(filter); - break; - default: - Log.d(TAG, "Unknown mode: " + filter.mode); - break; + } + case MODE_UPLOADER -> mUploaderFilterList.add(filter); + case MODE_COMMENTER -> mCommenterFilterList.add(filter); + case MODE_COMMENT -> mCommentFilterList.add(filter); + default -> Log.d(TAG, "Unknown mode: " + filter.mode); } } } @@ -88,30 +92,36 @@ public final class EhFilter { return mTagNamespaceFilterList; } + public List getCommenterFilterList() { + return mCommenterFilterList; + } + + public List getCommentFilterList() { + return mCommentFilterList; + } + public synchronized boolean addFilter(Filter filter) { // enable filter by default before it is added to database filter.enable = true; if (!EhDB.addFilter(filter)) return false; switch (filter.mode) { - case MODE_TITLE: + case MODE_TITLE -> { filter.text = filter.text.toLowerCase(); mTitleFilterList.add(filter); - break; - case MODE_UPLOADER: - mUploaderFilterList.add(filter); - break; - case MODE_TAG: + } + case MODE_TAG -> { filter.text = filter.text.toLowerCase(); mTagFilterList.add(filter); - break; - case MODE_TAG_NAMESPACE: + } + case MODE_TAG_NAMESPACE -> { filter.text = filter.text.toLowerCase(); mTagNamespaceFilterList.add(filter); - break; - default: - Log.d(TAG, "Unknown mode: " + filter.mode); - break; + } + case MODE_UPLOADER -> mUploaderFilterList.add(filter); + case MODE_COMMENTER -> mCommenterFilterList.add(filter); + case MODE_COMMENT -> mCommentFilterList.add(filter); + default -> Log.d(TAG, "Unknown mode: " + filter.mode); } return true; } @@ -124,21 +134,13 @@ public final class EhFilter { 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; - case MODE_TAG_NAMESPACE: - mTagNamespaceFilterList.remove(filter); - break; - default: - Log.d(TAG, "Unknown mode: " + filter.mode); - break; + case MODE_TITLE -> mTitleFilterList.remove(filter); + case MODE_TAG -> mTagFilterList.remove(filter); + case MODE_TAG_NAMESPACE -> mTagNamespaceFilterList.remove(filter); + case MODE_UPLOADER -> mUploaderFilterList.remove(filter); + case MODE_COMMENTER -> mCommenterFilterList.remove(filter); + case MODE_COMMENT -> mCommentFilterList.remove(filter); + default -> Log.d(TAG, "Unknown mode: " + filter.mode); } } @@ -272,4 +274,40 @@ public final class EhFilter { return true; } + + public synchronized boolean filterCommenter(String commenter) { + if (null == commenter) { + return false; + } + + List filters = mCommenterFilterList; + if (filters.size() > 0) { + for (int i = 0, n = filters.size(); i < n; i++) { + if (filters.get(i).enable && commenter.equals(filters.get(i).text)) { + return false; + } + } + } + + return true; + } + + public synchronized boolean filterComment(String comment) { + if (null == comment) { + return false; + } + + List filters = mCommentFilterList; + if (filters.size() > 0) { + for (int i = 0, n = filters.size(); i < n; i++) { + if (filters.get(i).enable) { + Pattern p = Pattern.compile(filters.get(i).text); + Matcher m = p.matcher(comment); + if (m.find()) return false; + } + } + } + + return true; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.java index 802ba4abc..5b33ddede 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.java @@ -20,6 +20,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.hippo.ehviewer.EhDB; +import com.hippo.ehviewer.client.EhFilter; import com.hippo.ehviewer.client.EhUtils; import com.hippo.ehviewer.client.data.GalleryComment; import com.hippo.ehviewer.client.data.GalleryCommentList; @@ -473,6 +474,13 @@ public class GalleryDetailParser { comment.user = c3.child(0).text(); // comment comment.comment = JsoupUtils.getElementByClass(element, "c6").html(); + // filter comment + if (!comment.uploader) { + EhFilter sEhFilter = EhFilter.getInstance(); + if (!sEhFilter.filterCommenter(comment.user) || !sEhFilter.filterComment(comment.comment)) { + return null; + } + } // last edited Element c8 = JsoupUtils.getElementByClass(element, "c8"); if (c8 != null) { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/fragment/FilterFragment.kt b/app/src/main/java/com/hippo/ehviewer/ui/fragment/FilterFragment.kt index cdc0caee2..fdc7f5cca 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/fragment/FilterFragment.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/fragment/FilterFragment.kt @@ -264,10 +264,14 @@ class FilterFragment : BaseFragment() { private val mUploaderFilterList: List = mEhFilter.uploaderFilterList private val mTagFilterList: List = mEhFilter.tagFilterList private val mTagNamespaceFilterList: List = mEhFilter.tagNamespaceFilterList + private val mCommenterFilterList: List = mEhFilter.commenterFilterList + private val mCommentFilterList: List = mEhFilter.commentFilterList private var mTitleHeader: Filter? = null private var mUploaderHeader: Filter? = null private var mTagHeader: Filter? = null private var mTagNamespaceHeader: Filter? = null + private var mCommenterHeader: Filter? = null + private var mCommentHeader: Filter? = null fun size(): Int { var count = 0 var size = mTitleFilterList.size @@ -278,6 +282,10 @@ class FilterFragment : BaseFragment() { count += if (0 == size) 0 else size + 1 size = mTagNamespaceFilterList.size count += if (0 == size) 0 else size + 1 + size = mCommenterFilterList.size; + count += if (0 == size) 0 else size + 1 + size = mCommentFilterList.size; + count += if (0 == size) 0 else size + 1 return count } @@ -317,6 +325,24 @@ class FilterFragment : BaseFragment() { } return mTagNamespaceHeader!! } + private val commenterHeader: Filter + get() { + if (null == mCommenterHeader) { + mCommenterHeader = Filter() + mCommenterHeader!!.mode = MODE_HEADER + mCommenterHeader!!.text = getString(R.string.filter_commenter) + } + return mCommenterHeader!! + } + private val commentHeader: Filter + get() { + if (null == mCommentHeader) { + mCommentHeader = Filter() + mCommentHeader!!.mode = MODE_HEADER + mCommentHeader!!.text = getString(R.string.filter_comment) + } + return mCommentHeader!! + } operator fun get(index: Int): Filter { var index1 = index @@ -356,6 +382,26 @@ class FilterFragment : BaseFragment() { return tagNamespaceHeader } else if (index1 <= size) { return mTagNamespaceFilterList[index1 - 1] + } else { + size + 1 + } + } + size = mCommenterFilterList.size + if (0 != size) { + index1 -= if (index1 == 0) { + return commenterHeader + } else if (index1 <= size) { + return mCommenterFilterList[index1 - 1] + } else { + size + 1 + } + } + size = mCommentFilterList.size + if (0 != size) { + if (index1 == 0) { + return commentHeader + } else if (index1 <= size) { + return mCommentFilterList[index1 - 1] } } throw IndexOutOfBoundsException() diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java index 448ac131a..f788efbcf 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java @@ -62,6 +62,7 @@ import com.hippo.ehviewer.R; import com.hippo.ehviewer.UrlOpener; import com.hippo.ehviewer.WindowInsetsAnimationHelper; 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.data.GalleryComment; @@ -69,6 +70,7 @@ import com.hippo.ehviewer.client.data.GalleryCommentList; import com.hippo.ehviewer.client.data.GalleryDetail; import com.hippo.ehviewer.client.data.ListUrlBuilder; import com.hippo.ehviewer.client.parser.VoteCommentParser; +import com.hippo.ehviewer.dao.Filter; import com.hippo.ehviewer.ui.MainActivity; import com.hippo.scene.SceneFragment; import com.hippo.text.URLImageGetter; @@ -265,6 +267,45 @@ public final class GalleryCommentsScene extends ToolbarScene onBackPressed(); } + private void showFilterCommenterDialog(String commenter, int position) { + Context context = getContext(); + if (context == null || commenter == null) { + return; + } + + new BaseDialogBuilder(context) + .setMessage(getString(R.string.filter_the_commenter, commenter)) + .setPositiveButton(android.R.string.ok, (dialog, which) -> { + Filter filter = new Filter(); + filter.mode = EhFilter.MODE_COMMENTER; + filter.text = commenter; + EhFilter.getInstance().addFilter(filter); + hideComment(position); + showTip(R.string.filter_added, LENGTH_SHORT); + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } + + private void hideComment(int position) { + if (mGalleryDetail == null || mGalleryDetail.comments == null || mGalleryDetail.comments.comments == null) { + return; + } + + GalleryComment[] oldCommentsList = mGalleryDetail.comments.comments; + GalleryComment[] newCommentsList = new GalleryComment[oldCommentsList.length - 1]; + for (int i = 0, j = 0; i < oldCommentsList.length; i++) { + if (i != position) { + newCommentsList[j] = oldCommentsList[i]; + j++; + } + } + + mGalleryDetail.comments.comments = newCommentsList; + mAdapter.notifyDataSetChanged(); + updateView(true); + } + private void voteComment(long id, int vote) { Context context = getContext(); MainActivity activity = getMainActivity(); @@ -339,6 +380,10 @@ public final class GalleryCommentsScene extends ToolbarScene menu.add(resources.getString(R.string.copy_comment_text)); menuId.add(R.id.copy); + if (!comment.uploader && !comment.editable) { + menu.add(resources.getString(R.string.block_commenter)); + menuId.add(R.id.block_commenter); + } if (comment.editable) { menu.add(resources.getString(R.string.edit_comment)); menuId.add(R.id.edit_comment); @@ -364,6 +409,8 @@ public final class GalleryCommentsScene extends ToolbarScene int id = menuId.get(which); if (id == R.id.copy) { ClipboardUtilKt.addTextToClipboard(requireActivity(), comment.comment, false); + } else if (id == R.id.block_commenter) { + showFilterCommenterDialog(comment.user, position); } else if (id == R.id.vote_up) { voteComment(comment.id, 1); } else if (id == R.id.vote_down) { diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index be4a967c2..e92e9f267 100755 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -238,6 +238,8 @@ 评论成功 评论失败 复制评论文字 + 屏蔽评论者 + 屏蔽评论者“%s”? 修改评论 评论已被修改 修改评论失败 @@ -309,17 +311,19 @@ 清除所有阅读历史? - 屏蔽画廊 + 屏蔽列表 标题 上传者 标签 标签组 + 评论者 + 评论 删除屏蔽项“%s”? 提示 添加屏蔽项 查看定义 屏蔽项文本 - 该屏蔽系统会在 EHentai 网站屏蔽系统的基础上继续屏蔽画廊。\n\n标题屏蔽项:排除标题含有该关键字的画廊。\n\n上传者屏蔽项:排除该上传者上传的画廊。\n\n标签屏蔽项:排除包含该标签的画廊,这会使获取画廊列表花费更多时间。\n\n标签组屏蔽项:排除包含该标签组的画廊,这会使获取画廊列表花费更多时间。 + 该屏蔽系统会在 EHentai 网站屏蔽系统的基础上继续屏蔽画廊。\n\n标题屏蔽项:排除标题含有该关键字的画廊。\n\n上传者屏蔽项:排除该上传者上传的画廊。\n\n标签屏蔽项:排除包含该标签的画廊,这会使获取画廊列表花费更多时间。\n\n标签组屏蔽项:排除包含该标签组的画廊,这会使获取画廊列表花费更多时间。\n\n评论者屏蔽项:排除该评论者发布的评论。\n\n评论屏蔽项:排除匹配该正则表达式的评论。 @@ -382,8 +386,8 @@ https://github.com/EhTagTranslation/Editor/wiki 画廊详情页预览图最大数量 由于历史因素,此值过大会导致卡顿 - 屏蔽画廊 - 根据标题、上传者、标签屏蔽画廊 + 屏蔽列表 + 根据标题、上传者、标签、评论者屏蔽画廊或评论 使用音量键翻页 翻转音量键 diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 358ab8cbe..247b19dad 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -178,6 +178,8 @@ @string/filter_uploader @string/filter_tag @string/filter_tag_namespace + @string/filter_commenter + @string/filter_comment diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml index 155242912..ebec5c301 100644 --- a/app/src/main/res/values/ids.xml +++ b/app/src/main/res/values/ids.xml @@ -22,6 +22,7 @@ + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 12e9068e7..a5d9428a7 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -284,6 +284,8 @@ Comment post successfully Failed to post the comment Copy comment text + Block the commenter + Block the commenter \"%s\"? Edit comment The comment has been edited Failed to edit the comment @@ -355,17 +357,19 @@ Clear all history? - Block gallery + Blockers Title Uploader Tag Tag namespace + Commenter + Comment Regex Delete blocker \"%s\"? Tip Add blocker Show definition Blocker text - This blocking system filters the result of EHentai website blocking system.\n\nTitle Blocker: exclude the gallery whose title contains the word.\n\nUploader Blocker: exclude the gallery which was uploaded by the uploader.\n\nTag Blocker: exclude the gallery which contain the tag, it takes more time to get gallery list.\n\nTag Namespace Blocker: exclude the gallery which contain the tag namespace, it takes more time to get gallery list. + This blocking system filters the result of EHentai website blocking system.\n\nTitle Blocker: exclude the gallery whose title contains the word.\n\nUploader Blocker: exclude the gallery which was uploaded by the uploader.\n\nTag Blocker: exclude the gallery which contain the tag, it takes more time to get gallery list.\n\nTag Namespace Blocker: exclude the gallery which contain the tag namespace, it takes more time to get gallery list.\n\nCommenter Blocker: exclude the comments posted by the commenter.\n\nComment Blocker: exclude the comments matching the regex. @@ -427,8 +431,8 @@ https://placeholder The maximum number of previews on the gallery details page For historical reasons, large value could cause lag - Block gallery - Block gallery by title, uploader and tags + Blockers + Block gallery or comment by title, uploader, tags and commenter Use volume key to turn pages Reverse volume key