Home | History | Annotate | Download | only in systemui
      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 package com.android.systemui;
     17 
     18 import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
     19 import static android.app.StatusBarManager.DISABLE_NONE;
     20 import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
     21 
     22 import android.animation.ArgbEvaluator;
     23 import android.app.ActivityManager;
     24 import android.content.Context;
     25 import android.content.res.Resources;
     26 import android.content.res.TypedArray;
     27 import android.database.ContentObserver;
     28 import android.graphics.Rect;
     29 import android.net.Uri;
     30 import android.os.Handler;
     31 import android.provider.Settings;
     32 import android.util.ArraySet;
     33 import android.util.AttributeSet;
     34 import android.util.TypedValue;
     35 import android.view.ContextThemeWrapper;
     36 import android.view.Gravity;
     37 import android.view.LayoutInflater;
     38 import android.view.View;
     39 import android.view.ViewGroup;
     40 import android.widget.ImageView;
     41 import android.widget.LinearLayout;
     42 import android.widget.TextView;
     43 
     44 import com.android.settingslib.Utils;
     45 import com.android.settingslib.graph.BatteryMeterDrawableBase;
     46 import com.android.systemui.settings.CurrentUserTracker;
     47 import com.android.systemui.statusbar.phone.StatusBarIconController;
     48 import com.android.systemui.statusbar.policy.BatteryController;
     49 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
     50 import com.android.systemui.statusbar.policy.ConfigurationController;
     51 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
     52 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
     53 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
     54 import com.android.systemui.statusbar.policy.IconLogger;
     55 import com.android.systemui.tuner.TunerService;
     56 import com.android.systemui.tuner.TunerService.Tunable;
     57 import com.android.systemui.util.Utils.DisableStateTracker;
     58 
     59 import java.text.NumberFormat;
     60 
     61 public class BatteryMeterView extends LinearLayout implements
     62         BatteryStateChangeCallback, Tunable, DarkReceiver, ConfigurationListener {
     63 
     64     private final BatteryMeterDrawableBase mDrawable;
     65     private final String mSlotBattery;
     66     private final ImageView mBatteryIconView;
     67     private final CurrentUserTracker mUserTracker;
     68     private TextView mBatteryPercentView;
     69 
     70     private BatteryController mBatteryController;
     71     private SettingObserver mSettingObserver;
     72     private int mTextColor;
     73     private int mLevel;
     74     private boolean mForceShowPercent;
     75 
     76     private int mDarkModeBackgroundColor;
     77     private int mDarkModeFillColor;
     78 
     79     private int mLightModeBackgroundColor;
     80     private int mLightModeFillColor;
     81     private float mDarkIntensity;
     82     private int mUser;
     83 
     84     /**
     85      * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings.
     86      */
     87     private boolean mUseWallpaperTextColors;
     88 
     89     private int mNonAdaptedForegroundColor;
     90     private int mNonAdaptedBackgroundColor;
     91 
     92     public BatteryMeterView(Context context) {
     93         this(context, null, 0);
     94     }
     95 
     96     public BatteryMeterView(Context context, AttributeSet attrs) {
     97         this(context, attrs, 0);
     98     }
     99 
    100     public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
    101         super(context, attrs, defStyle);
    102 
    103         setOrientation(LinearLayout.HORIZONTAL);
    104         setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
    105 
    106         TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView,
    107                 defStyle, 0);
    108         final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
    109                 context.getColor(R.color.meter_background_color));
    110         mDrawable = new BatteryMeterDrawableBase(context, frameColor);
    111         atts.recycle();
    112 
    113         mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
    114 
    115         addOnAttachStateChangeListener(
    116                 new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS));
    117 
    118         mSlotBattery = context.getString(
    119                 com.android.internal.R.string.status_bar_battery);
    120         mBatteryIconView = new ImageView(context);
    121         mBatteryIconView.setImageDrawable(mDrawable);
    122         final MarginLayoutParams mlp = new MarginLayoutParams(
    123                 getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_width),
    124                 getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_height));
    125         mlp.setMargins(0, 0, 0,
    126                 getResources().getDimensionPixelOffset(R.dimen.battery_margin_bottom));
    127         addView(mBatteryIconView, mlp);
    128 
    129         updateShowPercent();
    130         setColorsFromContext(context);
    131         // Init to not dark at all.
    132         onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
    133 
    134         mUserTracker = new CurrentUserTracker(mContext) {
    135             @Override
    136             public void onUserSwitched(int newUserId) {
    137                 mUser = newUserId;
    138                 getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
    139                 getContext().getContentResolver().registerContentObserver(
    140                         Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver,
    141                         newUserId);
    142             }
    143         };
    144 
    145         setClipChildren(false);
    146         setClipToPadding(false);
    147     }
    148 
    149     public void setForceShowPercent(boolean show) {
    150         mForceShowPercent = show;
    151         updateShowPercent();
    152     }
    153 
    154     /**
    155      * Sets whether the battery meter view uses the wallpaperTextColor. If we're not using it, we'll
    156      * revert back to dark-mode-based/tinted colors.
    157      *
    158      * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for all
    159      *                                    components
    160      */
    161     public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) {
    162         if (shouldUseWallpaperTextColor == mUseWallpaperTextColors) {
    163             return;
    164         }
    165 
    166         mUseWallpaperTextColors = shouldUseWallpaperTextColor;
    167 
    168         if (mUseWallpaperTextColors) {
    169             updateColors(
    170                     Utils.getColorAttr(mContext, R.attr.wallpaperTextColor),
    171                     Utils.getColorAttr(mContext, R.attr.wallpaperTextColorSecondary));
    172         } else {
    173             updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor);
    174         }
    175     }
    176 
    177     public void setColorsFromContext(Context context) {
    178         if (context == null) {
    179             return;
    180         }
    181 
    182         Context dualToneDarkTheme = new ContextThemeWrapper(context,
    183                 Utils.getThemeAttr(context, R.attr.darkIconTheme));
    184         Context dualToneLightTheme = new ContextThemeWrapper(context,
    185                 Utils.getThemeAttr(context, R.attr.lightIconTheme));
    186         mDarkModeBackgroundColor = Utils.getColorAttr(dualToneDarkTheme, R.attr.backgroundColor);
    187         mDarkModeFillColor = Utils.getColorAttr(dualToneDarkTheme, R.attr.fillColor);
    188         mLightModeBackgroundColor = Utils.getColorAttr(dualToneLightTheme, R.attr.backgroundColor);
    189         mLightModeFillColor = Utils.getColorAttr(dualToneLightTheme, R.attr.fillColor);
    190     }
    191 
    192     @Override
    193     public boolean hasOverlappingRendering() {
    194         return false;
    195     }
    196 
    197     @Override
    198     public void onTuningChanged(String key, String newValue) {
    199         if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
    200             ArraySet<String> icons = StatusBarIconController.getIconBlacklist(newValue);
    201             boolean hidden = icons.contains(mSlotBattery);
    202             Dependency.get(IconLogger.class).onIconVisibility(mSlotBattery, !hidden);
    203             setVisibility(hidden ? View.GONE : View.VISIBLE);
    204         }
    205     }
    206 
    207     @Override
    208     public void onAttachedToWindow() {
    209         super.onAttachedToWindow();
    210         mBatteryController = Dependency.get(BatteryController.class);
    211         mBatteryController.addCallback(this);
    212         mUser = ActivityManager.getCurrentUser();
    213         getContext().getContentResolver().registerContentObserver(
    214                 Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
    215         updateShowPercent();
    216         Dependency.get(TunerService.class)
    217                 .addTunable(this, StatusBarIconController.ICON_BLACKLIST);
    218         Dependency.get(ConfigurationController.class).addCallback(this);
    219         mUserTracker.startTracking();
    220     }
    221 
    222     @Override
    223     public void onDetachedFromWindow() {
    224         super.onDetachedFromWindow();
    225         mUserTracker.stopTracking();
    226         mBatteryController.removeCallback(this);
    227         getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
    228         Dependency.get(TunerService.class).removeTunable(this);
    229         Dependency.get(ConfigurationController.class).removeCallback(this);
    230     }
    231 
    232     @Override
    233     public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
    234         mDrawable.setBatteryLevel(level);
    235         mDrawable.setCharging(pluggedIn);
    236         mLevel = level;
    237         updatePercentText();
    238         setContentDescription(
    239                 getContext().getString(charging ? R.string.accessibility_battery_level_charging
    240                         : R.string.accessibility_battery_level, level));
    241     }
    242 
    243     @Override
    244     public void onPowerSaveChanged(boolean isPowerSave) {
    245         mDrawable.setPowerSave(isPowerSave);
    246     }
    247 
    248     private TextView loadPercentView() {
    249         return (TextView) LayoutInflater.from(getContext())
    250                 .inflate(R.layout.battery_percentage_view, null);
    251     }
    252 
    253     private void updatePercentText() {
    254         if (mBatteryPercentView != null) {
    255             mBatteryPercentView.setText(
    256                     NumberFormat.getPercentInstance().format(mLevel / 100f));
    257         }
    258     }
    259 
    260     private void updateShowPercent() {
    261         final boolean showing = mBatteryPercentView != null;
    262         if (0 != Settings.System.getIntForUser(getContext().getContentResolver(),
    263                 SHOW_BATTERY_PERCENT, 0, mUser) || mForceShowPercent) {
    264             if (!showing) {
    265                 mBatteryPercentView = loadPercentView();
    266                 if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
    267                 updatePercentText();
    268                 addView(mBatteryPercentView,
    269                         new ViewGroup.LayoutParams(
    270                                 LayoutParams.WRAP_CONTENT,
    271                                 LayoutParams.MATCH_PARENT));
    272             }
    273         } else {
    274             if (showing) {
    275                 removeView(mBatteryPercentView);
    276                 mBatteryPercentView = null;
    277             }
    278         }
    279     }
    280 
    281     @Override
    282     public void onDensityOrFontScaleChanged() {
    283         scaleBatteryMeterViews();
    284     }
    285 
    286     /**
    287      * Looks up the scale factor for status bar icons and scales the battery view by that amount.
    288      */
    289     private void scaleBatteryMeterViews() {
    290         Resources res = getContext().getResources();
    291         TypedValue typedValue = new TypedValue();
    292 
    293         res.getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
    294         float iconScaleFactor = typedValue.getFloat();
    295 
    296         int batteryHeight = res.getDimensionPixelSize(R.dimen.status_bar_battery_icon_height);
    297         int batteryWidth = res.getDimensionPixelSize(R.dimen.status_bar_battery_icon_width);
    298         int marginBottom = res.getDimensionPixelSize(R.dimen.battery_margin_bottom);
    299 
    300         LinearLayout.LayoutParams scaledLayoutParams = new LinearLayout.LayoutParams(
    301                 (int) (batteryWidth * iconScaleFactor), (int) (batteryHeight * iconScaleFactor));
    302         scaledLayoutParams.setMargins(0, 0, 0, marginBottom);
    303 
    304         mBatteryIconView.setLayoutParams(scaledLayoutParams);
    305         FontSizeUtils.updateFontSize(mBatteryPercentView, R.dimen.qs_time_expanded_size);
    306     }
    307 
    308     @Override
    309     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
    310         mDarkIntensity = darkIntensity;
    311 
    312         float intensity = DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0;
    313         mNonAdaptedForegroundColor = getColorForDarkIntensity(
    314                 intensity, mLightModeFillColor, mDarkModeFillColor);
    315         mNonAdaptedBackgroundColor = getColorForDarkIntensity(
    316                 intensity, mLightModeBackgroundColor,mDarkModeBackgroundColor);
    317 
    318         if (!mUseWallpaperTextColors) {
    319             updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor);
    320         }
    321     }
    322 
    323     private void updateColors(int foregroundColor, int backgroundColor) {
    324         mDrawable.setColors(foregroundColor, backgroundColor);
    325         mTextColor = foregroundColor;
    326         if (mBatteryPercentView != null) {
    327             mBatteryPercentView.setTextColor(foregroundColor);
    328         }
    329     }
    330 
    331     public void setFillColor(int color) {
    332         if (mLightModeFillColor == color) {
    333             return;
    334         }
    335         mLightModeFillColor = color;
    336         onDarkChanged(new Rect(), mDarkIntensity, DarkIconDispatcher.DEFAULT_ICON_TINT);
    337     }
    338 
    339     private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) {
    340         return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor);
    341     }
    342 
    343     private final class SettingObserver extends ContentObserver {
    344         public SettingObserver(Handler handler) {
    345             super(handler);
    346         }
    347 
    348         @Override
    349         public void onChange(boolean selfChange, Uri uri) {
    350             super.onChange(selfChange, uri);
    351             updateShowPercent();
    352         }
    353     }
    354 }
    355