Show only 1 entry for hearing aid devices without killing the activity.

This CL tries to detect Bluetooth hearing aid devices and tries to
combine the entry of the hearing aids with the same HiSyncIds and
show only 1 entry for each pair in the connected devices list.

This CL also shows 2 battery status in the device details page.

This change shows the combined entry after a user returns to the
settings activity after pressing the back button or somehow
without killing it. It also combines the entries just after pairing.

Test: RunSettingsRoboTests
Bug: 74204427

Change-Id: I47fb0bdd96b1cc972d88a4aef85d0113985d63bb
This commit is contained in:
Isha Bobra
2018-02-08 16:09:39 -08:00
committed by Stanley Tng
parent 6baefa4b51
commit 35e217f91f
9 changed files with 88 additions and 7 deletions

View File

@@ -81,6 +81,17 @@
android:textAppearance="@android:style/TextAppearance.Material.Body1" android:textAppearance="@android:style/TextAppearance.Material.Body1"
android:textColor="?android:attr/textColorSecondary" /> android:textColor="?android:attr/textColorSecondary" />
<TextView
android:id="@+id/entity_header_second_summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:singleLine="true"
android:ellipsize="marquee"
android:textAlignment="viewStart"
android:textAppearance="@android:style/TextAppearance.Material.Body1"
android:textColor="?android:attr/textColorSecondary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -75,6 +75,17 @@
android:textAppearance="@android:style/TextAppearance.Material.Body1" android:textAppearance="@android:style/TextAppearance.Material.Body1"
android:textColor="?android:attr/textColorSecondary" /> android:textColor="?android:attr/textColorSecondary" />
<TextView
android:id="@+id/entity_header_second_summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:singleLine="true"
android:ellipsize="marquee"
android:textAlignment="viewStart"
android:textAppearance="@android:style/TextAppearance.Material.Body1"
android:textColor="?android:attr/textColorSecondary" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout

View File

