From 05d9591277668d7f614f2fe762bb40192e2f6af3 Mon Sep 17 00:00:00 2001 From: Michal Brzezinski Date: Thu, 15 Jul 2021 16:23:57 +0100 Subject: [PATCH] Showing brightness slider in collapsed QS in split shade This is first step of showing brightnesss slider - this change just makes it appear and actually working. It doesn't cover: - animation between collapsed slider and expanded slider - showing slider mirror (disappearing background when changing brightness) Bug: 193651759 Test: QuickQSBrightnessControllerTest Change-Id: I21e71e3204352a3801386924f33094e029ac3da8 --- .../quick_status_bar_expanded_header.xml | 4 +- .../systemui/qs/QSPanelControllerBase.java | 5 +- .../qs/QuickQSBrightnessController.kt | 94 +++++++++++++++ .../com/android/systemui/qs/QuickQSPanel.java | 5 - .../systemui/qs/QuickQSPanelController.java | 25 +++- .../qs/QuickStatusBarHeaderController.java | 14 +-- .../brightness/BrightnessController.java | 8 ++ .../settings/brightness/BrightnessSlider.java | 10 ++ .../settings/brightness/ToggleSlider.java | 3 + .../phone/dagger/StatusBarComponent.java | 4 +- .../qs/QuickQSBrightnessControllerTest.kt | 107 ++++++++++++++++++ .../systemui/qs/QuickQSPanelControllerTest.kt | 5 +- 12 files changed, 265 insertions(+), 19 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml index 1be20d823007d..03189fa2af037 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml @@ -32,7 +32,7 @@ android:paddingStart="0dp" android:elevation="4dp" > - + - + extends ViewContr private final DumpManager mDumpManager; private final FeatureFlags mFeatureFlags; protected final ArrayList mRecords = new ArrayList<>(); - private boolean mShouldUseSplitNotificationShade; + protected boolean mShouldUseSplitNotificationShade; @Nullable private Consumer mMediaVisibilityChangedListener; @@ -88,11 +88,14 @@ public abstract class QSPanelControllerBase extends ViewContr Utils.shouldUseSplitNotificationShade(mFeatureFlags, getResources()); if (newConfig.orientation != mLastOrientation) { mLastOrientation = newConfig.orientation; + onScreenRotated(); switchTileLayout(false); } } }; + protected void onScreenRotated() { } + private final Function1 mMediaHostVisibilityListener = (visible) -> { if (mMediaVisibilityChangedListener != null) { mMediaVisibilityChangedListener.accept(visible); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt new file mode 100644 index 0000000000000..7c81abcfe5840 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt @@ -0,0 +1,94 @@ +/* + * 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.qs + +import androidx.annotation.VisibleForTesting +import com.android.systemui.settings.brightness.BrightnessController +import com.android.systemui.settings.brightness.BrightnessSlider +import javax.inject.Inject + +/** + * Controls brightness slider in QQS, which is visible only in split shade. It's responsible for + * showing/hiding it when appropriate and (un)registering listeners + */ +class QuickQSBrightnessController @VisibleForTesting constructor( + private val brightnessControllerFactory: () -> BrightnessController +) { + + @Inject constructor( + brightnessControllerFactory: BrightnessController.Factory, + brightnessSliderFactory: BrightnessSlider.Factory, + quickQSPanel: QuickQSPanel + ) : this(brightnessControllerFactory = { + val slider = brightnessSliderFactory.create(quickQSPanel.context, quickQSPanel) + slider.init() + quickQSPanel.setBrightnessView(slider.rootView) + brightnessControllerFactory.create(slider) + }) + + private var isListening = false + private var brightnessController: BrightnessController? = null + + fun init(shouldUseSplitNotificationShade: Boolean) { + refreshVisibility(shouldUseSplitNotificationShade) + } + + /** + * Starts/Stops listening for brightness changing events. + * It's fine to call this function even if slider is not visible (which would be the case for + * all small screen devices), it will just do nothing in that case + */ + fun setListening(listening: Boolean) { + if (listening) { + // controller can be null when slider was never shown + if (!isListening && brightnessController != null) { + brightnessController?.registerCallbacks() + isListening = true + } + } else { + brightnessController?.unregisterCallbacks() + isListening = false + } + } + + fun checkRestrictionAndSetEnabled() { + brightnessController?.checkRestrictionAndSetEnabled() + } + + fun refreshVisibility(shouldUseSplitNotificationShade: Boolean) { + if (shouldUseSplitNotificationShade) { + showBrightnessSlider() + } else { + hideBrightnessSlider() + } + } + + private fun hideBrightnessSlider() { + brightnessController?.hideSlider() + } + + private fun showBrightnessSlider() { + if (brightnessController == null) { + brightnessController = brightnessControllerFactory() + } + brightnessController?.showSlider() + if (!isListening) { + brightnessController?.registerCallbacks() + isListening = true + } + } +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 4cd4048f72868..aed085d469862 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -47,11 +47,6 @@ public class QuickQSPanel extends QSPanel { getResources().getInteger(R.integer.quick_qs_panel_max_columns)); } - @Override - public void setBrightnessView(View view) { - // Don't add brightness view - } - @Override void initialize() { super.initialize(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java index fee56b984eccb..fc62a977f061c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -49,16 +49,21 @@ public class QuickQSPanelController extends QSPanelControllerBase } }; + // brightness is visible only in split shade + private final QuickQSBrightnessController mBrightnessController; + @Inject QuickQSPanelController(QuickQSPanel view, QSTileHost qsTileHost, QSCustomizerController qsCustomizerController, @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer, @Named(QUICK_QS_PANEL) MediaHost mediaHost, MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger, - DumpManager dumpManager, FeatureFlags featureFlags + DumpManager dumpManager, FeatureFlags featureFlags, + QuickQSBrightnessController quickQSBrightnessController ) { super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags); + mBrightnessController = quickQSBrightnessController; } @Override @@ -67,6 +72,7 @@ public class QuickQSPanelController extends QSPanelControllerBase mMediaHost.setExpansion(0.0f); mMediaHost.setShowsOnlyActiveMedia(true); mMediaHost.init(MediaHierarchyManager.LOCATION_QQS); + mBrightnessController.init(mShouldUseSplitNotificationShade); } @Override @@ -81,6 +87,12 @@ public class QuickQSPanelController extends QSPanelControllerBase mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener); } + @Override + void setListening(boolean listening) { + super.setListening(listening); + mBrightnessController.setListening(listening); + } + public boolean isListening() { return mView.isListening(); } @@ -90,6 +102,17 @@ public class QuickQSPanelController extends QSPanelControllerBase setTiles(); } + @Override + public void refreshAllTiles() { + mBrightnessController.checkRestrictionAndSetEnabled(); + super.refreshAllTiles(); + } + + @Override + protected void onScreenRotated() { + mBrightnessController.refreshVisibility(mShouldUseSplitNotificationShade); + } + @Override public void setTiles() { List tiles = new ArrayList<>(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index fcf1302b8fb41..5f6a19dbbaf9c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -59,7 +59,7 @@ class QuickStatusBarHeaderController extends ViewController imple return mView.getValue(); } + @Override + public void hideView() { + mView.setVisibility(View.GONE); + } + + @Override + public void showView() { + mView.setVisibility(View.VISIBLE); + } + private final SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBarChangeListener() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java index a988c7aeb436e..5de22d43a21b8 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java @@ -35,4 +35,7 @@ public interface ToggleSlider { int getMax(); void setValue(int value); int getValue(); + + void showView(); + void hideView(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java index 4e632c7180915..4fab22676eaea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java @@ -89,8 +89,8 @@ public interface StatusBarComponent { AuthRippleController getAuthRippleController(); /** - * Creates a SplitShadeStatusBarController. + * Creates a SplitShadeHeaderController. */ @StatusBarScope - SplitShadeHeaderController getSplitShadeStatusBarController(); + SplitShadeHeaderController getSplitShadeHeaderController(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt new file mode 100644 index 0000000000000..f8373ffca955a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt @@ -0,0 +1,107 @@ +/* + * 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.qs + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.settings.brightness.BrightnessController +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.times +import org.mockito.Mockito.never +import org.mockito.junit.MockitoJUnit + +@SmallTest +class QuickQSBrightnessControllerTest : SysuiTestCase() { + + @Mock + lateinit var brightnessController: BrightnessController + @get:Rule + val mockito = MockitoJUnit.rule() + + lateinit var quickQSBrightnessController: QuickQSBrightnessController + + @Before + fun setUp() { + quickQSBrightnessController = QuickQSBrightnessController( + brightnessControllerFactory = { brightnessController }) + } + + @Test + fun testSliderIsShownWhenInitializedInSplitShade() { + quickQSBrightnessController.init(shouldUseSplitNotificationShade = true) + + verify(brightnessController).showSlider() + } + + @Test + fun testSliderIsShownWhenRefreshedInSplitShade() { + quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true) + + verify(brightnessController, times(1)).showSlider() + } + + @Test + fun testSliderIsHiddenWhenRefreshedInNonSplitShade() { + // needs to be shown first + quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true) + quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false) + + verify(brightnessController).hideSlider() + } + + @Test + fun testSliderChangesVisibilityWhenRotating() { + quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true) + verify(brightnessController, times(1)).showSlider() + + quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false) + verify(brightnessController, times(1)).hideSlider() + } + + @Test + fun testCallbacksAreRegisteredOnlyOnce() { + // this flow simulates expanding shade in portrait... + quickQSBrightnessController.setListening(true) + quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false) + // ... and rotating to landscape/split shade where slider is visible + quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true) + + verify(brightnessController, times(1)).registerCallbacks() + } + + @Test + fun testCallbacksAreRegisteredOnlyOnceWhenRotatingPhone() { + quickQSBrightnessController.setListening(true) + quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true) + quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false) + quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true) + + verify(brightnessController, times(1)).registerCallbacks() + } + + @Test + fun testCallbacksAreNotRegisteredWhenSliderNotVisible() { + quickQSBrightnessController.setListening(true) + quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false) + + verify(brightnessController, never()).registerCallbacks() + } +} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt index 0604e1b42c9da..6e1519a2dd435 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt @@ -66,6 +66,8 @@ class QuickQSPanelControllerTest : SysuiTestCase() { private lateinit var tileView: QSTileView @Mock private lateinit var featureFlags: FeatureFlags + @Mock + private lateinit var quickQsBrightnessController: QuickQSBrightnessController private lateinit var controller: QuickQSPanelController @@ -87,7 +89,8 @@ class QuickQSPanelControllerTest : SysuiTestCase() { uiEventLogger, qsLogger, dumpManager, - featureFlags + featureFlags, + quickQsBrightnessController ) controller.init()