From 8306072389fd672429f6825f5f869dd28f54f399 Mon Sep 17 00:00:00 2001 From: Nate Myren Date: Tue, 4 May 2021 19:12:09 -0700 Subject: [PATCH] Ensure that animation does not play after brief lapses in app usage If an app stops using mic/camera for less than three seconds, and then starts again, only show the dot. Fixes: 187051104 Test: Went into camera, and switch modes several times Change-Id: I496046e5d18417e62f1c898340ac6ad06364514a --- .../events/PrivacyDotViewController.kt | 26 ++++++++++++++----- .../events/SystemEventCoordinator.kt | 25 +++++++++++++++--- .../events/SystemStatusAnimationScheduler.kt | 19 +++++++++----- 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt index b3f7ca6f26305..eb3a17f4ccce4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt @@ -181,12 +181,19 @@ class PrivacyDotViewController @Inject constructor( designatedCorner = newCorner if (animationScheduler.hasPersistentDot) { - designatedCorner!!.visibility = View.VISIBLE - designatedCorner!!.alpha = 0f - designatedCorner!!.animate() - .alpha(1.0f) - .setDuration(300) - .start() + fadeInDot() + } + } + + @UiThread + private fun fadeInDot() { + designatedCorner?.let { dot -> + dot.visibility = View.VISIBLE + dot.alpha = 0f + dot.animate() + .alpha(1.0f) + .setDuration(300) + .start() } } @@ -300,9 +307,14 @@ class PrivacyDotViewController @Inject constructor( private val systemStatusAnimationCallback: SystemStatusAnimationCallback = object : SystemStatusAnimationCallback { - override fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? { + override fun onSystemStatusAnimationTransitionToPersistentDot( + showAnimation: Boolean + ): Animator? { if (designatedCorner == null) { return null + } else if (!showAnimation) { + uiExecutor?.execute { fadeInDot() } + return null } val alpha = ObjectAnimator.ofFloat( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt index bde085e4e0b88..20135447c3654 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.events +import android.os.SystemClock import com.android.systemui.dagger.SysUISingleton import com.android.systemui.privacy.PrivacyItem import com.android.systemui.privacy.PrivacyItemController @@ -59,10 +60,10 @@ class SystemEventCoordinator @Inject constructor( scheduler.setShouldShowPersistentPrivacyIndicator(false) } - fun notifyPrivacyItemsChanged() { + fun notifyPrivacyItemsChanged(showAnimation: Boolean = true) { val event = PrivacyEvent() event.privacyItems = privacyStateListener.currentPrivacyItems - scheduler.onStatusEvent(event) + scheduler.onStatusEvent(event, showAnimation) } private val batteryStateListener = object : BatteryController.BatteryStateChangeCallback { @@ -90,8 +91,17 @@ class SystemEventCoordinator @Inject constructor( private val privacyStateListener = object : PrivacyItemController.Callback { var currentPrivacyItems = listOf() + var previousPrivacyItems = listOf() + var timeLastEmpty = SystemClock.elapsedRealtime() override fun onPrivacyItemsChanged(privacyItems: List) { + if (uniqueItemsMatch(privacyItems, currentPrivacyItems)) { + return + } else if (privacyItems.isEmpty()) { + previousPrivacyItems = currentPrivacyItems + timeLastEmpty = SystemClock.elapsedRealtime() + } + currentPrivacyItems = privacyItems notifyListeners() } @@ -100,10 +110,19 @@ class SystemEventCoordinator @Inject constructor( if (currentPrivacyItems.isEmpty()) { notifyPrivacyItemsEmpty() } else { - notifyPrivacyItemsChanged() + val showAnimation = !uniqueItemsMatch(currentPrivacyItems, previousPrivacyItems) || + SystemClock.elapsedRealtime() - timeLastEmpty >= DEBOUNCE_TIME + notifyPrivacyItemsChanged(showAnimation) } } + + // Return true if the lists contain the same permission groups, used by the same UIDs + private fun uniqueItemsMatch(one: List, two: List): Boolean { + return one.map { it.application.uid to it.privacyType.permGroupName }.toSet() == + two.map { it.application.uid to it.privacyType.permGroupName }.toSet() + } } } +private const val DEBOUNCE_TIME = 3000L private const val TAG = "SystemEventCoordinator" \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt index 8da7fda242c7b..2f565037a359c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt @@ -90,7 +90,7 @@ class SystemStatusAnimationScheduler @Inject constructor( coordinator.attachScheduler(this) } - fun onStatusEvent(event: StatusEvent) { + fun onStatusEvent(event: StatusEvent, showAnimation: Boolean = true) { // Ignore any updates until the system is up and running if (isTooEarly() || !isImmersiveIndicatorEnabled()) { return @@ -103,7 +103,12 @@ class SystemStatusAnimationScheduler @Inject constructor( if (DEBUG) { Log.d(TAG, "scheduling event $event") } - scheduleEvent(event) + if (showAnimation) { + scheduleEvent(event) + } else if (event.forceVisible) { + hasPersistentDot = true + notifyTransitionToPersistentDot(showAnimation = false) + } } else { if (DEBUG) { Log.d(TAG, "ignoring event $event") @@ -197,7 +202,7 @@ class SystemStatusAnimationScheduler @Inject constructor( aSet2.play(chipAnimator).before(systemAnimator) if (hasPersistentDot) { - val dotAnim = notifyTransitionToPersistentDot() + val dotAnim = notifyTransitionToPersistentDot(showAnimation = true) if (dotAnim != null) aSet2.playTogether(systemAnimator, dotAnim) } @@ -209,9 +214,9 @@ class SystemStatusAnimationScheduler @Inject constructor( }, DELAY) } - private fun notifyTransitionToPersistentDot(): Animator? { + private fun notifyTransitionToPersistentDot(showAnimation: Boolean): Animator? { val anims: List = listeners.mapNotNull { - it.onSystemStatusAnimationTransitionToPersistentDot() + it.onSystemStatusAnimationTransitionToPersistentDot(showAnimation) } if (anims.isNotEmpty()) { val aSet = AnimatorSet() @@ -321,7 +326,9 @@ interface SystemStatusAnimationCallback { @JvmDefault fun onSystemChromeAnimationEnd() {} // Best method name, change my mind - @JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? { return null } + @JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot( + showAnimation: Boolean + ): Animator? { return null } @JvmDefault fun onHidePersistentDot(): Animator? { return null } }