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