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

Commit 7217e9a5 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov Committed by Automerger Merge Worker
Browse files

Merge "[Media TTT] Don't use an animated-vector for the loading spinner." into...

Merge "[Media TTT] Don't use an animated-vector for the loading spinner." into tm-qpr-dev am: 9f085a91 am: 7f74e101

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



Change-Id: I3b8f29569f1a9f90a3d4136bd038db8f8fa679e1
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents db78c254 7f74e101
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2023 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="48dp"
    android:height="48dp"
    android:viewportWidth="48"
    android:viewportHeight="48"
    android:tint="?attr/colorControlNormal">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M24,44Q19.8,44 16.15,42.45Q12.5,40.9 9.8,38.2Q7.1,35.5 5.55,31.85Q4,28.2 4,24Q4,19.8 5.55,16.15Q7.1,12.5 9.8,9.8Q12.5,7.1 16.15,5.55Q19.8,4 24,4Q24.6,4 25.05,4.45Q25.5,4.9 25.5,5.5Q25.5,6.1 25.05,6.55Q24.6,7 24,7Q16.95,7 11.975,11.975Q7,16.95 7,24Q7,31.05 11.975,36.025Q16.95,41 24,41Q31.05,41 36.025,36.025Q41,31.05 41,24Q41,23.4 41.45,22.95Q41.9,22.5 42.5,22.5Q43.1,22.5 43.55,22.95Q44,23.4 44,24Q44,28.2 42.45,31.85Q40.9,35.5 38.2,38.2Q35.5,40.9 31.85,42.45Q28.2,44 24,44Z"/>
</vector>
+3 −4
Original line number Diff line number Diff line
@@ -60,14 +60,13 @@
            />

        <!-- At most one of [loading, failure_icon, undo] will be visible at a time. -->
        <ProgressBar
        <ImageView
            android:id="@+id/loading"
            android:indeterminate="true"
            android:layout_width="@dimen/media_ttt_status_icon_size"
            android:layout_height="@dimen/media_ttt_status_icon_size"
            android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
            android:indeterminateTint="?androidprv:attr/colorAccentPrimaryVariant"
            style="?android:attr/progressBarStyleSmall"
            android:src="@drawable/ic_progress_activity"
            android:tint="?androidprv:attr/colorAccentPrimaryVariant"
            android:alpha="0.0"
            />

+58 −5
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.systemui.temporarydisplay.chipbar

import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Rect
import android.os.PowerManager
@@ -27,11 +29,14 @@ import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.IdRes
import androidx.annotation.VisibleForTesting
import com.android.internal.widget.CachingIconView
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text.Companion.loadText
@@ -101,6 +106,15 @@ constructor(

    private lateinit var parent: ChipbarRootView

    /** The current loading information, or null we're not currently loading. */
    @VisibleForTesting
    internal var loadingDetails: LoadingDetails? = null
        private set(value) {
            // Always cancel the old one before updating
            field?.animator?.cancel()
            field = value
        }

    override val windowLayoutParams =
        commonWindowLayoutParams.apply { gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL) }

