Android8.0 在settings中添加蓝牙耳机的电池电量信息

分析:
1.获取蓝牙耳机电池电量
参考之前的文章 获取蓝牙耳机电池电量

2.分析Bluetooth settings布局
packages/apps/Settigns/src/com/android/settings/bluetooth/BluetoothSettings.java
进入蓝牙界面就是这里。这里怎么加载的还没弄清楚,主要修改的也不是这里。mPairedDevicesCategory就是配对的设备。

主要看BluetoothDevicePreference.java它就是你所看到的每一条蓝牙记录,就是list中的item。包括配对和发现的设备,都是这个。我们就是修改这个,在最右边的设置齿轮那里加上蓝牙耳机的电池电量。

BluetoothDevicePreference继承GearPreference,GearPreference继承RestrictedPreference,RestrictedPreference继承TwoTargetPreference,TwoTargetPreference继承Preference。
直接看TwoTargetPreference的布局preference_two_target.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
  Copyright (C) 2017 The Android Open Source Project

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  -->

<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical"
    android:background="@android:color/transparent"
    android:clipToPadding="false">

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:background="?android:attr/selectableItemBackground"
        android:gravity="start|center_vertical"
        android:paddingStart="?android:attr/listPreferredItemPaddingStart">

        <LinearLayout
            android:id="@+id/icon_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:minWidth="56dp"
            android:orientation="horizontal"
            android:paddingEnd="12dp"
            android:paddingTop="4dp"
            android:paddingBottom="4dp">
            <com.android.internal.widget.PreferenceImageView
                android:id="@android:id/icon"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:maxWidth="48dp"
                android:maxHeight="48dp" />
        </LinearLayout>

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:paddingTop="16dp"
            android:paddingBottom="16dp">

            <TextView
                android:id="@android:id/title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:singleLine="true"
                android:textAppearance="?android:attr/textAppearanceListItem"
                android:ellipsize="marquee" />

            <TextView
                android:id="@android:id/summary"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@android:id/title"
                android:layout_alignStart="@android:id/title"
                android:textAppearance="?android:attr/textAppearanceListItemSecondary"
                android:textColor="?android:attr/textColorSecondary"
                android:maxLines="10" />

        </RelativeLayout>

    </LinearLayout>

    <include layout="@layout/preference_two_target_divider" />

    <!-- Preference should place its actual preference widget here. -->
    <LinearLayout
        android:id="@android:id/widget_frame"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:minWidth="64dp"
        android:gravity="center"
        android:orientation="vertical" />

</LinearLayout>

只看widget_frame,这里是留给后面子类定制的。

TwoTargetPreference.java中的setWidgetLayoutResource就是去设置widget_frame,首先会判断子类是否有重写,没有就不去设置。我们这边的BluetoothDevicePreference是最后的子类,重写了getSecondTargetResId()方法,所以最终会把BluetoothDevicePreference设置的布局加载进去。我们只需要修改BluetoothDevicePreference中的getSecondTargetResId()。把我们要的布局放进去。

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib;

import android.content.Context;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.view.View;

public class TwoTargetPreference extends Preference {

    public TwoTargetPreference(Context context, AttributeSet attrs,
            int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    public TwoTargetPreference(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public TwoTargetPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TwoTargetPreference(Context context) {
        super(context);
        init();
    }

    private void init() {
        setLayoutResource(R.layout.preference_two_target);
        final int secondTargetResId = getSecondTargetResId();
        if (secondTargetResId != 0) {
            setWidgetLayoutResource(secondTargetResId);
        }
    }

    @Override
    public void onBindViewHolder(PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);
        final View divider = holder.findViewById(R.id.two_target_divider);
        final View widgetFrame = holder.findViewById(android.R.id.widget_frame);
        final boolean shouldHideSecondTarget = shouldHideSecondTarget();
        if (divider != null) {
            divider.setVisibility(shouldHideSecondTarget ? View.GONE : View.VISIBLE);
        }
        if (widgetFrame != null) {
            widgetFrame.setVisibility(shouldHideSecondTarget ? View.GONE : View.VISIBLE);
        }
    }

    protected boolean shouldHideSecondTarget() {
        return getSecondTargetResId() == 0;
    }

    protected int getSecondTargetResId() {
        return 0;
    }
}

3.自定义布局
最后面的imageview就是原来BluetoothDevicePreference加载的内容,我们修改下,在这个齿轮的左边加上电池信息和图片。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="90dp"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <LinearLayout
    android:id="@+id/battery_layout"
    android:layout_gravity="right"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="horizontal">

