低电量提醒

概览

PowerUI继承于SystemUI,用于电源管理,目前承载了电量提醒和温度提醒的业务内容:

电量提醒目前有三个阶段,android使用了bucket(木桶)的概念,来表示当前处于哪个阶段。当然这三个阶段的具体范围,我们可以自由定制,可以从用户自定义获取(Settings数据库),也可以使用默认的参数(res资源)

确认了这三个阶段之后,在电量变化的时候我们会获取当前电池信息,来判别到达哪个阶段,然后做对应的处理
简单来说:

  • 电量低于7,弹对话框警示
  • 电量低于10,弹通知提示(区分正常模式和游戏模式)

当然在实际的过程中,我们还会考虑一些交互细节去做更细致的处理,例如充电过程不提醒,超级省电模式不提醒,插入充电器要取消通知,游戏模式不打扰用吐司替换通知等等,这里不再细讲。

温度提醒很简单,接收触发的广播,弹框通知,OVER

正文

UI处理

电量处理涉及两个类:

1
2
com.android.systemui.power.PowerUI
com.android.systemui.power.PowerNotificationWarnings#PowerNotificationWarnings

处理UI更新的是com.android.systemui.power.PowerUI.WarningUI
只有一个实现类com.android.systemui.power.PowerNotificationWarnings,负责以下功能:

1
2
3
4
5
6
7
8
9
10
11
public interface WarningsUI {
void update(int batteryLevel, int bucket, long screenOffTime);
void dismissLowBatteryWarning();
void showLowBatteryWarning(boolean playSound/* flyme extends */, boolean showToast);
void dismissInvalidChargerWarning();
void showInvalidChargerWarning();
void updateLowBatteryWarning();
boolean isInvalidChargerWarningShowing();
void dump(PrintWriter pw);
void userSwitched();
}

Bucket

接下来我们看一下bucket的概念

android定义了bucket木桶的概念,用来表示当前处于哪个状态,具体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* Buckets the battery level.
*
* The code in this function is a little weird because I couldn't comprehend
* the bucket going up when the battery level was going down. --joeo
*
* 1 means that the battery is "ok"
* 0 means that the battery is between "ok" and what we should warn about.
* less than 0 means that the battery is low
*/
private int findBatteryLevelBucket(int level) {
if (level >= mLowBatteryAlertCloseLevel) {
return 1;
}
if (level > mLowBatteryReminderLevels[0]) {
return 0;
}
final int N = mLowBatteryReminderLevels.length;
// 为啥要倒着循环,-2,,1
// 因为要比较小的话,大范围会包含小范围,所以从小到大确认
// 当电池电量越来越低,木桶也越来越小,不同的电量临界,代表切换不同的木桶,也就是切换模式
for (int i=N-1; i>=0; i--) {
if (level <= mLowBatteryReminderLevels[i]) {
return -1-i;
}
}
throw new RuntimeException("not possible!");
}

不同的状态就是我们之前说的不同阶段,其中,这几个阶段Android原生是可以定义的,flyme直接配置了相关的值, 见以下内容

1
2
3
4
5
6
7
8
9
10
11
12
int warnLevel = mContext.getResources().getInteger(R.integer.config_mz_lowBatteryWarningLevel) - 1;
int critLevel = mContext.getResources().getInteger(R.integer.config_mz_criticalBatteryWarningLevel) - 1;
mLowBatteryReminderLevels[0] = warnLevel;
mLowBatteryReminderLevels[1] = critLevel;
mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
+ mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningBump);

// 警告电量30-1=29
int warnLevel = mContext.getResources().getInteger(R.integer.config_mz_lowBatteryWarningLevel) - 1;
// 危险电量10-1=9
int critLevel = mContext.getResources().getInteger(R.integer.config_mz_criticalBatteryWarningLevel) - 1;
  • mLowBatteryAlertCloseLevel 表示接近warnLevel,我们将这个阈值在warnLeve基础上提高了5
  • mLowBatteryReminderLevels[0]warnLevel,30-1 = 29
  • mLowBatteryReminderLevels[1]critLeve,10-1 = 9

