From 432c29f19af0f8d83e3e23eea1f1da5f2558bb92 Mon Sep 17 00:00:00 2001 From: seven332 Date: Sat, 4 Jul 2015 23:33:11 +0800 Subject: [PATCH] Improve url analysis in comment --- .../hippo/ehviewer/ui/scene/CommentScene.java | 25 ++++- .../ehviewer/widget/LinkifyTextView.java | 97 ++++++++++++------- 2 files changed, 83 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/CommentScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/CommentScene.java index 53c0cfe70..e31cbe222 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/CommentScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/CommentScene.java @@ -1,7 +1,9 @@ package com.hippo.ehviewer.ui.scene; import android.content.Context; +import android.content.Intent; import android.content.res.Resources; +import android.net.Uri; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.Html; @@ -12,6 +14,7 @@ import android.widget.TextView; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; import com.hippo.ehviewer.client.data.Comment; +import com.hippo.ehviewer.widget.LinkifyTextView; import com.hippo.rippleold.RippleSalon; import com.hippo.scene.Announcer; import com.hippo.scene.AppbarScene; @@ -22,7 +25,7 @@ import com.hippo.widget.recyclerview.LinearDividerItemDecoration; import java.util.List; -public class CommentScene extends AppbarScene { +public class CommentScene extends AppbarScene implements EasyRecyclerView.OnItemClickListener { public static final String KEY_COMMENTS = "key_comments"; @@ -70,6 +73,7 @@ public class CommentScene extends AppbarScene { mRecyclerView.setSelector(RippleSalon.generateRippleDrawable(false)); mRecyclerView.setClipToPadding(false); mRecyclerView.setHasFixedSize(true); + mRecyclerView.setOnItemClickListener(this); } @Override @@ -91,18 +95,31 @@ public class CommentScene extends AppbarScene { } } + @Override + public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) { + LinkifyTextView comment = ((LinkifyTextView) view.findViewById(R.id.comment)); + String url = comment.getTouchedUrl(); + if (url != null) { + comment.clearTouchedUrl(); + Uri uri = Uri.parse(url); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + getStageActivity().startActivity(intent); + } + return true; + } + private static class CommentHolder extends RecyclerView.ViewHolder { private TextView user; private TextView time; - private TextView comment; + private LinkifyTextView comment; public CommentHolder(View itemView) { super(itemView); user = (TextView) itemView.findViewById(R.id.user); time = (TextView) itemView.findViewById(R.id.time); - comment = (TextView) itemView.findViewById(R.id.comment); + comment = (LinkifyTextView) itemView.findViewById(R.id.comment); } } @@ -122,7 +139,7 @@ public class CommentScene extends AppbarScene { holder.time.setText(comment.time); URLImageGetter p = new URLImageGetter( holder.comment, EhApplication.getConaco(getStageActivity())); - holder.comment.setText(Html.fromHtml(comment.comment, p, null)); + holder.comment.setLinkifyText(Html.fromHtml(comment.comment, p, null)); } @Override diff --git a/app/src/main/java/com/hippo/ehviewer/widget/LinkifyTextView.java b/app/src/main/java/com/hippo/ehviewer/widget/LinkifyTextView.java index 186b8a76d..7c25dbd39 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/LinkifyTextView.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/LinkifyTextView.java @@ -16,42 +16,63 @@ package com.hippo.ehviewer.widget; -import android.annotation.SuppressLint; import android.content.Context; import android.support.annotation.NonNull; import android.text.Layout; import android.text.Spannable; -import android.text.style.ClickableSpan; +import android.text.SpannableString; +import android.text.Spanned; import android.text.style.URLSpan; -import android.text.util.Linkify; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.TextView; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class LinkifyTextView extends TextView { + private static final Pattern URL_PATTERN = Pattern.compile("(http|https)://[a-z0-9A-Z%-]+(\\.[a-z0-9A-Z%-]+)+(:\\d{1,5})?(/[a-zA-Z0-9-_~:#@!&',;=%/\\*\\.\\?\\+\\$\\[\\]\\(\\)]*)?"); + private String mUrl; public LinkifyTextView(Context context) { super(context); - init(); } public LinkifyTextView(Context context, AttributeSet attrs) { super(context, attrs); - init(); } public LinkifyTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - init(); } - public void init() { - // Just make urls striking - // LinkifyTextView itself should not handle touch url event - setAutoLinkMask(Linkify.WEB_URLS); - setLinksClickable(false); + public void setLinkifyText(CharSequence text) { + Matcher m = URL_PATTERN.matcher(text); + + Spannable spannable; + if (text instanceof Spannable) { + spannable = (Spannable) text; + } else { + spannable = new SpannableString(text); + } + + while (m.find()) { + int start = m.start(); + int end = m.end(); + + URLSpan[] links = spannable.getSpans(start, end, URLSpan.class); + if (links.length > 0) { + // There has been URLSpan already, leave it alone + break; + } + + URLSpan urlSpan = new URLSpan(m.group(0)); + spannable.setSpan(urlSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + setText(spannable); } public String getTouchedUrl() { @@ -66,39 +87,45 @@ public class LinkifyTextView extends TextView { } @Override - @SuppressLint("ClickableViewAccessibility") public boolean onTouchEvent(@NonNull MotionEvent event) { - if (getText() instanceof Spannable) { - // Get this code from android.text.method.LinkMovementMethod. - // Work fine ! - int x = (int) event.getX(); - int y = (int) event.getY(); - boolean isGetUrl = false; + // Let the parent or grandparent of TextView to handles click aciton. + // Otherwise click effect like ripple will not work, and if touch area + // do not contain a url, the TextView will still get MotionEvent. + // onTouchEven must be called with MotionEvent.ACTION_DOWN for each touch + // action on it, so we analyze touched url here. + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (getText() instanceof Spanned) { + // Get this code from android.text.method.LinkMovementMethod. + // Work fine ! + int x = (int) event.getX(); + int y = (int) event.getY(); + boolean isGetUrl = false; - x -= getTotalPaddingLeft(); - y -= getTotalPaddingTop(); + x -= getTotalPaddingLeft(); + y -= getTotalPaddingTop(); - x += getScrollX(); - y += getScrollY(); + x += getScrollX(); + y += getScrollY(); - Layout layout = getLayout(); - int line = layout.getLineForVertical(y); - int off = layout.getOffsetForHorizontal(line, x); + Layout layout = getLayout(); + int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); - ClickableSpan[] links = ((Spannable)getText()).getSpans(off, off, ClickableSpan.class); + URLSpan[] links = ((Spanned)getText()).getSpans(off, off, URLSpan.class); - for (ClickableSpan link : links) { - if (link instanceof URLSpan) { - mUrl = ((URLSpan)link).getURL(); - isGetUrl = true; - break; + for (URLSpan link : links) { + mUrl = link.getURL(); + if (mUrl != null) { + isGetUrl = true; + break; + } } - } - if (!isGetUrl) { + if (!isGetUrl) { + mUrl = null; + } + } else { mUrl = null; } - } else { - mUrl = null; } return super.onTouchEvent(event);