• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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;
18 
19 import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX;
20 import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinear;
21 import static com.android.settingslib.display.BrightnessUtils.convertLinearToGamma;
22 
23 import android.animation.ValueAnimator;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.database.ContentObserver;
27 import android.hardware.display.DisplayManager;
28 import android.net.Uri;
29 import android.os.AsyncTask;
30 import android.os.Handler;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.PowerManager;
34 import android.os.RemoteException;
35 import android.os.ServiceManager;
36 import android.os.UserHandle;
37 import android.os.UserManager;
38 import android.provider.Settings;
39 import android.service.vr.IVrManager;
40 import android.service.vr.IVrStateCallbacks;
41 import android.util.Log;
42 import android.widget.ImageView;
43 
44 import com.android.internal.logging.MetricsLogger;
45 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
46 import com.android.settingslib.RestrictedLockUtils;
47 import com.android.systemui.Dependency;
48 
49 import java.util.ArrayList;
50 
51 public class BrightnessController implements ToggleSlider.Listener {
52     private static final String TAG = "StatusBar.BrightnessController";
53     private static final boolean SHOW_AUTOMATIC_ICON = false;
54 
55     private static final int SLIDER_ANIMATION_DURATION = 3000;
56 
57     private static final int MSG_UPDATE_ICON = 0;
58     private static final int MSG_UPDATE_SLIDER = 1;
59     private static final int MSG_SET_CHECKED = 2;
60     private static final int MSG_ATTACH_LISTENER = 3;
61     private static final int MSG_DETACH_LISTENER = 4;
62     private static final int MSG_VR_MODE_CHANGED = 5;
63 
64     private final int mMinimumBacklight;
65     private final int mMaximumBacklight;
66     private final int mDefaultBacklight;
67     private final int mMinimumBacklightForVr;
68     private final int mMaximumBacklightForVr;
69     private final int mDefaultBacklightForVr;
70 
71     private final Context mContext;
72     private final ImageView mIcon;
73     private final ToggleSlider mControl;
74     private final boolean mAutomaticAvailable;
75     private final DisplayManager mDisplayManager;
76     private final CurrentUserTracker mUserTracker;
77     private final IVrManager mVrManager;
78 
79     private final Handler mBackgroundHandler;
80     private final BrightnessObserver mBrightnessObserver;
81 
82     private ArrayList<BrightnessStateChangeCallback> mChangeCallbacks =
83             new ArrayList<BrightnessStateChangeCallback>();
84 
85     private volatile boolean mAutomatic;  // Brightness adjusted automatically using ambient light.
86     private volatile boolean mIsVrModeEnabled;
87     private boolean mListening;
88     private boolean mExternalChange;
89     private boolean mControlValueInitialized;
90 
91     private ValueAnimator mSliderAnimator;
92 
93     public interface BrightnessStateChangeCallback {
onBrightnessLevelChanged()94         public void onBrightnessLevelChanged();
95     }
96 
97     /** ContentObserver to watch brightness **/
98     private class BrightnessObserver extends ContentObserver {
99 
100         private final Uri BRIGHTNESS_MODE_URI =
101                 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
102         private final Uri BRIGHTNESS_URI =
103                 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
104         private final Uri BRIGHTNESS_FOR_VR_URI =
105                 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR);
106 
BrightnessObserver(Handler handler)107         public BrightnessObserver(Handler handler) {
108             super(handler);
109         }
110 
111         @Override
onChange(boolean selfChange)112         public void onChange(boolean selfChange) {
113             onChange(selfChange, null);
114         }
115 
116         @Override
onChange(boolean selfChange, Uri uri)117         public void onChange(boolean selfChange, Uri uri) {
118             if (selfChange) return;
119 
120             if (BRIGHTNESS_MODE_URI.equals(uri)) {
121                 mBackgroundHandler.post(mUpdateModeRunnable);
122                 mBackgroundHandler.post(mUpdateSliderRunnable);
123             } else if (BRIGHTNESS_URI.equals(uri)) {
124                 mBackgroundHandler.post(mUpdateSliderRunnable);
125             } else if (BRIGHTNESS_FOR_VR_URI.equals(uri)) {
126                 mBackgroundHandler.post(mUpdateSliderRunnable);
127             } else {
128                 mBackgroundHandler.post(mUpdateModeRunnable);
129                 mBackgroundHandler.post(mUpdateSliderRunnable);
130             }
131             for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
132                 cb.onBrightnessLevelChanged();
133             }
134         }
135 
startObserving()136         public void startObserving() {
137             final ContentResolver cr = mContext.getContentResolver();
138             cr.unregisterContentObserver(this);
139             cr.registerContentObserver(
140                     BRIGHTNESS_MODE_URI,
141                     false, this, UserHandle.USER_ALL);
142             cr.registerContentObserver(
143                     BRIGHTNESS_URI,
144                     false, this, UserHandle.USER_ALL);
145             cr.registerContentObserver(
146                     BRIGHTNESS_FOR_VR_URI,
147                     false, this, UserHandle.USER_ALL);
148         }
149 
stopObserving()150         public void stopObserving() {
151             final ContentResolver cr = mContext.getContentResolver();
152             cr.unregisterContentObserver(this);
153         }
154 
155     }
156 
157     private final Runnable mStartListeningRunnable = new Runnable() {
158         @Override
159         public void run() {
160             mBrightnessObserver.startObserving();
161             mUserTracker.startTracking();
162 
163             // Update the slider and mode before attaching the listener so we don't
164             // receive the onChanged notifications for the initial values.
165             mUpdateModeRunnable.run();
166             mUpdateSliderRunnable.run();
167 
168             mHandler.sendEmptyMessage(MSG_ATTACH_LISTENER);
169         }
170     };
171 
172     private final Runnable mStopListeningRunnable = new Runnable() {
173         @Override
174         public void run() {
175             mBrightnessObserver.stopObserving();
176             mUserTracker.stopTracking();
177 
178             mHandler.sendEmptyMessage(MSG_DETACH_LISTENER);
179         }
180     };
181 
182     /**
183      * Fetch the brightness mode from the system settings and update the icon. Should be called from
184      * background thread.
185      */
186     private final Runnable mUpdateModeRunnable = new Runnable() {
187         @Override
188         public void run() {
189             if (mAutomaticAvailable) {
190                 int automatic;
191                 automatic = Settings.System.getIntForUser(mContext.getContentResolver(),
192                         Settings.System.SCREEN_BRIGHTNESS_MODE,
193                         Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
194                         UserHandle.USER_CURRENT);
195                 mAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
196                 mHandler.obtainMessage(MSG_UPDATE_ICON, mAutomatic ? 1 : 0).sendToTarget();
197             } else {
198                 mHandler.obtainMessage(MSG_SET_CHECKED, 0).sendToTarget();
199                 mHandler.obtainMessage(MSG_UPDATE_ICON, 0 /* automatic */).sendToTarget();
200             }
201         }
202     };
203 
204     /**
205      * Fetch the brightness from the system settings and update the slider. Should be called from
206      * background thread.
207      */
208     private final Runnable mUpdateSliderRunnable = new Runnable() {
209         @Override
210         public void run() {
211             final int val;
212             final boolean inVrMode = mIsVrModeEnabled;
213             if (inVrMode) {
214                 val = Settings.System.getIntForUser(mContext.getContentResolver(),
215                         Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mDefaultBacklightForVr,
216                         UserHandle.USER_CURRENT);
217             } else {
218                 val = Settings.System.getIntForUser(mContext.getContentResolver(),
219                         Settings.System.SCREEN_BRIGHTNESS, mDefaultBacklight,
220                         UserHandle.USER_CURRENT);
221             }
222             mHandler.obtainMessage(MSG_UPDATE_SLIDER, val, inVrMode ? 1 : 0).sendToTarget();
223         }
224     };
225 
226     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
227         @Override
228         public void onVrStateChanged(boolean enabled) {
229             mHandler.obtainMessage(MSG_VR_MODE_CHANGED, enabled ? 1 : 0, 0)
230                     .sendToTarget();
231         }
232     };
233 
234     private final Handler mHandler = new Handler() {
235         @Override
236         public void handleMessage(Message msg) {
237             mExternalChange = true;
238             try {
239                 switch (msg.what) {
240                     case MSG_UPDATE_ICON:
241                         updateIcon(msg.arg1 != 0);
242                         break;
243                     case MSG_UPDATE_SLIDER:
244                         updateSlider(msg.arg1, msg.arg2 != 0);
245                         break;
246                     case MSG_SET_CHECKED:
247                         mControl.setChecked(msg.arg1 != 0);
248                         break;
249                     case MSG_ATTACH_LISTENER:
250                         mControl.setOnChangedListener(BrightnessController.this);
251                         break;
252                     case MSG_DETACH_LISTENER:
253                         mControl.setOnChangedListener(null);
254                         break;
255                     case MSG_VR_MODE_CHANGED:
256                         updateVrMode(msg.arg1 != 0);
257                         break;
258                     default:
259                         super.handleMessage(msg);
260                 }
261             } finally {
262                 mExternalChange = false;
263             }
264         }
265     };
266 
BrightnessController(Context context, ImageView icon, ToggleSlider control)267     public BrightnessController(Context context, ImageView icon, ToggleSlider control) {
268         mContext = context;
269         mIcon = icon;
270         mControl = control;
271         mControl.setMax(GAMMA_SPACE_MAX);
272         mBackgroundHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER));
273         mUserTracker = new CurrentUserTracker(mContext) {
274             @Override
275             public void onUserSwitched(int newUserId) {
276                 mBackgroundHandler.post(mUpdateModeRunnable);
277                 mBackgroundHandler.post(mUpdateSliderRunnable);
278             }
279         };
280         mBrightnessObserver = new BrightnessObserver(mHandler);
281 
282         PowerManager pm = context.getSystemService(PowerManager.class);
283         mMinimumBacklight = pm.getMinimumScreenBrightnessSetting();
284         mMaximumBacklight = pm.getMaximumScreenBrightnessSetting();
285         mDefaultBacklight = pm.getDefaultScreenBrightnessSetting();
286         mMinimumBacklightForVr = pm.getMinimumScreenBrightnessForVrSetting();
287         mMaximumBacklightForVr = pm.getMaximumScreenBrightnessForVrSetting();
288         mDefaultBacklightForVr = pm.getDefaultScreenBrightnessForVrSetting();
289 
290         mAutomaticAvailable = context.getResources().getBoolean(
291                 com.android.internal.R.bool.config_automatic_brightness_available);
292         mDisplayManager = context.getSystemService(DisplayManager.class);
293         mVrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
294                 Context.VR_SERVICE));
295     }
296 
addStateChangedCallback(BrightnessStateChangeCallback cb)297     public void addStateChangedCallback(BrightnessStateChangeCallback cb) {
298         mChangeCallbacks.add(cb);
299     }
300 
removeStateChangedCallback(BrightnessStateChangeCallback cb)301     public boolean removeStateChangedCallback(BrightnessStateChangeCallback cb) {
302         return mChangeCallbacks.remove(cb);
303     }
304 
305     @Override
onInit(ToggleSlider control)306     public void onInit(ToggleSlider control) {
307         // Do nothing
308     }
309 
registerCallbacks()310     public void registerCallbacks() {
311         if (mListening) {
312             return;
313         }
314 
315         if (mVrManager != null) {
316             try {
317                 mVrManager.registerListener(mVrStateCallbacks);
318                 mIsVrModeEnabled = mVrManager.getVrModeState();
319             } catch (RemoteException e) {
320                 Log.e(TAG, "Failed to register VR mode state listener: ", e);
321             }
322         }
323 
324         mBackgroundHandler.post(mStartListeningRunnable);
325         mListening = true;
326     }
327 
328     /** Unregister all call backs, both to and from the controller */
unregisterCallbacks()329     public void unregisterCallbacks() {
330         if (!mListening) {
331             return;
332         }
333 
334         if (mVrManager != null) {
335             try {
336                 mVrManager.unregisterListener(mVrStateCallbacks);
337             } catch (RemoteException e) {
338                 Log.e(TAG, "Failed to unregister VR mode state listener: ", e);
339             }
340         }
341 
342         mBackgroundHandler.post(mStopListeningRunnable);
343         mListening = false;
344         mControlValueInitialized = false;
345     }
346 
347     @Override
onChanged(ToggleSlider toggleSlider, boolean tracking, boolean automatic, int value, boolean stopTracking)348     public void onChanged(ToggleSlider toggleSlider, boolean tracking, boolean automatic,
349             int value, boolean stopTracking) {
350         updateIcon(mAutomatic);
351         if (mExternalChange) return;
352 
353         if (mSliderAnimator != null) {
354             mSliderAnimator.cancel();
355         }
356 
357         final int min;
358         final int max;
359         final int metric;
360         final String setting;
361 
362         if (mIsVrModeEnabled) {
363             metric = MetricsEvent.ACTION_BRIGHTNESS_FOR_VR;
364             min = mMinimumBacklightForVr;
365             max = mMaximumBacklightForVr;
366             setting = Settings.System.SCREEN_BRIGHTNESS_FOR_VR;
367         } else {
368             metric = mAutomatic
369                     ? MetricsEvent.ACTION_BRIGHTNESS_AUTO
370                     : MetricsEvent.ACTION_BRIGHTNESS;
371             min = mMinimumBacklight;
372             max = mMaximumBacklight;
373             setting = Settings.System.SCREEN_BRIGHTNESS;
374         }
375 
376         final int val = convertGammaToLinear(value, min, max);
377 
378         if (stopTracking) {
379             MetricsLogger.action(mContext, metric, val);
380         }
381 
382         setBrightness(val);
383         if (!tracking) {
384             AsyncTask.execute(new Runnable() {
385                     public void run() {
386                         Settings.System.putIntForUser(mContext.getContentResolver(),
387                                 setting, val, UserHandle.USER_CURRENT);
388                     }
389                 });
390         }
391 
392         for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
393             cb.onBrightnessLevelChanged();
394         }
395     }
396 
checkRestrictionAndSetEnabled()397     public void checkRestrictionAndSetEnabled() {
398         mBackgroundHandler.post(new Runnable() {
399             @Override
400             public void run() {
401                 ((ToggleSliderView)mControl).setEnforcedAdmin(
402                         RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
403                                 UserManager.DISALLOW_CONFIG_BRIGHTNESS,
404                                 mUserTracker.getCurrentUserId()));
405             }
406         });
407     }
408 
setMode(int mode)409     private void setMode(int mode) {
410         Settings.System.putIntForUser(mContext.getContentResolver(),
411                 Settings.System.SCREEN_BRIGHTNESS_MODE, mode,
412                 mUserTracker.getCurrentUserId());
413     }
414 
setBrightness(int brightness)415     private void setBrightness(int brightness) {
416         mDisplayManager.setTemporaryBrightness(brightness);
417     }
418 
setBrightnessAdj(float adj)419     private void setBrightnessAdj(float adj) {
420         mDisplayManager.setTemporaryAutoBrightnessAdjustment(adj);
421     }
422 
updateIcon(boolean automatic)423     private void updateIcon(boolean automatic) {
424         if (mIcon != null) {
425             mIcon.setImageResource(automatic && SHOW_AUTOMATIC_ICON ?
426                     com.android.systemui.R.drawable.ic_qs_brightness_auto_on :
427                     com.android.systemui.R.drawable.ic_qs_brightness_auto_off);
428         }
429     }
430 
updateVrMode(boolean isEnabled)431     private void updateVrMode(boolean isEnabled) {
432         if (mIsVrModeEnabled != isEnabled) {
433             mIsVrModeEnabled = isEnabled;
434             mBackgroundHandler.post(mUpdateSliderRunnable);
435         }
436     }
437 
updateSlider(int val, boolean inVrMode)438     private void updateSlider(int val, boolean inVrMode) {
439         final int min;
440         final int max;
441         if (inVrMode) {
442             min = mMinimumBacklightForVr;
443             max = mMaximumBacklightForVr;
444         } else {
445             min = mMinimumBacklight;
446             max = mMaximumBacklight;
447         }
448         if (val == convertGammaToLinear(mControl.getValue(), min, max)) {
449             // If we have more resolution on the slider than we do in the actual setting, then
450             // multiple slider positions will map to the same setting value. Thus, if we see a
451             // setting value here that maps to the current slider position, we don't bother to
452             // calculate the new slider position since it may differ and look like a brightness
453             // change to the user even though it isn't one.
454             return;
455         }
456         final int sliderVal = convertLinearToGamma(val, min, max);
457         animateSliderTo(sliderVal);
458     }
459 
animateSliderTo(int target)460     private void animateSliderTo(int target) {
461         if (!mControlValueInitialized) {
462             // Don't animate the first value since it's default state isn't meaningful to users.
463             mControl.setValue(target);
464             mControlValueInitialized = true;
465         }
466         if (mSliderAnimator != null && mSliderAnimator.isStarted()) {
467             mSliderAnimator.cancel();
468         }
469         mSliderAnimator = ValueAnimator.ofInt(mControl.getValue(), target);
470         mSliderAnimator.addUpdateListener((ValueAnimator animation) -> {
471             mExternalChange = true;
472             mControl.setValue((int)animation.getAnimatedValue());
473             mExternalChange = false;
474         });
475         mSliderAnimator.setDuration(SLIDER_ANIMATION_DURATION);
476         mSliderAnimator.start();
477     }
478 
479 }
480