Home | History | Annotate | Download | only in settings
      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