From b3ca9f6d98a523ebe526d6c6c922175a205d874a Mon Sep 17 00:00:00 2001 From: Caitlin Shkuratov Date: Wed, 26 Apr 2023 19:52:26 +0000 Subject: [PATCH 1/2] [Status Bar] Don't propagate internally computed disable flags. The test with the /** Regression test for b/279790651 */ header is a test that fails without this change and passes with it. Fixes: 279790651 Test: atest CollapsedStatusBarFragmentTest Test: verify status bar still hidden on keyguard Test: verify clock & notif icons hidden when there's a HUN Test: verify notif icons hidden when there's an ongoing call Change-Id: I0616d6f4990541397aaed34001b9a471f324064d --- .../fragment/CollapsedStatusBarFragment.java | 36 ++++++---- .../CollapsedStatusBarFragmentTest.java | 68 ++++++++++++++++++- 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 453dd1bb6f819..9c3c53e7ed96d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -112,8 +112,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private View mClockView; private View mOngoingCallChip; private View mNotificationIconAreaInner; - private int mDisabled1; - private int mDisabled2; + // Disabled flags come in from external callers, but we also sometimes modify them internally. + // We need to store both so that we don't accidentally propagate our internally modified flags + // for too long. + private int mExternalDisabled1; + private int mExternalDisabled2; + private int mInternalDisabled1; + private int mInternalDisabled2; private DarkIconManager mDarkIconManager; private final StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory; private final CommandQueue mCommandQueue; @@ -141,7 +146,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final OngoingCallListener mOngoingCallListener = new OngoingCallListener() { @Override public void onOngoingCallStateChanged(boolean animate) { - disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate); + updateStatusBarVisibilities(animate); } }; private OperatorNameViewController mOperatorNameViewController; @@ -389,7 +394,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue notificationIconArea.addView(mNotificationIconAreaInner); // #disable should have already been called, so use the disable values to set visibility. - updateNotificationIconAreaAndCallChip(mDisabled1, false); + updateNotificationIconAreaAndCallChip(mInternalDisabled1, false); } /** @@ -403,12 +408,21 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue return mStatusBarFragmentComponent; } + private void updateStatusBarVisibilities(boolean animate) { + // Make sure that we pass the last *external* flags so that we don't accidentally propagate + // our internal adjustments. + disable(getContext().getDisplayId(), mExternalDisabled1, mExternalDisabled2, animate); + } + @Override public void disable(int displayId, int state1, int state2, boolean animate) { if (displayId != getContext().getDisplayId()) { return; } + mExternalDisabled1 = state1; + mExternalDisabled2 = state2; + int state1BeforeAdjustment = state1; state1 = adjustDisableFlags(state1); @@ -416,12 +430,12 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue /* new= */ new DisableState(state1BeforeAdjustment, state2), /* newAfterLocalModification= */ new DisableState(state1, state2)); - final int old1 = mDisabled1; + final int old1 = mInternalDisabled1; final int diff1 = state1 ^ old1; - final int old2 = mDisabled2; + final int old2 = mInternalDisabled2; final int diff2 = state2 ^ old2; - mDisabled1 = state1; - mDisabled2 = state2; + mInternalDisabled1 = state1; + mInternalDisabled2 = state2; if ((diff1 & DISABLE_SYSTEM_INFO) != 0 || ((diff2 & DISABLE2_SYSTEM_ICONS) != 0)) { if ((state1 & DISABLE_SYSTEM_INFO) != 0 || ((state2 & DISABLE2_SYSTEM_ICONS) != 0)) { hideEndSideContent(animate); @@ -683,7 +697,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onDozingChanged(boolean isDozing) { - disable(getContext().getDisplayId(), mDisabled1, mDisabled2, false /* animate */); + updateStatusBarVisibilities(/* animate= */ false); } @Nullable @@ -698,10 +712,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue return mSystemEventAnimator.onSystemEventAnimationFinish(hasPersistentDot); } - private boolean isSystemIconAreaDisabled() { - return (mDisabled1 & DISABLE_SYSTEM_INFO) != 0 || (mDisabled2 & DISABLE2_SYSTEM_ICONS) != 0; - } - private void updateStatusBarLocation(int left, int right) { int leftMargin = left - mStatusBar.getLeft(); int rightMargin = mStatusBar.getRight() - right; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 2a3c775eaf544..209dc5ad48d4b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone.fragment; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED; +import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN; import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN; import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT; import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE; @@ -93,6 +95,7 @@ import java.util.List; public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private NotificationIconAreaController mMockNotificationAreaController; + private ShadeExpansionStateManager mShadeExpansionStateManager; private View mNotificationAreaInner; private OngoingCallController mOngoingCallController; private SystemStatusAnimationScheduler mAnimationScheduler; @@ -293,6 +296,67 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { assertEquals(View.VISIBLE, getClockView().getVisibility()); } + + @Test + public void disable_shadeOpenAndShouldHide_everythingHidden() { + CollapsedStatusBarFragment fragment = resumeAndGetFragment(); + + // WHEN the shade is open and configured to hide the status bar icons + mShadeExpansionStateManager.updateState(STATE_OPEN); + when(mShadeViewController.shouldHideStatusBarIconsWhenExpanded()).thenReturn(true); + + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); + + // THEN all views are hidden + assertEquals(View.INVISIBLE, getClockView().getVisibility()); + Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE)); + assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility()); + } + + @Test + public void disable_shadeOpenButNotShouldHide_everythingShown() { + CollapsedStatusBarFragment fragment = resumeAndGetFragment(); + + // WHEN the shade is open but *not* configured to hide the status bar icons + mShadeExpansionStateManager.updateState(STATE_OPEN); + when(mShadeViewController.shouldHideStatusBarIconsWhenExpanded()).thenReturn(false); + + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); + + // THEN all views are shown + assertEquals(View.VISIBLE, getClockView().getVisibility()); + Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE)); + assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); + } + + /** Regression test for b/279790651. */ + @Test + public void disable_shadeOpenAndShouldHide_thenShadeNotOpenAndDozingUpdate_everythingShown() { + CollapsedStatusBarFragment fragment = resumeAndGetFragment(); + + // WHEN the shade is open and configured to hide the status bar icons + mShadeExpansionStateManager.updateState(STATE_OPEN); + when(mShadeViewController.shouldHideStatusBarIconsWhenExpanded()).thenReturn(true); + + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); + + // THEN all views are hidden + assertEquals(View.INVISIBLE, getClockView().getVisibility()); + Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE)); + assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility()); + + // WHEN the shade is updated to no longer be open + mShadeExpansionStateManager.updateState(STATE_CLOSED); + + // AND we internally request an update via dozing change + fragment.onDozingChanged(true); + + // THEN all views are shown + assertEquals(View.VISIBLE, getClockView().getVisibility()); + Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE)); + assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); + } + @Test public void userChip_defaultVisibilityIsGone() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -494,6 +558,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { when(mIconManagerFactory.create(any(), any())).thenReturn(mIconManager); mSecureSettings = mock(SecureSettings.class); + mShadeExpansionStateManager = new ShadeExpansionStateManager(); + setUpNotificationIconAreaController(); return new CollapsedStatusBarFragment( mStatusBarFragmentComponentFactory, @@ -501,7 +567,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mAnimationScheduler, mLocationPublisher, mMockNotificationAreaController, - new ShadeExpansionStateManager(), + mShadeExpansionStateManager, mock(FeatureFlags.class), mStatusBarIconController, mIconManagerFactory, From 1067feb92bb41d5a70a2e4396ecb98e3f1acf2c8 Mon Sep 17 00:00:00 2001 From: Caitlin Shkuratov Date: Wed, 26 Apr 2023 20:56:21 +0000 Subject: [PATCH 2/2] [Status Bar] Store disable flag calculations in a data class. The disable flags are split into two ints and are difficult to reason about because they're negative booleans and involve bitwise logic. This immediately converts the disable flags into a data class object with clear naming and positive booleans. Fixes: 279899176 Test: atest CollapsedStatuBarFragmentTest (existing tests are mostly sufficient, added a few additional ones) Test: atest StatusBarVisibilityModelTest Test: verify status bar still hidden on keyguard Test: verify clock & notif icons hidden when there's a HUN Test: verify notif icons hidden when there's an ongoing call Test: verify CollapsedSbFragmentLog statements Change-Id: If0d82a5be1858d71a68e2c1aeda9c790be229168 --- .../systemui/log/dagger/LogModule.java | 2 +- .../fragment/CollapsedStatusBarFragment.java | 120 ++++++++---------- .../CollapsedStatusBarFragmentLogger.kt | 24 +++- .../fragment/StatusBarVisibilityModel.kt | 58 +++++++++ .../CollapsedStatusBarFragmentLoggerTest.kt | 32 ++++- .../CollapsedStatusBarFragmentTest.java | 26 +++- .../fragment/StatusBarVisibilityModelTest.kt | 108 ++++++++++++++++ 7 files changed, 287 insertions(+), 83 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 658f6a0878542..6988bd899e23c 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -209,7 +209,7 @@ public class LogModule { @SysUISingleton @CollapsedSbFragmentLog public static LogBuffer provideCollapsedSbFragmentLogBuffer(LogBufferFactory factory) { - return factory.create("CollapsedSbFragmentLog", 20); + return factory.create("CollapsedSbFragmentLog", 40); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 9c3c53e7ed96d..620d2824acfaa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -14,11 +14,7 @@ package com.android.systemui.statusbar.phone.fragment; -import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS; -import static android.app.StatusBarManager.DISABLE_CLOCK; -import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS; -import static android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP; -import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO; + import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE; import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT; @@ -112,13 +108,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private View mClockView; private View mOngoingCallChip; private View mNotificationIconAreaInner; - // Disabled flags come in from external callers, but we also sometimes modify them internally. - // We need to store both so that we don't accidentally propagate our internally modified flags - // for too long. - private int mExternalDisabled1; - private int mExternalDisabled2; - private int mInternalDisabled1; - private int mInternalDisabled2; + // Visibilities come in from external system callers via disable flags, but we also sometimes + // modify the visibilities internally. We need to store both so that we don't accidentally + // propagate our internally modified flags for too long. + private StatusBarVisibilityModel mLastSystemVisibility = + StatusBarVisibilityModel.createDefaultModel(); + private StatusBarVisibilityModel mLastModifiedVisibility = + StatusBarVisibilityModel.createDefaultModel(); private DarkIconManager mDarkIconManager; private final StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory; private final CommandQueue mCommandQueue; @@ -393,8 +389,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } notificationIconArea.addView(mNotificationIconAreaInner); - // #disable should have already been called, so use the disable values to set visibility. - updateNotificationIconAreaAndCallChip(mInternalDisabled1, false); + updateNotificationIconAreaAndCallChip(/* animate= */ false); } /** @@ -408,63 +403,55 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue return mStatusBarFragmentComponent; } - private void updateStatusBarVisibilities(boolean animate) { - // Make sure that we pass the last *external* flags so that we don't accidentally propagate - // our internal adjustments. - disable(getContext().getDisplayId(), mExternalDisabled1, mExternalDisabled2, animate); - } - @Override public void disable(int displayId, int state1, int state2, boolean animate) { if (displayId != getContext().getDisplayId()) { return; } + mCollapsedStatusBarFragmentLogger + .logDisableFlagChange(new DisableState(state1, state2)); + mLastSystemVisibility = + StatusBarVisibilityModel.createModelFromFlags(state1, state2); + updateStatusBarVisibilities(animate); + } - mExternalDisabled1 = state1; - mExternalDisabled2 = state2; + private void updateStatusBarVisibilities(boolean animate) { + StatusBarVisibilityModel previousModel = mLastModifiedVisibility; + StatusBarVisibilityModel newModel = calculateInternalModel(mLastSystemVisibility); + mCollapsedStatusBarFragmentLogger.logVisibilityModel(newModel); + mLastModifiedVisibility = newModel; - int state1BeforeAdjustment = state1; - state1 = adjustDisableFlags(state1); - - mCollapsedStatusBarFragmentLogger.logDisableFlagChange( - /* new= */ new DisableState(state1BeforeAdjustment, state2), - /* newAfterLocalModification= */ new DisableState(state1, state2)); - - final int old1 = mInternalDisabled1; - final int diff1 = state1 ^ old1; - final int old2 = mInternalDisabled2; - final int diff2 = state2 ^ old2; - mInternalDisabled1 = state1; - mInternalDisabled2 = state2; - if ((diff1 & DISABLE_SYSTEM_INFO) != 0 || ((diff2 & DISABLE2_SYSTEM_ICONS) != 0)) { - if ((state1 & DISABLE_SYSTEM_INFO) != 0 || ((state2 & DISABLE2_SYSTEM_ICONS) != 0)) { - hideEndSideContent(animate); - hideOperatorName(animate); - } else { + if (newModel.getShowSystemInfo() != previousModel.getShowSystemInfo()) { + if (newModel.getShowSystemInfo()) { showEndSideContent(animate); showOperatorName(animate); + } else { + hideEndSideContent(animate); + hideOperatorName(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); + if (newModel.getShowNotificationIcons() != previousModel.getShowNotificationIcons() + || newModel.getShowOngoingCallChip() != previousModel.getShowOngoingCallChip()) { + updateNotificationIconAreaAndCallChip(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()) { - if ((state1 & DISABLE_CLOCK) != 0) { - hideClock(animate); - } else { + if (newModel.getShowClock() != previousModel.getShowClock() + || mClockView.getVisibility() != clockHiddenMode()) { + if (newModel.getShowClock()) { showClock(animate); + } else { + hideClock(animate); } } } - protected int adjustDisableFlags(int state) { + private StatusBarVisibilityModel calculateInternalModel( + StatusBarVisibilityModel externalModel) { boolean headsUpVisible = mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible(); @@ -473,34 +460,31 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue && shouldHideNotificationIcons() && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD && headsUpVisible)) { - state |= DISABLE_NOTIFICATION_ICONS; - state |= DISABLE_SYSTEM_INFO; - state |= DISABLE_CLOCK; + // Hide everything + return new StatusBarVisibilityModel( + /* showClock= */ false, + /* showNotificationIcons= */ false, + /* showOngoingCallChip= */ false, + /* showSystemInfo= */ false); } - if (mOngoingCallController.hasOngoingCall()) { - state &= ~DISABLE_ONGOING_CALL_CHIP; - } else { - state |= DISABLE_ONGOING_CALL_CHIP; - } - - if (headsUpVisible) { - // Disable everything on the left side of the status bar, since the app name for the - // heads up notification appears there instead. - state |= DISABLE_CLOCK; - state |= DISABLE_ONGOING_CALL_CHIP; - } - - return state; + boolean showClock = externalModel.getShowClock() && !headsUpVisible; + boolean showOngoingCallChip = mOngoingCallController.hasOngoingCall() && !headsUpVisible; + return new StatusBarVisibilityModel( + showClock, + externalModel.getShowNotificationIcons(), + showOngoingCallChip, + externalModel.getShowSystemInfo()); } /** * 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; + private void updateNotificationIconAreaAndCallChip(boolean animate) { + StatusBarVisibilityModel visibilityModel = mLastModifiedVisibility; + boolean disableNotifications = !visibilityModel.getShowNotificationIcons(); + boolean hasOngoingCall = visibilityModel.getShowOngoingCallChip(); // Hide notifications if the disable flag is set or we have an ongoing call. if (disableNotifications || hasOngoingCall) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt index d64bc58a0c37c..59f74ec453a68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt @@ -37,7 +37,6 @@ class CollapsedStatusBarFragmentLogger @Inject constructor( */ fun logDisableFlagChange( new: DisableFlagsLogger.DisableState, - newAfterLocalModification: DisableFlagsLogger.DisableState ) { buffer.log( TAG, @@ -45,19 +44,34 @@ class CollapsedStatusBarFragmentLogger @Inject constructor( { int1 = new.disable1 int2 = new.disable2 - long1 = newAfterLocalModification.disable1.toLong() - long2 = newAfterLocalModification.disable2.toLong() }, { disableFlagsLogger.getDisableFlagsString( old = null, new = DisableFlagsLogger.DisableState(int1, int2), - newAfterLocalModification = - DisableFlagsLogger.DisableState(long1.toInt(), long2.toInt()) ) } ) } + + fun logVisibilityModel(model: StatusBarVisibilityModel) { + buffer.log( + TAG, + LogLevel.INFO, + { + bool1 = model.showClock + bool2 = model.showNotificationIcons + bool3 = model.showOngoingCallChip + bool4 = model.showSystemInfo + }, + { "New visibilities calculated internally. " + + "showClock=$bool1 " + + "showNotificationIcons=$bool2 " + + "showOngoingCallChip=$bool3 " + + "showSystemInfo=$bool4" + } + ) + } } private const val TAG = "CollapsedSbFragment" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt new file mode 100644 index 0000000000000..cf54cb7aa9549 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 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.fragment + +import android.app.StatusBarManager.DISABLE2_NONE +import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS +import android.app.StatusBarManager.DISABLE_CLOCK +import android.app.StatusBarManager.DISABLE_NONE +import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS +import android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP +import android.app.StatusBarManager.DISABLE_SYSTEM_INFO + +/** A model for which parts of the status bar should be visible or not visible. */ +data class StatusBarVisibilityModel( + val showClock: Boolean, + val showNotificationIcons: Boolean, + val showOngoingCallChip: Boolean, + val showSystemInfo: Boolean, +) { + companion object { + /** Creates the default model. */ + @JvmStatic + fun createDefaultModel(): StatusBarVisibilityModel { + return createModelFromFlags(DISABLE_NONE, DISABLE2_NONE) + } + + /** + * Given a set of disabled flags, converts them into the correct visibility statuses. + * + * See [CommandQueue.Callbacks.disable]. + */ + @JvmStatic + fun createModelFromFlags(disabled1: Int, disabled2: Int): StatusBarVisibilityModel { + return StatusBarVisibilityModel( + showClock = (disabled1 and DISABLE_CLOCK) == 0, + showNotificationIcons = (disabled1 and DISABLE_NOTIFICATION_ICONS) == 0, + // TODO(b/279899176): [CollapsedStatusBarFragment] always overwrites this with the + // value of [OngoingCallController]. Do we need to process the flag here? + showOngoingCallChip = (disabled1 and DISABLE_ONGOING_CALL_CHIP) == 0, + showSystemInfo = + (disabled1 and DISABLE_SYSTEM_INFO) == 0 && + (disabled2 and DISABLE2_SYSTEM_ICONS) == 0 + ) + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt index 3a0a94ddd5112..ac3b28c72fa28 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt @@ -43,15 +43,39 @@ class CollapsedStatusBarFragmentLoggerTest : SysuiTestCase() { fun logDisableFlagChange_bufferHasStates() { val state = DisableFlagsLogger.DisableState(0, 1) - logger.logDisableFlagChange(state, state) + logger.logDisableFlagChange(state) val stringWriter = StringWriter() buffer.dump(PrintWriter(stringWriter), tailLength = 0) val actualString = stringWriter.toString() - val expectedLogString = disableFlagsLogger.getDisableFlagsString( - old = null, new = state, newAfterLocalModification = state - ) + val expectedLogString = + disableFlagsLogger.getDisableFlagsString( + old = null, + new = state, + newAfterLocalModification = null, + ) assertThat(actualString).contains(expectedLogString) } + + @Test + fun logVisibilityModel_bufferCorrect() { + logger.logVisibilityModel( + StatusBarVisibilityModel( + showClock = false, + showNotificationIcons = true, + showOngoingCallChip = false, + showSystemInfo = true, + ) + ) + + val stringWriter = StringWriter() + buffer.dump(PrintWriter(stringWriter), tailLength = 0) + val actualString = stringWriter.toString() + + assertThat(actualString).contains("showClock=false") + assertThat(actualString).contains("showNotificationIcons=true") + assertThat(actualString).contains("showOngoingCallChip=false") + assertThat(actualString).contains("showSystemInfo=true") + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 209dc5ad48d4b..03fafcb853251 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -176,6 +176,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, 0, 0, false); assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); + + fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false); + + assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility()); } @Test @@ -281,6 +285,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, 0, 0, false); Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE)); + + fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false); + + Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE)); } @Test @@ -294,8 +302,11 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, 0, 0, false); assertEquals(View.VISIBLE, getClockView().getVisibility()); - } + fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false); + + assertEquals(View.GONE, getClockView().getVisibility()); + } @Test public void disable_shadeOpenAndShouldHide_everythingHidden() { @@ -387,7 +398,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE)); - } @Test @@ -420,20 +430,26 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { public void disable_ongoingCallEnded_chipHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); - when(mOngoingCallController.hasOngoingCall()).thenReturn(true); - // Ongoing call started + when(mOngoingCallController.hasOngoingCall()).thenReturn(true); 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()); + + // Ongoing call started + when(mOngoingCallController.hasOngoingCall()).thenReturn(true); + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); + + assertEquals(View.VISIBLE, + mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt new file mode 100644 index 0000000000000..8e789cb2cae65 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 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.fragment + +import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS +import android.app.StatusBarManager.DISABLE_CLOCK +import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS +import android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP +import android.app.StatusBarManager.DISABLE_SYSTEM_INFO +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.phone.fragment.StatusBarVisibilityModel.Companion.createDefaultModel +import com.android.systemui.statusbar.phone.fragment.StatusBarVisibilityModel.Companion.createModelFromFlags +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +@SmallTest +class StatusBarVisibilityModelTest : SysuiTestCase() { + @Test + fun createDefaultModel_everythingEnabled() { + val result = createDefaultModel() + + val expected = + StatusBarVisibilityModel( + showClock = true, + showNotificationIcons = true, + showOngoingCallChip = true, + showSystemInfo = true, + ) + + assertThat(result).isEqualTo(expected) + } + + @Test + fun createModelFromFlags_clockNotDisabled_showClockTrue() { + val result = createModelFromFlags(disabled1 = 0, disabled2 = 0) + + assertThat(result.showClock).isTrue() + } + + @Test + fun createModelFromFlags_clockDisabled_showClockFalse() { + val result = createModelFromFlags(disabled1 = DISABLE_CLOCK, disabled2 = 0) + + assertThat(result.showClock).isFalse() + } + + @Test + fun createModelFromFlags_notificationIconsNotDisabled_showNotificationIconsTrue() { + val result = createModelFromFlags(disabled1 = 0, disabled2 = 0) + + assertThat(result.showNotificationIcons).isTrue() + } + + @Test + fun createModelFromFlags_notificationIconsDisabled_showNotificationIconsFalse() { + val result = createModelFromFlags(disabled1 = DISABLE_NOTIFICATION_ICONS, disabled2 = 0) + + assertThat(result.showNotificationIcons).isFalse() + } + + @Test + fun createModelFromFlags_ongoingCallChipNotDisabled_showOngoingCallChipTrue() { + val result = createModelFromFlags(disabled1 = 0, disabled2 = 0) + + assertThat(result.showOngoingCallChip).isTrue() + } + + @Test + fun createModelFromFlags_ongoingCallChipDisabled_showOngoingCallChipFalse() { + val result = createModelFromFlags(disabled1 = DISABLE_ONGOING_CALL_CHIP, disabled2 = 0) + + assertThat(result.showOngoingCallChip).isFalse() + } + + @Test + fun createModelFromFlags_systemInfoAndIconsNotDisabled_showSystemInfoTrue() { + val result = createModelFromFlags(disabled1 = 0, disabled2 = 0) + + assertThat(result.showSystemInfo).isTrue() + } + + @Test + fun createModelFromFlags_disable1SystemInfoDisabled_showSystemInfoFalse() { + val result = createModelFromFlags(disabled1 = DISABLE_SYSTEM_INFO, disabled2 = 0) + + assertThat(result.showSystemInfo).isFalse() + } + + @Test + fun createModelFromFlags_disable2SystemIconsDisabled_showSystemInfoFalse() { + val result = createModelFromFlags(disabled1 = 0, disabled2 = DISABLE2_SYSTEM_ICONS) + + assertThat(result.showSystemInfo).isFalse() + } +}