Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72dc3726 authored by shaoweishen's avatar shaoweishen Committed by Shaowei Shen
Browse files

[Output Switcher] Add suport for subtext and ongoing session

Add support for device with subtext, set click action based on selection
behavior and provide status icon based on subtext.
For ongoing session, currently only support participant, which not allow
volume control.
Enable feature flag in this branch for following up testing.

Test: atest MediaOutputAdapterTest MediaOutputControllerTest MediaOutputBaseDialogTest MediaOutputDialogTest
Bug: 260021936
Bug: 260010689
Bug: 262013662
Change-Id: I93619a76eeaacc8fbde2b5eca2fff6e6b87b55a4
parent 4f767358
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -1315,6 +1315,13 @@
    <string name="media_transfer_this_device_name" product="tablet">This tablet</string>
    <!-- Name of the phone device with an active remote session. [CHAR LIMIT=30] -->
    <string name="media_transfer_this_phone">This phone</string>
    <!-- Sub status indicates device need premium account. [CHAR LIMIT=NONE] -->
    <string name="media_output_status_require_premium">Upgrade account to switch</string>
    <!-- Sub status indicates device not support download content. [CHAR LIMIT=NONE] -->
    <string name="media_output_status_not_support_downloads">Can\’t play downloads here</string>
    <!-- Sub status indicates device need to wait after ad. [CHAR LIMIT=NONE] -->
    <string name="media_output_status_try_after_ad">Try again after the ad</string>


    <!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] -->
    <string name="profile_connect_timeout_subtext">Problem connecting. Turn device off &amp; back on</string>
+20 −0
Original line number Diff line number Diff line
@@ -34,10 +34,12 @@ import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;

import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;

import android.annotation.Nullable;
import android.annotation.TargetApi;
import android.app.Notification;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
@@ -191,6 +193,14 @@ public class InfoMediaManager extends MediaManager {
                && Api34Impl.preferRouteListingOrdering(mRouterManager, mPackageName);
    }

    @Nullable
    ComponentName getLinkedItemComponentName() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
            return null;
        }
        return Api34Impl.getLinkedItemComponentName(mRouterManager, mPackageName);
    }

    /**
     * Remove a {@code device} from current media.
     *
@@ -681,6 +691,16 @@ public class InfoMediaManager extends MediaManager {
                    && !routeListingPreference.getUseSystemOrdering();
        }

        @DoNotInline
        @Nullable
        static ComponentName getLinkedItemComponentName(
                MediaRouter2Manager mediaRouter2Manager, String packageName) {
            RouteListingPreference routeListingPreference =
                    mediaRouter2Manager.getRouteListingPreference(packageName);
            return routeListingPreference == null ? null
                    : routeListingPreference.getLinkedItemComponentName();
        }

        @DoNotInline
        static void onRouteListingPreferenceUpdated(
                String packageName,
+11 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
import android.app.Notification;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
@@ -216,6 +217,16 @@ public class LocalMediaManager implements BluetoothCallback {
        return mInfoMediaManager.preferRouteListingOrdering();
    }

    /**
     * Returns required component name for system to take the user back to the app by launching an
     * intent with the returned {@link ComponentName}, using action {@link #ACTION_TRANSFER_MEDIA},
     * with the extra {@link #EXTRA_ROUTE_ID}.
     */
    @Nullable
    public ComponentName getLinkedItemComponentName() {
        return mInfoMediaManager.getLinkedItemComponentName();
    }

    /**
     * Start scan connected MediaDevice
     */
+67 −9
Original line number Diff line number Diff line
@@ -30,8 +30,14 @@ import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
import static android.media.MediaRoute2Info.TYPE_USB_HEADSET;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
import static android.media.RouteListingPreference.Item.FLAG_ONGOING_SESSION;
import static android.media.RouteListingPreference.Item.FLAG_SUGGESTED_ROUTE;
import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_NONE;
import static android.media.RouteListingPreference.Item.SUBTEXT_AD_ROUTING_DISALLOWED;
import static android.media.RouteListingPreference.Item.SUBTEXT_CUSTOM;
import static android.media.RouteListingPreference.Item.SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED;
import static android.media.RouteListingPreference.Item.SUBTEXT_NONE;
import static android.media.RouteListingPreference.Item.SUBTEXT_SUBSCRIPTION_REQUIRED;

import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;

