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