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

Commit 9391ef81 authored by Michael Mikhail's avatar Michael Mikhail Committed by Android (Google) Code Review
Browse files

Merge "[Media TTT] Add animation out for receiver chip" into tm-qpr-dev

parents 420d5743 ee3ca08b
Loading
Loading
Loading
Loading
+33 −6
Original line number Original line Diff line number Diff line
@@ -56,7 +56,7 @@ import javax.inject.Inject
 * TODO(b/245610654): Re-name this to be MediaTttReceiverCoordinator.
 * TODO(b/245610654): Re-name this to be MediaTttReceiverCoordinator.
 */
 */
@SysUISingleton
@SysUISingleton
class MediaTttChipControllerReceiver @Inject constructor(
open class MediaTttChipControllerReceiver @Inject constructor(
        private val commandQueue: CommandQueue,
        private val commandQueue: CommandQueue,
        context: Context,
        context: Context,
        @MediaTttReceiverLogger logger: MediaTttLogger,
        @MediaTttReceiverLogger logger: MediaTttLogger,
@@ -183,15 +183,28 @@ class MediaTttChipControllerReceiver @Inject constructor(
        val appIconView = view.getAppIconView()
        val appIconView = view.getAppIconView()
        appIconView.animate()
        appIconView.animate()
                .translationYBy(-1 * getTranslationAmount().toFloat())
                .translationYBy(-1 * getTranslationAmount().toFloat())
                .setDuration(30.frames)
                .setDuration(ICON_TRANSLATION_ANIM_DURATION)
                .start()
                .start()
        appIconView.animate()
        appIconView.animate()
                .alpha(1f)
                .alpha(1f)
                .setDuration(5.frames)
                .setDuration(ICON_ALPHA_ANIM_DURATION)
                .start()
                .start()
        // Using withEndAction{} doesn't apply a11y focus when screen is unlocked.
        // Using withEndAction{} doesn't apply a11y focus when screen is unlocked.
        appIconView.postOnAnimation { view.requestAccessibilityFocus() }
        appIconView.postOnAnimation { view.requestAccessibilityFocus() }
        startRipple(view.requireViewById(R.id.ripple))
        expandRipple(view.requireViewById(R.id.ripple))
    }

    override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
        val appIconView = view.getAppIconView()
        appIconView.animate()
                .translationYBy(getTranslationAmount().toFloat())
                .setDuration(ICON_TRANSLATION_ANIM_DURATION)
                .start()
        appIconView.animate()
                .alpha(0f)
                .setDuration(ICON_ALPHA_ANIM_DURATION)
                .start()
        (view.requireViewById(R.id.ripple) as ReceiverChipRippleView).collapseRipple(onAnimationEnd)
    }
    }


    override fun getTouchableRegion(view: View, outRect: Rect) {
    override fun getTouchableRegion(view: View, outRect: Rect) {
@@ -205,11 +218,22 @@ class MediaTttChipControllerReceiver @Inject constructor(
        return context.resources.getDimensionPixelSize(R.dimen.media_ttt_receiver_vert_translation)
        return context.resources.getDimensionPixelSize(R.dimen.media_ttt_receiver_vert_translation)
    }
    }


    private fun startRipple(rippleView: ReceiverChipRippleView) {
    private fun expandRipple(rippleView: ReceiverChipRippleView) {
        if (rippleView.rippleInProgress()) {
        if (rippleView.rippleInProgress()) {
            // Skip if ripple is still playing
            // Skip if ripple is still playing
            return
            return
        }
        }

        // In case the device orientation changes, we need to reset the layout.
        rippleView.addOnLayoutChangeListener (
            View.OnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
                if (v == null) return@OnLayoutChangeListener

                val layoutChangedRippleView = v as ReceiverChipRippleView
                layoutRipple(layoutChangedRippleView)
                layoutChangedRippleView.invalidate()
            }
        )
        rippleView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
        rippleView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
            override fun onViewDetachedFromWindow(view: View?) {}
            override fun onViewDetachedFromWindow(view: View?) {}


@@ -219,7 +243,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
                }
                }
                val attachedRippleView = view as ReceiverChipRippleView
                val attachedRippleView = view as ReceiverChipRippleView
                layoutRipple(attachedRippleView)
                layoutRipple(attachedRippleView)
                attachedRippleView.startRipple()
                attachedRippleView.expandRipple()
                attachedRippleView.removeOnAttachStateChangeListener(this)
                attachedRippleView.removeOnAttachStateChangeListener(this)
            }
            }
        })
        })
