From 0d6849be2cfe4896b7f1a4397080f732dda87d3d Mon Sep 17 00:00:00 2001 From: Alexander Oprisnik Date: Mon, 13 Jan 2025 10:19:26 -0800 Subject: [PATCH] Added Vito Activity transition util, similar to DraweeTransition and migrate Showcase sample Reviewed By: kartavya-ramnani Differential Revision: D64238554 fbshipit-source-id: c60a191643bba7c847b08d81fda052da44069478 --- samples/showcase/src/main/AndroidManifest.xml | 4 +- .../samples/showcase/ExampleDatabase.kt | 4 +- .../transition/ImageDetailsActivity.java | 44 +++--- .../transition/VitoTransitionFragment.java} | 36 +++-- ...ml => activity_vito_transition_detail.xml} | 6 +- ...ition.xml => fragment_vito_transition.xml} | 8 +- .../showcase/src/main/res/values/strings.xml | 6 +- .../fresco/vito/view/impl/VitoViewImpl2.kt | 1 + .../vito/view/transition/VitoTransition.kt | 129 ++++++++++++++++++ 9 files changed, 189 insertions(+), 49 deletions(-) rename samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/{drawee => vito}/transition/ImageDetailsActivity.java (55%) rename samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/{drawee/transition/DraweeTransitionFragment.java => vito/transition/VitoTransitionFragment.java} (57%) rename samples/showcase/src/main/res/layout/{activity_drawee_transition_detail.xml => activity_vito_transition_detail.xml} (78%) rename samples/showcase/src/main/res/layout/{fragment_drawee_transition.xml => fragment_vito_transition.xml} (74%) create mode 100644 vito/view/src/main/java/com/facebook/fresco/vito/view/transition/VitoTransition.kt diff --git a/samples/showcase/src/main/AndroidManifest.xml b/samples/showcase/src/main/AndroidManifest.xml index 2ab885506d..332997cc87 100644 --- a/samples/showcase/src/main/AndroidManifest.xml +++ b/samples/showcase/src/main/AndroidManifest.xml @@ -35,8 +35,8 @@ = Build.VERSION_CODES.LOLLIPOP) { - ScalingUtils.ScaleType fromScaleType = ScalingUtils.ScaleType.FOCUS_CROP; - PointF fromFocusPoint = DraweeTransitionFragment.FOCUS_POINT; + ScalingUtils.ScaleType fromScaleType = ScalingUtils.ScaleType.FOCUS_CROP; + PointF fromFocusPoint = VitoTransitionFragment.FOCUS_POINT; - getWindow() - .setSharedElementEnterTransition( - DraweeTransition.createTransitionSet( - fromScaleType, toScaleType, fromFocusPoint, toFocusPoint)); - getWindow() - .setSharedElementReturnTransition( - DraweeTransition.createTransitionSet( - toScaleType, fromScaleType, toFocusPoint, fromFocusPoint)); - } + getWindow() + .setSharedElementEnterTransition( + VitoTransition.createTransitionSet( + CALLER_CONTEXT, fromScaleType, toScaleType, fromFocusPoint, toFocusPoint)); + getWindow() + .setSharedElementReturnTransition( + VitoTransition.createTransitionSet( + CALLER_CONTEXT, toScaleType, fromScaleType, toFocusPoint, fromFocusPoint)); } @Override diff --git a/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/drawee/transition/DraweeTransitionFragment.java b/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/vito/transition/VitoTransitionFragment.java similarity index 57% rename from samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/drawee/transition/DraweeTransitionFragment.java rename to samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/vito/transition/VitoTransitionFragment.java index ced91ee287..bd3393ca4a 100644 --- a/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/drawee/transition/DraweeTransitionFragment.java +++ b/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/vito/transition/VitoTransitionFragment.java @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -package com.facebook.fresco.samples.showcase.drawee.transition; +package com.facebook.fresco.samples.showcase.vito.transition; import android.app.ActivityOptions; import android.content.Intent; @@ -15,37 +15,49 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import androidx.annotation.Nullable; +import com.facebook.common.internal.Suppliers; import com.facebook.drawee.drawable.ScalingUtils; -import com.facebook.drawee.view.SimpleDraweeView; import com.facebook.fresco.samples.showcase.BaseShowcaseFragment; import com.facebook.fresco.samples.showcase.R; import com.facebook.fresco.samples.showcase.misc.ImageUriProvider; +import com.facebook.fresco.vito.options.ImageOptions; +import com.facebook.fresco.vito.view.VitoView; +import com.facebook.fresco.vito.view.impl.VitoViewImpl2; /** Simple drawee fragment that just displays an image. */ -public class DraweeTransitionFragment extends BaseShowcaseFragment { +public class VitoTransitionFragment extends BaseShowcaseFragment { public static final PointF FOCUS_POINT = new PointF(1, 0.5f); + private static final String CALLER_CONTEXT = "VitoTransitionFragment"; @Nullable @Override public View onCreateView( LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_drawee_transition, container, false); + return inflater.inflate(R.layout.fragment_vito_transition, container, false); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { final Uri imageUri = sampleUris().createSampleUri(ImageUriProvider.ImageSize.M); - final SimpleDraweeView simpleDraweeView = - (SimpleDraweeView) view.findViewById(R.id.drawee_view); - // You have to enable legacy visibility handling for the start view in order for this to work - simpleDraweeView.setLegacyVisibilityHandlingEnabled(true); - simpleDraweeView.setImageURI(imageUri); - simpleDraweeView.getHierarchy().setActualImageScaleType(ScalingUtils.ScaleType.FOCUS_CROP); - simpleDraweeView.getHierarchy().setActualImageFocusPoint(FOCUS_POINT); - simpleDraweeView.setOnClickListener( + // In Android N, visibility handling for ImageView was changed to use onVisibilityAggregated, + // which doesn't update the visibility of the starting Vito view correctly when the activity + // transition is reversed. + // Hence, we have to (globally) disable Visibility APIs for now. + VitoViewImpl2.useVisibilityCallbacks = Suppliers.BOOLEAN_FALSE; + + final ImageView imageView = view.findViewById(R.id.image_view); + final ImageOptions imageOptions = + ImageOptions.create() + .scale(ScalingUtils.ScaleType.FOCUS_CROP) + .focusPoint(FOCUS_POINT) + .build(); + imageView.setImageURI(imageUri); + VitoView.show(imageUri, imageOptions, CALLER_CONTEXT, imageView); + imageView.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/samples/showcase/src/main/res/layout/activity_drawee_transition_detail.xml b/samples/showcase/src/main/res/layout/activity_vito_transition_detail.xml similarity index 78% rename from samples/showcase/src/main/res/layout/activity_drawee_transition_detail.xml rename to samples/showcase/src/main/res/layout/activity_vito_transition_detail.xml index 0b2ab9c827..2167daf022 100644 --- a/samples/showcase/src/main/res/layout/activity_drawee_transition_detail.xml +++ b/samples/showcase/src/main/res/layout/activity_vito_transition_detail.xml @@ -1,19 +1,17 @@ - diff --git a/samples/showcase/src/main/res/layout/fragment_drawee_transition.xml b/samples/showcase/src/main/res/layout/fragment_vito_transition.xml similarity index 74% rename from samples/showcase/src/main/res/layout/fragment_drawee_transition.xml rename to samples/showcase/src/main/res/layout/fragment_vito_transition.xml index b6b4b4feb2..804828ce74 100644 --- a/samples/showcase/src/main/res/layout/fragment_drawee_transition.xml +++ b/samples/showcase/src/main/res/layout/fragment_vito_transition.xml @@ -1,18 +1,16 @@ - diff --git a/samples/showcase/src/main/res/values/strings.xml b/samples/showcase/src/main/res/values/strings.xml index 2216bdb011..f27c0105ed 100644 --- a/samples/showcase/src/main/res/values/strings.xml +++ b/samples/showcase/src/main/res/values/strings.xml @@ -25,9 +25,9 @@ Change the used RotationOptions using the drop-down menu on top. - Tap the image to start the activity transition. - Image Detail Activity - This is the second activity. Press back to reverse the transition. + Tap the image to start the activity transition. + Image Detail Activity + This is the second activity. Press back to reverse the transition. Click on the image to show the next one. The previous one will be retained until the new image is ready. diff --git a/vito/view/src/main/java/com/facebook/fresco/vito/view/impl/VitoViewImpl2.kt b/vito/view/src/main/java/com/facebook/fresco/vito/view/impl/VitoViewImpl2.kt index ede2a9d9a2..9f9d37ddd6 100644 --- a/vito/view/src/main/java/com/facebook/fresco/vito/view/impl/VitoViewImpl2.kt +++ b/vito/view/src/main/java/com/facebook/fresco/vito/view/impl/VitoViewImpl2.kt @@ -28,6 +28,7 @@ import com.facebook.fresco.vito.source.ImageSource /** Vito View implementation */ object VitoViewImpl2 { @JvmField var useVisibilityCallbacks: Supplier = Suppliers.BOOLEAN_TRUE + @JvmField var useVisibilityAggregatedCallbacks: Supplier = Suppliers.BOOLEAN_FALSE @JvmField var useSimpleFetchLogic: Supplier = Suppliers.BOOLEAN_FALSE @JvmField var useReleaseInViewDetached: Supplier = Suppliers.BOOLEAN_TRUE @JvmField var useReleaseDelayedInViewDetached: Supplier = Suppliers.BOOLEAN_FALSE diff --git a/vito/view/src/main/java/com/facebook/fresco/vito/view/transition/VitoTransition.kt b/vito/view/src/main/java/com/facebook/fresco/vito/view/transition/VitoTransition.kt new file mode 100644 index 0000000000..5d2a62ca04 --- /dev/null +++ b/vito/view/src/main/java/com/facebook/fresco/vito/view/transition/VitoTransition.kt @@ -0,0 +1,129 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.vito.view.transition + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ValueAnimator +import android.annotation.TargetApi +import android.graphics.PointF +import android.graphics.Rect +import android.os.Build +import android.transition.ChangeBounds +import android.transition.Transition +import android.transition.TransitionSet +import android.transition.TransitionValues +import android.view.ViewGroup +import android.widget.ImageView +import com.facebook.drawee.drawable.ScalingUtils +import com.facebook.drawee.drawable.ScalingUtils.InterpolatingScaleType +import com.facebook.fresco.vito.view.VitoView +import com.facebook.infer.annotation.Nullsafe + +/** + * This Transition animates changes of an [ImageView] using Vito between two ScaleTypes + * + * In combination with ChangeBounds, VitoTransition allows ImageViews that change size, shape, or + * [ScalingUtils.ScaleType] to animate contents smoothly. + */ +@Nullsafe(Nullsafe.Mode.LOCAL) +@TargetApi(Build.VERSION_CODES.KITKAT) +class VitoTransition +@JvmOverloads +constructor( + private val callerContext: Any, + private val fromScale: ScalingUtils.ScaleType, + private val toScale: ScalingUtils.ScaleType, + private val fromFocusPoint: PointF? = null, + private val toFocusPoint: PointF? = null +) : Transition() { + override fun captureStartValues(transitionValues: TransitionValues) { + captureValues(transitionValues) + } + + override fun captureEndValues(transitionValues: TransitionValues) { + captureValues(transitionValues) + } + + override fun createAnimator( + sceneRoot: ViewGroup, + startValues: TransitionValues?, + endValues: TransitionValues?, + ): Animator? { + startValues ?: return null + endValues ?: return null + val startBounds = startValues.values[PROPNAME_BOUNDS] as Rect? + val endBounds = endValues.values[PROPNAME_BOUNDS] as Rect? + if (startBounds == null || endBounds == null) { + return null + } + if (fromScale === toScale && fromFocusPoint === toFocusPoint) { + return null + } + val imageView = startValues.view + val scaleType = + InterpolatingScaleType( + fromScale, toScale, startBounds, endBounds, fromFocusPoint, toFocusPoint) + val vitoDrawable = VitoView.getDrawable(imageView) ?: return null + val originalVitoImageRequest = vitoDrawable.imageRequest ?: return null + VitoView.show( + originalVitoImageRequest.imageSource, + originalVitoImageRequest.imageOptions.extend().scale(scaleType).build(), + callerContext, + imageView) + + val animator = ValueAnimator.ofFloat(0f, 1f) + animator.addUpdateListener { animation -> + val fraction = animation.animatedValue as Float + scaleType.value = fraction + } + animator.addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + VitoView.show( + originalVitoImageRequest.imageSource, + originalVitoImageRequest.imageOptions + .extend() + .scale(toScale) + .focusPoint(toFocusPoint) + .build(), + callerContext, + imageView) + } + }) + + return animator + } + + private fun captureValues(transitionValues: TransitionValues) { + if (transitionValues.view is ImageView) { + transitionValues.values[PROPNAME_BOUNDS] = + Rect(0, 0, transitionValues.view.width, transitionValues.view.height) + } + } + + companion object { + private const val PROPNAME_BOUNDS = "vitoTransition:bounds" + + @JvmOverloads + @JvmStatic + fun createTransitionSet( + callerContext: Any, + fromScale: ScalingUtils.ScaleType, + toScale: ScalingUtils.ScaleType, + fromFocusPoint: PointF? = null, + toFocusPoint: PointF? = null + ): TransitionSet { + val transitionSet = TransitionSet() + transitionSet.addTransition(ChangeBounds()) + transitionSet.addTransition( + VitoTransition(callerContext, fromScale, toScale, fromFocusPoint, toFocusPoint)) + return transitionSet + } + } +}