From 41b12fe8eb86768495a91e0ad00a75e359d2bf6a Mon Sep 17 00:00:00 2001 From: Ze Li Date: Thu, 11 Jan 2024 18:29:28 +0800 Subject: [PATCH] [Connected devices page] Reorder devices by most recently used. Order the saved bluetooth devices by most recently connected in connected devices settings page. Test: atest: com.android.settings.connecteddevice.SavedDeviceGroupControllerTest, com.android.settings.connecteddevice.PreviouslyConnectedDevicePreferenceControllerTest Bug: 306160434 Change-Id: Id5ad8555a026d775d96ada37f989b4346336af93 --- ..._connecteddevice_flag_declarations.aconfig | 7 + ...lyConnectedDevicePreferenceController.java | 110 ++++++++++++--- .../SavedDeviceGroupController.java | 79 ++++++++++- ...nnectedDevicePreferenceControllerTest.java | 108 ++++++++++++++- .../SavedDeviceGroupControllerTest.java | 126 +++++++++++++++++- 5 files changed, 402 insertions(+), 28 deletions(-) diff --git a/aconfig/settings_connecteddevice_flag_declarations.aconfig b/aconfig/settings_connecteddevice_flag_declarations.aconfig index 1a3afed0a1e..5ba21296a6d 100644 --- a/aconfig/settings_connecteddevice_flag_declarations.aconfig +++ b/aconfig/settings_connecteddevice_flag_declarations.aconfig @@ -27,3 +27,10 @@ flag { description: "Gates whether to require an auth challenge for changing USB preferences" bug: "317367746" } + +flag { + name: "enable_saved_devices_order_by_recency" + namespace: "pixel_cross_device_control" + description: "Order the saved bluetooth devices by most recently connected." + bug: "306160434" +} \ No newline at end of file diff --git a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java index 7a2ae04f3e1..33f8b735588 100644 --- a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java +++ b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java @@ -17,6 +17,7 @@ package com.android.settings.connecteddevice; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -36,13 +37,16 @@ import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater; import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.core.BasePreferenceController; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class PreviouslyConnectedDevicePreferenceController extends BasePreferenceController implements LifecycleObserver, OnStart, OnStop, DevicePreferenceCallback { @@ -56,11 +60,12 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc private final List mDevicesList = new ArrayList<>(); private final List mDockDevicesList = new ArrayList<>(); + private final Map mDevicePreferenceMap = new HashMap<>(); + private final BluetoothAdapter mBluetoothAdapter; private PreferenceGroup mPreferenceGroup; private BluetoothDeviceUpdater mBluetoothDeviceUpdater; private DockUpdater mSavedDockUpdater; - private BluetoothAdapter mBluetoothAdapter; @VisibleForTesting Preference mSeeAllPreference; @@ -81,7 +86,11 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc mSavedDockUpdater = FeatureFactory.getFeatureFactory().getDockUpdaterFeatureProvider() .getSavedDockUpdater(context, this); mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + if (Flags.enableSavedDevicesOrderByRecency()) { + mBluetoothAdapter = context.getSystemService(BluetoothManager.class).getAdapter(); + } else { + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + } } @Override @@ -114,6 +123,9 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc mContext.registerReceiver(mReceiver, mIntentFilter, Context.RECEIVER_EXPORTED_UNAUDITED); mBluetoothDeviceUpdater.refreshPreference(); + if (Flags.enableSavedDevicesOrderByRecency()) { + updatePreferenceGroup(); + } } @Override @@ -131,19 +143,37 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc @Override public void onDeviceAdded(Preference preference) { - final List bluetoothDevices = - mBluetoothAdapter.getMostRecentlyConnectedDevices(); - final int index = preference instanceof BluetoothDevicePreference - ? bluetoothDevices.indexOf(((BluetoothDevicePreference) preference) - .getBluetoothDevice().getDevice()) : DOCK_DEVICE_INDEX; - if (DEBUG) { - Log.d(TAG, "onDeviceAdded() " + preference.getTitle() + ", index of : " + index); - for (BluetoothDevice device : bluetoothDevices) { - Log.d(TAG, "onDeviceAdded() most recently device : " + device.getName()); + if (Flags.enableSavedDevicesOrderByRecency()) { + if (preference instanceof BluetoothDevicePreference) { + mDevicePreferenceMap.put( + ((BluetoothDevicePreference) preference).getBluetoothDevice().getDevice(), + preference); + } else { + mDockDevicesList.add(preference); } + if (DEBUG) { + Log.d(TAG, "onDeviceAdded() " + preference.getTitle()); + } + updatePreferenceGroup(); + } else { + final List bluetoothDevices = + mBluetoothAdapter.getMostRecentlyConnectedDevices(); + final int index = + preference instanceof BluetoothDevicePreference + ? bluetoothDevices.indexOf( + ((BluetoothDevicePreference) preference) + .getBluetoothDevice() + .getDevice()) + : DOCK_DEVICE_INDEX; + if (DEBUG) { + Log.d(TAG, "onDeviceAdded() " + preference.getTitle() + ", index of : " + index); + for (BluetoothDevice device : bluetoothDevices) { + Log.d(TAG, "onDeviceAdded() most recently device : " + device.getName()); + } + } + addPreference(index, preference); + updatePreferenceVisibility(); } - addPreference(index, preference); - updatePreferenceVisibility(); } private void addPreference(int index, Preference preference) { @@ -194,13 +224,57 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc @Override public void onDeviceRemoved(Preference preference) { - if (preference instanceof BluetoothDevicePreference) { - mDevicesList.remove(preference); + if (Flags.enableSavedDevicesOrderByRecency()) { + if (preference instanceof BluetoothDevicePreference) { + mDevicePreferenceMap.remove( + ((BluetoothDevicePreference) preference).getBluetoothDevice().getDevice(), + preference); + } else { + mDockDevicesList.remove(preference); + } + if (DEBUG) { + Log.d(TAG, "onDeviceRemoved() " + preference.getTitle()); + } + updatePreferenceGroup(); } else { - mDockDevicesList.remove(preference); - } + if (preference instanceof BluetoothDevicePreference) { + mDevicesList.remove(preference); + } else { + mDockDevicesList.remove(preference); + } - addPreference(); + addPreference(); + updatePreferenceVisibility(); + } + } + + /** Sort the preferenceGroup by most recently used. */ + public void updatePreferenceGroup() { + mPreferenceGroup.removeAll(); + mPreferenceGroup.addPreference(mSeeAllPreference); + if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) { + // Bluetooth is supported + int order = 0; + for (BluetoothDevice device : mBluetoothAdapter.getMostRecentlyConnectedDevices()) { + Preference preference = mDevicePreferenceMap.getOrDefault(device, null); + if (preference != null) { + preference.setOrder(order); + mPreferenceGroup.addPreference(preference); + order += 1; + } + if (order == MAX_DEVICE_NUM) { + break; + } + } + for (Preference preference : mDockDevicesList) { + if (order == MAX_DEVICE_NUM) { + break; + } + preference.setOrder(order); + mPreferenceGroup.addPreference(preference); + order += 1; + } + } updatePreferenceVisibility(); } diff --git a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java index fb35dd9f58c..c73481d3cd3 100644 --- a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java +++ b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java @@ -15,6 +15,9 @@ */ package com.android.settings.connecteddevice; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.pm.PackageManager; @@ -23,18 +26,25 @@ import androidx.preference.Preference; import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceScreen; +import com.android.settings.bluetooth.BluetoothDevicePreference; import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater; import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; import com.android.settings.overlay.DockUpdaterFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Controller to maintain the {@link PreferenceGroup} for all * saved devices. It uses {@link DevicePreferenceCallback} to add/remove {@link Preference} @@ -45,6 +55,10 @@ public class SavedDeviceGroupController extends BasePreferenceController private static final String KEY = "saved_device_list"; + private final Map mDevicePreferenceMap = new HashMap<>(); + private final List mDockDevicesList = new ArrayList<>(); + private final BluetoothAdapter mBluetoothAdapter; + @VisibleForTesting PreferenceGroup mPreferenceGroup; private BluetoothDeviceUpdater mBluetoothDeviceUpdater; @@ -57,6 +71,7 @@ public class SavedDeviceGroupController extends BasePreferenceController FeatureFactory.getFeatureFactory().getDockUpdaterFeatureProvider(); mSavedDockUpdater = dockUpdaterFeatureProvider.getSavedDockUpdater(context, this); + mBluetoothAdapter = context.getSystemService(BluetoothManager.class).getAdapter(); } @Override @@ -64,6 +79,9 @@ public class SavedDeviceGroupController extends BasePreferenceController mBluetoothDeviceUpdater.registerCallback(); mSavedDockUpdater.registerCallback(); mBluetoothDeviceUpdater.refreshPreference(); + if (Flags.enableSavedDevicesOrderByRecency()) { + updatePreferenceGroup(); + } } @Override @@ -101,17 +119,63 @@ public class SavedDeviceGroupController extends BasePreferenceController @Override public void onDeviceAdded(Preference preference) { - if (mPreferenceGroup.getPreferenceCount() == 0) { - mPreferenceGroup.setVisible(true); + if (Flags.enableSavedDevicesOrderByRecency()) { + mPreferenceGroup.addPreference(preference); + if (preference instanceof BluetoothDevicePreference) { + mDevicePreferenceMap.put( + ((BluetoothDevicePreference) preference).getBluetoothDevice().getDevice(), + preference); + } else { + mDockDevicesList.add(preference); + } + updatePreferenceGroup(); + } else { + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(true); + } + mPreferenceGroup.addPreference(preference); } - mPreferenceGroup.addPreference(preference); } @Override public void onDeviceRemoved(Preference preference) { - mPreferenceGroup.removePreference(preference); - if (mPreferenceGroup.getPreferenceCount() == 0) { + if (Flags.enableSavedDevicesOrderByRecency()) { + mPreferenceGroup.removePreference(preference); + if (preference instanceof BluetoothDevicePreference) { + mDevicePreferenceMap.remove( + ((BluetoothDevicePreference) preference).getBluetoothDevice().getDevice(), + preference); + } else { + mDockDevicesList.remove(preference); + } + updatePreferenceGroup(); + } else { + mPreferenceGroup.removePreference(preference); + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(false); + } + } + } + + /** Sort the preferenceGroup by most recently used. */ + public void updatePreferenceGroup() { + if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { + // Bluetooth is unsupported or disabled mPreferenceGroup.setVisible(false); + } else { + mPreferenceGroup.setVisible(true); + int order = 0; + for (BluetoothDevice device : mBluetoothAdapter.getMostRecentlyConnectedDevices()) { + Preference preference = mDevicePreferenceMap.getOrDefault(device, null); + if (preference != null) { + preference.setOrder(order); + order += 1; + } + } + for (Preference preference : mDockDevicesList) { + preference.setOrder(order); + order += 1; + } } } @@ -130,4 +194,9 @@ public class SavedDeviceGroupController extends BasePreferenceController public void setSavedDockUpdater(DockUpdater savedDockUpdater) { mSavedDockUpdater = savedDockUpdater; } + + @VisibleForTesting + void setPreferenceGroup(PreferenceGroup preferenceGroup) { + mPreferenceGroup = preferenceGroup; + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java index e351b71b3dc..005c1311747 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java @@ -27,9 +27,14 @@ import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.Pair; import androidx.preference.Preference; @@ -42,11 +47,13 @@ import com.android.settings.bluetooth.BluetoothDevicePreference; import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.widget.SingleTargetGearPreference; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -70,6 +77,9 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { private static final String FAKE_ADDRESS_4 = "AA:AA:AA:AA:AA:04"; private static final String FAKE_ADDRESS_5 = "AA:AA:AA:AA:AA:05"; + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Mock private DashboardFragment mDashboardFragment; @Mock @@ -105,6 +115,9 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { @Mock private Drawable mDrawable; + @Mock private BluetoothManager mBluetoothManager; + @Mock private BluetoothAdapter mBluetoothAdapter; + private Context mContext; private PreviouslyConnectedDevicePreferenceController mPreConnectedDeviceController; private PreferenceGroup mPreferenceGroup; @@ -117,10 +130,8 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { mContext = spy(RuntimeEnvironment.application); doReturn(mContext).when(mDashboardFragment).getContext(); doReturn(mPackageManager).when(mContext).getPackageManager(); - mPreConnectedDeviceController = - new PreviouslyConnectedDevicePreferenceController(mContext, KEY); - mPreConnectedDeviceController.setBluetoothDeviceUpdater(mBluetoothDeviceUpdater); - mPreConnectedDeviceController.setSavedDockUpdater(mDockUpdater); + when(mContext.getSystemService(BluetoothManager.class)).thenReturn(mBluetoothManager); + when(mBluetoothManager.getAdapter()).thenReturn(mBluetoothAdapter); mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); when(mCachedDevice1.getDevice()).thenReturn(mBluetoothDevice1); @@ -145,7 +156,13 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { mMostRecentlyConnectedDevices.add(mBluetoothDevice4); mMostRecentlyConnectedDevices.add(mBluetoothDevice3); mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(mMostRecentlyConnectedDevices); + when(mBluetoothAdapter.getMostRecentlyConnectedDevices()) + .thenReturn(mMostRecentlyConnectedDevices); + mPreConnectedDeviceController = + new PreviouslyConnectedDevicePreferenceController(mContext, KEY); + mPreConnectedDeviceController.setBluetoothDeviceUpdater(mBluetoothDeviceUpdater); + mPreConnectedDeviceController.setSavedDockUpdater(mDockUpdater); mPreferenceGroup = spy(new PreferenceCategory(mContext)); doReturn(mPreferenceManager).when(mPreferenceGroup).getPreferenceManager(); mPreferenceGroup.setVisible(false); @@ -249,6 +266,7 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_SAVED_DEVICES_ORDER_BY_RECENCY) public void onDeviceAdded_addPreferenceNotExistInRecentlyDevices_noCrash() { final BluetoothDevicePreference preference = new BluetoothDevicePreference( mContext, mCachedDevice5, true, BluetoothDevicePreference.SortType.TYPE_NO_SORT); @@ -259,6 +277,18 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(2); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SAVED_DEVICES_ORDER_BY_RECENCY) + public void onDeviceAdded_addPreferenceNotExistInRecentlyDevices_doNothing() { + final BluetoothDevicePreference preference = new BluetoothDevicePreference( + mContext, mCachedDevice5, true, BluetoothDevicePreference.SortType.TYPE_NO_SORT); + + mPreConnectedDeviceController.onDeviceAdded(preference); + + // 1 see all preference + assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1); + } + @Test public void onDeviceRemoved_removeLastDevice_showSeeAllPreference() { final BluetoothDevicePreference preference1 = new BluetoothDevicePreference( @@ -277,6 +307,7 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { @Test public void updatePreferenceVisibility_bluetoothIsEnable_shouldShowCorrectText() { mShadowBluetoothAdapter.setEnabled(true); + when(mBluetoothAdapter.isEnabled()).thenReturn(true); mPreConnectedDeviceController.updatePreferenceVisibility(); verify(mSeeAllPreference).setSummary(""); @@ -285,9 +316,78 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { @Test public void updatePreferenceVisibility_bluetoothIsDisable_shouldShowCorrectText() { mShadowBluetoothAdapter.setEnabled(false); + when(mBluetoothAdapter.isEnabled()).thenReturn(false); mPreConnectedDeviceController.updatePreferenceVisibility(); verify(mSeeAllPreference).setSummary( mContext.getString(R.string.connected_device_see_all_summary)); } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SAVED_DEVICES_ORDER_BY_RECENCY) + public void updatePreferenceGroup_bluetoothIsEnable_shouldOrderByMostRecentlyConnected() { + when(mBluetoothAdapter.isEnabled()).thenReturn(true); + final BluetoothDevicePreference preference4 = + new BluetoothDevicePreference( + mContext, + mCachedDevice4, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference3 = + new BluetoothDevicePreference( + mContext, + mCachedDevice3, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference2 = + new BluetoothDevicePreference( + mContext, + mCachedDevice2, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + mPreConnectedDeviceController.onDeviceAdded(preference4); + mPreConnectedDeviceController.onDeviceAdded(preference3); + mPreConnectedDeviceController.onDeviceAdded(preference2); + + mPreConnectedDeviceController.updatePreferenceGroup(); + + // Refer to the order of {@link #mMostRecentlyConnectedDevices}, the first one is see all + // preference + assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(4); + assertThat(preference2.getOrder()).isEqualTo(0); + assertThat(preference4.getOrder()).isEqualTo(1); + assertThat(preference3.getOrder()).isEqualTo(2); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SAVED_DEVICES_ORDER_BY_RECENCY) + public void updatePreferenceGroup_bluetoothIsDisable_shouldShowOnlySeeAllPreference() { + when(mBluetoothAdapter.isEnabled()).thenReturn(false); + final BluetoothDevicePreference preference4 = + new BluetoothDevicePreference( + mContext, + mCachedDevice4, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference3 = + new BluetoothDevicePreference( + mContext, + mCachedDevice3, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference2 = + new BluetoothDevicePreference( + mContext, + mCachedDevice2, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + mPreConnectedDeviceController.onDeviceAdded(preference4); + mPreConnectedDeviceController.onDeviceAdded(preference3); + mPreConnectedDeviceController.onDeviceAdded(preference2); + + mPreConnectedDeviceController.updatePreferenceGroup(); + + // 1 see all preference + assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java index d2c44f938e4..81c0c35ccd6 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java @@ -25,29 +25,52 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.util.Pair; import androidx.lifecycle.LifecycleOwner; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceManager; +import com.android.settings.bluetooth.BluetoothDevicePreference; import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.ArrayList; +import java.util.List; + @RunWith(RobolectricTestRunner.class) public class SavedDeviceGroupControllerTest { + private static final String FAKE_ADDRESS_1 = "AA:AA:AA:AA:AA:01"; + private static final String FAKE_ADDRESS_2 = "AA:AA:AA:AA:AA:02"; + private static final String FAKE_ADDRESS_3 = "AA:AA:AA:AA:AA:03"; + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Mock private DashboardFragment mDashboardFragment; @Mock @@ -56,23 +79,57 @@ public class SavedDeviceGroupControllerTest { private DockUpdater mSavedDockUpdater; @Mock private PackageManager mPackageManager; + @Mock private BluetoothManager mBluetoothManager; + @Mock private BluetoothAdapter mBluetoothAdapter; + @Mock private CachedBluetoothDevice mCachedDevice1; + @Mock private CachedBluetoothDevice mCachedDevice2; + @Mock private CachedBluetoothDevice mCachedDevice3; + @Mock private BluetoothDevice mBluetoothDevice1; + @Mock private BluetoothDevice mBluetoothDevice2; + @Mock private BluetoothDevice mBluetoothDevice3; + @Mock private Drawable mDrawable; + @Mock private PreferenceManager mPreferenceManager; private Context mContext; private SavedDeviceGroupController mSavedDeviceGroupController; private LifecycleOwner mLifecycleOwner; private Lifecycle mLifecycle; + private PreferenceGroup mPreferenceGroup; @Before public void setUp() { MockitoAnnotations.initMocks(this); + Pair pairs = new Pair<>(mDrawable, "fake_device"); mContext = spy(RuntimeEnvironment.application); mLifecycleOwner = () -> mLifecycle; mLifecycle = new Lifecycle(mLifecycleOwner); doReturn(mContext).when(mDashboardFragment).getContext(); doReturn(mPackageManager).when(mContext).getPackageManager(); + + when(mCachedDevice1.getDevice()).thenReturn(mBluetoothDevice1); + when(mCachedDevice1.getAddress()).thenReturn(FAKE_ADDRESS_1); + when(mCachedDevice1.getDrawableWithDescription()).thenReturn(pairs); + when(mCachedDevice2.getDevice()).thenReturn(mBluetoothDevice2); + when(mCachedDevice2.getAddress()).thenReturn(FAKE_ADDRESS_2); + when(mCachedDevice2.getDrawableWithDescription()).thenReturn(pairs); + when(mCachedDevice3.getDevice()).thenReturn(mBluetoothDevice3); + when(mCachedDevice3.getAddress()).thenReturn(FAKE_ADDRESS_3); + when(mCachedDevice3.getDrawableWithDescription()).thenReturn(pairs); + final List mMostRecentlyConnectedDevices = new ArrayList<>(); + mMostRecentlyConnectedDevices.add(mBluetoothDevice1); + mMostRecentlyConnectedDevices.add(mBluetoothDevice2); + mMostRecentlyConnectedDevices.add(mBluetoothDevice3); + when(mContext.getSystemService(BluetoothManager.class)).thenReturn(mBluetoothManager); + when(mBluetoothManager.getAdapter()).thenReturn(mBluetoothAdapter); + when(mBluetoothAdapter.getMostRecentlyConnectedDevices()) + .thenReturn(mMostRecentlyConnectedDevices); + + mPreferenceGroup = spy(new PreferenceCategory(mContext)); + when(mPreferenceGroup.getPreferenceManager()).thenReturn(mPreferenceManager); mSavedDeviceGroupController = new SavedDeviceGroupController(mContext); mSavedDeviceGroupController.setBluetoothDeviceUpdater(mBluetoothDeviceUpdater); mSavedDeviceGroupController.setSavedDockUpdater(mSavedDockUpdater); + mSavedDeviceGroupController.setPreferenceGroup(mPreferenceGroup); } @Test @@ -118,4 +175,71 @@ public class SavedDeviceGroupControllerTest { assertThat(mSavedDeviceGroupController.getAvailabilityStatus()).isEqualTo( AVAILABLE); } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SAVED_DEVICES_ORDER_BY_RECENCY) + public void updatePreferenceGroup_bluetoothIsEnable_shouldOrderByMostRecentlyConnected() { + when(mBluetoothAdapter.isEnabled()).thenReturn(true); + final BluetoothDevicePreference preference3 = + new BluetoothDevicePreference( + mContext, + mCachedDevice3, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference2 = + new BluetoothDevicePreference( + mContext, + mCachedDevice2, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference1 = + new BluetoothDevicePreference( + mContext, + mCachedDevice1, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + mSavedDeviceGroupController.onDeviceAdded(preference3); + mSavedDeviceGroupController.onDeviceAdded(preference2); + mSavedDeviceGroupController.onDeviceAdded(preference1); + + mSavedDeviceGroupController.updatePreferenceGroup(); + + // Refer to the order of {@link #mMostRecentlyConnectedDevices} + assertThat(mPreferenceGroup.isVisible()).isTrue(); + assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(3); + assertThat(preference1.getOrder()).isEqualTo(0); + assertThat(preference2.getOrder()).isEqualTo(1); + assertThat(preference3.getOrder()).isEqualTo(2); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SAVED_DEVICES_ORDER_BY_RECENCY) + public void updatePreferenceGroup_bluetoothIsDisable_shouldShowNoPreference() { + when(mBluetoothAdapter.isEnabled()).thenReturn(false); + final BluetoothDevicePreference preference3 = + new BluetoothDevicePreference( + mContext, + mCachedDevice3, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference2 = + new BluetoothDevicePreference( + mContext, + mCachedDevice2, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference1 = + new BluetoothDevicePreference( + mContext, + mCachedDevice2, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + mSavedDeviceGroupController.onDeviceAdded(preference3); + mSavedDeviceGroupController.onDeviceAdded(preference2); + mSavedDeviceGroupController.onDeviceAdded(preference1); + + mSavedDeviceGroupController.updatePreferenceGroup(); + + assertThat(mPreferenceGroup.isVisible()).isFalse(); + } }