2019-08-01 01:33:56 +02:00
|
|
|
package org.thoughtcrime.securesms.revealable;
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.os.Bundle;
|
2019-09-27 15:10:30 +02:00
|
|
|
import android.os.Handler;
|
|
|
|
import android.os.Looper;
|
|
|
|
import android.view.GestureDetector;
|
|
|
|
import android.view.MotionEvent;
|
2019-08-01 01:33:56 +02:00
|
|
|
import android.view.View;
|
|
|
|
import android.widget.ImageView;
|
2019-09-27 15:10:30 +02:00
|
|
|
import android.widget.TextView;
|
2019-08-01 01:33:56 +02:00
|
|
|
|
|
|
|
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;
|
2019-09-27 15:10:30 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.PartAuthority;
|
|
|
|
import org.thoughtcrime.securesms.mms.VideoSlide;
|
2019-08-01 01:33:56 +02:00
|
|
|
import org.thoughtcrime.securesms.providers.BlobProvider;
|
2019-09-27 15:10:30 +02:00
|
|
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
|
|
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
|
|
|
import org.thoughtcrime.securesms.video.VideoPlayer;
|
2019-08-01 01:33:56 +02:00
|
|
|
|
2019-09-27 15:10:30 +02:00
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
public class ViewOnceMessageActivity extends PassphraseRequiredActionBarActivity implements VideoPlayer.PlayerStateCallback {
|
2019-08-01 01:33:56 +02:00
|
|
|
|
|
|
|
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";
|
|
|
|
|
2019-09-27 15:10:30 +02:00
|
|
|
private static final int OVERLAY_TIMEOUT_S = 2;
|
|
|
|
private static final int FADE_OUT_DURATION_MS = 200;
|
|
|
|
|
2019-08-01 01:33:56 +02:00
|
|
|
private ImageView image;
|
2019-09-27 15:10:30 +02:00
|
|
|
private VideoPlayer video;
|
2019-08-01 01:33:56 +02:00
|
|
|
private View closeButton;
|
2019-09-27 15:10:30 +02:00
|
|
|
private TextView duration;
|
2019-08-01 01:33:56 +02:00
|
|
|
private ViewOnceMessageViewModel viewModel;
|
|
|
|
private Uri uri;
|
|
|
|
|
2019-09-27 15:10:30 +02:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-08-01 01:33:56 +02:00
|
|
|
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);
|
2019-09-27 15:10:30 +02:00
|
|
|
this.video = findViewById(R.id.view_once_video);
|
|
|
|
this.duration = findViewById(R.id.view_once_duration);
|
2019-08-01 01:33:56 +02:00
|
|
|
this.closeButton = findViewById(R.id.view_once_close_button);
|
|
|
|
this.uri = getIntent().getParcelableExtra(KEY_URI);
|
|
|
|
|
2019-09-27 15:10:30 +02:00
|
|
|
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));
|
2019-08-01 01:33:56 +02:00
|
|
|
image.setOnClickListener(v -> finish());
|
|
|
|
|
2019-09-27 15:10:30 +02:00
|
|
|
video.setOnTouchListener((view, event) -> videoDetector.onTouchEvent(event));
|
|
|
|
video.setOnClickListener(v -> finish());
|
|
|
|
|
|
|
|
closeButton.setOnClickListener(v -> finish());
|
2019-08-01 01:33:56 +02:00
|
|
|
|
|
|
|
initViewModel(getIntent().getLongExtra(KEY_MESSAGE_ID, -1), uri);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onStop() {
|
|
|
|
super.onStop();
|
2019-09-27 15:10:30 +02:00
|
|
|
cancelDurationUpdate();
|
|
|
|
video.cleanup();
|
2019-08-01 01:33:56 +02:00
|
|
|
BlobProvider.getInstance().delete(this, uri);
|
|
|
|
finish();
|
|
|
|
}
|
|
|
|
|
2019-09-27 15:10:30 +02:00
|
|
|
@Override
|
|
|
|
public void onPlayerReady() {
|
|
|
|
updateCounter = 0;
|
|
|
|
handler.post(durationUpdateRunnable);
|
|
|
|
}
|
|
|
|
|
2019-08-01 01:33:56 +02:00
|
|
|
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()) {
|
2019-09-27 15:10:30 +02:00
|
|
|
displayMedia(uri);
|
2019-08-01 01:33:56 +02:00
|
|
|
} else {
|
|
|
|
image.setImageDrawable(null);
|
|
|
|
finish();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2019-09-27 15:10:30 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2019-08-01 01:33:56 +02:00
|
|
|
}
|