2020-05-28 16:05:34 -03:00
|
|
|
package org.thoughtcrime.securesms;
|
|
|
|
|
2020-08-12 13:00:48 -04:00
|
|
|
import android.annotation.TargetApi;
|
2020-05-28 16:05:34 -03:00
|
|
|
import android.app.Activity;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
2020-06-24 17:18:02 -03:00
|
|
|
import android.content.res.Resources;
|
|
|
|
import android.graphics.Bitmap;
|
2020-05-28 16:05:34 -03:00
|
|
|
import android.graphics.drawable.Drawable;
|
2020-06-24 17:18:02 -03:00
|
|
|
import android.os.Build;
|
2020-05-28 16:05:34 -03:00
|
|
|
import android.os.Bundle;
|
2020-06-24 17:18:02 -03:00
|
|
|
import android.transition.TransitionInflater;
|
2020-08-12 13:00:48 -04:00
|
|
|
import android.view.DisplayCutout;
|
2020-05-28 16:05:34 -03:00
|
|
|
import android.view.View;
|
2020-08-12 13:00:48 -04:00
|
|
|
import android.view.ViewGroup;
|
|
|
|
import android.view.ViewTreeObserver;
|
2020-05-28 16:05:34 -03:00
|
|
|
import android.view.Window;
|
|
|
|
import android.view.WindowManager;
|
|
|
|
import android.widget.ImageView;
|
|
|
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
import androidx.appcompat.widget.Toolbar;
|
|
|
|
import androidx.core.app.ActivityOptionsCompat;
|
2020-06-24 17:18:02 -03:00
|
|
|
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
2020-05-28 16:05:34 -03:00
|
|
|
|
|
|
|
import com.bumptech.glide.load.DataSource;
|
|
|
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
|
|
|
import com.bumptech.glide.load.engine.GlideException;
|
|
|
|
import com.bumptech.glide.request.RequestListener;
|
2020-06-24 17:18:02 -03:00
|
|
|
import com.bumptech.glide.request.target.CustomTarget;
|
2020-05-28 16:05:34 -03:00
|
|
|
import com.bumptech.glide.request.target.Target;
|
2020-06-24 17:18:02 -03:00
|
|
|
import com.bumptech.glide.request.transition.Transition;
|
2020-05-28 16:05:34 -03:00
|
|
|
|
|
|
|
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
|
|
|
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
|
|
|
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
|
|
|
|
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
|
|
|
import org.thoughtcrime.securesms.logging.Log;
|
|
|
|
import org.thoughtcrime.securesms.mms.GlideApp;
|
|
|
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
|
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Activity for displaying avatars full screen.
|
|
|
|
*/
|
2020-06-22 17:01:40 -07:00
|
|
|
public final class AvatarPreviewActivity extends PassphraseRequiredActivity {
|
2020-05-28 16:05:34 -03:00
|
|
|
|
|
|
|
private static final String TAG = Log.tag(AvatarPreviewActivity.class);
|
|
|
|
|
|
|
|
private static final String RECIPIENT_ID_EXTRA = "recipient_id";
|
|
|
|
|
|
|
|
public static @NonNull Intent intentFromRecipientId(@NonNull Context context,
|
|
|
|
@NonNull RecipientId recipientId)
|
|
|
|
{
|
|
|
|
Intent intent = new Intent(context, AvatarPreviewActivity.class);
|
|
|
|
intent.putExtra(RECIPIENT_ID_EXTRA, recipientId.serialize());
|
|
|
|
return intent;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Bundle createTransitionBundle(@NonNull Activity activity, @NonNull View from) {
|
|
|
|
return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, from, "avatar").toBundle();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onCreate(Bundle savedInstanceState, boolean ready) {
|
|
|
|
super.onCreate(savedInstanceState, ready);
|
|
|
|
|
|
|
|
setTheme(R.style.TextSecure_MediaPreview);
|
|
|
|
setContentView(R.layout.contact_photo_preview_activity);
|
|
|
|
|
2020-06-24 17:18:02 -03:00
|
|
|
if (Build.VERSION.SDK_INT >= 21) {
|
|
|
|
postponeEnterTransition();
|
|
|
|
TransitionInflater inflater = TransitionInflater.from(this);
|
|
|
|
getWindow().setSharedElementEnterTransition(inflater.inflateTransition(R.transition.full_screen_avatar_image_enter_transition_set));
|
|
|
|
getWindow().setSharedElementReturnTransition(inflater.inflateTransition(R.transition.full_screen_avatar_image_return_transition_set));
|
|
|
|
}
|
|
|
|
|
2020-08-12 13:00:48 -04:00
|
|
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
|
|
|
ImageView avatar = findViewById(R.id.avatar);
|
2020-05-28 16:05:34 -03:00
|
|
|
|
|
|
|
setSupportActionBar(toolbar);
|
|
|
|
|
|
|
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
|
|
|
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
|
|
|
|
2020-08-12 13:00:48 -04:00
|
|
|
if (Build.VERSION.SDK_INT >= 28) {
|
|
|
|
getWindow().getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
|
|
|
|
toolbar.getViewTreeObserver().addOnGlobalLayoutListener(new DisplayCutoutAdjuster(toolbar, findViewById(R.id.toolbar_cutout_spacer)));
|
|
|
|
}
|
|
|
|
|
2020-05-28 16:05:34 -03:00
|
|
|
showSystemUI();
|
|
|
|
|
|
|
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
|
|
|
|
|
|
Context context = getApplicationContext();
|
|
|
|
RecipientId recipientId = RecipientId.from(getIntent().getStringExtra(RECIPIENT_ID_EXTRA));
|
|
|
|
|
|
|
|
Recipient.live(recipientId).observe(this, recipient -> {
|
|
|
|
ContactPhoto contactPhoto = recipient.isLocalNumber() ? new ProfileContactPhoto(recipient, recipient.getProfileAvatar())
|
|
|
|
: recipient.getContactPhoto();
|
|
|
|
FallbackContactPhoto fallbackPhoto = recipient.isLocalNumber() ? new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20, R.drawable.ic_person_large)
|
|
|
|
: recipient.getFallbackContactPhoto();
|
|
|
|
|
2020-06-24 17:18:02 -03:00
|
|
|
Resources resources = this.getResources();
|
|
|
|
|
|
|
|
GlideApp.with(this)
|
|
|
|
.asBitmap()
|
|
|
|
.load(contactPhoto)
|
|
|
|
.fallback(fallbackPhoto.asCallCard(this))
|
|
|
|
.error(fallbackPhoto.asCallCard(this))
|
|
|
|
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
|
|
|
.addListener(new RequestListener<Bitmap>() {
|
|
|
|
@Override
|
|
|
|
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Bitmap> target, boolean isFirstResource) {
|
|
|
|
Log.w(TAG, "Unable to load avatar, or avatar removed, closing");
|
|
|
|
finish();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.into(new CustomTarget<Bitmap>() {
|
|
|
|
@Override
|
|
|
|
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
|
|
|
|
avatar.setImageDrawable(RoundedBitmapDrawableFactory.create(resources, resource));
|
|
|
|
if (Build.VERSION.SDK_INT >= 21) {
|
|
|
|
startPostponedEnterTransition();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onLoadCleared(@Nullable Drawable placeholder) {
|
|
|
|
}
|
|
|
|
});
|
2020-05-28 16:05:34 -03:00
|
|
|
|
2020-06-16 07:37:27 -07:00
|
|
|
toolbar.setTitle(recipient.getDisplayName(context));
|
2020-05-28 16:05:34 -03:00
|
|
|
});
|
|
|
|
|
|
|
|
avatar.setOnClickListener(v -> toggleUiVisibility());
|
|
|
|
|
|
|
|
showAndHideWithSystemUI(getWindow(), findViewById(R.id.toolbar_layout));
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void showAndHideWithSystemUI(@NonNull Window window, @NonNull View... views) {
|
|
|
|
window.getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
|
|
|
|
boolean hide = (visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
|
|
|
|
|
|
|
|
for (View view : views) {
|
|
|
|
view.animate()
|
|
|
|
.alpha(hide ? 0 : 1)
|
|
|
|
.start();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private void toggleUiVisibility() {
|
|
|
|
int systemUiVisibility = getWindow().getDecorView().getSystemUiVisibility();
|
|
|
|
if ((systemUiVisibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
|
|
|
|
showSystemUI();
|
|
|
|
} else {
|
|
|
|
hideSystemUI();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void hideSystemUI() {
|
|
|
|
getWindow().getDecorView().setSystemUiVisibility(
|
|
|
|
View.SYSTEM_UI_FLAG_IMMERSIVE |
|
|
|
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
|
|
|
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
|
|
|
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
|
|
|
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
|
|
|
View.SYSTEM_UI_FLAG_FULLSCREEN );
|
|
|
|
}
|
|
|
|
|
|
|
|
private void showSystemUI() {
|
|
|
|
getWindow().getDecorView().setSystemUiVisibility(
|
|
|
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
|
|
|
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
|
|
|
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN );
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onSupportNavigateUp() {
|
|
|
|
onBackPressed();
|
|
|
|
return true;
|
|
|
|
}
|
2020-08-12 13:00:48 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Adjust a spacer for the toolbar when a display cutout is detected. Runs within
|
|
|
|
* a layout listener because the activity delays view attachment due to the transitions
|
|
|
|
* and needs to update on device rotation.
|
|
|
|
*/
|
|
|
|
@TargetApi(28)
|
|
|
|
private static class DisplayCutoutAdjuster implements ViewTreeObserver.OnGlobalLayoutListener {
|
|
|
|
|
|
|
|
private final View view;
|
|
|
|
private final View spacer;
|
|
|
|
|
|
|
|
private DisplayCutoutAdjuster(@NonNull View view, @NonNull View spacer) {
|
|
|
|
this.view = view;
|
|
|
|
this.spacer = spacer;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onGlobalLayout() {
|
|
|
|
if (view.getRootWindowInsets() == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
DisplayCutout cutout = view.getRootWindowInsets().getDisplayCutout();
|
|
|
|
if (cutout != null) {
|
|
|
|
ViewGroup.LayoutParams params = spacer.getLayoutParams();
|
|
|
|
params.height = cutout.getSafeInsetTop();
|
|
|
|
spacer.setLayoutParams(params);
|
|
|
|
spacer.setVisibility(View.VISIBLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-28 16:05:34 -03:00
|
|
|
}
|