summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2024-12-12 17:28:50 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2024-12-12 17:28:50 -0800
commit75d3ee72e32f0ff678c12f42035b5a25cacc3a82 (patch)
tree3e056a621bb05f48cd298b8bf215a270e3418889
parent744a3f5da9a4ac348a80863a280a2985cae32e1b (diff)
parent171d08801f011863d556fccb821940fb833a295e (diff)
Merge "Add support for swiping up on the status bar in OngoingCallInteractor" into main
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt157
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt74
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandlerKosmos.kt25
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt2
5 files changed, 221 insertions, 51 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
index b19645fadbdf..8fb95e843ec1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.gesture.swipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
@@ -40,9 +41,14 @@ import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -51,6 +57,11 @@ class OngoingCallInteractorTest : SysuiTestCase() {
private val repository = kosmos.activeNotificationListRepository
private val underTest = kosmos.ongoingCallInteractor
+ @Before
+ fun setUp() {
+ underTest.start()
+ }
+
@Test
fun noNotification_emitsNoCall() = runTest {
val state by collectLastValue(underTest.ongoingCallState)
@@ -210,8 +221,7 @@ class OngoingCallInteractorTest : SysuiTestCase() {
@Test
fun ongoingCallNotification_setsRequiresStatusBarVisibleTrue() =
kosmos.runTest {
- val ongoingCallState by collectLastValue(underTest.ongoingCallState)
-
+ val isStatusBarRequired by collectLastValue(underTest.isStatusBarRequiredForOngoingCall)
val requiresStatusBarVisibleInRepository by
collectLastValue(
kosmos.fakeStatusBarModeRepository.defaultDisplay
@@ -222,21 +232,9 @@ class OngoingCallInteractorTest : SysuiTestCase() {
kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
.ongoingProcessRequiresStatusBarVisible
)
- repository.activeNotifications.value =
- ActiveNotificationsStore.Builder()
- .apply {
- addIndividualNotif(
- activeNotificationModel(
- key = "notif1",
- whenTime = 1000L,
- callType = CallType.Ongoing,
- uid = UID,
- )
- )
- }
- .build()
+ postOngoingCallNotification()
- assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.InCall::class.java)
+ assertThat(isStatusBarRequired).isTrue()
assertThat(requiresStatusBarVisibleInRepository).isTrue()
assertThat(requiresStatusBarVisibleInWindowController).isTrue()
}
@@ -244,8 +242,7 @@ class OngoingCallInteractorTest : SysuiTestCase() {
@Test
fun notificationRemoved_setsRequiresStatusBarVisibleFalse() =
kosmos.runTest {
- val ongoingCallState by collectLastValue(underTest.ongoingCallState)
-
+ val isStatusBarRequired by collectLastValue(underTest.isStatusBarRequiredForOngoingCall)
val requiresStatusBarVisibleInRepository by
collectLastValue(
kosmos.fakeStatusBarModeRepository.defaultDisplay
@@ -257,23 +254,11 @@ class OngoingCallInteractorTest : SysuiTestCase() {
.ongoingProcessRequiresStatusBarVisible
)
- repository.activeNotifications.value =
- ActiveNotificationsStore.Builder()
- .apply {
- addIndividualNotif(
- activeNotificationModel(
- key = "notif1",
- whenTime = 1000L,
- callType = CallType.Ongoing,
- uid = UID,
- )
- )
- }
- .build()
+ postOngoingCallNotification()
repository.activeNotifications.value = ActiveNotificationsStore()
- assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.NoCall::class.java)
+ assertThat(isStatusBarRequired).isFalse()
assertThat(requiresStatusBarVisibleInRepository).isFalse()
assertThat(requiresStatusBarVisibleInWindowController).isFalse()
}
@@ -295,19 +280,8 @@ class OngoingCallInteractorTest : SysuiTestCase() {
)
kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
- repository.activeNotifications.value =
- ActiveNotificationsStore.Builder()
- .apply {
- addIndividualNotif(
- activeNotificationModel(
- key = "notif1",
- whenTime = 1000L,
- callType = CallType.Ongoing,
- uid = UID,
- )
- )
- }
- .build()
+
+ postOngoingCallNotification()
assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.InCall::class.java)
assertThat(requiresStatusBarVisibleInRepository).isTrue()
@@ -321,6 +295,99 @@ class OngoingCallInteractorTest : SysuiTestCase() {
assertThat(requiresStatusBarVisibleInWindowController).isFalse()
}
+ @Test
+ fun gestureHandler_inCall_notFullscreen_doesNotListen() =
+ kosmos.runTest {
+ val ongoingCallState by collectLastValue(underTest.ongoingCallState)
+
+ clearInvocations(kosmos.swipeStatusBarAwayGestureHandler)
+ // Set up notification but not in fullscreen
+ kosmos.fakeStatusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false
+ postOngoingCallNotification()
+
+ assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.InCall::class.java)
+ verify(kosmos.swipeStatusBarAwayGestureHandler, never())
+ .addOnGestureDetectedCallback(any(), any())
+ }
+
+ @Test
+ fun gestureHandler_inCall_fullscreen_addsListener() =
+ kosmos.runTest {
+ val isGestureListeningEnabled by collectLastValue(underTest.isGestureListeningEnabled)
+
+ // Set up notification and fullscreen mode
+ kosmos.fakeStatusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
+ postOngoingCallNotification()
+
+ assertThat(isGestureListeningEnabled).isTrue()
+ verify(kosmos.swipeStatusBarAwayGestureHandler)
+ .addOnGestureDetectedCallback(any(), any())
+ }
+
+ @Test
+ fun gestureHandler_inCall_fullscreen_chipSwiped_removesListener() =
+ kosmos.runTest {
+ val swipeAwayState by collectLastValue(underTest.isChipSwipedAway)
+
+ // Set up notification and fullscreen mode
+ kosmos.fakeStatusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
+ postOngoingCallNotification()
+
+ clearInvocations(kosmos.swipeStatusBarAwayGestureHandler)
+
+ underTest.onStatusBarSwiped()
+
+ assertThat(swipeAwayState).isTrue()
+ verify(kosmos.swipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(any())
+ }
+
+ @Test
+ fun chipSwipedAway_setsRequiresStatusBarVisibleFalse() =
+ kosmos.runTest {
+ val isStatusBarRequiredForOngoingCall by
+ collectLastValue(underTest.isStatusBarRequiredForOngoingCall)
+ val requiresStatusBarVisibleInRepository by
+ collectLastValue(
+ kosmos.fakeStatusBarModeRepository.defaultDisplay
+ .ongoingProcessRequiresStatusBarVisible
+ )
+ val requiresStatusBarVisibleInWindowController by
+ collectLastValue(
+ kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
+ .ongoingProcessRequiresStatusBarVisible
+ )
+
+ // Start with an ongoing call (which should set status bar required)
+ postOngoingCallNotification()
+
+ assertThat(isStatusBarRequiredForOngoingCall).isTrue()
+ assertThat(requiresStatusBarVisibleInRepository).isTrue()
+ assertThat(requiresStatusBarVisibleInWindowController).isTrue()
+
+ // Swipe away the chip
+ underTest.onStatusBarSwiped()
+
+ // Verify status bar is no longer required
+ assertThat(requiresStatusBarVisibleInRepository).isFalse()
+ assertThat(requiresStatusBarVisibleInWindowController).isFalse()
+ }
+
+ private fun postOngoingCallNotification() {
+ repository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1",
+ whenTime = 1000L,
+ callType = CallType.Ongoing,
+ uid = UID,
+ )
+ )
+ }
+ .build()
+ }
+
companion object {
private const val UID = 885
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index 2588c7ae2363..46c84fbc19aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -37,6 +37,7 @@ import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
+import com.android.systemui.statusbar.phone.ongoingcall.domain.interactor.OngoingCallInteractor
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl
import com.android.systemui.statusbar.window.MultiDisplayStatusBarWindowControllerStore
@@ -101,6 +102,19 @@ interface StatusBarModule {
@Provides
@SysUISingleton
+ @IntoMap
+ @ClassKey(OngoingCallInteractor::class)
+ fun ongoingCallInteractor(
+ interactor: OngoingCallInteractor
+ ): CoreStartable =
+ if (StatusBarChipsModernization.isEnabled) {
+ interactor
+ } else {
+ CoreStartable.NOP
+ }
+
+ @Provides
+ @SysUISingleton
fun lightBarController(store: LightBarControllerStore): LightBarController {
return store.defaultDisplay
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
index b7ccfa01c92c..2f7b24393ae0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
@@ -16,12 +16,15 @@
package com.android.systemui.statusbar.phone.ongoingcall.domain.interactor
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.CoreStartable
import com.android.systemui.activity.data.repository.ActivityManagerRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
@@ -31,11 +34,15 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
@@ -55,12 +62,19 @@ class OngoingCallInteractor @Inject constructor(
private val activityManagerRepository: ActivityManagerRepository,
private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler,
activeNotificationsInteractor: ActiveNotificationsInteractor,
@OngoingCallLog private val logBuffer: LogBuffer,
-) {
+) : CoreStartable {
private val logger = Logger(logBuffer, TAG)
/**
+ * Tracks whether the call chip has been swiped away.
+ */
+ private val _isChipSwipedAway = MutableStateFlow(false)
+ val isChipSwipedAway: StateFlow<Boolean> = _isChipSwipedAway.asStateFlow()
+
+ /**
* The current state of ongoing calls.
*/
val ongoingCallState: StateFlow<OngoingCallModel> =
@@ -70,15 +84,29 @@ class OngoingCallInteractor @Inject constructor(
notification = notification
)
}
- .onEach { state ->
- setStatusBarRequiredForOngoingCall(state)
- }
.stateIn(
scope = scope,
started = SharingStarted.WhileSubscribed(),
initialValue = OngoingCallModel.NoCall
)
+ @VisibleForTesting
+ val isStatusBarRequiredForOngoingCall = combine(
+ ongoingCallState,
+ isChipSwipedAway
+ ) { callState, chipSwipedAway ->
+ callState is OngoingCallModel.InCall && !chipSwipedAway
+ }
+
+ @VisibleForTesting
+ val isGestureListeningEnabled = combine(
+ ongoingCallState,
+ statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode,
+ isChipSwipedAway
+ ) { callState, isFullscreen, chipSwipedAway ->
+ callState is OngoingCallModel.InCall && !chipSwipedAway && isFullscreen
+ }
+
private fun createOngoingCallStateFlow(
notification: ActiveNotificationModel?
): Flow<OngoingCallModel> {
@@ -99,6 +127,31 @@ class OngoingCallInteractor @Inject constructor(
}
}
+ override fun start() {
+ ongoingCallState
+ .filterIsInstance<OngoingCallModel.NoCall>()
+ .onEach {
+ _isChipSwipedAway.value = false
+ }.launchIn(scope)
+
+ isStatusBarRequiredForOngoingCall.onEach { statusBarRequired ->
+ setStatusBarRequiredForOngoingCall(statusBarRequired)
+ }.launchIn(scope)
+
+ isGestureListeningEnabled.onEach { isEnabled ->
+ updateGestureListening(isEnabled)
+ }.launchIn(scope)
+ }
+
+ /**
+ * Callback that must run when the status bar is swiped while gesture listening is active.
+ */
+ @VisibleForTesting
+ fun onStatusBarSwiped() {
+ logger.d("Status bar chip swiped away")
+ _isChipSwipedAway.value = true
+ }
+
private fun deriveOngoingCallState(
model: ActiveNotificationModel,
isVisible: Boolean
@@ -126,8 +179,7 @@ class OngoingCallInteractor @Inject constructor(
}
}
- private fun setStatusBarRequiredForOngoingCall(state: OngoingCallModel) {
- val statusBarRequired = state is OngoingCallModel.InCall
+ private fun setStatusBarRequiredForOngoingCall(statusBarRequired: Boolean) {
// TODO(b/382808183): Create a single repository that can be utilized in
// `statusBarModeRepositoryStore` and `statusBarWindowControllerStore` so we do not need
// two separate calls to force the status bar to stay visible.
@@ -138,6 +190,16 @@ class OngoingCallInteractor @Inject constructor(
.setOngoingProcessRequiresStatusBarVisible(statusBarRequired)
}
+ private fun updateGestureListening(isEnabled: Boolean) {
+ if (isEnabled) {
+ swipeStatusBarAwayGestureHandler.addOnGestureDetectedCallback(TAG) { _ ->
+ onStatusBarSwiped()
+ }
+ } else {
+ swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
+ }
+ }
+
companion object {
private val TAG = "OngoingCall"
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandlerKosmos.kt
new file mode 100644
index 000000000000..72165c95fc55
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandlerKosmos.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.statusbar.gesture
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler by
+Kosmos.Fixture {
+ mock<SwipeStatusBarAwayGestureHandler>()
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
index 9090e02b22b6..40d91017eeef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
@@ -21,6 +21,7 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.gesture.swipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
@@ -32,6 +33,7 @@ val Kosmos.ongoingCallInteractor: OngoingCallInteractor by
activityManagerRepository = activityManagerRepository,
statusBarModeRepositoryStore = fakeStatusBarModeRepository,
statusBarWindowControllerStore = fakeStatusBarWindowControllerStore,
+ swipeStatusBarAwayGestureHandler = swipeStatusBarAwayGestureHandler,
logBuffer = logcatLogBuffer("OngoingCallInteractorKosmos"),
)
}