        <TextView
        android:id="@+id/battery_level"
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:text="0%" />

        <ImageView
        android:id="@+id/battery_level_image"
            android:scaleType="center"
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

        <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="horizontal">
           <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="start|center_vertical"
        android:orientation="horizontal"
        android:paddingTop="16dp"
        android:paddingBottom="16dp">

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="?android:attr/dividerVertical" />
    </LinearLayout>
    <ImageView
        android:id="@+id/settings_button"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:background="?android:attr/selectableItemBackground"
        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
        android:paddingStart="?android:attr/listPreferredItemPaddingEnd"
        android:scaleType="center"
        android:src="@drawable/ic_settings"
        android:contentDescription="@string/settings_button" />
        </LinearLayout>

</LinearLayout>

实现:
1.在framework层监听蓝牙电池信息
在BluetoothManagerService中添加蓝牙电池的监听,把读取到的值保存下来,这里保存电量和mac地址,方便后面使用。
为什么还要mac地址,因为这个广播1分钟发一次,所以要把电量先保存下来,等进去蓝牙界面的时候去读取,但是如果这次连接的蓝牙耳机不支持读电量,那么如果不用mac比较,就会把之前的蓝牙耳机电量读到了。

BluetoothManagerService(Context context) {
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
        filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
        filter.addAction(Intent.ACTION_SETTING_RESTORED);

//add        filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);

//add        filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY+"."+BluetoothAssignedNumbers.GOOGLE);
        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
        mContext.registerReceiver(mReceiver, filter);
}


private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    //省略原生代码
                else if (BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT.equals(action)) {
                   //aaron

                String command = intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);

                if ("+IPHONEACCEV".equals(command)) {
                    Object[] args = (Object[]) intent.getSerializableExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);
                    if (args.length >= 3 && args[0] instanceof Integer && ((Integer)args[0])*2+1<=args.length) {
                        for (int i=0;i<((Integer)args[0]);i++) {
                            if (!(args[i*2+1] instanceof Integer) || !(args[i*2+2] instanceof Integer)) {
                                continue;
                            }
                            if (args[i*2+1].equals(1)) {
                                float level = (((Integer)args[i*2+2])+1)/10.0f;
                                level=level*100;
                                if (DBG) Slog.d(TAG, "battery   "+level);
                                mLevel=(int) level;
                                if (DBG) Slog.d(TAG, "battery mLevel  "+mLevel);
                                break;
                            }
                        }
                    }
                    BluetoothDevice device=(BluetoothDevice) intent.getExtra(BluetoothDevice.EXTRA_DEVICE, null);
                    if(device!=null) {
                        String mac=device.getAddress();
                        mMac=mac;
                        if (DBG) Slog.d(TAG, "mac   "+mac);
                    }
                }
            }

        }
}

这里添加一个新的方法,透个接口出去
这里用的本来就是AIDL,我们直接多添加一个方法就行。
frameworks/base/core/java/android/bluetooth/IBluetoothManager.aidl

/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.bluetooth;

import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManagerCallback;
import android.bluetooth.IBluetoothProfileServiceConnection;
import android.bluetooth.IBluetoothStateChangeCallback;

/**
 * System private API for talking with the Bluetooth service.
 *
 * {@hide}
 */
interface IBluetoothManager
{
    IBluetooth registerAdapter(in IBluetoothManagerCallback callback);
    void unregisterAdapter(in IBluetoothManagerCallback callback);
    void registerStateChangeCallback(in IBluetoothStateChangeCallback callback);
    void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback);
    boolean isEnabled();
    boolean enable(String packageName);
    boolean enableNoAutoConnect(String packageName);
    boolean disable(String packageName, boolean persist);
    int getState();
    IBluetoothGatt getBluetoothGatt();

    boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy);
    void unbindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy);

    String getAddress();
    String getName();
    int getBatteryLevel(String mac);

    boolean isBleScanAlwaysAvailable();
    int updateBleAppCount(IBinder b, boolean enable, String packageName);
    boolean isBleAppPresent();
}