@@ -22,11 +22,14 @@ import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.util.Pair; import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.applications.LayoutPreference; import com.android.settings.applications.LayoutPreference;
import com.android.settings.widget.EntityHeaderController; import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
/** /**
* This class adds a header with device name and status (connected/disconnected, etc.). * This class adds a header with device name and status (connected/disconnected, etc.).
@@ -35,10 +38,15 @@ public class BluetoothDetailsHeaderController extends BluetoothDetailsController
private static final String KEY_DEVICE_HEADER = "bluetooth_device_header"; private static final String KEY_DEVICE_HEADER = "bluetooth_device_header";
private EntityHeaderController mHeaderController; private EntityHeaderController mHeaderController;
private LocalBluetoothManager mLocalManager;
private CachedBluetoothDeviceManager mDeviceManager;
public BluetoothDetailsHeaderController(Context context, PreferenceFragment fragment, public BluetoothDetailsHeaderController(Context context, PreferenceFragment fragment,
CachedBluetoothDevice device, Lifecycle lifecycle) { CachedBluetoothDevice device, Lifecycle lifecycle,
LocalBluetoothManager bluetoothManager) {
super(context, fragment, device, lifecycle); super(context, fragment, device, lifecycle);
mLocalManager = bluetoothManager;
mDeviceManager = mLocalManager.getCachedDeviceManager();
} }
@Override @Override
@@ -55,6 +63,12 @@ public class BluetoothDetailsHeaderController extends BluetoothDetailsController
.getBtClassDrawableWithDescription(mContext, mCachedDevice, .getBtClassDrawableWithDescription(mContext, mCachedDevice,
mContext.getResources().getFraction(R.fraction.bt_battery_scale_fraction, 1, 1)); mContext.getResources().getFraction(R.fraction.bt_battery_scale_fraction, 1, 1));
String summaryText = mCachedDevice.getConnectionSummary(); String summaryText = mCachedDevice.getConnectionSummary();
// If both the hearing aids are connected, two battery status should be shown.
final String pairDeviceSummary = mDeviceManager
.getHearingAidPairDeviceSummary(mCachedDevice);
if (pairDeviceSummary != null) {
mHeaderController.setSecondSummary(pairDeviceSummary);
}
mHeaderController.setLabel(mCachedDevice.getName()); mHeaderController.setLabel(mCachedDevice.getName());
mHeaderController.setIcon(pair.first); mHeaderController.setIcon(pair.first);
mHeaderController.setIconContentDescription(pair.second); mHeaderController.setIconContentDescription(pair.second);

View File

@@ -142,7 +142,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
if (mCachedDevice != null) { if (mCachedDevice != null) {
Lifecycle lifecycle = getLifecycle(); Lifecycle lifecycle = getLifecycle();
controllers.add(new BluetoothDetailsHeaderController(context, this, mCachedDevice, controllers.add(new BluetoothDetailsHeaderController(context, this, mCachedDevice,
lifecycle)); lifecycle, mManager));
controllers.add(new BluetoothDetailsButtonsController(context, this, mCachedDevice, controllers.add(new BluetoothDetailsButtonsController(context, this, mCachedDevice,
lifecycle)); lifecycle));
controllers.add(new BluetoothDetailsProfilesController(context, this, mManager, controllers.add(new BluetoothDetailsProfilesController(context, this, mManager,

View File

@@ -152,7 +152,12 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
} }
@Override @Override
public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {} public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
// Used to combine the hearing aid entries just after pairing. Once both the hearing aids
// get connected and their hiSyncId gets populated, this gets called for one of the
// 2 hearing aids so that only one entry in the connected devices list will be seen.
removePreference(cachedDevice);
}
@Override @Override
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {

View File

@@ -85,6 +85,8 @@ public class EntityHeaderController {
private String mIconContentDescription; private String mIconContentDescription;
private CharSequence mLabel; private CharSequence mLabel;
private CharSequence mSummary; private CharSequence mSummary;
// Required for hearing aid devices.
private CharSequence mSecondSummary;
private String mPackageName; private String mPackageName;
private Intent mAppNotifPrefIntent; private Intent mAppNotifPrefIntent;
@UserIdInt @UserIdInt
@@ -181,6 +183,18 @@ public class EntityHeaderController {
return this; return this;
} }
public EntityHeaderController setSecondSummary(CharSequence summary) {
mSecondSummary = summary;
return this;
}
public EntityHeaderController setSecondSummary(PackageInfo packageInfo) {
if (packageInfo != null) {
mSummary = packageInfo.versionName;
}
return this;
}
public EntityHeaderController setHasAppInfoLink(boolean hasAppInfoLink) { public EntityHeaderController setHasAppInfoLink(boolean hasAppInfoLink) {
mHasAppInfoLink = hasAppInfoLink; mHasAppInfoLink = hasAppInfoLink;
return this; return this;
@@ -242,6 +256,7 @@ public class EntityHeaderController {
} }
setText(R.id.entity_header_title, mLabel); setText(R.id.entity_header_title, mLabel);
setText(R.id.entity_header_summary, mSummary); setText(R.id.entity_header_summary, mSummary);
setText(R.id.entity_header_second_summary, mSecondSummary);
if (mIsInstantApp) { if (mIsInstantApp) {
setText(R.id.install_type, setText(R.id.install_type,
mHeader.getResources().getString(R.string.install_type_instant)); mHeader.getResources().getString(R.string.install_type_instant));

View File

@@ -32,6 +32,9 @@ import com.android.settings.testutils.shadow.SettingsShadowBluetoothDevice;
import com.android.settings.testutils.shadow.ShadowEntityHeaderController; import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
import com.android.settings.widget.EntityHeaderController; import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -50,14 +53,21 @@ public class BluetoothDetailsHeaderControllerTest extends BluetoothDetailsContro
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private EntityHeaderController mHeaderController; private EntityHeaderController mHeaderController;
@Mock
private LocalBluetoothManager mBluetoothManager;
@Mock
private CachedBluetoothDeviceManager mCachedDeviceManager;
@Override @Override
public void setUp() { public void setUp() {
super.setUp(); super.setUp();
FakeFeatureFactory.setupForTest(); FakeFeatureFactory.setupForTest();
ShadowEntityHeaderController.setUseMock(mHeaderController); ShadowEntityHeaderController.setUseMock(mHeaderController);
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
when(mCachedDeviceManager.getHearingAidPairDeviceSummary(mCachedDevice)).thenReturn("abc");
mController = mController =
new BluetoothDetailsHeaderController(mContext, mFragment, mCachedDevice, mLifecycle); new BluetoothDetailsHeaderController(mContext, mFragment, mCachedDevice, mLifecycle,
mBluetoothManager);
mPreference = new LayoutPreference(mContext, R.layout.settings_entity_header); mPreference = new LayoutPreference(mContext, R.layout.settings_entity_header);
mPreference.setKey(mController.getPreferenceKey()); mPreference.setKey(mController.getPreferenceKey());
mScreen.addPreference(mPreference); mScreen.addPreference(mPreference);
@@ -86,6 +96,7 @@ public class BluetoothDetailsHeaderControllerTest extends BluetoothDetailsContro
verify(mHeaderController).setIcon(any(Drawable.class)); verify(mHeaderController).setIcon(any(Drawable.class));
verify(mHeaderController).setIconContentDescription(any(String.class)); verify(mHeaderController).setIconContentDescription(any(String.class));
verify(mHeaderController).setSummary(any(String.class)); verify(mHeaderController).setSummary(any(String.class));
verify(mHeaderController).setSecondSummary(any(String.class));
verify(mHeaderController).done(mActivity, true); verify(mHeaderController).done(mActivity, true);
} }

View File

@@ -106,6 +106,16 @@ public class BluetoothDeviceUpdaterTest {
assertThat(mBluetoothDeviceUpdater.mPreferenceMap.containsKey(mBluetoothDevice)).isFalse(); assertThat(mBluetoothDeviceUpdater.mPreferenceMap.containsKey(mBluetoothDevice)).isFalse();
} }
@Test
public void testOnDeviceDeleted_deviceExists_removePreference() {
mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
mBluetoothDeviceUpdater.onDeviceDeleted(mCachedBluetoothDevice);
verify(mDevicePreferenceCallback).onDeviceRemoved(mPreference);
assertThat(mBluetoothDeviceUpdater.mPreferenceMap.containsKey(mBluetoothDevice)).isFalse();
}
@Test @Test
public void testRemovePreference_deviceNotExist_doNothing() { public void testRemovePreference_deviceNotExist_doNothing() {
mBluetoothDeviceUpdater.removePreference(mCachedBluetoothDevice); mBluetoothDeviceUpdater.removePreference(mCachedBluetoothDevice);

View File

@@ -115,18 +115,22 @@ public class EntityHeaderControllerTest {
final View header = final View header =
mLayoutInflater.inflate(R.layout.settings_entity_header, null /* root */); mLayoutInflater.inflate(R.layout.settings_entity_header, null /* root */);
final TextView label = header.findViewById(R.id.entity_header_title); final TextView label = header.findViewById(R.id.entity_header_title);
final TextView version = header.findViewById(R.id.entity_header_summary); final TextView summary = header.findViewById(R.id.entity_header_summary);
final TextView secondSummary = header.findViewById(R.id.entity_header_second_summary);
mController = EntityHeaderController.newInstance(mActivity, mFragment, header); mController = EntityHeaderController.newInstance(mActivity, mFragment, header);
mController.setLabel(testString); mController.setLabel(testString);
mController.setSummary(testString); mController.setSummary(testString);
mController.setSecondSummary(testString);
mController.setIcon(mShadowContext.getDrawable(R.drawable.ic_add)); mController.setIcon(mShadowContext.getDrawable(R.drawable.ic_add));
mController.done(mActivity); mController.done(mActivity);
assertThat(label).isNotNull(); assertThat(label).isNotNull();
assertThat(label.getText()).isEqualTo(testString); assertThat(label.getText()).isEqualTo(testString);
assertThat(version).isNotNull(); assertThat(summary).isNotNull();
assertThat(version.getText()).isEqualTo(testString); assertThat(summary.getText()).isEqualTo(testString);
assertThat(secondSummary).isNotNull();
assertThat(secondSummary.getText()).isEqualTo(testString);
} }
@Test @Test