1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.settings.brightness;
18 
19 import android.content.Context;
20 import android.graphics.Rect;
21 import android.graphics.drawable.Drawable;
22 import android.graphics.drawable.DrawableWrapper;
23 import android.graphics.drawable.LayerDrawable;
24 import android.util.AttributeSet;
25 import android.view.MotionEvent;
26 import android.view.View;
27 import android.widget.FrameLayout;
28 import android.widget.SeekBar.OnSeekBarChangeListener;
29 
30 import androidx.annotation.Keep;
31 import androidx.annotation.NonNull;
32 import androidx.annotation.Nullable;
33 
34 import com.android.settingslib.RestrictedLockUtils;
35 import com.android.systemui.Gefingerpoken;
36 import com.android.systemui.R;
37 
38 /**
39  * {@code FrameLayout} used to show and manipulate a {@link ToggleSeekBar}.
40  *
41  */
42 public class BrightnessSliderView extends FrameLayout {
43 
44     @NonNull
45     private ToggleSeekBar mSlider;
46     private DispatchTouchEventListener mListener;
47     private Gefingerpoken mOnInterceptListener;
48     @Nullable
49     private Drawable mProgressDrawable;
50     private float mScale = 1f;
51 
BrightnessSliderView(Context context)52     public BrightnessSliderView(Context context) {
53         this(context, null);
54     }
55 
BrightnessSliderView(Context context, AttributeSet attrs)56     public BrightnessSliderView(Context context, AttributeSet attrs) {
57         super(context, attrs);
58     }
59 
60     // Inflated from quick_settings_brightness_dialog
61     @Override
onFinishInflate()62     protected void onFinishInflate() {
63         super.onFinishInflate();
64         setLayerType(LAYER_TYPE_HARDWARE, null);
65 
66         mSlider = requireViewById(R.id.slider);
67         mSlider.setAccessibilityLabel(getContentDescription().toString());
68 
69         // Finds the progress drawable. Assumes brightness_progress_drawable.xml
70         try {
71             LayerDrawable progress = (LayerDrawable) mSlider.getProgressDrawable();
72             DrawableWrapper progressSlider = (DrawableWrapper) progress
73                     .findDrawableByLayerId(android.R.id.progress);
74             LayerDrawable actualProgressSlider = (LayerDrawable) progressSlider.getDrawable();
75             mProgressDrawable = actualProgressSlider.findDrawableByLayerId(R.id.slider_foreground);
76         } catch (Exception e) {
77             // Nothing to do, mProgressDrawable will be null.
78         }
79     }
80 
81     /**
82      * Attaches a listener to relay touch events.
83      * @param listener use {@code null} to remove listener
84      */
setOnDispatchTouchEventListener( DispatchTouchEventListener listener)85     public void setOnDispatchTouchEventListener(
86             DispatchTouchEventListener listener) {
87         mListener = listener;
88     }
89 
90     @Override
dispatchTouchEvent(MotionEvent ev)91     public boolean dispatchTouchEvent(MotionEvent ev) {
92         if (mListener != null) {
93             mListener.onDispatchTouchEvent(ev);
94         }
95         return super.dispatchTouchEvent(ev);
96     }
97 
98     @Override
requestDisallowInterceptTouchEvent(boolean disallowIntercept)99     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
100         // We prevent disallowing on this view, but bubble it up to our parents.
101         // We need interception to handle falsing.
102         if (mParent != null) {
103             mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
104         }
105     }
106 
107     /**
108      * Attaches a listener to the {@link ToggleSeekBar} in the view so changes can be observed
109      * @param seekListener use {@code null} to remove listener
110      */
setOnSeekBarChangeListener(OnSeekBarChangeListener seekListener)111     public void setOnSeekBarChangeListener(OnSeekBarChangeListener seekListener) {
112         mSlider.setOnSeekBarChangeListener(seekListener);
113     }
114 
115     /**
116      * Enforces admin rules for toggling auto-brightness and changing value of brightness
117      * @param admin
118      * @see ToggleSeekBar#setEnforcedAdmin
119      */
setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin)120     public void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
121         mSlider.setEnabled(admin == null);
122         mSlider.setEnforcedAdmin(admin);
123     }
124 
125     /**
126      * Enables or disables the slider
127      * @param enable
128      */
enableSlider(boolean enable)129     public void enableSlider(boolean enable) {
130         mSlider.setEnabled(enable);
131     }
132 
133     /**
134      * @return the maximum value of the {@link ToggleSeekBar}.
135      */
getMax()136     public int getMax() {
137         return mSlider.getMax();
138     }
139 
140     /**
141      * Sets the maximum value of the {@link ToggleSeekBar}.
142      * @param max
143      */
setMax(int max)144     public void setMax(int max) {
145         mSlider.setMax(max);
146     }
147 
148     /**
149      * Sets the current value of the {@link ToggleSeekBar}.
150      * @param value
151      */
setValue(int value)152     public void setValue(int value) {
153         mSlider.setProgress(value);
154     }
155 
156     /**
157      * @return the current value of the {@link ToggleSeekBar}
158      */
getValue()159     public int getValue() {
160         return mSlider.getProgress();
161     }
162 
setOnInterceptListener(Gefingerpoken onInterceptListener)163     public void setOnInterceptListener(Gefingerpoken onInterceptListener) {
164         mOnInterceptListener = onInterceptListener;
165     }
166 
167     @Override
onInterceptTouchEvent(MotionEvent ev)168     public boolean onInterceptTouchEvent(MotionEvent ev) {
169         if (mOnInterceptListener != null) {
170             return mOnInterceptListener.onInterceptTouchEvent(ev);
171         }
172         return super.onInterceptTouchEvent(ev);
173     }
174 
175     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)176     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
177         super.onLayout(changed, left, top, right, bottom);
178         applySliderScale();
179     }
180 
181     /**
182      * Sets the scale for the progress bar (for brightness_progress_drawable.xml)
183      *
184      * This will only scale the thick progress bar and not the icon inside
185      *
186      * Used in {@link com.android.systemui.qs.QSAnimator}.
187      */
188     @Keep
setSliderScaleY(float scale)189     public void setSliderScaleY(float scale) {
190         if (scale != mScale) {
191             mScale = scale;
192             applySliderScale();
193         }
194     }
195 
applySliderScale()196     private void applySliderScale() {
197         if (mProgressDrawable != null) {
198             final Rect r = mProgressDrawable.getBounds();
199             int height = (int) (mProgressDrawable.getIntrinsicHeight() * mScale);
200             int inset = (mProgressDrawable.getIntrinsicHeight() - height) / 2;
201             mProgressDrawable.setBounds(r.left, inset, r.right, inset + height);
202         }
203     }
204 
205     @Keep
getSliderScaleY()206     public float getSliderScaleY() {
207         return mScale;
208     }
209 
210     /**
211      * Interface to attach a listener for {@link View#dispatchTouchEvent}.
212      */
213     @FunctionalInterface
214     interface DispatchTouchEventListener {
onDispatchTouchEvent(MotionEvent ev)215         boolean onDispatchTouchEvent(MotionEvent ev);
216     }
217 }
218 
219