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.convertGammaToLinearFloat; 21 import static com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat; 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 import android.util.MathUtils; 43 44 import com.android.internal.BrightnessSynchronizer; 45 import com.android.internal.logging.MetricsLogger; 46 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 47 import com.android.settingslib.RestrictedLockUtilsInternal; 48 import com.android.systemui.Dependency; 49 import com.android.systemui.broadcast.BroadcastDispatcher; 50 51 import java.util.ArrayList; 52 53 public class BrightnessController implements ToggleSlider.Listener { 54 private static final String TAG = "StatusBar.BrightnessController"; 55 private static final int SLIDER_ANIMATION_DURATION = 3000; 56 57 private static final int MSG_UPDATE_SLIDER = 1; 58 private static final int MSG_SET_CHECKED = 2; 59 private static final int MSG_ATTACH_LISTENER = 3; 60 private static final int MSG_DETACH_LISTENER = 4; 61 private static final int MSG_VR_MODE_CHANGED = 5; 62 63 private static final Uri BRIGHTNESS_MODE_URI = 64 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE); 65 private static final Uri BRIGHTNESS_URI = 66 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); 67 private static final Uri BRIGHTNESS_FLOAT_URI = 68 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT); 69 private static final Uri BRIGHTNESS_FOR_VR_FLOAT_URI = 70 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT); 71 72 private final float mMinimumBacklight; 73 private final float mMaximumBacklight; 74 private final float mDefaultBacklight; 75 private final float mMinimumBacklightForVr; 76 private final float mMaximumBacklightForVr; 77 private final float mDefaultBacklightForVr; 78 79 private final Context mContext; 80 private final ToggleSlider mControl; 81 private final boolean mAutomaticAvailable; 82 private final DisplayManager mDisplayManager; 83 private final CurrentUserTracker mUserTracker; 84 private final IVrManager mVrManager; 85 86 private final Handler mBackgroundHandler; 87 private final BrightnessObserver mBrightnessObserver; 88 89 private ArrayList<BrightnessStateChangeCallback> mChangeCallbacks = 90 new ArrayList<BrightnessStateChangeCallback>(); 91 92 private volatile boolean mAutomatic; // Brightness adjusted automatically using ambient light. 93 private volatile boolean mIsVrModeEnabled; 94 private boolean mListening; 95 private boolean mExternalChange; 96 private boolean mControlValueInitialized; 97 98 private ValueAnimator mSliderAnimator; 99 100 public interface BrightnessStateChangeCallback { onBrightnessLevelChanged()101 public void onBrightnessLevelChanged(); 102 } 103 104 /** ContentObserver to watch brightness */ 105 private class BrightnessObserver extends ContentObserver { 106 BrightnessObserver(Handler handler)107 public BrightnessObserver(Handler handler) { 108 super(handler); 109 } 110 111 @Override onChange(boolean selfChange)112 public void onChange(boolean selfChange) { 113 onChange(selfChange, null); 114 } 115 116 @Override onChange(boolean selfChange, Uri uri)117 public void onChange(boolean selfChange, Uri uri) { 118 if (selfChange) return; 119 120 if (BRIGHTNESS_MODE_URI.equals(uri)) { 121 mBackgroundHandler.post(mUpdateModeRunnable); 122 mBackgroundHandler.post(mUpdateSliderRunnable); 123 } else if (BRIGHTNESS_FLOAT_URI.equals(uri)) { 124 mBackgroundHandler.post(mUpdateSliderRunnable); 125 } else if (BRIGHTNESS_FOR_VR_FLOAT_URI.equals(uri)) { 126 mBackgroundHandler.post(mUpdateSliderRunnable); 127 } else { 128 mBackgroundHandler.post(mUpdateModeRunnable); 129 mBackgroundHandler.post(mUpdateSliderRunnable); 130 } 131 for (BrightnessStateChangeCallback cb : mChangeCallbacks) { 132 cb.onBrightnessLevelChanged(); 133 } 134 } 135 startObserving()136 public void startObserving() { 137 final ContentResolver cr = mContext.getContentResolver(); 138 cr.unregisterContentObserver(this); 139 cr.registerContentObserver( 140 BRIGHTNESS_MODE_URI, 141 false, this, UserHandle.USER_ALL); 142 cr.registerContentObserver( 143 BRIGHTNESS_URI, 144 false, this, UserHandle.USER_ALL); 145 cr.registerContentObserver( 146 BRIGHTNESS_FLOAT_URI, 147 false, this, UserHandle.USER_ALL); 148 cr.registerContentObserver( 149 BRIGHTNESS_FOR_VR_FLOAT_URI, 150 false, this, UserHandle.USER_ALL); 151 } 152 stopObserving()153 public void stopObserving() { 154 final ContentResolver cr = mContext.getContentResolver(); 155 cr.unregisterContentObserver(this); 156 } 157 158 } 159 160 private final Runnable mStartListeningRunnable = new Runnable() { 161 @Override 162 public void run() { 163 if (mListening) { 164 return; 165 } 166 mListening = true; 167 168 if (mVrManager != null) { 169 try { 170 mVrManager.registerListener(mVrStateCallbacks); 171 mIsVrModeEnabled = mVrManager.getVrModeState(); 172 } catch (RemoteException e) { 173 Log.e(TAG, "Failed to register VR mode state listener: ", e); 174 } 175 } 176 177 mBrightnessObserver.startObserving(); 178 mUserTracker.startTracking(); 179 180 // Update the slider and mode before attaching the listener so we don't 181 // receive the onChanged notifications for the initial values. 182 mUpdateModeRunnable.run(); 183 mUpdateSliderRunnable.run(); 184 185 mHandler.sendEmptyMessage(MSG_ATTACH_LISTENER); 186 } 187 }; 188 189 private final Runnable mStopListeningRunnable = new Runnable() { 190 @Override 191 public void run() { 192 if (!mListening) { 193 return; 194 } 195 mListening = false; 196 197 if (mVrManager != null) { 198 try { 199 mVrManager.unregisterListener(mVrStateCallbacks); 200 } catch (RemoteException e) { 201 Log.e(TAG, "Failed to unregister VR mode state listener: ", e); 202 } 203 } 204 205 mBrightnessObserver.stopObserving(); 206 mUserTracker.stopTracking(); 207 208 mHandler.sendEmptyMessage(MSG_DETACH_LISTENER); 209 } 210 }; 211 212 /** 213 * Fetch the brightness mode from the system settings and update the icon. Should be called from 214 * background thread. 215 */ 216 private final Runnable mUpdateModeRunnable = new Runnable() { 217 @Override 218 public void run() { 219 if (mAutomaticAvailable) { 220 int automatic; 221 automatic = Settings.System.getIntForUser(mContext.getContentResolver(), 222 Settings.System.SCREEN_BRIGHTNESS_MODE, 223 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, 224 UserHandle.USER_CURRENT); 225 mAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL; 226 } else { 227 mHandler.obtainMessage(MSG_SET_CHECKED, 0).sendToTarget(); 228 } 229 } 230 }; 231 232 /** 233 * Fetch the brightness from the system settings and update the slider. Should be called from 234 * background thread. 235 */ 236 private final Runnable mUpdateSliderRunnable = new Runnable() { 237 @Override 238 public void run() { 239 final float valFloat; 240 final boolean inVrMode = mIsVrModeEnabled; 241 if (inVrMode) { 242 valFloat = Settings.System.getFloatForUser(mContext.getContentResolver(), 243 Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT, mDefaultBacklightForVr, 244 UserHandle.USER_CURRENT); 245 } else { 246 valFloat = Settings.System.getFloatForUser(mContext.getContentResolver(), 247 Settings.System.SCREEN_BRIGHTNESS_FLOAT, mDefaultBacklight, 248 UserHandle.USER_CURRENT); 249 } 250 // Value is passed as intbits, since this is what the message takes. 251 final int valueAsIntBits = Float.floatToIntBits(valFloat); 252 mHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits, 253 inVrMode ? 1 : 0).sendToTarget(); 254 } 255 }; 256 257 private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { 258 @Override 259 public void onVrStateChanged(boolean enabled) { 260 mHandler.obtainMessage(MSG_VR_MODE_CHANGED, enabled ? 1 : 0, 0) 261 .sendToTarget(); 262 } 263 }; 264 265 private final Handler mHandler = new Handler() { 266 @Override 267 public void handleMessage(Message msg) { 268 mExternalChange = true; 269 try { 270 switch (msg.what) { 271 case MSG_UPDATE_SLIDER: 272 updateSlider(Float.intBitsToFloat(msg.arg1), msg.arg2 != 0); 273 break; 274 case MSG_SET_CHECKED: 275 mControl.setChecked(msg.arg1 != 0); 276 break; 277 case MSG_ATTACH_LISTENER: 278 mControl.setOnChangedListener(BrightnessController.this); 279 break; 280 case MSG_DETACH_LISTENER: 281 mControl.setOnChangedListener(null); 282 break; 283 case MSG_VR_MODE_CHANGED: 284 updateVrMode(msg.arg1 != 0); 285 break; 286 default: 287 super.handleMessage(msg); 288 } 289 } finally { 290 mExternalChange = false; 291 } 292 } 293 }; 294 BrightnessController(Context context, ToggleSlider control, BroadcastDispatcher broadcastDispatcher)295 public BrightnessController(Context context, ToggleSlider control, 296 BroadcastDispatcher broadcastDispatcher) { 297 mContext = context; 298 mControl = control; 299 mControl.setMax(GAMMA_SPACE_MAX); 300 mBackgroundHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER)); 301 mUserTracker = new CurrentUserTracker(broadcastDispatcher) { 302 @Override 303 public void onUserSwitched(int newUserId) { 304 mBackgroundHandler.post(mUpdateModeRunnable); 305 mBackgroundHandler.post(mUpdateSliderRunnable); 306 } 307 }; 308 mBrightnessObserver = new BrightnessObserver(mHandler); 309 310 PowerManager pm = context.getSystemService(PowerManager.class); 311 mMinimumBacklight = pm.getBrightnessConstraint( 312 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); 313 mMaximumBacklight = pm.getBrightnessConstraint( 314 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); 315 mDefaultBacklight = pm.getBrightnessConstraint( 316 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT); 317 mMinimumBacklightForVr = pm.getBrightnessConstraint( 318 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR); 319 mMaximumBacklightForVr = pm.getBrightnessConstraint( 320 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR); 321 mDefaultBacklightForVr = pm.getBrightnessConstraint( 322 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR); 323 324 325 mAutomaticAvailable = context.getResources().getBoolean( 326 com.android.internal.R.bool.config_automatic_brightness_available); 327 mDisplayManager = context.getSystemService(DisplayManager.class); 328 mVrManager = IVrManager.Stub.asInterface(ServiceManager.getService( 329 Context.VR_SERVICE)); 330 } 331 addStateChangedCallback(BrightnessStateChangeCallback cb)332 public void addStateChangedCallback(BrightnessStateChangeCallback cb) { 333 mChangeCallbacks.add(cb); 334 } 335 removeStateChangedCallback(BrightnessStateChangeCallback cb)336 public boolean removeStateChangedCallback(BrightnessStateChangeCallback cb) { 337 return mChangeCallbacks.remove(cb); 338 } 339 340 @Override onInit(ToggleSlider control)341 public void onInit(ToggleSlider control) { 342 // Do nothing 343 } 344 registerCallbacks()345 public void registerCallbacks() { 346 mBackgroundHandler.post(mStartListeningRunnable); 347 } 348 349 /** Unregister all call backs, both to and from the controller */ unregisterCallbacks()350 public void unregisterCallbacks() { 351 mBackgroundHandler.post(mStopListeningRunnable); 352 mControlValueInitialized = false; 353 } 354 355 @Override onChanged(ToggleSlider toggleSlider, boolean tracking, boolean automatic, int value, boolean stopTracking)356 public void onChanged(ToggleSlider toggleSlider, boolean tracking, boolean automatic, 357 int value, boolean stopTracking) { 358 if (mExternalChange) return; 359 360 if (mSliderAnimator != null) { 361 mSliderAnimator.cancel(); 362 } 363 364 final float minBacklight; 365 final float maxBacklight; 366 final int metric; 367 final String settingToChange; 368 369 if (mIsVrModeEnabled) { 370 metric = MetricsEvent.ACTION_BRIGHTNESS_FOR_VR; 371 minBacklight = mMinimumBacklightForVr; 372 maxBacklight = mMaximumBacklightForVr; 373 settingToChange = Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT; 374 } else { 375 metric = mAutomatic 376 ? MetricsEvent.ACTION_BRIGHTNESS_AUTO 377 : MetricsEvent.ACTION_BRIGHTNESS; 378 minBacklight = mMinimumBacklight; 379 maxBacklight = mMaximumBacklight; 380 settingToChange = Settings.System.SCREEN_BRIGHTNESS_FLOAT; 381 } 382 final float valFloat = MathUtils.min(convertGammaToLinearFloat(value, 383 minBacklight, maxBacklight), 384 1.0f); 385 if (stopTracking) { 386 // TODO(brightnessfloat): change to use float value instead. 387 MetricsLogger.action(mContext, metric, 388 BrightnessSynchronizer.brightnessFloatToInt(mContext, valFloat)); 389 390 } 391 setBrightness(valFloat); 392 if (!tracking) { 393 AsyncTask.execute(new Runnable() { 394 public void run() { 395 Settings.System.putFloatForUser(mContext.getContentResolver(), 396 settingToChange, valFloat, UserHandle.USER_CURRENT); 397 } 398 }); 399 } 400 401 for (BrightnessStateChangeCallback cb : mChangeCallbacks) { 402 cb.onBrightnessLevelChanged(); 403 } 404 } 405 checkRestrictionAndSetEnabled()406 public void checkRestrictionAndSetEnabled() { 407 mBackgroundHandler.post(new Runnable() { 408 @Override 409 public void run() { 410 ((ToggleSliderView)mControl).setEnforcedAdmin( 411 RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext, 412 UserManager.DISALLOW_CONFIG_BRIGHTNESS, 413 mUserTracker.getCurrentUserId())); 414 } 415 }); 416 } 417 setMode(int mode)418 private void setMode(int mode) { 419 Settings.System.putIntForUser(mContext.getContentResolver(), 420 Settings.System.SCREEN_BRIGHTNESS_MODE, mode, 421 mUserTracker.getCurrentUserId()); 422 } 423 setBrightness(float brightness)424 private void setBrightness(float brightness) { 425 mDisplayManager.setTemporaryBrightness(brightness); 426 } 427 updateVrMode(boolean isEnabled)428 private void updateVrMode(boolean isEnabled) { 429 if (mIsVrModeEnabled != isEnabled) { 430 mIsVrModeEnabled = isEnabled; 431 mBackgroundHandler.post(mUpdateSliderRunnable); 432 } 433 } 434 updateSlider(float brightnessValue, boolean inVrMode)435 private void updateSlider(float brightnessValue, boolean inVrMode) { 436 final float min; 437 final float max; 438 if (inVrMode) { 439 min = mMinimumBacklightForVr; 440 max = mMaximumBacklightForVr; 441 } else { 442 min = mMinimumBacklight; 443 max = mMaximumBacklight; 444 } 445 // convertGammaToLinearFloat returns 0-1 446 if (BrightnessSynchronizer.floatEquals(brightnessValue, 447 convertGammaToLinearFloat(mControl.getValue(), min, max))) { 448 // If the value in the slider is equal to the value on the current brightness 449 // then the slider does not need to animate, since the brightness will not change. 450 return; 451 } 452 // Returns GAMMA_SPACE_MIN - GAMMA_SPACE_MAX 453 final int sliderVal = convertLinearToGammaFloat(brightnessValue, min, max); 454 animateSliderTo(sliderVal); 455 } 456 animateSliderTo(int target)457 private void animateSliderTo(int target) { 458 if (!mControlValueInitialized) { 459 // Don't animate the first value since its default state isn't meaningful to users. 460 mControl.setValue(target); 461 mControlValueInitialized = true; 462 } 463 if (mSliderAnimator != null && mSliderAnimator.isStarted()) { 464 mSliderAnimator.cancel(); 465 } 466 mSliderAnimator = ValueAnimator.ofInt(mControl.getValue(), target); 467 mSliderAnimator.addUpdateListener((ValueAnimator animation) -> { 468 mExternalChange = true; 469 mControl.setValue((int) animation.getAnimatedValue()); 470 mExternalChange = false; 471 }); 472 final long animationDuration = SLIDER_ANIMATION_DURATION * Math.abs( 473 mControl.getValue() - target) / GAMMA_SPACE_MAX; 474 mSliderAnimator.setDuration(animationDuration); 475 mSliderAnimator.start(); 476 } 477 478 } 479