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.systemui.Gefingerpoken; 35 import com.android.systemui.res.R; 36 37 import java.util.Collections; 38 39 /** 40 * {@code FrameLayout} used to show and manipulate a {@link ToggleSeekBar}. 41 * 42 */ 43 public class BrightnessSliderView extends FrameLayout { 44 45 @NonNull 46 private ToggleSeekBar mSlider; 47 private DispatchTouchEventListener mListener; 48 private Gefingerpoken mOnInterceptListener; 49 @Nullable 50 private Drawable mProgressDrawable; 51 private float mScale = 1f; 52 private final Rect mSystemGestureExclusionRect = new Rect(); 53 BrightnessSliderView(Context context)54 public BrightnessSliderView(Context context) { 55 this(context, null); 56 } 57 BrightnessSliderView(Context context, AttributeSet attrs)58 public BrightnessSliderView(Context context, AttributeSet attrs) { 59 super(context, attrs); 60 } 61 62 // Inflated from quick_settings_brightness_dialog 63 @Override onFinishInflate()64 protected void onFinishInflate() { 65 super.onFinishInflate(); 66 setLayerType(LAYER_TYPE_HARDWARE, null); 67 68 mSlider = requireViewById(R.id.slider); 69 mSlider.setAccessibilityLabel(getContentDescription().toString()); 70 setBoundaryOffset(); 71 72 // Finds the progress drawable. Assumes brightness_progress_drawable.xml 73 try { 74 LayerDrawable progress = (LayerDrawable) mSlider.getProgressDrawable(); 75 DrawableWrapper progressSlider = (DrawableWrapper) progress 76 .findDrawableByLayerId(android.R.id.progress); 77 LayerDrawable actualProgressSlider = (LayerDrawable) progressSlider.getDrawable(); 78 mProgressDrawable = actualProgressSlider.findDrawableByLayerId(R.id.slider_foreground); 79 } catch (Exception e) { 80 // Nothing to do, mProgressDrawable will be null. 81 } 82 } 83 setBoundaryOffset()84 private void setBoundaryOffset() { 85 // BrightnessSliderView uses hardware layer; if the background of its children exceed its 86 // boundary, it'll be cropped. We need to expand its boundary so that the background of 87 // ToggleSeekBar (i.e. the focus state) can be correctly rendered. 88 int offset = getResources().getDimensionPixelSize(R.dimen.rounded_slider_boundary_offset); 89 MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); 90 lp.setMargins(-offset, -offset, -offset, -offset); 91 setLayoutParams(lp); 92 setPadding(offset, offset, offset, offset); 93 } 94 95 /** 96 * Attaches a listener to relay touch events. 97 * @param listener use {@code null} to remove listener 98 */ setOnDispatchTouchEventListener( DispatchTouchEventListener listener)99 public void setOnDispatchTouchEventListener( 100 DispatchTouchEventListener listener) { 101 mListener = listener; 102 } 103 104 @Override dispatchTouchEvent(MotionEvent ev)105 public boolean dispatchTouchEvent(MotionEvent ev) { 106 if (mListener != null) { 107 mListener.onDispatchTouchEvent(ev); 108 } 109 return super.dispatchTouchEvent(ev); 110 } 111 112 @Override requestDisallowInterceptTouchEvent(boolean disallowIntercept)113 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 114 // We prevent disallowing on this view, but bubble it up to our parents. 115 // We need interception to handle falsing. 116 if (mParent != null) { 117 mParent.requestDisallowInterceptTouchEvent(disallowIntercept); 118 } 119 } 120 121 /** 122 * Attaches a listener to the {@link ToggleSeekBar} in the view so changes can be observed 123 * @param seekListener use {@code null} to remove listener 124 */ setOnSeekBarChangeListener(OnSeekBarChangeListener seekListener)125 public void setOnSeekBarChangeListener(OnSeekBarChangeListener seekListener) { 126 mSlider.setOnSeekBarChangeListener(seekListener); 127 } 128 129 /** 130 * Enforces admin rules for toggling auto-brightness and changing value of brightness 131 * @param admin 132 * @see ToggleSeekBar#setEnforcedAdmin 133 */ setAdminBlocker(ToggleSeekBar.AdminBlocker blocker)134 void setAdminBlocker(ToggleSeekBar.AdminBlocker blocker) { 135 mSlider.setAdminBlocker(blocker); 136 } 137 138 /** 139 * Enables or disables the slider 140 * @param enable 141 */ enableSlider(boolean enable)142 public void enableSlider(boolean enable) { 143 mSlider.setEnabled(enable); 144 } 145 146 /** 147 * @return the maximum value of the {@link ToggleSeekBar}. 148 */ getMax()149 public int getMax() { 150 return mSlider.getMax(); 151 } 152 153 /** 154 * Sets the maximum value of the {@link ToggleSeekBar}. 155 * @param max 156 */ setMax(int max)157 public void setMax(int max) { 158 mSlider.setMax(max); 159 } 160 161 /** 162 * Sets the current value of the {@link ToggleSeekBar}. 163 * @param value 164 */ setValue(int value)165 public void setValue(int value) { 166 mSlider.setProgress(value); 167 } 168 169 /** 170 * @return the current value of the {@link ToggleSeekBar} 171 */ getValue()172 public int getValue() { 173 return mSlider.getProgress(); 174 } 175 setOnInterceptListener(Gefingerpoken onInterceptListener)176 public void setOnInterceptListener(Gefingerpoken onInterceptListener) { 177 mOnInterceptListener = onInterceptListener; 178 } 179 180 @Override onInterceptTouchEvent(MotionEvent ev)181 public boolean onInterceptTouchEvent(MotionEvent ev) { 182 if (mOnInterceptListener != null) { 183 return mOnInterceptListener.onInterceptTouchEvent(ev); 184 } 185 return super.onInterceptTouchEvent(ev); 186 } 187 188 @Override onLayout(boolean changed, int left, int top, int right, int bottom)189 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 190 super.onLayout(changed, left, top, right, bottom); 191 applySliderScale(); 192 int horizontalMargin = 193 getResources().getDimensionPixelSize(R.dimen.notification_side_paddings); 194 mSystemGestureExclusionRect.set(-horizontalMargin, 0, right - left + horizontalMargin, 195 bottom - top); 196 setSystemGestureExclusionRects(Collections.singletonList(mSystemGestureExclusionRect)); 197 } 198 199 /** 200 * Sets the scale for the progress bar (for brightness_progress_drawable.xml) 201 * 202 * This will only scale the thick progress bar and not the icon inside 203 * 204 * Used in {@link com.android.systemui.qs.QSAnimator}. 205 */ 206 @Keep setSliderScaleY(float scale)207 public void setSliderScaleY(float scale) { 208 if (scale != mScale) { 209 mScale = scale; 210 applySliderScale(); 211 } 212 } 213 applySliderScale()214 private void applySliderScale() { 215 if (mProgressDrawable != null) { 216 final Rect r = mProgressDrawable.getBounds(); 217 int height = (int) (mProgressDrawable.getIntrinsicHeight() * mScale); 218 int inset = (mProgressDrawable.getIntrinsicHeight() - height) / 2; 219 mProgressDrawable.setBounds(r.left, inset, r.right, inset + height); 220 } 221 } 222 223 @Keep getSliderScaleY()224 public float getSliderScaleY() { 225 return mScale; 226 } 227 228 /** 229 * Interface to attach a listener for {@link View#dispatchTouchEvent}. 230 */ 231 @FunctionalInterface 232 interface DispatchTouchEventListener { onDispatchTouchEvent(MotionEvent ev)233 boolean onDispatchTouchEvent(MotionEvent ev); 234 } 235 } 236 237