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 { 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 90 public BrightnessObserver(Handler handler) { 91 super(handler); 92 } 93 94 @Override 95 public void onChange(boolean selfChange) { 96 onChange(selfChange, null); 97 } 98 99 @Override 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 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 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 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 264 public void setBackgroundLooper(Looper backgroundLooper) { 265 mBackgroundHandler = new Handler(backgroundLooper); 266 } 267 268 public void addStateChangedCallback(BrightnessStateChangeCallback cb) { 269 mChangeCallbacks.add(cb); 270 } 271 272 public boolean removeStateChangedCallback(BrightnessStateChangeCallback cb) { 273 return mChangeCallbacks.remove(cb); 274 } 275 276 @Override 277 public void onInit(ToggleSlider control) { 278 // Do nothing 279 } 280 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 */ 291 public void unregisterCallbacks() { 292 if (!mListening) { 293 return; 294 } 295 296 mBackgroundHandler.post(mStopListeningRunnable); 297 mListening = false; 298 } 299 300 @Override 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 343 private void setMode(int mode) { 344 Settings.System.putIntForUser(mContext.getContentResolver(), 345 Settings.System.SCREEN_BRIGHTNESS_MODE, mode, 346 mUserTracker.getCurrentUserId()); 347 } 348 349 private void setBrightness(int brightness) { 350 try { 351 mPower.setTemporaryScreenBrightnessSettingOverride(brightness); 352 } catch (RemoteException ex) { 353 } 354 } 355 356 private void setBrightnessAdj(float adj) { 357 try { 358 mPower.setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(adj); 359 } catch (RemoteException ex) { 360 } 361 } 362 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