package org.thoughtcrime.securesms.stories import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.ObjectAnimator import android.animation.PropertyValuesHolder.ofFloat import android.content.Context import android.transition.Transition import android.transition.TransitionValues import android.util.AttributeSet import android.view.View import android.view.ViewGroup import androidx.annotation.RequiresApi import androidx.core.view.ViewCompat class ScaleTransition : Transition { companion object { private const val LAYOUT_WIDTH = "ScaleTransition:layout_width" private const val LAYOUT_HEIGHT = "ScaleTransition:layout_height" private const val POSITION_X = "ScaleTransition:position_x" private const val POSITION_Y = "ScaleTransition:position_y" private const val SCALE_X = "ScaleTransition:scale_x" private const val SCALE_Y = "ScaleTransition:scale_y" private val PROPERTIES = arrayOf( LAYOUT_WIDTH, LAYOUT_HEIGHT, POSITION_X, POSITION_Y, SCALE_X, SCALE_Y ) } constructor() : super() @RequiresApi(21) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) override fun getTransitionProperties(): Array { return PROPERTIES } override fun captureStartValues(transitionValues: TransitionValues) { if (ViewCompat.getTransitionName(transitionValues.view) == "story") { captureValues(transitionValues) } } override fun captureEndValues(transitionValues: TransitionValues) { if (ViewCompat.getTransitionName(transitionValues.view) == "story") { resetValues(transitionValues.view) captureValues(transitionValues) } } private fun captureValues(transitionValues: TransitionValues) = with(transitionValues.view) { transitionValues.values[LAYOUT_WIDTH] = width.toFloat() transitionValues.values[LAYOUT_HEIGHT] = height.toFloat() transitionValues.values[POSITION_X] = x transitionValues.values[POSITION_Y] = y transitionValues.values[SCALE_X] = scaleX transitionValues.values[SCALE_Y] = scaleY } private fun resetValues(view: View) = with(view) { translationX = 0f translationY = 0f scaleX = 1f scaleY = 1f } override fun createAnimator( sceneRoot: ViewGroup, start: TransitionValues?, end: TransitionValues? ): Animator? { if (start == null || end == null) { return null } val startWidth = start.values[LAYOUT_WIDTH] as Float val endWidth = end.values[LAYOUT_WIDTH] as Float val startHeight = start.values[LAYOUT_HEIGHT] as Float val endHeight = end.values[LAYOUT_HEIGHT] as Float val startX = start.values[POSITION_X] as Float val endX = end.values[POSITION_X] as Float val startY = start.values[POSITION_Y] as Float val endY = end.values[POSITION_Y] as Float val startScaleX = start.values[SCALE_X] as Float val startScaleY = start.values[SCALE_Y] as Float end.view.translationX = (startX - endX) - (endWidth - startWidth) / 2 end.view.translationY = (startY - endY) - (endHeight - startHeight) / 2 end.view.scaleX = (startWidth / endWidth) * startScaleX end.view.scaleY = (startHeight / endHeight) * startScaleY return ObjectAnimator.ofPropertyValuesHolder( end.view, ofFloat(View.TRANSLATION_X, 0f), ofFloat(View.TRANSLATION_Y, 0f), ofFloat(View.SCALE_X, 1f), ofFloat(View.SCALE_Y, 1f) ).apply { addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { resetValues(start.view) resetValues(end.view) } }) } } }