Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4c389024 authored by Michael Mikhail's avatar Michael Mikhail Committed by Automerger Merge Worker
Browse files

Merge "[Media TTT] Add bounce animation to the receiver icon" into tm-qpr-dev...

Merge "[Media TTT] Add bounce animation to the receiver icon" into tm-qpr-dev am: 3cb3420b am: fbef5a9c

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21234481



Change-Id: Iae3c402c8ca45e11a8e6eb2bb520b9e1275ca4f8
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 3c1043a1 fbef5a9c
Loading
Loading
Loading
Loading
+19 −14
Original line number Diff line number Diff line
@@ -27,6 +27,11 @@
        android:layout_height="wrap_content"
        />

    <FrameLayout
        android:id="@+id/icon_container_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        >
        <com.android.systemui.media.taptotransfer.receiver.ReceiverChipRippleView
            android:id="@+id/icon_glow_ripple"
            android:layout_width="wrap_content"
@@ -41,8 +46,8 @@
            android:layout_width="@dimen/media_ttt_icon_size_receiver"
            android:layout_height="@dimen/media_ttt_icon_size_receiver"
            android:layout_gravity="center|bottom"
        android:alpha="0.0"
            android:layout_marginBottom="@dimen/media_ttt_receiver_icon_bottom_margin"
            />
    </FrameLayout>

</FrameLayout>
+68 −20
Original line number Diff line number Diff line
@@ -16,7 +16,9 @@

package com.android.systemui.media.taptotransfer.receiver

