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