Merge "[Output Switcher] Deal with deplicated id and route preference"
This commit is contained in:
@@ -41,11 +41,13 @@ import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouter2Manager;
|
||||
import android.media.RouteListingPreference;
|
||||
import android.media.RoutingSessionInfo;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.DoNotInline;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -53,9 +55,13 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* InfoMediaManager provide interface to get InfoMediaDevice list.
|
||||
@@ -448,10 +454,12 @@ public class InfoMediaManager extends MediaManager {
|
||||
}
|
||||
|
||||
private synchronized List<MediaRoute2Info> getAvailableRoutes(String packageName) {
|
||||
final List<MediaRoute2Info> infos = new ArrayList<>();
|
||||
List<MediaRoute2Info> infos = new ArrayList<>();
|
||||
RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo(packageName);
|
||||
List<MediaRoute2Info> selectedRouteInfos = new ArrayList<>();
|
||||
if (routingSessionInfo != null) {
|
||||
infos.addAll(mRouterManager.getSelectedRoutes(routingSessionInfo));
|
||||
selectedRouteInfos = mRouterManager.getSelectedRoutes(routingSessionInfo);
|
||||
infos.addAll(selectedRouteInfos);
|
||||
infos.addAll(mRouterManager.getSelectableRoutes(routingSessionInfo));
|
||||
}
|
||||
final List<MediaRoute2Info> transferableRoutes =
|
||||
@@ -468,11 +476,26 @@ public class InfoMediaManager extends MediaManager {
|
||||
infos.add(transferableRoute);
|
||||
}
|
||||
}
|
||||
return infos;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
RouteListingPreference routeListingPreference =
|
||||
mRouterManager.getRouteListingPreference(mPackageName);
|
||||
if (routeListingPreference != null) {
|
||||
final List<RouteListingPreference.Item> preferenceRouteListing =
|
||||
Api34Impl.composePreferenceRouteListing(
|
||||
routeListingPreference);
|
||||
infos = Api34Impl.arrangeRouteListByPreference(selectedRouteInfos,
|
||||
infos,
|
||||
preferenceRouteListing);
|
||||
}
|
||||
return Api34Impl.filterDuplicatedIds(infos);
|
||||
} else {
|
||||
return infos;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void addMediaDevice(MediaRoute2Info route) {
|
||||
//TODO(b/258141461): Attach flag and disable reason in MediaDevice
|
||||
final int deviceType = route.getType();
|
||||
MediaDevice mediaDevice = null;
|
||||
switch (deviceType) {
|
||||
@@ -574,4 +597,61 @@ public class InfoMediaManager extends MediaManager {
|
||||
refreshDevices();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(34)
|
||||
private static class Api34Impl {
|
||||
@DoNotInline
|
||||
static List<RouteListingPreference.Item> composePreferenceRouteListing(
|
||||
RouteListingPreference routeListingPreference) {
|
||||
List<RouteListingPreference.Item> finalizedItemList = new ArrayList<>();
|
||||
List<RouteListingPreference.Item> itemList = routeListingPreference.getItems();
|
||||
for (RouteListingPreference.Item item : itemList) {
|
||||
//Put suggested devices on the top first before further organization
|
||||
if (item.getFlags() == RouteListingPreference.Item.FLAG_SUGGESTED_ROUTE) {
|
||||
finalizedItemList.add(0, item);
|
||||
} else {
|
||||
finalizedItemList.add(item);
|
||||
}
|
||||
}
|
||||
return finalizedItemList;
|
||||
}
|
||||
|
||||
|
||||
@DoNotInline
|
||||
static synchronized List<MediaRoute2Info> filterDuplicatedIds(List<MediaRoute2Info> infos) {
|
||||
List<MediaRoute2Info> filteredInfos = new ArrayList<>();
|
||||
Set<String> foundDeduplicationIds = new HashSet<>();
|
||||
for (MediaRoute2Info mediaRoute2Info : infos) {
|
||||
if (!Collections.disjoint(mediaRoute2Info.getDeduplicationIds(),
|
||||
foundDeduplicationIds)) {
|
||||
continue;
|
||||
}
|
||||
filteredInfos.add(mediaRoute2Info);
|
||||
foundDeduplicationIds.addAll(mediaRoute2Info.getDeduplicationIds());
|
||||
}
|
||||
return filteredInfos;
|
||||
}
|
||||
|
||||
@DoNotInline
|
||||
static List<MediaRoute2Info> arrangeRouteListByPreference(
|
||||
List<MediaRoute2Info> selectedRouteInfos, List<MediaRoute2Info> infolist,
|
||||
List<RouteListingPreference.Item> preferenceRouteListing) {
|
||||
final List<MediaRoute2Info> sortedInfoList = new ArrayList<>(selectedRouteInfos);
|
||||
for (RouteListingPreference.Item item : preferenceRouteListing) {
|
||||
for (MediaRoute2Info info : infolist) {
|
||||
if (item.getRouteId().equals(info.getId())
|
||||
&& !selectedRouteInfos.contains(info)) {
|
||||
sortedInfoList.add(info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sortedInfoList.size() != infolist.size()) {
|
||||
infolist.removeAll(sortedInfoList);
|
||||
sortedInfoList.addAll(infolist.stream().filter(
|
||||
MediaRoute2Info::isSystemRoute).collect(Collectors.toList()));
|
||||
}
|
||||
return sortedInfoList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.settingslib.media;
|
||||
import static android.media.MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
|
||||
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
|
||||
import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
|
||||
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
|
||||
import static android.media.MediaRoute2ProviderService.REASON_NETWORK_ERROR;
|
||||
@@ -37,14 +38,18 @@ import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouter2Manager;
|
||||
import android.media.RouteListingPreference;
|
||||
import android.media.RoutingSessionInfo;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.os.Build;
|
||||
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.testutils.shadow.ShadowRouter2Manager;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -53,9 +58,11 @@ import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowRouter2Manager.class})
|
||||
@@ -63,7 +70,15 @@ public class InfoMediaManagerTest {
|
||||
|
||||
private static final String TEST_PACKAGE_NAME = "com.test.packagename";
|
||||
private static final String TEST_ID = "test_id";
|
||||
private static final String TEST_ID_1 = "test_id_1";
|
||||
private static final String TEST_ID_2 = "test_id_2";
|
||||
private static final String TEST_ID_3 = "test_id_3";
|
||||
private static final String TEST_ID_4 = "test_id_4";
|
||||
|
||||
private static final String TEST_NAME = "test_name";
|
||||
private static final String TEST_DUPLICATED_ID_1 = "test_duplicated_id_1";
|
||||
private static final String TEST_DUPLICATED_ID_2 = "test_duplicated_id_2";
|
||||
private static final String TEST_DUPLICATED_ID_3 = "test_duplicated_id_3";
|
||||
|
||||
@Mock
|
||||
private MediaRouter2Manager mRouterManager;
|
||||
@@ -104,6 +119,7 @@ public class InfoMediaManagerTest {
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getDeduplicationIds()).thenReturn(Set.of());
|
||||
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
routes.add(info);
|
||||
@@ -155,6 +171,7 @@ public class InfoMediaManagerTest {
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getDeduplicationIds()).thenReturn(Set.of());
|
||||
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
routes.add(info);
|
||||
@@ -191,6 +208,7 @@ public class InfoMediaManagerTest {
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getDeduplicationIds()).thenReturn(Set.of());
|
||||
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
routes.add(info);
|
||||
@@ -207,12 +225,105 @@ public class InfoMediaManagerTest {
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRoutesChanged_getAvailableRoutes_shouldFilterDevice() {
|
||||
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
|
||||
Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo);
|
||||
|
||||
final List<String> selectedRoutes = new ArrayList<>();
|
||||
selectedRoutes.add(TEST_ID);
|
||||
when(sessionInfo.getSelectedRoutes()).thenReturn(selectedRoutes);
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
|
||||
mShadowRouter2Manager.setTransferableRoutes(getRoutesListWithDuplicatedIds());
|
||||
|
||||
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
|
||||
assertThat(mediaDevice).isNull();
|
||||
|
||||
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
|
||||
|
||||
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
|
||||
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
|
||||
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRouteChanged_getAvailableRoutesWithPrefernceListExit_ordersRoutes() {
|
||||
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
|
||||
Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
|
||||
final List<RouteListingPreference.Item> preferenceItemList = new ArrayList<>();
|
||||
RouteListingPreference.Item item1 = new RouteListingPreference.Item.Builder(
|
||||
TEST_ID_4).build();
|
||||
RouteListingPreference.Item item2 = new RouteListingPreference.Item.Builder(
|
||||
TEST_ID_3).build();
|
||||
preferenceItemList.add(item1);
|
||||
preferenceItemList.add(item2);
|
||||
when(mRouterManager.getRouteListingPreference(TEST_PACKAGE_NAME))
|
||||
.thenReturn(new RouteListingPreference.Builder().setItems(
|
||||
preferenceItemList).setUseSystemOrdering(false).build());
|
||||
|
||||
final List<MediaRoute2Info> selectedRoutes = new ArrayList<>();
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.isSystemRoute()).thenReturn(true);
|
||||
selectedRoutes.add(info);
|
||||
when(mRouterManager.getSelectedRoutes(any())).thenReturn(selectedRoutes);
|
||||
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo);
|
||||
|
||||
when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME)).thenReturn(routingSessionInfos);
|
||||
when(sessionInfo.getSelectedRoutes()).thenReturn(ImmutableList.of(TEST_ID));
|
||||
|
||||
setTransferableRoutesList();
|
||||
|
||||
mInfoMediaManager.mRouterManager = mRouterManager;
|
||||
|
||||
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
|
||||
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID);
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_4);
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_3);
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(3);
|
||||
}
|
||||
|
||||
private List<MediaRoute2Info> setTransferableRoutesList() {
|
||||
final List<MediaRoute2Info> transferableRoutes = new ArrayList<>();
|
||||
final MediaRoute2Info transferableInfo1 = mock(MediaRoute2Info.class);
|
||||
when(transferableInfo1.getId()).thenReturn(TEST_ID_2);
|
||||
when(transferableInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(transferableInfo1.getType()).thenReturn(TYPE_REMOTE_TV);
|
||||
transferableRoutes.add(transferableInfo1);
|
||||
|
||||
final MediaRoute2Info transferableInfo2 = mock(MediaRoute2Info.class);
|
||||
when(transferableInfo2.getId()).thenReturn(TEST_ID_3);
|
||||
when(transferableInfo2.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
transferableRoutes.add(transferableInfo2);
|
||||
|
||||
final MediaRoute2Info transferableInfo3 = mock(MediaRoute2Info.class);
|
||||
when(transferableInfo3.getId()).thenReturn(TEST_ID_4);
|
||||
when(transferableInfo3.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
transferableRoutes.add(transferableInfo3);
|
||||
|
||||
when(mRouterManager.getTransferableRoutes(TEST_PACKAGE_NAME)).thenReturn(
|
||||
transferableRoutes);
|
||||
|
||||
return transferableRoutes;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRoutesChanged_buildAllRoutes_shouldAddMediaDevice() {
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.isSystemRoute()).thenReturn(true);
|
||||
when(info.getDeduplicationIds()).thenReturn(Set.of());
|
||||
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
routes.add(info);
|
||||
@@ -229,6 +340,47 @@ public class InfoMediaManagerTest {
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
|
||||
}
|
||||
|
||||
private List<MediaRoute2Info> getRoutesListWithDuplicatedIds() {
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.isSystemRoute()).thenReturn(true);
|
||||
when(info.getDeduplicationIds()).thenReturn(
|
||||
Set.of(TEST_DUPLICATED_ID_1, TEST_DUPLICATED_ID_2));
|
||||
routes.add(info);
|
||||
|
||||
final MediaRoute2Info info1 = mock(MediaRoute2Info.class);
|
||||
when(info1.getId()).thenReturn(TEST_ID_1);
|
||||
when(info1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info1.isSystemRoute()).thenReturn(true);
|
||||
when(info1.getDeduplicationIds()).thenReturn(Set.of(TEST_DUPLICATED_ID_3));
|
||||
routes.add(info1);
|
||||
|
||||
final MediaRoute2Info info2 = mock(MediaRoute2Info.class);
|
||||
when(info2.getId()).thenReturn(TEST_ID_2);
|
||||
when(info2.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info2.isSystemRoute()).thenReturn(true);
|
||||
when(info2.getDeduplicationIds()).thenReturn(Set.of(TEST_DUPLICATED_ID_3));
|
||||
routes.add(info2);
|
||||
|
||||
final MediaRoute2Info info3 = mock(MediaRoute2Info.class);
|
||||
when(info3.getId()).thenReturn(TEST_ID_3);
|
||||
when(info3.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info3.isSystemRoute()).thenReturn(true);
|
||||
when(info3.getDeduplicationIds()).thenReturn(Set.of(TEST_DUPLICATED_ID_1));
|
||||
routes.add(info3);
|
||||
|
||||
final MediaRoute2Info info4 = mock(MediaRoute2Info.class);
|
||||
when(info4.getId()).thenReturn(TEST_ID_4);
|
||||
when(info4.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info4.isSystemRoute()).thenReturn(true);
|
||||
when(info4.getDeduplicationIds()).thenReturn(Set.of(TEST_DUPLICATED_ID_2));
|
||||
routes.add(info4);
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectDeviceWithoutPackageName_noSession_returnFalse() {
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
@@ -255,6 +407,7 @@ public class InfoMediaManagerTest {
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getDeduplicationIds()).thenReturn(Set.of());
|
||||
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
routes.add(info);
|
||||
@@ -620,6 +773,7 @@ public class InfoMediaManagerTest {
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
mInfoMediaManager.registerCallback(mCallback);
|
||||
|
||||
when(info.getDeduplicationIds()).thenReturn(Set.of());
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user