@@ -51,6 +57,8 @@ import androidx.annotation.IntDef;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;

import com.android.settingslib.R;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -194,28 +202,58 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
    public abstract String getId();

    /**
     * Get disabled reason of device
     * Get selection behavior of device
     *
     * @return disabled reason of device
     * @return selection behavior of device
     */
    @RouteListingPreference.Item.SubText
    public int getDisableReason() {
    public int getSelectionBehavior() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && mItem != null
                ? mItem.getSubText()
                : -1;
                ? mItem.getSelectionBehavior() : SELECTION_BEHAVIOR_NONE;
    }

    /**
     * Checks if device is has disabled reason
     * Checks if device is has subtext
     *
     * @return true if device has disabled reason
     * @return true if device has subtext
     */
    public boolean hasDisabledReason() {
    public boolean hasSubtext() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
                && mItem != null
                && mItem.getSubText() != SUBTEXT_NONE;
    }

    /**
     * Get subtext of device
     *
     * @return subtext of device
     */
    @RouteListingPreference.Item.SubText
    public int getSubtext() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && mItem != null
                ? mItem.getSubText() : SUBTEXT_NONE;
    }

    /**
     * Returns subtext string for current route.
     *
     * @return subtext string for this route
     */
    public String getSubtextString() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && mItem != null
                ? Api34Impl.composeSubtext(mItem, mContext) : null;
    }

    /**
     * Checks if device has ongoing shared session, which allow user to join
     *
     * @return true if device has ongoing session
     */
    public boolean hasOngoingSession() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
                && Api34Impl.hasOngoingSession(mItem);
    }

    /**
     * Checks if device is suggested device from application
     *
@@ -513,7 +551,27 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
    private static class Api34Impl {
        @DoNotInline
        static boolean isSuggestedDevice(RouteListingPreference.Item item) {
            return item != null && item.getFlags() == FLAG_SUGGESTED_ROUTE;
            return item != null && (item.getFlags() & FLAG_SUGGESTED_ROUTE) != 0;
        }

        @DoNotInline
        static boolean hasOngoingSession(RouteListingPreference.Item item) {
            return item != null && (item.getFlags() & FLAG_ONGOING_SESSION) != 0;
        }

        @DoNotInline
        static String composeSubtext(RouteListingPreference.Item item, Context context) {
            switch (item.getSubText()) {
                case SUBTEXT_SUBSCRIPTION_REQUIRED:
                    return context.getString(R.string.media_output_status_require_premium);
                case SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED:
                    return context.getString(R.string.media_output_status_not_support_downloads);
                case SUBTEXT_AD_ROUTING_DISALLOWED:
                    return context.getString(R.string.media_output_status_try_after_ad);
                case SUBTEXT_CUSTOM:
                    return (String) item.getCustomSubtextMessage();
            }
            return "";
        }
    }
}
+21 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
@@ -88,6 +89,8 @@ public class InfoMediaManagerTest {
    private MediaManager.MediaDeviceCallback mCallback;
    @Mock
    private MediaSessionManager mMediaSessionManager;
    @Mock
    private ComponentName mComponentName;

    private InfoMediaManager mInfoMediaManager;
    private Context mContext;
@@ -372,6 +375,24 @@ public class InfoMediaManagerTest {
        assertThat(mInfoMediaManager.preferRouteListingOrdering()).isFalse();
    }

    @Test
    public void getInAppOnlyItemRoutingReceiver_oldSdkVersion_returnsNull() {
        assertThat(mInfoMediaManager.getLinkedItemComponentName()).isNull();
    }

    @Test
    public void getInAppOnlyItemRoutingReceiver_newSdkVersionWithReceiverExist_returns() {
        ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
                Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
        when(mRouterManager.getRouteListingPreference(any())).thenReturn(
                new RouteListingPreference.Builder().setItems(
                        ImmutableList.of()).setUseSystemOrdering(
                        false).setLinkedItemComponentName(mComponentName).build());
        mInfoMediaManager.mRouterManager = mRouterManager;

        assertThat(mInfoMediaManager.getLinkedItemComponentName()).isEqualTo(mComponentName);
    }

    private List<MediaRoute2Info> getRoutesListWithDuplicatedIds() {
        final List<MediaRoute2Info> routes = new ArrayList<>();
        final MediaRoute2Info info = mock(MediaRoute2Info.class);
Loading