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

Commit 9f1fc82e authored by Caitlin Cassidy's avatar Caitlin Cassidy
Browse files

[Media TTT] Animate the chip in.

This includes updating ViewHierarchyAnimator to also handle fading in
during #animateAddition (if requested).

Bug: 203800644
Test: manual: See fast + slow animations of the chip attached to the
bug.
Test: ViewHierarchyAnimatorTest

Change-Id: Iedf29695b187eaeddc8734d326ecf08a0ad63dcb
parent 38615a5a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@

    <!-- ViewBoundsAnimator -->
    <item type="id" name="tag_animator"/>
    <item type="id" name="tag_alpha_animator"/>
    <item type="id" name="tag_layout_listener"/>
    <item type="id" name="tag_override_bottom"/>
    <item type="id" name="tag_override_left"/>
+65 −1
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ class ViewHierarchyAnimator {
        private val DEFAULT_INTERPOLATOR = Interpolators.STANDARD
        private val DEFAULT_ADDITION_INTERPOLATOR = Interpolators.STANDARD_DECELERATE
        private val DEFAULT_REMOVAL_INTERPOLATOR = Interpolators.STANDARD_ACCELERATE
        private val DEFAULT_FADE_IN_INTERPOLATOR = Interpolators.ALPHA_IN

        /** The properties used to animate the view bounds. */
        private val PROPERTIES = mapOf(
@@ -162,6 +163,10 @@ class ViewHierarchyAnimator {
         * animate an already visible view, see [animate] and [animateNextUpdate].
         *
         * Then animator unregisters itself once the first addition animation is complete.
         *
         * @param includeFadeIn true if the animator should also fade in the view and child views.
         * @param fadeInInterpolator the interpolator to use when fading in the view. Unused if
         *     [includeFadeIn] is false.
         */
        @JvmOverloads
        fun animateAddition(
@@ -169,7 +174,9 @@ class ViewHierarchyAnimator {
            origin: Hotspot = Hotspot.CENTER,
            interpolator: Interpolator = DEFAULT_ADDITION_INTERPOLATOR,
            duration: Long = DEFAULT_DURATION,
            includeMargins: Boolean = false
            includeMargins: Boolean = false,
            includeFadeIn: Boolean = false,
            fadeInInterpolator: Interpolator = DEFAULT_FADE_IN_INTERPOLATOR
        ): Boolean {
            if (isVisible(
                    rootView.visibility,
@@ -186,6 +193,42 @@ class ViewHierarchyAnimator {
                origin, interpolator, duration, ignorePreviousValues = !includeMargins
            )
            addListener(rootView, listener, recursive = true)

            if (!includeFadeIn) {
                return true
            }

            if (rootView is ViewGroup) {
                // First, fade in the container view
                val containerDuration = duration / 6
                createAndStartFadeInAnimator(
                    rootView, containerDuration, startDelay = 0, interpolator = fadeInInterpolator
                )

                // Then, fade in the child views
                val childDuration = duration / 3
                for (i in 0 until rootView.childCount) {
                    val view = rootView.getChildAt(i)
                    createAndStartFadeInAnimator(
                        view,
                        childDuration,
                        // Wait until the container fades in before fading in the children
                        startDelay = containerDuration,
                        interpolator = fadeInInterpolator
                    )
                }
                // For now, we don't recursively fade in additional sub views (e.g. grandchild
                // views) since it hasn't been necessary, but we could add that functionality.
            } else {
                // Fade in the view during the first half of the addition
                createAndStartFadeInAnimator(
                    rootView,
                    duration / 2,
                    startDelay = 0,
                    interpolator = fadeInInterpolator
                )
            }

            return true
        }

@@ -834,6 +877,27 @@ class ViewHierarchyAnimator {
            view.setTag(R.id.tag_animator, animator)
            animator.start()
        }

        private fun createAndStartFadeInAnimator(
            view: View,
            duration: Long,
            startDelay: Long,
            interpolator: Interpolator
        ) {
            val animator = ObjectAnimator.ofFloat(view, "alpha", 1f)
            animator.startDelay = startDelay
            animator.duration = duration
            animator.interpolator = interpolator
            animator.addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    view.setTag(R.id.tag_alpha_animator, null /* tag */)
                }
            })

            (view.getTag(R.id.tag_alpha_animator) as? ObjectAnimator)?.cancel()
            view.setTag(R.id.tag_alpha_animator, animator)
            animator.start()
        }
    }

    /** An enum used to determine the origin of addition animations. */
+70 −56
Original line number Diff line number Diff line
@@ -13,18 +13,27 @@
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->
<LinearLayout
<!-- Wrap in a frame layout so that we can update the margins on the inner layout. (Since this view
     is the root view of a window, we cannot change the root view's margins.) -->
<!-- Alphas start as 0 because the view will be animated in. -->
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
    android:id="@+id/media_ttt_sender_chip"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <LinearLayout
        android:id="@+id/media_ttt_sender_chip_inner"
        android:orientation="horizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/media_ttt_chip_outer_padding"
        android:background="@drawable/media_ttt_chip_background"
    android:layout_marginTop="50dp"
        android:layout_marginTop="20dp"
        android:clipToPadding="false"
        android:gravity="center_vertical"
        android:alpha="0.0"
        >

        <com.android.internal.widget.CachingIconView
@@ -32,6 +41,7 @@
            android:layout_width="@dimen/media_ttt_app_icon_size"
            android:layout_height="@dimen/media_ttt_app_icon_size"
            android:layout_marginEnd="12dp"
            android:alpha="0.0"
            />

        <TextView
@@ -40,10 +50,10 @@
            android:layout_height="wrap_content"
            android:textSize="@dimen/media_ttt_text_size"
            android:textColor="?android:attr/textColorPrimary"
            android:alpha="0.0"
            />

        <!-- At most one of [loading, failure_icon, undo] will be visible at a time. -->

        <ProgressBar
            android:id="@+id/loading"
            android:indeterminate="true"
@@ -52,6 +62,7 @@
            android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
            android:indeterminateTint="?androidprv:attr/colorAccentPrimaryVariant"
            style="?android:attr/progressBarStyleSmall"
            android:alpha="0.0"
            />

        <ImageView
@@ -61,6 +72,7 @@
            android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
            android:src="@drawable/ic_warning"
            android:tint="@color/GM2_red_500"
            android:alpha="0.0"
            />

        <TextView
@@ -78,6 +90,8 @@
            android:layout_marginTop="@dimen/media_ttt_undo_button_vertical_negative_margin"
            android:layout_marginBottom="@dimen/media_ttt_undo_button_vertical_negative_margin"
            android:background="@drawable/media_ttt_undo_background"
            android:alpha="0.0"
            />

    </LinearLayout>
</FrameLayout>
+7 −0
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
                PowerManager.WAKE_REASON_APPLICATION,
                "com.android.systemui:media_tap_to_transfer_activated"
            )
            animateChipIn(currentChipView)
        }

        // Cancel and re-set the chip timeout each time we get a new state.
@@ -137,6 +138,12 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
     */
    abstract fun updateChipView(chipInfo: T, currentChipView: ViewGroup)

    /**
     * A method that can be implemented by subclcasses to do custom animations for when the chip
     * appears.
     */
    open fun animateChipIn(chipView: ViewGroup) {}

    /**
     * Returns the size that the icon should be, or null if no size override is needed.
     */
+13 −1
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import android.view.WindowManager
import android.widget.TextView
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ViewHierarchyAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
@@ -124,7 +126,6 @@ class MediaTttChipControllerSender @Inject constructor(
        currentChipView.requireViewById<View>(R.id.loading).visibility =
            chipState.isMidTransfer.visibleIfTrue()


        // Undo
        val undoView = currentChipView.requireViewById<View>(R.id.undo)
        val undoClickListener = chipState.undoClickListener(
@@ -138,6 +139,17 @@ class MediaTttChipControllerSender @Inject constructor(
            chipState.isTransferFailure.visibleIfTrue()
    }

    override fun animateChipIn(chipView: ViewGroup) {
        ViewHierarchyAnimator.animateAddition(
            chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner),
            ViewHierarchyAnimator.Hotspot.TOP,
            Interpolators.EMPHASIZED_DECELERATE,
            duration = 500L,
            includeMargins = true,
            includeFadeIn = true,
        )
    }

    override fun removeChip(removalReason: String) {
        // Don't remove the chip if we're mid-transfer since the user should still be able to
        // see the status of the transfer. (But do remove it if it's finally timed out.)
Loading