Loading packages/SystemUI/res/values/flags.xml +2 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,8 @@ <bool name="flag_ongoing_call_in_immersive">false</bool> <bool name="flag_ongoing_call_in_immersive_chip_tap">true</bool> <bool name="flag_smartspace">false</bool> <bool name="flag_smartspace_deduping">true</bool> Loading packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java +5 −0 Original line number Diff line number Diff line Loading @@ -166,6 +166,11 @@ public class FeatureFlags { && mFlagReader.isEnabled(R.bool.flag_ongoing_call_in_immersive); } public boolean isOngoingCallInImmersiveChipTapEnabled() { return isOngoingCallInImmersiveEnabled() && mFlagReader.isEnabled(R.bool.flag_ongoing_call_in_immersive_chip_tap); } public boolean isSmartspaceEnabled() { return mFlagReader.isEnabled(R.bool.flag_smartspace); } Loading packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +4 −3 Original line number Diff line number Diff line Loading @@ -257,7 +257,8 @@ public interface StatusBarDependenciesModule { OngoingCallLogger logger, DumpManager dumpManager, StatusBarWindowController statusBarWindowController, SwipeStatusBarAwayGestureHandler swipeStatusBarAwayGestureHandler) { SwipeStatusBarAwayGestureHandler swipeStatusBarAwayGestureHandler, StatusBarStateController statusBarStateController) { Optional<StatusBarWindowController> windowController = featureFlags.isOngoingCallInImmersiveEnabled() ? Optional.of(statusBarWindowController) Loading @@ -277,8 +278,8 @@ public interface StatusBarDependenciesModule { logger, dumpManager, windowController, gestureHandler ); gestureHandler, statusBarStateController); ongoingCallController.init(); return ongoingCallController; } Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +45 −23 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.flags.FeatureFlags import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener Loading Loading @@ -61,8 +62,10 @@ class OngoingCallController @Inject constructor( private val dumpManager: DumpManager, private val statusBarWindowController: Optional<StatusBarWindowController>, private val swipeStatusBarAwayGestureHandler: Optional<SwipeStatusBarAwayGestureHandler>, private val statusBarStateController: StatusBarStateController, ) : CallbackController<OngoingCallListener>, Dumpable { private var isFullscreen: Boolean = false /** Non-null if there's an active call notification. */ private var callNotificationInfo: CallNotificationInfo? = null /** True if the application managing the call is visible to the user. */ Loading Loading @@ -124,6 +127,7 @@ class OngoingCallController @Inject constructor( dumpManager.registerDumpable(this) if (featureFlags.isOngoingCallStatusBarChipEnabled) { notifCollection.addCollectionListener(notifListener) statusBarStateController.addCallback(statusBarStateListener) } } Loading Loading @@ -177,10 +181,8 @@ class OngoingCallController @Inject constructor( val currentChipView = chipView val timeView = currentChipView?.getTimeView() val backgroundView = currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background) if (currentChipView != null && timeView != null && backgroundView != null) { if (currentChipView != null && timeView != null) { if (currentCallNotificationInfo.hasValidStartTime()) { timeView.setShouldHideText(false) timeView.base = currentCallNotificationInfo.callStartTime - Loading @@ -191,19 +193,8 @@ class OngoingCallController @Inject constructor( timeView.setShouldHideText(true) timeView.stop() } updateChipClickListener() currentCallNotificationInfo.intent?.let { intent -> currentChipView.setOnClickListener { logger.logChipClicked() activityStarter.postStartActivityDismissingKeyguard( intent, 0, ActivityLaunchAnimator.Controller.fromView( backgroundView, InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP) ) } } setUpUidObserver(currentCallNotificationInfo) if (!currentCallNotificationInfo.statusBarSwipedAway) { statusBarWindowController.ifPresent { Loading @@ -227,6 +218,30 @@ class OngoingCallController @Inject constructor( } } private fun updateChipClickListener() { if (callNotificationInfo == null) { return } if (isFullscreen && !featureFlags.isOngoingCallInImmersiveChipTapEnabled) { chipView?.setOnClickListener(null) } else { val currentChipView = chipView val backgroundView = currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background) val intent = callNotificationInfo?.intent if (currentChipView != null && backgroundView != null && intent != null) { currentChipView.setOnClickListener { logger.logChipClicked() activityStarter.postStartActivityDismissingKeyguard( intent, 0, ActivityLaunchAnimator.Controller.fromView( backgroundView, InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP) ) } } } } /** * Sets up an [IUidObserver] to monitor the status of the application managing the ongoing call. */ Loading Loading @@ -315,6 +330,13 @@ class OngoingCallController @Inject constructor( } } private val statusBarStateListener = object : StatusBarStateController.StateListener { override fun onFullscreenStateChanged(isFullscreen: Boolean) { this@OngoingCallController.isFullscreen = isFullscreen updateChipClickListener() } } private data class CallNotificationInfo( val key: String, val callStartTime: Long, Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +64 −4 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.flags.FeatureFlags import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection Loading Loading @@ -82,11 +83,13 @@ class OngoingCallControllerTest : SysuiTestCase() { private lateinit var controller: OngoingCallController private lateinit var notifCollectionListener: NotifCollectionListener @Mock private lateinit var mockFeatureFlags: FeatureFlags @Mock private lateinit var mockSwipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler @Mock private lateinit var mockOngoingCallListener: OngoingCallListener @Mock private lateinit var mockActivityStarter: ActivityStarter @Mock private lateinit var mockIActivityManager: IActivityManager @Mock private lateinit var mockStatusBarWindowController: StatusBarWindowController @Mock private lateinit var mockStatusBarStateController: StatusBarStateController private lateinit var chipView: View Loading @@ -98,13 +101,12 @@ class OngoingCallControllerTest : SysuiTestCase() { } MockitoAnnotations.initMocks(this) val featureFlags = mock(FeatureFlags::class.java) `when`(featureFlags.isOngoingCallStatusBarChipEnabled).thenReturn(true) `when`(mockFeatureFlags.isOngoingCallStatusBarChipEnabled).thenReturn(true) val notificationCollection = mock(CommonNotifCollection::class.java) controller = OngoingCallController( notificationCollection, featureFlags, mockFeatureFlags, clock, mockActivityStarter, mainExecutor, Loading @@ -113,6 +115,7 @@ class OngoingCallControllerTest : SysuiTestCase() { DumpManager(), Optional.of(mockStatusBarWindowController), Optional.of(mockSwipeStatusBarAwayGestureHandler), mockStatusBarStateController, ) controller.init() controller.addCallback(mockOngoingCallListener) Loading Loading @@ -455,6 +458,56 @@ class OngoingCallControllerTest : SysuiTestCase() { // Other tests for notifyChipVisibilityChanged are in [OngoingCallLogger], since // [OngoingCallController.notifyChipVisibilityChanged] just delegates to that class. @Test fun callNotificationAdded_chipIsClickable() { notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) assertThat(chipView.hasOnClickListeners()).isTrue() } @Test fun fullscreenIsTrue_thenCallNotificationAdded_chipNotClickable() { `when`(mockFeatureFlags.isOngoingCallInImmersiveChipTapEnabled).thenReturn(false) getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true) notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) assertThat(chipView.hasOnClickListeners()).isFalse() } @Test fun callNotificationAdded_thenFullscreenIsTrue_chipNotClickable() { `when`(mockFeatureFlags.isOngoingCallInImmersiveChipTapEnabled).thenReturn(false) notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true) assertThat(chipView.hasOnClickListeners()).isFalse() } @Test fun fullscreenChangesToFalse_chipClickable() { `when`(mockFeatureFlags.isOngoingCallInImmersiveChipTapEnabled).thenReturn(false) notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) // First, update to true getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true) // Then, update to false getStateListener().onFullscreenStateChanged(/* isFullscreen= */ false) assertThat(chipView.hasOnClickListeners()).isTrue() } @Test fun fullscreenIsTrue_butChipClickInImmersiveEnabled_chipClickable() { `when`(mockFeatureFlags.isOngoingCallInImmersiveChipTapEnabled).thenReturn(true) notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true) assertThat(chipView.hasOnClickListeners()).isTrue() } private fun createOngoingCallNotifEntry() = createCallNotifEntry(ongoingCallStyle) private fun createScreeningCallNotifEntry() = createCallNotifEntry(screeningCallStyle) Loading @@ -479,6 +532,13 @@ class OngoingCallControllerTest : SysuiTestCase() { } private fun createNotCallNotifEntry() = NotificationEntryBuilder().build() private fun getStateListener(): StatusBarStateController.StateListener { val statusBarStateListenerCaptor = ArgumentCaptor.forClass( StatusBarStateController.StateListener::class.java) verify(mockStatusBarStateController).addCallback(statusBarStateListenerCaptor.capture()) return statusBarStateListenerCaptor.value!! } } private val person = Person.Builder().setName("name").build() Loading Loading
packages/SystemUI/res/values/flags.xml +2 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,8 @@ <bool name="flag_ongoing_call_in_immersive">false</bool> <bool name="flag_ongoing_call_in_immersive_chip_tap">true</bool> <bool name="flag_smartspace">false</bool> <bool name="flag_smartspace_deduping">true</bool> Loading
packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java +5 −0 Original line number Diff line number Diff line Loading @@ -166,6 +166,11 @@ public class FeatureFlags { && mFlagReader.isEnabled(R.bool.flag_ongoing_call_in_immersive); } public boolean isOngoingCallInImmersiveChipTapEnabled() { return isOngoingCallInImmersiveEnabled() && mFlagReader.isEnabled(R.bool.flag_ongoing_call_in_immersive_chip_tap); } public boolean isSmartspaceEnabled() { return mFlagReader.isEnabled(R.bool.flag_smartspace); } Loading
packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +4 −3 Original line number Diff line number Diff line Loading @@ -257,7 +257,8 @@ public interface StatusBarDependenciesModule { OngoingCallLogger logger, DumpManager dumpManager, StatusBarWindowController statusBarWindowController, SwipeStatusBarAwayGestureHandler swipeStatusBarAwayGestureHandler) { SwipeStatusBarAwayGestureHandler swipeStatusBarAwayGestureHandler, StatusBarStateController statusBarStateController) { Optional<StatusBarWindowController> windowController = featureFlags.isOngoingCallInImmersiveEnabled() ? Optional.of(statusBarWindowController) Loading @@ -277,8 +278,8 @@ public interface StatusBarDependenciesModule { logger, dumpManager, windowController, gestureHandler ); gestureHandler, statusBarStateController); ongoingCallController.init(); return ongoingCallController; } Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +45 −23 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.flags.FeatureFlags import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener Loading Loading @@ -61,8 +62,10 @@ class OngoingCallController @Inject constructor( private val dumpManager: DumpManager, private val statusBarWindowController: Optional<StatusBarWindowController>, private val swipeStatusBarAwayGestureHandler: Optional<SwipeStatusBarAwayGestureHandler>, private val statusBarStateController: StatusBarStateController, ) : CallbackController<OngoingCallListener>, Dumpable { private var isFullscreen: Boolean = false /** Non-null if there's an active call notification. */ private var callNotificationInfo: CallNotificationInfo? = null /** True if the application managing the call is visible to the user. */ Loading Loading @@ -124,6 +127,7 @@ class OngoingCallController @Inject constructor( dumpManager.registerDumpable(this) if (featureFlags.isOngoingCallStatusBarChipEnabled) { notifCollection.addCollectionListener(notifListener) statusBarStateController.addCallback(statusBarStateListener) } } Loading Loading @@ -177,10 +181,8 @@ class OngoingCallController @Inject constructor( val currentChipView = chipView val timeView = currentChipView?.getTimeView() val backgroundView = currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background) if (currentChipView != null && timeView != null && backgroundView != null) { if (currentChipView != null && timeView != null) { if (currentCallNotificationInfo.hasValidStartTime()) { timeView.setShouldHideText(false) timeView.base = currentCallNotificationInfo.callStartTime - Loading @@ -191,19 +193,8 @@ class OngoingCallController @Inject constructor( timeView.setShouldHideText(true) timeView.stop() } updateChipClickListener() currentCallNotificationInfo.intent?.let { intent -> currentChipView.setOnClickListener { logger.logChipClicked() activityStarter.postStartActivityDismissingKeyguard( intent, 0, ActivityLaunchAnimator.Controller.fromView( backgroundView, InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP) ) } } setUpUidObserver(currentCallNotificationInfo) if (!currentCallNotificationInfo.statusBarSwipedAway) { statusBarWindowController.ifPresent { Loading @@ -227,6 +218,30 @@ class OngoingCallController @Inject constructor( } } private fun updateChipClickListener() { if (callNotificationInfo == null) { return } if (isFullscreen && !featureFlags.isOngoingCallInImmersiveChipTapEnabled) { chipView?.setOnClickListener(null) } else { val currentChipView = chipView val backgroundView = currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background) val intent = callNotificationInfo?.intent if (currentChipView != null && backgroundView != null && intent != null) { currentChipView.setOnClickListener { logger.logChipClicked() activityStarter.postStartActivityDismissingKeyguard( intent, 0, ActivityLaunchAnimator.Controller.fromView( backgroundView, InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP) ) } } } } /** * Sets up an [IUidObserver] to monitor the status of the application managing the ongoing call. */ Loading Loading @@ -315,6 +330,13 @@ class OngoingCallController @Inject constructor( } } private val statusBarStateListener = object : StatusBarStateController.StateListener { override fun onFullscreenStateChanged(isFullscreen: Boolean) { this@OngoingCallController.isFullscreen = isFullscreen updateChipClickListener() } } private data class CallNotificationInfo( val key: String, val callStartTime: Long, Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +64 −4 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.flags.FeatureFlags import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection Loading Loading @@ -82,11 +83,13 @@ class OngoingCallControllerTest : SysuiTestCase() { private lateinit var controller: OngoingCallController private lateinit var notifCollectionListener: NotifCollectionListener @Mock private lateinit var mockFeatureFlags: FeatureFlags @Mock private lateinit var mockSwipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler @Mock private lateinit var mockOngoingCallListener: OngoingCallListener @Mock private lateinit var mockActivityStarter: ActivityStarter @Mock private lateinit var mockIActivityManager: IActivityManager @Mock private lateinit var mockStatusBarWindowController: StatusBarWindowController @Mock private lateinit var mockStatusBarStateController: StatusBarStateController private lateinit var chipView: View Loading @@ -98,13 +101,12 @@ class OngoingCallControllerTest : SysuiTestCase() { } MockitoAnnotations.initMocks(this) val featureFlags = mock(FeatureFlags::class.java) `when`(featureFlags.isOngoingCallStatusBarChipEnabled).thenReturn(true) `when`(mockFeatureFlags.isOngoingCallStatusBarChipEnabled).thenReturn(true) val notificationCollection = mock(CommonNotifCollection::class.java) controller = OngoingCallController( notificationCollection, featureFlags, mockFeatureFlags, clock, mockActivityStarter, mainExecutor, Loading @@ -113,6 +115,7 @@ class OngoingCallControllerTest : SysuiTestCase() { DumpManager(), Optional.of(mockStatusBarWindowController), Optional.of(mockSwipeStatusBarAwayGestureHandler), mockStatusBarStateController, ) controller.init() controller.addCallback(mockOngoingCallListener) Loading Loading @@ -455,6 +458,56 @@ class OngoingCallControllerTest : SysuiTestCase() { // Other tests for notifyChipVisibilityChanged are in [OngoingCallLogger], since // [OngoingCallController.notifyChipVisibilityChanged] just delegates to that class. @Test fun callNotificationAdded_chipIsClickable() { notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) assertThat(chipView.hasOnClickListeners()).isTrue() } @Test fun fullscreenIsTrue_thenCallNotificationAdded_chipNotClickable() { `when`(mockFeatureFlags.isOngoingCallInImmersiveChipTapEnabled).thenReturn(false) getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true) notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) assertThat(chipView.hasOnClickListeners()).isFalse() } @Test fun callNotificationAdded_thenFullscreenIsTrue_chipNotClickable() { `when`(mockFeatureFlags.isOngoingCallInImmersiveChipTapEnabled).thenReturn(false) notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true) assertThat(chipView.hasOnClickListeners()).isFalse() } @Test fun fullscreenChangesToFalse_chipClickable() { `when`(mockFeatureFlags.isOngoingCallInImmersiveChipTapEnabled).thenReturn(false) notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) // First, update to true getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true) // Then, update to false getStateListener().onFullscreenStateChanged(/* isFullscreen= */ false) assertThat(chipView.hasOnClickListeners()).isTrue() } @Test fun fullscreenIsTrue_butChipClickInImmersiveEnabled_chipClickable() { `when`(mockFeatureFlags.isOngoingCallInImmersiveChipTapEnabled).thenReturn(true) notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true) assertThat(chipView.hasOnClickListeners()).isTrue() } private fun createOngoingCallNotifEntry() = createCallNotifEntry(ongoingCallStyle) private fun createScreeningCallNotifEntry() = createCallNotifEntry(screeningCallStyle) Loading @@ -479,6 +532,13 @@ class OngoingCallControllerTest : SysuiTestCase() { } private fun createNotCallNotifEntry() = NotificationEntryBuilder().build() private fun getStateListener(): StatusBarStateController.StateListener { val statusBarStateListenerCaptor = ArgumentCaptor.forClass( StatusBarStateController.StateListener::class.java) verify(mockStatusBarStateController).addCallback(statusBarStateListenerCaptor.capture()) return statusBarStateListenerCaptor.value!! } } private val person = Person.Builder().setName("name").build() Loading