Expose an Output Switcher dialog for system routing

The new dialog will hide app and playback related metadata and will
only control system routing.

Test: adb shell am broadcast -a com.android.systemui.action.LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG com.android.systemui
Bug: 284227163
Flag: NA
Change-Id: I13bbcfe9c5446e41f722a154b7740d2c472afad4
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 5dacba5..52b51d7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -413,12 +413,8 @@
      */
     @NonNull
     List<MediaDevice> getSelectedMediaDevices() {
-        if (TextUtils.isEmpty(mPackageName)) {
-            Log.w(TAG, "getSelectedMediaDevices() package name is null or empty!");
-            return Collections.emptyList();
-        }
+        RoutingSessionInfo info = getRoutingSessionInfo();
 
-        final RoutingSessionInfo info = getRoutingSessionInfo();
         if (info == null) {
             Log.w(TAG, "getSelectedMediaDevices() cannot find selectable MediaDevice from : "
                     + mPackageName);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
index 3514932..02ec90d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
@@ -48,6 +48,17 @@
             "com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG";
 
     /**
+     * An intent action to launch a media output dialog without any app or playback metadata, which
+     * only controls system routing.
+     *
+     * <p>System routes are those provided by the system, such as built-in speakers, wired headsets,
+     * bluetooth devices, and other outputs that require the app to feed media samples to the
+     * framework.
+     */
+    public static final String ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG =
+            "com.android.systemui.action.LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG";
+
+    /**
      * An intent action to launch media output broadcast dialog.
      */
     public static final String ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG =
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e218308..0a71cda 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -1044,6 +1044,7 @@
                   android:exported="true">
             <intent-filter android:priority="1">
                 <action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG" />
+                <action android:name="com.android.systemui.action.LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG" />
                 <action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG" />
                 <action android:name="com.android.systemui.action.DISMISS_MEDIA_OUTPUT_DIALOG" />
             </intent-filter>
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 0c5a14f..48f432e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -58,8 +58,8 @@
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.systemui.res.R;
 import com.android.systemui.broadcast.BroadcastSender;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
 import java.util.concurrent.Executor;
@@ -85,6 +85,13 @@
     final MediaOutputController mMediaOutputController;
     final BroadcastSender mBroadcastSender;
 
+    /**
+     * Signals whether the dialog should NOT show app-related metadata.
+     *
+     * <p>A metadata-less dialog hides the title, subtitle, and app icon in the header.
+     */
+    private final boolean mIncludePlaybackAndAppMetadata;
+
     @VisibleForTesting
     View mDialogView;
     private TextView mHeaderTitle;
@@ -210,8 +217,11 @@
         }
     }
 
-    public MediaOutputBaseDialog(Context context, BroadcastSender broadcastSender,
-            MediaOutputController mediaOutputController) {
+    public MediaOutputBaseDialog(
+            Context context,
+            BroadcastSender broadcastSender,
+            MediaOutputController mediaOutputController,
+            boolean includePlaybackAndAppMetadata) {
         super(context, R.style.Theme_SystemUI_Dialog_Media);
 
         // Save the context that is wrapped with our theme.
@@ -226,6 +236,7 @@
         mListPaddingTop = mContext.getResources().getDimensionPixelSize(
                 R.dimen.media_output_dialog_list_padding_top);
         mExecutor = Executors.newSingleThreadExecutor();
+        mIncludePlaybackAndAppMetadata = includePlaybackAndAppMetadata;
     }
 
     @Override
@@ -354,7 +365,10 @@
             updateDialogBackgroundColor();
             mHeaderIcon.setVisibility(View.GONE);
         }
-        if (appSourceIcon != null) {
+
+        if (!mIncludePlaybackAndAppMetadata) {
+            mAppResourceIcon.setVisibility(View.GONE);
+        } else if (appSourceIcon != null) {
             Icon appIcon = appSourceIcon.toIcon(mContext);
             mAppResourceIcon.setColorFilter(mMediaOutputController.getColorItemContent());
             mAppResourceIcon.setImageIcon(appIcon);
@@ -373,17 +387,24 @@
             mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size + padding, size));
         }
         mAppButton.setText(mMediaOutputController.getAppSourceName());
