Commit 6c14cdd1 authored by Tiago Cunha's avatar Tiago Cunha

Better handling of attachments

parent d13702d9
......@@ -30,10 +30,16 @@ public class Avatar {
};
private final String hostname;
private final String username;
private final String avatar;
public Avatar(String hostname, String username) {
this(hostname, username, null);
}
public Avatar(String hostname, String username, String avatar) {
this.hostname = hostname;
this.username = username;
this.avatar = avatar;
}
private static int getColorForUser(String username) {
......@@ -76,11 +82,15 @@ public class Avatar {
private String getImageUrl() {
//from Rocket.Chat:packages/rocketchat-ui/lib/avatar.coffee
//REMARK! this is often SVG image! (see: Rocket.Chat:server/startup/avatar.coffee)
try {
return "https://" + hostname + "/avatar/" + URLEncoder.encode(username, "UTF-8") + ".jpg";
} catch (UnsupportedEncodingException exception) {
RCLog.e(exception, "failed to get URL for user: %s", username);
return null;
if (TextUtils.isEmpty(avatar)) {
try {
return "https://" + hostname + "/avatar/" + URLEncoder.encode(username, "UTF-8") + ".jpg";
} catch (UnsupportedEncodingException exception) {
RCLog.e(exception, "failed to get URL for user: %s", username);
return null;
}
} else {
return avatar;
}
}
......@@ -100,6 +110,10 @@ public class Avatar {
}
private Drawable getTextDrawable(Context context) {
if (username == null) {
return null;
}
int round = (int) (4 * context.getResources().getDisplayMetrics().density);
return TextDrawable.builder()
......@@ -114,16 +128,16 @@ public class Avatar {
// Picasso can be triggered only on Main Thread.
if (Looper.myLooper() != Looper.getMainLooper()) {
new Handler(Looper.getMainLooper()).post(() -> {
getBitmap(context, size).continueWith(_task -> {
if (_task.isFaulted()) {
task.setError(_task.getError());
} else {
task.setResult(_task.getResult());
}
return null;
});
});
new Handler(Looper.getMainLooper()).post(() ->
getBitmap(context, size)
.continueWith(_task -> {
if (_task.isFaulted()) {
task.setError(_task.getError());
} else {
task.setResult(_task.getResult());
}
return null;
}));
return task.getTask();
}
......
......@@ -38,6 +38,8 @@ public class Message extends RealmObject {
private String msg;
private User u;
private boolean groupable;
private String alias;
private String avatar;
private String attachments; //JSONArray.
private String urls; //JSONArray.
......@@ -133,6 +135,22 @@ public class Message extends RealmObject {
this.urls = urls;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
@Override
public String toString() {
return "Message{" +
......@@ -144,6 +162,8 @@ public class Message extends RealmObject {
", msg='" + msg + '\'' +
", u=" + u +
", groupable=" + groupable +
", alias='" + alias + '\'' +
", avatar='" + avatar + '\'' +
", attachments='" + attachments + '\'' +
", urls='" + urls + '\'' +
'}';
......@@ -184,6 +204,12 @@ public class Message extends RealmObject {
if (u != null ? !u.equals(message.u) : message.u != null) {
return false;
}
if (alias != null ? !alias.equals(message.alias) : message.alias != null) {
return false;
}
if (avatar != null ? !avatar.equals(message.avatar) : message.avatar != null) {
return false;
}
if (attachments != null ? !attachments.equals(message.attachments)
: message.attachments != null) {
return false;
......@@ -202,6 +228,8 @@ public class Message extends RealmObject {
result = 31 * result + (msg != null ? msg.hashCode() : 0);
result = 31 * result + (u != null ? u.hashCode() : 0);
result = 31 * result + (groupable ? 1 : 0);
result = 31 * result + (alias != null ? alias.hashCode() : 0);
result = 31 * result + (avatar != null ? avatar.hashCode() : 0);
result = 31 * result + (attachments != null ? attachments.hashCode() : 0);
result = 31 * result + (urls != null ? urls.hashCode() : 0);
return result;
......
......@@ -35,7 +35,7 @@ public class MessageRenderer extends AbstractRenderer<Message> {
imageView.setImageResource(R.drawable.ic_error_outline_black_24dp);
break;
default:
userRenderer.avatarInto(imageView, hostname);
userRenderer.avatarInto(imageView, hostname, object.getAvatar());
break;
}
return this;
......@@ -45,7 +45,7 @@ public class MessageRenderer extends AbstractRenderer<Message> {
* show Username in textView.
*/
public MessageRenderer usernameInto(TextView textView) {
userRenderer.usernameInto(textView);
userRenderer.usernameInto(textView, object.getAlias());
return this;
}
......
package chat.rocket.android.renderer;
import android.content.Context;
import android.graphics.Color;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.widget.ImageView;
import android.widget.TextView;
......@@ -22,12 +26,19 @@ public class UserRenderer extends AbstractRenderer<User> {
* show Avatar image.
*/
public UserRenderer avatarInto(ImageView imageView, String hostname) {
return avatarInto(imageView, hostname, null);
}
/**
* show Avatar image - overriding the user default.
*/
public UserRenderer avatarInto(ImageView imageView, String hostname, String avatar) {
if (!shouldHandle(imageView)) {
return this;
}
if (!TextUtils.isEmpty(object.getUsername())) {
new Avatar(hostname, object.getUsername()).into(imageView);
if (!TextUtils.isEmpty(object.getUsername()) || !TextUtils.isEmpty(avatar)) {
new Avatar(hostname, object.getUsername(), avatar).into(imageView);
}
return this;
}
......@@ -36,11 +47,34 @@ public class UserRenderer extends AbstractRenderer<User> {
* show Username in textView.
*/
public UserRenderer usernameInto(TextView textView) {
return usernameInto(textView, null);
}
/**
* show Username in textView - adding the alias first.
*/
public UserRenderer usernameInto(TextView textView, String alias) {
if (!shouldHandle(textView)) {
return this;
}
textView.setText(object.getUsername());
final SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
final ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.BLACK);
if (TextUtils.isEmpty(alias)) {
spannableStringBuilder.append(object.getUsername());
spannableStringBuilder.setSpan(foregroundColorSpan, 0, object.getUsername().length(),
Spannable.SPAN_INCLUSIVE_INCLUSIVE);
} else {
spannableStringBuilder.append(alias);
spannableStringBuilder.append(" @");
spannableStringBuilder.append(object.getUsername());
spannableStringBuilder
.setSpan(foregroundColorSpan, 0, alias.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
textView.setText(spannableStringBuilder);
return this;
}
......
package chat.rocket.android.widget.message;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.emojione.Emojione;
import chat.rocket.android.widget.R;
import chat.rocket.android.widget.helper.InlineHightlighter;
import chat.rocket.android.widget.helper.Linkify;
import chat.rocket.android.widget.helper.MarkDown;
public class MessageAttachmentFieldLayout extends LinearLayout {
private TextView titleView;
private TextView valueView;
public MessageAttachmentFieldLayout(Context context) {
super(context);
initialize(context, null);
}
public MessageAttachmentFieldLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs);
}
public MessageAttachmentFieldLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MessageAttachmentFieldLayout(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize(context, attrs);
}
private void initialize(Context context, AttributeSet attrs) {
setOrientation(VERTICAL);
LayoutInflater.from(context)
.inflate(R.layout.message_inline_attachment_field, this, true);
titleView = (TextView) findViewById(R.id.field_title);
valueView = (TextView) findViewById(R.id.field_value);
}
public void setTitle(String title) {
titleView.setText(title);
}
public void setValue(String value) {
valueView.setText(Emojione.shortnameToUnicode(value, false));
MarkDown.apply(valueView);
Linkify.markup(valueView);
InlineHightlighter.highlight(valueView);
}
}
......@@ -3,13 +3,16 @@ package chat.rocket.android.widget.message;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.support.graphics.drawable.VectorDrawableCompat;
import android.support.v4.widget.TextViewCompat;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
......@@ -21,7 +24,6 @@ import org.json.JSONObject;
import java.io.IOException;
import chat.rocket.android.widget.R;
import chat.rocket.android.widget.helper.ImageFormat;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
......@@ -81,10 +83,15 @@ public class RocketChatMessageAttachmentsLayout extends LinearLayout {
public Response intercept(Chain chain) throws IOException {
// uid/token is required to download attachment files.
// see: RocketChat:lib/fileUpload.coffee
Request newRequest = chain.request().newBuilder()
.header("Cookie", "rc_uid=" + userId + ";rc_token=" + token)
.build();
return chain.proceed(newRequest);
if (chain.request().url().host().equals(hostname)) {
Request newRequest = chain.request().newBuilder()
.header("Cookie", "rc_uid=" + userId + ";rc_token=" + token)
.build();
return chain.proceed(newRequest);
}
return chain.proceed(chain.request());
}
};
OkHttpClient okHttpClient = new OkHttpClient.Builder()
......@@ -114,56 +121,178 @@ public class RocketChatMessageAttachmentsLayout extends LinearLayout {
}
private void appendAttachmentView(JSONObject attachmentObj) throws JSONException {
if (attachmentObj.isNull("image_url")) {
if (attachmentObj == null) {
return;
}
String imageURL = attachmentObj.getString("image_url");
String imageType = attachmentObj.getString("image_type");
View attachmentView = inflater.inflate(R.layout.message_inline_attachment, this, false);
colorizeAttachmentBar(attachmentObj, attachmentView);
showAuthorAttachment(attachmentObj, attachmentView);
showTitleAttachment(attachmentObj, attachmentView);
showReferenceAttachment(attachmentObj, attachmentView);
showImageAttachment(attachmentObj, attachmentView);
// audio
// video
showFieldsAttachment(attachmentObj, attachmentView);
addView(attachmentView);
}
private void colorizeAttachmentBar(JSONObject attachmentObj, View attachmentView)
throws JSONException {
final View attachmentStrip = attachmentView.findViewById(R.id.attachment_strip);
if (TextUtils.isEmpty(imageURL)
|| !imageType.startsWith("image/")
|| !ImageFormat.SUPPORTED_LIST.contains(imageType)) {
final String colorString = attachmentObj.optString("color");
if (TextUtils.isEmpty(colorString)) {
attachmentStrip.setBackgroundResource(R.color.inline_attachment_quote_line);
return;
}
View attachmentView = inflater.inflate(R.layout.message_inline_attachment, this, false);
try {
attachmentStrip.setBackgroundColor(Color.parseColor(colorString));
} catch (Exception e) {
attachmentStrip.setBackgroundResource(R.color.inline_attachment_quote_line);
}
}
new Picasso.Builder(getContext())
.downloader(getDownloader())
.build()
.load(absolutize(imageURL))
.placeholder(VectorDrawableCompat.create(getResources(), R.drawable.image_dummy, null))
.error(VectorDrawableCompat.create(getResources(), R.drawable.image_error, null))
.into((ImageView) attachmentView.findViewById(R.id.image));
private void showAuthorAttachment(JSONObject attachmentObj, View attachmentView)
throws JSONException {
final View authorBox = attachmentView.findViewById(R.id.author_box);
if (attachmentObj.isNull("author_name") || attachmentObj.isNull("author_link")
|| attachmentObj.isNull("author_icon")) {
authorBox.setVisibility(GONE);
return;
}
authorBox.setVisibility(VISIBLE);
loadImage(attachmentObj.getString("author_icon"),
(ImageView) attachmentView.findViewById(R.id.author_icon));
final TextView authorName = (TextView) attachmentView.findViewById(R.id.author_name);
authorName.setText(attachmentObj.getString("author_name"));
final String link = absolutize(attachmentObj.getString("author_link"));
authorName.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(intent);
}
});
// timestamp and link - need to format time
}
private void showTitleAttachment(JSONObject attachmentObj, View attachmentView)
throws JSONException {
TextView titleView = (TextView) attachmentView.findViewById(R.id.title);
if (attachmentObj.isNull("title")) {
titleView.setVisibility(View.GONE);
return;
}
titleView.setVisibility(View.VISIBLE);
titleView.setText(attachmentObj.getString("title"));
if (attachmentObj.isNull("title_link")) {
titleView.setOnClickListener(null);
titleView.setClickable(false);
} else {
titleView.setVisibility(View.VISIBLE);
titleView.setText(attachmentObj.getString("title"));
if (attachmentObj.isNull("title_link")) {
titleView.setOnClickListener(null);
titleView.setClickable(false);
} else {
final String link = absolutize(attachmentObj.getString("title_link"));
titleView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(intent);
}
});
}
final String link = absolutize(attachmentObj.getString("title_link"));
titleView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(intent);
}
});
TextViewCompat.setTextAppearance(titleView,
R.style.TextAppearance_RocketChat_MessageAttachment_Title_Link);
}
}
addView(attachmentView);
private void showReferenceAttachment(JSONObject attachmentObj, View attachmentView)
throws JSONException {
final View refBox = attachmentView.findViewById(R.id.ref_box);
if (attachmentObj.isNull("thumb_url") && attachmentObj.isNull("text")) {
refBox.setVisibility(GONE);
return;
}
refBox.setVisibility(VISIBLE);
final ImageView thumbImage = (ImageView) refBox.findViewById(R.id.thumb);
final String thumbUrl = attachmentObj.optString("thumb_url");
if (TextUtils.isEmpty(thumbUrl)) {
thumbImage.setVisibility(GONE);
} else {
thumbImage.setVisibility(VISIBLE);
loadImage(thumbUrl, thumbImage);
}
final TextView refText = (TextView) refBox.findViewById(R.id.text);
final String refString = attachmentObj.optString("text");
if (TextUtils.isEmpty(refString)) {
refText.setVisibility(GONE);
} else {
refText.setVisibility(VISIBLE);
refText.setText(refString);
}
}
private void showImageAttachment(JSONObject attachmentObj, View attachmentView)
throws JSONException {
final ImageView attachedImage = (ImageView) attachmentView.findViewById(R.id.image);
if (attachmentObj.isNull("image_url")) {
attachedImage.setVisibility(GONE);
return;
}
attachedImage.setVisibility(VISIBLE);
loadImage(attachmentObj.getString("image_url"), attachedImage);
}
private void showFieldsAttachment(JSONObject attachmentObj, View attachmentView)
throws JSONException {
if (attachmentObj.isNull("fields")) {
return;
}
final ViewGroup attachmentContent =
(ViewGroup) attachmentView.findViewById(R.id.attachment_content);
final JSONArray fields = attachmentObj.getJSONArray("fields");
for (int i = 0, size = fields.length(); i < size; i++) {
final JSONObject fieldObject = fields.getJSONObject(i);
if (fieldObject.isNull("title") || fieldObject.isNull("value")) {
return;
}
MessageAttachmentFieldLayout fieldLayout = new MessageAttachmentFieldLayout(getContext());
fieldLayout.setTitle(fieldObject.getString("title"));
fieldLayout.setValue(fieldObject.getString("value"));
attachmentContent.addView(fieldLayout);
}
}
private String absolutize(String url) {
return url.startsWith("/") ? "https://" + hostname + url : url;
}
private void loadImage(String url, ImageView imageView) {
new Picasso.Builder(getContext())
.downloader(getDownloader())
.build()
.load(absolutize(url))
.placeholder(VectorDrawableCompat.create(getResources(), R.drawable.image_dummy, null))
.error(VectorDrawableCompat.create(getResources(), R.drawable.image_error, null))
.into(imageView);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_margin="4dp"
android:padding="4dp">
android:paddingTop="4dp"
android:paddingBottom="4dp">
<View
android:id="@+id/attachment_strip"
android:layout_width="3dp"
android:layout_height="match_parent"
android:layout_marginRight="5dp"
android:background="@color/inline_attachment_quote_line" />
<LinearLayout
android:id="@+id/attachment_content"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:id="@+id/author_box"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
<ImageView
android:id="@+id/author_icon"
android:layout_width="16dp"
android:layout_height="16dp"
tools:src="@drawable/circle_black" />
<android.support.v4.widget.Space
android:layout_width="8dp"
android:layout_height="8dp" />
<TextView
android:id="@+id/author_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.RocketChat.MessageAttachment.Title.Link"
tools:text="Bradley Hilton" />
<android.support.v4.widget.Space
android:layout_width="8dp"
android:layout_height="8dp" />
<TextView
android:id="@+id/timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="14:53" />
</LinearLayout>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:layout_marginBottom="8dp"
android:textAppearance="@style/TextAppearance.RocketChat.MessageAttachment.Title"
android:background="?attr/selectableItemBackground" />
android:background="?attr/selectableItemBackground"
tools:text="Attachment Example" />
<LinearLayout
android:id="@+id/ref_box"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
<ImageView
android:id="@+id/thumb"
android:layout_width="32dp"
android:layout_height="32dp"
tools:src="@drawable/circle_black" />
<android.support.v4.widget.Space
android:layout_width="8dp"
android:layout_height="8dp" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Bradley Hilton" />
</LinearLayout>
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxHeight="200dp"
android:layout_marginTop="4dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:adjustViewBounds="true"
android:scaleType="fitStart" />
<!-- audio -->
<!-- video -->
</LinearLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/field_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textColor="@android:color/black"
tools:text="Test" />
<TextView
android:id="@+id/field_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Test" />
</LinearLayout>
\ No newline at end of file
......@@ -17,6 +17,9 @@
<style name="TextAppearance.RocketChat.MessageAttachment.Title" parent="TextAppearance.AppCompat.Title">
<item name="android:textSize">14sp</item>
</style>
<style name="TextAppearance.RocketChat.MessageAttachment.Title.Link" parent="TextAppearance.RocketChat.MessageAttachment.Title">
<item name="android:textColor">?android:attr/textColorLink</item>
</style>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment