1dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler/*
2dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler * Copyright (C) 2013 The Android Open Source Project
3dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler *
4dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler * Licensed under the Apache License, Version 2.0 (the "License");
5dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler * you may not use this file except in compliance with the License.
6dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler * You may obtain a copy of the License at
7dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler *
8dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler *      http://www.apache.org/licenses/LICENSE-2.0
9dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler *
10dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler * Unless required by applicable law or agreed to in writing, software
11dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler * distributed under the License is distributed on an "AS IS" BASIS,
12dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler * See the License for the specific language governing permissions and
14dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler * limitations under the License.
15dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler */
16dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
17dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerpackage com.android.systemui;
18dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
195443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggiimport android.animation.ArgbEvaluator;
20dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.content.BroadcastReceiver;
21dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.content.Context;
22dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.content.Intent;
23dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.content.IntentFilter;
24dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.content.res.Resources;
25dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.content.res.TypedArray;
26f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlockimport android.database.ContentObserver;
27dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.graphics.Canvas;
28dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggiimport android.graphics.Color;
29dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.graphics.Paint;
30fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlockimport android.graphics.Path;
31dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.graphics.RectF;
32dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.graphics.Typeface;
33f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlockimport android.net.Uri;
34dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.os.BatteryManager;
353c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlockimport android.os.Bundle;
36f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlockimport android.os.Handler;
37dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.provider.Settings;
38dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.util.AttributeSet;
39dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandlerimport android.view.View;
40dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
41708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggiimport com.android.systemui.statusbar.policy.BatteryController;
42708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi
43708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggipublic class BatteryMeterView extends View implements DemoMode,
44708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi        BatteryController.BatteryStateChangeCallback {
45dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    public static final String TAG = BatteryMeterView.class.getSimpleName();
46dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
47f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent";
48dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
494b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock    private static final boolean SINGLE_DIGIT_PERCENT = false;
50dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
514b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock    private static final int FULL = 96;
52dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
534b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock    private static final float BOLT_LEVEL_THRESHOLD = 0.3f;  // opaque bolt below this fraction
54cfab99005360819294c65280d8b695ab50c1f0b3John Spurlock
554b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock    private final int[] mColors;
56dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
57f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    private boolean mShowPercent;
585172dc2f36f9c417befd0957ce2ae20f9d083a88Jorim Jaggi    private float mButtonHeightFraction;
59a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi    private float mSubpixelSmoothingLeft;
60a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi    private float mSubpixelSmoothingRight;
614b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock    private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
62dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    private float mTextHeight, mWarningTextHeight;
63dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi    private int mIconTint = Color.WHITE;
64dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
65dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    private int mHeight;
66dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    private int mWidth;
67dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    private String mWarningString;
683332ba54ae85df14d761447d86d2aa19d448ce11John Spurlock    private final int mCriticalLevel;
69dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi    private int mChargeColor;
70fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock    private final float[] mBoltPoints;
71fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock    private final Path mBoltPath = new Path();
72fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock
73fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock    private final RectF mFrame = new RectF();
74fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock    private final RectF mButtonFrame = new RectF();
75d8595fd8feca567d48c4e271070a75c576a7dfbfJohn Spurlock    private final RectF mBoltFrame = new RectF();
76dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
774b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock    private final Path mShapePath = new Path();
784b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock    private final Path mClipPath = new Path();
791fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock    private final Path mTextPath = new Path();
804b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock
81708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi    private BatteryController mBatteryController;
82708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi    private boolean mPowerSaveEnabled;
83708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi
845443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi    private int mDarkModeBackgroundColor;
855443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi    private int mDarkModeFillColor;
865443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi
875443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi    private int mLightModeBackgroundColor;
885443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi    private int mLightModeFillColor;
895443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi
90f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    private BatteryTracker mTracker = new BatteryTracker();
91f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    private final SettingObserver mSettingObserver = new SettingObserver();
92dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
93dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    public BatteryMeterView(Context context) {
94dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        this(context, null, 0);
95dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    }
96dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
97dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    public BatteryMeterView(Context context, AttributeSet attrs) {
98dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        this(context, attrs, 0);
99dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    }
100dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
101dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
102dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        super(context, attrs, defStyle);
103dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
104dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        final Resources res = context.getResources();
10529786fc72d071335c49f7b768a25071846a968feJohn Spurlock        TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView,
10629786fc72d071335c49f7b768a25071846a968feJohn Spurlock                defStyle, 0);
10729786fc72d071335c49f7b768a25071846a968feJohn Spurlock        final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
1084a357cd2e55293402d7172766f7f9419815fc1e8Alan Viverette                context.getColor(R.color.batterymeter_frame_color));
109dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
110dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);
111dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
112dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        final int N = levels.length();
113dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        mColors = new int[2*N];
114dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        for (int i=0; i<N; i++) {
115dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler            mColors[2*i] = levels.getInt(i, 0);
116dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler            mColors[2*i+1] = colors.getColor(i, 0);
117dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        }
118fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        levels.recycle();
119fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        colors.recycle();
12029786fc72d071335c49f7b768a25071846a968feJohn Spurlock        atts.recycle();
121f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        updateShowPercent();
122dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);
1233332ba54ae85df14d761447d86d2aa19d448ce11John Spurlock        mCriticalLevel = mContext.getResources().getInteger(
1243332ba54ae85df14d761447d86d2aa19d448ce11John Spurlock                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
1255172dc2f36f9c417befd0957ce2ae20f9d083a88Jorim Jaggi        mButtonHeightFraction = context.getResources().getFraction(
1265172dc2f36f9c417befd0957ce2ae20f9d083a88Jorim Jaggi                R.fraction.battery_button_height_fraction, 1, 1);
127a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi        mSubpixelSmoothingLeft = context.getResources().getFraction(
128a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi                R.fraction.battery_subpixel_smoothing_left, 1, 1);
129a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi        mSubpixelSmoothingRight = context.getResources().getFraction(
130a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi                R.fraction.battery_subpixel_smoothing_right, 1, 1);
131dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
1326a49ddef62865c1b245ad60a13c334f0ffaf1a5fChris Craik        mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
13329786fc72d071335c49f7b768a25071846a968feJohn Spurlock        mFramePaint.setColor(frameColor);
134cfab99005360819294c65280d8b695ab50c1f0b3John Spurlock        mFramePaint.setDither(true);
135cfab99005360819294c65280d8b695ab50c1f0b3John Spurlock        mFramePaint.setStrokeWidth(0);
136cfab99005360819294c65280d8b695ab50c1f0b3John Spurlock        mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);
137cfab99005360819294c65280d8b695ab50c1f0b3John Spurlock
1386a49ddef62865c1b245ad60a13c334f0ffaf1a5fChris Craik        mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
139cfab99005360819294c65280d8b695ab50c1f0b3John Spurlock        mBatteryPaint.setDither(true);
140cfab99005360819294c65280d8b695ab50c1f0b3John Spurlock        mBatteryPaint.setStrokeWidth(0);
141cfab99005360819294c65280d8b695ab50c1f0b3John Spurlock        mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);
142dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
1436a49ddef62865c1b245ad60a13c334f0ffaf1a5fChris Craik        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
1444b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD);
145dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        mTextPaint.setTypeface(font);
146dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        mTextPaint.setTextAlign(Paint.Align.CENTER);
147dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
1486a49ddef62865c1b245ad60a13c334f0ffaf1a5fChris Craik        mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
149dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        mWarningTextPaint.setColor(mColors[1]);
150dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        font = Typeface.create("sans-serif", Typeface.BOLD);
151dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        mWarningTextPaint.setTypeface(font);
152dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        mWarningTextPaint.setTextAlign(Paint.Align.CENTER);
153dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
1544a357cd2e55293402d7172766f7f9419815fc1e8Alan Viverette        mChargeColor = context.getColor(R.color.batterymeter_charge_color);
155fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock
1566a49ddef62865c1b245ad60a13c334f0ffaf1a5fChris Craik        mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
1574a357cd2e55293402d7172766f7f9419815fc1e8Alan Viverette        mBoltPaint.setColor(context.getColor(R.color.batterymeter_bolt_color));
158fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        mBoltPoints = loadBoltPoints(res);
1595443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi
1605443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi        mDarkModeBackgroundColor =
1615443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi                context.getColor(R.color.dark_mode_icon_color_dual_tone_background);
1625443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi        mDarkModeFillColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_fill);
1635443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi        mLightModeBackgroundColor =
1645443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi                context.getColor(R.color.light_mode_icon_color_dual_tone_background);
1655443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi        mLightModeFillColor = context.getColor(R.color.light_mode_icon_color_dual_tone_fill);
166fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock    }
167fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock
168f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    @Override
169f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    public void onAttachedToWindow() {
170f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        super.onAttachedToWindow();
171f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
172f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        IntentFilter filter = new IntentFilter();
173f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
174f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        filter.addAction(ACTION_LEVEL_TEST);
175f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        final Intent sticky = getContext().registerReceiver(mTracker, filter);
176f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        if (sticky != null) {
177f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock            // preload the battery level
178f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock            mTracker.onReceive(getContext(), sticky);
179f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        }
180f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        mBatteryController.addStateChangedCallback(this);
181f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        getContext().getContentResolver().registerContentObserver(
182f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
183f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    }
184f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
185f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    @Override
186f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    public void onDetachedFromWindow() {
187f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        super.onDetachedFromWindow();
188f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
189f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        getContext().unregisterReceiver(mTracker);
190f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        mBatteryController.removeStateChangedCallback(this);
191f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
192f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    }
193f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
194708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi    public void setBatteryController(BatteryController batteryController) {
195708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi        mBatteryController = batteryController;
196708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi        mPowerSaveEnabled = mBatteryController.isPowerSave();
197708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi    }
198708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi
199708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi    @Override
200708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
201708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi        // TODO: Use this callback instead of own broadcast receiver.
202708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi    }
203708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi
204708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi    @Override
205708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi    public void onPowerSaveChanged() {
206708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi        mPowerSaveEnabled = mBatteryController.isPowerSave();
207708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi        invalidate();
208708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi    }
209708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi
210fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock    private static float[] loadBoltPoints(Resources res) {
211fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);
212fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        int maxX = 0, maxY = 0;
213fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        for (int i = 0; i < pts.length; i += 2) {
214fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock            maxX = Math.max(maxX, pts[i]);
215fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock            maxY = Math.max(maxY, pts[i + 1]);
216fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        }
217fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        final float[] ptsF = new float[pts.length];
218fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        for (int i = 0; i < pts.length; i += 2) {
219fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock            ptsF[i] = (float)pts[i] / maxX;
220fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock            ptsF[i + 1] = (float)pts[i + 1] / maxY;
221fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        }
222fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        return ptsF;
223dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    }
224dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
225dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    @Override
226dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
227dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        mHeight = h;
228dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        mWidth = w;
229dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        mWarningTextPaint.setTextSize(h * 0.75f);
230dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;
231dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    }
232dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
233f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    private void updateShowPercent() {
234f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        mShowPercent = 0 != Settings.System.getInt(getContext().getContentResolver(),
235f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                SHOW_PERCENT_SETTING, 0);
236f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    }
237f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
238dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    private int getColorForLevel(int percent) {
239708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi
240708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi        // If we are in power save mode, always use the normal color.
241708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi        if (mPowerSaveEnabled) {
242708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi            return mColors[mColors.length-1];
243708f7721e910d2c1fe984d2b8a6bf10565a00a61Jorim Jaggi        }
244dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        int thresh, color = 0;
245dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        for (int i=0; i<mColors.length; i+=2) {
246dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler            thresh = mColors[i];
247dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler            color = mColors[i+1];
248dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi            if (percent <= thresh) {
249dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi
250dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi                // Respect tinting for "normal" level
251dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi                if (i == mColors.length-2) {
252dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi                    return mIconTint;
253dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi                } else {
254dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi                    return color;
255dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi                }
256dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi            }
257dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        }
258dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        return color;
259dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    }
260dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
2615443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi    public void setDarkIntensity(float darkIntensity) {
2625443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi        int backgroundColor = getBackgroundColor(darkIntensity);
2635443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi        int fillColor = getFillColor(darkIntensity);
2645443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi        mIconTint = fillColor;
2655443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi        mFramePaint.setColor(backgroundColor);
266b07ce17328be90d61cde67e5ed43dd23f0c2fdd3Xiyuan Xia        mBoltPaint.setColor(fillColor);
2675443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi        mChargeColor = fillColor;
268dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi        invalidate();
269dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi    }
270dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi
2715443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi    private int getBackgroundColor(float darkIntensity) {
2725443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi        return getColorForDarkIntensity(
2735443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi                darkIntensity, mLightModeBackgroundColor, mDarkModeBackgroundColor);
2745443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi    }
2755443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi
2765443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi    private int getFillColor(float darkIntensity) {
2775443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi        return getColorForDarkIntensity(
2785443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi                darkIntensity, mLightModeFillColor, mDarkModeFillColor);
2795443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi    }
2805443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi
2815443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi    private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) {
2825443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi        return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor);
2835443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi    }
2845443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi
285dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    @Override
286dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    public void draw(Canvas c) {
2873c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock        BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;
2883c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock        final int level = tracker.level;
28905021e5e47132799efc3f8312fbca7cb20e6fc71Daniel Sandler
29005021e5e47132799efc3f8312fbca7cb20e6fc71Daniel Sandler        if (level == BatteryTracker.UNKNOWN_LEVEL) return;
29105021e5e47132799efc3f8312fbca7cb20e6fc71Daniel Sandler
292dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        float drawFrac = (float) level / 100f;
293dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        final int pt = getPaddingTop();
294dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        final int pl = getPaddingLeft();
295dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        final int pr = getPaddingRight();
296dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        final int pb = getPaddingBottom();
2974b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        final int height = mHeight - pt - pb;
2984b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        final int width = mWidth - pl - pr;
299dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
3005172dc2f36f9c417befd0957ce2ae20f9d083a88Jorim Jaggi        final int buttonHeight = (int) (height * mButtonHeightFraction);
301dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
302fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        mFrame.set(0, 0, width, height);
303fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        mFrame.offset(pl, pt);
304dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
3054b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        // button-frame: area above the battery body
306fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock        mButtonFrame.set(
307a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi                mFrame.left + Math.round(width * 0.25f),
308fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                mFrame.top,
309a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi                mFrame.right - Math.round(width * 0.25f),
3104b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock                mFrame.top + buttonHeight);
311cfab99005360819294c65280d8b695ab50c1f0b3John Spurlock
312a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi        mButtonFrame.top += mSubpixelSmoothingLeft;
313a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi        mButtonFrame.left += mSubpixelSmoothingLeft;
314a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi        mButtonFrame.right -= mSubpixelSmoothingRight;
315dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
3164b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        // frame: battery body area
3174b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mFrame.top += buttonHeight;
318a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi        mFrame.left += mSubpixelSmoothingLeft;
319a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi        mFrame.top += mSubpixelSmoothingLeft;
320a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi        mFrame.right -= mSubpixelSmoothingRight;
321a0221f3a9fb8014b24954a5a59e1e001e4a2619fJorim Jaggi        mFrame.bottom -= mSubpixelSmoothingRight;
322dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
3234b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        // set the battery charging color
3244b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mBatteryPaint.setColor(tracker.plugged ? mChargeColor : getColorForLevel(level));
325dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
326dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        if (level >= FULL) {
327dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler            drawFrac = 1f;
3283332ba54ae85df14d761447d86d2aa19d448ce11John Spurlock        } else if (level <= mCriticalLevel) {
329dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler            drawFrac = 0f;
330dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        }
331dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
3324b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        final float levelTop = drawFrac == 1f ? mButtonFrame.top
3334b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock                : (mFrame.top + (mFrame.height() * (1f - drawFrac)));
3344b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock
3354b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        // define the battery shape
3364b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mShapePath.reset();
3374b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mShapePath.moveTo(mButtonFrame.left, mButtonFrame.top);
3384b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mShapePath.lineTo(mButtonFrame.right, mButtonFrame.top);
3394b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mShapePath.lineTo(mButtonFrame.right, mFrame.top);
3404b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mShapePath.lineTo(mFrame.right, mFrame.top);
3414b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mShapePath.lineTo(mFrame.right, mFrame.bottom);
3424b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mShapePath.lineTo(mFrame.left, mFrame.bottom);
3434b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mShapePath.lineTo(mFrame.left, mFrame.top);
3444b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mShapePath.lineTo(mButtonFrame.left, mFrame.top);
3454b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top);
346dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler
347d19877f8a3557702d87bc9ed8c4be947e947b8b7John Spurlock        if (tracker.plugged) {
3484b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock            // define the bolt shape
349d8595fd8feca567d48c4e271070a75c576a7dfbfJohn Spurlock            final float bl = mFrame.left + mFrame.width() / 4.5f;
350d8595fd8feca567d48c4e271070a75c576a7dfbfJohn Spurlock            final float bt = mFrame.top + mFrame.height() / 6f;
351d8595fd8feca567d48c4e271070a75c576a7dfbfJohn Spurlock            final float br = mFrame.right - mFrame.width() / 7f;
352d8595fd8feca567d48c4e271070a75c576a7dfbfJohn Spurlock            final float bb = mFrame.bottom - mFrame.height() / 10f;
353fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock            if (mBoltFrame.left != bl || mBoltFrame.top != bt
354fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                    || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
355fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                mBoltFrame.set(bl, bt, br, bb);
356fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                mBoltPath.reset();
357fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                mBoltPath.moveTo(
358fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                        mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
359fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                        mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
360fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                for (int i = 2; i < mBoltPoints.length; i += 2) {
361fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                    mBoltPath.lineTo(
362fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                            mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
363fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                            mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
364fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                }
365fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                mBoltPath.lineTo(
366fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                        mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
367fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock                        mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
368fceb7edddeda6962212377cb88391d9affe5967aJohn Spurlock            }
3694b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock
3704b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock            float boltPct = (mBoltFrame.bottom - levelTop) / (mBoltFrame.bottom - mBoltFrame.top);
3714b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock            boltPct = Math.min(Math.max(boltPct, 0), 1);
3724b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock            if (boltPct <= BOLT_LEVEL_THRESHOLD) {
3734b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock                // draw the bolt if opaque
3744b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock                c.drawPath(mBoltPath, mBoltPaint);
3754b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock            } else {
3764b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock                // otherwise cut the bolt out of the overall shape
3774b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock                mShapePath.op(mBoltPath, Path.Op.DIFFERENCE);
3784b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock            }
3794b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        }
3804b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock
3811fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock        // compute percentage text
3821fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock        boolean pctOpaque = false;
3831fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock        float pctX = 0, pctY = 0;
3841fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock        String pctText = null;
385f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        if (!tracker.plugged && level > mCriticalLevel && mShowPercent) {
3861fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock            mTextPaint.setColor(getColorForLevel(level));
3871fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock            mTextPaint.setTextSize(height *
3881fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock                    (SINGLE_DIGIT_PERCENT ? 0.75f
3891fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock                            : (tracker.level == 100 ? 0.38f : 0.5f)));
3901fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock            mTextHeight = -mTextPaint.getFontMetrics().ascent;
3911fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock            pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);
3921fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock            pctX = mWidth * 0.5f;
3931fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock            pctY = (mHeight + mTextHeight) * 0.47f;
3941fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock            pctOpaque = levelTop > pctY;
3951fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock            if (!pctOpaque) {
3961fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock                mTextPath.reset();
3971fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock                mTextPaint.getTextPath(pctText, 0, pctText.length(), pctX, pctY, mTextPath);
3981fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock                // cut the percentage text out of the overall shape
3991fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock                mShapePath.op(mTextPath, Path.Op.DIFFERENCE);
4001fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock            }
4011fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock        }
4021fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock
4034b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        // draw the battery shape background
4044b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        c.drawPath(mShapePath, mFramePaint);
4054b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock
4064b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        // draw the battery shape, clipped to charging level
4074b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mFrame.top = levelTop;
4084b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mClipPath.reset();
4094b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mClipPath.addRect(mFrame,  Path.Direction.CCW);
4104b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        mShapePath.op(mClipPath, Path.Op.INTERSECT);
4114b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        c.drawPath(mShapePath, mBatteryPaint);
4124b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock
4134b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock        if (!tracker.plugged) {
4143332ba54ae85df14d761447d86d2aa19d448ce11John Spurlock            if (level <= mCriticalLevel) {
4154b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock                // draw the warning text
4164b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock                final float x = mWidth * 0.5f;
4174b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock                final float y = (mHeight + mWarningTextHeight) * 0.48f;
4184b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock                c.drawText(mWarningString, x, y, mWarningTextPaint);
4191fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock            } else if (pctOpaque) {
4204b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock                // draw the percentage text
4211fd0b933b86bbb20bad278de5c5c3fd268b4645bJohn Spurlock                c.drawText(pctText, pctX, pctY, mTextPaint);
4224b786ff4b519b4537adb5da7113fb7797c67e385John Spurlock            }
423dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler        }
424dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler    }
4253c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock
4260d266899da8f612194a8393ec69c4fd870f2f45cJorim Jaggi    @Override
4270d266899da8f612194a8393ec69c4fd870f2f45cJorim Jaggi    public boolean hasOverlappingRendering() {
4280d266899da8f612194a8393ec69c4fd870f2f45cJorim Jaggi        return false;
4290d266899da8f612194a8393ec69c4fd870f2f45cJorim Jaggi    }
4300d266899da8f612194a8393ec69c4fd870f2f45cJorim Jaggi
4313c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock    private boolean mDemoMode;
4323c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock    private BatteryTracker mDemoTracker = new BatteryTracker();
4333c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock
4343c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock    @Override
4353c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock    public void dispatchDemoCommand(String command, Bundle args) {
4363c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock        if (!mDemoMode && command.equals(COMMAND_ENTER)) {
4373c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock            mDemoMode = true;
4383c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock            mDemoTracker.level = mTracker.level;
4393c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock            mDemoTracker.plugged = mTracker.plugged;
4403c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock        } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
4413c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock            mDemoMode = false;
4423c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock            postInvalidate();
4433c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock        } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
4443c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock           String level = args.getString("level");
4453c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock           String plugged = args.getString("plugged");
4463c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock           if (level != null) {
4473c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock               mDemoTracker.level = Math.min(Math.max(Integer.parseInt(level), 0), 100);
4483c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock           }
4493c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock           if (plugged != null) {
4503c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock               mDemoTracker.plugged = Boolean.parseBoolean(plugged);
4513c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock           }
4523c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock           postInvalidate();
4533c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock        }
4543c87566c115e1fce3fdc09a6fe40488bcdc04cdeJohn Spurlock    }
455f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
456f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    private final class BatteryTracker extends BroadcastReceiver {
457f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        public static final int UNKNOWN_LEVEL = -1;
458f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
459f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        // current battery status
460f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        int level = UNKNOWN_LEVEL;
461f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        String percentStr;
462f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        int plugType;
463f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        boolean plugged;
464f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        int health;
465f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        int status;
466f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        String technology;
467f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        int voltage;
468f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        int temperature;
469f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        boolean testmode = false;
470f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
471f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        @Override
472f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        public void onReceive(Context context, Intent intent) {
473f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock            final String action = intent.getAction();
474f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
475f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                if (testmode && ! intent.getBooleanExtra("testmode", false)) return;
476f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
477f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                level = (int)(100f
478f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
479f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
480f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
481f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
482f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                plugged = plugType != 0;
483f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,
484f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        BatteryManager.BATTERY_HEALTH_UNKNOWN);
485f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
486f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        BatteryManager.BATTERY_STATUS_UNKNOWN);
487f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);
488f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);
489f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
490f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
491f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                setContentDescription(
492f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        context.getString(R.string.accessibility_battery_level, level));
493f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                postInvalidate();
494f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock            } else if (action.equals(ACTION_LEVEL_TEST)) {
495f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                testmode = true;
496f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                post(new Runnable() {
497f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                    int curLevel = 0;
498f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                    int incr = 1;
499f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                    int saveLevel = level;
500f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                    int savePlugged = plugType;
501f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                    Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
502f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                    @Override
503f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                    public void run() {
504f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        if (curLevel < 0) {
505f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                            testmode = false;
506f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                            dummy.putExtra("level", saveLevel);
507f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                            dummy.putExtra("plugged", savePlugged);
508f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                            dummy.putExtra("testmode", false);
509f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        } else {
510f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                            dummy.putExtra("level", curLevel);
511f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                            dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC
512f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                                    : 0);
513f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                            dummy.putExtra("testmode", true);
514f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        }
515f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        getContext().sendBroadcast(dummy);
516f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
517f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        if (!testmode) return;
518f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
519f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        curLevel += incr;
520f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        if (curLevel == 100) {
521f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                            incr *= -1;
522f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        }
523f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                        postDelayed(this, 200);
524f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                    }
525f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock                });
526f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock            }
527f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        }
528f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    }
529f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
530f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    private final class SettingObserver extends ContentObserver {
531f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        public SettingObserver() {
532f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock            super(new Handler());
533f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        }
534f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
535f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        @Override
536f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        public void onChange(boolean selfChange, Uri uri) {
537f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock            super.onChange(selfChange, uri);
538f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock            updateShowPercent();
539f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock            postInvalidate();
540f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock        }
541f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock    }
542f40d08f8a3094ac5d5478efc5de0b96f57d2c5dfJohn Spurlock
543dfaf3bdeeb21c8421074c70d82642612bcdd8e98Daniel Sandler}
544