mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 12:49:55 +01:00
[android] Use a nested RecyclerView with a single items to show image attachments
This is preparation for showing multiple image attachments in one message bubble.
This commit is contained in:
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.conversation;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.LayoutRes;
|
import android.support.annotation.LayoutRes;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView.RecycledViewPool;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -21,11 +22,14 @@ class ConversationAdapter
|
|||||||
extends BriarAdapter<ConversationItem, ConversationItemViewHolder> {
|
extends BriarAdapter<ConversationItem, ConversationItemViewHolder> {
|
||||||
|
|
||||||
private ConversationListener listener;
|
private ConversationListener listener;
|
||||||
|
private final RecycledViewPool imageViewPool;
|
||||||
|
|
||||||
ConversationAdapter(Context ctx,
|
ConversationAdapter(Context ctx,
|
||||||
ConversationListener conversationListener) {
|
ConversationListener conversationListener) {
|
||||||
super(ctx, ConversationItem.class);
|
super(ctx, ConversationItem.class);
|
||||||
listener = conversationListener;
|
listener = conversationListener;
|
||||||
|
// This shares the same pool for view recycling between all image lists
|
||||||
|
imageViewPool = new RecycledViewPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
@LayoutRes
|
@LayoutRes
|
||||||
@@ -42,15 +46,17 @@ class ConversationAdapter
|
|||||||
type, viewGroup, false);
|
type, viewGroup, false);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case R.layout.list_item_conversation_msg_in:
|
case R.layout.list_item_conversation_msg_in:
|
||||||
return new ConversationMessageViewHolder(v, true);
|
return new ConversationMessageViewHolder(v, listener, true,
|
||||||
|
imageViewPool);
|
||||||
case R.layout.list_item_conversation_msg_out:
|
case R.layout.list_item_conversation_msg_out:
|
||||||
return new ConversationMessageViewHolder(v, false);
|
return new ConversationMessageViewHolder(v, listener, false,
|
||||||
|
imageViewPool);
|
||||||
case R.layout.list_item_conversation_notice_in:
|
case R.layout.list_item_conversation_notice_in:
|
||||||
return new ConversationNoticeViewHolder(v, true);
|
return new ConversationNoticeViewHolder(v, listener, true);
|
||||||
case R.layout.list_item_conversation_notice_out:
|
case R.layout.list_item_conversation_notice_out:
|
||||||
return new ConversationNoticeViewHolder(v, false);
|
return new ConversationNoticeViewHolder(v, listener, false);
|
||||||
case R.layout.list_item_conversation_request:
|
case R.layout.list_item_conversation_request:
|
||||||
return new ConversationRequestViewHolder(v, true);
|
return new ConversationRequestViewHolder(v, listener, true);
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown ConversationItem");
|
throw new IllegalArgumentException("Unknown ConversationItem");
|
||||||
}
|
}
|
||||||
@@ -59,7 +65,7 @@ class ConversationAdapter
|
|||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(ConversationItemViewHolder ui, int position) {
|
public void onBindViewHolder(ConversationItemViewHolder ui, int position) {
|
||||||
ConversationItem item = items.get(position);
|
ConversationItem item = items.get(position);
|
||||||
ui.bind(item, listener);
|
ui.bind(item);
|
||||||
listener.onItemVisible(item);
|
listener.onItemVisible(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,14 +18,17 @@ import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
abstract class ConversationItemViewHolder extends ViewHolder {
|
abstract class ConversationItemViewHolder extends ViewHolder {
|
||||||
|
|
||||||
|
protected final ConversationListener listener;
|
||||||
protected final ConstraintLayout layout;
|
protected final ConstraintLayout layout;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final OutItemViewHolder outViewHolder;
|
private final OutItemViewHolder outViewHolder;
|
||||||
private final TextView text;
|
private final TextView text;
|
||||||
protected final TextView time;
|
protected final TextView time;
|
||||||
|
|
||||||
ConversationItemViewHolder(View v, boolean isIncoming) {
|
ConversationItemViewHolder(View v, ConversationListener listener,
|
||||||
|
boolean isIncoming) {
|
||||||
super(v);
|
super(v);
|
||||||
|
this.listener = listener;
|
||||||
this.outViewHolder = isIncoming ? null : new OutItemViewHolder(v);
|
this.outViewHolder = isIncoming ? null : new OutItemViewHolder(v);
|
||||||
layout = v.findViewById(R.id.layout);
|
layout = v.findViewById(R.id.layout);
|
||||||
text = v.findViewById(R.id.text);
|
text = v.findViewById(R.id.text);
|
||||||
@@ -33,7 +36,7 @@ abstract class ConversationItemViewHolder extends ViewHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
void bind(ConversationItem item, ConversationListener listener) {
|
void bind(ConversationItem item) {
|
||||||
if (item.getText() != null) {
|
if (item.getText() != null) {
|
||||||
text.setText(trim(item.getText()));
|
text.setText(trim(item.getText()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,64 +1,47 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.conversation;
|
||||||
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.support.annotation.DrawableRes;
|
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.constraint.ConstraintSet;
|
import android.support.constraint.ConstraintSet;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.support.v7.widget.GridLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.support.v7.widget.RecyclerView.RecycledViewPool;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
import com.bumptech.glide.load.Transformation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.conversation.glide.BriarImageTransformation;
|
|
||||||
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.support.constraint.ConstraintSet.WRAP_CONTENT;
|
||||||
import static android.support.v4.view.ViewCompat.LAYOUT_DIRECTION_RTL;
|
|
||||||
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
|
||||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ConversationMessageViewHolder extends ConversationItemViewHolder {
|
class ConversationMessageViewHolder extends ConversationItemViewHolder {
|
||||||
|
|
||||||
@DrawableRes
|
private final ImageAdapter adapter;
|
||||||
private static final int ERROR_RES = R.drawable.ic_image_broken;
|
|
||||||
|
|
||||||
private final ImageView imageView;
|
|
||||||
private final ViewGroup statusLayout;
|
private final ViewGroup statusLayout;
|
||||||
private final int timeColor, timeColorBubble;
|
private final int timeColor, timeColorBubble;
|
||||||
private final int radiusBig, radiusSmall;
|
|
||||||
private final boolean isRtl;
|
|
||||||
private final ConstraintSet textConstraints = new ConstraintSet();
|
private final ConstraintSet textConstraints = new ConstraintSet();
|
||||||
private final ConstraintSet imageConstraints = new ConstraintSet();
|
private final ConstraintSet imageConstraints = new ConstraintSet();
|
||||||
private final ConstraintSet imageTextConstraints = new ConstraintSet();
|
private final ConstraintSet imageTextConstraints = new ConstraintSet();
|
||||||
|
|
||||||
ConversationMessageViewHolder(View v, boolean isIncoming) {
|
ConversationMessageViewHolder(View v, ConversationListener listener,
|
||||||
super(v, isIncoming);
|
boolean isIncoming, RecycledViewPool imageViewPool) {
|
||||||
imageView = v.findViewById(R.id.imageView);
|
super(v, listener, isIncoming);
|
||||||
statusLayout = v.findViewById(R.id.statusLayout);
|
statusLayout = v.findViewById(R.id.statusLayout);
|
||||||
radiusBig = v.getContext().getResources()
|
|
||||||
.getDimensionPixelSize(R.dimen.message_bubble_radius_big);
|
// image list
|
||||||
radiusSmall = v.getContext().getResources()
|
RecyclerView list = v.findViewById(R.id.imageView);
|
||||||
.getDimensionPixelSize(R.dimen.message_bubble_radius_small);
|
list.setRecycledViewPool(imageViewPool);
|
||||||
|
list.setLayoutManager(new GridLayoutManager(v.getContext(), 2));
|
||||||
|
adapter = new ImageAdapter(listener);
|
||||||
|
list.setAdapter(adapter);
|
||||||
|
|
||||||
// remember original status text color
|
// remember original status text color
|
||||||
timeColor = time.getCurrentTextColor();
|
timeColor = time.getCurrentTextColor();
|
||||||
timeColorBubble =
|
timeColorBubble =
|
||||||
ContextCompat.getColor(v.getContext(), R.color.briar_white);
|
ContextCompat.getColor(v.getContext(), R.color.briar_white);
|
||||||
|
|
||||||
// find out if we are showing a RTL language, Use the configuration,
|
|
||||||
// because getting the layout direction of views is not reliable
|
|
||||||
Configuration config =
|
|
||||||
imageView.getContext().getResources().getConfiguration();
|
|
||||||
isRtl = SDK_INT >= 17 &&
|
|
||||||
config.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
|
|
||||||
|
|
||||||
// clone constraint sets from layout files
|
// clone constraint sets from layout files
|
||||||
textConstraints
|
textConstraints
|
||||||
.clone(v.getContext(), R.layout.list_item_conversation_msg_in);
|
.clone(v.getContext(), R.layout.list_item_conversation_msg_in);
|
||||||
@@ -77,32 +60,24 @@ class ConversationMessageViewHolder extends ConversationItemViewHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void bind(ConversationItem conversationItem,
|
void bind(ConversationItem conversationItem) {
|
||||||
ConversationListener listener) {
|
super.bind(conversationItem);
|
||||||
super.bind(conversationItem, listener);
|
|
||||||
ConversationMessageItem item =
|
ConversationMessageItem item =
|
||||||
(ConversationMessageItem) conversationItem;
|
(ConversationMessageItem) conversationItem;
|
||||||
if (item.getAttachments().isEmpty()) {
|
if (item.getAttachments().isEmpty()) {
|
||||||
bindTextItem();
|
bindTextItem();
|
||||||
} else {
|
} else {
|
||||||
bindImageItem(item, listener);
|
bindImageItem(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindTextItem() {
|
private void bindTextItem() {
|
||||||
clearImage();
|
resetStatusLayoutForText();
|
||||||
statusLayout.setBackgroundResource(0);
|
|
||||||
// also reset padding (the background drawable defines some)
|
|
||||||
statusLayout.setPadding(0, 0, 0, 0);
|
|
||||||
time.setTextColor(timeColor);
|
|
||||||
textConstraints.applyTo(layout);
|
textConstraints.applyTo(layout);
|
||||||
|
adapter.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindImageItem(ConversationMessageItem item,
|
private void bindImageItem(ConversationMessageItem item) {
|
||||||
ConversationListener listener) {
|
|
||||||
// TODO show more than just the first image
|
|
||||||
AttachmentItem attachment = item.getAttachments().get(0);
|
|
||||||
|
|
||||||
ConstraintSet constraintSet;
|
ConstraintSet constraintSet;
|
||||||
if (item.getText() == null) {
|
if (item.getText() == null) {
|
||||||
statusLayout
|
statusLayout
|
||||||
@@ -110,52 +85,30 @@ class ConversationMessageViewHolder extends ConversationItemViewHolder {
|
|||||||
time.setTextColor(timeColorBubble);
|
time.setTextColor(timeColorBubble);
|
||||||
constraintSet = imageConstraints;
|
constraintSet = imageConstraints;
|
||||||
} else {
|
} else {
|
||||||
statusLayout.setBackgroundResource(0);
|
resetStatusLayoutForText();
|
||||||
// also reset padding (the background drawable defines some)
|
|
||||||
statusLayout.setPadding(0, 0, 0, 0);
|
|
||||||
time.setTextColor(timeColor);
|
|
||||||
constraintSet = imageTextConstraints;
|
constraintSet = imageTextConstraints;
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply image size constraints, so glides picks them up for scaling
|
if (item.getAttachments().size() == 1) {
|
||||||
int width = attachment.getThumbnailWidth();
|
// apply image size constraints for a single image
|
||||||
int height = attachment.getThumbnailHeight();
|
AttachmentItem attachment = item.getAttachments().get(0);
|
||||||
constraintSet.constrainWidth(R.id.imageView, width);
|
int width = attachment.getThumbnailWidth();
|
||||||
constraintSet.constrainHeight(R.id.imageView, height);
|
int height = attachment.getThumbnailHeight();
|
||||||
constraintSet.applyTo(layout);
|
constraintSet.constrainWidth(R.id.imageView, width);
|
||||||
|
constraintSet.constrainHeight(R.id.imageView, height);
|
||||||
if (attachment.hasError()) {
|
|
||||||
clearImage();
|
|
||||||
imageView.setImageResource(ERROR_RES);
|
|
||||||
} else {
|
} else {
|
||||||
loadImage(item, attachment, listener);
|
constraintSet.constrainWidth(R.id.imageView, WRAP_CONTENT);
|
||||||
|
constraintSet.constrainHeight(R.id.imageView, WRAP_CONTENT);
|
||||||
}
|
}
|
||||||
|
constraintSet.applyTo(layout);
|
||||||
|
adapter.setConversationItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearImage() {
|
private void resetStatusLayoutForText() {
|
||||||
GlideApp.with(imageView)
|
statusLayout.setBackgroundResource(0);
|
||||||
.clear(imageView);
|
// also reset padding (the background drawable defines some)
|
||||||
imageView.setOnClickListener(null);
|
statusLayout.setPadding(0, 0, 0, 0);
|
||||||
}
|
time.setTextColor(timeColor);
|
||||||
|
|
||||||
private void loadImage(ConversationMessageItem item,
|
|
||||||
AttachmentItem attachment, ConversationListener listener) {
|
|
||||||
boolean leftCornerSmall =
|
|
||||||
(isIncoming() && !isRtl) || (!isIncoming() && isRtl);
|
|
||||||
boolean bottomRound = item.getText() == null;
|
|
||||||
Transformation<Bitmap> transformation = new BriarImageTransformation(
|
|
||||||
radiusSmall, radiusBig, leftCornerSmall, bottomRound);
|
|
||||||
|
|
||||||
GlideApp.with(imageView)
|
|
||||||
.load(attachment)
|
|
||||||
.diskCacheStrategy(NONE)
|
|
||||||
.error(ERROR_RES)
|
|
||||||
.transform(transformation)
|
|
||||||
.transition(withCrossFade())
|
|
||||||
.into(imageView)
|
|
||||||
.waitForLayout();
|
|
||||||
imageView.setOnClickListener(
|
|
||||||
view -> listener.onAttachmentClicked(view, item, attachment));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,16 +19,17 @@ class ConversationNoticeViewHolder extends ConversationItemViewHolder {
|
|||||||
|
|
||||||
private final TextView msgText;
|
private final TextView msgText;
|
||||||
|
|
||||||
ConversationNoticeViewHolder(View v, boolean isIncoming) {
|
ConversationNoticeViewHolder(View v, ConversationListener listener,
|
||||||
super(v, isIncoming);
|
boolean isIncoming) {
|
||||||
|
super(v, listener, isIncoming);
|
||||||
msgText = v.findViewById(R.id.msgText);
|
msgText = v.findViewById(R.id.msgText);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@CallSuper
|
@CallSuper
|
||||||
void bind(ConversationItem item, ConversationListener listener) {
|
void bind(ConversationItem item) {
|
||||||
ConversationNoticeItem notice = (ConversationNoticeItem) item;
|
ConversationNoticeItem notice = (ConversationNoticeItem) item;
|
||||||
super.bind(notice, listener);
|
super.bind(notice);
|
||||||
|
|
||||||
String text = notice.getMsgText();
|
String text = notice.getMsgText();
|
||||||
if (isNullOrEmpty(text)) {
|
if (isNullOrEmpty(text)) {
|
||||||
|
|||||||
@@ -17,16 +17,17 @@ class ConversationRequestViewHolder extends ConversationNoticeViewHolder {
|
|||||||
private final Button acceptButton;
|
private final Button acceptButton;
|
||||||
private final Button declineButton;
|
private final Button declineButton;
|
||||||
|
|
||||||
ConversationRequestViewHolder(View v, boolean isIncoming) {
|
ConversationRequestViewHolder(View v, ConversationListener listener,
|
||||||
super(v, isIncoming);
|
boolean isIncoming) {
|
||||||
|
super(v, listener, isIncoming);
|
||||||
acceptButton = v.findViewById(R.id.acceptButton);
|
acceptButton = v.findViewById(R.id.acceptButton);
|
||||||
declineButton = v.findViewById(R.id.declineButton);
|
declineButton = v.findViewById(R.id.declineButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void bind(ConversationItem item, ConversationListener listener) {
|
void bind(ConversationItem item) {
|
||||||
ConversationRequestItem request = (ConversationRequestItem) item;
|
ConversationRequestItem request = (ConversationRequestItem) item;
|
||||||
super.bind(request, listener);
|
super.bind(request);
|
||||||
|
|
||||||
if (request.wasAnswered() && request.canBeOpened()) {
|
if (request.wasAnswered() && request.canBeOpened()) {
|
||||||
acceptButton.setVisibility(VISIBLE);
|
acceptButton.setVisibility(VISIBLE);
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package org.briarproject.briar.android.conversation;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.widget.RecyclerView.Adapter;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class ImageAdapter extends Adapter<ImageViewHolder> {
|
||||||
|
|
||||||
|
private final static int TYPE_SINGLE = 0;
|
||||||
|
private final static int TYPE_MULTIPLE = 1;
|
||||||
|
|
||||||
|
private final List<AttachmentItem> items = new ArrayList<>();
|
||||||
|
private final ConversationListener listener;
|
||||||
|
@Nullable
|
||||||
|
private ConversationMessageItem conversationItem;
|
||||||
|
|
||||||
|
public ImageAdapter(ConversationListener listener) {
|
||||||
|
super();
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
return items.size() == 1 ? TYPE_SINGLE : TYPE_MULTIPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
|
||||||
|
View v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||||
|
R.layout.list_item_image, viewGroup, false);
|
||||||
|
return type == TYPE_SINGLE ? new SingleImageViewHolder(v) :
|
||||||
|
new ImageViewHolder(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(ImageViewHolder imageViewHolder,
|
||||||
|
int position) {
|
||||||
|
requireNonNull(conversationItem);
|
||||||
|
AttachmentItem item = items.get(position);
|
||||||
|
imageViewHolder.itemView.setOnClickListener(v ->
|
||||||
|
listener.onAttachmentClicked(v, conversationItem, item)
|
||||||
|
);
|
||||||
|
if (imageViewHolder instanceof SingleImageViewHolder) {
|
||||||
|
boolean isIncoming = conversationItem.isIncoming();
|
||||||
|
boolean hasText = conversationItem.getText() != null;
|
||||||
|
((SingleImageViewHolder) imageViewHolder)
|
||||||
|
.bind(item, isIncoming, hasText);
|
||||||
|
} else {
|
||||||
|
imageViewHolder.bind(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setConversationItem(ConversationMessageItem item) {
|
||||||
|
this.conversationItem = item;
|
||||||
|
this.items.clear();
|
||||||
|
this.items.addAll(item.getAttachments());
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
items.clear();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package org.briarproject.briar.android.conversation;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.support.annotation.DrawableRes;
|
||||||
|
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.Transformation;
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
||||||
|
|
||||||
|
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
||||||
|
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class ImageViewHolder extends ViewHolder {
|
||||||
|
|
||||||
|
@DrawableRes
|
||||||
|
private static final int ERROR_RES = R.drawable.ic_image_broken;
|
||||||
|
|
||||||
|
protected final ImageView imageView;
|
||||||
|
protected Transformation<Bitmap> transformation = new CenterCrop();
|
||||||
|
|
||||||
|
public ImageViewHolder(View v) {
|
||||||
|
super(v);
|
||||||
|
imageView = v.findViewById(R.id.imageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind(AttachmentItem attachment) {
|
||||||
|
if (attachment.hasError()) {
|
||||||
|
GlideApp.with(imageView)
|
||||||
|
.clear(imageView);
|
||||||
|
imageView.setImageResource(ERROR_RES);
|
||||||
|
} else {
|
||||||
|
loadImage(attachment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadImage(AttachmentItem a) {
|
||||||
|
GlideApp.with(imageView)
|
||||||
|
.load(a)
|
||||||
|
.diskCacheStrategy(NONE)
|
||||||
|
.error(ERROR_RES)
|
||||||
|
.transform(transformation)
|
||||||
|
.transition(withCrossFade())
|
||||||
|
.into(imageView)
|
||||||
|
.waitForLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package org.briarproject.briar.android.conversation;
|
||||||
|
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup.LayoutParams;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.conversation.glide.BriarImageTransformation;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static android.support.v4.view.ViewCompat.LAYOUT_DIRECTION_RTL;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class SingleImageViewHolder extends ImageViewHolder {
|
||||||
|
|
||||||
|
private final int radiusBig, radiusSmall;
|
||||||
|
private final boolean isRtl;
|
||||||
|
|
||||||
|
public SingleImageViewHolder(View v) {
|
||||||
|
super(v);
|
||||||
|
radiusBig = v.getContext().getResources()
|
||||||
|
.getDimensionPixelSize(R.dimen.message_bubble_radius_big);
|
||||||
|
radiusSmall = v.getContext().getResources()
|
||||||
|
.getDimensionPixelSize(R.dimen.message_bubble_radius_small);
|
||||||
|
|
||||||
|
// find out if we are showing a RTL language, Use the configuration,
|
||||||
|
// because getting the layout direction of views is not reliable
|
||||||
|
Configuration config = v.getContext().getResources().getConfiguration();
|
||||||
|
isRtl = SDK_INT >= 17 &&
|
||||||
|
config.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind(AttachmentItem a, boolean isIncoming, boolean hasText) {
|
||||||
|
if (!a.hasError()) beforeLoadingImage(a, isIncoming, hasText);
|
||||||
|
super.bind(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void beforeLoadingImage(AttachmentItem a, boolean isIncoming,
|
||||||
|
boolean hasText) {
|
||||||
|
// apply image size constraints, so glides picks them up for scaling
|
||||||
|
LayoutParams layoutParams =
|
||||||
|
new LayoutParams(a.getThumbnailWidth(), a.getThumbnailHeight());
|
||||||
|
imageView.setLayoutParams(layoutParams);
|
||||||
|
|
||||||
|
boolean leftCornerSmall =
|
||||||
|
(isIncoming && !isRtl) || (!isIncoming && isRtl);
|
||||||
|
boolean bottomRound = !hasText;
|
||||||
|
transformation = new BriarImageTransformation(radiusSmall, radiusBig,
|
||||||
|
leftCornerSmall, bottomRound);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
android:background="@drawable/msg_in"
|
android:background="@drawable/msg_in"
|
||||||
android:elevation="@dimen/message_bubble_elevation">
|
android:elevation="@dimen/message_bubble_elevation">
|
||||||
|
|
||||||
<ImageView
|
<android.support.v7.widget.RecyclerView
|
||||||
android:id="@+id/imageView"
|
android:id="@+id/imageView"
|
||||||
android:layout_width="@dimen/message_bubble_image_default"
|
android:layout_width="@dimen/message_bubble_image_default"
|
||||||
android:layout_height="@dimen/message_bubble_image_default"
|
android:layout_height="@dimen/message_bubble_image_default"
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
|
tools:listitem="@layout/list_item_image"
|
||||||
tools:src="@drawable/alerts_and_states_error"/>
|
tools:src="@drawable/alerts_and_states_error"/>
|
||||||
|
|
||||||
<com.vanniktech.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
android:background="@drawable/msg_in"
|
android:background="@drawable/msg_in"
|
||||||
android:elevation="@dimen/message_bubble_elevation">
|
android:elevation="@dimen/message_bubble_elevation">
|
||||||
|
|
||||||
<ImageView
|
<android.support.v7.widget.RecyclerView
|
||||||
android:id="@+id/imageView"
|
android:id="@+id/imageView"
|
||||||
android:layout_width="@dimen/message_bubble_image_default"
|
android:layout_width="@dimen/message_bubble_image_default"
|
||||||
android:layout_height="@dimen/message_bubble_image_default"
|
android:layout_height="@dimen/message_bubble_image_default"
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
|
tools:listitem="@layout/list_item_image"
|
||||||
tools:src="@drawable/alerts_and_states_error"/>
|
tools:src="@drawable/alerts_and_states_error"/>
|
||||||
|
|
||||||
<com.vanniktech.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
android:background="@drawable/msg_in"
|
android:background="@drawable/msg_in"
|
||||||
android:elevation="@dimen/message_bubble_elevation">
|
android:elevation="@dimen/message_bubble_elevation">
|
||||||
|
|
||||||
<ImageView
|
<android.support.v7.widget.RecyclerView
|
||||||
android:id="@+id/imageView"
|
android:id="@+id/imageView"
|
||||||
android:layout_width="@dimen/message_bubble_image_default"
|
android:layout_width="@dimen/message_bubble_image_default"
|
||||||
android:layout_height="@dimen/message_bubble_image_default"
|
android:layout_height="@dimen/message_bubble_image_default"
|
||||||
@@ -23,7 +23,8 @@
|
|||||||
app:layout_constraintBottom_toTopOf="@+id/text"
|
app:layout_constraintBottom_toTopOf="@+id/text"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription"
|
||||||
|
tools:listitem="@layout/list_item_image"/>
|
||||||
|
|
||||||
<com.vanniktech.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
android:background="@drawable/msg_out"
|
android:background="@drawable/msg_out"
|
||||||
android:elevation="@dimen/message_bubble_elevation">
|
android:elevation="@dimen/message_bubble_elevation">
|
||||||
|
|
||||||
<ImageView
|
<android.support.v7.widget.RecyclerView
|
||||||
android:id="@+id/imageView"
|
android:id="@+id/imageView"
|
||||||
android:layout_width="@dimen/message_bubble_image_default"
|
android:layout_width="@dimen/message_bubble_image_default"
|
||||||
android:layout_height="@dimen/message_bubble_image_default"
|
android:layout_height="@dimen/message_bubble_image_default"
|
||||||
|
|||||||
9
briar-android/src/main/res/layout/list_item_image.xml
Normal file
9
briar-android/src/main/res/layout/list_item_image.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageView"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="@dimen/message_bubble_image_default"
|
||||||
|
android:layout_height="@dimen/message_bubble_image_default"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:src="@drawable/alerts_and_states_error"/>
|
||||||
Reference in New Issue
Block a user