Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f55a879 authored by Jack He's avatar Jack He Committed by android-build-merger
Browse files

Bluetooth: Display battery level of connected devices

am: 6258aae5

Change-Id: I24768c7c4353e1fe0100206f402daa34df54517f
parents 8e995823 6258aae5
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ public class BluetoothEventManager {
        // Fine-grained state broadcasts
        addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, new ClassChangedHandler());
        addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler());
        addHandler(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED, new BatteryLevelChangedHandler());

        // Dock event broadcasts
        addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
@@ -376,6 +377,17 @@ public class BluetoothEventManager {
            }
        }
    }

    private class BatteryLevelChangedHandler implements Handler {
        public void onReceive(Context context, Intent intent,
                BluetoothDevice device) {
            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
            if (cachedDevice != null) {
                cachedDevice.refresh();
            }
        }
    }

    boolean readPairedDevices() {
        Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
        if (bondedDevices == null) {
+51 −7
Original line number Diff line number Diff line
@@ -419,6 +419,14 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
        }
    }

    /**
     * Get battery level from remote device
     * @return battery level in percentage [0-100], or {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
     */
    public int getBatteryLevel() {
        return mDevice.getBatteryLevel();
    }

    void refresh() {
        dispatchAttributesChanged();
    }
@@ -837,7 +845,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
    /**
     * @return resource for string that discribes the connection state of this device.
     */
    public int getConnectionSummary() {
    public String getConnectionSummary() {
        boolean profileConnected = false;       // at least one profile is connected
        boolean a2dpNotConnected = false;       // A2DP is preferred but not connected
        boolean hfpNotConnected = false;    // HFP is preferred but not connected
@@ -848,7 +856,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
            switch (connectionStatus) {
                case BluetoothProfile.STATE_CONNECTING:
                case BluetoothProfile.STATE_DISCONNECTING:
                    return Utils.getConnectionStateSummary(connectionStatus);
                    return mContext.getString(Utils.getConnectionStateSummary(connectionStatus));

                case BluetoothProfile.STATE_CONNECTED:
                    profileConnected = true;
@@ -868,18 +876,54 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
            }
        }

        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);
        }

        if (profileConnected) {
            if (a2dpNotConnected && hfpNotConnected) {
                return R.string.bluetooth_connected_no_headset_no_a2dp;
                if (batteryLevelPercentageString != null) {
                    return mContext.getString(
                            R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
                            batteryLevelPercentageString);
                } else {
                    return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp);
                }

            } else if (a2dpNotConnected) {
                return R.string.bluetooth_connected_no_a2dp;
                if (batteryLevelPercentageString != null) {
                    return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
                            batteryLevelPercentageString);
                } else {
                    return mContext.getString(R.string.bluetooth_connected_no_a2dp);
                }

            } else if (hfpNotConnected) {
                return R.string.bluetooth_connected_no_headset;
                if (batteryLevelPercentageString != null) {
                    return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
                            batteryLevelPercentageString);
                } else {
                return R.string.bluetooth_connected;
                    return mContext.getString(R.string.bluetooth_connected_no_headset);
                }
            } else {
                if (batteryLevelPercentageString != null) {
                    return mContext.getString(R.string.bluetooth_connected_battery_level,
                            batteryLevelPercentageString);
                } else {
                    return mContext.getString(R.string.bluetooth_connected);
                }
            }
        }

        return getBondState() == BluetoothDevice.BOND_BONDING ? R.string.bluetooth_pairing : 0;
        return getBondState() == BluetoothDevice.BOND_BONDING ?
                mContext.getString(R.string.bluetooth_pairing) : null;
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ import java.util.List;
/**
 * HeadsetProfile handles Bluetooth HFP and Headset profiles.
 */
public final class HeadsetProfile implements LocalBluetoothProfile {
public class HeadsetProfile implements LocalBluetoothProfile {
    private static final String TAG = "HeadsetProfile";
    private static boolean V = true;

+1 −1
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ import java.util.List;
/**
 * HidProfile handles Bluetooth HID profile.
 */
public final class HidProfile implements LocalBluetoothProfile {
public class HidProfile implements LocalBluetoothProfile {
    private static final String TAG = "HidProfile";
    private static boolean V = true;

+155 −0
Original line number Diff line number Diff line
/*
 * 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.bluetooth;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;

import com.android.settingslib.R;
import com.android.settingslib.TestConfig;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

@RunWith(RobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, resourceDir =
        "../../res")
public class CachedBluetoothDeviceTest {
    @Mock
    private LocalBluetoothAdapter mAdapter;
    @Mock
    private LocalBluetoothProfileManager mProfileManager;
    @Mock
    private HeadsetProfile mHfpProfile;
    @Mock
    private A2dpProfile mA2dpProfile;
    @Mock
    private HidProfile mHidProfile;
    @Mock
    private BluetoothDevice mDevice;
    private CachedBluetoothDevice mCachedDevice;
    private Context mContext;
    private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mContext = RuntimeEnvironment.application;
        when(mAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
        when(mHfpProfile.isProfileReady()).thenReturn(true);
        when(mA2dpProfile.isProfileReady()).thenReturn(true);
        when(mHidProfile.isProfileReady()).thenReturn(true);
        mCachedDevice = spy(
                new CachedBluetoothDevice(mContext, mAdapter, mProfileManager, mDevice));
        doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
    }

    /**
     * Test to verify the current test context object works so that we are not checking null
     * against null
     */
    @Test
    public void testContextMock() {
        assertThat(mContext.getString(R.string.bluetooth_connected)).isEqualTo("Connected");
    }

    @Test
    public void testGetConnectionSummary_testSingleProfileConnectDisconnect() {
        // Test without battery level
        // Set HID profile to be connected and test connection state summary
        mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
                R.string.bluetooth_connected));

        // Set HID profile to be disconnected and test connection state summary
        mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
        assertThat(mCachedDevice.getConnectionSummary()).isNull();

        // Test with battery level
        mBatteryLevel = 10;
        // Set HID profile to be connected and test connection state summary
        mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
                R.string.bluetooth_connected_battery_level,
                com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));

        // Set HID profile to be disconnected and test connection state summary
        mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
        assertThat(mCachedDevice.getConnectionSummary()).isNull();

        // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
        mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;

        // Set HID profile to be connected and test connection state summary
        mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
                R.string.bluetooth_connected));

        // Set HID profile to be disconnected and test connection state summary
        mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
        assertThat(mCachedDevice.getConnectionSummary()).isNull();
    }

    @Test
    public void testGetConnectionSummary_testMultipleProfileConnectDisconnect() {
        mBatteryLevel = 10;

        // Set HFP, A2DP and HID profile to be connected and test connection state summary
        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
        mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
                R.string.bluetooth_connected_battery_level,
                com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));

        // Disconnect HFP only and test connection state summary
        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
                R.string.bluetooth_connected_no_headset_battery_level,
                com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));

        // Disconnect A2DP only and test connection state summary
        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
                R.string.bluetooth_connected_no_a2dp_battery_level,
                com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));

        // Disconnect both HFP and A2DP and test connection state summary
        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
                R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
                com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));

        // Disconnect all profiles and test connection state summary
        mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
        assertThat(mCachedDevice.getConnectionSummary()).isNull();
    }
}
Loading