mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 06:39:54 +01:00
[android] allow to attach multiple images
This commit is contained in:
@@ -1,43 +1,27 @@
|
|||||||
package org.briarproject.briar.android.view;
|
package org.briarproject.briar.android.view;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.constraint.ConstraintLayout;
|
import android.support.constraint.ConstraintLayout;
|
||||||
import android.support.v7.graphics.Palette;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.LayoutInflater;
|
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.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
|
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
|
||||||
import static android.graphics.Color.BLACK;
|
import static android.support.v4.content.ContextCompat.getColor;
|
||||||
import static android.graphics.Color.WHITE;
|
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
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;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ImagePreview extends ConstraintLayout {
|
public class ImagePreview extends ConstraintLayout {
|
||||||
|
|
||||||
private final ImageView imageView;
|
private final RecyclerView imageList;
|
||||||
private final int backgroundColor =
|
|
||||||
getDefaultNightMode() == MODE_NIGHT_YES ? BLACK : WHITE;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private ImagePreviewListener listener;
|
private ImagePreviewListener listener;
|
||||||
@@ -59,9 +43,11 @@ public class ImagePreview extends ConstraintLayout {
|
|||||||
context.getSystemService(LAYOUT_INFLATER_SERVICE));
|
context.getSystemService(LAYOUT_INFLATER_SERVICE));
|
||||||
inflater.inflate(R.layout.image_preview, this, true);
|
inflater.inflate(R.layout.image_preview, this, true);
|
||||||
|
|
||||||
// find image view and set background color
|
// set background color
|
||||||
imageView = findViewById(R.id.imageView);
|
setBackgroundColor(getColor(context, R.color.card_background));
|
||||||
imageView.setBackgroundColor(backgroundColor);
|
|
||||||
|
// find list
|
||||||
|
imageList = findViewById(R.id.imageList);
|
||||||
|
|
||||||
// set cancel listener
|
// set cancel listener
|
||||||
findViewById(R.id.imageCancelButton).setOnClickListener(view -> {
|
findViewById(R.id.imageCancelButton).setOnClickListener(view -> {
|
||||||
@@ -74,42 +60,14 @@ public class ImagePreview extends ConstraintLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void showPreview(List<Uri> imageUris) {
|
void showPreview(List<Uri> imageUris) {
|
||||||
|
if (listener == null) throw new IllegalStateException();
|
||||||
|
if (imageUris.size() == 1) {
|
||||||
|
LayoutParams params = (LayoutParams) imageList.getLayoutParams();
|
||||||
|
params.width = MATCH_PARENT;
|
||||||
|
imageList.setLayoutParams(params);
|
||||||
|
}
|
||||||
setVisibility(VISIBLE);
|
setVisibility(VISIBLE);
|
||||||
GlideApp.with(imageView)
|
imageList.setAdapter(new ImagePreviewAdapter(imageUris, listener));
|
||||||
.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 {
|
interface ImagePreviewListener {
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package org.briarproject.briar.android.view;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.LayoutRes;
|
||||||
|
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 org.briarproject.briar.android.view.ImagePreview.ImagePreviewListener;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class ImagePreviewAdapter extends Adapter<ImagePreviewViewHolder> {
|
||||||
|
|
||||||
|
private final List<Uri> items;
|
||||||
|
private final ImagePreviewListener listener;
|
||||||
|
@LayoutRes
|
||||||
|
private final int layout;
|
||||||
|
|
||||||
|
public ImagePreviewAdapter(List<Uri> items, ImagePreviewListener listener) {
|
||||||
|
this.items = items;
|
||||||
|
this.listener = listener;
|
||||||
|
this.layout = items.size() == 1 ?
|
||||||
|
R.layout.list_item_image_preview_single :
|
||||||
|
R.layout.list_item_image_preview;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImagePreviewViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
|
||||||
|
View v = LayoutInflater.from(viewGroup.getContext())
|
||||||
|
.inflate(layout, viewGroup, false);
|
||||||
|
return new ImagePreviewViewHolder(v, items.size() == 1,
|
||||||
|
requireNonNull(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(ImagePreviewViewHolder viewHolder, int position) {
|
||||||
|
viewHolder.bind(items.get(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package org.briarproject.briar.android.view;
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.DrawableRes;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
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 org.briarproject.briar.android.view.ImagePreview.ImagePreviewListener;
|
||||||
|
|
||||||
|
import static android.view.View.INVISIBLE;
|
||||||
|
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 com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class ImagePreviewViewHolder extends ViewHolder {
|
||||||
|
|
||||||
|
@DrawableRes
|
||||||
|
private static final int ERROR_RES = R.drawable.ic_image_broken;
|
||||||
|
|
||||||
|
private final boolean single;
|
||||||
|
private final ImagePreviewListener listener;
|
||||||
|
|
||||||
|
private final ImageView imageView;
|
||||||
|
private final ProgressBar progressBar;
|
||||||
|
|
||||||
|
ImagePreviewViewHolder(View v, boolean single,
|
||||||
|
ImagePreviewListener listener) {
|
||||||
|
super(v);
|
||||||
|
this.single = single;
|
||||||
|
this.listener = listener;
|
||||||
|
this.imageView = v.findViewById(R.id.imageView);
|
||||||
|
this.progressBar = v.findViewById(R.id.progressBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind(Uri uri) {
|
||||||
|
GlideApp.with(imageView)
|
||||||
|
.load(uri)
|
||||||
|
.diskCacheStrategy(NONE)
|
||||||
|
.error(ERROR_RES)
|
||||||
|
.downsample(FIT_CENTER)
|
||||||
|
.transition(withCrossFade())
|
||||||
|
.addListener(new RequestListener<Drawable>() {
|
||||||
|
@Override
|
||||||
|
public boolean onLoadFailed(@Nullable GlideException e,
|
||||||
|
Object model, Target<Drawable> target,
|
||||||
|
boolean isFirstResource) {
|
||||||
|
if (single) listener.onCancel();
|
||||||
|
progressBar.setVisibility(INVISIBLE);
|
||||||
|
Toast.makeText(imageView.getContext(),
|
||||||
|
R.string.image_attach_error, LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onResourceReady(Drawable resource,
|
||||||
|
Object model, Target<Drawable> target,
|
||||||
|
DataSource dataSource, boolean isFirstResource) {
|
||||||
|
progressBar.setVisibility(INVISIBLE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into(imageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -81,8 +81,7 @@ public class TextAttachmentController extends TextSendController
|
|||||||
ACTION_OPEN_DOCUMENT : ACTION_GET_CONTENT);
|
ACTION_OPEN_DOCUMENT : ACTION_GET_CONTENT);
|
||||||
intent.addCategory(CATEGORY_OPENABLE);
|
intent.addCategory(CATEGORY_OPENABLE);
|
||||||
intent.setType("image/*");
|
intent.setType("image/*");
|
||||||
if (SDK_INT >= 18) // TODO set true to allow attaching multiple images
|
if (SDK_INT >= 18) intent.putExtra(EXTRA_ALLOW_MULTIPLE, true);
|
||||||
intent.putExtra(EXTRA_ALLOW_MULTIPLE, false);
|
|
||||||
requireNonNull(imageListener).onAttachImage(intent);
|
requireNonNull(imageListener).onAttachImage(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,21 +13,23 @@
|
|||||||
android:id="@+id/divider"
|
android:id="@+id/divider"
|
||||||
style="@style/Divider.Horizontal"
|
style="@style/Divider.Horizontal"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/imageView"
|
app:layout_constraintBottom_toTopOf="@+id/imageList"
|
||||||
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"/>
|
||||||
|
|
||||||
<ImageView
|
<android.support.v7.widget.RecyclerView
|
||||||
android:id="@+id/imageView"
|
android:id="@+id/imageList"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/divider"
|
app:layout_constraintTop_toBottomOf="@+id/divider"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
tools:srcCompat="@tools:sample/avatars"/>
|
tools:listitem="@layout/list_item_image"/>
|
||||||
|
|
||||||
<android.support.design.widget.FloatingActionButton
|
<android.support.design.widget.FloatingActionButton
|
||||||
android:id="@+id/imageCancelButton"
|
android:id="@+id/imageCancelButton"
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout
|
||||||
|
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="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:layout_height="200dp">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
style="?android:attr/progressBarStyleLarge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="1:1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:srcCompat="@tools:sample/avatars"
|
||||||
|
tools:visibility="visible"/>
|
||||||
|
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout
|
||||||
|
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"
|
||||||
|
tools:layout_height="200dp">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
style="?android:attr/progressBarStyleLarge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:srcCompat="@tools:sample/backgrounds/scenic"
|
||||||
|
tools:visibility="visible"/>
|
||||||
|
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
||||||
Reference in New Issue
Block a user