diff options
4 files changed, 106 insertions, 41 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index 4e57e44f38d7..1eccfd8e7254 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -162,6 +162,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager); mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area); mClockView = mStatusBar.findViewById(R.id.clock); + mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip); showSystemIconArea(false); showClock(false); initEmergencyCryptkeeperText(); @@ -182,7 +183,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue super.onResume(); mCommandQueue.addCallback(this); mStatusBarStateController.addCallback(this); - mOngoingCallController.addCallback(mOngoingCallListener); + initOngoingCallChip(); } @Override @@ -221,8 +222,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } statusBarCenteredIconArea.addView(mCenteredIconArea); - initOngoingCallChip(); - // Default to showing until we know otherwise. showNotificationIconArea(false); } @@ -273,7 +272,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue state |= DISABLE_CLOCK; } - if (mOngoingCallController.getHasOngoingCall()) { + if (mOngoingCallController.hasOngoingCall()) { state |= DISABLE_NOTIFICATION_ICONS; } @@ -448,7 +447,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void initOngoingCallChip() { - mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip); + mOngoingCallController.addCallback(mOngoingCallListener); mOngoingCallController.setChipView(mOngoingCallChip); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index 93ea77b8378c..96473c2a961c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone.ongoingcall import android.app.Notification import android.app.Notification.CallStyle.CALL_TYPE_ONGOING +import android.content.Intent import android.util.Log import android.view.ViewGroup import android.widget.Chronometer @@ -44,8 +45,8 @@ class OngoingCallController @Inject constructor( private val activityStarter: ActivityStarter ) : CallbackController<OngoingCallListener> { - var hasOngoingCall = false - private set + /** Null if there's no ongoing call. */ + private var ongoingCallInfo: OngoingCallInfo? = null private var chipView: ViewGroup? = null private val mListeners: MutableList<OngoingCallListener> = mutableListOf() @@ -53,35 +54,16 @@ class OngoingCallController @Inject constructor( private val notifListener = object : NotifCollectionListener { override fun onEntryUpdated(entry: NotificationEntry) { if (isOngoingCallNotification(entry)) { - val currentChipView = chipView - val timeView = - currentChipView?.findViewById<Chronometer>(R.id.ongoing_call_chip_time) - - if (currentChipView != null && timeView != null) { - hasOngoingCall = true - val callStartTime = entry.sbn.notification.`when` - timeView.base = callStartTime - - System.currentTimeMillis() + - systemClock.elapsedRealtime() - timeView.start() - - currentChipView.setOnClickListener { - activityStarter.postStartActivityDismissingKeyguard( - entry.sbn.notification.contentIntent.intent, 0, - ActivityLaunchAnimator.Controller.fromView(it)) - } - - mListeners.forEach { l -> l.onOngoingCallStarted(animate = true) } - } else if (DEBUG) { - Log.w(TAG, "Ongoing call chip view could not be found; " + - "Not displaying chip in status bar") - } + ongoingCallInfo = OngoingCallInfo( + entry.sbn.notification.`when`, + entry.sbn.notification.contentIntent.intent) + updateChip() } } override fun onEntryRemoved(entry: NotificationEntry, reason: Int) { if (isOngoingCallNotification(entry)) { - hasOngoingCall = false + ongoingCallInfo = null mListeners.forEach { l -> l.onOngoingCallEnded(animate = true) } } } @@ -93,10 +75,23 @@ class OngoingCallController @Inject constructor( } } - fun setChipView(chipView: ViewGroup?) { + /** + * Sets the chip view that will contain ongoing call information. + * + * Should only be called from [CollapedStatusBarFragment]. + */ + fun setChipView(chipView: ViewGroup) { this.chipView = chipView + if (hasOngoingCall()) { + updateChip() + } } + /** + * Returns true if there's an active ongoing call that can be displayed in a status bar chip. + */ + fun hasOngoingCall(): Boolean = ongoingCallInfo != null + override fun addCallback(listener: OngoingCallListener) { synchronized(mListeners) { if (!mListeners.contains(listener)) { @@ -110,6 +105,43 @@ class OngoingCallController @Inject constructor( mListeners.remove(listener) } } + + private fun updateChip() { + val currentOngoingCallInfo = ongoingCallInfo ?: return + + val currentChipView = chipView + val timeView = + currentChipView?.findViewById<Chronometer>(R.id.ongoing_call_chip_time) + + if (currentChipView != null && timeView != null) { + timeView.base = currentOngoingCallInfo.callStartTime - + System.currentTimeMillis() + + systemClock.elapsedRealtime() + timeView.start() + + currentChipView.setOnClickListener { + activityStarter.postStartActivityDismissingKeyguard( + currentOngoingCallInfo.intent, 0, + ActivityLaunchAnimator.Controller.fromView(it)) + } + + mListeners.forEach { l -> l.onOngoingCallStarted(animate = true) } + } else { + // If we failed to update the chip, don't store the ongoing call info. Then + // [hasOngoingCall] will return false and we fall back to typical notification handling. + ongoingCallInfo = null + + if (DEBUG) { + Log.w(TAG, "Ongoing call chip view could not be found; " + + "Not displaying chip in status bar") + } + } + } + + private class OngoingCallInfo( + val callStartTime: Long, + val intent: Intent + ) } private fun isOngoingCallNotification(entry: NotificationEntry): Boolean { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java index 929a7e1543fb..0e3e0cc8ea97 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java @@ -184,7 +184,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture()); OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue()); - when(mOngoingCallController.getHasOngoingCall()).thenReturn(true); + when(mOngoingCallController.hasOngoingCall()).thenReturn(true); listener.onOngoingCallStarted(/* animate= */ false); assertEquals(View.VISIBLE, @@ -205,7 +205,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture()); OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue()); - when(mOngoingCallController.getHasOngoingCall()).thenReturn(false); + when(mOngoingCallController.hasOngoingCall()).thenReturn(false); listener.onOngoingCallEnded(/* animate= */ false); assertEquals(View.GONE, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt index 73fcc919eb81..c244290fdacd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt @@ -19,12 +19,13 @@ package com.android.systemui.statusbar.phone.ongoingcall import android.app.Notification import android.app.PendingIntent import android.app.Person +import android.content.Intent import android.service.notification.NotificationListenerService.REASON_USER_STOPPED -import androidx.test.filters.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater import android.widget.LinearLayout +import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.ActivityStarter @@ -44,6 +45,7 @@ import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.Mockito.never +import org.mockito.Mockito.times import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @@ -114,22 +116,24 @@ class OngoingCallControllerTest : SysuiTestCase() { @Test fun hasOngoingCall_noOngoingCallNotifSent_returnsFalse() { - assertThat(controller.hasOngoingCall).isFalse() + assertThat(controller.hasOngoingCall()).isFalse() } @Test fun hasOngoingCall_ongoingCallNotifSentAndChipViewSet_returnsTrue() { notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) - assertThat(controller.hasOngoingCall).isTrue() + assertThat(controller.hasOngoingCall()).isTrue() } @Test - fun hasOngoingCall_ongoingCallNotifSentButNoChipView_returnsFalse() { - controller.setChipView(null) + fun hasOngoingCall_ongoingCallNotifSentButInvalidChipView_returnsFalse() { + val invalidChipView = LinearLayout(context) + controller.setChipView(invalidChipView) + notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) - assertThat(controller.hasOngoingCall).isFalse() + assertThat(controller.hasOngoingCall()).isFalse() } @Test @@ -139,12 +143,42 @@ class OngoingCallControllerTest : SysuiTestCase() { notifCollectionListener.onEntryUpdated(ongoingCallNotifEntry) notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED) - assertThat(controller.hasOngoingCall).isFalse() + assertThat(controller.hasOngoingCall()).isFalse() + } + + /** + * This test fakes a theme change during an ongoing call. + * + * When a theme change happens, [CollapsedStatusBarFragment] and its views get re-created, so + * [OngoingCallController.setChipView] gets called with a new view. If there's an active ongoing + * call when the theme changes, the new view needs to be updated with the call information. + */ + @Test + fun setChipView_whenHasOngoingCallIsTrue_listenerNotifiedWithNewView() { + // Start an ongoing call. + notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) + + lateinit var newChipView: LinearLayout + TestableLooper.get(this).runWithLooper { + newChipView = LayoutInflater.from(mContext) + .inflate(R.layout.ongoing_call_chip, null) as LinearLayout + } + + // Change the chip view associated with the controller. + controller.setChipView(newChipView) + + // Verify the listener was notified once for the initial call and again when the new view + // was set. + verify(mockOngoingCallListener, times(2)).onOngoingCallStarted(anyBoolean()) } private fun createOngoingCallNotifEntry(): NotificationEntry { val notificationEntryBuilder = NotificationEntryBuilder() notificationEntryBuilder.modifyNotification(context).style = ongoingCallStyle + + val contentIntent = mock(PendingIntent::class.java) + `when`(contentIntent.intent).thenReturn(mock(Intent::class.java)) + notificationEntryBuilder.modifyNotification(context).setContentIntent(contentIntent) return notificationEntryBuilder.build() } |