-        // Update title and subtitle
-        mHeaderTitle.setText(getHeaderText());
-        final CharSequence subTitle = getHeaderSubtitle();
-        if (TextUtils.isEmpty(subTitle)) {
+
+        if (!mIncludePlaybackAndAppMetadata) {
+            mHeaderTitle.setVisibility(View.GONE);
             mHeaderSubtitle.setVisibility(View.GONE);
-            mHeaderTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
         } else {
-            mHeaderSubtitle.setVisibility(View.VISIBLE);
-            mHeaderSubtitle.setText(subTitle);
-            mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
+            // Update title and subtitle
+            mHeaderTitle.setText(getHeaderText());
+            final CharSequence subTitle = getHeaderSubtitle();
+            if (TextUtils.isEmpty(subTitle)) {
+                mHeaderSubtitle.setVisibility(View.GONE);
+                mHeaderTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
+            } else {
+                mHeaderSubtitle.setVisibility(View.VISIBLE);
+                mHeaderSubtitle.setText(subTitle);
+                mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
+            }
         }
+
         // Show when remote media session is available or
         //      when the device supports BT LE audio + media is playing
         mStopButton.setVisibility(getStopButtonVisibility());
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index ac64300..8e0191e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -42,12 +42,10 @@
 import androidx.core.graphics.drawable.IconCompat;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.media.BluetoothMediaDevice;
-import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.qrcode.QrCodeGenerator;
-import com.android.systemui.res.R;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
 import com.google.zxing.WriterException;
@@ -237,7 +235,11 @@
 
     MediaOutputBroadcastDialog(Context context, boolean aboveStatusbar,
             BroadcastSender broadcastSender, MediaOutputController mediaOutputController) {
-        super(context, broadcastSender, mediaOutputController);
+        super(
+                context,
+                broadcastSender,
+                mediaOutputController, /* includePlaybackAndAppMetadata */
+                true);
         mAdapter = new MediaOutputAdapter(mMediaOutputController);
         // TODO(b/226710953): Move the part to MediaOutputBaseDialog for every class
         //  that extends MediaOutputBaseDialog
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 426a497..375a0ce 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -78,7 +78,6 @@
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.res.R;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
@@ -86,6 +85,7 @@
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.monet.ColorScheme;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -358,7 +358,7 @@
     }
 
     Drawable getAppSourceIconFromPackage() {
-        if (mPackageName.isEmpty()) {
+        if (TextUtils.isEmpty(mPackageName)) {
             return null;
         }
         try {
@@ -372,7 +372,7 @@
     }
 
     String getAppSourceName() {
-        if (mPackageName.isEmpty()) {
+        if (TextUtils.isEmpty(mPackageName)) {
             return null;
         }
         final PackageManager packageManager = mContext.getPackageManager();
@@ -391,7 +391,7 @@
     }
 
     Intent getAppLaunchIntent() {
-        if (mPackageName.isEmpty()) {
+        if (TextUtils.isEmpty(mPackageName)) {
             return null;
         }
         return mContext.getPackageManager().getLaunchIntentForPackage(mPackageName);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index 4640a5d..d40699c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -27,10 +27,10 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.res.R;
 
 /**
  * Dialog for media output transferring.
@@ -40,10 +40,15 @@
     private final DialogLaunchAnimator mDialogLaunchAnimator;
     private final UiEventLogger mUiEventLogger;
 
-    MediaOutputDialog(Context context, boolean aboveStatusbar, BroadcastSender broadcastSender,
-            MediaOutputController mediaOutputController, DialogLaunchAnimator dialogLaunchAnimator,
-            UiEventLogger uiEventLogger) {
-        super(context, broadcastSender, mediaOutputController);
+    MediaOutputDialog(
+            Context context,
+            boolean aboveStatusbar,
+            BroadcastSender broadcastSender,
+            MediaOutputController mediaOutputController,
+            DialogLaunchAnimator dialogLaunchAnimator,
+            UiEventLogger uiEventLogger,
+            boolean includePlaybackAndAppMetadata) {
+        super(context, broadcastSender, mediaOutputController, includePlaybackAndAppMetadata);
         mDialogLaunchAnimator = dialogLaunchAnimator;
         mUiEventLogger = uiEventLogger;
         mAdapter = new MediaOutputAdapter(mMediaOutputController);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 2b38edb..b04a7a4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -61,6 +61,19 @@
 
     /** Creates a [MediaOutputDialog] for the given package. */
     open fun create(packageName: String, aboveStatusBar: Boolean, view: View? = null) {
+        create(packageName, aboveStatusBar, view, includePlaybackAndAppMetadata = true)
+    }
+
+    open fun createDialogForSystemRouting() {
+        create(packageName = null, aboveStatusBar = false, includePlaybackAndAppMetadata = false)
+    }
+
+    private fun create(
+            packageName: String?,
+            aboveStatusBar: Boolean,
+            view: View? = null,
+            includePlaybackAndAppMetadata: Boolean = true
+    ) {
         // Dismiss the previous dialog, if any.
         mediaOutputDialog?.dismiss()
 
@@ -71,7 +84,7 @@
             powerExemptionManager, keyGuardManager, featureFlags, userTracker)
         val dialog =
             MediaOutputDialog(context, aboveStatusBar, broadcastSender, controller,
-                    dialogLaunchAnimator, uiEventLogger)
+                    dialogLaunchAnimator, uiEventLogger, includePlaybackAndAppMetadata)
         mediaOutputDialog = dialog
 
         // Show the dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
index 132bf99..1002cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
@@ -19,7 +19,6 @@
 import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
-import android.text.TextUtils
 import android.util.Log
 import com.android.settingslib.media.MediaOutputConstants
 import javax.inject.Inject
@@ -35,16 +34,16 @@
     private val mediaOutputBroadcastDialogFactory: MediaOutputBroadcastDialogFactory
 ) : BroadcastReceiver() {
     override fun onReceive(context: Context, intent: Intent) {
-        when {
-            TextUtils.equals(
-                MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG, intent.action) -> {
+        when (intent.action) {
+            MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG -> {
                 val packageName: String? =
                     intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME)
                 launchMediaOutputDialogIfPossible(packageName)
             }
-            TextUtils.equals(
-                MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG,
-                intent.action) -> {
+            MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG -> {
+                mediaOutputDialogFactory.createDialogForSystemRouting()
+            }
+            MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG -> {
                 val packageName: String? =
                     intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME)
                 launchMediaOutputBroadcastDialogIfPossible(packageName)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 9dfb5a5..e082ca8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -47,13 +47,13 @@
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 
@@ -305,7 +305,11 @@
 
         MediaOutputBaseDialogImpl(Context context, BroadcastSender broadcastSender,
                 MediaOutputController mediaOutputController) {
-            super(context, broadcastSender, mediaOutputController);
+            super(
+                    context,
+                    broadcastSender,
+                    mediaOutputController, /* includePlaybackAndAppMetadata */
+                    true);
 
             mAdapter = mMediaOutputBaseAdapter;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 379136b..d5dc502 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -49,13 +49,13 @@
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 
@@ -394,8 +394,14 @@
 
     @NonNull
     private MediaOutputDialog makeTestDialog(MediaOutputController controller) {
-        return new MediaOutputDialog(mContext, false, mBroadcastSender,
-                controller, mDialogLaunchAnimator, mUiEventLogger);
+        return new MediaOutputDialog(
+                mContext,
+                false,
+                mBroadcastSender,
+                controller,
+                mDialogLaunchAnimator,
+                mUiEventLogger,
+                true);
     }
 
     private void withTestDialog(MediaOutputController controller, Consumer<MediaOutputDialog> c) {