效果图
华为手机顶部状态栏

我们客制化后最终效果

实现步骤
1、获取蓝牙设备连接成功后的电量值
2、跟踪蓝牙图标显示流程
3、制作蓝牙带电量图标
4、获取电量后显示对应电量值图标
文件修改清单
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable/ic_bluetooth_connected_super.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_0.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_1.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_2.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_3.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_4.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_5.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
除了 PhoneStatusBarPolicy.java 之外其余都是新增蓝牙图标相关资源文件,资源文件不再贴出,可以去这里下载
资源文件选用 xml path 绘制,好处就是能跟随系统状态栏切换背景色,如果采用 png 图片将需要处理监听状态栏底色改变
读取已连接蓝牙设备电量值,在网上搜索后找到了一个工具类,简单试了下发现可用,通过反射调用 BluetoothDevice 的
getBatteryLevel 方法获取电量值
private int getBluetoothDeviceBattery(){
int battery = -1;
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
Class<BluetoothAdapter> bluetoothAdapterClass = BluetoothAdapter.class;
try {
Method method = bluetoothAdapterClass.getDeclaredMethod("getConnectionState", (Class[]) null);
method.setAccessible(true);
int state = (int) method.invoke(btAdapter, (Object[]) null);
if (state == BluetoothAdapter.STATE_CONNECTED) {
Set<BluetoothDevice> devices = btAdapter.getBondedDevices();
for (BluetoothDevice device : devices) {
Method batteryMethod = BluetoothDevice.class.getDeclaredMethod("getBatteryLevel", (Class[]) null);
batteryMethod.setAccessible(true);
Method isConnectedMethod = BluetoothDevice.class.getDeclaredMethod("isConnected", (Class[]) null);
isConnectedMethod.setAccessible(true);
boolean isConnected = (boolean) isConnectedMethod.invoke(device, (Object[]) null);
int level = (int) batteryMethod.invoke(device, (Object[]) null);
if (device != null && level > 0 && isConnected) {
String deviceName = device .getName();
battery = level;
android.util.Log.i(TAG, "Connected Bluetooth="+deviceName + " battery="+level);
}
}
} else {
android.util.Log.e(TAG, "No Connected Bluetooth Devices Found");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
return battery;
}
}
电量值为 -1 说明蓝牙设备不支持读取电量
其实后来通过跟踪系统蓝牙连接界面显示电量值逻辑,最终找到的也是调用此方法获取电量
frameworks\base\packages\SettingsLib\src\com\android\settingslib\bluetooth\CachedBluetoothDevice.java
/**
* Return summary that describes connection state of this device. Summary depends on:
* 1. Whether device has battery info
* 2. Whether device is in active usage(or in phone call)
*
* @param shortSummary {@code true} if need to return short version summary
*/
public String getConnectionSummary(boolean shortSummary) {
boolean profileConnected = false; // Updated as long as BluetoothProfile is connected
boolean a2dpConnected = true; // A2DP is connected
boolean hfpConnected = true; // HFP is connected
boolean hearingAidConnected = true; // Hearing Aid is connected
int leftBattery = -1;
int rightBattery = -1;
.....
String batteryLevelPercentageString = null;
// Android framework should only set mBatteryLevel to valid range [0-100] or
// BluetoothDevice.BATTERY_LEVEL_UNKNOWN, any other value should be a framework bug.
// Thus assume here that if value is not BluetoothDevice.BATTERY_LEVEL_UNKNOWN, it must
// be valid
final int batteryLevel = getBatteryLevel();
if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
// TODO: name com.android.settingslib.bluetooth.Utils something different
batteryLevelPercentageString =
com.android.settingslib.Utils.formatPercentage(batteryLevel);
}
int stringRes = R.string.bluetooth_pairing;
//when profile is connected, information would be available
if (profileConnected) {
// Update Meta data for connected device
if (BluetoothUtils.getBooleanMetaData(
mDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) {
leftBattery = BluetoothUtils.getIntMetaData(mDevice,
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY);
rightBattery = BluetoothUtils.getIntMetaData(mDevice,
BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY);
}
......
}
/**
* Get battery level from remote device
* @return battery level in percentage [0-100], or {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
*/
public int getBatteryLevel() {
//android.bluetooth.BluetoothDevice mDevice;
return mDevice.getBatteryLevel();
}
通过跟踪发现控制蓝牙图标显示代码在 PhoneStatusBarPolicy.java 中,只有当蓝牙设备连接成功后蓝牙图标才会显示,
默认用的资源文件为 stat_sys_data_bluetooth_connected.xml,我们就可以在这块做调整,当蓝牙设备连接成功后读取电量值,
根据电量值获取对应电池格数资源文件就ok了。
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@Override
public void onBluetoothStateChange(boolean enabled) {
updateBluetooth();
}
private final void updateBluetooth() {
//int iconId = R.drawable.stat_sys_data_bluetooth_connected;
int iconId = R.drawable.ic_bluetooth_connected_super;//cczheng change
String contentDescription =
mContext.getString(R.string.accessibility_quick_settings_bluetooth_on);
boolean bluetoothVisible = false;
if (mBluetooth != null) {
if (mBluetooth.isBluetoothConnected()) {
contentDescription = mContext.getString(R.string.accessibility_bluetooth_connected);
bluetoothVisible = mBluetooth.isBluetoothEnabled();
}
}
//20200602 cczheng add
if (bluetoothVisible) {
int battery = getBluetoothDeviceBattery();
if (battery > 0) {
iconId = getBatteryLevelIconId(battery);
}
}//E
mIconController.setIcon(mSlotBluetooth, iconId, contentDescription);
mIconController.setIconVisibility(mSlotBluetooth, bluetoothVisible);
}
//20200602 cczheng add for show bt headset battery S
private int getBatteryLevelIconId(int battery){
int iconId = 0;
if (battery <= 10) {
iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_0;
}else if (battery <= 20) {
iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_1;
}else if (battery <= 40) {
iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_2;
}else if (battery <= 60) {
iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_3;
}else if (battery <= 80) {
iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_4;
}else if (battery <= 100) {
iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_5;
}
Log.e(TAG, "iconId="+iconId);
return iconId;
}
private int getBluetoothDeviceBattery(){
int battery = -1;
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
Class<BluetoothAdapter> bluetoothAdapterClass = BluetoothAdapter.class;
try {
Method method = bluetoothAdapterClass.getDeclaredMethod("getConnectionState", (Class[]) null);
method.setAccessible(true);
int state = (int) method.invoke(btAdapter, (Object[]) null);
if (state == BluetoothAdapter.STATE_CONNECTED) {
Set<BluetoothDevice> devices = btAdapter.getBondedDevices();
for (BluetoothDevice device : devices) {
Method batteryMethod = BluetoothDevice.class.getDeclaredMethod("getBatteryLevel", (Class[]) null);
batteryMethod.setAccessible(true);
Method isConnectedMethod = BluetoothDevice.class.getDeclaredMethod("isConnected", (Class[]) null);
isConnectedMethod.setAccessible(true);
boolean isConnected = (boolean) isConnectedMethod.invoke(device, (Object[]) null);
int level = (int) batteryMethod.invoke(device, (Object[]) null);
if (device != null && level > 0 && isConnected) {
String deviceName = device .getName();
battery = level;
android.util.Log.i(TAG, "Connected Bluetooth="+deviceName + " battery="+level);
}
}
} else {
android.util.Log.e(TAG, "No Connected Bluetooth Devices Found");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
return battery;
}
}
//20200602 cczheng add for show bt headset battery E
private final void updateTTY() {
TelecomManager telecomManager =
(TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
if (telecomManager == null) {
updateTTY(TelecomManager.TTY_MODE_OFF);
} else {
updateTTY(telecomManager.getCurrentTtyMode());
}
}