diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 1765849c8314d..232b077538ef8 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -73,6 +73,9 @@ public class StatusBarManager { /** @hide */ public static final int DISABLE_SEARCH = View.STATUS_BAR_DISABLE_SEARCH; + /** @hide */ + public static final int DISABLE_ONGOING_CALL_CHIP = View.STATUS_BAR_DISABLE_ONGOING_CALL_CHIP; + /** @hide */ @Deprecated public static final int DISABLE_NAVIGATION = @@ -85,7 +88,7 @@ public class StatusBarManager { public static final int DISABLE_MASK = DISABLE_EXPAND | DISABLE_NOTIFICATION_ICONS | DISABLE_NOTIFICATION_ALERTS | DISABLE_NOTIFICATION_TICKER | DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK - | DISABLE_SEARCH; + | DISABLE_SEARCH | DISABLE_ONGOING_CALL_CHIP; /** @hide */ @IntDef(flag = true, prefix = {"DISABLE_"}, value = { @@ -99,7 +102,8 @@ public class StatusBarManager { DISABLE_RECENT, DISABLE_BACK, DISABLE_CLOCK, - DISABLE_SEARCH + DISABLE_SEARCH, + DISABLE_ONGOING_CALL_CHIP }) @Retention(RetentionPolicy.SOURCE) public @interface DisableFlags {} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b2df3a942469e..11fac05619078 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4001,6 +4001,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public static final int STATUS_BAR_DISABLE_SEARCH = 0x02000000; + /** + * @hide + * + * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked + * out of the public fields to keep the undefined bits out of the developer's way. + * + * Flag to disable the ongoing call chip. + */ + public static final int STATUS_BAR_DISABLE_ONGOING_CALL_CHIP = 0x04000000; + /** * @hide */ @@ -4227,7 +4237,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, name = "STATUS_BAR_DISABLE_RECENT"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_SEARCH, equals = STATUS_BAR_DISABLE_SEARCH, - name = "STATUS_BAR_DISABLE_SEARCH") + name = "STATUS_BAR_DISABLE_SEARCH"), + @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_ONGOING_CALL_CHIP, + equals = STATUS_BAR_DISABLE_ONGOING_CALL_CHIP, + name = "STATUS_BAR_DISABLE_ONGOING_CALL_CHIP") }, formatToHexString = true) @SystemUiVisibility int mSystemUiVisibility; @@ -4256,6 +4269,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, STATUS_BAR_DISABLE_CLOCK, STATUS_BAR_DISABLE_RECENT, STATUS_BAR_DISABLE_SEARCH, + STATUS_BAR_DISABLE_ONGOING_CALL_CHIP, }) @Retention(RetentionPolicy.SOURCE) public @interface SystemUiVisibility {} diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml index c90fc3151ee74..a5e7f5d4cfe62 100644 --- a/packages/SystemUI/res/layout/ongoing_call_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml @@ -23,7 +23,6 @@ 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" > mBlockedIcons = new ArrayList<>(); @@ -106,13 +108,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @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); } }; @@ -120,11 +120,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue public CollapsedStatusBarFragment( OngoingCallController ongoingCallController, SystemStatusAnimationScheduler animationScheduler, - PrivacyDotViewController dotViewController + PrivacyDotViewController dotViewController, + NotificationIconAreaController notificationIconAreaController ) { mOngoingCallController = ongoingCallController; mAnimationScheduler = animationScheduler; mDotViewController = dotViewController; + mNotificationIconAreaController = notificationIconAreaController; } @Override @@ -168,6 +170,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue showClock(false); initEmergencyCryptkeeperText(); initOperatorName(); + initNotificationIconArea(); mAnimationScheduler.addCallback(this); } @@ -204,11 +207,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } } - public void initNotificationIconArea(NotificationIconAreaController - notificationIconAreaController) { + /** Initializes views related to the notification icon area. */ + public void initNotificationIconArea() { ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area); mNotificationIconAreaInner = - notificationIconAreaController.getNotificationInnerAreaView(); + mNotificationIconAreaController.getNotificationInnerAreaView(); if (mNotificationIconAreaInner.getParent() != null) { ((ViewGroup) mNotificationIconAreaInner.getParent()) .removeView(mNotificationIconAreaInner); @@ -216,15 +219,15 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue notificationIconArea.addView(mNotificationIconAreaInner); ViewGroup statusBarCenteredIconArea = mStatusBar.findViewById(R.id.centered_icon_area); - mCenteredIconArea = notificationIconAreaController.getCenteredNotificationAreaView(); + mCenteredIconArea = mNotificationIconAreaController.getCenteredNotificationAreaView(); if (mCenteredIconArea.getParent() != null) { ((ViewGroup) mCenteredIconArea.getParent()) .removeView(mCenteredIconArea); } statusBarCenteredIconArea.addView(mCenteredIconArea); - // Default to showing until we know otherwise. - showNotificationIconArea(false); + // #disable should have already been called, so use the disable values to set visibility. + updateNotificationIconAreaAndCallChip(mDisabled1, false); } @Override @@ -249,13 +252,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue showOperatorName(animate); } } - if ((diff1 & DISABLE_NOTIFICATION_ICONS) != 0) { - if ((state1 & DISABLE_NOTIFICATION_ICONS) != 0) { - hideNotificationIconArea(animate); - } else { - showNotificationIconArea(animate); - } + + // The ongoing call chip and notification icon visibilities are intertwined, so update both + // if either change. + if (((diff1 & DISABLE_ONGOING_CALL_CHIP) != 0) + || ((diff1 & DISABLE_NOTIFICATION_ICONS) != 0)) { + updateNotificationIconAreaAndCallChip(state1, animate); } + // The clock may have already been hidden, but we might want to shift its // visibility to GONE from INVISIBLE or vice versa if ((diff1 & DISABLE_CLOCK) != 0 || mClockView.getVisibility() != clockHiddenMode()) { @@ -273,10 +277,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue state |= DISABLE_CLOCK; } - if (mOngoingCallController.hasOngoingCall()) { - state |= DISABLE_NOTIFICATION_ICONS; - } - if (!mKeyguardStateController.isLaunchTransitionFadingAway() && !mKeyguardStateController.isKeyguardFadingAway() && shouldHideNotificationIcons() @@ -304,9 +304,40 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO; } + if (mOngoingCallController.hasOngoingCall()) { + state &= ~DISABLE_ONGOING_CALL_CHIP; + } else { + state |= DISABLE_ONGOING_CALL_CHIP; + } + return state; } + /** + * Updates the visibility of the notification icon area and ongoing call chip based on disabled1 + * state. + */ + private void updateNotificationIconAreaAndCallChip(int state1, boolean animate) { + boolean disableNotifications = (state1 & DISABLE_NOTIFICATION_ICONS) != 0; + boolean hasOngoingCall = (state1 & DISABLE_ONGOING_CALL_CHIP) == 0; + + // Hide notifications if the disable flag is set or we have an ongoing call. + if (disableNotifications || hasOngoingCall) { + hideNotificationIconArea(animate); + } else { + showNotificationIconArea(animate); + } + + // Show the ongoing call chip only if there is an ongoing call *and* notification icons + // are allowed. (The ongoing call chip occupies the same area as the notification icons, + // so if the icons are disabled then the call chip should be, too.) + if (hasOngoingCall && !disableNotifications) { + showOngoingCallChip(animate); + } else { + hideOngoingCallChip(animate); + } + } + private boolean shouldHideNotificationIcons() { if (!mStatusBar.isClosed() && mStatusBarComponent.hideStatusBarIconsWhenExpanded()) { return true; @@ -336,6 +367,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue animateShow(mClockView, animate); } + /** Hides the ongoing call chip. */ + public void hideOngoingCallChip(boolean animate) { + animateHiddenState(mOngoingCallChip, View.GONE, animate); + } + + /** Displays the ongoing call chip. */ + public void showOngoingCallChip(boolean animate) { + animateShow(mOngoingCallChip, animate); + } + /** * If panel is expanded/expanding it usually means QS shade is opening, so * don't set the clock GONE otherwise it'll mess up the animation. @@ -457,7 +498,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onDozingChanged(boolean isDozing) { - disable(getContext().getDisplayId(), mDisabled1, mDisabled1, false /* animate */); + disable(getContext().getDisplayId(), mDisabled1, mDisabled2, false /* animate */); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 026072bf0d6a9..20b7a12cd5f63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1139,7 +1139,6 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarView.setScrimController(mScrimController); mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners); - statusBarFragment.initNotificationIconArea(mNotificationIconAreaController); // CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of // mStatusBarView.mExpanded and mStatusBarView.mBouncerShowing are false. // PhoneStatusBarView's new instance will set to be gone in @@ -1182,7 +1181,8 @@ public class StatusBar extends SystemUI implements DemoMode, new CollapsedStatusBarFragment( mOngoingCallController, mAnimationScheduler, - mDotViewController), + mDotViewController, + mNotificationIconAreaController), CollapsedStatusBarFragment.TAG) .commit(); 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 0e3e0cc8ea97f..a01e0b1c3e3a1 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 @@ -31,6 +31,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.view.View; import android.view.ViewPropertyAnimator; +import android.widget.FrameLayout; import androidx.test.filters.SmallTest; @@ -40,24 +41,19 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.events.PrivacyDotViewController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; 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 public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { - private NotificationIconAreaController mMockNotificiationAreaController; + private NotificationIconAreaController mMockNotificationAreaController; private View mNotificationAreaInner; - private View mCenteredNotificationAreaView; private StatusBarStateController mStatusBarStateController; private OngoingCallController mOngoingCallController; private SystemStatusAnimationScheduler mAnimationScheduler; @@ -74,26 +70,16 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mStatusBarStateController = mDependency .injectMockDependency(StatusBarStateController.class); injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); - mMockNotificiationAreaController = mock(NotificationIconAreaController.class); - mNotificationAreaInner = mock(View.class); - mCenteredNotificationAreaView = mock(View.class); when(statusBar.getPanelController()).thenReturn( mock(NotificationPanelViewController.class)); - when(mNotificationAreaInner.animate()).thenReturn(mock(ViewPropertyAnimator.class)); - when(mMockNotificiationAreaController.getNotificationInnerAreaView()).thenReturn( - mNotificationAreaInner); - when(mCenteredNotificationAreaView.animate()).thenReturn(mock(ViewPropertyAnimator.class)); - when(mMockNotificiationAreaController.getCenteredNotificationAreaView()).thenReturn( - mCenteredNotificationAreaView); } @Test public void testDisableNone() throws Exception { mFragments.dispatchResume(); processAllMessages(); - CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; - fragment.initNotificationIconArea(mMockNotificiationAreaController); + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area) @@ -106,9 +92,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { public void testDisableSystemInfo() throws Exception { mFragments.dispatchResume(); processAllMessages(); - CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; - fragment.initNotificationIconArea(mMockNotificiationAreaController); + fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false); assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area) @@ -124,12 +109,11 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { public void testDisableNotifications() throws Exception { mFragments.dispatchResume(); processAllMessages(); - CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; - fragment.initNotificationIconArea(mMockNotificiationAreaController); + fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false); - Mockito.verify(mNotificationAreaInner).setVisibility(eq(View.INVISIBLE)); + Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE)); fragment.disable(DEFAULT_DISPLAY, 0, 0, false); @@ -140,9 +124,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { public void testDisableClock() throws Exception { mFragments.dispatchResume(); processAllMessages(); - CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; - fragment.initNotificationIconArea(mMockNotificiationAreaController); + fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false); assertEquals(View.GONE, mFragment.getView().findViewById(R.id.clock).getVisibility()); @@ -152,16 +135,82 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility()); } + @Test + public void disable_noOngoingCall_chipHidden() { + mFragments.dispatchResume(); + processAllMessages(); + CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; + + when(mOngoingCallController.hasOngoingCall()).thenReturn(false); + + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); + + assertEquals(View.GONE, + mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + } + + @Test + public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() { + mFragments.dispatchResume(); + processAllMessages(); + CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; + + when(mOngoingCallController.hasOngoingCall()).thenReturn(true); + + fragment.disable(DEFAULT_DISPLAY, 0, 0, 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 disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() { + mFragments.dispatchResume(); + processAllMessages(); + CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; + + when(mOngoingCallController.hasOngoingCall()).thenReturn(true); + + fragment.disable(DEFAULT_DISPLAY, + StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false); + + assertEquals(View.GONE, + mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + } + + @Test + public void disable_ongoingCallEnded_chipHidden() { + mFragments.dispatchResume(); + processAllMessages(); + CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; + + when(mOngoingCallController.hasOngoingCall()).thenReturn(true); + + // Ongoing call started + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); + assertEquals(View.VISIBLE, + mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + + // Ongoing call ended + when(mOngoingCallController.hasOngoingCall()).thenReturn(false); + + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); + + assertEquals(View.GONE, + mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + } + @Test public void testOnDozingChanged() throws Exception { mFragments.dispatchResume(); processAllMessages(); - CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; - fragment.initNotificationIconArea(mMockNotificiationAreaController); + fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false); - Mockito.verify(mNotificationAreaInner).setVisibility(eq(View.INVISIBLE)); + Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE)); reset(mStatusBarStateController); when(mStatusBarStateController.isDozing()).thenReturn(true); @@ -171,56 +220,35 @@ 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 ongoingCallListenerCaptor = ArgumentCaptor.forClass( - OngoingCallListener.class); - Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture()); - OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue()); - - when(mOngoingCallController.hasOngoingCall()).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 ongoingCallListenerCaptor = ArgumentCaptor.forClass( - OngoingCallListener.class); - Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture()); - OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue()); - - when(mOngoingCallController.hasOngoingCall()).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) { mOngoingCallController = mock(OngoingCallController.class); mAnimationScheduler = mock(SystemStatusAnimationScheduler.class); mDotViewController = mock(PrivacyDotViewController.class); + setUpNotificationIconAreaController(); return new CollapsedStatusBarFragment( mOngoingCallController, mAnimationScheduler, - mDotViewController); + mDotViewController, + mMockNotificationAreaController); + } + + private void setUpNotificationIconAreaController() { + mMockNotificationAreaController = mock(NotificationIconAreaController.class); + + mNotificationAreaInner = mock(View.class); + View centeredNotificationAreaView = mock(View.class); + + when(mNotificationAreaInner.getLayoutParams()).thenReturn( + new FrameLayout.LayoutParams(100, 100)); + when(centeredNotificationAreaView.getLayoutParams()).thenReturn( + new FrameLayout.LayoutParams(100, 100)); + when(mNotificationAreaInner.animate()).thenReturn(mock(ViewPropertyAnimator.class)); + when(centeredNotificationAreaView.animate()).thenReturn(mock(ViewPropertyAnimator.class)); + + when(mMockNotificationAreaController.getCenteredNotificationAreaView()).thenReturn( + centeredNotificationAreaView); + when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn( + mNotificationAreaInner); } }