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