@@ -143,8 +157,22 @@ constructor(

        // ---- End item ----
        // Loading
        currentView.requireViewById<View>(R.id.loading).visibility =
            (newInfo.endItem == ChipbarEndItem.Loading).visibleIfTrue()
        val isLoading = newInfo.endItem == ChipbarEndItem.Loading
        val loadingView = currentView.requireViewById<ImageView>(R.id.loading)
        loadingView.visibility = isLoading.visibleIfTrue()

        if (isLoading) {
            val currentLoadingDetails = loadingDetails
            // Since there can be multiple chipbars, we need to check if the loading view is the
            // same and possibly re-start the loading animation on the new view.
            if (currentLoadingDetails == null || currentLoadingDetails.loadingView != loadingView) {
                val newDetails = createLoadingDetails(loadingView)
                newDetails.animator.start()
                loadingDetails = newDetails
            }
        } else {
            loadingDetails = null
        }

        // Error
        currentView.requireViewById<View>(R.id.error).visibility =
@@ -223,12 +251,17 @@ constructor(
    override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
        val innerView = view.getInnerView()
        innerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_NONE
        val removed = chipbarAnimator.animateViewOut(innerView, onAnimationEnd)

        val fullEndRunnable = Runnable {
            loadingDetails = null
            onAnimationEnd.run()
        }
        val removed = chipbarAnimator.animateViewOut(innerView, fullEndRunnable)
        // If the view doesn't get animated, the [onAnimationEnd] runnable won't get run. So, just
        // run it immediately.
        if (!removed) {
            logger.logAnimateOutFailure()
            onAnimationEnd.run()
            fullEndRunnable.run()
        }

        updateGestureListening()
@@ -269,7 +302,7 @@ constructor(
    }

    private fun ViewGroup.getInnerView(): ViewGroup {
        return requireViewById(R.id.chipbar_inner)
        return this.requireViewById(R.id.chipbar_inner)
    }

    override fun getTouchableRegion(view: View, outRect: Rect) {
@@ -283,8 +316,28 @@ constructor(
            View.GONE
        }
    }

    private fun createLoadingDetails(loadingView: View): LoadingDetails {
        // Ideally, we would use a <ProgressBar> view, which would automatically handle the loading
        // spinner rotation for us. However, due to b/243983980, the ProgressBar animation
        // unexpectedly pauses when SysUI starts another window. ObjectAnimator is a workaround that
        // won't pause.
        val animator =
            ObjectAnimator.ofFloat(loadingView, View.ROTATION, 0f, 360f).apply {
                duration = LOADING_ANIMATION_DURATION_MS
                repeatCount = ValueAnimator.INFINITE
                interpolator = Interpolators.LINEAR
            }
        return LoadingDetails(loadingView, animator)
    }

    internal data class LoadingDetails(
        val loadingView: View,
        val animator: ObjectAnimator,
    )
}

@IdRes private val INFO_TAG = R.id.tag_chipbar_info
private const val SWIPE_UP_GESTURE_REASON = "SWIPE_UP_GESTURE_DETECTED"
private const val TAG = "ChipbarCoordinator"
private const val LOADING_ANIMATION_DURATION_MS = 1000L
+100 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
import android.widget.TextView
import androidx.core.animation.doOnCancel
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
@@ -360,6 +361,105 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
        assertThat(isClicked).isTrue()
    }

    @Test
    fun displayView_loading_animationStarted() {
        underTest.displayView(
            createChipbarInfo(
                Icon.Resource(R.id.check_box, null),
                Text.Loaded("text"),
                endItem = ChipbarEndItem.Loading,
            )
        )

        assertThat(underTest.loadingDetails!!.animator.isStarted).isTrue()
    }

    @Test
    fun displayView_notLoading_noAnimation() {
        underTest.displayView(
            createChipbarInfo(
                Icon.Resource(R.id.check_box, null),
                Text.Loaded("text"),
                endItem = ChipbarEndItem.Error,
            )
        )

        assertThat(underTest.loadingDetails).isNull()
    }

    @Test
    fun displayView_loadingThenNotLoading_animationStopped() {
        underTest.displayView(
            createChipbarInfo(
                Icon.Resource(R.id.check_box, null),
                Text.Loaded("text"),
                endItem = ChipbarEndItem.Loading,
            )
        )

        val animator = underTest.loadingDetails!!.animator
        var cancelled = false
        animator.doOnCancel { cancelled = true }

        underTest.displayView(
            createChipbarInfo(
                Icon.Resource(R.id.check_box, null),
                Text.Loaded("text"),
                endItem = ChipbarEndItem.Button(Text.Loaded("button")) {},
            )
        )

        assertThat(cancelled).isTrue()
        assertThat(underTest.loadingDetails).isNull()
    }

    @Test
    fun displayView_loadingThenHideView_animationStopped() {
        underTest.displayView(
            createChipbarInfo(
                Icon.Resource(R.id.check_box, null),
                Text.Loaded("text"),
                endItem = ChipbarEndItem.Loading,
            )
        )

        val animator = underTest.loadingDetails!!.animator
        var cancelled = false
        animator.doOnCancel { cancelled = true }

        underTest.removeView(DEVICE_ID, "TestReason")

        assertThat(cancelled).isTrue()
        assertThat(underTest.loadingDetails).isNull()
    }

    @Test
    fun displayView_loadingThenNewLoading_animationStaysTheSame() {
        underTest.displayView(
            createChipbarInfo(
                Icon.Resource(R.id.check_box, null),
                Text.Loaded("text"),
                endItem = ChipbarEndItem.Loading,
            )
        )

        val animator = underTest.loadingDetails!!.animator
        var cancelled = false
        animator.doOnCancel { cancelled = true }

        underTest.displayView(
            createChipbarInfo(
                Icon.Resource(R.id.check_box, null),
                Text.Loaded("new text"),
                endItem = ChipbarEndItem.Loading,
            )
        )

        assertThat(underTest.loadingDetails!!.animator).isEqualTo(animator)
        assertThat(underTest.loadingDetails!!.animator.isStarted).isTrue()
        assertThat(cancelled).isFalse()
    }

    @Test
    fun displayView_vibrationEffect_doubleClickEffect() {
        underTest.displayView(