summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author William Xiao <wxyz@google.com> 2024-08-26 21:47:22 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-08-26 21:47:22 +0000
commit23a02a0efc2fc20db1f752e2c5e339b76808c44f (patch)
tree31485084085d4db4c8d081d6f8ae453ae5007b56
parent2c836346d5710a668415ac4def590ebf41e36536 (diff)
parent9aa24c21eb8314c5ccbc6e9c9faa41aa4250d737 (diff)
Merge "Add logging for glanceable hub touch handling" into main
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/CommunalTouchLog.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt137
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt13
4 files changed, 156 insertions, 29 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalTouchLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalTouchLog.kt
new file mode 100644
index 000000000000..b0abdb7c128d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalTouchLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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.log.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for communal touch-handling logging. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class CommunalTouchLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 5cae58a81cf9..ba3c1d216099 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -618,6 +618,16 @@ public class LogModule {
}
/**
+ * Provides a {@link LogBuffer} for communal touch-handling logs.
+ */
+ @Provides
+ @SysUISingleton
+ @CommunalTouchLog
+ public static LogBuffer provideCommunalTouchLogBuffer(LogBufferFactory factory) {
+ return factory.create("CommunalTouchLog", 250);
+ }
+
+ /**
* Provides a {@link TableLogBuffer} for communal-related logs.
*/
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 181c3df92222..6223ca778815 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -33,6 +33,8 @@ import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.lifecycleScope
@@ -55,6 +57,9 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalTouchLog
import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -91,8 +96,10 @@ constructor(
@Communal private val dataSourceDelegator: SceneDataSourceDelegator,
private val notificationStackScrollLayoutController: NotificationStackScrollLayoutController,
private val keyguardMediaController: KeyguardMediaController,
- private val lockscreenSmartspaceController: LockscreenSmartspaceController
+ private val lockscreenSmartspaceController: LockscreenSmartspaceController,
+ @CommunalTouchLog logBuffer: LogBuffer,
) : LifecycleOwner {
+ private val logger = Logger(logBuffer, "GlanceableHubContainerController")
private class CommunalWrapper(context: Context) : FrameLayout(context) {
private val consumers: MutableSet<Consumer<Boolean>> = ArraySet()
@@ -143,6 +150,17 @@ constructor(
private var isTrackingHubTouch = false
/**
+ * True if a touch gesture on the lock screen has been consumed by the shade/bouncer and thus
+ * should be ignored by the hub.
+ *
+ * This is necessary on the lock screen as gestures on an empty spot go through special touch
+ * handling logic in [NotificationShadeWindowViewController] that decides if they should go to
+ * the shade or bouncer. Once the shade or bouncer are moving, we don't get the typical cancel
+ * event so to play nice, we ignore touches once we see the shade or bouncer are opening.
+ */
+ private var touchTakenByKeyguardGesture = false
+
+ /**
* True if the hub UI is fully open, meaning it should receive touch input.
*
* Tracks [CommunalInteractor.isCommunalShowing].
@@ -206,6 +224,21 @@ constructor(
*/
private var isDreaming = false
+ /** Observes and logs state when the lifecycle that controls the [touchMonitor] updates. */
+ private val touchLifecycleLogger: LifecycleObserver = LifecycleEventObserver { _, event ->
+ logger.d({
+ "Touch handler lifecycle changed to $str1. hubShowing: $bool1, " +
+ "shadeShowingAndConsumingTouches: $bool2, " +
+ "anyBouncerShowing: $bool3, inEditModeTransition: $bool4"
+ }) {
+ str1 = event.toString()
+ bool1 = hubShowing
+ bool2 = shadeShowingAndConsumingTouches
+ bool3 = anyBouncerShowing
+ bool4 = inEditModeTransition
+ }
+ }
+
/** Returns a flow that tracks whether communal hub is available. */
fun communalAvailable(): Flow<Boolean> =
anyOf(communalInteractor.isCommunalAvailable, communalInteractor.editModeOpen)
@@ -268,6 +301,7 @@ constructor(
init()
}
}
+ lifecycleRegistry.addObserver(touchLifecycleLogger)
lifecycleRegistry.currentState = Lifecycle.State.CREATED
communalContainerView = containerView
@@ -328,6 +362,9 @@ constructor(
backGestureInset
)
}
+ logger.d({ "Insets updated: $str1" }) {
+ str1 = containerView.systemGestureExclusionRects.toString()
+ }
}
}
@@ -343,6 +380,9 @@ constructor(
),
{
anyBouncerShowing = it
+ if (hubShowing) {
+ logger.d({ "New value for anyBouncerShowing: $bool1" }) { bool1 = it }
+ }
updateTouchHandlingState()
}
)
@@ -396,7 +436,13 @@ constructor(
// If the shade reaches full expansion without interaction, then we should allow it
// to consume touches rather than handling it here until it disappears.
shadeShowingAndConsumingTouches =
- userNotInteractiveAtShadeFullyExpanded || expandedAndNotInteractive
+ (userNotInteractiveAtShadeFullyExpanded || expandedAndNotInteractive).also {
+ if (it != shadeShowingAndConsumingTouches && hubShowing) {
+ logger.d({ "New value for shadeShowingAndConsumingTouches: $bool1" }) {
+ bool1 = it
+ }
+ }
+ }
updateTouchHandlingState()
}
)
@@ -404,6 +450,7 @@ constructor(
communalContainerWrapper = CommunalWrapper(containerView.context)
communalContainerWrapper?.addView(communalContainerView)
+ logger.d("Hub container initialized")
return communalContainerWrapper!!
}
@@ -446,6 +493,10 @@ constructor(
(it.parent as ViewGroup).removeView(it)
communalContainerWrapper = null
}
+
+ lifecycleRegistry.removeObserver(touchLifecycleLogger)
+
+ logger.d("Hub container disposed")
}
/**
@@ -463,15 +514,20 @@ constructor(
// In the case that we are handling full swipes on the lockscreen, are on the lockscreen,
// and the touch is within the horizontal notification band on the screen, do not process
// the touch.
- if (
- !hubShowing &&
- (!notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y) ||
- keyguardMediaController.isWithinMediaViewBounds(ev.x.toInt(), ev.y.toInt()) ||
- lockscreenSmartspaceController.isWithinSmartspaceBounds(
- ev.x.toInt(),
- ev.y.toInt()
- ))
- ) {
+ val touchOnNotifications =
+ !notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y)
+ val touchOnUmo = keyguardMediaController.isWithinMediaViewBounds(ev.x.toInt(), ev.y.toInt())
+ val touchOnSmartspace =
+ lockscreenSmartspaceController.isWithinSmartspaceBounds(ev.x.toInt(), ev.y.toInt())
+ if (!hubShowing && (touchOnNotifications || touchOnUmo || touchOnSmartspace)) {
+ logger.d({
+ "Lockscreen touch ignored: touchOnNotifications: $bool1, touchOnUmo: $bool2, " +
+ "touchOnSmartspace: $bool3"
+ }) {
+ bool1 = touchOnNotifications
+ bool2 = touchOnUmo
+ bool3 = touchOnSmartspace
+ }
return false
}
@@ -487,12 +543,56 @@ constructor(
val hubOccluded = anyBouncerShowing || shadeShowingAndConsumingTouches
if ((isDown || isMove) && !hubOccluded) {
+ if (isDown) {
+ logger.d({
+ "Touch started. x: $int1, y: $int2, hubShowing: $bool1, isDreaming: $bool2, " +
+ "onLockscreen: $bool3"
+ }) {
+ int1 = ev.x.toInt()
+ int2 = ev.y.toInt()
+ bool1 = hubShowing
+ bool2 = isDreaming
+ bool3 = onLockscreen
+ }
+ }
isTrackingHubTouch = true
}
if (isTrackingHubTouch) {
+ // On the lock screen, our touch handlers are not active and we rely on the NSWVC's
+ // touch handling for gestures on blank areas, which can go up to show the bouncer or
+ // down to show the notification shade. We see the touches first and they are not
+ // consumed and cancelled like on the dream or hub so we have to gracefully ignore them
+ // if the shade or bouncer are handling them. This issue only applies to touches on the
+ // keyguard itself, once the bouncer or shade are fully open, our logic stops us from
+ // taking touches.
+ touchTakenByKeyguardGesture =
+ (onLockscreen && (shadeConsumingTouches || anyBouncerShowing)).also {
+ if (it != touchTakenByKeyguardGesture && it) {
+ logger.d(
+ "Lock screen touch consumed by shade or bouncer, ignoring " +
+ "subsequent touches"
+ )
+ }
+ }
if (isUp || isCancel) {
+ logger.d({
+ val endReason = if (bool1) "up" else "cancel"
+ "Touch ended with $endReason. x: $int1, y: $int2, " +
+ "shadeConsumingTouches: $bool2, anyBouncerShowing: $bool3"
+ }) {
+ int1 = ev.x.toInt()
+ int2 = ev.y.toInt()
+ bool1 = isUp
+ bool2 = shadeConsumingTouches
+ bool3 = anyBouncerShowing
+ }
isTrackingHubTouch = false
+
+ // Clear out touch taken state to ensure the up/cancel event still gets dispatched
+ // to the hub. This is necessary as the hub always receives at least the initial
+ // down even if the shade or bouncer end up handling the touch.
+ touchTakenByKeyguardGesture = false
}
return dispatchTouchEvent(ev)
}
@@ -513,21 +613,8 @@ constructor(
return true
}
try {
- // On the lock screen, our touch handlers are not active and we rely on the NSWVC's
- // touch handling for gestures on blank areas, which can go up to show the bouncer or
- // down to show the notification shade. We see the touches first and they are not
- // consumed and cancelled like on the dream or hub so we have to gracefully ignore them
- // if the shade or bouncer are handling them. This issue only applies to touches on the
- // keyguard itself, once the bouncer or shade are fully open, our logic stops us from
- // taking touches.
- val touchTaken = onLockscreen && (shadeConsumingTouches || anyBouncerShowing)
-
- // Only dispatch touches to communal if not already handled or the touch is ending,
- // meaning the event is an up or cancel. This is necessary as the hub always receives at
- // least the initial down even if the shade or bouncer end up handling the touch.
- val dispatchToCommunal = !touchTaken || !isTrackingHubTouch
var handled = false
- if (dispatchToCommunal) {
+ if (!touchTakenByKeyguardGesture) {
communalContainerWrapper?.dispatchTouchEvent(ev) {
if (it) {
handled = true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index b67e111af13d..90c70f26e26b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -58,6 +58,7 @@ import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.media.controls.controller.keyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.sceneDataSourceDelegator
@@ -140,7 +141,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
kosmos.sceneDataSourceDelegator,
kosmos.notificationStackScrollLayoutController,
kosmos.keyguardMediaController,
- kosmos.lockscreenSmartspaceController
+ kosmos.lockscreenSmartspaceController,
+ logcatLogBuffer("GlanceableHubContainerControllerTest")
)
}
testableLooper = TestableLooper.get(this)
@@ -186,7 +188,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
kosmos.sceneDataSourceDelegator,
kosmos.notificationStackScrollLayoutController,
kosmos.keyguardMediaController,
- kosmos.lockscreenSmartspaceController
+ kosmos.lockscreenSmartspaceController,
+ logcatLogBuffer("GlanceableHubContainerControllerTest")
)
// First call succeeds.
@@ -214,7 +217,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
kosmos.sceneDataSourceDelegator,
kosmos.notificationStackScrollLayoutController,
kosmos.keyguardMediaController,
- kosmos.lockscreenSmartspaceController
+ kosmos.lockscreenSmartspaceController,
+ logcatLogBuffer("GlanceableHubContainerControllerTest")
)
assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
@@ -237,7 +241,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
kosmos.sceneDataSourceDelegator,
kosmos.notificationStackScrollLayoutController,
kosmos.keyguardMediaController,
- kosmos.lockscreenSmartspaceController
+ kosmos.lockscreenSmartspaceController,
+ logcatLogBuffer("GlanceableHubContainerControllerTest")
)
// Only initView without attaching a view as we don't want the flows to start collecting