mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-09 05:48:34 +00:00
Full screen avatar circle to square shape transition.
This commit is contained in:
parent
66f2668326
commit
52747782a7
@ -3,8 +3,12 @@ package org.thoughtcrime.securesms;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.transition.TransitionInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
@ -14,12 +18,15 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.core.app.ActivityOptionsCompat;
|
import androidx.core.app.ActivityOptionsCompat;
|
||||||
|
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||||
|
|
||||||
import com.bumptech.glide.load.DataSource;
|
import com.bumptech.glide.load.DataSource;
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
import com.bumptech.glide.load.engine.GlideException;
|
import com.bumptech.glide.load.engine.GlideException;
|
||||||
import com.bumptech.glide.request.RequestListener;
|
import com.bumptech.glide.request.RequestListener;
|
||||||
|
import com.bumptech.glide.request.target.CustomTarget;
|
||||||
import com.bumptech.glide.request.target.Target;
|
import com.bumptech.glide.request.target.Target;
|
||||||
|
import com.bumptech.glide.request.transition.Transition;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||||
@ -58,7 +65,15 @@ public final class AvatarPreviewActivity extends PassphraseRequiredActivity {
|
|||||||
setTheme(R.style.TextSecure_MediaPreview);
|
setTheme(R.style.TextSecure_MediaPreview);
|
||||||
setContentView(R.layout.contact_photo_preview_activity);
|
setContentView(R.layout.contact_photo_preview_activity);
|
||||||
|
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
|
||||||
ImageView avatar = findViewById(R.id.avatar);
|
ImageView avatar = findViewById(R.id.avatar);
|
||||||
|
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
@ -79,24 +94,40 @@ public final class AvatarPreviewActivity extends PassphraseRequiredActivity {
|
|||||||
FallbackContactPhoto fallbackPhoto = recipient.isLocalNumber() ? new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20, R.drawable.ic_person_large)
|
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();
|
: recipient.getFallbackContactPhoto();
|
||||||
|
|
||||||
GlideApp.with(this).load(contactPhoto)
|
Resources resources = this.getResources();
|
||||||
.fallback(fallbackPhoto.asCallCard(this))
|
|
||||||
.error(fallbackPhoto.asCallCard(this))
|
|
||||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
|
||||||
.addListener(new RequestListener<Drawable>() {
|
|
||||||
@Override
|
|
||||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
|
|
||||||
Log.w(TAG, "Unable to load avatar, or avatar removed, closing");
|
|
||||||
finish();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
GlideApp.with(this)
|
||||||
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
|
.asBitmap()
|
||||||
return false;
|
.load(contactPhoto)
|
||||||
}
|
.fallback(fallbackPhoto.asCallCard(this))
|
||||||
})
|
.error(fallbackPhoto.asCallCard(this))
|
||||||
.into(avatar);
|
.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) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
toolbar.setTitle(recipient.getDisplayName(context));
|
toolbar.setTitle(recipient.getDisplayName(context));
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
package org.thoughtcrime.securesms.animation.transitions;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.transition.Transition;
|
||||||
|
import android.transition.TransitionValues;
|
||||||
|
import android.util.Property;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
|
||||||
|
|
||||||
|
@TargetApi(21)
|
||||||
|
abstract class CircleSquareImageViewTransition extends Transition {
|
||||||
|
|
||||||
|
private static final String CIRCLE_RATIO = "CIRCLE_RATIO";
|
||||||
|
|
||||||
|
private final boolean toCircle;
|
||||||
|
|
||||||
|
CircleSquareImageViewTransition(boolean toCircle) {
|
||||||
|
this.toCircle = toCircle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void captureStartValues(TransitionValues transitionValues) {
|
||||||
|
View view = transitionValues.view;
|
||||||
|
if (view instanceof ImageView) {
|
||||||
|
transitionValues.values.put(CIRCLE_RATIO, toCircle ? 0f : 1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void captureEndValues(TransitionValues transitionValues) {
|
||||||
|
View view = transitionValues.view;
|
||||||
|
if (view instanceof ImageView) {
|
||||||
|
transitionValues.values.put(CIRCLE_RATIO, toCircle ? 1f : 0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
|
||||||
|
if (startValues == null || endValues == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageView endImageView = (ImageView) endValues.view;
|
||||||
|
float start = (float) startValues.values.get(CIRCLE_RATIO);
|
||||||
|
float end = (float) endValues.values.get(CIRCLE_RATIO);
|
||||||
|
|
||||||
|
return ObjectAnimator.ofFloat(endImageView, new RadiusRatioProperty(), start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class RadiusRatioProperty extends Property<ImageView, Float> {
|
||||||
|
|
||||||
|
private float ratio;
|
||||||
|
|
||||||
|
RadiusRatioProperty() {
|
||||||
|
super(Float.class, "circle_ratio");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
final public void set(ImageView imageView, Float ratio) {
|
||||||
|
this.ratio = ratio;
|
||||||
|
Drawable imageViewDrawable = imageView.getDrawable();
|
||||||
|
if (imageViewDrawable instanceof RoundedBitmapDrawable) {
|
||||||
|
RoundedBitmapDrawable drawable = (RoundedBitmapDrawable) imageViewDrawable;
|
||||||
|
if (ratio > 0.95) {
|
||||||
|
drawable.setCircular(true);
|
||||||
|
} else {
|
||||||
|
drawable.setCornerRadius(Math.min(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()) * ratio * 0.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Float get(ImageView object) {
|
||||||
|
return ratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package org.thoughtcrime.securesms.animation.transitions;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will only transition {@link android.widget.ImageView}s that contain a {@link androidx.core.graphics.drawable.RoundedBitmapDrawable}.
|
||||||
|
*/
|
||||||
|
@TargetApi(21)
|
||||||
|
public final class CircleToSquareImageViewTransition extends CircleSquareImageViewTransition {
|
||||||
|
public CircleToSquareImageViewTransition(Context context, AttributeSet attrs) {
|
||||||
|
super(false);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package org.thoughtcrime.securesms.animation.transitions;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will only transition {@link android.widget.ImageView}s that contain a {@link androidx.core.graphics.drawable.RoundedBitmapDrawable}.
|
||||||
|
*/
|
||||||
|
@TargetApi(21)
|
||||||
|
public final class SquareToCircleImageViewTransition extends CircleSquareImageViewTransition {
|
||||||
|
public SquareToCircleImageViewTransition(Context context, AttributeSet attrs) {
|
||||||
|
super(true);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/core_grey_95">
|
android:background="@color/core_grey_95">
|
||||||
@ -8,8 +9,12 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/avatar"
|
android:id="@+id/avatar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:transitionName="avatar" />
|
android:layout_gravity="center_vertical"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:transitionName="avatar"
|
||||||
|
tools:src="@drawable/ic_signal_downloading" />
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/toolbar_layout"
|
android:id="@+id/toolbar_layout"
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:duration="300"
|
||||||
|
android:transitionOrdering="together">
|
||||||
|
|
||||||
|
<changeImageTransform />
|
||||||
|
<changeBounds />
|
||||||
|
|
||||||
|
<transition class="org.thoughtcrime.securesms.animation.transitions.CircleToSquareImageViewTransition" />
|
||||||
|
|
||||||
|
</transitionSet>
|
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:duration="300"
|
||||||
|
android:transitionOrdering="together">
|
||||||
|
|
||||||
|
<changeImageTransform />
|
||||||
|
<changeBounds />
|
||||||
|
|
||||||
|
<transition class="org.thoughtcrime.securesms.animation.transitions.SquareToCircleImageViewTransition" />
|
||||||
|
|
||||||
|
</transitionSet>
|
Loading…
x
Reference in New Issue
Block a user