import android.animation.TimeInterpolator
import android.annotation.SuppressLint
import android.animation.ValueAnimator
import android.app.StatusBarManager
import android.content.Context
import android.graphics.Rect
@@ -31,8 +33,10 @@ import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
import com.android.internal.widget.CachingIconView
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.ui.binder.TintedIconViewBinder
import com.android.systemui.dagger.SysUISingleton
@@ -101,6 +105,13 @@ open class MediaTttChipControllerReceiver @Inject constructor(
        fitInsetsTypes = 0 // Ignore insets from all system bars
    }

    // Value animator that controls the bouncing animation of views.
    private val bounceAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
        repeatCount = ValueAnimator.INFINITE
        repeatMode = ValueAnimator.REVERSE
        duration = ICON_BOUNCE_ANIM_DURATION
    }

    private val commandQueueCallbacks = object : CommandQueue.Callbacks {
        override fun updateMediaTapToTransferReceiverDisplay(
            @StatusBarManager.MediaTransferReceiverState displayState: Int,
@@ -203,44 +214,52 @@ open class MediaTttChipControllerReceiver @Inject constructor(

        val iconView = currentView.getAppIconView()
        iconView.setPadding(iconPadding, iconPadding, iconPadding, iconPadding)
        iconView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE
        TintedIconViewBinder.bind(iconInfo.toTintedIcon(), iconView)

        val iconContainerView = currentView.getIconContainerView()
        iconContainerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE
    }

    override fun animateViewIn(view: ViewGroup) {
        val appIconView = view.getAppIconView()
        val iconContainerView = view.getIconContainerView()
        val iconRippleView: ReceiverChipRippleView = view.requireViewById(R.id.icon_glow_ripple)
        val rippleView: ReceiverChipRippleView = view.requireViewById(R.id.ripple)
        animateViewTranslationAndFade(appIconView, -1 * getTranslationAmount(), 1f)
        animateViewTranslationAndFade(iconRippleView, -1 * getTranslationAmount(), 1f)
        val translationYBy = getTranslationAmount()
        // Make the icon container view starts animation from bottom of the screen.
        iconContainerView.translationY += rippleController.getReceiverIconSize()
        animateViewTranslationAndFade(
            iconContainerView,
            translationYBy = -1 * translationYBy,
            alphaEndValue = 1f,
            Interpolators.EMPHASIZED_DECELERATE,
        ) {
            animateBouncingView(iconContainerView, translationYBy * BOUNCE_TRANSLATION_RATIO)
        }
        rippleController.expandToInProgressState(rippleView, iconRippleView)
    }

    override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
        val appIconView = view.getAppIconView()
        val iconRippleView: ReceiverChipRippleView = view.requireViewById(R.id.icon_glow_ripple)
        val iconContainerView = view.getIconContainerView()
        val rippleView: ReceiverChipRippleView = view.requireViewById(R.id.ripple)
        val translationYBy = getTranslationAmount()

        // Remove update listeners from bounce animator to prevent any conflict with
        // translation animation.
        bounceAnimator.removeAllUpdateListeners()
        bounceAnimator.cancel()
        if (removalReason == ChipStateReceiver.TRANSFER_TO_RECEIVER_SUCCEEDED.name &&
                mediaTttFlags.isMediaTttReceiverSuccessRippleEnabled()) {
            rippleController.expandToSuccessState(rippleView, onAnimationEnd)
            animateViewTranslationAndFade(
                iconRippleView,
                -1 * getTranslationAmount(),
                0f,
                translationDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
                alphaDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
            )
            animateViewTranslationAndFade(
                appIconView,
                -1 * getTranslationAmount(),
                iconContainerView,
                -1 * translationYBy,
                0f,
                translationDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
                alphaDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
            )
        } else {
            rippleController.collapseRipple(rippleView, onAnimationEnd)
            animateViewTranslationAndFade(iconRippleView, getTranslationAmount(), 0f)
            animateViewTranslationAndFade(appIconView, getTranslationAmount(), 0f)
            animateViewTranslationAndFade(iconContainerView, translationYBy, 0f)
        }
    }

@@ -252,15 +271,19 @@ open class MediaTttChipControllerReceiver @Inject constructor(

    /** Animation of view translation and fading. */
    private fun animateViewTranslationAndFade(
        view: View,
        view: ViewGroup,
        translationYBy: Float,
        alphaEndValue: Float,
        interpolator: TimeInterpolator? = null,
        translationDuration: Long = ICON_TRANSLATION_ANIM_DURATION,
        alphaDuration: Long = ICON_ALPHA_ANIM_DURATION,
        onAnimationEnd: Runnable? = null,
    ) {
        view.animate()
            .translationYBy(translationYBy)
            .setInterpolator(interpolator)
            .setDuration(translationDuration)
            .withEndAction { onAnimationEnd?.run() }
            .start()
        view.animate()
            .alpha(alphaEndValue)
@@ -270,17 +293,42 @@ open class MediaTttChipControllerReceiver @Inject constructor(

    /** Returns the amount that the chip will be translated by in its intro animation. */
    private fun getTranslationAmount(): Float {
        return rippleController.getRippleSize() * 0.5f -
            rippleController.getReceiverIconSize()
        return rippleController.getRippleSize() * 0.5f
    }

    private fun View.getAppIconView(): CachingIconView {
        return this.requireViewById(R.id.app_icon)
    }

    private fun View.getIconContainerView(): ViewGroup {
        return this.requireViewById(R.id.icon_container_view)
    }

    private fun animateBouncingView(iconContainerView: ViewGroup, translationYBy: Float) {
        if (bounceAnimator.isStarted) {
            return
        }

        addViewToBounceAnimation(iconContainerView, translationYBy)

        // In order not to announce description every time the view animate.
        iconContainerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_NONE
        bounceAnimator.start()
    }

    private fun addViewToBounceAnimation(view: View, translationYBy: Float) {
        val prevTranslationY = view.translationY
        bounceAnimator.addUpdateListener { updateListener ->
            val progress = updateListener.animatedValue as Float
            view.translationY = prevTranslationY + translationYBy * progress
        }
    }

    companion object {
        private const val ICON_TRANSLATION_ANIM_DURATION = 500L
        private const val ICON_BOUNCE_ANIM_DURATION = 750L
        private const val ICON_TRANSLATION_SUCCEEDED_DURATION = 167L
        private const val BOUNCE_TRANSLATION_RATIO = 0.15f
        private val ICON_ALPHA_ANIM_DURATION = 5.frames
    }
}