diff options
4 files changed, 284 insertions, 20 deletions
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 7008d32fd574..d919d78a8c08 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -16,13 +16,17 @@ package android.media; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.net.Uri; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -46,6 +50,34 @@ public final class MediaRoute2Info implements Parcelable { } }; + /** @hide */ + @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING, + CONNECTION_STATE_CONNECTED}) + @Retention(RetentionPolicy.SOURCE) + private @interface ConnectionState {} + + /** + * The default connection state indicating the route is disconnected. + * + * @see #getConnectionState + */ + public static final int CONNECTION_STATE_DISCONNECTED = 0; + + /** + * A connection state indicating the route is in the process of connecting and is not yet + * ready for use. + * + * @see #getConnectionState + */ + public static final int CONNECTION_STATE_CONNECTING = 1; + + /** + * A connection state indicating the route is connected. + * + * @see #getConnectionState + */ + public static final int CONNECTION_STATE_CONNECTED = 2; + /** * Playback information indicating the playback volume is fixed, i.e. it cannot be * controlled from this object. An example of fixed playback volume is a remote player, @@ -61,6 +93,46 @@ public final class MediaRoute2Info implements Parcelable { */ public static final int PLAYBACK_VOLUME_VARIABLE = 1; + /** @hide */ + @IntDef({ + DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, + DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH}) + @Retention(RetentionPolicy.SOURCE) + private @interface DeviceType {} + + /** + * The default receiver device type of the route indicating the type is unknown. + * + * @see #getDeviceType + * @hide + */ + public static final int DEVICE_TYPE_UNKNOWN = 0; + + /** + * A receiver device type of the route indicating the presentation of the media is happening + * on a TV. + * + * @see #getDeviceType + */ + public static final int DEVICE_TYPE_TV = 1; + + /** + * A receiver device type of the route indicating the presentation of the media is happening + * on a speaker. + * + * @see #getDeviceType + */ + public static final int DEVICE_TYPE_SPEAKER = 2; + + /** + * A receiver device type of the route indicating the presentation of the media is happening + * on a bluetooth device such as a bluetooth speaker. + * + * @see #getDeviceType + * @hide + */ + public static final int DEVICE_TYPE_BLUETOOTH = 3; + @NonNull final String mId; @Nullable @@ -70,12 +142,17 @@ public final class MediaRoute2Info implements Parcelable { @Nullable final CharSequence mDescription; @Nullable + final @ConnectionState int mConnectionState; + @Nullable + final Uri mIconUri; + @Nullable final String mClientPackageName; @NonNull final List<String> mSupportedCategories; final int mVolume; final int mVolumeMax; final int mVolumeHandling; + final @DeviceType int mDeviceType; @Nullable final Bundle mExtras; @@ -86,11 +163,14 @@ public final class MediaRoute2Info implements Parcelable { mProviderId = builder.mProviderId; mName = builder.mName; mDescription = builder.mDescription; + mConnectionState = builder.mConnectionState; + mIconUri = builder.mIconUri; mClientPackageName = builder.mClientPackageName; mSupportedCategories = builder.mSupportedCategories; mVolume = builder.mVolume; mVolumeMax = builder.mVolumeMax; mVolumeHandling = builder.mVolumeHandling; + mDeviceType = builder.mDeviceType; mExtras = builder.mExtras; mUniqueId = createUniqueId(); } @@ -100,11 +180,14 @@ public final class MediaRoute2Info implements Parcelable { mProviderId = in.readString(); mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mConnectionState = in.readInt(); + mIconUri = in.readParcelable(null); mClientPackageName = in.readString(); mSupportedCategories = in.createStringArrayList(); mVolume = in.readInt(); mVolumeMax = in.readInt(); mVolumeHandling = in.readInt(); + mDeviceType = in.readInt(); mExtras = in.readBundle(); mUniqueId = createUniqueId(); } @@ -145,18 +228,22 @@ public final class MediaRoute2Info implements Parcelable { && Objects.equals(mProviderId, other.mProviderId) && Objects.equals(mName, other.mName) && Objects.equals(mDescription, other.mDescription) + && (mConnectionState == other.mConnectionState) + && Objects.equals(mIconUri, other.mIconUri) && Objects.equals(mClientPackageName, other.mClientPackageName) && Objects.equals(mSupportedCategories, other.mSupportedCategories) && (mVolume == other.mVolume) && (mVolumeMax == other.mVolumeMax) && (mVolumeHandling == other.mVolumeHandling) + && (mDeviceType == other.mDeviceType) //TODO: This will be evaluated as false in most cases. Try not to. && Objects.equals(mExtras, other.mExtras); } @Override public int hashCode() { - return Objects.hash(mId, mName, mDescription, mSupportedCategories); + return Objects.hash(mId, mName, mDescription, mConnectionState, mIconUri, + mSupportedCategories, mVolume, mVolumeMax, mVolumeHandling, mDeviceType); } /** @@ -204,6 +291,29 @@ public final class MediaRoute2Info implements Parcelable { } /** + * Gets the connection state of the route. + * + * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED}, + * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}. + */ + @ConnectionState + public int getConnectionState() { + return mConnectionState; + } + + /** + * Gets the URI of the icon representing this route. + * <p> + * This icon will be used in picker UIs if available. + * + * @return The URI of the icon representing this route, or null if none. + */ + @Nullable + public Uri getIconUri() { + return mIconUri; + } + + /** * Gets the package name of the client that uses the route. * Returns null if no clients use this. * @hide @@ -221,6 +331,18 @@ public final class MediaRoute2Info implements Parcelable { return mSupportedCategories; } + //TODO: once device types are confirmed, reflect those into the comment. + /** + * Gets the type of the receiver device associated with this route. + * + * @return The type of the receiver device associated with this route: + * {@link #DEVICE_TYPE_TV} or {@link #DEVICE_TYPE_SPEAKER}. + */ + @DeviceType + public int getDeviceType() { + return mDeviceType; + } + /** * Gets the current volume of the route. This may be invalid if the route is not selected. */ @@ -293,11 +415,14 @@ public final class MediaRoute2Info implements Parcelable { dest.writeString(mProviderId); TextUtils.writeToParcel(mName, dest, flags); TextUtils.writeToParcel(mDescription, dest, flags); + dest.writeInt(mConnectionState); + dest.writeParcelable(mIconUri, flags); dest.writeString(mClientPackageName); dest.writeStringList(mSupportedCategories); dest.writeInt(mVolume); dest.writeInt(mVolumeMax); dest.writeInt(mVolumeHandling); + dest.writeInt(mDeviceType); dest.writeBundle(mExtras); } @@ -308,9 +433,12 @@ public final class MediaRoute2Info implements Parcelable { .append("id=").append(getId()) .append(", name=").append(getName()) .append(", description=").append(getDescription()) + .append(", connectionState=").append(getConnectionState()) + .append(", iconUri=").append(getIconUri()) .append(", volume=").append(getVolume()) .append(", volumeMax=").append(getVolumeMax()) .append(", volumeHandling=").append(getVolumeHandling()) + .append(", deviceType=").append(getDeviceType()) .append(", providerId=").append(getProviderId()) .append(" }"); return result.toString(); @@ -324,11 +452,16 @@ public final class MediaRoute2Info implements Parcelable { String mProviderId; CharSequence mName; CharSequence mDescription; + @ConnectionState + int mConnectionState; + Uri mIconUri; String mClientPackageName; List<String> mSupportedCategories; int mVolume; int mVolumeMax; int mVolumeHandling = PLAYBACK_VOLUME_FIXED; + @DeviceType + int mDeviceType = DEVICE_TYPE_UNKNOWN; Bundle mExtras; public Builder(@NonNull String id, @NonNull CharSequence name) { @@ -348,11 +481,14 @@ public final class MediaRoute2Info implements Parcelable { } setName(routeInfo.mName); mDescription = routeInfo.mDescription; + mConnectionState = routeInfo.mConnectionState; + mIconUri = routeInfo.mIconUri; setClientPackageName(routeInfo.mClientPackageName); setSupportedCategories(routeInfo.mSupportedCategories); setVolume(routeInfo.mVolume); setVolumeMax(routeInfo.mVolumeMax); setVolumeHandling(routeInfo.mVolumeHandling); + setDeviceType(routeInfo.mDeviceType); if (routeInfo.mExtras != null) { mExtras = new Bundle(routeInfo.mExtras); } @@ -403,6 +539,39 @@ public final class MediaRoute2Info implements Parcelable { } /** + * Sets the route's connection state. + * + * {@link #CONNECTION_STATE_DISCONNECTED}, + * {@link #CONNECTION_STATE_CONNECTING}, or + * {@link #CONNECTION_STATE_CONNECTED}. + */ + @NonNull + public Builder setConnectionState(@ConnectionState int connectionState) { + mConnectionState = connectionState; + return this; + } + + /** + * Sets the URI of the icon representing this route. + * <p> + * This icon will be used in picker UIs if available. + * </p><p> + * The URI must be one of the following formats: + * <ul> + * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> + * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) + * </li> + * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li> + * </ul> + * </p> + */ + @NonNull + public Builder setIconUri(@Nullable Uri iconUri) { + mIconUri = iconUri; + return this; + } + + /** * Sets the package name of the app using the route. */ @NonNull @@ -470,6 +639,16 @@ public final class MediaRoute2Info implements Parcelable { mVolumeHandling = volumeHandling; return this; } + + /** + * Sets the route's device type. + */ + @NonNull + public Builder setDeviceType(@DeviceType int deviceType) { + mDeviceType = deviceType; + return this; + } + /** * Sets a bundle of extras for the route. */ diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java index df6345fb7db0..524be8c014c2 100644 --- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java +++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java @@ -16,6 +16,9 @@ package com.android.mediarouteprovider.example; +import static android.media.MediaRoute2Info.DEVICE_TYPE_SPEAKER; +import static android.media.MediaRoute2Info.DEVICE_TYPE_TV; + import android.content.Intent; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; @@ -57,9 +60,11 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService private void initializeRoutes() { MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1) .addSupportedCategory(CATEGORY_SAMPLE) + .setDeviceType(DEVICE_TYPE_TV) .build(); MediaRoute2Info route2 = new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2) .addSupportedCategory(CATEGORY_SAMPLE) + .setDeviceType(DEVICE_TYPE_SPEAKER) .build(); MediaRoute2Info routeSpecial = new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_CATEGORY, ROUTE_NAME_SPECIAL_CATEGORY) @@ -123,7 +128,8 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService @Override public void onControlRequest(String routeId, Intent request) { - if (ACTION_REMOVE_ROUTE.equals(request.getAction())) { + String action = request.getAction(); + if (ACTION_REMOVE_ROUTE.equals(action)) { MediaRoute2Info route = mRoutes.get(routeId); if (route != null) { mRoutes.remove(routeId); diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java index 326628587837..6f1a070d6f7c 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java @@ -16,19 +16,31 @@ package com.android.mediaroutertest; +import static android.media.MediaRoute2Info.CONNECTION_STATE_CONNECTED; +import static android.media.MediaRoute2Info.CONNECTION_STATE_CONNECTING; +import static android.media.MediaRoute2Info.DEVICE_TYPE_SPEAKER; +import static android.media.MediaRoute2Info.DEVICE_TYPE_TV; +import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED; +import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE; + import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORIES_ALL; import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORIES_SPECIAL; +import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORY_SAMPLE; +import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORY_SPECIAL; import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_SPECIAL_CATEGORY; import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_VARIABLE_VOLUME; import static com.android.mediaroutertest.MediaRouterManagerTest.SYSTEM_PROVIDER_ID; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.content.Context; import android.media.MediaRoute2Info; import android.media.MediaRouter2; +import android.net.Uri; +import android.os.Parcel; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -88,6 +100,90 @@ public class MediaRouter2Test { } @Test + public void testRouteInfoEquality() { + MediaRoute2Info routeInfo = new MediaRoute2Info.Builder("id", "name") + .setDescription("description") + .setClientPackageName("com.android.mediaroutertest") + .setConnectionState(CONNECTION_STATE_CONNECTING) + .setIconUri(new Uri.Builder().path("icon").build()) + .setVolume(5) + .setVolumeMax(20) + .addSupportedCategory(CATEGORY_SAMPLE) + .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE) + .setDeviceType(DEVICE_TYPE_SPEAKER) + .build(); + + MediaRoute2Info routeInfoRebuilt = new MediaRoute2Info.Builder(routeInfo).build(); + assertEquals(routeInfo, routeInfoRebuilt); + + Parcel parcel = Parcel.obtain(); + parcel.writeParcelable(routeInfo, 0); + parcel.setDataPosition(0); + MediaRoute2Info routeInfoFromParcel = parcel.readParcelable(null); + + assertEquals(routeInfo, routeInfoFromParcel); + } + + @Test + public void testRouteInfoInequality() { + MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name") + .setDescription("description") + .setClientPackageName("com.android.mediaroutertest") + .setConnectionState(CONNECTION_STATE_CONNECTING) + .setIconUri(new Uri.Builder().path("icon").build()) + .addSupportedCategory(CATEGORY_SAMPLE) + .setVolume(5) + .setVolumeMax(20) + .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE) + .setDeviceType(DEVICE_TYPE_SPEAKER) + .build(); + + MediaRoute2Info routeId = new MediaRoute2Info.Builder(route) + .setId("another id").build(); + assertNotEquals(route, routeId); + + MediaRoute2Info routeName = new MediaRoute2Info.Builder(route) + .setName("another name").build(); + assertNotEquals(route, routeName); + + MediaRoute2Info routeDescription = new MediaRoute2Info.Builder(route) + .setDescription("another description").build(); + assertNotEquals(route, routeDescription); + + MediaRoute2Info routeConnectionState = new MediaRoute2Info.Builder(route) + .setConnectionState(CONNECTION_STATE_CONNECTED).build(); + assertNotEquals(route, routeConnectionState); + + MediaRoute2Info routeIcon = new MediaRoute2Info.Builder(route) + .setIconUri(new Uri.Builder().path("new icon").build()).build(); + assertNotEquals(route, routeIcon); + + MediaRoute2Info routeClient = new MediaRoute2Info.Builder(route) + .setClientPackageName("another.client.package").build(); + assertNotEquals(route, routeClient); + + MediaRoute2Info routeCategory = new MediaRoute2Info.Builder(route) + .addSupportedCategory(CATEGORY_SPECIAL).build(); + assertNotEquals(route, routeCategory); + + MediaRoute2Info routeVolume = new MediaRoute2Info.Builder(route) + .setVolume(10).build(); + assertNotEquals(route, routeVolume); + + MediaRoute2Info routeVolumeMax = new MediaRoute2Info.Builder(route) + .setVolumeMax(30).build(); + assertNotEquals(route, routeVolumeMax); + + MediaRoute2Info routeVolumeHandling = new MediaRoute2Info.Builder(route) + .setVolumeHandling(PLAYBACK_VOLUME_FIXED).build(); + assertNotEquals(route, routeVolumeHandling); + + MediaRoute2Info routeDeviceType = new MediaRoute2Info.Builder(route) + .setVolume(DEVICE_TYPE_TV).build(); + assertNotEquals(route, routeDeviceType); + } + + @Test public void testControlVolumeWithRouter() throws Exception { Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_ALL); diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index 2772aa46911c..c5d8a96c7e08 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -20,7 +20,6 @@ import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED; import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -124,21 +123,7 @@ public class MediaRouterManagerTest { clearCallbacks(); } - //TODO: Move to a separate file - @Test - public void testMediaRoute2Info() { - MediaRoute2Info routeInfo1 = new MediaRoute2Info.Builder("id", "name") - .build(); - MediaRoute2Info routeInfo2 = new MediaRoute2Info.Builder(routeInfo1).build(); - - MediaRoute2Info routeInfo3 = new MediaRoute2Info.Builder(routeInfo1) - .setClientPackageName(mPackageName).build(); - - assertEquals(routeInfo1, routeInfo2); - assertNotEquals(routeInfo1, routeInfo3); - } - - /** + /** * Tests if routes are added correctly when a new callback is registered. */ @Test @@ -177,8 +162,6 @@ public class MediaRouterManagerTest { } }); - //TODO: Figure out a more proper way to test. - // (Control requests shouldn't be used in this way.) mRouter2.sendControlRequest(routes.get(ROUTE_ID2), new Intent(ACTION_REMOVE_ROUTE)); assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); } |