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.view.LayoutInflater; 21 import android.view.MotionEvent; 22 import android.view.View; 23 import android.view.ViewGroup; 24 import android.widget.SeekBar; 25 26 import androidx.annotation.Nullable; 27 28 import com.android.internal.logging.UiEventLogger; 29 import com.android.settingslib.RestrictedLockUtils; 30 import com.android.systemui.Gefingerpoken; 31 import com.android.systemui.R; 32 import com.android.systemui.classifier.Classifier; 33 import com.android.systemui.plugins.FalsingManager; 34 import com.android.systemui.statusbar.policy.BrightnessMirrorController; 35 import com.android.systemui.util.ViewController; 36 37 import javax.inject.Inject; 38 39 /** 40 * {@code ViewController} for a {@code BrightnessSliderView} 41 * 42 * This class allows to control the views of a {@code BrightnessSliderViewView} and get callbacks 43 * when the views change value. It also propagates and manipulates another {@link ToggleSlider} as a 44 * mirror. 45 * 46 * @see BrightnessMirrorController 47 */ 48 public class BrightnessSliderController extends ViewController<BrightnessSliderView> implements 49 ToggleSlider { 50 51 private Listener mListener; 52 private ToggleSlider mMirror; 53 private BrightnessMirrorController mMirrorController; 54 private boolean mTracking; 55 private final FalsingManager mFalsingManager; 56 private final UiEventLogger mUiEventLogger; 57 58 private final Gefingerpoken mOnInterceptListener = new Gefingerpoken() { 59 @Override 60 public boolean onInterceptTouchEvent(MotionEvent ev) { 61 int action = ev.getActionMasked(); 62 if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { 63 mFalsingManager.isFalseTouch(Classifier.BRIGHTNESS_SLIDER); 64 } 65 66 return false; 67 } 68 69 @Override 70 public boolean onTouchEvent(MotionEvent ev) { 71 return false; 72 } 73 }; 74 BrightnessSliderController( BrightnessSliderView brightnessSliderView, FalsingManager falsingManager, UiEventLogger uiEventLogger)75 BrightnessSliderController( 76 BrightnessSliderView brightnessSliderView, 77 FalsingManager falsingManager, 78 UiEventLogger uiEventLogger) { 79 super(brightnessSliderView); 80 mFalsingManager = falsingManager; 81 mUiEventLogger = uiEventLogger; 82 } 83 84 /** 85 * Returns a top level view in the hierarchy that can be attached where necessary 86 */ getRootView()87 public View getRootView() { 88 return mView; 89 } 90 91 92 @Override onViewAttached()93 protected void onViewAttached() { 94 mView.setOnSeekBarChangeListener(mSeekListener); 95 mView.setOnInterceptListener(mOnInterceptListener); 96 } 97 98 @Override onViewDetached()99 protected void onViewDetached() { 100 mView.setOnSeekBarChangeListener(null); 101 mView.setOnDispatchTouchEventListener(null); 102 mView.setOnInterceptListener(null); 103 } 104 105 @Override mirrorTouchEvent(MotionEvent ev)106 public boolean mirrorTouchEvent(MotionEvent ev) { 107 if (mMirror != null) { 108 return copyEventToMirror(ev); 109 } else { 110 // We are the mirror, so we have to dispatch the event 111 return mView.dispatchTouchEvent(ev); 112 } 113 } 114 copyEventToMirror(MotionEvent ev)115 private boolean copyEventToMirror(MotionEvent ev) { 116 MotionEvent copy = ev.copy(); 117 boolean out = mMirror.mirrorTouchEvent(copy); 118 copy.recycle(); 119 return out; 120 } 121 122 @Override setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin)123 public void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin) { 124 mView.setEnforcedAdmin(admin); 125 } 126 setMirror(ToggleSlider toggleSlider)127 private void setMirror(ToggleSlider toggleSlider) { 128 mMirror = toggleSlider; 129 if (mMirror != null) { 130 mMirror.setMax(mView.getMax()); 131 mMirror.setValue(mView.getValue()); 132 mView.setOnDispatchTouchEventListener(this::mirrorTouchEvent); 133 } else { 134 // If there's no mirror, we may be the ones dispatching, events but we should not mirror 135 // them 136 mView.setOnDispatchTouchEventListener(null); 137 } 138 } 139 140 /** 141 * This will set the mirror from the controller using 142 * {@link BrightnessMirrorController#getToggleSlider} as a side-effect. 143 * @param c 144 */ 145 @Override setMirrorControllerAndMirror(BrightnessMirrorController c)146 public void setMirrorControllerAndMirror(BrightnessMirrorController c) { 147 mMirrorController = c; 148 setMirror(c.getToggleSlider()); 149 } 150 151 @Override setOnChangedListener(Listener l)152 public void setOnChangedListener(Listener l) { 153 mListener = l; 154 } 155 156 @Override setMax(int max)157 public void setMax(int max) { 158 mView.setMax(max); 159 if (mMirror != null) { 160 mMirror.setMax(max); 161 } 162 } 163 164 @Override getMax()165 public int getMax() { 166 return mView.getMax(); 167 } 168 169 @Override setValue(int value)170 public void setValue(int value) { 171 mView.setValue(value); 172 if (mMirror != null) { 173 mMirror.setValue(value); 174 } 175 } 176 177 @Override getValue()178 public int getValue() { 179 return mView.getValue(); 180 } 181 182 @Override hideView()183 public void hideView() { 184 mView.setVisibility(View.GONE); 185 } 186 187 @Override showView()188 public void showView() { 189 mView.setVisibility(View.VISIBLE); 190 } 191 192 @Override isVisible()193 public boolean isVisible() { 194 // this should be called rarely - once or twice per slider's value change, but not for 195 // every value change when user slides finger - only the final one. 196 // If view is not visible this call is quick (around 50 µs) as it sees parent is not visible 197 // otherwise it's slightly longer (70 µs) because there are more checks to be done 198 return mView.isVisibleToUser(); 199 } 200 201 private final SeekBar.OnSeekBarChangeListener mSeekListener = 202 new SeekBar.OnSeekBarChangeListener() { 203 @Override 204 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 205 if (mListener != null) { 206 mListener.onChanged(mTracking, progress, false); 207 } 208 } 209 210 @Override 211 public void onStartTrackingTouch(SeekBar seekBar) { 212 mTracking = true; 213 mUiEventLogger.log(BrightnessSliderEvent.SLIDER_STARTED_TRACKING_TOUCH); 214 if (mListener != null) { 215 mListener.onChanged(mTracking, getValue(), false); 216 } 217 218 if (mMirrorController != null) { 219 mMirrorController.showMirror(); 220 mMirrorController.setLocationAndSize(mView); 221 } 222 } 223 224 @Override 225 public void onStopTrackingTouch(SeekBar seekBar) { 226 mTracking = false; 227 mUiEventLogger.log(BrightnessSliderEvent.SLIDER_STOPPED_TRACKING_TOUCH); 228 if (mListener != null) { 229 mListener.onChanged(mTracking, getValue(), true); 230 } 231 232 if (mMirrorController != null) { 233 mMirrorController.hideMirror(); 234 } 235 } 236 }; 237 238 /** 239 * Creates a {@link BrightnessSliderController} with its associated view. 240 */ 241 public static class Factory { 242 243 private final FalsingManager mFalsingManager; 244 private final UiEventLogger mUiEventLogger; 245 246 @Inject Factory(FalsingManager falsingManager, UiEventLogger uiEventLogger)247 public Factory(FalsingManager falsingManager, UiEventLogger uiEventLogger) { 248 mFalsingManager = falsingManager; 249 mUiEventLogger = uiEventLogger; 250 } 251 252 /** 253 * Creates the view hierarchy and controller 254 * 255 * @param context a {@link Context} to inflate the hierarchy 256 * @param viewRoot the {@link ViewGroup} that will contain the hierarchy. The inflated 257 * hierarchy will not be attached 258 */ create( Context context, @Nullable ViewGroup viewRoot)259 public BrightnessSliderController create( 260 Context context, 261 @Nullable ViewGroup viewRoot) { 262 int layout = getLayout(); 263 BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context) 264 .inflate(layout, viewRoot, false); 265 return new BrightnessSliderController(root, mFalsingManager, mUiEventLogger); 266 } 267 268 /** Get the layout to inflate based on what slider to use */ getLayout()269 private int getLayout() { 270 return R.layout.quick_settings_brightness_dialog; 271 } 272 } 273 } 274