mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 00:59:49 +01:00
Move all files to natural position.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
package org.thoughtcrime.securesms.revealable;
|
||||
|
||||
public class ViewOnceExpirationInfo {
|
||||
|
||||
private final long messageId;
|
||||
private final long receiveTime;
|
||||
|
||||
public ViewOnceExpirationInfo(long messageId, long receiveTime) {
|
||||
this.messageId = messageId;
|
||||
this.receiveTime = receiveTime;
|
||||
}
|
||||
|
||||
public long getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public long getReceiveTime() {
|
||||
return receiveTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package org.thoughtcrime.securesms.revealable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.mms.VideoSlide;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.video.VideoPlayer;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ViewOnceMessageActivity extends PassphraseRequiredActionBarActivity implements VideoPlayer.PlayerStateCallback {
|
||||
|
||||
private static final String TAG = Log.tag(ViewOnceMessageActivity.class);
|
||||
|
||||
private static final String KEY_MESSAGE_ID = "message_id";
|
||||
private static final String KEY_URI = "uri";
|
||||
|
||||
private static final int OVERLAY_TIMEOUT_S = 2;
|
||||
private static final int FADE_OUT_DURATION_MS = 200;
|
||||
|
||||
private ImageView image;
|
||||
private VideoPlayer video;
|
||||
private View closeButton;
|
||||
private TextView duration;
|
||||
private ViewOnceMessageViewModel viewModel;
|
||||
private Uri uri;
|
||||
|
||||
private int updateCounter;
|
||||
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
private final Runnable durationUpdateRunnable = () -> {
|
||||
long timeLeft = TimeUnit.MILLISECONDS.toSeconds(video.getDuration()) - updateCounter;
|
||||
long minutes = timeLeft / 60;
|
||||
long seconds = timeLeft % 60;
|
||||
duration.setText(getString(R.string.ViewOnceMessageActivity_video_duration, minutes, seconds));
|
||||
updateCounter++;
|
||||
if (updateCounter > OVERLAY_TIMEOUT_S) {
|
||||
animateOutOverlay();
|
||||
} else {
|
||||
scheduleDurationUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
public static Intent getIntent(@NonNull Context context, long messageId, @NonNull Uri uri) {
|
||||
Intent intent = new Intent(context, ViewOnceMessageActivity.class);
|
||||
intent.putExtra(KEY_MESSAGE_ID, messageId);
|
||||
intent.putExtra(KEY_URI, uri);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState, boolean ready) {
|
||||
super.onCreate(savedInstanceState, ready);
|
||||
setContentView(R.layout.view_once_message_activity);
|
||||
|
||||
this.image = findViewById(R.id.view_once_image);
|
||||
this.video = findViewById(R.id.view_once_video);
|
||||
this.duration = findViewById(R.id.view_once_duration);
|
||||
this.closeButton = findViewById(R.id.view_once_close_button);
|
||||
this.uri = getIntent().getParcelableExtra(KEY_URI);
|
||||
|
||||
ViewOnceGestureListener imageListener = new ViewOnceGestureListener(image);
|
||||
GestureDetector imageDetector = new GestureDetector(this, imageListener);
|
||||
|
||||
ViewOnceGestureListener videoListener = new ViewOnceGestureListener(video);
|
||||
GestureDetector videoDetector = new GestureDetector(this, videoListener);
|
||||
|
||||
image.setOnTouchListener((view, event) -> imageDetector.onTouchEvent(event));
|
||||
image.setOnClickListener(v -> finish());
|
||||
|
||||
video.setOnTouchListener((view, event) -> videoDetector.onTouchEvent(event));
|
||||
video.setOnClickListener(v -> finish());
|
||||
|
||||
closeButton.setOnClickListener(v -> finish());
|
||||
|
||||
initViewModel(getIntent().getLongExtra(KEY_MESSAGE_ID, -1), uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
cancelDurationUpdate();
|
||||
video.cleanup();
|
||||
BlobProvider.getInstance().delete(this, uri);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerReady() {
|
||||
updateCounter = 0;
|
||||
handler.post(durationUpdateRunnable);
|
||||
}
|
||||
|
||||
private void initViewModel(long messageId, @NonNull Uri uri) {
|
||||
ViewOnceMessageRepository repository = new ViewOnceMessageRepository(this);
|
||||
|
||||
viewModel = ViewModelProviders.of(this, new ViewOnceMessageViewModel.Factory(getApplication(), messageId, repository))
|
||||
.get(ViewOnceMessageViewModel.class);
|
||||
|
||||
viewModel.getMessage().observe(this, (message) -> {
|
||||
if (message == null) return;
|
||||
|
||||
if (message.isPresent()) {
|
||||
displayMedia(uri);
|
||||
} else {
|
||||
image.setImageDrawable(null);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void displayMedia(@NonNull Uri uri) {
|
||||
if (MediaUtil.isVideoType(PartAuthority.getAttachmentContentType(this, uri))) {
|
||||
displayVideo(uri);
|
||||
} else {
|
||||
displayImage(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private void displayVideo(@NonNull Uri uri) {
|
||||
video.setVisibility(View.VISIBLE);
|
||||
image.setVisibility(View.GONE);
|
||||
duration.setVisibility(View.VISIBLE);
|
||||
|
||||
VideoSlide videoSlide = new VideoSlide(this, uri, 0);
|
||||
|
||||
video.setWindow(getWindow());
|
||||
video.setPlayerStateCallbacks(this);
|
||||
video.setVideoSource(videoSlide, true);
|
||||
|
||||
video.hideControls();
|
||||
video.loopForever();
|
||||
}
|
||||
|
||||
private void displayImage(@NonNull Uri uri) {
|
||||
video.setVisibility(View.GONE);
|
||||
image.setVisibility(View.VISIBLE);
|
||||
duration.setVisibility(View.GONE);
|
||||
|
||||
GlideApp.with(this)
|
||||
.load(new DecryptableUri(uri))
|
||||
.into(image);
|
||||
}
|
||||
|
||||
private void animateOutOverlay() {
|
||||
duration.animate().alpha(0f).setDuration(200).start();
|
||||
closeButton.animate().alpha(0f).setDuration(200).start();
|
||||
}
|
||||
|
||||
private void scheduleDurationUpdate() {
|
||||
handler.postDelayed(durationUpdateRunnable, 1000L);
|
||||
}
|
||||
|
||||
private void cancelDurationUpdate() {
|
||||
handler.removeCallbacks(durationUpdateRunnable);
|
||||
}
|
||||
|
||||
private class ViewOnceGestureListener extends GestureDetector.SimpleOnGestureListener {
|
||||
|
||||
private final View view;
|
||||
|
||||
private ViewOnceGestureListener(View view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
view.performClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package org.thoughtcrime.securesms.revealable;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.service.TimedEventManager;
|
||||
|
||||
/**
|
||||
* Manages clearing removable message content after they're opened.
|
||||
*/
|
||||
public class ViewOnceMessageManager extends TimedEventManager<ViewOnceExpirationInfo> {
|
||||
|
||||
private static final String TAG = Log.tag(ViewOnceMessageManager.class);
|
||||
|
||||
private final MmsDatabase mmsDatabase;
|
||||
private final AttachmentDatabase attachmentDatabase;
|
||||
|
||||
public ViewOnceMessageManager(@NonNull Application application) {
|
||||
super(application, "RevealableMessageManager");
|
||||
|
||||
this.mmsDatabase = DatabaseFactory.getMmsDatabase(application);
|
||||
this.attachmentDatabase = DatabaseFactory.getAttachmentDatabase(application);
|
||||
|
||||
scheduleIfNecessary();
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@Override
|
||||
protected @Nullable ViewOnceExpirationInfo getNextClosestEvent() {
|
||||
ViewOnceExpirationInfo expirationInfo = mmsDatabase.getNearestExpiringViewOnceMessage();
|
||||
|
||||
if (expirationInfo != null) {
|
||||
Log.i(TAG, "Next closest expiration is in " + getDelayForEvent(expirationInfo) + " ms for messsage " + expirationInfo.getMessageId() + ".");
|
||||
} else {
|
||||
Log.i(TAG, "No messages to schedule.");
|
||||
}
|
||||
|
||||
return expirationInfo;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@Override
|
||||
protected void executeEvent(@NonNull ViewOnceExpirationInfo event) {
|
||||
Log.i(TAG, "Deleting attachments for message " + event.getMessageId());
|
||||
attachmentDatabase.deleteAttachmentFilesForMessage(event.getMessageId());
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@Override
|
||||
protected long getDelayForEvent(@NonNull ViewOnceExpirationInfo event) {
|
||||
long expiresAt = event.getReceiveTime() + ViewOnceUtil.MAX_LIFESPAN;
|
||||
long timeLeft = expiresAt - System.currentTimeMillis();
|
||||
|
||||
return Math.max(0, timeLeft);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@Override
|
||||
protected void scheduleAlarm(@NonNull Application application, long delay) {
|
||||
setAlarm(application, delay, ViewOnceAlarm.class);
|
||||
}
|
||||
|
||||
public static class ViewOnceAlarm extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = Log.tag(ViewOnceAlarm.class);
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(TAG, "onReceive()");
|
||||
ApplicationContext.getInstance(context).getViewOnceMessageManager().scheduleIfNecessary();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.thoughtcrime.securesms.revealable;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
class ViewOnceMessageRepository {
|
||||
|
||||
private static final String TAG = Log.tag(ViewOnceMessageRepository.class);
|
||||
|
||||
private final MmsDatabase mmsDatabase;
|
||||
|
||||
ViewOnceMessageRepository(@NonNull Context context) {
|
||||
this.mmsDatabase = DatabaseFactory.getMmsDatabase(context);
|
||||
}
|
||||
|
||||
void getMessage(long messageId, @NonNull Callback<Optional<MmsMessageRecord>> callback) {
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
try (MmsDatabase.Reader reader = mmsDatabase.readerFor(mmsDatabase.getMessage(messageId))) {
|
||||
MmsMessageRecord record = (MmsMessageRecord) reader.getNext();
|
||||
callback.onComplete(Optional.fromNullable(record));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
interface Callback<T> {
|
||||
void onComplete(T result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package org.thoughtcrime.securesms.revealable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import com.pnikosis.materialishprogress.ProgressWheel;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.events.PartProgressEvent;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
public class ViewOnceMessageView extends LinearLayout {
|
||||
|
||||
private static final String TAG = Log.tag(ViewOnceMessageView.class);
|
||||
|
||||
private ImageView icon;
|
||||
private ProgressWheel progress;
|
||||
private TextView text;
|
||||
private Attachment attachment;
|
||||
private int unopenedForegroundColor;
|
||||
private int openedForegroundColor;
|
||||
private int foregroundColor;
|
||||
|
||||
public ViewOnceMessageView(Context context) {
|
||||
super(context);
|
||||
init(null);
|
||||
}
|
||||
|
||||
public ViewOnceMessageView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
private void init(@Nullable AttributeSet attrs) {
|
||||
inflate(getContext(), R.layout.revealable_message_view, this);
|
||||
setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
if (attrs != null) {
|
||||
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ViewOnceMessageView, 0, 0);
|
||||
|
||||
unopenedForegroundColor = typedArray.getColor(R.styleable.ViewOnceMessageView_revealable_unopenedForegroundColor, Color.BLACK);
|
||||
openedForegroundColor = typedArray.getColor(R.styleable.ViewOnceMessageView_revealable_openedForegroundColor, Color.BLACK);
|
||||
|
||||
typedArray.recycle();
|
||||
}
|
||||
|
||||
this.icon = findViewById(R.id.revealable_icon);
|
||||
this.progress = findViewById(R.id.revealable_progress);
|
||||
this.text = findViewById(R.id.revealable_text);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
if (!EventBus.getDefault().isRegistered(this)) {
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
public boolean requiresTapToDownload(@NonNull MmsMessageRecord messageRecord) {
|
||||
if (messageRecord.isOutgoing() || messageRecord.getSlideDeck().getThumbnailSlide() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Attachment attachment = messageRecord.getSlideDeck().getThumbnailSlide().asAttachment();
|
||||
return attachment.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_FAILED ||
|
||||
attachment.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_PENDING;
|
||||
}
|
||||
|
||||
public void setMessage(@NonNull MmsMessageRecord message) {
|
||||
this.attachment = message.getSlideDeck().getThumbnailSlide() != null ? message.getSlideDeck().getThumbnailSlide().asAttachment() : null;
|
||||
|
||||
presentMessage(message);
|
||||
}
|
||||
|
||||
public void presentMessage(@NonNull MmsMessageRecord message) {
|
||||
presentText(message);
|
||||
}
|
||||
|
||||
private void presentText(@NonNull MmsMessageRecord messageRecord) {
|
||||
if (messageRecord.isOutgoing()) {
|
||||
foregroundColor = openedForegroundColor;
|
||||
text.setText(R.string.RevealableMessageView_outgoing_media);
|
||||
icon.setImageResource(R.drawable.ic_play_outline_24);
|
||||
progress.setVisibility(GONE);
|
||||
} else if (ViewOnceUtil.isViewable(messageRecord)) {
|
||||
foregroundColor = unopenedForegroundColor;
|
||||
text.setText(getDescriptionId(messageRecord));
|
||||
icon.setImageResource(R.drawable.ic_play_solid_24);
|
||||
progress.setVisibility(GONE);
|
||||
} else if (networkInProgress(messageRecord)) {
|
||||
foregroundColor = unopenedForegroundColor;
|
||||
text.setText("");
|
||||
icon.setImageResource(0);
|
||||
progress.setVisibility(VISIBLE);
|
||||
} else if (requiresTapToDownload(messageRecord)) {
|
||||
foregroundColor = unopenedForegroundColor;
|
||||
text.setText(formatFileSize(messageRecord));
|
||||
icon.setImageResource(R.drawable.ic_arrow_down_circle_outline_24);
|
||||
progress.setVisibility(GONE);
|
||||
} else {
|
||||
foregroundColor = openedForegroundColor;
|
||||
text.setText(R.string.RevealableMessageView_viewed);
|
||||
icon.setImageResource(R.drawable.ic_play_outline_24);
|
||||
progress.setVisibility(GONE);
|
||||
}
|
||||
|
||||
text.setTextColor(foregroundColor);
|
||||
icon.setColorFilter(foregroundColor);
|
||||
progress.setBarColor(foregroundColor);
|
||||
progress.setRimColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
private boolean networkInProgress(@NonNull MmsMessageRecord messageRecord) {
|
||||
if (messageRecord.getSlideDeck().getThumbnailSlide() == null) return false;
|
||||
|
||||
Attachment attachment = messageRecord.getSlideDeck().getThumbnailSlide().asAttachment();
|
||||
return attachment.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_STARTED;
|
||||
}
|
||||
|
||||
private @NonNull String formatFileSize(@NonNull MmsMessageRecord messageRecord) {
|
||||
if (messageRecord.getSlideDeck().getThumbnailSlide() == null) return "";
|
||||
|
||||
long size = messageRecord.getSlideDeck().getThumbnailSlide().getFileSize();
|
||||
return Util.getPrettyFileSize(size);
|
||||
}
|
||||
|
||||
private static @StringRes int getDescriptionId(@NonNull MmsMessageRecord messageRecord) {
|
||||
Slide thumbnailSlide = messageRecord.getSlideDeck().getThumbnailSlide();
|
||||
|
||||
if (thumbnailSlide != null && MediaUtil.isVideoType(thumbnailSlide.getContentType())) {
|
||||
return R.string.RevealableMessageView_video;
|
||||
}
|
||||
|
||||
return R.string.RevealableMessageView_photo;
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEventAsync(final PartProgressEvent event) {
|
||||
if (event.attachment.equals(attachment)) {
|
||||
progress.setInstantProgress((float) event.progress / (float) event.total);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package org.thoughtcrime.securesms.revealable;
|
||||
|
||||
import android.app.Application;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
class ViewOnceMessageViewModel extends ViewModel {
|
||||
|
||||
private static final String TAG = Log.tag(ViewOnceMessageViewModel.class);
|
||||
|
||||
private final Application application;
|
||||
private final ViewOnceMessageRepository repository;
|
||||
private final MutableLiveData<Optional<MmsMessageRecord>> message;
|
||||
private final ContentObserver observer;
|
||||
|
||||
private ViewOnceMessageViewModel(@NonNull Application application,
|
||||
long messageId,
|
||||
@NonNull ViewOnceMessageRepository repository)
|
||||
{
|
||||
this.application = application;
|
||||
this.repository = repository;
|
||||
this.message = new MutableLiveData<>();
|
||||
this.observer = new ContentObserver(new Handler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
repository.getMessage(messageId, optionalMessage -> onMessageRetrieved(optionalMessage));
|
||||
}
|
||||
};
|
||||
|
||||
repository.getMessage(messageId, message -> {
|
||||
if (message.isPresent()) {
|
||||
Uri uri = DatabaseContentProviders.Conversation.getUriForThread(message.get().getThreadId());
|
||||
application.getContentResolver().registerContentObserver(uri, true, observer);
|
||||
}
|
||||
|
||||
onMessageRetrieved(message);
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull LiveData<Optional<MmsMessageRecord>> getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
application.getContentResolver().unregisterContentObserver(observer);
|
||||
}
|
||||
|
||||
private void onMessageRetrieved(@NonNull Optional<MmsMessageRecord> optionalMessage) {
|
||||
Util.runOnMain(() -> {
|
||||
MmsMessageRecord current = message.getValue() != null ? message.getValue().orNull() : null;
|
||||
MmsMessageRecord proposed = optionalMessage.orNull();
|
||||
|
||||
if (current != null && proposed != null && current.getId() == proposed.getId()) {
|
||||
Log.d(TAG, "Same ID -- skipping update");
|
||||
} else {
|
||||
message.setValue(optionalMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
|
||||
private final Application application;
|
||||
private final long messageId;
|
||||
private final ViewOnceMessageRepository repository;
|
||||
|
||||
Factory(@NonNull Application application,
|
||||
long messageId,
|
||||
@NonNull ViewOnceMessageRepository repository)
|
||||
{
|
||||
this.application = application;
|
||||
this.messageId = messageId;
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
//noinspection ConstantConditions
|
||||
return modelClass.cast(new ViewOnceMessageViewModel(application, messageId, repository));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package org.thoughtcrime.securesms.revealable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ViewOnceUtil {
|
||||
|
||||
public static final long MAX_LIFESPAN = TimeUnit.DAYS.toMillis(30);
|
||||
|
||||
public static boolean isViewable(@NonNull MmsMessageRecord message) {
|
||||
if (!message.isViewOnce()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (message.isOutgoing()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (message.getSlideDeck().getThumbnailSlide() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (message.getSlideDeck().getThumbnailSlide().getUri() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (message.getSlideDeck().getThumbnailSlide().getTransferState() != AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isViewed(message)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isViewed(@NonNull MmsMessageRecord message) {
|
||||
if (!message.isViewOnce()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (message.getDateReceived() + MAX_LIFESPAN <= System.currentTimeMillis()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (message.getSlideDeck().getThumbnailSlide() != null && message.getSlideDeck().getThumbnailSlide().getTransferState() != AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (message.getSlideDeck().getThumbnailSlide() == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (message.getSlideDeck().getThumbnailSlide().getUri() == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (message.isOutgoing()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user