效果图
华为手机顶部状态栏
我们客制化后最终效果
实现步骤
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()); } }