Merge "[Misc] Convert CastDevice to a Kotlin data class." into main
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
index 9bb591e..8ad647d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.CastTile
import com.android.systemui.statusbar.policy.CastController
+import com.android.systemui.statusbar.policy.CastDevice
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -73,9 +74,12 @@
@Test
fun onCastDevicesChanged_deviceNotConnectedOrConnecting_noSignal() = runTest {
val device =
- CastController.CastDevice().apply {
- state = CastController.CastDevice.STATE_DISCONNECTED
- }
+ CastDevice(
+ id = "id",
+ name = null,
+ state = CastDevice.CastState.Disconnected,
+ origin = CastDevice.CastOrigin.MediaProjection,
+ )
whenever(castController.castDevices).thenReturn(listOf(device))
val signal by collectLastValue(underTest.autoAddSignal(0))
@@ -91,11 +95,19 @@
@Test
fun onCastDevicesChanged_someDeviceConnecting_addSignal() = runTest {
val disconnectedDevice =
- CastController.CastDevice().apply {
- state = CastController.CastDevice.STATE_DISCONNECTED
- }
+ CastDevice(
+ id = "id",
+ name = null,
+ state = CastDevice.CastState.Disconnected,
+ origin = CastDevice.CastOrigin.MediaProjection,
+ )
val connectingDevice =
- CastController.CastDevice().apply { state = CastController.CastDevice.STATE_CONNECTING }
+ CastDevice(
+ id = "id",
+ name = null,
+ state = CastDevice.CastState.Connecting,
+ origin = CastDevice.CastOrigin.MediaProjection,
+ )
whenever(castController.castDevices)
.thenReturn(listOf(disconnectedDevice, connectingDevice))
@@ -112,11 +124,19 @@
@Test
fun onCastDevicesChanged_someDeviceConnected_addSignal() = runTest {
val disconnectedDevice =
- CastController.CastDevice().apply {
- state = CastController.CastDevice.STATE_DISCONNECTED
- }
+ CastDevice(
+ id = "id",
+ name = null,
+ state = CastDevice.CastState.Disconnected,
+ origin = CastDevice.CastOrigin.MediaProjection,
+ )
val connectedDevice =
- CastController.CastDevice().apply { state = CastController.CastDevice.STATE_CONNECTED }
+ CastDevice(
+ id = "id",
+ name = null,
+ state = CastDevice.CastState.Connected,
+ origin = CastDevice.CastOrigin.MediaProjection,
+ )
whenever(castController.castDevices).thenReturn(listOf(disconnectedDevice, connectedDevice))
val signal by collectLastValue(underTest.autoAddSignal(0))
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddable.kt
index b5bef9f..88f7169 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddable.kt
@@ -41,12 +41,7 @@
override fun ProducerScope<AutoAddSignal>.getCallback(): CastController.Callback {
return CastController.Callback {
- val isCasting =
- controller.castDevices.any {
- it.state == CastController.CastDevice.STATE_CONNECTED ||
- it.state == CastController.CastDevice.STATE_CONNECTING
- }
- if (isCasting) {
+ if (controller.castDevices.any { it.isCasting }) {
sendAdd()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 169cdc1..8a72e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -60,7 +60,7 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel;
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.DialogKt;
@@ -193,14 +193,13 @@
// case where multiple devices were active :-/.
private boolean willPopDialog() {
List<CastDevice> activeDevices = getActiveDevices();
- return activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo);
+ return activeDevices.isEmpty() || (activeDevices.get(0).getTag() instanceof RouteInfo);
}
private List<CastDevice> getActiveDevices() {
ArrayList<CastDevice> activeDevices = new ArrayList<>();
for (CastDevice device : mController.getCastDevices()) {
- if (device.state == CastDevice.STATE_CONNECTED
- || device.state == CastDevice.STATE_CONNECTING) {
+ if (device.isCasting()) {
activeDevices.add(device);
}
}
@@ -276,7 +275,7 @@
// We always choose the first device that's in the CONNECTED state in the case where
// multiple devices are CONNECTED at the same time.
for (CastDevice device : devices) {
- if (device.state == CastDevice.STATE_CONNECTED) {
+ if (device.getState() == CastDevice.CastState.Connected) {
state.value = true;
state.secondaryLabel = getDeviceName(device);
state.stateDescription = state.stateDescription + ","
@@ -284,7 +283,7 @@
R.string.accessibility_cast_name, state.label);
connecting = false;
break;
- } else if (device.state == CastDevice.STATE_CONNECTING) {
+ } else if (device.getState() == CastDevice.CastState.Connecting) {
connecting = true;
}
}
@@ -315,7 +314,7 @@
}
private String getDeviceName(CastDevice device) {
- return device.name != null ? device.name
+ return device.getName() != null ? device.getName()
: mContext.getString(R.string.quick_settings_cast_device_default_name);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 2011332..91b5d0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -39,7 +39,7 @@
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.DeviceControlsController;
@@ -430,8 +430,7 @@
boolean isCasting = false;
for (CastDevice device : mCastController.getCastDevices()) {
- if (device.state == CastDevice.STATE_CONNECTED
- || device.state == CastDevice.STATE_CONNECTING) {
+ if (device.isCasting()) {
isCasting = true;
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 3784132..2371eed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -62,7 +62,7 @@
import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -521,8 +521,7 @@
private void updateCast() {
boolean isCasting = false;
for (CastDevice device : mCast.getCastDevices()) {
- if (device.state == CastDevice.STATE_CONNECTING
- || device.state == CastDevice.STATE_CONNECTED) {
+ if (device.isCasting()) {
isCasting = true;
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
index abedd3220..a3dcc3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
@@ -37,15 +37,4 @@
void onCastDevicesChanged();
}
- public static final class CastDevice {
- public static final int STATE_DISCONNECTED = 0;
- public static final int STATE_CONNECTING = 1;
- public static final int STATE_CONNECTED = 2;
-
- public String id;
- public String name;
- public String description;
- public int state = STATE_DISCONNECTED;
- public Object tag;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index 64bdf60..45cb52a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -19,15 +19,12 @@
import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.os.Handler;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -37,8 +34,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.res.R;
-import com.android.systemui.util.Utils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -51,10 +46,11 @@
/** Platform implementation of the cast controller. **/
@SysUISingleton
public class CastControllerImpl implements CastController {
- private static final String TAG = "CastController";
+ public static final String TAG = "CastController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Context mContext;
+ private final PackageManager mPackageManager;
@GuardedBy("mCallbacks")
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final MediaRouter mMediaRouter;
@@ -68,8 +64,12 @@
private MediaProjectionInfo mProjection;
@Inject
- public CastControllerImpl(Context context, DumpManager dumpManager) {
+ public CastControllerImpl(
+ Context context,
+ PackageManager packageManager,
+ DumpManager dumpManager) {
mContext = context;
+ mPackageManager = packageManager;
mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mMediaRouter.setRouterGroupId(MediaRouter.MIRRORING_GROUP_ID);
mProjectionManager = (MediaProjectionManager)
@@ -156,36 +156,17 @@
final ArrayList<CastDevice> devices = new ArrayList<>();
synchronized(mRoutes) {
for (RouteInfo route : mRoutes.values()) {
- final CastDevice device = new CastDevice();
- device.id = route.getTag().toString();
- final CharSequence name = route.getName(mContext);
- device.name = name != null ? name.toString() : null;
- final CharSequence description = route.getDescription();
- device.description = description != null ? description.toString() : null;
-
- int statusCode = route.getStatusCode();
- if (statusCode == RouteInfo.STATUS_CONNECTING) {
- device.state = CastDevice.STATE_CONNECTING;
- } else if (route.isSelected() || statusCode == RouteInfo.STATUS_CONNECTED) {
- device.state = CastDevice.STATE_CONNECTED;
- } else {
- device.state = CastDevice.STATE_DISCONNECTED;
- }
-
- device.tag = route;
- devices.add(device);
+ devices.add(CastDevice.Companion.toCastDevice(route, mContext));
}
}
synchronized (mProjectionLock) {
if (mProjection != null) {
- final CastDevice device = new CastDevice();
- device.id = mProjection.getPackageName();
- device.name = getAppName(mProjection.getPackageName());
- device.description = mContext.getString(R.string.quick_settings_casting);
- device.state = CastDevice.STATE_CONNECTED;
- device.tag = mProjection;
- devices.add(device);
+ devices.add(
+ CastDevice.Companion.toCastDevice(
+ mProjection,
+ mContext,
+ mPackageManager));
}
}
@@ -194,18 +175,18 @@
@Override
public void startCasting(CastDevice device) {
- if (device == null || device.tag == null) return;
- final RouteInfo route = (RouteInfo) device.tag;
+ if (device == null || device.getTag() == null) return;
+ final RouteInfo route = (RouteInfo) device.getTag();
if (DEBUG) Log.d(TAG, "startCasting: " + routeToString(route));
mMediaRouter.selectRoute(ROUTE_TYPE_REMOTE_DISPLAY, route);
}
@Override
public void stopCasting(CastDevice device) {
- final boolean isProjection = device.tag instanceof MediaProjectionInfo;
+ final boolean isProjection = device.getTag() instanceof MediaProjectionInfo;
if (DEBUG) Log.d(TAG, "stopCasting isProjection=" + isProjection);
if (isProjection) {
- final MediaProjectionInfo projection = (MediaProjectionInfo) device.tag;
+ final MediaProjectionInfo projection = (MediaProjectionInfo) device.getTag();
if (Objects.equals(mProjectionManager.getActiveProjectionInfo(), projection)) {
mProjectionManager.stopActiveProjection();
} else {
@@ -219,7 +200,7 @@
@Override
public boolean hasConnectedCastDevice() {
return getCastDevices().stream().anyMatch(
- castDevice -> castDevice.state == CastDevice.STATE_CONNECTED);
+ castDevice -> castDevice.getState() == CastDevice.CastState.Connected);
}
private void setProjection(MediaProjectionInfo projection, boolean started) {
@@ -241,27 +222,6 @@
}
}
- private String getAppName(String packageName) {
- final PackageManager pm = mContext.getPackageManager();
- if (Utils.isHeadlessRemoteDisplayProvider(pm, packageName)) {
- return "";
- }
-
- try {
- final ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
- if (appInfo != null) {
- final CharSequence label = appInfo.loadLabel(pm);
- if (!TextUtils.isEmpty(label)) {
- return label.toString();
- }
- }
- Log.w(TAG, "No label found for package: " + packageName);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Error getting appName for package: " + packageName, e);
- }
- return packageName;
- }
-
private void updateRemoteDisplays() {
synchronized(mRoutes) {
mRoutes.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastDevice.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastDevice.kt
new file mode 100644
index 0000000..5fc160b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastDevice.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 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.systemui.statusbar.policy
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.media.MediaRouter
+import android.media.projection.MediaProjectionInfo
+import android.text.TextUtils
+import android.util.Log
+import com.android.systemui.res.R
+import com.android.systemui.util.Utils
+
+/** Represents a specific cast session. */
+data class CastDevice(
+ val id: String,
+ /** A human-readable name of what is receiving the cast (e.g. "Home Speaker", "Abc App"). */
+ val name: String?,
+ /** An optional description with more information about the cast. */
+ val description: String? = null,
+ val state: CastState,
+ val origin: CastOrigin,
+ /** Optional tag to use as a comparison value between cast sessions. */
+ val tag: Any? = null,
+) {
+ val isCasting = state == CastState.Connecting || state == CastState.Connected
+
+ companion object {
+ /** Creates a [CastDevice] based on the provided information from MediaRouter. */
+ fun MediaRouter.RouteInfo.toCastDevice(context: Context): CastDevice {
+ val state =
+ when {
+ statusCode == MediaRouter.RouteInfo.STATUS_CONNECTING -> CastState.Connecting
+ this.isSelected || statusCode == MediaRouter.RouteInfo.STATUS_CONNECTED ->
+ CastState.Connected
+ else -> CastState.Disconnected
+ }
+ return CastDevice(
+ id = this.tag.toString(),
+ name = this.getName(context)?.toString(),
+ description = this.description?.toString(),
+ state = state,
+ tag = this,
+ origin = CastOrigin.MediaRouter,
+ )
+ }
+
+ /** Creates a [CastDevice] based on the provided information from MediaProjection. */
+ fun MediaProjectionInfo.toCastDevice(
+ context: Context,
+ packageManager: PackageManager
+ ): CastDevice {
+ return CastDevice(
+ id = this.packageName,
+ name = getAppName(this.packageName, packageManager),
+ description = context.getString(R.string.quick_settings_casting),
+ state = CastState.Connected,
+ tag = this,
+ origin = CastOrigin.MediaProjection,
+ )
+ }
+
+ private fun getAppName(packageName: String, packageManager: PackageManager): String {
+ if (Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName)) {
+ return ""
+ }
+ try {
+ val appInfo = packageManager.getApplicationInfo(packageName, 0)
+ val label = appInfo.loadLabel(packageManager)
+ if (!TextUtils.isEmpty(label)) {
+ return label.toString()
+ }
+ Log.w(CastControllerImpl.TAG, "No label found for package: $packageName")
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(CastControllerImpl.TAG, "Error getting appName for package: $packageName", e)
+ }
+ return packageName
+ }
+ }
+
+ enum class CastState {
+ Disconnected,
+ Connecting,
+ Connected,
+ }
+
+ enum class CastOrigin {
+ /** SysUI found out about this cast device from MediaRouter APIs. */
+ MediaRouter,
+ /** SysUI found out about this cast device from MediaProjection APIs. */
+ MediaProjection,
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 50cf5cc5..9f12b18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -39,7 +39,6 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.keyguard.TestScopeProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.classifier.FalsingManagerFake;
@@ -54,14 +53,11 @@
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.WifiIndicators;
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository;
-import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import kotlinx.coroutines.test.TestScope;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -105,12 +101,10 @@
@Mock
private QsEventLogger mUiEventLogger;
- private WifiInteractor mWifiInteractor;
private final TileJavaAdapter mJavaAdapter = new TileJavaAdapter();
private final FakeConnectivityRepository mConnectivityRepository =
new FakeConnectivityRepository();
private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
- private final TestScope mTestScope = TestScopeProvider.getTestScope();
private TestableLooper mTestableLooper;
private CastTile mCastTile;
@@ -172,8 +166,7 @@
@Test
public void testStateActive_wifiEnabledAndCasting() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastController.CastDevice.STATE_CONNECTED;
+ CastDevice device = createConnectedCastDevice();
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -232,8 +225,7 @@
@Test
public void stateActive_wifiConnectedAndCasting_newPipeline() {
createAndStartTileNewImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastDevice.STATE_CONNECTED;
+ CastDevice device = createConnectedCastDevice();
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -248,8 +240,7 @@
@Test
public void stateActive_ethernetConnectedAndCasting_newPipeline() {
createAndStartTileNewImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastDevice.STATE_CONNECTED;
+ CastDevice device = createConnectedCastDevice();
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -286,8 +277,7 @@
@Test
public void testStateActive_hotspotEnabledAndConnectedAndCasting() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastController.CastDevice.STATE_CONNECTED;
+ CastDevice device = createConnectedCastDevice();
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -309,9 +299,13 @@
@Test
public void testHandleClick_castDevicePresent() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastDevice.STATE_CONNECTED;
- device.tag = mock(MediaRouter.RouteInfo.class);
+ CastDevice device = new CastDevice(
+ "id",
+ /* name= */ null,
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaRouter,
+ /* tag= */ mock(MediaRouter.RouteInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -327,9 +321,13 @@
@Test
public void testHandleClick_projectionOnly() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastDevice.STATE_CONNECTED;
- device.tag = mock(MediaProjectionInfo.class);
+ CastDevice device = new CastDevice(
+ "id",
+ /* name= */ null,
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ mock(MediaProjectionInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -344,31 +342,40 @@
@Test
public void testUpdateState_projectionOnly() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastDevice.STATE_CONNECTED;
- device.tag = mock(MediaProjectionInfo.class);
- device.name = "Test Projection Device";
+ CastDevice device = new CastDevice(
+ "id",
+ /* name= */ "Test Projection Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ mock(MediaProjectionInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
enableWifiAndProcessMessages();
assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
- assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(device.name));
+ assertTrue(mCastTile.getState().secondaryLabel.toString()
+ .startsWith("Test Projection Device"));
}
@Test
public void testUpdateState_castingAndProjection() {
createAndStartTileOldImpl();
- CastController.CastDevice casting = new CastController.CastDevice();
- casting.state = CastDevice.STATE_CONNECTED;
- casting.tag = mock(RouteInfo.class);
- casting.name = "Test Casting Device";
-
- CastController.CastDevice projection = new CastController.CastDevice();
- projection.state = CastDevice.STATE_CONNECTED;
- projection.tag = mock(MediaProjectionInfo.class);
- projection.name = "Test Projection Device";
+ CastDevice casting = new CastDevice(
+ "id1",
+ /* name= */ "Test Casting Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaRouter,
+ /* tag= */ mock(RouteInfo.class));
+ CastDevice projection = new CastDevice(
+ "id2",
+ /* name= */ "Test Projection Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ mock(MediaProjectionInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(casting);
@@ -379,22 +386,27 @@
// Note here that the tile should be active, and should choose casting over projection.
assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
- assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(casting.name));
+ assertTrue(mCastTile.getState().secondaryLabel.toString()
+ .startsWith("Test Casting Device"));
}
@Test
public void testUpdateState_connectedAndConnecting() {
createAndStartTileOldImpl();
- CastController.CastDevice connecting = new CastController.CastDevice();
- connecting.state = CastDevice.STATE_CONNECTING;
- connecting.tag = mock(RouteInfo.class);
- connecting.name = "Test Casting Device";
-
- CastController.CastDevice connected = new CastController.CastDevice();
- connected.state = CastDevice.STATE_CONNECTED;
- connected.tag = mock(RouteInfo.class);
- connected.name = "Test Casting Device";
-
+ CastDevice connecting = new CastDevice(
+ "id",
+ /* name= */ "Test Connecting Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connecting,
+ /* origin= */ CastDevice.CastOrigin.MediaRouter,
+ /* tag= */ mock(RouteInfo.class));
+ CastDevice connected = new CastDevice(
+ "id",
+ /* name= */ "Test Connected Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaRouter,
+ /* tag= */ mock(RouteInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(connecting);
devices.add(connected);
@@ -404,7 +416,8 @@
// Tile should be connected and always prefer the connected device.
assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
- assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(connected.name));
+ assertTrue(mCastTile.getState().secondaryLabel.toString()
+ .startsWith("Test Connected Device"));
}
@Test
@@ -427,8 +440,7 @@
@Test
public void testExpandView_casting_projection() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastController.CastDevice.STATE_CONNECTED;
+ CastDevice device = createConnectedCastDevice();
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -441,9 +453,14 @@
@Test
public void testExpandView_connecting_projection() {
createAndStartTileOldImpl();
- CastController.CastDevice connecting = new CastController.CastDevice();
- connecting.state = CastDevice.STATE_CONNECTING;
- connecting.name = "Test Casting Device";
+ CastDevice connecting = new CastDevice(
+ "id",
+ /* name= */
+ "Test Projection Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ mock(MediaProjectionInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(connecting);
@@ -457,9 +474,14 @@
@Test
public void testExpandView_casting_mediaRoute() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastDevice.STATE_CONNECTED;
- device.tag = mock(MediaRouter.RouteInfo.class);
+ CastDevice device = new CastDevice(
+ "id",
+ /* name= */ "Test Router Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaRouter,
+ /* tag= */ mock(RouteInfo.class));
+
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -472,11 +494,13 @@
@Test
public void testExpandView_connecting_mediaRoute() {
createAndStartTileOldImpl();
- CastController.CastDevice connecting = new CastController.CastDevice();
- connecting.state = CastDevice.STATE_CONNECTING;
- connecting.tag = mock(RouteInfo.class);
- connecting.name = "Test Casting Device";
-
+ CastDevice connecting = new CastDevice(
+ "id",
+ /* name= */ "Test Router Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connecting,
+ /* origin= */ CastDevice.CastOrigin.MediaRouter,
+ /* tag= */ mock(RouteInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(connecting);
when(mController.getCastDevices()).thenReturn(devices);
@@ -567,4 +591,14 @@
hotspotCallbackArgumentCaptor.capture());
mHotspotCallback = hotspotCallbackArgumentCaptor.getValue();
}
+
+ private CastDevice createConnectedCastDevice() {
+ return new CastDevice(
+ "id",
+ /* name= */ null,
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ null);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index f0bc655..665544d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -61,7 +61,7 @@
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceControlsController;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -422,9 +422,17 @@
}
private static List<CastDevice> buildFakeCastDevice(boolean isCasting) {
- CastDevice cd = new CastDevice();
- cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED;
- return Collections.singletonList(cd);
+ CastDevice.CastState state = isCasting
+ ? CastDevice.CastState.Connected
+ : CastDevice.CastState.Disconnected;
+ return Collections.singletonList(
+ new CastDevice(
+ "id",
+ /* name= */ null,
+ /* description= */ null,
+ /* state= */ state,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ null));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
index 68c1b8d..6894e6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
@@ -9,6 +9,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.pm.PackageManager;
import android.media.MediaRouter;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
@@ -52,8 +53,12 @@
mContext.addMockSystemService(MediaRouter.class, mMediaRouter);
mContext.addMockSystemService(MediaProjectionManager.class, mMediaProjectionManager);
when(mMediaProjectionManager.getActiveProjectionInfo()).thenReturn(mProjection);
+ when(mProjection.getPackageName()).thenReturn("fake.package");
- mController = new CastControllerImpl(mContext, mock(DumpManager.class));
+ mController = new CastControllerImpl(
+ mContext,
+ mock(PackageManager.class),
+ mock(DumpManager.class));
}
@Test
@@ -148,16 +153,26 @@
@Test
public void hasConnectedCastDevice_connected() {
- CastController.CastDevice castDevice = new CastController.CastDevice();
- castDevice.state = CastController.CastDevice.STATE_CONNECTED;
+ CastDevice castDevice = new CastDevice(
+ "id",
+ /* name= */ null,
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ null);
mController.startCasting(castDevice);
assertTrue(mController.hasConnectedCastDevice());
}
@Test
public void hasConnectedCastDevice_notConnected() {
- CastController.CastDevice castDevice = new CastController.CastDevice();
- castDevice.state = CastController.CastDevice.STATE_CONNECTING;
+ CastDevice castDevice = new CastDevice(
+ "id",
+ /* name= */ null,
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connecting,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ null);
mController.startCasting(castDevice);
assertTrue(mController.hasConnectedCastDevice());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
new file mode 100644
index 0000000..03ad66c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2024 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.systemui.statusbar.policy
+
+import android.Manifest
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.media.MediaRouter
+import android.media.projection.MediaProjectionInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.CastDevice.Companion.toCastDevice
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito.doAnswer
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class CastDeviceTest : SysuiTestCase() {
+ private val mockAppInfo =
+ mock<ApplicationInfo>().apply { whenever(this.loadLabel(any())).thenReturn("") }
+
+ private val packageManager =
+ mock<PackageManager>().apply {
+ whenever(
+ this.checkPermission(
+ Manifest.permission.REMOTE_DISPLAY_PROVIDER,
+ HEADLESS_REMOTE_PACKAGE,
+ )
+ )
+ .thenReturn(PackageManager.PERMISSION_GRANTED)
+ whenever(
+ this.checkPermission(
+ Manifest.permission.REMOTE_DISPLAY_PROVIDER,
+ NORMAL_PACKAGE,
+ )
+ )
+ .thenReturn(PackageManager.PERMISSION_DENIED)
+
+ doAnswer {
+ // See Utils.isHeadlessRemoteDisplayProvider
+ if ((it.arguments[0] as Intent).`package` == HEADLESS_REMOTE_PACKAGE) {
+ emptyList()
+ } else {
+ listOf(mock<ResolveInfo>())
+ }
+ }
+ .whenever(this)
+ .queryIntentActivities(any(), ArgumentMatchers.anyInt())
+
+ whenever(this.getApplicationInfo(any<String>(), any<Int>())).thenReturn(mockAppInfo)
+ }
+
+ @Test
+ fun isCasting_disconnected_false() {
+ val device =
+ CastDevice(
+ id = "id",
+ name = "name",
+ state = CastDevice.CastState.Disconnected,
+ origin = CastDevice.CastOrigin.MediaRouter,
+ )
+
+ assertThat(device.isCasting).isFalse()
+ }
+
+ @Test
+ fun isCasting_connecting_true() {
+ val device =
+ CastDevice(
+ id = "id",
+ name = "name",
+ state = CastDevice.CastState.Connecting,
+ origin = CastDevice.CastOrigin.MediaRouter,
+ )
+
+ assertThat(device.isCasting).isTrue()
+ }
+
+ @Test
+ fun isCasting_connected_true() {
+ val device =
+ CastDevice(
+ id = "id",
+ name = "name",
+ state = CastDevice.CastState.Connected,
+ origin = CastDevice.CastOrigin.MediaRouter,
+ )
+
+ assertThat(device.isCasting).isTrue()
+ }
+
+ @Test
+ fun routeToCastDevice_statusNone_stateDisconnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_NONE)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusNotAvailable_stateDisconnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusScanning_stateDisconnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_SCANNING)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusAvailable_isSelectedFalse_stateDisconnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_AVAILABLE)
+ whenever(this.isSelected).thenReturn(false)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusAvailable_isSelectedTrue_stateConnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_AVAILABLE)
+ whenever(this.isSelected).thenReturn(true)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusInUse_isSelectedFalse_stateDisconnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_IN_USE)
+ whenever(this.isSelected).thenReturn(false)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusInUse_isSelectedTrue_stateConnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_IN_USE)
+ whenever(this.isSelected).thenReturn(true)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusConnecting_isSelectedFalse_stateConnecting() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_CONNECTING)
+ whenever(this.isSelected).thenReturn(false)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connecting)
+ }
+
+ @Test
+ fun routeToCastDevice_statusConnecting_isSelectedTrue_stateConnecting() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_CONNECTING)
+ whenever(this.isSelected).thenReturn(true)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connecting)
+ }
+
+ @Test
+ fun routeToCastDevice_statusConnected_isSelectedFalse_stateConnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_CONNECTED)
+ whenever(this.isSelected).thenReturn(false)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusConnected_isSelectedTrue_stateConnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_CONNECTED)
+ whenever(this.isSelected).thenReturn(true)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+ }
+
+ @Test
+ fun routeToCastDevice_tagIsStringType_idMatchesTag() {
+ val route = mock<MediaRouter.RouteInfo>().apply { whenever(this.tag).thenReturn("FakeTag") }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.id).isEqualTo("FakeTag")
+ }
+
+ @Test
+ fun routeToCastDevice_tagIsOtherType_idMatchesTag() {
+ val tag = listOf("tag1", "tag2")
+ val route = mock<MediaRouter.RouteInfo>().apply { whenever(this.tag).thenReturn(tag) }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.id).isEqualTo(tag.toString())
+ }
+
+ @Test
+ fun routeToCastDevice_nameMatchesName() {
+ val route = mockRouteInfo().apply { whenever(this.getName(context)).thenReturn("FakeName") }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.name).isEqualTo("FakeName")
+ }
+
+ @Test
+ fun routeToCastDevice_descriptionMatchesDescription() {
+ val route =
+ mockRouteInfo().apply { whenever(this.description).thenReturn("FakeDescription") }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.description).isEqualTo("FakeDescription")
+ }
+
+ @Test
+ fun routeToCastDevice_tagIsRoute() {
+ val route = mockRouteInfo()
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.tag).isEqualTo(route)
+ }
+
+ @Test
+ fun routeToCastDevice_originIsMediaRouter() {
+ val route = mockRouteInfo()
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.origin).isEqualTo(CastDevice.CastOrigin.MediaRouter)
+ }
+
+ @Test
+ fun routeToCastDevice_nullValues_ok() {
+ val device = mockRouteInfo().toCastDevice(context)
+
+ assertThat(device.name).isNull()
+ assertThat(device.description).isNull()
+ }
+
+ @Test
+ fun projectionToCastDevice_idMatchesPackage() {
+ val projection =
+ mock<MediaProjectionInfo>().apply {
+ whenever(this.packageName).thenReturn("fake.package")
+ }
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.id).isEqualTo("fake.package")
+ }
+
+ @Test
+ fun projectionToCastDevice_name_packageIsHeadlessRemote_isEmpty() {
+ val projection =
+ mock<MediaProjectionInfo>().apply {
+ whenever(this.packageName).thenReturn(HEADLESS_REMOTE_PACKAGE)
+ }
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.name).isEmpty()
+ }
+
+ @Test
+ fun projectionToCastDevice_name_packageMissingFromPackageManager_isPackageName() {
+ val projection =
+ mock<MediaProjectionInfo>().apply {
+ whenever(this.packageName).thenReturn(NORMAL_PACKAGE)
+ }
+
+ whenever(packageManager.getApplicationInfo(eq(NORMAL_PACKAGE), any<Int>()))
+ .thenThrow(PackageManager.NameNotFoundException())
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.name).isEqualTo(NORMAL_PACKAGE)
+ }
+
+ @Test
+ fun projectionToCastDevice_name_nameFromPackageManagerEmpty_isPackageName() {
+ val projection =
+ mock<MediaProjectionInfo>().apply {
+ whenever(this.packageName).thenReturn(NORMAL_PACKAGE)
+ }
+
+ val appInfo = mock<ApplicationInfo>()
+ whenever(appInfo.loadLabel(packageManager)).thenReturn("")
+ whenever(packageManager.getApplicationInfo(eq(NORMAL_PACKAGE), any<Int>()))
+ .thenReturn(appInfo)
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.name).isEqualTo(NORMAL_PACKAGE)
+ }
+
+ @Test
+ fun projectionToCastDevice_name_packageManagerHasName_isName() {
+ val projection =
+ mock<MediaProjectionInfo>().apply {
+ whenever(this.packageName).thenReturn(NORMAL_PACKAGE)
+ }
+
+ val appInfo = mock<ApplicationInfo>()
+ whenever(appInfo.loadLabel(packageManager)).thenReturn("Valid App Name")
+ whenever(packageManager.getApplicationInfo(eq(NORMAL_PACKAGE), any<Int>()))
+ .thenReturn(appInfo)
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.name).isEqualTo("Valid App Name")
+ }
+
+ @Test
+ fun projectionToCastDevice_descriptionIsCasting() {
+ val projection = mockProjectionInfo()
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.description).isEqualTo(context.getString(R.string.quick_settings_casting))
+ }
+
+ @Test
+ fun projectionToCastDevice_stateIsConnected() {
+ val projection = mockProjectionInfo()
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+ }
+
+ @Test
+ fun projectionToCastDevice_tagIsProjection() {
+ val projection = mockProjectionInfo()
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.tag).isEqualTo(projection)
+ }
+
+ @Test
+ fun projectionToCastDevice_originIsMediaProjection() {
+ val projection = mockProjectionInfo()
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.origin).isEqualTo(CastDevice.CastOrigin.MediaProjection)
+ }
+
+ private fun mockRouteInfo(): MediaRouter.RouteInfo {
+ return mock<MediaRouter.RouteInfo>().apply { whenever(this.tag).thenReturn(Any()) }
+ }
+
+ private fun mockProjectionInfo(): MediaProjectionInfo {
+ return mock<MediaProjectionInfo>().apply {
+ whenever(this.packageName).thenReturn("fake.package")
+ }
+ }
+
+ private companion object {
+ const val HEADLESS_REMOTE_PACKAGE = "headless.remote.package"
+ const val NORMAL_PACKAGE = "normal.package"
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
index 84ace7c..5fae38f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
@@ -18,6 +18,7 @@
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.Callback;
+import com.android.systemui.statusbar.policy.CastDevice;
import java.util.ArrayList;
import java.util.List;