From e6229a3a13eec3302d460402cf585b56fe523439 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Thu, 6 Dec 2018 16:21:03 -0200 Subject: [PATCH] [android] Factor out image preview into its own view class --- .../conversation/ConversationActivity.java | 6 +- .../briar/android/view/ImagePreview.java | 119 ++++++++++++++++++ .../view/TextAttachmentController.java | 102 +++------------ .../main/res/layout/activity_conversation.xml | 11 +- .../src/main/res/layout/image_preview.xml | 49 ++++++++ .../src/main/res/layout/text_input_view.xml | 31 ----- briar-android/src/main/res/values/dimens.xml | 1 - 7 files changed, 199 insertions(+), 120 deletions(-) create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/view/ImagePreview.java create mode 100644 briar-android/src/main/res/layout/image_preview.xml diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java index 9ba1051df..3d3611f90 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java @@ -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); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/ImagePreview.java b/briar-android/src/main/java/org/briarproject/briar/android/view/ImagePreview.java new file mode 100644 index 000000000..c19c9d771 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/ImagePreview.java @@ -0,0 +1,119 @@ +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 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() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, + Object model, Target 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 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(); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java b/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java index 531737f4a..883119cf2 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java @@ -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 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() { - @Override - public boolean onLoadFailed(@Nullable GlideException e, - Object model, Target 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 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 imageUris; diff --git a/briar-android/src/main/res/layout/activity_conversation.xml b/briar-android/src/main/res/layout/activity_conversation.xml index a3afa6b6d..f27144b06 100644 --- a/briar-android/src/main/res/layout/activity_conversation.xml +++ b/briar-android/src/main/res/layout/activity_conversation.xml @@ -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"/> + + + + + + + + + + + diff --git a/briar-android/src/main/res/layout/text_input_view.xml b/briar-android/src/main/res/layout/text_input_view.xml index d50ee521a..3ca815785 100644 --- a/briar-android/src/main/res/layout/text_input_view.xml +++ b/briar-android/src/main/res/layout/text_input_view.xml @@ -12,37 +12,6 @@ style="@style/Divider.Horizontal" android:layout_alignParentTop="true"/> - - - - - - - - 42dp - 150dp 16sp 32sp 2dp