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