diff options
| author | 2021-04-08 20:49:49 +0000 | |
|---|---|---|
| committer | 2021-04-08 20:49:49 +0000 | |
| commit | 1ba1dd34067e3e643bc65506981047bf4c1f59d5 (patch) | |
| tree | 02897e4cc03a842e3a71ac8df89782b519e919f4 | |
| parent | 5b6504f6c7e24f61595981c8ce3f71a888cd1b15 (diff) | |
| parent | 2cbafc1941f5225ec35a5ca9405a69dd46b1dc9e (diff) | |
Merge "[Ongoing Call Chip] Add an ongoing call chip in the status bar." into sc-dev
11 files changed, 391 insertions, 10 deletions
diff --git a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml b/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml new file mode 100644 index 000000000000..bdd6270bb50b --- /dev/null +++ b/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml @@ -0,0 +1,20 @@ +<!-- + ~ Copyright (C) 2021 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. + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="?android:attr/colorAccent" /> + <corners android:radius="@dimen/ongoing_call_chip_corner_radius" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml new file mode 100644 index 000000000000..c90fc3151ee7 --- /dev/null +++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml @@ -0,0 +1,50 @@ +<!-- + ~ Copyright (C) 2021 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/ongoing_call_chip" + android:layout_width="wrap_content" + android:layout_height="@dimen/ongoing_appops_chip_height" + android:layout_gravity="center_vertical|start" + android:gravity="center_vertical" + android:background="@drawable/ongoing_call_chip_bg" + android:paddingStart="@dimen/ongoing_call_chip_side_padding" + android:paddingEnd="@dimen/ongoing_call_chip_side_padding" + android:visibility="gone" +> + + <ImageView + android:src="@*android:drawable/ic_phone" + android:layout_width="@dimen/ongoing_call_chip_icon_size" + android:layout_height="@dimen/ongoing_call_chip_icon_size" + android:paddingEnd="@dimen/ongoing_call_chip_icon_text_padding" + android:tint="?android:attr/colorPrimary" + /> + + <!-- TODO(b/183229367): The text in this view isn't quite centered within the chip. --> + <!-- TODO(b/183229367): This text view's width shouldn't change as the time increases. --> + <Chronometer + android:id="@+id/ongoing_call_chip_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:gravity="center" + android:textAppearance="@android:style/TextAppearance.Material.Small" + android:fontFamily="@*android:string/config_headlineFontFamily" + android:textColor="?android:attr/colorPrimary" + /> + +</LinearLayout> diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index f8db97dbf800..8b244c757649 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -82,6 +82,8 @@ android:gravity="center_vertical|start" /> + <include layout="@layout/ongoing_call_chip" /> + <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/notification_icon_area" android:layout_width="0dp" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 237edf8c973b..e834f3f18c10 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1442,4 +1442,11 @@ <dimen name="wallet_empty_state_corner_radius">24dp</dimen> <dimen name="wallet_tile_card_view_height">32dp</dimen> <dimen name="wallet_tile_card_view_width">50dp</dimen> + + <!-- Ongoing call chip --> + <dimen name="ongoing_call_chip_side_padding">12dp</dimen> + <dimen name="ongoing_call_chip_icon_size">16dp</dimen> + <!-- The padding between the icon and the text. --> + <dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen> + <dimen name="ongoing_call_chip_corner_radius">28dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index c39db941af38..bbf204844e29 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -49,7 +49,7 @@ <bool name="flag_charging_ripple">false</bool> - <bool name="flag_ongoing_call_status_bar_chip">false</bool> + <bool name="flag_ongoing_call_status_bar_chip">true</bool> <bool name="flag_smartspace">false</bool> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index 14c73b5cbb4c..20383feff9bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -63,6 +63,7 @@ import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.bubbles.Bubbles; import java.util.Optional; @@ -234,9 +235,11 @@ public interface StatusBarDependenciesModule { @Provides @SysUISingleton static OngoingCallController provideOngoingCallController( - CommonNotifCollection notifCollection, FeatureFlags featureFlags) { + CommonNotifCollection notifCollection, + FeatureFlags featureFlags, + SystemClock systemClock) { OngoingCallController ongoingCallController = - new OngoingCallController(notifCollection, featureFlags); + new OngoingCallController(notifCollection, featureFlags, systemClock); ongoingCallController.init(); return ongoingCallController; } 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 f64a0e03698a..9d1c60928106 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -38,6 +38,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; +import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import com.android.systemui.statusbar.policy.EncryptionHelper; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.NetworkController; @@ -67,6 +68,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private NetworkController mNetworkController; private LinearLayout mSystemIconArea; private View mClockView; + private ViewGroup mOngoingCallChip; private View mNotificationIconAreaInner; private View mCenteredIconArea; private int mDisabled1; @@ -86,6 +88,20 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } }; + private final OngoingCallListener mOngoingCallListener = new OngoingCallListener() { + @Override + public void onOngoingCallStarted(boolean animate) { + disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate); + animateShow(mOngoingCallChip, animate); + } + + @Override + public void onOngoingCallEnded(boolean animate) { + disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate); + animateHiddenState(mOngoingCallChip, View.GONE, animate); + } + }; + @Inject public CollapsedStatusBarFragment(OngoingCallController ongoingCallController) { mOngoingCallController = ongoingCallController; @@ -142,6 +158,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue super.onResume(); mCommandQueue.addCallback(this); mStatusBarStateController.addCallback(this); + mOngoingCallController.addCallback(mOngoingCallListener); } @Override @@ -149,6 +166,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue super.onPause(); mCommandQueue.removeCallback(this); mStatusBarStateController.removeCallback(this); + mOngoingCallController.removeCallback(mOngoingCallListener); } @Override @@ -179,6 +197,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } statusBarCenteredIconArea.addView(mCenteredIconArea); + initOngoingCallChip(); + // Default to showing until we know otherwise. showNotificationIconArea(false); } @@ -228,6 +248,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue state |= DISABLE_CLOCK; } + if (mOngoingCallController.getHasOngoingCall()) { + state |= DISABLE_NOTIFICATION_ICONS; + } + if (!mKeyguardStateController.isLaunchTransitionFadingAway() && !mKeyguardStateController.isKeyguardFadingAway() && shouldHideNotificationIcons() @@ -395,6 +419,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } } + private void initOngoingCallChip() { + mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip); + mOngoingCallController.setChipView(mOngoingCallChip); + } + @Override public void onStateChanged(int newState) { 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 60d3ea3a7093..b55db6fd1e7d 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 @@ -19,11 +19,16 @@ package com.android.systemui.statusbar.phone.ongoingcall import android.app.Notification import android.app.Notification.CallStyle.CALL_TYPE_ONGOING import android.util.Log +import android.view.ViewGroup +import android.widget.Chronometer +import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.FeatureFlags 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 +import com.android.systemui.statusbar.policy.CallbackController +import com.android.systemui.util.time.SystemClock import javax.inject.Inject /** @@ -32,19 +37,39 @@ import javax.inject.Inject @SysUISingleton class OngoingCallController @Inject constructor( private val notifCollection: CommonNotifCollection, - private val featureFlags: FeatureFlags -) { + private val featureFlags: FeatureFlags, + private val systemClock: SystemClock +) : CallbackController<OngoingCallListener> { + + var hasOngoingCall = false + private set + private var chipView: ViewGroup? = null + + private val mListeners: MutableList<OngoingCallListener> = mutableListOf() private val notifListener = object : NotifCollectionListener { override fun onEntryUpdated(entry: NotificationEntry) { - if (isOngoingCallNotification(entry) && DEBUG) { - Log.d(TAG, "Ongoing call notification updated") + if (isOngoingCallNotification(entry)) { + val timeView = chipView?.findViewById<Chronometer>(R.id.ongoing_call_chip_time) + if (timeView != null) { + hasOngoingCall = true + val callStartTime = entry.sbn.notification.`when` + timeView.base = callStartTime - + System.currentTimeMillis() + + systemClock.elapsedRealtime() + timeView.start() + 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") + } } } override fun onEntryRemoved(entry: NotificationEntry, reason: Int) { - if (isOngoingCallNotification(entry) && DEBUG) { - Log.d(TAG, "Ongoing call notification removed") + if (isOngoingCallNotification(entry)) { + hasOngoingCall = false + mListeners.forEach { l -> l.onOngoingCallEnded(animate = true) } } } } @@ -54,6 +79,24 @@ class OngoingCallController @Inject constructor( notifCollection.addCollectionListener(notifListener) } } + + fun setChipView(chipView: ViewGroup?) { + this.chipView = chipView + } + + override fun addCallback(listener: OngoingCallListener) { + synchronized(mListeners) { + if (!mListeners.contains(listener)) { + mListeners.add(listener) + } + } + } + + override fun removeCallback(listener: OngoingCallListener) { + synchronized(mListeners) { + mListeners.remove(listener) + } + } } private fun isOngoingCallNotification(entry: NotificationEntry): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt new file mode 100644 index 000000000000..7c583a1491e0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2021 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.phone.ongoingcall + +/** A listener that's notified when an ongoing call is started or ended. */ +interface OngoingCallListener { + /** Called when an ongoing call is started. */ + fun onOngoingCallStarted(animate: Boolean) + + /** Called when an ongoing call is ended. */ + fun onOngoingCallEnded(animate: 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 96cdaf98d964..67fd5eb1acac 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 @@ -38,12 +38,16 @@ import com.android.systemui.R; import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; +import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; +import java.util.Objects; + @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) @SmallTest @@ -53,6 +57,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private View mNotificationAreaInner; private View mCenteredNotificationAreaView; private StatusBarStateController mStatusBarStateController; + private OngoingCallController mOngoingCallController; public CollapsedStatusBarFragmentTest() { super(CollapsedStatusBarFragment.class); @@ -162,8 +167,51 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE)); } + @Test + public void onOngoingCallStarted_notificationsHiddenAndOngoingCallChipDisplayed() { + mFragments.dispatchResume(); + processAllMessages(); + + CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; + fragment.initNotificationIconArea(mMockNotificiationAreaController); + + ArgumentCaptor<OngoingCallListener> ongoingCallListenerCaptor = ArgumentCaptor.forClass( + OngoingCallListener.class); + Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture()); + OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue()); + + when(mOngoingCallController.getHasOngoingCall()).thenReturn(true); + listener.onOngoingCallStarted(/* animate= */ false); + + assertEquals(View.VISIBLE, + mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE)); + } + + @Test + public void onOngoingCallEnded_notificationsDisplayedAndOngoingCallChipHidden() { + mFragments.dispatchResume(); + processAllMessages(); + + CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; + fragment.initNotificationIconArea(mMockNotificiationAreaController); + + ArgumentCaptor<OngoingCallListener> ongoingCallListenerCaptor = ArgumentCaptor.forClass( + OngoingCallListener.class); + Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture()); + OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue()); + + when(mOngoingCallController.getHasOngoingCall()).thenReturn(false); + listener.onOngoingCallEnded(/* animate= */ false); + + assertEquals(View.GONE, + mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE)); + } + @Override protected Fragment instantiate(Context context, String className, Bundle arguments) { - return new CollapsedStatusBarFragment(mock(OngoingCallController.class)); + mOngoingCallController = mock(OngoingCallController.class); + return new CollapsedStatusBarFragment(mOngoingCallController); } } 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 new file mode 100644 index 000000000000..d87d1d1b92db --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2021 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.phone.ongoingcall + +import android.app.Notification +import android.app.PendingIntent +import android.app.Person +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 com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.FeatureFlags +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 +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.never +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class OngoingCallControllerTest : SysuiTestCase() { + + private lateinit var controller: OngoingCallController + private lateinit var notifCollectionListener: NotifCollectionListener + + @Mock private lateinit var mockOngoingCallListener: OngoingCallListener + + private lateinit var chipView: LinearLayout + + @Before + fun setUp() { + allowTestableLooperAsMainThread() + TestableLooper.get(this).runWithLooper { + chipView = LayoutInflater.from(mContext) + .inflate(R.layout.ongoing_call_chip, null) as LinearLayout + } + + MockitoAnnotations.initMocks(this) + val featureFlags = mock(FeatureFlags::class.java) + `when`(featureFlags.isOngoingCallStatusBarChipEnabled).thenReturn(true) + val notificationCollection = mock(CommonNotifCollection::class.java) + + controller = OngoingCallController(notificationCollection, featureFlags, FakeSystemClock()) + controller.init() + controller.addCallback(mockOngoingCallListener) + controller.setChipView(chipView) + + val collectionListenerCaptor = ArgumentCaptor.forClass(NotifCollectionListener::class.java) + verify(notificationCollection).addCollectionListener(collectionListenerCaptor.capture()) + notifCollectionListener = collectionListenerCaptor.value!! + } + + @Test + fun onEntryUpdated_isOngoingCallNotif_listenerNotifiedWithRightCallTime() { + notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) + + verify(mockOngoingCallListener).onOngoingCallStarted(anyBoolean()) + } + + @Test + fun onEntryUpdated_notOngoingCallNotif_listenerNotNotified() { + notifCollectionListener.onEntryUpdated(createNotCallNotifEntry()) + + verify(mockOngoingCallListener, never()).onOngoingCallStarted(anyBoolean()) + } + + @Test + fun onEntryRemoved_ongoingCallNotif_listenerNotified() { + notifCollectionListener.onEntryRemoved(createOngoingCallNotifEntry(), REASON_USER_STOPPED) + + verify(mockOngoingCallListener).onOngoingCallEnded(anyBoolean()) + } + + @Test + fun onEntryRemoved_notOngoingCallNotif_listenerNotNotified() { + notifCollectionListener.onEntryRemoved(createNotCallNotifEntry(), REASON_USER_STOPPED) + + verify(mockOngoingCallListener, never()).onOngoingCallEnded(anyBoolean()) + } + + @Test + fun hasOngoingCall_noOngoingCallNotifSent_returnsFalse() { + assertThat(controller.hasOngoingCall).isFalse() + } + + @Test + fun hasOngoingCall_ongoingCallNotifSentAndChipViewSet_returnsTrue() { + notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) + + assertThat(controller.hasOngoingCall).isTrue() + } + + @Test + fun hasOngoingCall_ongoingCallNotifSentButNoChipView_returnsFalse() { + controller.setChipView(null) + notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) + + assertThat(controller.hasOngoingCall).isFalse() + } + + @Test + fun hasOngoingCall_ongoingCallNotifSentThenRemoved_returnsFalse() { + val ongoingCallNotifEntry = createOngoingCallNotifEntry() + + notifCollectionListener.onEntryUpdated(ongoingCallNotifEntry) + notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED) + + assertThat(controller.hasOngoingCall).isFalse() + } + + private fun createOngoingCallNotifEntry(): NotificationEntry { + val notificationEntryBuilder = NotificationEntryBuilder() + notificationEntryBuilder.modifyNotification(context).style = ongoingCallStyle + return notificationEntryBuilder.build() + } + + private fun createNotCallNotifEntry() = NotificationEntryBuilder().build() +} + +private val ongoingCallStyle = Notification.CallStyle.forOngoingCall( + Person.Builder().setName("name").build(), + /* hangUpIntent= */ mock(PendingIntent::class.java)) |