Commit e6229a3a authored by Torsten Grote's avatar Torsten Grote

[android] Factor out image preview into its own view class

parent 5fbacb4e
Pipeline #2859 passed with stage
in 11 minutes and 1 second
......@@ -63,6 +63,7 @@ import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.introduction.IntroductionActivity;
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.android.view.ImagePreview;
import org.briarproject.briar.android.view.TextAttachmentController;
import org.briarproject.briar.android.view.TextAttachmentController.AttachImageListener;
import org.briarproject.briar.android.view.TextInputView;
......@@ -259,8 +260,9 @@ public class ConversationActivity extends BriarActivity
textInputView = findViewById(R.id.text_input_container);
if (FEATURE_FLAG_IMAGE_ATTACHMENTS) {
sendController = new TextAttachmentController(textInputView, this,
this, getWindowManager());
ImagePreview imagePreview = findViewById(R.id.imagePreview);
sendController = new TextAttachmentController(textInputView,
imagePreview, this, this);
} else {
sendController = new TextSendController(textInputView, this, false);
}
......
package org.briarproject.briar.android.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.support.constraint.ConstraintLayout;
import android.support.v7.graphics.Palette;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.Toast;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.conversation.glide.GlideApp;
import java.util.List;
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
import static android.graphics.Color.BLACK;
import static android.graphics.Color.WHITE;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_YES;
import static android.support.v7.app.AppCompatDelegate.getDefaultNightMode;
import static android.widget.Toast.LENGTH_LONG;
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
import static com.bumptech.glide.load.resource.bitmap.DownsampleStrategy.FIT_CENTER;
import static java.util.Objects.requireNonNull;
@NotNullByDefault
public class ImagePreview extends ConstraintLayout {
private final ImageView imageView;
private final int backgroundColor =
getDefaultNightMode() == MODE_NIGHT_YES ? BLACK : WHITE;
@Nullable
private ImagePreviewListener listener;
public ImagePreview(Context context) {
this(context, null);
}
public ImagePreview(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ImagePreview(Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
// inflate layout
LayoutInflater inflater = (LayoutInflater) requireNonNull(
context.getSystemService(LAYOUT_INFLATER_SERVICE));
inflater.inflate(R.layout.image_preview, this, true);
// find image view and set background color
imageView = findViewById(R.id.imageView);
imageView.setBackgroundColor(backgroundColor);
// set cancel listener
findViewById(R.id.imageCancelButton).setOnClickListener(view -> {
if (listener != null) listener.onCancel();
});
}
void setImagePreviewListener(ImagePreviewListener listener) {
this.listener = listener;
}
void showPreview(List<Uri> imageUris) {
setVisibility(VISIBLE);
GlideApp.with(imageView)
.asBitmap()
.load(imageUris.get(0)) // TODO show more than the first
.diskCacheStrategy(NONE)
.downsample(FIT_CENTER)
.addListener(new RequestListener<Bitmap>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e,
Object model, Target<Bitmap> target,
boolean isFirstResource) {
if (listener != null) listener.onCancel();
Toast.makeText(imageView.getContext(),
R.string.image_attach_error, LENGTH_LONG)
.show();
return false;
}
@Override
public boolean onResourceReady(Bitmap resource,
Object model, Target<Bitmap> target,
DataSource dataSource, boolean isFirstResource) {
Palette.from(resource).generate(
ImagePreview.this::onPaletteGenerated);
return false;
}
})
.into(imageView);
}
void onPaletteGenerated(@Nullable Palette palette) {
if (palette == null) return;
int color = getDefaultNightMode() == MODE_NIGHT_YES ?
palette.getDarkMutedColor(backgroundColor) :
palette.getLightMutedColor(backgroundColor);
imageView.setBackgroundColor(color);
}
interface ImagePreviewListener {
void onCancel();
}
}
......@@ -2,31 +2,17 @@ package org.briarproject.briar.android.view;
import android.content.ClipData;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.AbsSavedState;
import android.support.v7.graphics.Palette;
import android.support.v7.widget.AppCompatImageButton;
import android.util.DisplayMetrics;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import org.briarproject.briar.R;
import org.briarproject.briar.android.conversation.glide.GlideApp;
import org.briarproject.briar.android.view.ImagePreview.ImagePreviewListener;
import java.util.ArrayList;
import java.util.List;
......@@ -35,59 +21,39 @@ import static android.content.Intent.ACTION_GET_CONTENT;
import static android.content.Intent.ACTION_OPEN_DOCUMENT;
import static android.content.Intent.CATEGORY_OPENABLE;
import static android.content.Intent.EXTRA_ALLOW_MULTIPLE;
import static android.graphics.Color.BLACK;
import static android.graphics.Color.WHITE;
import static android.os.Build.VERSION.SDK_INT;
import static android.support.v4.view.AbsSavedState.EMPTY_STATE;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_YES;
import static android.support.v7.app.AppCompatDelegate.getDefaultNightMode;
import static android.view.View.GONE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_LONG;
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
import static com.bumptech.glide.load.resource.bitmap.DownsampleStrategy.FIT_CENTER;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
@UiThread
public class TextAttachmentController extends TextSendController {
public class TextAttachmentController extends TextSendController
implements ImagePreviewListener {
private final AppCompatImageButton imageButton;
private final ViewGroup imageLayout;
private final ImageView imageView;
private final ImagePreview imagePreview;
private final AttachImageListener imageListener;
private CharSequence textHint;
private List<Uri> imageUris = emptyList();
public TextAttachmentController(TextInputView v, SendListener listener,
AttachImageListener imageListener, WindowManager windowManager) {
public TextAttachmentController(TextInputView v, ImagePreview imagePreview,
SendListener listener, AttachImageListener imageListener) {
super(v, listener, false);
this.imageListener = imageListener;
this.imagePreview = imagePreview;
this.imagePreview.setImagePreviewListener(this);
imageLayout = v.findViewById(R.id.imageLayout);
imageView = v.findViewById(R.id.imageView);
FloatingActionButton imageCancelButton =
v.findViewById(R.id.imageCancelButton);
imageButton = v.findViewById(R.id.imageButton);
imageButton.setOnClickListener(view -> onImageButtonClicked());
textHint = textInput.getHint();
imageButton.setOnClickListener(view -> onImageButtonClicked());
imageCancelButton.setOnClickListener(view -> {
textInput.clearText();
reset();
});
// set preview size based on screen height
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
LayoutParams layoutParams = imageView.getLayoutParams();
layoutParams.height = displayMetrics.heightPixels / 4;
imageView.setLayoutParams(layoutParams);
// show image button
showImageButton(true);
}
......@@ -139,47 +105,7 @@ public class TextAttachmentController extends TextSendController {
if (imageUris.isEmpty()) return;
showImageButton(false);
textInput.setHint(R.string.image_caption_hint);
imageLayout.setVisibility(VISIBLE);
GlideApp.with(imageView)
.asBitmap()
.load(imageUris.get(0)) // TODO show more than the first
.diskCacheStrategy(NONE)
.downsample(FIT_CENTER)
.addListener(new RequestListener<Bitmap>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e,
Object model, Target<Bitmap> target,
boolean isFirstResource) {
reset();
Toast.makeText(imageView.getContext(),
R.string.image_attach_error, LENGTH_LONG)
.show();
return false;
}
@Override
public boolean onResourceReady(Bitmap resource,
Object model, Target<Bitmap> target,
DataSource dataSource, boolean isFirstResource) {
Palette.from(resource).generate(
TextAttachmentController.this::onPaletteGenerated);
return false;
}
})
.into(imageView);
}
@UiThread
private void onPaletteGenerated(@Nullable Palette palette) {
int color;
if (palette == null) {
color = getDefaultNightMode() == MODE_NIGHT_YES ? BLACK : WHITE;
} else {
color = getDefaultNightMode() == MODE_NIGHT_YES ?
palette.getDarkMutedColor(BLACK) :
palette.getLightMutedColor(WHITE);
}
imageView.setBackgroundColor(color);
imagePreview.showPreview(imageUris);
}
private void showImageButton(boolean showImageButton) {
......@@ -220,7 +146,7 @@ public class TextAttachmentController extends TextSendController {
// restore hint
textInput.setHint(textHint);
// hide image layout
imageLayout.setVisibility(GONE);
imagePreview.setVisibility(GONE);
// reset image URIs
imageUris = emptyList();
// show the image button again, so images can get attached
......@@ -244,6 +170,12 @@ public class TextAttachmentController extends TextSendController {
return state.getSuperState();
}
@Override
public void onCancel() {
textInput.clearText();
reset();
}
private static class SavedState extends AbsSavedState {
private List<Uri> imageUris;
......
......@@ -5,6 +5,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical"
tools:context=".android.conversation.ConversationActivity">
......@@ -48,7 +49,15 @@
android:id="@+id/conversationView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
android:layout_weight="2"/>
<org.briarproject.briar.android.view.ImagePreview
android:id="@+id/imagePreview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:visibility="gone"
tools:visibility="visible"/>
<org.briarproject.briar.android.view.TextInputView
android:id="@+id/text_input_container"
......
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
tools:parentTag="android.support.constraint.ConstraintLayout"
tools:showIn="@layout/activity_conversation">
<View
android:id="@+id/divider"
style="@style/Divider.Horizontal"
android:layout_alignParentTop="true"
app:layout_constraintBottom_toTopOf="@+id/imageView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider"
tools:ignore="ContentDescription"
tools:srcCompat="@tools:sample/avatars"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/imageCancelButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:src="@drawable/ic_close"
app:backgroundTint="@color/briar_red"
app:fabCustomSize="26dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0"
app:maxImageSize="18dp"/>
</merge>
......@@ -12,37 +12,6 @@
style="@style/Divider.Horizontal"
android:layout_alignParentTop="true"/>
<FrameLayout
android:id="@+id/imageLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="@dimen/text_input_image_height"
android:minHeight="@dimen/button_size"
tools:background="@color/msg_status_bubble_background"
tools:ignore="ContentDescription"
tools:srcCompat="@tools:sample/avatars"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/imageCancelButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:src="@drawable/ic_close"
app:backgroundTint="@color/briar_red"
app:fabCustomSize="26dp"
app:maxImageSize="18dp"/>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
......
......@@ -70,7 +70,6 @@
<!-- Emoji -->
<dimen name="text_input_height">42dp</dimen>
<dimen name="text_input_image_height">150dp</dimen>
<dimen name="conversation_item_body_text_size">16sp</dimen>
<dimen name="emoji_drawer_size">32sp</dimen>
<dimen name="emoji_drawer_indicator_height">2dp</dimen>
......
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