Parse URLs in conversation messages

This commit is contained in:
Pierre HUBERT 2018-09-03 11:13:36 +02:00
parent 5b46e559af
commit 50ccebfc91
7 changed files with 166 additions and 15 deletions

View File

@ -95,6 +95,16 @@ public class StringsUtils {
return !TextUtils.isEmpty(mail) && Patterns.EMAIL_ADDRESS.matcher(mail).matches(); 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 * Generate the SHA-1 summary of a given string
* *

View File

@ -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.models.UserInfo;
import org.communiquons.android.comunic.client.data.utils.StringsUtils; 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.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.EnlargeableWebImageView;
import org.communiquons.android.comunic.client.ui.views.WebUserAccountImage; 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 private class BaseMessageHolder extends RecyclerView.ViewHolder
implements View.OnLongClickListener{ implements View.OnLongClickListener{
private TextView mMessage; ContentTextView mMessage;
private TextView mSentDate; private TextView mSentDate;
private EnlargeableWebImageView mImage; private EnlargeableWebImageView mImage;
@ -133,7 +134,7 @@ public class ConversationMessageAdapter extends RecyclerView.Adapter {
mImage = itemView.findViewById(R.id.messageImage); mImage = itemView.findViewById(R.id.messageImage);
mSentDate = itemView.findViewById(R.id.text_message_time); mSentDate = itemView.findViewById(R.id.text_message_time);
itemView.setOnLongClickListener(this); mMessage.setOnLongClickListener(this);
mImage.setOnLongClickListener(this); mImage.setOnLongClickListener(this);
} }
@ -156,7 +157,7 @@ public class ConversationMessageAdapter extends RecyclerView.Adapter {
void bind(int pos){ void bind(int pos){
ConversationMessage message = mList.get(pos); ConversationMessage message = mList.get(pos);
mMessage.setText(message.getContent()); mMessage.setParsedText(message.getContent());
mMessage.setVisibility(mMessage.getText().length() > 0 ? View.VISIBLE : View.GONE); mMessage.setVisibility(mMessage.getText().length() > 0 ? View.VISIBLE : View.GONE);
mImage.setVisibility(message.hasImage() ? 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) { SentMessageHolder(@NonNull View itemView) {
super(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); mUserAccountImage = itemView.findViewById(R.id.account_image);
mUserName = itemView.findViewById(R.id.user_name); mUserName = itemView.findViewById(R.id.user_name);
mMessage.setLinksColor(R.color.conversation_otheruser_links_color);
} }
void setUserInfoVisibility(boolean visible){ void setUserInfoVisibility(boolean visible){

View File

@ -31,13 +31,23 @@ abstract class BaseFrameLayoutView extends FrameLayout {
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);
} }
/** /**
* Get hosting activity * Get hosting activity
* *
* @return Hosting activity, if found, null else * @return Hosting activity, if found, null else
*/ */
protected Activity getActivity(){ 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){ while(context instanceof ContextWrapper){
if(context instanceof Activity) if(context instanceof Activity)

View File

@ -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)));
}
}
}

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -39,7 +38,7 @@
app:layout_constraintTop_toTopOf="@id/account_image" app:layout_constraintTop_toTopOf="@id/account_image"
tools:text="John Doe" /> tools:text="John Doe" />
<TextView <org.communiquons.android.comunic.client.ui.views.ContentTextView
android:id="@+id/message_body" android:id="@+id/message_body"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -62,12 +61,12 @@
android:layout_height="98dp" android:layout_height="98dp"
android:layout_marginStart="60dp" android:layout_marginStart="60dp"
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:scaleType="fitStart"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/constraintLayout2" app:layout_constraintTop_toBottomOf="@id/constraintLayout2"
app:layout_constraintVertical_bias="0.0" app:layout_constraintVertical_bias="0.0"
app:srcCompat="@drawable/img_placeholder" app:srcCompat="@drawable/img_placeholder" />
android:scaleType="fitStart"/>
<TextView <TextView

View File

@ -1,18 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="16dp"> android:paddingTop="16dp">
<TextView <org.communiquons.android.comunic.client.ui.views.ContentTextView
android:id="@+id/message_body" android:id="@+id/message_body"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="8dp" android:layout_marginEnd="8dp"
android:background="@drawable/conversation_message_currentuser_bg" android:background="@drawable/conversation_message_currentuser_bg"
android:maxWidth="240dp" android:maxWidth="240dp"
android:padding="8dp" android:padding="8dp"
@ -45,10 +44,10 @@
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="@dimen/conversation_message_time_padding_top" android:layout_marginTop="@dimen/conversation_message_time_padding_top"
android:text="11:40"
android:textSize="10sp" android:textSize="10sp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
tools:text="11:40" />
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View File

@ -22,8 +22,10 @@
<!-- Conversation messages --> <!-- Conversation messages -->
<color name="conversation_user_messages_background">#3F51B5</color> <color name="conversation_user_messages_background">#3F51B5</color>
<color name="conversation_user_messages_textColor">#FFFFFF</color> <color name="conversation_user_messages_textColor">#FFFFFF</color>
<color name="conversation_user_links_color">#c5cae9</color>
<color name="conversation_otheruser_messages_background">#D8D8D8</color> <color name="conversation_otheruser_messages_background">#D8D8D8</color>
<color name="conversation_otheruser_messages_textColor">#000000</color> <color name="conversation_otheruser_messages_textColor">#000000</color>
<color name="conversation_otheruser_links_color">@color/conversation_user_messages_background</color>
<!-- Conversations footer --> <!-- Conversations footer -->
<color name="conversation_footer_bg">#8c9eff</color> <color name="conversation_footer_bg">#8c9eff</color>