Merge "[Ongoing Call Chip] Keep chip displayed on theme change." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
4b537e9170
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user