• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BatteryMeterView(Context context)92     public BatteryMeterView(Context context) {
93         this(context, null, 0);
94     }
95 
BatteryMeterView(Context context, AttributeSet attrs)96     public BatteryMeterView(Context context, AttributeSet attrs) {
97         this(context, attrs, 0);
98     }
99 
BatteryMeterView(Context context, AttributeSet attrs, int defStyle)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 
setForceShowPercent(boolean show)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      */
useWallpaperTextColor(boolean shouldUseWallpaperTextColor)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 
setColorsFromContext(Context context)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
hasOverlappingRendering()193     public boolean hasOverlappingRendering() {
194         return false;
195     }
196 
197     @Override
onTuningChanged(String key, String newValue)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
onAttachedToWindow()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
onDetachedFromWindow()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
onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging)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
onPowerSaveChanged(boolean isPowerSave)244     public void onPowerSaveChanged(boolean isPowerSave) {
245         mDrawable.setPowerSave(isPowerSave);
246     }
247 
loadPercentView()248     private TextView loadPercentView() {
249         return (TextView) LayoutInflater.from(getContext())
250                 .inflate(R.layout.battery_percentage_view, null);
251     }
252 
updatePercentText()253     private void updatePercentText() {
254         if (mBatteryPercentView != null) {
255             mBatteryPercentView.setText(
256                     NumberFormat.getPercentInstance().format(mLevel / 100f));
257         }
258     }
259 
updateShowPercent()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
onDensityOrFontScaleChanged()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      */
scaleBatteryMeterViews()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
onDarkChanged(Rect area, float darkIntensity, int tint)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 
updateColors(int foregroundColor, int backgroundColor)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 
setFillColor(int color)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 
getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor)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 {
SettingObserver(Handler handler)344         public SettingObserver(Handler handler) {
345             super(handler);
346         }
347 
348         @Override
onChange(boolean selfChange, Uri uri)349         public void onChange(boolean selfChange, Uri uri) {
350             super.onChange(selfChange, uri);
351             updateShowPercent();
352         }
353     }
354 }
355