• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX;
20 import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinearFloat;
21 import static com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat;
22 
23 import android.animation.ValueAnimator;
24 import android.annotation.NonNull;
25 import android.content.Context;
26 import android.database.ContentObserver;
27 import android.hardware.display.BrightnessInfo;
28 import android.hardware.display.DisplayManager;
29 import android.net.Uri;
30 import android.os.AsyncTask;
31 import android.os.Handler;
32 import android.os.HandlerExecutor;
33 import android.os.Looper;
34 import android.os.Message;
35 import android.os.PowerManager;
36 import android.os.RemoteException;
37 import android.os.UserHandle;
38 import android.os.UserManager;
39 import android.provider.Settings;
40 import android.service.vr.IVrManager;
41 import android.service.vr.IVrStateCallbacks;
42 import android.util.Log;
43 import android.util.MathUtils;
44 
45 import androidx.annotation.Nullable;
46 
47 import com.android.internal.display.BrightnessSynchronizer;
48 import com.android.internal.logging.MetricsLogger;
49 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
50 import com.android.settingslib.RestrictedLockUtilsInternal;
51 import com.android.systemui.dagger.qualifiers.Background;
52 import com.android.systemui.dagger.qualifiers.Main;
53 import com.android.systemui.settings.DisplayTracker;
54 import com.android.systemui.settings.UserTracker;
55 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
56 import com.android.systemui.util.settings.SecureSettings;
57 
58 import java.util.concurrent.Executor;
59 
60 import dagger.assisted.Assisted;
61 import dagger.assisted.AssistedFactory;
62 import dagger.assisted.AssistedInject;
63 
64 public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController {
65     private static final String TAG = "CentralSurfaces.BrightnessController";
66     private static final int SLIDER_ANIMATION_DURATION = 3000;
67 
68     private static final int MSG_UPDATE_SLIDER = 1;
69     private static final int MSG_ATTACH_LISTENER = 2;
70     private static final int MSG_DETACH_LISTENER = 3;
71     private static final int MSG_VR_MODE_CHANGED = 4;
72 
73     private static final Uri BRIGHTNESS_MODE_URI =
74             Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
75 
76     private final int mDisplayId;
77     private final Context mContext;
78     private final ToggleSlider mControl;
79     private final DisplayManager mDisplayManager;
80     private final UserTracker mUserTracker;
81     private final DisplayTracker mDisplayTracker;
82     @Nullable
83     private final IVrManager mVrManager;
84 
85     private final SecureSettings mSecureSettings;
86 
87     private final Executor mMainExecutor;
88     private final Handler mBackgroundHandler;
89     private final BrightnessObserver mBrightnessObserver;
90 
91     private final DisplayTracker.Callback mBrightnessListener = new DisplayTracker.Callback() {
92         @Override
93         public void onDisplayChanged(int displayId) {
94             mBackgroundHandler.post(mUpdateSliderRunnable);
95         }
96     };
97 
98     private volatile boolean mAutomatic;  // Brightness adjusted automatically using ambient light.
99     private volatile boolean mIsVrModeEnabled;
100     private boolean mListening;
101     private boolean mExternalChange;
102     private boolean mControlValueInitialized;
103     private float mBrightnessMin = PowerManager.BRIGHTNESS_MIN;
104     private float mBrightnessMax = PowerManager.BRIGHTNESS_MAX;
105 
106     private ValueAnimator mSliderAnimator;
107 
108     @Override
setMirror(BrightnessMirrorController controller)109     public void setMirror(BrightnessMirrorController controller) {
110         mControl.setMirrorControllerAndMirror(controller);
111     }
112 
113     /** ContentObserver to watch brightness */
114     private class BrightnessObserver extends ContentObserver {
115 
116         private boolean mObserving = false;
117 
BrightnessObserver(Handler handler)118         BrightnessObserver(Handler handler) {
119             super(handler);
120         }
121 
122         @Override
onChange(boolean selfChange, Uri uri)123         public void onChange(boolean selfChange, Uri uri) {
124             if (selfChange) return;
125 
126             if (BRIGHTNESS_MODE_URI.equals(uri)) {
127                 mBackgroundHandler.post(mUpdateModeRunnable);
128                 mBackgroundHandler.post(mUpdateSliderRunnable);
129             } else {
130                 mBackgroundHandler.post(mUpdateModeRunnable);
131                 mBackgroundHandler.post(mUpdateSliderRunnable);
132             }
133         }
134 
startObserving()135         public void startObserving() {
136             if (!mObserving) {
137                 mObserving = true;
138                 mSecureSettings.registerContentObserverForUser(
139                         BRIGHTNESS_MODE_URI,
140                         false, this, UserHandle.USER_ALL);
141             }
142         }
143 
stopObserving()144         public void stopObserving() {
145             mSecureSettings.unregisterContentObserver(this);
146             mObserving = false;
147         }
148 
149     }
150 
151     private final Runnable mStartListeningRunnable = new Runnable() {
152         @Override
153         public void run() {
154             if (mListening) {
155                 return;
156             }
157             mListening = true;
158 
159             if (mVrManager != null) {
160                 try {
161                     mVrManager.registerListener(mVrStateCallbacks);
162                     mIsVrModeEnabled = mVrManager.getVrModeState();
163                 } catch (RemoteException e) {
164                     Log.e(TAG, "Failed to register VR mode state listener: ", e);
165                 }
166             }
167 
168             mBrightnessObserver.startObserving();
169             mDisplayTracker.addBrightnessChangeCallback(mBrightnessListener,
170                     new HandlerExecutor(mMainHandler));
171             mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
172 
173             // Update the slider and mode before attaching the listener so we don't
174             // receive the onChanged notifications for the initial values.
175             mUpdateModeRunnable.run();
176             mUpdateSliderRunnable.run();
177 
178             mMainHandler.sendEmptyMessage(MSG_ATTACH_LISTENER);
179         }
180     };
181 
182     private final Runnable mStopListeningRunnable = new Runnable() {
183         @Override
184         public void run() {
185             if (!mListening) {
186                 return;
187             }
188             mListening = false;
189 
190             if (mVrManager != null) {
191                 try {
192                     mVrManager.unregisterListener(mVrStateCallbacks);
193                 } catch (RemoteException e) {
194                     Log.e(TAG, "Failed to unregister VR mode state listener: ", e);
195                 }
196             }
197 
198             mBrightnessObserver.stopObserving();
199             mDisplayTracker.removeCallback(mBrightnessListener);
200             mUserTracker.removeCallback(mUserChangedCallback);
201 
202             mMainHandler.sendEmptyMessage(MSG_DETACH_LISTENER);
203         }
204     };
205 
206     /**
207      * Fetch the brightness mode from the system settings and update the icon. Should be called from
208      * background thread.
209      */
210     private final Runnable mUpdateModeRunnable = new Runnable() {
211         @Override
212         public void run() {
213             int automatic;
214             automatic = Settings.System.getIntForUser(mContext.getContentResolver(),
215                     Settings.System.SCREEN_BRIGHTNESS_MODE,
216                     Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
217                     mUserTracker.getUserId());
218             mAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
219         }
220     };
221 
222     /**
223      * Fetch the brightness from the system settings and update the slider. Should be called from
224      * background thread.
225      */
226     private final Runnable mUpdateSliderRunnable = new Runnable() {
227         @Override
228         public void run() {
229             final boolean inVrMode = mIsVrModeEnabled;
230             final BrightnessInfo info = mContext.getDisplay().getBrightnessInfo();
231             if (info == null) {
232                 return;
233             }
234             mBrightnessMax = info.brightnessMaximum;
235             mBrightnessMin = info.brightnessMinimum;
236             // Value is passed as intbits, since this is what the message takes.
237             final int valueAsIntBits = Float.floatToIntBits(info.brightness);
238             mMainHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits,
239                     inVrMode ? 1 : 0).sendToTarget();
240         }
241     };
242 
243     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
244         @Override
245         public void onVrStateChanged(boolean enabled) {
246             mMainHandler.obtainMessage(MSG_VR_MODE_CHANGED, enabled ? 1 : 0, 0)
247                     .sendToTarget();
248         }
249     };
250 
251     private final Handler.Callback mHandlerCallback = new Handler.Callback() {
252         @Override
253         public boolean handleMessage(Message msg) {
254             mExternalChange = true;
255             try {
256                 switch (msg.what) {
257                     case MSG_UPDATE_SLIDER:
258                         updateSlider(Float.intBitsToFloat(msg.arg1), msg.arg2 != 0);
259                         break;
260                     case MSG_ATTACH_LISTENER:
261                         mControl.setOnChangedListener(BrightnessController.this);
262                         break;
263                     case MSG_DETACH_LISTENER:
264                         mControl.setOnChangedListener(null);
265                         break;
266                     case MSG_VR_MODE_CHANGED:
267                         updateVrMode(msg.arg1 != 0);
268                         break;
269                     default:
270                         return false;
271 
272                 }
273             } finally {
274                 mExternalChange = false;
275             }
276             return true;
277         }
278     };
279 
280     private final Handler mMainHandler;
281 
282     private final UserTracker.Callback mUserChangedCallback =
283             new UserTracker.Callback() {
284                 @Override
285                 public void onUserChanged(int newUser, @NonNull Context userContext) {
286                     mBackgroundHandler.post(mUpdateModeRunnable);
287                     mBackgroundHandler.post(mUpdateSliderRunnable);
288                 }
289             };
290 
291     @AssistedInject
BrightnessController( Context context, @Assisted ToggleSlider control, UserTracker userTracker, DisplayTracker displayTracker, DisplayManager displayManager, SecureSettings secureSettings, @Nullable IVrManager iVrManager, @Main Executor mainExecutor, @Main Looper mainLooper, @Background Handler bgHandler)292     public BrightnessController(
293             Context context,
294             @Assisted ToggleSlider control,
295             UserTracker userTracker,
296             DisplayTracker displayTracker,
297             DisplayManager displayManager,
298             SecureSettings secureSettings,
299             @Nullable IVrManager iVrManager,
300             @Main Executor mainExecutor,
301             @Main Looper mainLooper,
302             @Background Handler bgHandler) {
303         mContext = context;
304         mControl = control;
305         mControl.setMax(GAMMA_SPACE_MAX);
306         mMainExecutor = mainExecutor;
307         mBackgroundHandler = bgHandler;
308         mUserTracker = userTracker;
309         mDisplayTracker = displayTracker;
310         mSecureSettings = secureSettings;
311         mDisplayId = mContext.getDisplayId();
312         mDisplayManager = displayManager;
313         mVrManager = iVrManager;
314 
315         mMainHandler = new Handler(mainLooper, mHandlerCallback);
316         mBrightnessObserver = new BrightnessObserver(mMainHandler);
317     }
318 
registerCallbacks()319     public void registerCallbacks() {
320         mBackgroundHandler.removeCallbacks(mStartListeningRunnable);
321         mBackgroundHandler.post(mStartListeningRunnable);
322     }
323 
324     /** Unregister all call backs, both to and from the controller */
unregisterCallbacks()325     public void unregisterCallbacks() {
326         mBackgroundHandler.removeCallbacks(mStopListeningRunnable);
327         mBackgroundHandler.post(mStopListeningRunnable);
328         mControlValueInitialized = false;
329     }
330 
331     @Override
onChanged(boolean tracking, int value, boolean stopTracking)332     public void onChanged(boolean tracking, int value, boolean stopTracking) {
333         if (mExternalChange) return;
334 
335         if (mSliderAnimator != null) {
336             mSliderAnimator.cancel();
337         }
338 
339         final float minBacklight;
340         final float maxBacklight;
341         final int metric;
342 
343 
344         metric = mAutomatic
345                 ? MetricsEvent.ACTION_BRIGHTNESS_AUTO
346                 : MetricsEvent.ACTION_BRIGHTNESS;
347         minBacklight = mBrightnessMin;
348         maxBacklight = mBrightnessMax;
349         final float valFloat = MathUtils.min(
350                 convertGammaToLinearFloat(value, minBacklight, maxBacklight),
351                 maxBacklight);
352         if (stopTracking) {
353             // TODO(brightnessfloat): change to use float value instead.
354             MetricsLogger.action(mContext, metric,
355                     BrightnessSynchronizer.brightnessFloatToInt(valFloat));
356 
357         }
358         setBrightness(valFloat);
359         if (!tracking) {
360             AsyncTask.execute(new Runnable() {
361                     public void run() {
362                         mDisplayManager.setBrightness(mDisplayId, valFloat);
363                     }
364                 });
365         }
366     }
367 
checkRestrictionAndSetEnabled()368     public void checkRestrictionAndSetEnabled() {
369         mBackgroundHandler.post(new Runnable() {
370             @Override
371             public void run() {
372                 mControl.setEnforcedAdmin(
373                         RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
374                                 UserManager.DISALLOW_CONFIG_BRIGHTNESS,
375                                 mUserTracker.getUserId()));
376             }
377         });
378     }
379 
hideSlider()380     public void hideSlider() {
381         mControl.hideView();
382     }
383 
showSlider()384     public void showSlider() {
385         mControl.showView();
386     }
387 
setBrightness(float brightness)388     private void setBrightness(float brightness) {
389         mDisplayManager.setTemporaryBrightness(mDisplayId, brightness);
390     }
391 
updateVrMode(boolean isEnabled)392     private void updateVrMode(boolean isEnabled) {
393         if (mIsVrModeEnabled != isEnabled) {
394             mIsVrModeEnabled = isEnabled;
395             mBackgroundHandler.post(mUpdateSliderRunnable);
396         }
397     }
398 
updateSlider(float brightnessValue, boolean inVrMode)399     private void updateSlider(float brightnessValue, boolean inVrMode) {
400         final float min = mBrightnessMin;
401         final float max = mBrightnessMax;
402 
403         // Ensure the slider is in a fixed position first, then check if we should animate.
404         if (mSliderAnimator != null && mSliderAnimator.isStarted()) {
405             mSliderAnimator.cancel();
406         }
407         // convertGammaToLinearFloat returns 0-1
408         if (BrightnessSynchronizer.floatEquals(brightnessValue,
409                 convertGammaToLinearFloat(mControl.getValue(), min, max))) {
410             // If the value in the slider is equal to the value on the current brightness
411             // then the slider does not need to animate, since the brightness will not change.
412             return;
413         }
414         // Returns GAMMA_SPACE_MIN - GAMMA_SPACE_MAX
415         final int sliderVal = convertLinearToGammaFloat(brightnessValue, min, max);
416         animateSliderTo(sliderVal);
417     }
418 
animateSliderTo(int target)419     private void animateSliderTo(int target) {
420         if (!mControlValueInitialized || !mControl.isVisible()) {
421             // Don't animate the first value since its default state isn't meaningful to users.
422             // We also don't want to animate slider if it's not visible - especially important when
423             // two sliders are active at the same time in split shade (one in QS and one in QQS),
424             // as this negatively affects transition between them and they share mirror slider -
425             // animating it from two different sources causes janky motion
426             mControl.setValue(target);
427             mControlValueInitialized = true;
428         }
429         mSliderAnimator = ValueAnimator.ofInt(mControl.getValue(), target);
430         mSliderAnimator.addUpdateListener((ValueAnimator animation) -> {
431             mExternalChange = true;
432             mControl.setValue((int) animation.getAnimatedValue());
433             mExternalChange = false;
434         });
435         final long animationDuration = SLIDER_ANIMATION_DURATION * Math.abs(
436                 mControl.getValue() - target) / GAMMA_SPACE_MAX;
437         mSliderAnimator.setDuration(animationDuration);
438         mSliderAnimator.start();
439     }
440 
441 
442 
443     /** Factory for creating a {@link BrightnessController}. */
444     @AssistedFactory
445     public interface Factory {
446         /** Create a {@link BrightnessController} */
create(ToggleSlider toggleSlider)447         BrightnessController create(ToggleSlider toggleSlider);
448     }
449 }
450