Add suitability status to routing sample app
Bug: b/319645714
Test: Manually using the app on devices with and without new APIs.
Change-Id: Ic8eba6db99db173a95f2ebb9f6d72aa4968cf619
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteItem.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteItem.java
index c92ced8..40f1308 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteItem.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteItem.java
@@ -19,71 +19,32 @@
import android.text.TextUtils;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import java.util.Objects;
-import javax.annotation.Nullable;
-
-/**
- * An abstract model that holds information about routes from different sources.
- *
- * Can represent media routers' routes, bluetooth routes, or audio routes.
- */
+/** Holds information about a system route. */
public final class SystemRouteItem implements SystemRoutesAdapterItem {
- @NonNull
- private final String mId;
+ @NonNull public final String mId;
- @NonNull
- private final String mName;
+ @NonNull public final String mName;
- @Nullable
- private final String mAddress;
+ @Nullable public final String mAddress;
- @Nullable
- private final String mDescription;
+ @Nullable public final String mDescription;
+
+ @Nullable public final String mSuitabilityStatus;
+
+ @Nullable public final Boolean mTransferInitiatedBySelf;
private SystemRouteItem(@NonNull Builder builder) {
- Objects.requireNonNull(builder.mId);
- Objects.requireNonNull(builder.mName);
-
- mId = builder.mId;
- mName = builder.mName;
-
+ mId = Objects.requireNonNull(builder.mId);
+ mName = Objects.requireNonNull(builder.mName);
mAddress = builder.mAddress;
mDescription = builder.mDescription;
- }
-
- /**
- * Returns a unique identifier of a route.
- */
- @NonNull
- public String getId() {
- return mId;
- }
-
- /**
- * Returns a human-readable name of the route.
- */
- @NonNull
- public String getName() {
- return mName;
- }
-
- /**
- * Returns address if the route is a Bluetooth route and {@code null} otherwise.
- */
- @Nullable
- public String getAddress() {
- return mAddress;
- }
-
- /**
- * Returns a route description or {@code null} if empty.
- */
- @Nullable
- public String getDescription() {
- return mDescription;
+ mSuitabilityStatus = builder.mSuitabilityStatus;
+ mTransferInitiatedBySelf = builder.mTransferInitiatedBySelf;
}
@Override
@@ -106,17 +67,12 @@
*/
public static final class Builder {
- @NonNull
- private final String mId;
-
- @NonNull
- private String mName;
-
- @Nullable
- private String mAddress;
-
- @Nullable
- private String mDescription;
+ @NonNull private final String mId;
+ @NonNull private String mName;
+ @Nullable private String mAddress;
+ @Nullable private String mDescription;
+ @Nullable private String mSuitabilityStatus;
+ @Nullable private Boolean mTransferInitiatedBySelf;
public Builder(@NonNull String id) {
mId = id;
@@ -154,6 +110,26 @@
}
/**
+ * Sets a human-readable string describing the transfer suitability of the route, or null if
+ * not applicable.
+ */
+ @NonNull
+ public Builder setSuitabilityStatus(@Nullable String suitabilityStatus) {
+ mSuitabilityStatus = suitabilityStatus;
+ return this;
+ }
+
+ /**
+ * Sets whether the corresponding route's selection is the result of an action of this app,
+ * or null if not applicable.
+ */
+ @NonNull
+ public Builder setTransferInitiatedBySelf(@Nullable Boolean transferInitiatedBySelf) {
+ mTransferInitiatedBySelf = transferInitiatedBySelf;
+ return this;
+ }
+
+ /**
* Builds {@link SystemRouteItem}.
*/
@NonNull
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapter.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapter.java
index aba49a1..f39259f 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapter.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapter.java
@@ -20,6 +20,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -114,6 +115,8 @@
private final AppCompatTextView mRouteIdTextView;
private final AppCompatTextView mRouteAddressTextView;
private final AppCompatTextView mRouteDescriptionTextView;
+ private final AppCompatTextView mSuitabilityStatusTextView;
+ private final AppCompatTextView mTransferInitiatedBySelfTextView;
ItemViewHolder(@NonNull View itemView) {
super(itemView);
@@ -122,21 +125,22 @@
mRouteIdTextView = itemView.findViewById(R.id.route_id);
mRouteAddressTextView = itemView.findViewById(R.id.route_address);
mRouteDescriptionTextView = itemView.findViewById(R.id.route_description);
+ mSuitabilityStatusTextView = itemView.findViewById(R.id.route_suitability_status);
+ mTransferInitiatedBySelfTextView =
+ itemView.findViewById(R.id.route_transfer_initiated_by_self);
}
void bind(SystemRouteItem systemRouteItem) {
- mRouteNameTextView.setText(systemRouteItem.getName());
- mRouteIdTextView.setText(systemRouteItem.getId());
-
- showViewIfNotNull(mRouteAddressTextView, systemRouteItem.getAddress());
- if (systemRouteItem.getAddress() != null) {
- mRouteAddressTextView.setText(systemRouteItem.getAddress());
- }
-
- showViewIfNotNull(mRouteDescriptionTextView, systemRouteItem.getDescription());
- if (systemRouteItem.getDescription() != null) {
- mRouteDescriptionTextView.setText(systemRouteItem.getDescription());
- }
+ mRouteNameTextView.setText(systemRouteItem.mName);
+ mRouteIdTextView.setText(systemRouteItem.mId);
+ setTextOrHide(mRouteAddressTextView, systemRouteItem.mAddress);
+ setTextOrHide(mRouteDescriptionTextView, systemRouteItem.mDescription);
+ setTextOrHide(mSuitabilityStatusTextView, systemRouteItem.mSuitabilityStatus);
+ String initiatedBySelfText =
+ systemRouteItem.mTransferInitiatedBySelf != null
+ ? "self-initiated: " + systemRouteItem.mTransferInitiatedBySelf
+ : null;
+ setTextOrHide(mTransferInitiatedBySelfTextView, initiatedBySelfText);
}
}
@@ -145,8 +149,7 @@
public boolean areItemsTheSame(@NonNull SystemRoutesAdapterItem oldItem,
@NonNull SystemRoutesAdapterItem newItem) {
if (oldItem instanceof SystemRouteItem && newItem instanceof SystemRouteItem) {
- return ((SystemRouteItem) oldItem).getId().equals(
- ((SystemRouteItem) newItem).getId());
+ return ((SystemRouteItem) oldItem).mId.equals(((SystemRouteItem) newItem).mId);
} else if (oldItem instanceof SystemRoutesSourceItem
&& newItem instanceof SystemRoutesSourceItem) {
return oldItem.equals(newItem);
@@ -169,11 +172,12 @@
}
}
- private static <T, V extends View> void showViewIfNotNull(@NonNull V view, @Nullable T obj) {
- if (obj == null) {
+ private static void setTextOrHide(@NonNull TextView view, @Nullable String text) {
+ if (text == null) {
view.setVisibility(View.GONE);
} else {
view.setVisibility(View.VISIBLE);
+ view.setText(text);
}
}
}
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouter2SystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouter2SystemRoutesSource.java
index 5785679..df60049 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouter2SystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouter2SystemRoutesSource.java
@@ -16,6 +16,7 @@
package com.example.androidx.mediarouting.activities.systemrouting.source;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2;
@@ -28,6 +29,8 @@
import com.example.androidx.mediarouting.activities.systemrouting.SystemRouteItem;
import com.example.androidx.mediarouting.activities.systemrouting.SystemRoutesSourceItem;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -38,11 +41,9 @@
@RequiresApi(Build.VERSION_CODES.R)
public final class MediaRouter2SystemRoutesSource extends SystemRoutesSource {
- @NonNull
- private Context mContext;
- @NonNull
- private MediaRouter2 mMediaRouter2;
-
+ @NonNull private final Context mContext;
+ @NonNull private final MediaRouter2 mMediaRouter2;
+ @NonNull private final Method mSuitabilityStatusMethod;
@NonNull
private final Map<String, MediaRoute2Info> mLastKnownRoutes = new HashMap<>();
@NonNull
@@ -81,6 +82,16 @@
@NonNull MediaRouter2 mediaRouter2) {
mContext = context;
mMediaRouter2 = mediaRouter2;
+
+ Method suitabilityStatusMethod = null;
+ // TODO: b/336510942 - Remove reflection once these APIs are available in
+ // androidx-platform-dev.
+ try {
+ suitabilityStatusMethod =
+ MediaRoute2Info.class.getDeclaredMethod("getSuitabilityStatus");
+ } catch (NoSuchMethodException | IllegalAccessError e) {
+ }
+ mSuitabilityStatusMethod = suitabilityStatusMethod;
}
@Override
@@ -127,10 +138,37 @@
}
@NonNull
- private static SystemRouteItem createRouteItemFor(@NonNull MediaRoute2Info routeInfo) {
- return new SystemRouteItem.Builder(routeInfo.getId())
- .setName(String.valueOf(routeInfo.getName()))
- .setDescription(String.valueOf(routeInfo.getDescription()))
- .build();
+ private SystemRouteItem createRouteItemFor(@NonNull MediaRoute2Info routeInfo) {
+ SystemRouteItem.Builder builder =
+ new SystemRouteItem.Builder(routeInfo.getId())
+ .setName(String.valueOf(routeInfo.getName()))
+ .setDescription(String.valueOf(routeInfo.getDescription()));
+ try {
+ if (mSuitabilityStatusMethod != null) {
+ // See b/336510942 for details on why reflection is needed.
+ @SuppressLint("BanUncheckedReflection")
+ int status = (Integer) mSuitabilityStatusMethod.invoke(routeInfo);
+ builder.setSuitabilityStatus(getHumanReadableSuitabilityStatus(status));
+ // TODO: b/319645714 - Populate wasTransferInitiatedBySelf. For that we need to
+ // change the implementation of this class to use the routing controller instead
+ // of a route callback.
+ }
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ }
+ return builder.build();
+ }
+
+ @NonNull
+ private String getHumanReadableSuitabilityStatus(int status) {
+ switch (status) {
+ case 0:
+ return "SUITABLE_FOR_DEFAULT_TRANSFER";
+ case 1:
+ return "SUITABLE_FOR_MANUAL_TRANSFER";
+ case 2:
+ return "NOT_SUITABLE_FOR_TRANSFER";
+ default:
+ return "UNKNOWN(" + status + ")";
+ }
}
}
diff --git a/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml b/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml
index 5d54748..2f8e533 100644
--- a/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml
+++ b/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml
@@ -77,6 +77,28 @@
android:textSize="14sp"
tools:text="This is a description of an amazing system route." />
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/route_suitability_status"
+ android:layout_marginTop="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/route_description"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:textSize="14sp"
+ tools:text="Suitability status." />
+
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/route_transfer_initiated_by_self"
+ android:layout_marginTop="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/route_suitability_status"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:textSize="14sp"
+ tools:text="Transfer initiated by self." />
+
</RelativeLayout>
</androidx.cardview.widget.CardView>