mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 22:29:53 +01:00
Show placeholders for missing attachments in ImageActivity
and display attachments as they arrive while ImageActivity is open.
This commit is contained in:
@@ -24,6 +24,7 @@ import org.briarproject.briar.android.conversation.glide.GlideApp;
|
|||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
@@ -33,14 +34,19 @@ import static android.os.Build.VERSION.SDK_INT;
|
|||||||
import static android.widget.ImageView.ScaleType.FIT_START;
|
import static android.widget.ImageView.ScaleType.FIT_START;
|
||||||
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.ERROR;
|
||||||
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT_POSITION;
|
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT_POSITION;
|
||||||
import static org.briarproject.briar.android.conversation.ImageActivity.ITEM_ID;
|
import static org.briarproject.briar.android.conversation.ImageActivity.ITEM_ID;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersAreNonnullByDefault
|
@ParametersAreNonnullByDefault
|
||||||
public class ImageFragment extends Fragment {
|
public class ImageFragment extends Fragment
|
||||||
|
implements RequestListener<Drawable> {
|
||||||
|
|
||||||
private final static String IS_FIRST = "isFirst";
|
private final static String IS_FIRST = "isFirst";
|
||||||
|
@DrawableRes
|
||||||
|
private static final int ERROR_RES = R.drawable.ic_image_broken;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ViewModelProvider.Factory viewModelFactory;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
@@ -89,55 +95,72 @@ public class ImageFragment extends Fragment {
|
|||||||
|
|
||||||
viewModel = ViewModelProviders.of(requireNonNull(getActivity()),
|
viewModel = ViewModelProviders.of(requireNonNull(getActivity()),
|
||||||
viewModelFactory).get(ImageViewModel.class);
|
viewModelFactory).get(ImageViewModel.class);
|
||||||
|
viewModel.getOnAttachmentLoaded()
|
||||||
|
.observeEvent(this, this::onAttachmentLoaded);
|
||||||
|
|
||||||
photoView = v.findViewById(R.id.photoView);
|
photoView = v.findViewById(R.id.photoView);
|
||||||
photoView.setScaleLevels(1, 2, 4);
|
photoView.setScaleLevels(1, 2, 4);
|
||||||
photoView.setOnClickListener(view -> viewModel.clickImage());
|
photoView.setOnClickListener(view -> viewModel.clickImage());
|
||||||
|
|
||||||
// Request Listener
|
if (attachment.getState() == AVAILABLE) {
|
||||||
RequestListener<Drawable> listener = new RequestListener<Drawable>() {
|
loadImage();
|
||||||
|
// postponed transition will be started when Image was loaded
|
||||||
@Override
|
} else if (attachment.getState() == ERROR) {
|
||||||
public boolean onLoadFailed(@Nullable GlideException e,
|
photoView.setImageResource(ERROR_RES);
|
||||||
Object model, Target<Drawable> target,
|
startPostponedTransition();
|
||||||
boolean isFirstResource) {
|
} else {
|
||||||
if (getActivity() != null && isFirst)
|
photoView.setImageResource(R.drawable.ic_image_missing);
|
||||||
getActivity().supportStartPostponedEnterTransition();
|
startPostponedTransition();
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onResourceReady(Drawable resource, Object model,
|
|
||||||
Target<Drawable> target, DataSource dataSource,
|
|
||||||
boolean isFirstResource) {
|
|
||||||
if (SDK_INT >= 21 && !(resource instanceof Animatable)) {
|
|
||||||
// set transition name only when not animatable,
|
|
||||||
// because the animation won't start otherwise
|
|
||||||
photoView.setTransitionName(
|
|
||||||
attachment.getTransitionName(conversationItemId));
|
|
||||||
}
|
|
||||||
// Move image to the top if overlapping toolbar
|
|
||||||
if (viewModel.isOverlappingToolbar(photoView, resource)) {
|
|
||||||
photoView.setScaleType(FIT_START);
|
|
||||||
}
|
|
||||||
if (getActivity() != null && isFirst) {
|
|
||||||
getActivity().supportStartPostponedEnterTransition();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load Image
|
|
||||||
GlideApp.with(this)
|
|
||||||
.load(attachment)
|
|
||||||
// TODO allow if size < maxTextureSize ?
|
|
||||||
// .override(SIZE_ORIGINAL)
|
|
||||||
.diskCacheStrategy(NONE)
|
|
||||||
.error(R.drawable.ic_image_broken)
|
|
||||||
.addListener(listener)
|
|
||||||
.into(photoView);
|
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadImage() {
|
||||||
|
GlideApp.with(this)
|
||||||
|
.load(attachment)
|
||||||
|
// TODO allow if size < maxTextureSize ?
|
||||||
|
// .override(SIZE_ORIGINAL)
|
||||||
|
.diskCacheStrategy(NONE)
|
||||||
|
.error(ERROR_RES)
|
||||||
|
.addListener(this)
|
||||||
|
.into(photoView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onAttachmentLoaded(MessageId messageId) {
|
||||||
|
if (attachment.getMessageId().equals(messageId)) loadImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onLoadFailed(@Nullable GlideException e,
|
||||||
|
Object model, Target<Drawable> target,
|
||||||
|
boolean isFirstResource) {
|
||||||
|
startPostponedTransition();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onResourceReady(Drawable resource, Object model,
|
||||||
|
Target<Drawable> target, DataSource dataSource,
|
||||||
|
boolean isFirstResource) {
|
||||||
|
if (SDK_INT >= 21 && !(resource instanceof Animatable)) {
|
||||||
|
// set transition name only when not animatable,
|
||||||
|
// because the animation won't start otherwise
|
||||||
|
photoView.setTransitionName(
|
||||||
|
attachment.getTransitionName(conversationItemId));
|
||||||
|
}
|
||||||
|
// Move image to the top if overlapping toolbar
|
||||||
|
if (viewModel.isOverlappingToolbar(photoView, resource)) {
|
||||||
|
photoView.setScaleType(FIT_START);
|
||||||
|
}
|
||||||
|
startPostponedTransition();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startPostponedTransition() {
|
||||||
|
if (getActivity() != null && isFirst) {
|
||||||
|
getActivity().supportStartPostponedEnterTransition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ class ImageViewHolder extends ViewHolder {
|
|||||||
imageView.setScaleType(FIT_CENTER);
|
imageView.setScaleType(FIT_CENTER);
|
||||||
} else {
|
} else {
|
||||||
loadImage(attachment, r);
|
loadImage(attachment, r);
|
||||||
if (SDK_INT >= 21) {
|
|
||||||
imageView.setTransitionName(
|
|
||||||
attachment.getTransitionName(conversationItemId));
|
|
||||||
}
|
|
||||||
imageView.setScaleType(CENTER_CROP);
|
imageView.setScaleType(CENTER_CROP);
|
||||||
}
|
}
|
||||||
|
if (SDK_INT >= 21) {
|
||||||
|
imageView.setTransitionName(
|
||||||
|
attachment.getTransitionName(conversationItemId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setImageViewDimensions(AttachmentItem a, boolean single,
|
private void setImageViewDimensions(AttachmentItem a, boolean single,
|
||||||
|
|||||||
@@ -7,13 +7,18 @@ import android.view.View;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||||
import org.briarproject.briar.api.messaging.Attachment;
|
import org.briarproject.briar.api.messaging.Attachment;
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
|
import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@@ -41,16 +46,19 @@ import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
|||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ImageViewModel extends AndroidViewModel {
|
public class ImageViewModel extends AndroidViewModel implements EventListener {
|
||||||
|
|
||||||
private static Logger LOG = getLogger(ImageViewModel.class.getName());
|
private static Logger LOG = getLogger(ImageViewModel.class.getName());
|
||||||
|
|
||||||
private final MessagingManager messagingManager;
|
private final MessagingManager messagingManager;
|
||||||
|
private final EventBus eventBus;
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
@IoExecutor
|
@IoExecutor
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
|
|
||||||
|
private final MutableLiveEvent<MessageId> attachmentLoaded =
|
||||||
|
new MutableLiveEvent<>();
|
||||||
/**
|
/**
|
||||||
* true means there was an error saving the image, false if image was saved.
|
* true means there was an error saving the image, false if image was saved.
|
||||||
*/
|
*/
|
||||||
@@ -62,13 +70,34 @@ public class ImageViewModel extends AndroidViewModel {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ImageViewModel(Application application,
|
ImageViewModel(Application application,
|
||||||
MessagingManager messagingManager,
|
MessagingManager messagingManager, EventBus eventBus,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
@IoExecutor Executor ioExecutor) {
|
@IoExecutor Executor ioExecutor) {
|
||||||
super(application);
|
super(application);
|
||||||
this.messagingManager = messagingManager;
|
this.messagingManager = messagingManager;
|
||||||
|
this.eventBus = eventBus;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
|
|
||||||
|
eventBus.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCleared() {
|
||||||
|
super.onCleared();
|
||||||
|
eventBus.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eventOccurred(Event e) {
|
||||||
|
if (e instanceof AttachmentReceivedEvent) {
|
||||||
|
attachmentLoaded
|
||||||
|
.postEvent(((AttachmentReceivedEvent) e).getMessageId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveEvent<MessageId> getOnAttachmentLoaded() {
|
||||||
|
return attachmentLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clickImage() {
|
void clickImage() {
|
||||||
|
|||||||
Reference in New Issue
Block a user