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