Add battery indicator to bluetooth icon

This cl change util method in bluetooth package to return
drawable instead of resId.

If the bt device has battery level, then method return customized
layerDrawable, otherwise return a simple drawable created from
resId.

Bug: 63393322
Test: RunSettingsRoboTests

Change-Id: Ib21822eafda0e8570212ce5cb070478e4f4876a2
This commit is contained in:
jackqdyulei
2017-08-09 09:57:27 -07:00
committed by Lei Yu
parent d0c1f65451
commit 6f5c990abd
7 changed files with 84 additions and 29 deletions

View File

@@ -38,7 +38,7 @@
android:id="@+id/entity_header_icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="fitXY"
android:scaleType="fitCenter"
android:layout_gravity="center_horizontal"
android:antialias="true" />

View File

@@ -16,8 +16,8 @@
package com.android.settings.bluetooth;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.PreferenceScreen;
import android.util.Pair;
@@ -51,11 +51,11 @@ public class BluetoothDetailsHeaderController extends BluetoothDetailsController
}
protected void setHeaderProperties() {
Pair<Integer, String> pair = Utils.getBtClassDrawableWithDescription
(mContext.getResources(), mCachedDevice);
final Pair<Drawable, String> pair = Utils.getBtClassDrawableWithDescription
(mContext, mCachedDevice);
String summaryText = mCachedDevice.getConnectionSummary();
mHeaderController.setLabel(mCachedDevice.getName());
mHeaderController.setIcon(mContext.getDrawable(pair.first));
mHeaderController.setIcon(pair.first);
mHeaderController.setIconContentDescription(pair.second);
mHeaderController.setSummary(summaryText);
}

View File

@@ -21,6 +21,7 @@ import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.UserManager;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
@@ -120,9 +121,9 @@ public final class BluetoothDevicePreference extends GearPreference implements
// Null check is done at the framework
setSummary(mCachedDevice.getConnectionSummary());
Pair<Integer, String> pair = Utils.getBtClassDrawableWithDescription(mResources,
final Pair<Drawable, String> pair = Utils.getBtClassDrawableWithDescription(getContext(),
mCachedDevice);
if (pair.first != 0) {
if (pair.first != null) {
setIcon(pair.first);
contentDescription = pair.second;
}

View File

@@ -23,6 +23,9 @@ import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.support.annotation.DrawableRes;
import android.support.annotation.IdRes;
import android.support.annotation.VisibleForTesting;
import android.util.Pair;
import android.widget.Toast;
@@ -36,6 +39,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.Utils.ErrorListener;
import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import java.util.List;
@@ -150,27 +154,31 @@ public final class Utils {
}
};
static Pair<Integer, String> getBtClassDrawableWithDescription(Resources r,
static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
CachedBluetoothDevice cachedDevice) {
BluetoothClass btClass = cachedDevice.getBtClass();
final int level = cachedDevice.getBatteryLevel();
if (btClass != null) {
switch (btClass.getMajorDeviceClass()) {
case BluetoothClass.Device.Major.COMPUTER:
return new Pair<Integer, String>(R.drawable.ic_bt_laptop,
r.getString(R.string.bluetooth_talkback_computer));
return new Pair<>(getBluetoothDrawable(context, R.drawable.ic_bt_laptop, level),
context.getString(R.string.bluetooth_talkback_computer));
case BluetoothClass.Device.Major.PHONE:
return new Pair<Integer, String>(R.drawable.ic_bt_cellphone,
r.getString(R.string.bluetooth_talkback_phone));
return new Pair<>(
getBluetoothDrawable(context, R.drawable.ic_bt_cellphone, level),
context.getString(R.string.bluetooth_talkback_phone));
case BluetoothClass.Device.Major.PERIPHERAL:
return new Pair<Integer, String>(HidProfile.getHidClassDrawable(btClass),
r.getString(
R.string.bluetooth_talkback_input_peripheral));
return new Pair<>(
getBluetoothDrawable(context, HidProfile.getHidClassDrawable(btClass),
level),
context.getString(R.string.bluetooth_talkback_input_peripheral));
case BluetoothClass.Device.Major.IMAGING:
return new Pair<Integer, String>(R.drawable.ic_settings_print,
r.getString(R.string.bluetooth_talkback_imaging));
return new Pair<>(
getBluetoothDrawable(context, R.drawable.ic_settings_print, level),
context.getString(R.string.bluetooth_talkback_imaging));
default:
// unrecognized device class; continue
@@ -181,20 +189,34 @@ public final class Utils {
for (LocalBluetoothProfile profile : profiles) {
int resId = profile.getDrawableResource(btClass);
if (resId != 0) {
return new Pair<Integer, String>(resId, null);
return new Pair<>(getBluetoothDrawable(context, resId, level), null);
}
}
if (btClass != null) {
if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
return new Pair<Integer, String>(R.drawable.ic_bt_headset_hfp,
r.getString(R.string.bluetooth_talkback_headset));
return new Pair<>(
getBluetoothDrawable(context, R.drawable.ic_bt_headset_hfp, level),
context.getString(R.string.bluetooth_talkback_headset));
}
if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
return new Pair<Integer, String>(R.drawable.ic_bt_headphones_a2dp,
r.getString(R.string.bluetooth_talkback_headphone));
return new Pair<>(
getBluetoothDrawable(context, R.drawable.ic_bt_headphones_a2dp, level),
context.getString(R.string.bluetooth_talkback_headphone));
}
}
return new Pair<Integer, String>(R.drawable.ic_settings_bluetooth,
r.getString(R.string.bluetooth_talkback_bluetooth));
return new Pair<>(getBluetoothDrawable(context, R.drawable.ic_settings_bluetooth, level),
context.getString(R.string.bluetooth_talkback_bluetooth));
}
@VisibleForTesting
static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId,
int batteryLevel) {
if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
return BluetoothDeviceLayerDrawable.createLayerDrawable(context, resId, batteryLevel);
} else if (resId != 0) {
return context.getDrawable(resId);
} else {
return null;
}
}
}

