From 50ccebfc91395ffcc090fc49a10c8101531c54d0 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Mon, 3 Sep 2018 11:13:36 +0200 Subject: [PATCH] Parse URLs in conversation messages --- .../client/data/utils/StringsUtils.java | 10 ++ .../adapters/ConversationMessageAdapter.java | 11 +- .../client/ui/views/BaseFrameLayoutView.java | 12 +- .../client/ui/views/ContentTextView.java | 126 ++++++++++++++++++ .../conversation_message_item_received.xml | 9 +- .../layout/conversation_message_item_sent.xml | 11 +- app/src/main/res/values/colors.xml | 2 + 7 files changed, 166 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/views/ContentTextView.java diff --git a/app/src/main/java/org/communiquons/android/comunic/client/data/utils/StringsUtils.java b/app/src/main/java/org/communiquons/android/comunic/client/data/utils/StringsUtils.java index 6ca7af6..a2b8437 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/data/utils/StringsUtils.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/data/utils/StringsUtils.java @@ -95,6 +95,16 @@ public class StringsUtils { return !TextUtils.isEmpty(mail) && Patterns.EMAIL_ADDRESS.matcher(mail).matches(); } + /** + * Check whether a specified string is an URL or not + * + * @param string The string to check + * @return TRUE if the string is an URL / FALSE else + */ + public static boolean isURL(CharSequence string){ + return !TextUtils.isEmpty(string) && Patterns.WEB_URL.matcher(string).matches(); + } + /** * Generate the SHA-1 summary of a given string * diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/ConversationMessageAdapter.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/ConversationMessageAdapter.java index a5baa62..3d2e58f 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/ConversationMessageAdapter.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/ConversationMessageAdapter.java @@ -15,6 +15,7 @@ import org.communiquons.android.comunic.client.data.models.ConversationMessage; import org.communiquons.android.comunic.client.data.models.UserInfo; import org.communiquons.android.comunic.client.data.utils.StringsUtils; import org.communiquons.android.comunic.client.ui.listeners.OnConversationMessageActionsListener; +import org.communiquons.android.comunic.client.ui.views.ContentTextView; import org.communiquons.android.comunic.client.ui.views.EnlargeableWebImageView; import org.communiquons.android.comunic.client.ui.views.WebUserAccountImage; @@ -122,7 +123,7 @@ public class ConversationMessageAdapter extends RecyclerView.Adapter { private class BaseMessageHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener{ - private TextView mMessage; + ContentTextView mMessage; private TextView mSentDate; private EnlargeableWebImageView mImage; @@ -133,7 +134,7 @@ public class ConversationMessageAdapter extends RecyclerView.Adapter { mImage = itemView.findViewById(R.id.messageImage); mSentDate = itemView.findViewById(R.id.text_message_time); - itemView.setOnLongClickListener(this); + mMessage.setOnLongClickListener(this); mImage.setOnLongClickListener(this); } @@ -156,7 +157,7 @@ public class ConversationMessageAdapter extends RecyclerView.Adapter { void bind(int pos){ ConversationMessage message = mList.get(pos); - mMessage.setText(message.getContent()); + mMessage.setParsedText(message.getContent()); mMessage.setVisibility(mMessage.getText().length() > 0 ? View.VISIBLE : View.GONE); mImage.setVisibility(message.hasImage() ? View.VISIBLE : View.GONE); @@ -193,6 +194,8 @@ public class ConversationMessageAdapter extends RecyclerView.Adapter { SentMessageHolder(@NonNull View itemView) { super(itemView); + + mMessage.setLinksColor(R.color.conversation_user_links_color); } } @@ -209,6 +212,8 @@ public class ConversationMessageAdapter extends RecyclerView.Adapter { mUserAccountImage = itemView.findViewById(R.id.account_image); mUserName = itemView.findViewById(R.id.user_name); + + mMessage.setLinksColor(R.color.conversation_otheruser_links_color); } void setUserInfoVisibility(boolean visible){ diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/views/BaseFrameLayoutView.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/views/BaseFrameLayoutView.java index 36a68c5..7b0ecce 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/views/BaseFrameLayoutView.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/views/BaseFrameLayoutView.java @@ -31,13 +31,23 @@ abstract class BaseFrameLayoutView extends FrameLayout { super(context, attrs, defStyleAttr, defStyleRes); } + /** * Get hosting activity * * @return Hosting activity, if found, null else */ protected Activity getActivity(){ - Context context = getContext(); + return GetActivityFromContext(getContext()); + } + + /** + * Get an activity from context + * + * @param context The context to parse + * @return The activity + */ + static Activity GetActivityFromContext(Context context){ while(context instanceof ContextWrapper){ if(context instanceof Activity) diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/views/ContentTextView.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/views/ContentTextView.java new file mode 100644 index 0000000..c51f31e --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/views/ContentTextView.java @@ -0,0 +1,126 @@ +package org.communiquons.android.comunic.client.ui.views; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.net.Uri; +import android.support.annotation.Nullable; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextPaint; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; +import android.util.AttributeSet; +import android.view.View; + +import org.communiquons.android.comunic.client.data.utils.StringsUtils; +import org.communiquons.android.comunic.client.ui.utils.UiUtils; + +/** + * TextView extension which parses the content passed to the + * view in order to make links live + * + * @author Pierre HUBERT + */ +public class ContentTextView extends android.support.v7.widget.AppCompatTextView { + + /** + * Debug tag + */ + private static final String TAG = ContentTextView.class.getSimpleName(); + + /** + * The color of detected links + */ + private int mLinksColor = Color.BLUE; + + public ContentTextView(Context context) { + this(context, null); + } + + public ContentTextView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public ContentTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setMovementMethod(LinkMovementMethod.getInstance()); + } + + protected Activity getActivity(){ + return BaseFrameLayoutView.GetActivityFromContext(getContext()); + } + + /** + * Set the color of selected links + * + * @param res_id The ID of the target color + */ + public void setLinksColor(int res_id){ + this.mLinksColor = UiUtils.getColor(getContext(), res_id); + } + + /** + * Set na new text, with links parsed + * + * @param text The text to parse + */ + public void setParsedText(String text) { + super.setText(text); + + //Parse text + SpannableStringBuilder ssb = new SpannableStringBuilder(text); + + String[] parts = text.split("\\s+"); + int pos = 0; + for (String part : parts) { + + //Mark it as URL + if (StringsUtils.isURL(part)) { + + ClickableSpan clickableSpan = new URLClickableSpan(part); + ssb.setSpan(clickableSpan, pos, pos + part.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + } + + pos += part.length() + 1; + + } + + setText(ssb); + + } + + /** + * Base ClickableSpan class + */ + private abstract class BaseClickableSpan extends ClickableSpan { + + @Override + public void updateDrawState(TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + ds.setColor(mLinksColor); + } + } + + /** + * Clickable span class for URL + */ + private class URLClickableSpan extends BaseClickableSpan { + + private String mURL; + + private URLClickableSpan(String url){ + super(); + mURL = url; + } + + @Override + public void onClick(View widget) { + getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(mURL))); + } + } +} diff --git a/app/src/main/res/layout/conversation_message_item_received.xml b/app/src/main/res/layout/conversation_message_item_received.xml index 9c92e10..5e18544 100644 --- a/app/src/main/res/layout/conversation_message_item_received.xml +++ b/app/src/main/res/layout/conversation_message_item_received.xml @@ -1,7 +1,6 @@ - - + app:srcCompat="@drawable/img_placeholder" /> - - + app:layout_constraintTop_toTopOf="parent" + tools:text="11:40" /> \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index ff143ae..05bd58d 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -22,8 +22,10 @@ #3F51B5 #FFFFFF + #c5cae9 #D8D8D8 #000000 + @color/conversation_user_messages_background #8c9eff