知晓了这几个阈值,对应上边的findBatteryLevelBucket()方法,我们可以发现bucket是用来干嘛的

  • level >= 35,返回1,表示ok
  • level > 29, 返回0,表示应该警告
  • level <= 29,返回-1,表示到达低电量
  • level <= 9,返回-2,表示达到警示电量

弄清楚这几个阶段后就可以去看代码中对应的逻辑处理了。

这里翻译一下最复杂的那个判断条件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
!plugged
&& (!isPowerSaver || bucket < -1)
&& !superPowerSaveEnabled
&& (bucket < oldBucket || oldPlugged)
&& mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
&& bucket < mNotifyBucket)

/*
如果当前不在充电状态
且 非省电模式或者处于警告电量木桶
且 非极限省电模式
且 木桶降低或者原来在充电
且 电池状态良好
且 当前处于警告危险电量
*/

广播信息

电量提醒很大一部分内容都是在监听广播获取信息然后触发对应的操作
以下是要监听的广播:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
filter.addAction(Intent.ACTION_BATTERY_CHANGED); // 电量变化广播
filter.addAction(Intent.ACTION_SCREEN_OFF); //
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
// Flyme Shi.Yingchun@shell,add for Flyme {@
filter.addAction(Intent.ACTION_POWER_CONNECTED);
//archermind:mengxuan.diao[#565728] modified{@
filter.addAction(MZ_ACTION_BATTERY_LOW_WRANING_DIALOG);
//@}
filter.addAction(Intent.ACTION_BATTERY_OKAY);
// @}

//Flyme:chenjianbo@SHELL.Feature.UsbChargeTemperatureAlert
filter.addAction(mzAlertIntentAction);

ACTION_BATTERY_CHANGED,电池信息广播

电量变化广播:ACTION_BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED"
这个广播可以附带15个字段,报告当前电池的一些信息,具体请看android.os.BatteryManager,我们只来分析我们用到的这5个

  • EXTRA_LEVEL:level,电池电量,int值,可取值为从0到EXTRA_SCALE
  • EXTRA_STATUS:status,电池状态,int值,可取值为unknown,charging,discharging,not_charging,full(android.os.BatteryManager#BATTERY_STATUS_UNKNOWN)
  • EXTRA_PLUGGED:plugged,当前使用哪种电源,0电池,1Ac,2USb,3无线
  • EXTRA_INVALID_CHARGER:invalid_charger,非0值表示不被支持的充电器
  • EXTRA_SCALE:scale,integer containing the maximum battery level.表示最大电量是多少,和leve的比例作为当前电量的显示接收到这个广播会处理以上状态,然后使用WarningUI来更新UI

ACTION_SCREEN_OFF | ACTION_SCREEN_ON,灭屏|亮屏广播

记录当前灭屏的时间点,有两个用途

  1. 如果灭屏持续时间太长,就不播放声音打扰用户。LOW_BATTERY_SOUND_TIMEOUT记录默认的阈值,超过阈值不打扰用户
  2. 如果温控弹框弹出,灭屏会关掉弹框

ACTION_USER_SWITCHED,用户切换广播

切换用户,使用WarningUI更新通知

ACTION_POWER_SAVE_MODE_CHANGING | ACTION_POWER_SAVE_MODE_CHANGED 省电模式正在切换|切换完成广播

暂未使用

MZ_ACTION_BATTERY_LOW_WRANING_DIALOG,低电量(小于8%)对话框广播

电量小于8,会弹出对话框,强打扰模式

ACTION_POWER_CONNECTED,在电源插入的时候触发,只有这种条件会触发

播放声音,并关闭低电量对话框

ACTION_BATTERY_OKAY,在进入低电量然后恢复回来的时候发送

关闭低电量对话框

mzAlertIntentAction,温控弹框

是否到达温控限制,播放alram声音,并弹框

声音

PowerUI会在合适的时间播放声音,总有三种类型的声音:
LOW_BATTERY_SOUND,PLUDIN_SOUND,CHARGED_SOUND

如果当前状态status为满电,之前非满电,且当前为电源插入状态,播放充满电声音
然后更新view的一些参数(View为PowerNotificationWarnings.java)

作者

0xforee

发布于

2017-08-07

更新于

2017-08-07

许可协议


欢迎关注我的公众号 0xforee,第一时间获取更多有价值的思考

评论