OSDN Git Service

Merge "Allow soft AP settings config before bring up" into gingerbread
[android-x86/packages-apps-Settings.git] / src / com / android / settings / fuelgauge / BatteryHistoryChart.java
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.settings.fuelgauge;
18
19 import com.android.settings.R;
20
21 import android.content.Context;
22 import android.content.res.ColorStateList;
23 import android.content.res.TypedArray;
24 import android.graphics.Canvas;
25 import android.graphics.Paint;
26 import android.graphics.Path;
27 import android.graphics.Typeface;
28 import android.os.BatteryStats;
29 import android.os.SystemClock;
30 import android.os.BatteryStats.HistoryItem;
31 import android.text.TextPaint;
32 import android.util.AttributeSet;
33 import android.util.TypedValue;
34 import android.view.View;
35
36 import java.util.ArrayList;
37
38 public class BatteryHistoryChart extends View {
39     static final int SANS = 1;
40     static final int SERIF = 2;
41     static final int MONOSPACE = 3;
42
43     static final int BATTERY_WARN = 29;
44     static final int BATTERY_CRITICAL = 14;
45     
46     final Paint mBatteryBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
47     final Paint mBatteryGoodPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
48     final Paint mBatteryWarnPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
49     final Paint mBatteryCriticalPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
50     final Paint mChargingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
51     final Paint mScreenOnPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
52     final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
53     
54     final Path mBatLevelPath = new Path();
55     final Path mBatGoodPath = new Path();
56     final Path mBatWarnPath = new Path();
57     final Path mBatCriticalPath = new Path();
58     final Path mChargingPath = new Path();
59     final Path mScreenOnPath = new Path();
60     
61     int mFontSize;
62     
63     BatteryStats mStats;
64     long mStatsPeriod;
65     String mDurationString;
66     
67     int mChargingOffset;
68     int mScreenOnOffset;
69     int mLevelOffset;
70     
71     int mTextAscent;
72     int mTextDescent;
73     int mDurationStringWidth;
74     
75     int mNumHist;
76     BatteryStats.HistoryItem mHistFirst;
77     long mHistStart;
78     long mHistEnd;
79     int mBatLow;
80     int mBatHigh;
81     
82     public BatteryHistoryChart(Context context, AttributeSet attrs) {
83         super(context, attrs);
84         
85         mBatteryBackgroundPaint.setARGB(255, 128, 128, 128);
86         mBatteryBackgroundPaint.setStyle(Paint.Style.FILL);
87         mBatteryGoodPaint.setARGB(128, 0, 255, 0);
88         int lineWidth = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
89                 2, getResources().getDisplayMetrics());
90         if (lineWidth <= 0) lineWidth = 1;
91         mBatteryGoodPaint.setStrokeWidth(lineWidth);
92         mBatteryGoodPaint.setStyle(Paint.Style.STROKE);
93         mBatteryWarnPaint.setARGB(128, 255, 255, 0);
94         mBatteryWarnPaint.setStrokeWidth(lineWidth);
95         mBatteryWarnPaint.setStyle(Paint.Style.STROKE);
96         mBatteryCriticalPaint.setARGB(192, 255, 0, 0);
97         mBatteryCriticalPaint.setStrokeWidth(lineWidth);
98         mBatteryCriticalPaint.setStyle(Paint.Style.STROKE);
99         mChargingPaint.setARGB(255, 0, 128, 0);
100         mChargingPaint.setStrokeWidth(lineWidth);
101         mChargingPaint.setStyle(Paint.Style.STROKE);
102         mScreenOnPaint.setARGB(255, 0, 0, 255);
103         mScreenOnPaint.setStrokeWidth(lineWidth);
104         mScreenOnPaint.setStyle(Paint.Style.STROKE);
105         
106         mScreenOnOffset = lineWidth;
107         mChargingOffset = lineWidth*2;
108         mLevelOffset = lineWidth*3;
109         
110         mTextPaint.density = getResources().getDisplayMetrics().density;
111         mTextPaint.setCompatibilityScaling(
112                 getResources().getCompatibilityInfo().applicationScale);
113         
114         TypedArray a =
115             context.obtainStyledAttributes(
116                 attrs, R.styleable.BatteryHistoryChart, 0, 0);
117         
118         ColorStateList textColor = null;
119         int textSize = 15;
120         int typefaceIndex = -1;
121         int styleIndex = -1;
122         
123         TypedArray appearance = null;
124         int ap = a.getResourceId(R.styleable.BatteryHistoryChart_android_textAppearance, -1);
125         if (ap != -1) {
126             appearance = context.obtainStyledAttributes(ap,
127                                 com.android.internal.R.styleable.
128                                 TextAppearance);
129         }
130         if (appearance != null) {
131             int n = appearance.getIndexCount();
132             for (int i = 0; i < n; i++) {
133                 int attr = appearance.getIndex(i);
134
135                 switch (attr) {
136                 case com.android.internal.R.styleable.TextAppearance_textColor:
137                     textColor = appearance.getColorStateList(attr);
138                     break;
139
140                 case com.android.internal.R.styleable.TextAppearance_textSize:
141                     textSize = appearance.getDimensionPixelSize(attr, textSize);
142                     break;
143
144                 case com.android.internal.R.styleable.TextAppearance_typeface:
145                     typefaceIndex = appearance.getInt(attr, -1);
146                     break;
147
148                 case com.android.internal.R.styleable.TextAppearance_textStyle:
149                     styleIndex = appearance.getInt(attr, -1);
150                     break;
151                 }
152             }
153
154             appearance.recycle();
155         }
156         
157         int shadowcolor = 0;
158         float dx=0, dy=0, r=0;
159         
160         int n = a.getIndexCount();
161         for (int i = 0; i < n; i++) {
162             int attr = a.getIndex(i);
163
164             switch (attr) {
165                 case R.styleable.BatteryHistoryChart_android_shadowColor:
166                     shadowcolor = a.getInt(attr, 0);
167                     break;
168
169                 case R.styleable.BatteryHistoryChart_android_shadowDx:
170                     dx = a.getFloat(attr, 0);
171                     break;
172
173                 case R.styleable.BatteryHistoryChart_android_shadowDy:
174                     dy = a.getFloat(attr, 0);
175                     break;
176
177                 case R.styleable.BatteryHistoryChart_android_shadowRadius:
178                     r = a.getFloat(attr, 0);
179                     break;
180
181                 case R.styleable.BatteryHistoryChart_android_textColor:
182                     textColor = a.getColorStateList(attr);
183                     break;
184
185                 case R.styleable.BatteryHistoryChart_android_textSize:
186                     textSize = a.getDimensionPixelSize(attr, textSize);
187                     break;
188
189                 case R.styleable.BatteryHistoryChart_android_typeface:
190                     typefaceIndex = a.getInt(attr, typefaceIndex);
191                     break;
192
193                 case R.styleable.BatteryHistoryChart_android_textStyle:
194                     styleIndex = a.getInt(attr, styleIndex);
195                     break;
196             }
197         }
198         
199         mTextPaint.setColor(textColor.getDefaultColor());
200         mTextPaint.setTextSize(textSize);
201         
202         Typeface tf = null;
203         switch (typefaceIndex) {
204             case SANS:
205                 tf = Typeface.SANS_SERIF;
206                 break;
207
208             case SERIF:
209                 tf = Typeface.SERIF;
210                 break;
211
212             case MONOSPACE:
213                 tf = Typeface.MONOSPACE;
214                 break;
215         }
216         
217         setTypeface(tf, styleIndex);
218         
219         if (shadowcolor != 0) {
220             mTextPaint.setShadowLayer(r, dx, dy, shadowcolor);
221         }
222     }
223     
224     public void setTypeface(Typeface tf, int style) {
225         if (style > 0) {
226             if (tf == null) {
227                 tf = Typeface.defaultFromStyle(style);
228             } else {
229                 tf = Typeface.create(tf, style);
230             }
231
232             mTextPaint.setTypeface(tf);
233             // now compute what (if any) algorithmic styling is needed
234             int typefaceStyle = tf != null ? tf.getStyle() : 0;
235             int need = style & ~typefaceStyle;
236             mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
237             mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
238         } else {
239             mTextPaint.setFakeBoldText(false);
240             mTextPaint.setTextSkewX(0);
241             mTextPaint.setTypeface(tf);
242         }
243     }
244     
245     void setStats(BatteryStats stats) {
246         mStats = stats;
247         
248         long uSecTime = mStats.computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000,
249                 BatteryStats.STATS_SINCE_CHARGED);
250         mStatsPeriod = uSecTime;
251         String durationString = Utils.formatElapsedTime(getContext(), mStatsPeriod / 1000);
252         mDurationString = getContext().getString(R.string.battery_stats_on_battery,
253                 durationString);
254         
255         BatteryStats.HistoryItem rec = stats.getHistory();
256         mHistFirst = null;
257         int pos = 0;
258         int lastInteresting = 0;
259         byte lastLevel = -1;
260         mBatLow = 0;
261         mBatHigh = 100;
262         while (rec != null) {
263             pos++;
264             if (rec.cmd == HistoryItem.CMD_UPDATE) {
265                 if (mHistFirst == null) {
266                     mHistFirst = rec;
267                     mHistStart = rec.time;
268                 }
269                 if (rec.batteryLevel != lastLevel || pos == 1) {
270                     lastLevel = rec.batteryLevel;
271                     lastInteresting = pos;
272                     mHistEnd = rec.time;
273                 }
274             }
275             rec = rec.next;
276         }
277         mNumHist = lastInteresting;
278         
279         if (mHistEnd <= mHistStart) mHistEnd = mHistStart+1;
280     }
281
282     @Override
283     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
284         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
285         mDurationStringWidth = (int)mTextPaint.measureText(mDurationString);
286         mTextAscent = (int)mTextPaint.ascent();
287         mTextDescent = (int)mTextPaint.descent();
288     }
289
290     void finishPaths(int w, int h, int levelh, int startX, int y, Path curLevelPath,
291             int lastX, boolean lastCharging, boolean lastScreenOn, Path lastPath) {
292         if (curLevelPath != null) {
293             if (lastX >= 0 && lastX < w) {
294                 if (lastPath != null) {
295                     lastPath.lineTo(w, y);
296                 }
297                 curLevelPath.lineTo(w, y);
298             }
299             curLevelPath.lineTo(w, levelh);
300             curLevelPath.lineTo(startX, levelh);
301             curLevelPath.close();
302         }
303         
304         if (lastCharging && lastX < w) {
305             mChargingPath.lineTo(w, h-mChargingOffset);
306         }
307         if (lastScreenOn && lastX < w) {
308             mScreenOnPath.lineTo(w, h-mScreenOnOffset);
309         }
310     }
311     
312     @Override
313     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
314         super.onSizeChanged(w, h, oldw, oldh);
315         
316         mBatLevelPath.reset();
317         mBatGoodPath.reset();
318         mBatWarnPath.reset();
319         mBatCriticalPath.reset();
320         mScreenOnPath.reset();
321         mChargingPath.reset();
322         
323         final long timeStart = mHistStart;
324         final long timeChange = mHistEnd-mHistStart;
325         
326         final int batLow = mBatLow;
327         final int batChange = mBatHigh-mBatLow;
328         
329         final int levelh = h - mLevelOffset;
330         
331         BatteryStats.HistoryItem rec = mHistFirst;
332         int x = 0, y = 0, startX = 0, lastX = -1, lastY = -1;
333         int i = 0;
334         Path curLevelPath = null;
335         Path lastLinePath = null;
336         boolean lastCharging = false, lastScreenOn = false;
337         final int N = mNumHist;
338         while (rec != null && i < N) {
339             if (rec.cmd == BatteryStats.HistoryItem.CMD_UPDATE) {
340                 x = (int)(((rec.time-timeStart)*w)/timeChange);
341                 y = levelh - ((rec.batteryLevel-batLow)*(levelh-1))/batChange;
342                 
343                 if (lastX != x) {
344                     // We have moved by at least a pixel.
345                     if (lastY != y) {
346                         // Don't plot changes within a pixel.
347                         Path path;
348                         byte value = rec.batteryLevel;
349                         if (value <= BATTERY_CRITICAL) path = mBatCriticalPath;
350                         else if (value <= BATTERY_WARN) path = mBatWarnPath;
351                         else path = mBatGoodPath;
352                         
353                         if (path != lastLinePath) {
354                             if (lastLinePath != null) {
355                                 lastLinePath.lineTo(x, y);
356                             }
357                             path.moveTo(x, y);
358                             lastLinePath = path;
359                         } else {
360                             path.lineTo(x, y);
361                         }
362                         
363                         if (curLevelPath == null) {
364                             curLevelPath = mBatLevelPath;
365                             curLevelPath.moveTo(x, y);
366                             startX = x;
367                         } else {
368                             curLevelPath.lineTo(x, y);
369                         }
370                         lastX = x;
371                         lastY = y;
372                         
373                         final boolean charging =
374                             (rec.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0;
375                         if (charging != lastCharging) {
376                             if (charging) {
377                                 mChargingPath.moveTo(x, h-mChargingOffset);
378                             } else {
379                                 mChargingPath.lineTo(x, h-mChargingOffset);
380                             }
381                             lastCharging = charging;
382                         }
383                         
384                         final boolean screenOn =
385                             (rec.states&HistoryItem.STATE_SCREEN_ON_FLAG) != 0;
386                         if (screenOn != lastScreenOn) {
387                             if (screenOn) {
388                                 mScreenOnPath.moveTo(x, h-mScreenOnOffset);
389                             } else {
390                                 mScreenOnPath.lineTo(x, h-mScreenOnOffset);
391                             }
392                             lastScreenOn = screenOn;
393                         }
394                     }
395                 }
396                 
397             } else if (curLevelPath != null) {
398                 finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastX,
399                         lastCharging, lastScreenOn, lastLinePath);
400                 lastX = lastY = -1;
401                 curLevelPath = null;
402                 lastLinePath = null;
403                 lastCharging = lastScreenOn = false;
404             }
405             
406             rec = rec.next;
407             i++;
408         }
409         
410         finishPaths(w, h, levelh, startX, lastY, curLevelPath, lastX,
411                 lastCharging, lastScreenOn, lastLinePath);
412     }
413     
414     @Override
415     protected void onDraw(Canvas canvas) {
416         super.onDraw(canvas);
417         
418         final int width = getWidth();
419         final int height = getHeight();
420         
421         canvas.drawPath(mBatLevelPath, mBatteryBackgroundPaint);
422         canvas.drawText(mDurationString, (width/2) - (mDurationStringWidth/2),
423                 (height/2) - ((mTextDescent-mTextAscent)/2) - mTextAscent, mTextPaint);
424         if (!mBatGoodPath.isEmpty()) {
425             canvas.drawPath(mBatGoodPath, mBatteryGoodPaint);
426         }
427         if (!mBatWarnPath.isEmpty()) {
428             canvas.drawPath(mBatWarnPath, mBatteryWarnPaint);
429         }
430         if (!mBatCriticalPath.isEmpty()) {
431             canvas.drawPath(mBatCriticalPath, mBatteryCriticalPaint);
432         }
433         if (!mChargingPath.isEmpty()) {
434             canvas.drawPath(mChargingPath, mChargingPaint);
435         }
436         if (!mScreenOnPath.isEmpty()) {
437             canvas.drawPath(mScreenOnPath, mScreenOnPaint);
438         }
439     }
440 }