在java中添加实现

    public int getBatteryLevel(String mac) {
        if (DBG) Slog.d(TAG, "getBatteryLevel mac   "+mac);
        if (DBG) Slog.d(TAG, "getBatteryLevel mMac   "+mMac);
        if (DBG) Slog.d(TAG, "getBatteryLevel mLevel   "+mLevel);
        if (mac.equals(mMac)) {
            if (DBG) Slog.d(TAG, "getBatteryLevel return   "+mLevel);
            return mLevel;
        } else {
            if (DBG) Slog.d(TAG, "getBatteryLevel return   -2");
            return -2;
        }

    }

frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java
在BluetoothAdapter.java中添加一个获取电池的方法,给settings使用。

BluetoothAdapter mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.getBatteryLevel(mCachedDevice.getDevice().getAddress())

    /**
     * 
     * @hide
     */
    public int getBatteryLevel(String mac) {
        try {
            int level = mManagerService.getBatteryLevel(mac);
            return level;
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
        }
        return -1;
    }

2.Bluetooth Settings获取蓝牙电池信息
packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
每次加载布局的时候就去获取蓝牙电池

    @Override
    public void onBindViewHolder(PreferenceViewHolder view) {
        ......

            setBattery(view);

        super.onBindViewHolder(view);
    }
    private void setBattery(PreferenceViewHolder view) {

        LinearLayout batteryLayout= (LinearLayout)view.findViewById(R.id.battery_layout);
        TextView batteryTextView=(TextView)view.findViewById(R.id.battery_level);
        ImageView batteryImage=(ImageView) view.findViewById(R.id.battery_level_image);
        batteryImage.setImageResource(R.drawable.bluetooth_device_battery);
        batteryLayout.setVisibility(View.INVISIBLE);

        if(mCachedDevice.isConnected()) {
            int level=0;
            BluetoothAdapter  mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
            level=mBluetoothAdapter.getBatteryLevel(mCachedDevice.getDevice().getAddress());
            if(level>=0) {
                batteryLayout.setVisibility(View.VISIBLE);
                batteryTextView.setText(Integer.toString(level)+"%");                
                batteryImage.setImageLevel(level);
            }
        }
    }

在BluetoothSettings中同时也监听蓝牙耳机电池的广播,因为每次更新view,也就是进来的时候会抓。如果一直在这个界面不动,怎么办,那就要实时更新。
BluetoothSettings.java

    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (action.equals(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT)) {
                String command = intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);

                if ("+IPHONEACCEV".equals(command)) {
                    if (mLocalAdapter != null) {
                        Log.i("b", "mIntentReceiver updatecontent ");
                        updateContent(mLocalAdapter.getBluetoothState());
                    }
                }
            }
        }
    };

3.布局
BluetoothDevicePreference.java

    @Override
    protected int getSecondTargetResId() {
        return R.layout.preference_widget_gear_ble_battery;
    }

preference_widget_gear_ble_battery.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="90dp"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <LinearLayout
    android:id="@+id/battery_layout"
    android:layout_gravity="right"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="horizontal">

        <TextView
        android:id="@+id/battery_level"
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:text="0%" />

        <ImageView
        android:id="@+id/battery_level_image"
            android:scaleType="center"
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

        <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="horizontal">
           <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="start|center_vertical"
        android:orientation="horizontal"
        android:paddingTop="16dp"
        android:paddingBottom="16dp">
        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="?android:attr/dividerVertical" />
    </LinearLayout>
    <ImageView
        android:id="@+id/settings_button"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:background="?android:attr/selectableItemBackground"
        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
        android:paddingStart="?android:attr/listPreferredItemPaddingEnd"
        android:scaleType="center"
        android:src="@drawable/ic_settings"
        android:contentDescription="@string/settings_button" />
        </LinearLayout>

</LinearLayout>
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值