From c45d61cfdfebab0065374afec539572a5e9326a4 Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Tue, 27 Oct 2020 13:09:22 -0400 Subject: [PATCH] Refactor ToggleSliderView ToggleSliderView logic is refactored into a separate controller. A factory that creates the controller inflates the correct view so the behavior is transparent to consumers. The new prototype for brightness slider is here. Allows to switch between regular brightness slider and thick one: `adb shell settings put secure sysui_thick_brightness X` where X is one of: 0 - regular brightness slider 1 - thick brightness slider with mirror 2 - thick brightness slider without mirror After changing restart device or sysui: `adb shell am crash com.android.systemui` Test: manual Test: atest SystemUITest Change-Id: Ica7b31d6880c3be831d3f99115945b330ba3d618 --- packages/SystemUI/AndroidManifest.xml | 2 +- .../brightness_progress_drawable_thick.xml | 36 ++ .../brightness_progress_full_drawable.xml | 24 ++ .../SystemUI/res/drawable/ic_brightness.xml | 29 ++ .../SystemUI/res/layout/brightness_mirror.xml | 9 +- .../quick_settings_brightness_dialog.xml | 52 ++- ...quick_settings_brightness_dialog_thick.xml | 53 +++ .../res/layout/status_bar_toggle_slider.xml | 60 ---- .../res/values-h740dp-port/dimens.xml | 4 +- packages/SystemUI/res/values-land/dimens.xml | 2 - packages/SystemUI/res/values/dimens.xml | 6 +- .../dagger/DefaultActivityBinder.java | 2 +- .../src/com/android/systemui/qs/QSPanel.java | 55 +-- .../systemui/qs/QSPanelController.java | 41 ++- .../com/android/systemui/qs/QuickQSPanel.java | 4 +- .../systemui/settings/ToggleSliderView.java | 207 ------------ .../BrightnessController.java | 20 +- .../BrightnessControllerSettings.java | 51 +++ .../{ => brightness}/BrightnessDialog.java | 30 +- .../settings/brightness/BrightnessSlider.java | 282 ++++++++++++++++ .../brightness/BrightnessSliderView.java | 206 ++++++++++++ .../{ => brightness}/ToggleSeekBar.java | 4 +- .../{ => brightness}/ToggleSlider.java | 19 +- .../systemui/statusbar/phone/StatusBar.java | 7 +- .../phone/dagger/StatusBarPhoneModule.java | 7 +- .../policy/BrightnessMirrorController.java | 42 ++- .../systemui/qs/QSPanelControllerTest.java | 14 +- .../brightness/BrightnessSliderTest.kt | 316 ++++++++++++++++++ .../statusbar/phone/StatusBarTest.java | 5 +- 29 files changed, 1214 insertions(+), 375 deletions(-) create mode 100644 packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml create mode 100644 packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml create mode 100644 packages/SystemUI/res/drawable/ic_brightness.xml create mode 100644 packages/SystemUI/res/layout/quick_settings_brightness_dialog_thick.xml delete mode 100644 packages/SystemUI/res/layout/status_bar_toggle_slider.xml delete mode 100644 packages/SystemUI/src/com/android/systemui/settings/ToggleSliderView.java rename packages/SystemUI/src/com/android/systemui/settings/{ => brightness}/BrightnessController.java (97%) create mode 100644 packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java rename packages/SystemUI/src/com/android/systemui/settings/{ => brightness}/BrightnessDialog.java (70%) create mode 100644 packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java create mode 100644 packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java rename packages/SystemUI/src/com/android/systemui/settings/{ => brightness}/ToggleSeekBar.java (95%) rename packages/SystemUI/src/com/android/systemui/settings/{ => brightness}/ToggleSlider.java (60%) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 39a6ed12c02d8..3de0fbdf8fb10 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -669,7 +669,7 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml new file mode 100644 index 0000000000000..a38b8b4e62dbb --- /dev/null +++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_brightness.xml b/packages/SystemUI/res/drawable/ic_brightness.xml new file mode 100644 index 0000000000000..f44333236a12d --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_brightness.xml @@ -0,0 +1,29 @@ + + + + + + + diff --git a/packages/SystemUI/res/layout/brightness_mirror.xml b/packages/SystemUI/res/layout/brightness_mirror.xml index e3440b53d2151..8b47ab915fb41 100644 --- a/packages/SystemUI/res/layout/brightness_mirror.xml +++ b/packages/SystemUI/res/layout/brightness_mirror.xml @@ -21,12 +21,5 @@ android:layout_height="@dimen/brightness_mirror_height" android:layout_gravity="@integer/notification_panel_layout_gravity" android:visibility="invisible"> - - - + diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml index 12127f5290546..11814a1caffae 100644 --- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml +++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml @@ -20,7 +20,7 @@ android:layout_gravity="center_vertical" style="@style/BrightnessDialogContainer"> - + systemui:text="@string/status_bar_settings_auto_brightness_label" > + + + + + + + + + diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog_thick.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog_thick.xml new file mode 100644 index 0000000000000..e08b44af10681 --- /dev/null +++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog_thick.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + diff --git a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml deleted file mode 100644 index 942f3dd03a8d4..0000000000000 --- a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - diff --git a/packages/SystemUI/res/values-h740dp-port/dimens.xml b/packages/SystemUI/res/values-h740dp-port/dimens.xml index 966066ffe56b7..4a23ee637e2db 100644 --- a/packages/SystemUI/res/values-h740dp-port/dimens.xml +++ b/packages/SystemUI/res/values-h740dp-port/dimens.xml @@ -19,9 +19,9 @@ 24dp - 46dp + 54dp 18dp \ No newline at end of file diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index e45f4eb3f2cae..51d7b8eff5fc3 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -22,8 +22,6 @@ 2dp 16dp - 40dp - 8dp 0dp diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index a2e9f39feefdc..b2092186c2b2d 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -427,7 +427,7 @@ @dimen/match_parent - 48dp + 56dp @dimen/notification_panel_width @@ -493,10 +493,10 @@ 12dp -12dp - 28dp + 36dp 0dp -1dp 44dp diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index d13e194a0d447..13ff3f5c4e6cd 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -21,7 +21,7 @@ import android.app.Activity; import com.android.systemui.ForegroundServicesDialog; import com.android.systemui.keyguard.WorkLockActivity; import com.android.systemui.screenrecord.ScreenRecordDialog; -import com.android.systemui.settings.BrightnessDialog; +import com.android.systemui.settings.brightness.BrightnessDialog; import com.android.systemui.tuner.TunerActivity; import com.android.systemui.usb.UsbDebuggingActivity; import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 758e0c566e5d6..08f38a83a4114 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -47,9 +47,8 @@ import com.android.systemui.media.MediaHost; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.logging.QSLogger; -import com.android.systemui.settings.ToggleSliderView; +import com.android.systemui.settings.brightness.BrightnessSlider; import com.android.systemui.statusbar.policy.BrightnessMirrorController; -import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.util.animation.DisappearParameters; @@ -62,7 +61,7 @@ import javax.inject.Inject; import javax.inject.Named; /** View that represents the quick settings tile panel (when expanded/pulled down). **/ -public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorListener { +public class QSPanel extends LinearLayout implements Tunable { public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness"; public static final String QS_SHOW_HEADER = "qs_show_header"; @@ -75,10 +74,12 @@ public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorLi /** * The index where the content starts that needs to be moved between parents */ - private final int mMovableContentStartIndex; + private int mMovableContentStartIndex; @Nullable protected View mBrightnessView; + @Nullable + protected BrightnessSlider mToggleSliderController; private final H mHandler = new H(); private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); @@ -153,7 +154,6 @@ public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorLi setOrientation(VERTICAL); - addViewsAboveTiles(); mMovableContentStartIndex = getChildCount(); mRegularTileLayout = createRegularTileLayout(); mTileLayout = mRegularTileLayout; @@ -191,10 +191,19 @@ public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorLi } } - protected void addViewsAboveTiles() { - mBrightnessView = LayoutInflater.from(mContext).inflate( - R.layout.quick_settings_brightness_dialog, this, false); - addView(mBrightnessView); + /** + * Add brightness view above the tile layout. + * + * Used to add the brightness slider after construction. + */ + public void setBrightnessView(@NonNull View view) { + if (mBrightnessView != null) { + removeView(mBrightnessView); + mMovableContentStartIndex--; + } + addView(view, 0); + mBrightnessView = view; + mMovableContentStartIndex++; } /** */ @@ -321,22 +330,6 @@ public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorLi } } - public void setBrightnessMirror(BrightnessMirrorController c) { - if (mBrightnessMirrorController != null) { - mBrightnessMirrorController.removeCallback(this); - } - mBrightnessMirrorController = c; - if (mBrightnessMirrorController != null) { - mBrightnessMirrorController.addCallback(this); - } - updateBrightnessMirror(); - } - - @Override - public void onBrightnessMirrorReinflated(View brightnessMirror) { - updateBrightnessMirror(); - } - @Nullable View getBrightnessView() { return mBrightnessView; @@ -415,8 +408,6 @@ public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorLi super.onConfigurationChanged(newConfig); mOnConfigurationChangedListeners.forEach( listener -> listener.onConfigurationChange(newConfig)); - - updateBrightnessMirror(); } @Override @@ -578,16 +569,6 @@ public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorLi } } - public void updateBrightnessMirror() { - if (mBrightnessMirrorController != null) { - ToggleSliderView brightnessSlider = findViewById(R.id.brightness_slider); - ToggleSliderView mirrorSlider = mBrightnessMirrorController.getMirror() - .findViewById(R.id.brightness_slider); - brightnessSlider.setMirror(mirrorSlider); - brightnessSlider.setMirrorController(mBrightnessMirrorController); - } - } - public void setExpanded(boolean expanded) { if (mExpanded == expanded) return; mQSLogger.logPanelExpanded(expanded, getDumpableTag()); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java index eef7e39c992d5..addaf7e456b84 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -26,14 +26,14 @@ import android.view.ViewGroup; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; -import com.android.systemui.R; import com.android.systemui.dump.DumpManager; import com.android.systemui.media.MediaHost; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.customize.QSCustomizerController; import com.android.systemui.qs.dagger.QSScope; -import com.android.systemui.settings.BrightnessController; +import com.android.systemui.settings.brightness.BrightnessController; +import com.android.systemui.settings.brightness.BrightnessSlider; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.tuner.TunerService; @@ -50,6 +50,8 @@ public class QSPanelController extends QSPanelControllerBase { private final TunerService mTunerService; private final QSCustomizerController mQsCustomizerController; private final BrightnessController mBrightnessController; + private final BrightnessSlider.Factory mBrightnessSliderFactory; + private final BrightnessSlider mBrightnessSlider; private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener = new QSPanel.OnConfigurationChangedListener() { @@ -60,29 +62,39 @@ public class QSPanelController extends QSPanelControllerBase { if (mView.isListening()) { refreshAllTiles(); } + updateBrightnessMirror(); } }; private BrightnessMirrorController mBrightnessMirrorController; + private final BrightnessMirrorController.BrightnessMirrorListener mBrightnessMirrorListener = + mirror -> updateBrightnessMirror(); + @Inject QSPanelController(QSPanel view, QSSecurityFooter qsSecurityFooter, TunerService tunerService, QSTileHost qstileHost, QSCustomizerController qsCustomizerController, QSTileRevealController.Factory qsTileRevealControllerFactory, DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger, - BrightnessController.Factory brightnessControllerFactory) { + BrightnessController.Factory brightnessControllerFactory, + BrightnessSlider.Factory brightnessSliderFactory) { super(view, qstileHost, qsCustomizerController, qsTileRevealControllerFactory, metricsLogger, uiEventLogger, dumpManager); mQsSecurityFooter = qsSecurityFooter; mTunerService = tunerService; mQsCustomizerController = qsCustomizerController; mQsSecurityFooter.setHostEnvironment(qstileHost); - mBrightnessController = brightnessControllerFactory.create( - mView.findViewById(R.id.brightness_slider)); + mBrightnessSliderFactory = brightnessSliderFactory; + + mBrightnessSlider = mBrightnessSliderFactory.create(getContext(), mView); + mView.setBrightnessView(mBrightnessSlider.getRootView()); + + mBrightnessController = brightnessControllerFactory.create(mBrightnessSlider); } @Override public void onInit() { mQsCustomizerController.init(); + mBrightnessSlider.init(); } @Override @@ -97,7 +109,7 @@ public class QSPanelController extends QSPanelControllerBase { mView.setSecurityFooter(mQsSecurityFooter.getView()); switchTileLayout(true); if (mBrightnessMirrorController != null) { - mBrightnessMirrorController.addCallback(mView); + mBrightnessMirrorController.addCallback(mBrightnessMirrorListener); } } @@ -106,7 +118,7 @@ public class QSPanelController extends QSPanelControllerBase { mTunerService.removeTunable(mView); mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener); if (mBrightnessMirrorController != null) { - mBrightnessMirrorController.removeCallback(mView); + mBrightnessMirrorController.removeCallback(mBrightnessMirrorListener); } super.onViewDetached(); } @@ -154,7 +166,20 @@ public class QSPanelController extends QSPanelControllerBase { /** */ public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) { mBrightnessMirrorController = brightnessMirrorController; - mView.setBrightnessMirror(brightnessMirrorController); + if (mBrightnessMirrorController != null) { + mBrightnessMirrorController.removeCallback(mBrightnessMirrorListener); + } + mBrightnessMirrorController = brightnessMirrorController; + if (mBrightnessMirrorController != null) { + mBrightnessMirrorController.addCallback(mBrightnessMirrorListener); + } + updateBrightnessMirror(); + } + + private void updateBrightnessMirror() { + if (mBrightnessMirrorController != null) { + mBrightnessSlider.setMirrorControllerAndMirror(mBrightnessMirrorController); + } } /** Get the QSTileHost this panel uses. */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 9c421b0e4f6e5..dde9a1fdf01bf 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -75,8 +75,8 @@ public class QuickQSPanel extends QSPanel { } @Override - protected void addViewsAboveTiles() { - // Nothing to add above the tiles + public void setBrightnessView(View view) { + // Don't add brightness view } @Override diff --git a/packages/SystemUI/src/com/android/systemui/settings/ToggleSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/ToggleSliderView.java deleted file mode 100644 index 90744a62677dd..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/settings/ToggleSliderView.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2013 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.settings; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.RelativeLayout; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; - -import com.android.settingslib.RestrictedLockUtils; -import com.android.systemui.R; -import com.android.systemui.statusbar.policy.BrightnessMirrorController; - -public class ToggleSliderView extends RelativeLayout implements ToggleSlider { - private Listener mListener; - private boolean mTracking; - - private CompoundButton mToggle; - private ToggleSeekBar mSlider; - private TextView mLabel; - - private ToggleSliderView mMirror; - private BrightnessMirrorController mMirrorController; - - public ToggleSliderView(Context context) { - this(context, null); - } - - public ToggleSliderView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ToggleSliderView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - View.inflate(context, R.layout.status_bar_toggle_slider, this); - - final Resources res = context.getResources(); - final TypedArray a = context.obtainStyledAttributes( - attrs, R.styleable.ToggleSliderView, defStyle, 0); - - mToggle = findViewById(R.id.toggle); - mToggle.setOnCheckedChangeListener(mCheckListener); - - mSlider = findViewById(R.id.slider); - mSlider.setOnSeekBarChangeListener(mSeekListener); - - mLabel = findViewById(R.id.label); - mLabel.setText(a.getString(R.styleable.ToggleSliderView_text)); - - mSlider.setAccessibilityLabel(getContentDescription().toString()); - - a.recycle(); - } - - public void setMirror(ToggleSliderView toggleSlider) { - mMirror = toggleSlider; - if (mMirror != null) { - mMirror.setChecked(mToggle.isChecked()); - mMirror.setMax(mSlider.getMax()); - mMirror.setValue(mSlider.getProgress()); - } - } - - public void setMirrorController(BrightnessMirrorController c) { - mMirrorController = c; - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (mListener != null) { - mListener.onInit(this); - } - } - - public void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin) { - mToggle.setEnabled(admin == null); - mSlider.setEnabled(admin == null); - mSlider.setEnforcedAdmin(admin); - } - - public void setOnChangedListener(Listener l) { - mListener = l; - } - - @Override - public void setChecked(boolean checked) { - mToggle.setChecked(checked); - } - - @Override - public boolean isChecked() { - return mToggle.isChecked(); - } - - @Override - public void setMax(int max) { - mSlider.setMax(max); - if (mMirror != null) { - mMirror.setMax(max); - } - } - - @Override - public void setValue(int value) { - mSlider.setProgress(value); - if (mMirror != null) { - mMirror.setValue(value); - } - } - - @Override - public int getValue() { - return mSlider.getProgress(); - } - - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - if (mMirror != null) { - MotionEvent copy = ev.copy(); - mMirror.dispatchTouchEvent(copy); - copy.recycle(); - } - return super.dispatchTouchEvent(ev); - } - - private final OnCheckedChangeListener mCheckListener = new OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton toggle, boolean checked) { - mSlider.setEnabled(!checked); - - if (mListener != null) { - mListener.onChanged( - ToggleSliderView.this, mTracking, checked, mSlider.getProgress(), false); - } - - if (mMirror != null) { - mMirror.mToggle.setChecked(checked); - } - } - }; - - private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (mListener != null) { - mListener.onChanged( - ToggleSliderView.this, mTracking, mToggle.isChecked(), progress, false); - } - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - mTracking = true; - - if (mListener != null) { - mListener.onChanged(ToggleSliderView.this, mTracking, mToggle.isChecked(), - mSlider.getProgress(), false); - } - - mToggle.setChecked(false); - - if (mMirrorController != null) { - mMirrorController.showMirror(); - mMirrorController.setLocation((View) getParent()); - } - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - mTracking = false; - - if (mListener != null) { - mListener.onChanged(ToggleSliderView.this, mTracking, mToggle.isChecked(), - mSlider.getProgress(), true); - } - - if (mMirrorController != null) { - mMirrorController.hideMirror(); - } - } - }; -} - diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java similarity index 97% rename from packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java rename to packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java index 72034f84fd309..be10c26df5bf6 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.settings; +package com.android.systemui.settings.brightness; import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX; import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinearFloat; @@ -47,6 +47,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.systemui.Dependency; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.settings.CurrentUserTracker; import java.util.ArrayList; @@ -100,13 +101,14 @@ public class BrightnessController implements ToggleSlider.Listener { private ValueAnimator mSliderAnimator; public interface BrightnessStateChangeCallback { - public void onBrightnessLevelChanged(); + /** Indicates that some of the brightness settings have changed */ + void onBrightnessLevelChanged(); } /** ContentObserver to watch brightness */ private class BrightnessObserver extends ContentObserver { - public BrightnessObserver(Handler handler) { + BrightnessObserver(Handler handler) { super(handler); } @@ -339,11 +341,6 @@ public class BrightnessController implements ToggleSlider.Listener { return mChangeCallbacks.remove(cb); } - @Override - public void onInit(ToggleSlider control) { - // Do nothing - } - public void registerCallbacks() { mBackgroundHandler.post(mStartListeningRunnable); } @@ -355,7 +352,7 @@ public class BrightnessController implements ToggleSlider.Listener { } @Override - public void onChanged(ToggleSlider toggleSlider, boolean tracking, boolean automatic, + public void onChanged(boolean tracking, boolean automatic, int value, boolean stopTracking) { if (mExternalChange) return; @@ -409,7 +406,7 @@ public class BrightnessController implements ToggleSlider.Listener { mBackgroundHandler.post(new Runnable() { @Override public void run() { - ((ToggleSliderView)mControl).setEnforcedAdmin( + mControl.setEnforcedAdmin( RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext, UserManager.DISALLOW_CONFIG_BRIGHTNESS, mUserTracker.getCurrentUserId())); @@ -493,4 +490,5 @@ public class BrightnessController implements ToggleSlider.Listener { return new BrightnessController(mContext, toggleSlider, mBroadcastDispatcher); } } + } diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java new file mode 100644 index 0000000000000..a151fd0b48ff0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 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.settings.brightness; + +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.util.settings.SecureSettings; + +import javax.inject.Inject; + +/** + * Settings for prototyping thick brightness slider + */ +@SysUISingleton +public class BrightnessControllerSettings { + + private static final String THICK_BRIGHTNESS_SLIDER = "sysui_thick_brighthness"; + private final boolean mUseThickSlider; + private final boolean mUseMirrorOnThickSlider; + + @Inject + public BrightnessControllerSettings(SecureSettings settings) { + mUseThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 0; + mUseMirrorOnThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 2; + } + + // Changing this setting between zero and non-zero may crash systemui down the line. Better to + // restart systemui after changing it. + /** */ + boolean useThickSlider() { + return mUseThickSlider; + } + + /** */ + boolean useMirrorOnThickSlider() { + return mUseMirrorOnThickSlider; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java similarity index 70% rename from packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java rename to packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java index 70c2531ca6d41..1961bfc9ee27c 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,20 +14,17 @@ * limitations under the License. */ -package com.android.systemui.settings; +package com.android.systemui.settings.brightness; import android.app.Activity; import android.os.Bundle; import android.view.Gravity; import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; import android.view.Window; import android.view.WindowManager; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import javax.inject.Inject; @@ -36,11 +33,15 @@ import javax.inject.Inject; public class BrightnessDialog extends Activity { private BrightnessController mBrightnessController; + private final BrightnessSlider.Factory mToggleSliderFactory; private final BroadcastDispatcher mBroadcastDispatcher; @Inject - public BrightnessDialog(BroadcastDispatcher broadcastDispatcher) { + public BrightnessDialog( + BroadcastDispatcher broadcastDispatcher, + BrightnessSlider.Factory factory) { mBroadcastDispatcher = broadcastDispatcher; + mToggleSliderFactory = factory; } @@ -50,16 +51,21 @@ public class BrightnessDialog extends Activity { final Window window = getWindow(); - window.setGravity(Gravity.TOP); + window.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL); window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); window.requestFeature(Window.FEATURE_NO_TITLE); - View v = LayoutInflater.from(this).inflate( - R.layout.quick_settings_brightness_dialog, null); - setContentView(v); + // Calling this creates the decor View, so setLayout takes proper effect + // (see Dialog#onWindowAttributesChanged) + window.getDecorView(); + window.setLayout( + WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT); - final ToggleSliderView slider = findViewById(R.id.brightness_slider); - mBrightnessController = new BrightnessController(this, slider, mBroadcastDispatcher); + + BrightnessSlider controller = mToggleSliderFactory.create(this, null); + controller.init(); + setContentView(controller.getRootView()); + mBrightnessController = new BrightnessController(this, controller, mBroadcastDispatcher); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java new file mode 100644 index 0000000000000..6c4f8083788a0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2020 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.settings.brightness; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.SeekBar; + +import androidx.annotation.Nullable; + +import com.android.settingslib.RestrictedLockUtils; +import com.android.systemui.R; +import com.android.systemui.statusbar.policy.BrightnessMirrorController; +import com.android.systemui.util.ViewController; + +import javax.inject.Inject; + +/** + * {@code ViewController} for a {@code BrightnessSliderView} + * + * This class allows to control the views of a {@code BrightnessSliderViewView} and get callbacks + * when the views change value. It also propagates and manipulates another {@link ToggleSlider} as a + * mirror. + * + * @see BrightnessMirrorController + */ +public class BrightnessSlider + extends ViewController + implements ToggleSlider { + + private Listener mListener; + private ToggleSlider mMirror; + private final BrightnessSliderView mBrightnessSliderView; + private BrightnessMirrorController mMirrorController; + private boolean mTracking; + private final boolean mUseMirror; + + BrightnessSlider( + View rootView, + BrightnessSliderView brightnessSliderView, + boolean useMirror + ) { + super(rootView); + mBrightnessSliderView = brightnessSliderView; + mUseMirror = useMirror; + } + + /** + * Returns a top level view in the hierarchy that can be attached where necessary + */ + public View getRootView() { + return mView; + } + + private void enableSlider(boolean enable) { + mBrightnessSliderView.enableSlider(enable); + } + + @Override + protected void onViewAttached() { + mBrightnessSliderView.setOnSeekBarChangeListener(mSeekListener); + mBrightnessSliderView.setOnCheckedChangeListener(mCheckListener); + } + + @Override + protected void onViewDetached() { + mBrightnessSliderView.setOnSeekBarChangeListener(null); + mBrightnessSliderView.setOnCheckedChangeListener(null); + mBrightnessSliderView.setOnDispatchTouchEventListener(null); + } + + @Override + public boolean mirrorTouchEvent(MotionEvent ev) { + if (mMirror != null) { + return copyEventToMirror(ev); + } else { + // We are the mirror, so we have to dispatch the event + return mBrightnessSliderView.dispatchTouchEvent(ev); + } + } + + private boolean copyEventToMirror(MotionEvent ev) { + MotionEvent copy = ev.copy(); + boolean out = mMirror.mirrorTouchEvent(copy); + copy.recycle(); + return out; + } + + @Override + public void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin) { + mBrightnessSliderView.setEnforcedAdmin(admin); + } + + private void setMirror(ToggleSlider toggleSlider) { + mMirror = toggleSlider; + if (mMirror != null) { + mMirror.setChecked(mBrightnessSliderView.isChecked()); + mMirror.setMax(mBrightnessSliderView.getMax()); + mMirror.setValue(mBrightnessSliderView.getValue()); + mBrightnessSliderView.setOnDispatchTouchEventListener(this::mirrorTouchEvent); + } else { + // If there's no mirror, we may be the ones dispatching, events but we should not mirror + // them + mBrightnessSliderView.setOnDispatchTouchEventListener(null); + } + } + + /** + * This will set the mirror from the controller using + * {@link BrightnessMirrorController#getToggleSlider} as a side-effect. + * @param c + */ + @Override + public void setMirrorControllerAndMirror(BrightnessMirrorController c) { + if (!mUseMirror) return; + mMirrorController = c; + if (c != null) { + setMirror(c.getToggleSlider()); + } else { + // If there's no mirror, we may be the ones dispatching, events but we should not mirror + // them + mBrightnessSliderView.setOnDispatchTouchEventListener(null); + } + } + + @Override + public void setOnChangedListener(Listener l) { + mListener = l; + } + + @Override + public void setChecked(boolean checked) { + mBrightnessSliderView.setChecked(checked); + } + + @Override + public boolean isChecked() { + return mBrightnessSliderView.isChecked(); + } + + @Override + public void setMax(int max) { + mBrightnessSliderView.setMax(max); + if (mMirror != null) { + mMirror.setMax(max); + } + } + + @Override + public int getMax() { + return mBrightnessSliderView.getMax(); + } + + @Override + public void setValue(int value) { + mBrightnessSliderView.setValue(value); + if (mMirror != null) { + mMirror.setValue(value); + } + } + + @Override + public int getValue() { + return mBrightnessSliderView.getValue(); + } + + private final SeekBar.OnSeekBarChangeListener mSeekListener = + new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (mListener != null) { + mListener.onChanged(mTracking, isChecked(), progress, false); + } + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + mTracking = true; + + if (mListener != null) { + mListener.onChanged(mTracking, isChecked(), + getValue(), false); + } + + setChecked(false); + + if (mMirrorController != null) { + mMirrorController.showMirror(); + mMirrorController.setLocation((View) mBrightnessSliderView.getParent()); + } + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + mTracking = false; + + if (mListener != null) { + mListener.onChanged(mTracking, isChecked(), + getValue(), true); + } + + if (mMirrorController != null) { + mMirrorController.hideMirror(); + } + } + }; + + private final CompoundButton.OnCheckedChangeListener mCheckListener = + new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton toggle, boolean checked) { + enableSlider(!checked); + + if (mListener != null) { + mListener.onChanged(mTracking, checked, getValue(), false); + } + + if (mMirror != null) { + mMirror.setChecked(checked); + } + } + }; + + /** + * Creates a {@link BrightnessSlider} with its associated view. + * + * The views inflated are determined by {@link BrightnessControllerSettings#useThickSlider()}. + */ + public static class Factory { + + BrightnessControllerSettings mSettings; + + @Inject + public Factory(BrightnessControllerSettings settings) { + mSettings = settings; + } + + /** + * Creates the view hierarchy and controller + * + * @param context a {@link Context} to inflate the hierarchy + * @param viewRoot the {@link ViewGroup} that will contain the hierarchy. The inflated + * hierarchy will not be attached + */ + public BrightnessSlider create(Context context, @Nullable ViewGroup viewRoot) { + int layout = getLayout(); + ViewGroup root = (ViewGroup) LayoutInflater.from(context) + .inflate(layout, viewRoot, false); + return fromTree(root, mSettings.useMirrorOnThickSlider()); + } + + private BrightnessSlider fromTree(ViewGroup root, boolean useMirror) { + BrightnessSliderView v = root.requireViewById(R.id.brightness_slider); + return new BrightnessSlider(root, v, useMirror); + } + + /** Get the layout to inflate based on what slider to use */ + public int getLayout() { + return mSettings.useThickSlider() + ? R.layout.quick_settings_brightness_dialog_thick + : R.layout.quick_settings_brightness_dialog; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java new file mode 100644 index 0000000000000..cbf4e88b3241e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2020 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.settings.brightness; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.FrameLayout; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.settingslib.RestrictedLockUtils; +import com.android.systemui.R; + +/** + * {@code FrameLayout} used to show and manipulate a {@link ToggleSeekBar}. + * + * It can additionally control a {@link CompoundButton} and display a label. For the class to work, + * add children before inflation with the following ids: + *
    + *
  • {@code @id/slider} of type {@link ToggleSeekBar}
  • + *
  • {@code @id/toggle} of type {@link CompoundButton} (optional)
  • + *
  • {@code @id/label} of type {@link TextView} (optional)
  • + *
+ */ +public class BrightnessSliderView extends FrameLayout { + + @Nullable + private CompoundButton mToggle; + @NonNull + private ToggleSeekBar mSlider; + @Nullable + private TextView mLabel; + private final CharSequence mText; + private DispatchTouchEventListener mListener; + + public BrightnessSliderView(Context context) { + this(context, null); + } + + public BrightnessSliderView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BrightnessSliderView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + final TypedArray a = context.obtainStyledAttributes( + attrs, R.styleable.ToggleSliderView, defStyle, 0); + mText = a.getString(R.styleable.ToggleSliderView_text); + + a.recycle(); + } + + // Inflated from quick_settings_brightness_dialog or quick_settings_brightness_dialog_thick + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mToggle = findViewById(R.id.toggle); + + mSlider = requireViewById(R.id.slider); + + mLabel = findViewById(R.id.label); + if (mLabel != null) { + mLabel.setText(mText); + } + mSlider.setAccessibilityLabel(getContentDescription().toString()); + } + + /** + * Attaches a listener to relay touch events. + * @param listener use {@code null} to remove listener + */ + public void setOnDispatchTouchEventListener( + DispatchTouchEventListener listener) { + mListener = listener; + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (mListener != null) { + mListener.onDispatchTouchEvent(ev); + } + return super.dispatchTouchEvent(ev); + } + + /** + * Attaches a listener to the {@link ToggleSeekBar} in the view so changes can be observed + * @param seekListener use {@code null} to remove listener + */ + public void setOnSeekBarChangeListener(OnSeekBarChangeListener seekListener) { + mSlider.setOnSeekBarChangeListener(seekListener); + } + + /** + * Attaches a listener to the {@link CompoundButton} in the view (if present) so changes to its + * state can be observed + * @param checkListener use {@code null} to remove listener + */ + public void setOnCheckedChangeListener(OnCheckedChangeListener checkListener) { + if (mToggle != null) { + mToggle.setOnCheckedChangeListener(checkListener); + } + } + + /** + * Enforces admin rules for toggling auto-brightness and changing value of brightness + * @param admin + * @see ToggleSeekBar#setEnforcedAdmin + */ + public void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin) { + if (mToggle != null) { + mToggle.setEnabled(admin == null); + } + mSlider.setEnabled(admin == null); + mSlider.setEnforcedAdmin(admin); + } + + /** + * Enables or disables the slider + * @param enable + */ + public void enableSlider(boolean enable) { + mSlider.setEnabled(enable); + } + + /** + * Sets the state of the {@link CompoundButton} if present + * @param checked + */ + public void setChecked(boolean checked) { + if (mToggle != null) { + mToggle.setChecked(checked); + } + } + + /** + * @return the state of the {@link CompoundButton} if present, or {@code true} if not. + */ + public boolean isChecked() { + if (mToggle != null) { + return mToggle.isChecked(); + } + return true; + } + + /** + * @return the maximum value of the {@link ToggleSeekBar}. + */ + public int getMax() { + return mSlider.getMax(); + } + + /** + * Sets the maximum value of the {@link ToggleSeekBar}. + * @param max + */ + public void setMax(int max) { + mSlider.setMax(max); + } + + /** + * Sets the current value of the {@link ToggleSeekBar}. + * @param value + */ + public void setValue(int value) { + mSlider.setProgress(value); + } + + /** + * @return the current value of the {@link ToggleSeekBar} + */ + public int getValue() { + return mSlider.getProgress(); + } + + /** + * Interface to attach a listener for {@link View#dispatchTouchEvent}. + */ + @FunctionalInterface + interface DispatchTouchEventListener { + boolean onDispatchTouchEvent(MotionEvent ev); + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/settings/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java similarity index 95% rename from packages/SystemUI/src/com/android/systemui/settings/ToggleSeekBar.java rename to packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java index 8ed4c75e86737..a5a0ae70045ea 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/ToggleSeekBar.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.settings; +package com.android.systemui.settings.brightness; import android.content.Context; import android.content.Intent; diff --git a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java similarity index 60% rename from packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java rename to packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java index 135f89d3f3431..71e4818c605ac 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2020 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. @@ -11,22 +11,31 @@ * 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 + * limitations under the License. */ -package com.android.systemui.settings; +package com.android.systemui.settings.brightness; + +import android.view.MotionEvent; + +import com.android.settingslib.RestrictedLockUtils; +import com.android.systemui.statusbar.policy.BrightnessMirrorController; public interface ToggleSlider { interface Listener { - void onInit(ToggleSlider control); - void onChanged(ToggleSlider slider, boolean tracking, boolean automatic, int value, + void onChanged(boolean tracking, boolean automatic, int value, boolean stopTracking); } + void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin); + void setMirrorControllerAndMirror(BrightnessMirrorController c); + boolean mirrorTouchEvent(MotionEvent ev); + void setOnChangedListener(Listener l); default void setChecked(boolean checked) {} default boolean isChecked() { return false; } void setMax(int max); + int getMax(); void setValue(int value); int getValue(); } 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 5c7f54b4a9a93..486d9da93ed8f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -172,6 +172,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSFragment; import com.android.systemui.qs.QSPanelController; import com.android.systemui.recents.ScreenPinningRequest; +import com.android.systemui.settings.brightness.BrightnessSlider; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.statusbar.AutoHideUiElement; @@ -428,6 +429,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final NotificationViewHierarchyManager mViewHierarchyManager; private final KeyguardViewMediator mKeyguardViewMediator; protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider; + private final BrightnessSlider.Factory mBrightnessSliderFactory; // for disabling the status bar private int mDisabled1 = 0; @@ -740,7 +742,8 @@ public class StatusBar extends SystemUI implements DemoMode, DemoModeController demoModeController, Lazy notificationShadeDepthControllerLazy, StatusBarTouchableRegionManager statusBarTouchableRegionManager, - NotificationIconAreaController notificationIconAreaController) { + NotificationIconAreaController notificationIconAreaController, + BrightnessSlider.Factory brightnessSliderFactory) { super(context); mNotificationsController = notificationsController; mLightBarController = lightBarController; @@ -817,6 +820,7 @@ public class StatusBar extends SystemUI implements DemoMode, mDismissCallbackRegistry = dismissCallbackRegistry; mDemoModeController = demoModeController; mNotificationIconAreaController = notificationIconAreaController; + mBrightnessSliderFactory = brightnessSliderFactory; mBubbleExpandListener = (isExpanding, key) -> { @@ -1194,6 +1198,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationShadeWindowView, mNotificationPanelViewController, mNotificationShadeDepthControllerLazy.get(), + mBrightnessSliderFactory, (visible) -> { mBrightnessMirrorVisible = visible; updateScrimController(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index 13d5bf59fb4b6..f6631ce1414ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -43,6 +43,7 @@ import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.recents.ScreenPinningRequest; +import com.android.systemui.settings.brightness.BrightnessSlider; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -198,7 +199,8 @@ public interface StatusBarPhoneModule { Lazy notificationShadeDepthController, DismissCallbackRegistry dismissCallbackRegistry, StatusBarTouchableRegionManager statusBarTouchableRegionManager, - NotificationIconAreaController notificationIconAreaController) { + NotificationIconAreaController notificationIconAreaController, + BrightnessSlider.Factory brightnessSliderFactory) { return new StatusBar( context, notificationsController, @@ -276,6 +278,7 @@ public interface StatusBarPhoneModule { demoModeController, notificationShadeDepthController, statusBarTouchableRegionManager, - notificationIconAreaController); + notificationIconAreaController, + brightnessSliderFactory); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java index a0b03e1c54c24..07d3f0122aa00 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java @@ -17,13 +17,18 @@ package com.android.systemui.statusbar.policy; import android.annotation.NonNull; +import android.content.Context; import android.content.res.Resources; +import android.graphics.drawable.Drawable; import android.util.ArraySet; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.FrameLayout; import com.android.systemui.R; +import com.android.systemui.settings.brightness.BrightnessSlider; +import com.android.systemui.settings.brightness.ToggleSlider; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.NotificationShadeWindowView; @@ -42,15 +47,20 @@ public class BrightnessMirrorController private final NotificationPanelViewController mNotificationPanel; private final NotificationShadeDepthController mDepthController; private final ArraySet mBrightnessMirrorListeners = new ArraySet<>(); + private final BrightnessSlider.Factory mToggleSliderFactory; + private BrightnessSlider mToggleSliderController; private final int[] mInt2Cache = new int[2]; - private View mBrightnessMirror; + private FrameLayout mBrightnessMirror; public BrightnessMirrorController(NotificationShadeWindowView statusBarWindow, NotificationPanelViewController notificationPanelViewController, NotificationShadeDepthController notificationShadeDepthController, + BrightnessSlider.Factory factory, @NonNull Consumer visibilityCallback) { mStatusBarWindow = statusBarWindow; + mToggleSliderFactory = factory; mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror); + mToggleSliderController = setMirrorLayout(); mNotificationPanel = notificationPanelViewController; mDepthController = notificationShadeDepthController; mNotificationPanel.setPanelAlphaEndAction(() -> { @@ -88,8 +98,8 @@ public class BrightnessMirrorController mBrightnessMirror.setTranslationY(originalY - mirrorY); } - public View getMirror() { - return mBrightnessMirror; + public ToggleSlider getToggleSlider() { + return mToggleSliderController; } public void updateResources() { @@ -110,11 +120,33 @@ public class BrightnessMirrorController reinflate(); } + private BrightnessSlider setMirrorLayout() { + Context context = mBrightnessMirror.getContext(); + BrightnessSlider controller = mToggleSliderFactory.create(context, mBrightnessMirror); + controller.init(); + Drawable mirrorBackground = context.getDrawable(R.drawable.brightness_mirror_background); + + View rootView = controller.getRootView(); + rootView.setBackground(mirrorBackground); + + ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) rootView.getLayoutParams(); + lp.height = ViewGroup.LayoutParams.MATCH_PARENT; + lp.width = ViewGroup.LayoutParams.MATCH_PARENT; + int margin = context.getResources() + .getDimensionPixelSize(R.dimen.notification_side_paddings); + lp.leftMargin = margin; + lp.rightMargin = margin; + mBrightnessMirror.addView(rootView, lp); + + return controller; + } + private void reinflate() { int index = mStatusBarWindow.indexOfChild(mBrightnessMirror); mStatusBarWindow.removeView(mBrightnessMirror); - mBrightnessMirror = LayoutInflater.from(mBrightnessMirror.getContext()).inflate( - R.layout.brightness_mirror, mStatusBarWindow, false); + mBrightnessMirror = (FrameLayout) LayoutInflater.from(mBrightnessMirror.getContext()) + .inflate(R.layout.brightness_mirror, mStatusBarWindow, false); + mToggleSliderController = setMirrorLayout(); mStatusBarWindow.addView(mBrightnessMirror, index); for (int i = 0; i < mBrightnessMirrorListeners.size(); i++) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java index bce376a13b7b3..f57e3c2892ea6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java @@ -37,8 +37,9 @@ import com.android.systemui.media.MediaHost; import com.android.systemui.plugins.qs.QSTileView; import com.android.systemui.qs.customize.QSCustomizerController; import com.android.systemui.qs.tileimpl.QSTileImpl; -import com.android.systemui.settings.BrightnessController; -import com.android.systemui.settings.ToggleSlider; +import com.android.systemui.settings.brightness.BrightnessController; +import com.android.systemui.settings.brightness.BrightnessSlider; +import com.android.systemui.settings.brightness.ToggleSlider; import com.android.systemui.tuner.TunerService; import org.junit.Before; @@ -79,6 +80,10 @@ public class QSPanelControllerTest extends SysuiTestCase { @Mock private BrightnessController mBrightnessController; @Mock + private BrightnessSlider.Factory mToggleSliderViewControllerFactory; + @Mock + private BrightnessSlider mBrightnessSlider; + @Mock QSTileImpl mQSTile; @Mock QSTileView mQSTileView; @@ -98,13 +103,16 @@ public class QSPanelControllerTest extends SysuiTestCase { when(mQSPanel.createRegularTileLayout()).thenReturn(mPagedTileLayout); when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile)); when(mQSTileHost.createTileView(eq(mQSTile), anyBoolean())).thenReturn(mQSTileView); + when(mToggleSliderViewControllerFactory.create(any(), any())) + .thenReturn(mBrightnessSlider); when(mBrightnessControllerFactory.create(any(ToggleSlider.class))) .thenReturn(mBrightnessController); when(mQSTileRevealControllerFactory.create(any())).thenReturn(mQSTileRevealController); mController = new QSPanelController(mQSPanel, mQSSecurityFooter, mTunerService, mQSTileHost, mQSCustomizerController, mQSTileRevealControllerFactory, mDumpManager, - mMetricsLogger, mUiEventLogger, mBrightnessControllerFactory); + mMetricsLogger, mUiEventLogger, mBrightnessControllerFactory, + mToggleSliderViewControllerFactory); mController.init(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt new file mode 100644 index 0000000000000..0cc2072b367fa --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2020 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.settings.brightness + +import android.testing.AndroidTestingRunner +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import android.widget.CompoundButton +import android.widget.SeekBar +import androidx.test.filters.SmallTest +import com.android.settingslib.RestrictedLockUtils +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.policy.BrightnessMirrorController +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.eq +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.isNull +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.notNull +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class BrightnessSliderTest : SysuiTestCase() { + + @Mock + private lateinit var rootView: View + @Mock + private lateinit var brightnessSliderView: BrightnessSliderView + @Mock + private lateinit var enforcedAdmin: RestrictedLockUtils.EnforcedAdmin + @Mock + private lateinit var mirrorController: BrightnessMirrorController + @Mock + private lateinit var mirror: ToggleSlider + @Mock + private lateinit var motionEvent: MotionEvent + @Mock + private lateinit var listener: ToggleSlider.Listener + + @Captor + private lateinit var seekBarChangeCaptor: ArgumentCaptor + @Mock + private lateinit var seekBar: SeekBar + @Captor + private lateinit var checkedChangeCaptor: ArgumentCaptor + @Mock + private lateinit var compoundButton: CompoundButton + + private lateinit var mController: BrightnessSlider + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + whenever(mirrorController.toggleSlider).thenReturn(mirror) + whenever(motionEvent.copy()).thenReturn(motionEvent) + + mController = BrightnessSlider(rootView, brightnessSliderView, true) + mController.init() + mController.setOnChangedListener(listener) + } + + @After + fun tearDown() { + mController.onViewDetached() + } + + @Test + fun testListenersAddedOnAttach() { + mController.onViewAttached() + + verify(brightnessSliderView).setOnCheckedChangeListener(notNull()) + verify(brightnessSliderView).setOnSeekBarChangeListener(notNull()) + } + + @Test + fun testAllListenersRemovedOnDettach() { + mController.onViewAttached() + mController.onViewDetached() + + verify(brightnessSliderView).setOnSeekBarChangeListener(isNull()) + verify(brightnessSliderView).setOnCheckedChangeListener(isNull()) + verify(brightnessSliderView).setOnDispatchTouchEventListener(isNull()) + } + + @Test + fun testEnforceAdminRelayed() { + mController.setEnforcedAdmin(enforcedAdmin) + verify(brightnessSliderView).setEnforcedAdmin(enforcedAdmin) + } + + @Test + fun testNullMirrorControllerNotTrackingTouch() { + mController.setMirrorControllerAndMirror(null) + + verify(brightnessSliderView, never()).max + verify(brightnessSliderView, never()).value + verify(brightnessSliderView, never()).isChecked + verify(brightnessSliderView).setOnDispatchTouchEventListener(isNull()) + } + + @Test + fun testNullMirrorNotTrackingTouch() { + whenever(mirrorController.toggleSlider).thenReturn(null) + + mController.setMirrorControllerAndMirror(mirrorController) + + verify(brightnessSliderView, never()).max + verify(brightnessSliderView, never()).value + verify(brightnessSliderView, never()).isChecked + verify(brightnessSliderView).setOnDispatchTouchEventListener(isNull()) + } + + @Test + fun testSettingMirrorControllerReliesValuesAndSetsTouchTracking() { + val maxValue = 100 + val progress = 30 + val checked = true + whenever(brightnessSliderView.max).thenReturn(maxValue) + whenever(brightnessSliderView.value).thenReturn(progress) + whenever(brightnessSliderView.isChecked).thenReturn(checked) + + mController.setMirrorControllerAndMirror(mirrorController) + + verify(mirror).max = maxValue + verify(mirror).isChecked = checked + verify(mirror).value = progress + verify(brightnessSliderView).setOnDispatchTouchEventListener(notNull()) + } + + @Test + fun testSettingMirrorWhenNotUseMirrorIsNoOp() { + val otherController = BrightnessSlider(rootView, brightnessSliderView, false) + otherController.init() + + otherController.setMirrorControllerAndMirror(mirrorController) + + verify(brightnessSliderView, never()).max + verify(brightnessSliderView, never()).value + verify(brightnessSliderView, never()).isChecked + verify(brightnessSliderView, never()).setOnDispatchTouchEventListener( + any(BrightnessSliderView.DispatchTouchEventListener::class.java)) + } + + @Test + fun testSetCheckedRelayed_true() { + mController.isChecked = true + verify(brightnessSliderView).isChecked = true + } + + @Test + fun testSetCheckedRelayed_false() { + mController.isChecked = false + verify(brightnessSliderView).isChecked = false + } + + @Test + fun testGetChecked() { + whenever(brightnessSliderView.isChecked).thenReturn(true) + + assertThat(mController.isChecked).isTrue() + + whenever(brightnessSliderView.isChecked).thenReturn(false) + + assertThat(mController.isChecked).isFalse() + } + + @Test + fun testSetMaxRelayed() { + mController.max = 120 + verify(brightnessSliderView).max = 120 + } + + @Test + fun testGetMax() { + whenever(brightnessSliderView.max).thenReturn(40) + + assertThat(mController.max).isEqualTo(40) + } + + @Test + fun testSetValue() { + mController.value = 30 + verify(brightnessSliderView).value = 30 + } + + @Test + fun testGetValue() { + whenever(brightnessSliderView.value).thenReturn(20) + + assertThat(mController.value).isEqualTo(20) + } + + @Test + fun testMirrorEventWithMirror() { + mController.setMirrorControllerAndMirror(mirrorController) + + mController.mirrorTouchEvent(motionEvent) + + verify(mirror).mirrorTouchEvent(motionEvent) + verify(brightnessSliderView, never()).dispatchTouchEvent(any(MotionEvent::class.java)) + } + + @Test + fun testMirrorEventWithoutMirror_dispatchToView() { + mController.mirrorTouchEvent(motionEvent) + + verify(brightnessSliderView).dispatchTouchEvent(motionEvent) + } + + @Test + fun testSeekBarProgressChanged() { + mController.onViewAttached() + whenever(brightnessSliderView.isChecked).thenReturn(true) + + verify(brightnessSliderView).setOnSeekBarChangeListener(capture(seekBarChangeCaptor)) + + seekBarChangeCaptor.value.onProgressChanged(seekBar, 23, true) + + verify(listener).onChanged(anyBoolean(), eq(true), eq(23), eq(false)) + } + + @Test + fun testSeekBarTrackingStarted() { + val parent = mock(ViewGroup::class.java) + whenever(brightnessSliderView.value).thenReturn(42) + whenever(brightnessSliderView.parent).thenReturn(parent) + whenever(brightnessSliderView.isChecked).thenReturn(true) + + mController.onViewAttached() + mController.setMirrorControllerAndMirror(mirrorController) + verify(brightnessSliderView).setOnSeekBarChangeListener(capture(seekBarChangeCaptor)) + + seekBarChangeCaptor.value.onStartTrackingTouch(seekBar) + + verify(listener).onChanged(eq(true), eq(true), eq(42), eq(false)) + verify(mirrorController).showMirror() + verify(mirrorController).setLocation(parent) + } + + @Test + fun testSeekBarTrackingStopped() { + whenever(brightnessSliderView.value).thenReturn(23) + whenever(brightnessSliderView.isChecked).thenReturn(true) + + mController.onViewAttached() + mController.setMirrorControllerAndMirror(mirrorController) + verify(brightnessSliderView).setOnSeekBarChangeListener(capture(seekBarChangeCaptor)) + + seekBarChangeCaptor.value.onStopTrackingTouch(seekBar) + + verify(listener).onChanged(eq(false), eq(true), eq(23), eq(true)) + verify(mirrorController).hideMirror() + } + + @Test + fun testButtonCheckedChanged_false() { + val checked = false + + mController.onViewAttached() + mController.setMirrorControllerAndMirror(mirrorController) + verify(brightnessSliderView).setOnCheckedChangeListener(capture(checkedChangeCaptor)) + + checkedChangeCaptor.value.onCheckedChanged(compoundButton, checked) + + verify(brightnessSliderView).enableSlider(!checked) + verify(listener).onChanged(anyBoolean(), eq(checked), anyInt(), eq(false)) + // Called once with false when the mirror is set + verify(mirror, times(2)).isChecked = checked + } + + @Test + fun testButtonCheckedChanged_true() { + val checked = true + + mController.onViewAttached() + mController.setMirrorControllerAndMirror(mirrorController) + verify(brightnessSliderView).setOnCheckedChangeListener(capture(checkedChangeCaptor)) + + checkedChangeCaptor.value.onCheckedChanged(compoundButton, checked) + + verify(brightnessSliderView).enableSlider(!checked) + verify(listener).onChanged(anyBoolean(), eq(checked), anyInt(), eq(false)) + verify(mirror).isChecked = checked + } +} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 2f9b6010b42de..710122d83b221 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -97,6 +97,7 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.ScreenPinningRequest; +import com.android.systemui.settings.brightness.BrightnessSlider; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -260,6 +261,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private PhoneStatusBarPolicy mPhoneStatusBarPolicy; @Mock private DemoModeController mDemoModeController; @Mock private Lazy mNotificationShadeDepthControllerLazy; + @Mock private BrightnessSlider.Factory mBrightnessSliderFactory; private ShadeController mShadeController; private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private InitController mInitController = new InitController(); @@ -419,7 +421,8 @@ public class StatusBarTest extends SysuiTestCase { mDemoModeController, mNotificationShadeDepthControllerLazy, mStatusBarTouchableRegionManager, - mNotificationIconAreaController); + mNotificationIconAreaController, + mBrightnessSliderFactory); when(mNotificationShadeWindowView.findViewById(R.id.lock_icon_container)).thenReturn( mLockIconContainer);