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() + } +}