summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author William Xiao <wxyz@google.com> 2023-12-15 20:48:57 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-12-15 20:48:57 +0000
commit2adc8deaad3e8e7017613f363264f52af83030b3 (patch)
treec0e560668d12720a952518879988c4d1cd0d0f8d
parent7f95d90fa6ab6c49c34b1b01ed1a1b4460e4ea9d (diff)
parent5dd441575dac16080e3a0119080969ff90f4815a (diff)
Merge "Move glanceable hub container to the bottom of NotificationShadeWindowView" into main
-rw-r--r--packages/SystemUI/res/layout/super_notification_shade.xml12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt186
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt220
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt10
7 files changed, 456 insertions, 44 deletions
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index ca0fb85beb6e..87c31c8b8aae 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -26,6 +26,12 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
+ <!-- Placeholder for the communal UI that will be replaced if the feature is enabled. -->
+ <ViewStub
+ android:id="@+id/communal_ui_stub"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
<com.android.systemui.scrim.ScrimView
android:id="@+id/scrim_behind"
android:layout_width="match_parent"
@@ -64,12 +70,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <!-- Placeholder for the communal UI that will be replaced if the feature is enabled. -->
- <ViewStub
- android:id="@+id/communal_ui_stub"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
<include layout="@layout/brightness_mirror_container" />
<com.android.systemui.scrim.ScrimView
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index cae6147b03ee..f5bcab96a5a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -80,6 +80,11 @@ enum class KeyguardState {
return state != GONE
}
+ /** Whether either of the bouncers are visible when we're FINISHED in the given state. */
+ fun isBouncerState(state: KeyguardState): Boolean {
+ return state == PRIMARY_BOUNCER || state == ALTERNATE_BOUNCER
+ }
+
/**
* Whether the device is awake ([PowerInteractor.isAwake]) when we're FINISHED in the given
* keyguard state.
@@ -107,4 +112,4 @@ enum class KeyguardState {
return !deviceIsAwakeInState(state)
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
new file mode 100644
index 000000000000..ab69acbc6e9d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.shade
+
+import android.content.Context
+import android.view.GestureDetector
+import android.view.MotionEvent
+import android.view.View
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.compose.ComposeFacade.createCommunalContainer
+import com.android.systemui.compose.ComposeFacade.isComposeAvailable
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.util.kotlin.collectFlow
+import javax.inject.Inject
+
+/**
+ * Controller that's responsible for the glanceable hub container view and its touch handling.
+ *
+ * This will be used until the glanceable hub is integrated into Flexiglass.
+ */
+class GlanceableHubContainerController
+@Inject
+constructor(
+ private val communalInteractor: CommunalInteractor,
+ private val communalViewModel: CommunalViewModel,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val shadeInteractor: ShadeInteractor,
+) {
+ /** The container view for the hub. This will not be initialized until [initView] is called. */
+ private lateinit var communalContainerView: View
+
+ /**
+ * The width of the area in which a right edge swipe can open the hub, in pixels. Read from
+ * resources when [initView] is called.
+ */
+ private var edgeSwipeRegionWidth: Int = 0
+
+ /**
+ * True if we are currently tracking a gesture for opening the hub that started in the edge
+ * swipe region.
+ */
+ private var isTrackingOpenGesture = false
+
+ /**
+ * True if the hub UI is fully open, meaning it should receive touch input.
+ *
+ * Tracks [CommunalInteractor.isCommunalShowing].
+ */
+ private var hubShowing = false
+
+ /**
+ * True if either the primary or alternate bouncer are open, meaning the hub should not receive
+ * any touch input.
+ *
+ * Tracks [KeyguardTransitionInteractor.isFinishedInState] for [KeyguardState.isBouncerState].
+ */
+ private var anyBouncerShowing = false
+
+ /**
+ * True if the shade is fully expanded, meaning the hub should not receive any touch input.
+ *
+ * Tracks [ShadeInteractor.isAnyFullyExpanded].
+ */
+ private var shadeShowing = false
+
+ /** Returns true if the glanceable hub is enabled and the container view can be created. */
+ fun isEnabled(): Boolean {
+ return communalInteractor.isCommunalEnabled && isComposeAvailable()
+ }
+
+ /**
+ * Creates the container view containing the glanceable hub UI.
+ *
+ * @throws RuntimeException if [isEnabled] is false or the view is already initialized
+ */
+ fun initView(
+ context: Context,
+ ): View {
+ return initView(createCommunalContainer(context, communalViewModel))
+ }
+
+ /** Override for testing. */
+ @VisibleForTesting
+ internal fun initView(containerView: View): View {
+ if (!isEnabled()) {
+ throw RuntimeException("Glanceable hub is not enabled")
+ }
+ if (::communalContainerView.isInitialized) {
+ throw RuntimeException("Communal view has already been initialized")
+ }
+
+ communalContainerView = containerView
+
+ edgeSwipeRegionWidth =
+ communalContainerView.resources.getDimensionPixelSize(R.dimen.communal_grid_gutter_size)
+
+ collectFlow(
+ communalContainerView,
+ keyguardTransitionInteractor.isFinishedInStateWhere(KeyguardState::isBouncerState),
+ { anyBouncerShowing = it }
+ )
+ collectFlow(
+ communalContainerView,
+ communalInteractor.isCommunalShowing,
+ { hubShowing = it }
+ )
+ collectFlow(
+ communalContainerView,
+ shadeInteractor.isAnyFullyExpanded,
+ { shadeShowing = it }
+ )
+
+ return communalContainerView
+ }
+
+ /**
+ * Notifies the hub container of a touch event. Returns true if it's determined that the touch
+ * should go to the hub container and no one else.
+ *
+ * Special handling is needed because the hub container sits at the lowest z-order in
+ * [NotificationShadeWindowView] and would not normally receive touches. We also cannot use a
+ * [GestureDetector] as the hub container's SceneTransitionLayout is a Compose view that expects
+ * to be fully in control of its own touch handling.
+ */
+ fun onTouchEvent(ev: MotionEvent): Boolean {
+ if (!::communalContainerView.isInitialized) {
+ return false
+ }
+
+ val isDown = ev.actionMasked == MotionEvent.ACTION_DOWN
+ val isUp = ev.actionMasked == MotionEvent.ACTION_UP
+ val isCancel = ev.actionMasked == MotionEvent.ACTION_CANCEL
+
+ // TODO(b/315207481): also account for opening animations of shade/bouncer and not just
+ // fully showing state
+ val hubOccluded = anyBouncerShowing || shadeShowing
+
+ // If the hub is fully visible, send all touch events to it.
+ val communalVisible = hubShowing && !hubOccluded
+ if (communalVisible) {
+ return communalContainerView.dispatchTouchEvent(ev)
+ }
+
+ if (edgeSwipeRegionWidth == 0) {
+ // If the edge region width has not been read yet or whatever reason, don't bother
+ // intercepting touches to open the hub.
+ return false
+ }
+
+ if (!isTrackingOpenGesture && isDown) {
+ val x = ev.rawX
+ val inOpeningSwipeRegion: Boolean =
+ x >= communalContainerView.width - edgeSwipeRegionWidth
+ if (inOpeningSwipeRegion && !hubOccluded) {
+ isTrackingOpenGesture = true
+ return communalContainerView.dispatchTouchEvent(ev)
+ }
+ } else if (isTrackingOpenGesture) {
+ if (isUp || isCancel) {
+ isTrackingOpenGesture = false
+ }
+ return communalContainerView.dispatchTouchEvent(ev)
+ }
+
+ return false
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 73537edcc94a..07ce57735dc7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -42,9 +42,6 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.bouncer.ui.binder.KeyguardBouncerViewBinder;
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel;
import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.communal.data.repository.CommunalRepository;
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
-import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.dock.DockManager;
@@ -108,14 +105,14 @@ public class NotificationShadeWindowViewController implements Dumpable {
private final PulsingGestureListener mPulsingGestureListener;
private final LockscreenHostedDreamGestureListener mLockscreenHostedDreamGestureListener;
private final NotificationInsetsController mNotificationInsetsController;
- private final CommunalViewModel mCommunalViewModel;
- private final CommunalRepository mCommunalRepository;
private final boolean mIsTrackpadCommonEnabled;
private final FeatureFlagsClassic mFeatureFlagsClassic;
private final SysUIKeyEventHandler mSysUIKeyEventHandler;
private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
private final QuickSettingsController mQuickSettingsController;
+ private final GlanceableHubContainerController
+ mGlanceableHubContainerController;
private GestureDetector mPulsingWakeupGestureHandler;
private GestureDetector mDreamingWakeupGestureHandler;
private View mBrightnessMirror;
@@ -183,8 +180,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
KeyguardTransitionInteractor keyguardTransitionInteractor,
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
- CommunalViewModel communalViewModel,
- CommunalRepository communalRepository,
+ GlanceableHubContainerController glanceableHubContainerController,
NotificationLaunchAnimationInteractor notificationLaunchAnimationInteractor,
FeatureFlagsClassic featureFlagsClassic,
SystemClock clock,
@@ -217,8 +213,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
mPulsingGestureListener = pulsingGestureListener;
mLockscreenHostedDreamGestureListener = lockscreenHostedDreamGestureListener;
mNotificationInsetsController = notificationInsetsController;
- mCommunalViewModel = communalViewModel;
- mCommunalRepository = communalRepository;
+ mGlanceableHubContainerController = glanceableHubContainerController;
mIsTrackpadCommonEnabled = featureFlagsClassic.isEnabled(TRACKPAD_GESTURE_COMMON);
mFeatureFlagsClassic = featureFlagsClassic;
mSysUIKeyEventHandler = sysUIKeyEventHandler;
@@ -347,6 +342,10 @@ public class NotificationShadeWindowViewController implements Dumpable {
mFalsingCollector.onTouchEvent(ev);
mPulsingWakeupGestureHandler.onTouchEvent(ev);
+
+ if (mGlanceableHubContainerController.onTouchEvent(ev)) {
+ return logDownDispatch(ev, "dispatched to glanceable hub container", true);
+ }
if (mDreamingWakeupGestureHandler != null
&& mDreamingWakeupGestureHandler.onTouchEvent(ev)) {
return logDownDispatch(ev, "dream wakeup gesture handled", true);
@@ -587,14 +586,13 @@ public class NotificationShadeWindowViewController implements Dumpable {
}
/**
- * Sets up the communal hub UI if the {@link com.android.systemui.Flags#FLAG_COMMUNAL_HUB} flag
- * is enabled.
+ * Sets up the glanceable hub UI if the {@link com.android.systemui.Flags#FLAG_COMMUNAL_HUB}
+ * flag is enabled.
*
- * The layout lives in {@link R.id.communal_ui_container}.
+ * The layout lives in {@link R.id.communal_ui_stub}.
*/
public void setupCommunalHubLayout() {
- if (!mCommunalRepository.isCommunalEnabled()
- || !ComposeFacade.INSTANCE.isComposeAvailable()) {
+ if (!mGlanceableHubContainerController.isEnabled()) {
return;
}
@@ -602,8 +600,8 @@ public class NotificationShadeWindowViewController implements Dumpable {
View communalPlaceholder = mView.findViewById(R.id.communal_ui_stub);
int index = mView.indexOfChild(communalPlaceholder);
mView.removeView(communalPlaceholder);
- mView.addView(ComposeFacade.INSTANCE.createCommunalContainer(mView.getContext(),
- mCommunalViewModel), index);
+
+ mView.addView(mGlanceableHubContainerController.initView(mView.getContext()), index);
}
private boolean didNotificationPanelInterceptEvent(MotionEvent ev) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
new file mode 100644
index 000000000000..5569ca9520e9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.shade
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.ViewUtils
+import android.view.MotionEvent
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
+import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
+import org.junit.Assert.assertThrows
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class GlanceableHubContainerControllerTest : SysuiTestCase() {
+ @Mock private lateinit var communalViewModel: CommunalViewModel
+ @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ @Mock private lateinit var shadeInteractor: ShadeInteractor
+
+ private lateinit var containerView: View
+ private lateinit var testableLooper: TestableLooper
+
+ private lateinit var communalInteractor: CommunalInteractor
+ private lateinit var communalRepository: FakeCommunalRepository
+ private lateinit var underTest: GlanceableHubContainerController
+
+ private val bouncerShowingFlow = MutableStateFlow(false)
+ private val shadeShowingFlow = MutableStateFlow(false)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ val withDeps = CommunalInteractorFactory.create()
+ communalInteractor = withDeps.communalInteractor
+ communalRepository = withDeps.communalRepository
+
+ underTest =
+ GlanceableHubContainerController(
+ communalInteractor,
+ communalViewModel,
+ keyguardTransitionInteractor,
+ shadeInteractor
+ )
+ testableLooper = TestableLooper.get(this)
+
+ communalRepository.setIsCommunalEnabled(true)
+
+ whenever(keyguardTransitionInteractor.isFinishedInStateWhere(any()))
+ .thenReturn(bouncerShowingFlow)
+ whenever(shadeInteractor.isAnyFullyExpanded).thenReturn(shadeShowingFlow)
+
+ overrideResource(R.dimen.communal_grid_gutter_size, SWIPE_REGION_WIDTH)
+ }
+
+ @Test
+ fun isEnabled_interactorEnabled_returnsTrue() {
+ communalRepository.setIsCommunalEnabled(true)
+
+ assertThat(underTest.isEnabled()).isTrue()
+ }
+
+ @Test
+ fun isEnabled_interactorDisabled_returnsFalse() {
+ communalRepository.setIsCommunalEnabled(false)
+
+ assertThat(underTest.isEnabled()).isFalse()
+ }
+
+ @Test
+ fun initView_notEnabled_throwsException() {
+ communalRepository.setIsCommunalEnabled(false)
+
+ assertThrows(RuntimeException::class.java) { underTest.initView(context) }
+ }
+
+ @Test
+ fun initView_calledTwice_throwsException() {
+ // First call succeeds.
+ underTest.initView(context)
+
+ // Second call throws.
+ assertThrows(RuntimeException::class.java) { underTest.initView(context) }
+ }
+
+ @Test
+ fun onTouchEvent_touchInsideGestureRegion_returnsTrue() {
+ // Communal is open.
+ communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+
+ initAndAttachContainerView()
+
+ // Touch events are intercepted.
+ assertThat(underTest.onTouchEvent(DOWN_IN_SWIPE_REGION_EVENT)).isTrue()
+ }
+
+ @Test
+ fun onTouchEvent_subsequentTouchesAfterGestureStart_returnsTrue() {
+ // Communal is open.
+ communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+
+ initAndAttachContainerView()
+
+ // Initial touch down is intercepted, and so are touches outside of the region, until an up
+ // event is received.
+ assertThat(underTest.onTouchEvent(DOWN_IN_SWIPE_REGION_EVENT)).isTrue()
+ assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
+ assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
+ assertThat(underTest.onTouchEvent(MOVE_EVENT)).isFalse()
+ }
+
+ @Test
+ fun onTouchEvent_communalOpen_returnsTrue() {
+ // Communal is open.
+ communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+
+ initAndAttachContainerView()
+ testableLooper.processAllMessages()
+
+ // Touch events are intercepted.
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
+ }
+
+ @Test
+ fun onTouchEvent_communalAndBouncerShowing_returnsFalse() {
+ // Communal is open.
+ communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+
+ initAndAttachContainerView()
+
+ // Bouncer is visible.
+ bouncerShowingFlow.value = true
+ testableLooper.processAllMessages()
+
+ // Touch events are not intercepted.
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+ }
+
+ @Test
+ fun onTouchEvent_communalAndShadeShowing_returnsFalse() {
+ // Communal is open.
+ communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+
+ initAndAttachContainerView()
+
+ shadeShowingFlow.value = true
+ testableLooper.processAllMessages()
+
+ // Touch events are not intercepted.
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+ }
+
+ private fun initAndAttachContainerView() {
+ containerView = View(context)
+ // Make view clickable so that dispatchTouchEvent returns true.
+ containerView.isClickable = true
+
+ underTest.initView(containerView)
+ // Attach the view so that flows start collecting.
+ ViewUtils.attachView(containerView)
+ // Give the view a size so that determining if a touch starts at the right edge works.
+ containerView.layout(0, 0, CONTAINER_WIDTH, CONTAINER_HEIGHT)
+ }
+
+ companion object {
+ private const val CONTAINER_WIDTH = 100
+ private const val CONTAINER_HEIGHT = 100
+ private const val SWIPE_REGION_WIDTH = 20
+
+ private val DOWN_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ private val DOWN_IN_SWIPE_REGION_EVENT =
+ MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, CONTAINER_WIDTH.toFloat(), 0f, 0)
+ private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
+ private val UP_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
+
+ @BeforeClass
+ @JvmStatic
+ fun beforeClass() {
+ // Glanceable hub requires Compose, no point running any of these tests if compose isn't
+ // enabled.
+ assumeTrue(ComposeFacade.isComposeAvailable())
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 6ff79660efec..9d997dae6836 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade
+import android.content.Context
import android.os.Handler
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -45,8 +46,6 @@ import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
-import com.android.systemui.communal.data.repository.FakeCommunalRepository
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.compose.ComposeFacade.isComposeAvailable
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
@@ -98,6 +97,7 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.TestScope
@@ -112,9 +112,8 @@ import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import java.util.Optional
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -150,8 +149,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock
private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
- @Mock private lateinit var mCommunalViewModel: CommunalViewModel
- private lateinit var mCommunalRepository: FakeCommunalRepository
+ @Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController
@Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
@Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@@ -201,8 +199,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- mCommunalRepository = FakeCommunalRepository()
-
testScope = TestScope()
fakeClock = FakeSystemClock()
underTest =
@@ -236,8 +232,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
mock(KeyguardMessageAreaController.Factory::class.java),
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
- mCommunalViewModel,
- mCommunalRepository,
+ mGlanceableHubContainerController,
notificationLaunchAnimationInteractor,
featureFlagsClassic,
fakeClock,
@@ -466,6 +461,16 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
}
@Test
+ fun handleDispatchTouchEvent_glanceableHubIntercepts_returnsTrue() {
+ whenever(mGlanceableHubContainerController.onTouchEvent(DOWN_EVENT)).thenReturn(true)
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
+
+ assertThat(returnVal).isTrue()
+ }
+
+ @Test
fun shouldInterceptTouchEvent_statusBarKeyguardViewManagerShouldIntercept() {
// down event should be intercepted by keyguardViewManager
whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
@@ -546,7 +551,11 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
return
}
- mCommunalRepository.setIsCommunalEnabled(true)
+ whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(true)
+
+ val mockCommunalView = mock(View::class.java)
+ whenever(mGlanceableHubContainerController.initView(any<Context>()))
+ .thenReturn(mockCommunalView)
val mockCommunalPlaceholder = mock(View::class.java)
val fakeViewIndex = 20
@@ -558,7 +567,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
// Communal view added as a child of the container at the proper index, the stub is removed.
verify(view).removeView(mockCommunalPlaceholder)
- verify(view).addView(any(), eq(fakeViewIndex))
+ verify(view).addView(eq(mockCommunalView), eq(fakeViewIndex))
}
@Test
@@ -567,7 +576,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
return
}
- mCommunalRepository.setIsCommunalEnabled(false)
+ whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(false)
val mockCommunalPlaceholder = mock(View::class.java)
val fakeViewIndex = 20
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 29b1366eb6c1..9750f60c7b19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -41,8 +41,6 @@ import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
-import com.android.systemui.communal.data.repository.FakeCommunalRepository
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
@@ -142,8 +140,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
private lateinit var unfoldTransitionProgressProvider:
Optional<UnfoldTransitionProgressProvider>
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
- @Mock private lateinit var mCommunalViewModel: CommunalViewModel
- private lateinit var mCommunalRepository: FakeCommunalRepository
+ @Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
@Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@@ -180,8 +177,6 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
.thenReturn(emptyFlow())
- mCommunalRepository = FakeCommunalRepository()
-
val featureFlags = FakeFeatureFlags()
featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true)
featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false)
@@ -221,8 +216,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
Mockito.mock(KeyguardMessageAreaController.Factory::class.java),
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
- mCommunalViewModel,
- mCommunalRepository,
+ mGlanceableHubContainerController,
NotificationLaunchAnimationInteractor(NotificationLaunchAnimationRepository()),
featureFlags,
FakeSystemClock(),