View File

@@ -32,6 +32,7 @@ import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.SettingsShadowBluetoothDevice;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
import com.android.settings.widget.EntityHeaderController;
@@ -45,7 +46,8 @@ import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
shadows={SettingsShadowBluetoothDevice.class, ShadowEntityHeaderController.class})
shadows={SettingsShadowBluetoothDevice.class, ShadowEntityHeaderController.class,
SettingsShadowResources.class})
public class BluetoothDetailsHeaderControllerTest extends BluetoothDetailsControllerTestBase {
private BluetoothDetailsHeaderController mController;
private LayoutPreference mPreference;

View File

@@ -26,6 +26,7 @@ import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import org.junit.Before;
@@ -44,7 +45,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
shadows = SettingsShadowResources.class)
public class BluetoothDevicePreferenceTest {
private Context mContext;
@@ -140,8 +142,11 @@ public class BluetoothDevicePreferenceTest {
@Test
public void imagingDeviceIcon_isICSettingsPrint() {
when(mCachedBluetoothDevice.getBatteryLevel()).thenReturn(
BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
when(mCachedBluetoothDevice.getBtClass()).thenReturn(
new BluetoothClass(BluetoothClass.Device.Major.IMAGING));
mPreference.onDeviceAttributesChanged();
assertThat(mPreference.getIcon()).isEqualTo(
mContext.getDrawable(R.drawable.ic_settings_print));

View File

@@ -15,14 +15,21 @@
*/
package com.android.settings.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.drawable.Drawable;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import org.junit.Before;
import org.junit.Test;
@@ -30,6 +37,7 @@ import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import static org.mockito.Matchers.anyInt;
@@ -40,7 +48,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
shadows = SettingsShadowResources.class)
public class UtilsTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -60,11 +69,27 @@ public class UtilsTest {
}
@Test
public void showConnectingError_shouldLogBluetoothConnectError() {
public void testShowConnectingError_shouldLogBluetoothConnectError() {
when(mContext.getString(anyInt(), anyString())).thenReturn("testMessage");
Utils.showConnectingError(mContext, "testName", mock(LocalBluetoothManager.class));
verify(mMetricsFeatureProvider).visible(eq(mContext), anyInt(),
eq(MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT_ERROR));
}
@Test
public void testGetBluetoothDrawable_noBatteryLevel_returnSimpleDrawable() {
final Drawable drawable = Utils.getBluetoothDrawable(RuntimeEnvironment.application,
R.drawable.ic_bt_laptop, BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
assertThat(drawable).isNotInstanceOf(BluetoothDeviceLayerDrawable.class);
}
@Test
public void testGetBluetoothDrawable_hasBatteryLevel_returnLayerDrawable() {
final Drawable drawable = Utils.getBluetoothDrawable(RuntimeEnvironment.application,
R.drawable.ic_bt_laptop, 10 /* batteryLevel */);
assertThat(drawable).isInstanceOf(BluetoothDeviceLayerDrawable.class);
}
}