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