Loading packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +22 −22 Original line number Original line Diff line number Diff line Loading @@ -21,7 +21,7 @@ import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY; import android.app.Dialog; import android.app.Dialog; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.media.projection.MediaProjectionInfo; import android.media.MediaRouter.RouteInfo; import android.provider.Settings; import android.provider.Settings; import android.service.quicksettings.Tile; import android.service.quicksettings.Tile; import android.util.Log; import android.util.Log; Loading @@ -48,8 +48,9 @@ import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NetworkController; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedHashMap; import java.util.Set; import java.util.List; import javax.inject.Inject; import javax.inject.Inject; Loading Loading @@ -128,35 +129,30 @@ public class CastTile extends QSTileImpl<BooleanState> { return; return; } } CastDevice activeProjection = getActiveDeviceMediaProjection(); List<CastDevice> activeDevices = getActiveDevices(); if (activeProjection == null) { // We want to pop up the media route selection dialog if we either have no active devices if (mKeyguard.isSecure() && !mKeyguard.canSkipBouncer()) { // (neither routes nor projection), or if we have an active route. In other cases, we assume // that a projection is active. This is messy, but this tile never correctly handled the // case where multiple devices were active :-/. if (activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo)) { mActivityStarter.postQSRunnableDismissingKeyguard(() -> { mActivityStarter.postQSRunnableDismissingKeyguard(() -> { showDetail(true); showDetail(true); }); }); } else { } else { showDetail(true); mController.stopCasting(activeDevices.get(0)); } } else { mController.stopCasting(activeProjection); } } } } private CastDevice getActiveDeviceMediaProjection() { private List<CastDevice> getActiveDevices() { CastDevice activeDevice = null; ArrayList<CastDevice> activeDevices = new ArrayList<>(); for (CastDevice device : mController.getCastDevices()) { for (CastDevice device : mController.getCastDevices()) { if (device.state == CastDevice.STATE_CONNECTED if (device.state == CastDevice.STATE_CONNECTED || device.state == CastDevice.STATE_CONNECTING) { || device.state == CastDevice.STATE_CONNECTING) { activeDevice = device; activeDevices.add(device); break; } } } } if (activeDevice != null && activeDevice.tag instanceof MediaProjectionInfo) { return activeDevices; return activeDevice; } return null; } } @Override @Override Loading Loading @@ -187,14 +183,18 @@ public class CastTile extends QSTileImpl<BooleanState> { state.label = mContext.getString(R.string.quick_settings_cast_title); state.label = mContext.getString(R.string.quick_settings_cast_title); state.contentDescription = state.label; state.contentDescription = state.label; state.value = false; state.value = false; final Set<CastDevice> devices = mController.getCastDevices(); final List<CastDevice> devices = mController.getCastDevices(); boolean connecting = false; boolean connecting = false; // 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) { for (CastDevice device : devices) { if (device.state == CastDevice.STATE_CONNECTED) { if (device.state == CastDevice.STATE_CONNECTED) { state.value = true; state.value = true; state.secondaryLabel = getDeviceName(device); state.secondaryLabel = getDeviceName(device); state.contentDescription = state.contentDescription + "," + state.contentDescription = state.contentDescription + "," + mContext.getString(R.string.accessibility_cast_name, state.label); mContext.getString(R.string.accessibility_cast_name, state.label); connecting = false; break; } else if (device.state == CastDevice.STATE_CONNECTING) { } else if (device.state == CastDevice.STATE_CONNECTING) { connecting = true; connecting = true; } } Loading Loading @@ -326,7 +326,7 @@ public class CastTile extends QSTileImpl<BooleanState> { return mItems; return mItems; } } private void updateItems(Set<CastDevice> devices) { private void updateItems(List<CastDevice> devices) { if (mItems == null) return; if (mItems == null) return; Item[] items = null; Item[] items = null; if (devices != null && !devices.isEmpty()) { if (devices != null && !devices.isEmpty()) { Loading packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java +2 −2 Original line number Original line Diff line number Diff line Loading @@ -19,12 +19,12 @@ package com.android.systemui.statusbar.policy; import com.android.systemui.Dumpable; import com.android.systemui.Dumpable; import com.android.systemui.statusbar.policy.CastController.Callback; import com.android.systemui.statusbar.policy.CastController.Callback; import java.util.Set; import java.util.List; public interface CastController extends CallbackController<Callback>, Dumpable { public interface CastController extends CallbackController<Callback>, Dumpable { void setDiscovering(boolean request); void setDiscovering(boolean request); void setCurrentUserId(int currentUserId); void setCurrentUserId(int currentUserId); Set<CastDevice> getCastDevices(); List<CastDevice> getCastDevices(); void startCasting(CastDevice device); void startCasting(CastDevice device); void stopCasting(CastDevice device); void stopCasting(CastDevice device); Loading packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java +26 −19 Original line number Original line Diff line number Diff line Loading @@ -29,7 +29,6 @@ import android.media.projection.MediaProjectionManager; import android.os.Handler; import android.os.Handler; import android.text.TextUtils; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting; Loading @@ -40,8 +39,8 @@ import com.android.systemui.R; import java.io.FileDescriptor; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.UUID; import javax.inject.Inject; import javax.inject.Inject; Loading Loading @@ -150,20 +149,8 @@ public class CastControllerImpl implements CastController { } } @Override @Override public Set<CastDevice> getCastDevices() { public List<CastDevice> getCastDevices() { final ArraySet<CastDevice> devices = new ArraySet<CastDevice>(); final ArrayList<CastDevice> devices = new ArrayList<>(); 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); return devices; } } synchronized(mRoutes) { synchronized(mRoutes) { for (RouteInfo route : mRoutes.values()) { for (RouteInfo route : mRoutes.values()) { final CastDevice device = new CastDevice(); final CastDevice device = new CastDevice(); Loading @@ -172,13 +159,33 @@ public class CastControllerImpl implements CastController { device.name = name != null ? name.toString() : null; device.name = name != null ? name.toString() : null; final CharSequence description = route.getDescription(); final CharSequence description = route.getDescription(); device.description = description != null ? description.toString() : null; device.description = description != null ? description.toString() : null; device.state = route.isConnecting() ? CastDevice.STATE_CONNECTING : route.isSelected() ? CastDevice.STATE_CONNECTED int statusCode = route.getStatusCode(); : CastDevice.STATE_DISCONNECTED; 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; device.tag = route; devices.add(device); devices.add(device); } } } } 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); } } return devices; return devices; } } Loading packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java +116 −17 Original line number Original line Diff line number Diff line Loading @@ -14,13 +14,19 @@ package com.android.systemui.qs.tiles; package com.android.systemui.qs.tiles; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.any; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import android.media.MediaRouter; import android.media.MediaRouter.RouteInfo; import android.media.projection.MediaProjectionInfo; import android.service.quicksettings.Tile; import android.service.quicksettings.Tile; import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper; Loading @@ -33,6 +39,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NetworkController; Loading @@ -43,8 +50,9 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations; import java.util.HashSet; import java.util.ArrayList; import java.util.Set; import java.util.List; @RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @TestableLooper.RunWithLooper Loading Loading @@ -93,7 +101,6 @@ public class CastTileTest extends SysuiTestCase { verify(mNetworkController).observe(any(LifecycleOwner.class), verify(mNetworkController).observe(any(LifecycleOwner.class), signalCallbackArgumentCaptor.capture()); signalCallbackArgumentCaptor.capture()); mCallback = signalCallbackArgumentCaptor.getValue(); mCallback = signalCallbackArgumentCaptor.getValue(); } } @Test @Test Loading @@ -120,33 +127,125 @@ public class CastTileTest extends SysuiTestCase { assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state); assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state); } } @Test private void enableWifiAndProcessMessages() { public void testStateActive_wifiEnabledAndCasting() { CastController.CastDevice device = mock(CastController.CastDevice.class); device.state = CastController.CastDevice.STATE_CONNECTED; Set<CastController.CastDevice> devices = new HashSet<>(); devices.add(device); when(mController.getCastDevices()).thenReturn(devices); NetworkController.IconState qsIcon = NetworkController.IconState qsIcon = new NetworkController.IconState(true, 0, ""); new NetworkController.IconState(true, 0, ""); mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class), mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class), qsIcon, false,false, "", qsIcon, false,false, "", false, ""); false, ""); mTestableLooper.processAllMessages(); mTestableLooper.processAllMessages(); } @Test public void testStateActive_wifiEnabledAndCasting() { CastController.CastDevice device = new CastController.CastDevice(); device.state = CastController.CastDevice.STATE_CONNECTED; List<CastDevice> devices = new ArrayList<>(); devices.add(device); when(mController.getCastDevices()).thenReturn(devices); enableWifiAndProcessMessages(); assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state); assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state); } } @Test @Test public void testStateInactive_wifiEnabledNotCasting() { public void testStateInactive_wifiEnabledNotCasting() { NetworkController.IconState qsIcon = enableWifiAndProcessMessages(); new NetworkController.IconState(true, 0, ""); assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state); mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class), } qsIcon, false,false, "", false, ""); @Test public void testHandleClick_castDevicePresent() { CastController.CastDevice device = new CastController.CastDevice(); device.state = CastDevice.STATE_CONNECTED; device.tag = mock(MediaRouter.RouteInfo.class); List<CastDevice> devices = new ArrayList<>(); devices.add(device); when(mController.getCastDevices()).thenReturn(devices); enableWifiAndProcessMessages(); mCastTile.handleClick(); mTestableLooper.processAllMessages(); mTestableLooper.processAllMessages(); assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state); verify(mActivityStarter, times(1)).postQSRunnableDismissingKeyguard(any()); } @Test public void testHandleClick_projectionOnly() { CastController.CastDevice device = new CastController.CastDevice(); device.state = CastDevice.STATE_CONNECTED; device.tag = mock(MediaProjectionInfo.class); List<CastDevice> devices = new ArrayList<>(); devices.add(device); when(mController.getCastDevices()).thenReturn(devices); enableWifiAndProcessMessages(); mCastTile.handleClick(); mTestableLooper.processAllMessages(); verify(mController, times(1)).stopCasting(same(device)); } @Test public void testUpdateState_projectionOnly() { CastController.CastDevice device = new CastController.CastDevice(); device.state = CastDevice.STATE_CONNECTED; device.tag = mock(MediaProjectionInfo.class); device.name = "Test Projection Device"; 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)); } @Test public void testUpdateState_castingAndProjection() { 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"; List<CastDevice> devices = new ArrayList<>(); devices.add(casting); devices.add(projection); when(mController.getCastDevices()).thenReturn(devices); enableWifiAndProcessMessages(); // 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)); } @Test public void testUpdateState_connectedAndConnecting() { 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"; List<CastDevice> devices = new ArrayList<>(); devices.add(connecting); devices.add(connected); when(mController.getCastDevices()).thenReturn(devices); enableWifiAndProcessMessages(); // 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)); } } } } packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java +3 −3 Original line number Original line Diff line number Diff line Loading @@ -45,7 +45,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations; import java.util.Collections; import java.util.Collections; import java.util.Set; import java.util.List; @RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class) @RunWithLooper @RunWithLooper Loading Loading @@ -118,10 +118,10 @@ public class AutoTileManagerTest extends SysuiTestCase { verify(mQsTileHost, never()).addTile("night"); verify(mQsTileHost, never()).addTile("night"); } } private static Set<CastDevice> buildFakeCastDevice(boolean isCasting) { private static List<CastDevice> buildFakeCastDevice(boolean isCasting) { CastDevice cd = new CastDevice(); CastDevice cd = new CastDevice(); cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED; cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED; return Collections.singleton(cd); return Collections.singletonList(cd); } } @Test @Test Loading Loading
packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +22 −22 Original line number Original line Diff line number Diff line Loading @@ -21,7 +21,7 @@ import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY; import android.app.Dialog; import android.app.Dialog; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.media.projection.MediaProjectionInfo; import android.media.MediaRouter.RouteInfo; import android.provider.Settings; import android.provider.Settings; import android.service.quicksettings.Tile; import android.service.quicksettings.Tile; import android.util.Log; import android.util.Log; Loading @@ -48,8 +48,9 @@ import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NetworkController; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedHashMap; import java.util.Set; import java.util.List; import javax.inject.Inject; import javax.inject.Inject; Loading Loading @@ -128,35 +129,30 @@ public class CastTile extends QSTileImpl<BooleanState> { return; return; } } CastDevice activeProjection = getActiveDeviceMediaProjection(); List<CastDevice> activeDevices = getActiveDevices(); if (activeProjection == null) { // We want to pop up the media route selection dialog if we either have no active devices if (mKeyguard.isSecure() && !mKeyguard.canSkipBouncer()) { // (neither routes nor projection), or if we have an active route. In other cases, we assume // that a projection is active. This is messy, but this tile never correctly handled the // case where multiple devices were active :-/. if (activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo)) { mActivityStarter.postQSRunnableDismissingKeyguard(() -> { mActivityStarter.postQSRunnableDismissingKeyguard(() -> { showDetail(true); showDetail(true); }); }); } else { } else { showDetail(true); mController.stopCasting(activeDevices.get(0)); } } else { mController.stopCasting(activeProjection); } } } } private CastDevice getActiveDeviceMediaProjection() { private List<CastDevice> getActiveDevices() { CastDevice activeDevice = null; ArrayList<CastDevice> activeDevices = new ArrayList<>(); for (CastDevice device : mController.getCastDevices()) { for (CastDevice device : mController.getCastDevices()) { if (device.state == CastDevice.STATE_CONNECTED if (device.state == CastDevice.STATE_CONNECTED || device.state == CastDevice.STATE_CONNECTING) { || device.state == CastDevice.STATE_CONNECTING) { activeDevice = device; activeDevices.add(device); break; } } } } if (activeDevice != null && activeDevice.tag instanceof MediaProjectionInfo) { return activeDevices; return activeDevice; } return null; } } @Override @Override Loading Loading @@ -187,14 +183,18 @@ public class CastTile extends QSTileImpl<BooleanState> { state.label = mContext.getString(R.string.quick_settings_cast_title); state.label = mContext.getString(R.string.quick_settings_cast_title); state.contentDescription = state.label; state.contentDescription = state.label; state.value = false; state.value = false; final Set<CastDevice> devices = mController.getCastDevices(); final List<CastDevice> devices = mController.getCastDevices(); boolean connecting = false; boolean connecting = false; // 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) { for (CastDevice device : devices) { if (device.state == CastDevice.STATE_CONNECTED) { if (device.state == CastDevice.STATE_CONNECTED) { state.value = true; state.value = true; state.secondaryLabel = getDeviceName(device); state.secondaryLabel = getDeviceName(device); state.contentDescription = state.contentDescription + "," + state.contentDescription = state.contentDescription + "," + mContext.getString(R.string.accessibility_cast_name, state.label); mContext.getString(R.string.accessibility_cast_name, state.label); connecting = false; break; } else if (device.state == CastDevice.STATE_CONNECTING) { } else if (device.state == CastDevice.STATE_CONNECTING) { connecting = true; connecting = true; } } Loading Loading @@ -326,7 +326,7 @@ public class CastTile extends QSTileImpl<BooleanState> { return mItems; return mItems; } } private void updateItems(Set<CastDevice> devices) { private void updateItems(List<CastDevice> devices) { if (mItems == null) return; if (mItems == null) return; Item[] items = null; Item[] items = null; if (devices != null && !devices.isEmpty()) { if (devices != null && !devices.isEmpty()) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java +2 −2 Original line number Original line Diff line number Diff line Loading @@ -19,12 +19,12 @@ package com.android.systemui.statusbar.policy; import com.android.systemui.Dumpable; import com.android.systemui.Dumpable; import com.android.systemui.statusbar.policy.CastController.Callback; import com.android.systemui.statusbar.policy.CastController.Callback; import java.util.Set; import java.util.List; public interface CastController extends CallbackController<Callback>, Dumpable { public interface CastController extends CallbackController<Callback>, Dumpable { void setDiscovering(boolean request); void setDiscovering(boolean request); void setCurrentUserId(int currentUserId); void setCurrentUserId(int currentUserId); Set<CastDevice> getCastDevices(); List<CastDevice> getCastDevices(); void startCasting(CastDevice device); void startCasting(CastDevice device); void stopCasting(CastDevice device); void stopCasting(CastDevice device); Loading
packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java +26 −19 Original line number Original line Diff line number Diff line Loading @@ -29,7 +29,6 @@ import android.media.projection.MediaProjectionManager; import android.os.Handler; import android.os.Handler; import android.text.TextUtils; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting; Loading @@ -40,8 +39,8 @@ import com.android.systemui.R; import java.io.FileDescriptor; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.UUID; import javax.inject.Inject; import javax.inject.Inject; Loading Loading @@ -150,20 +149,8 @@ public class CastControllerImpl implements CastController { } } @Override @Override public Set<CastDevice> getCastDevices() { public List<CastDevice> getCastDevices() { final ArraySet<CastDevice> devices = new ArraySet<CastDevice>(); final ArrayList<CastDevice> devices = new ArrayList<>(); 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); return devices; } } synchronized(mRoutes) { synchronized(mRoutes) { for (RouteInfo route : mRoutes.values()) { for (RouteInfo route : mRoutes.values()) { final CastDevice device = new CastDevice(); final CastDevice device = new CastDevice(); Loading @@ -172,13 +159,33 @@ public class CastControllerImpl implements CastController { device.name = name != null ? name.toString() : null; device.name = name != null ? name.toString() : null; final CharSequence description = route.getDescription(); final CharSequence description = route.getDescription(); device.description = description != null ? description.toString() : null; device.description = description != null ? description.toString() : null; device.state = route.isConnecting() ? CastDevice.STATE_CONNECTING : route.isSelected() ? CastDevice.STATE_CONNECTED int statusCode = route.getStatusCode(); : CastDevice.STATE_DISCONNECTED; 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; device.tag = route; devices.add(device); devices.add(device); } } } } 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); } } return devices; return devices; } } Loading
packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java +116 −17 Original line number Original line Diff line number Diff line Loading @@ -14,13 +14,19 @@ package com.android.systemui.qs.tiles; package com.android.systemui.qs.tiles; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.any; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import android.media.MediaRouter; import android.media.MediaRouter.RouteInfo; import android.media.projection.MediaProjectionInfo; import android.service.quicksettings.Tile; import android.service.quicksettings.Tile; import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper; Loading @@ -33,6 +39,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NetworkController; Loading @@ -43,8 +50,9 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations; import java.util.HashSet; import java.util.ArrayList; import java.util.Set; import java.util.List; @RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @TestableLooper.RunWithLooper Loading Loading @@ -93,7 +101,6 @@ public class CastTileTest extends SysuiTestCase { verify(mNetworkController).observe(any(LifecycleOwner.class), verify(mNetworkController).observe(any(LifecycleOwner.class), signalCallbackArgumentCaptor.capture()); signalCallbackArgumentCaptor.capture()); mCallback = signalCallbackArgumentCaptor.getValue(); mCallback = signalCallbackArgumentCaptor.getValue(); } } @Test @Test Loading @@ -120,33 +127,125 @@ public class CastTileTest extends SysuiTestCase { assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state); assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state); } } @Test private void enableWifiAndProcessMessages() { public void testStateActive_wifiEnabledAndCasting() { CastController.CastDevice device = mock(CastController.CastDevice.class); device.state = CastController.CastDevice.STATE_CONNECTED; Set<CastController.CastDevice> devices = new HashSet<>(); devices.add(device); when(mController.getCastDevices()).thenReturn(devices); NetworkController.IconState qsIcon = NetworkController.IconState qsIcon = new NetworkController.IconState(true, 0, ""); new NetworkController.IconState(true, 0, ""); mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class), mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class), qsIcon, false,false, "", qsIcon, false,false, "", false, ""); false, ""); mTestableLooper.processAllMessages(); mTestableLooper.processAllMessages(); } @Test public void testStateActive_wifiEnabledAndCasting() { CastController.CastDevice device = new CastController.CastDevice(); device.state = CastController.CastDevice.STATE_CONNECTED; List<CastDevice> devices = new ArrayList<>(); devices.add(device); when(mController.getCastDevices()).thenReturn(devices); enableWifiAndProcessMessages(); assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state); assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state); } } @Test @Test public void testStateInactive_wifiEnabledNotCasting() { public void testStateInactive_wifiEnabledNotCasting() { NetworkController.IconState qsIcon = enableWifiAndProcessMessages(); new NetworkController.IconState(true, 0, ""); assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state); mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class), } qsIcon, false,false, "", false, ""); @Test public void testHandleClick_castDevicePresent() { CastController.CastDevice device = new CastController.CastDevice(); device.state = CastDevice.STATE_CONNECTED; device.tag = mock(MediaRouter.RouteInfo.class); List<CastDevice> devices = new ArrayList<>(); devices.add(device); when(mController.getCastDevices()).thenReturn(devices); enableWifiAndProcessMessages(); mCastTile.handleClick(); mTestableLooper.processAllMessages(); mTestableLooper.processAllMessages(); assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state); verify(mActivityStarter, times(1)).postQSRunnableDismissingKeyguard(any()); } @Test public void testHandleClick_projectionOnly() { CastController.CastDevice device = new CastController.CastDevice(); device.state = CastDevice.STATE_CONNECTED; device.tag = mock(MediaProjectionInfo.class); List<CastDevice> devices = new ArrayList<>(); devices.add(device); when(mController.getCastDevices()).thenReturn(devices); enableWifiAndProcessMessages(); mCastTile.handleClick(); mTestableLooper.processAllMessages(); verify(mController, times(1)).stopCasting(same(device)); } @Test public void testUpdateState_projectionOnly() { CastController.CastDevice device = new CastController.CastDevice(); device.state = CastDevice.STATE_CONNECTED; device.tag = mock(MediaProjectionInfo.class); device.name = "Test Projection Device"; 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)); } @Test public void testUpdateState_castingAndProjection() { 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"; List<CastDevice> devices = new ArrayList<>(); devices.add(casting); devices.add(projection); when(mController.getCastDevices()).thenReturn(devices); enableWifiAndProcessMessages(); // 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)); } @Test public void testUpdateState_connectedAndConnecting() { 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"; List<CastDevice> devices = new ArrayList<>(); devices.add(connecting); devices.add(connected); when(mController.getCastDevices()).thenReturn(devices); enableWifiAndProcessMessages(); // 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)); } } } }
packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java +3 −3 Original line number Original line Diff line number Diff line Loading @@ -45,7 +45,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations; import java.util.Collections; import java.util.Collections; import java.util.Set; import java.util.List; @RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class) @RunWithLooper @RunWithLooper Loading Loading @@ -118,10 +118,10 @@ public class AutoTileManagerTest extends SysuiTestCase { verify(mQsTileHost, never()).addTile("night"); verify(mQsTileHost, never()).addTile("night"); } } private static Set<CastDevice> buildFakeCastDevice(boolean isCasting) { private static List<CastDevice> buildFakeCastDevice(boolean isCasting) { CastDevice cd = new CastDevice(); CastDevice cd = new CastDevice(); cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED; cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED; return Collections.singleton(cd); return Collections.singletonList(cd); } } @Test @Test Loading