@@ -242,6 +266,9 @@ class MediaTttChipControllerReceiver @Inject constructor(
    }
    }
}
}


val ICON_TRANSLATION_ANIM_DURATION = 30.frames
val ICON_ALPHA_ANIM_DURATION = 5.frames

data class ChipReceiverInfo(
data class ChipReceiverInfo(
    val routeInfo: MediaRoute2Info,
    val routeInfo: MediaRoute2Info,
    val appIconDrawableOverride: Drawable?,
    val appIconDrawableOverride: Drawable?,
+28 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


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


import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.content.Context
import android.util.AttributeSet
import android.util.AttributeSet
import com.android.systemui.surfaceeffects.ripple.RippleShader
import com.android.systemui.surfaceeffects.ripple.RippleShader
@@ -25,10 +27,36 @@ import com.android.systemui.surfaceeffects.ripple.RippleView
 * An expanding ripple effect for the media tap-to-transfer receiver chip.
 * An expanding ripple effect for the media tap-to-transfer receiver chip.
 */
 */
class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleView(context, attrs) {
class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleView(context, attrs) {

    // Indicates whether the ripple started expanding.
    private var isStarted: Boolean

    init {
    init {
        setupShader(RippleShader.RippleShape.ELLIPSE)
        setupShader(RippleShader.RippleShape.ELLIPSE)
        setRippleFill(true)
        setRippleFill(true)
        setSparkleStrength(0f)
        setSparkleStrength(0f)
        duration = 3000L
        duration = 3000L
        isStarted = false
    }

    fun expandRipple(onAnimationEnd: Runnable? = null) {
        isStarted = true
        super.startRipple(onAnimationEnd)
    }

    /** Used to animate out the ripple. No-op if the ripple was never started via [startRipple]. */
    fun collapseRipple(onAnimationEnd: Runnable? = null) {
        if (!isStarted) {
            return // Ignore if ripple is not started yet.
        }
        // Reset all listeners to animator.
        animator.removeAllListeners()
        animator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                onAnimationEnd?.run()
                isStarted = false
            }
        })
        animator.reverse()
    }
    }
}
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -41,7 +41,7 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a
        private set
        private set


    private val ripplePaint = Paint()
    private val ripplePaint = Paint()
    private val animator = ValueAnimator.ofFloat(0f, 1f)
    protected val animator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f)


    var duration: Long = 1750
    var duration: Long = 1750


+67 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

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

import android.content.Context
import android.os.Handler
import android.os.PowerManager
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
import com.android.systemui.util.wakelock.WakeLock

class FakeMediaTttChipControllerReceiver(
    commandQueue: CommandQueue,
    context: Context,
    logger: MediaTttLogger,
    windowManager: WindowManager,
    mainExecutor: DelayableExecutor,
    accessibilityManager: AccessibilityManager,
    configurationController: ConfigurationController,
    powerManager: PowerManager,
    mainHandler: Handler,
    mediaTttFlags: MediaTttFlags,
    uiEventLogger: MediaTttReceiverUiEventLogger,
    viewUtil: ViewUtil,
    wakeLockBuilder: WakeLock.Builder,
) :
    MediaTttChipControllerReceiver(
        commandQueue,
        context,
        logger,
        windowManager,
        mainExecutor,
        accessibilityManager,
        configurationController,
        powerManager,
        mainHandler,
        mediaTttFlags,
        uiEventLogger,
        viewUtil,
        wakeLockBuilder,
    ) {
    override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
        // Just bypass the animation in tests
        onAnimationEnd.run()
    }
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -114,7 +114,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
        fakeWakeLockBuilder = WakeLockFake.Builder(context)
        fakeWakeLockBuilder = WakeLockFake.Builder(context)
        fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
        fakeWakeLockBuilder.setWakeLock(fakeWakeLock)


        controllerReceiver = MediaTttChipControllerReceiver(
        controllerReceiver = FakeMediaTttChipControllerReceiver(
            commandQueue,
            commandQueue,
            context,
            context,
            logger,
            logger,