Enhancement for group operation in output switcher

-Add new group components in xml layout and assets
-Adjust view objects in adapter for new layout
-Add aboveStatusbar in MediaOutputControllter to keep the dialog style
when switching
-Add getGroupMediaDevices() in MediaOutputController to provide available
device list for group operation
-Update test cases

Bug: 155822415
Test: atest MediaOutputAdapterTest MediaOutputControllerTest MediaOutputBaseDialogTest MediaOutputDialogTest
Merged-In: If74bf2efb89a8ece61f4e0cf13e4dcfa30cb0a8a
Change-Id: If74bf2efb89a8ece61f4e0cf13e4dcfa30cb0a8a
diff --git a/packages/SystemUI/res/drawable/ic_check_box.xml b/packages/SystemUI/res/drawable/ic_check_box.xml
new file mode 100644
index 0000000..a8d1a65
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box.xml
@@ -0,0 +1,26 @@
+<!--
+  Copyright (C) 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/checked"
+        android:state_checked="true"
+        android:drawable="@drawable/ic_check_box_blue_24dp" />
+    <item
+        android:id="@+id/unchecked"
+        android:state_checked="false"
+        android:drawable="@drawable/ic_check_box_outline_24dp" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml b/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml
new file mode 100644
index 0000000..43cae69
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+  Copyright (C) 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"
+        android:fillColor="#4285F4"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml b/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml
new file mode 100644
index 0000000..f6f453a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+  Copyright (C) 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"
+        android:fillColor="#757575"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml b/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml
new file mode 100644
index 0000000..ae0d562
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml
@@ -0,0 +1,31 @@
+<!--
+  Copyright (C) 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
+        android:fillColor="#000000"/>
+    <path
+        android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
+        android:fillColor="#000000"/>
+    <path
+        android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
+        android:fillColor="#000000"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index ac8b7b5..c98c3a0 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -15,97 +15,124 @@
   ~ limitations under the License.
   -->
 
-<FrameLayout
-    android:id="@+id/device_container"
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/device_container"
     android:layout_width="match_parent"
-    android:layout_height="64dp">
-
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
     <FrameLayout
-        android:layout_width="36dp"
-        android:layout_height="36dp"
-        android:layout_gravity="center_vertical"
-        android:layout_marginStart="16dp">
-        <ImageView
-            android:id="@+id/title_icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"/>
-    </FrameLayout>
+        android:layout_width="match_parent"
+        android:layout_height="64dp">
 
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginStart="68dp"
-        android:ellipsize="end"
-        android:maxLines="1"
-        android:textColor="?android:attr/textColorPrimary"
-        android:textSize="14sp"/>
+        <FrameLayout
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="16dp">
+            <ImageView
+                android:id="@+id/title_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"/>
+        </FrameLayout>
 
-    <RelativeLayout
-        android:id="@+id/two_line_layout"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:layout_marginStart="52dp"
-        android:layout_marginEnd="69dp"
-        android:layout_marginTop="10dp">
         <TextView
-            android:id="@+id/two_line_title"
+            android:id="@+id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginStart="16dp"
-            android:layout_marginEnd="15dp"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="68dp"
             android:ellipsize="end"
             android:maxLines="1"
             android:textColor="?android:attr/textColorPrimary"
             android:textSize="14sp"/>
-        <TextView
-            android:id="@+id/subtitle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="16dp"
-            android:layout_marginEnd="15dp"
-            android:layout_marginBottom="7dp"
-            android:layout_alignParentBottom="true"
-            android:ellipsize="end"
-            android:maxLines="1"
-            android:textColor="?android:attr/textColorSecondary"
-            android:textSize="12sp"
-            android:fontFamily="roboto-regular"
-            android:visibility="gone"/>
-        <SeekBar
-            android:id="@+id/volume_seekbar"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentBottom="true"/>
-    </RelativeLayout>
 
-    <ProgressBar
-        android:id="@+id/volume_indeterminate_progress"
-        style="@*android:style/Widget.Material.ProgressBar.Horizontal"
-        android:layout_width="258dp"
-        android:layout_height="18dp"
-        android:layout_marginStart="68dp"
-        android:layout_marginTop="40dp"
-        android:indeterminate="true"
-        android:indeterminateOnly="true"
-        android:visibility="gone"/>
+        <RelativeLayout
+            android:id="@+id/two_line_layout"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:layout_marginStart="52dp"
+            android:layout_marginEnd="69dp"
+            android:layout_marginTop="10dp">
+            <TextView
+                android:id="@+id/two_line_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="16dp"
+                android:layout_marginEnd="15dp"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textSize="14sp"/>
+            <TextView
+                android:id="@+id/subtitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="16dp"
+                android:layout_marginEnd="15dp"
+                android:layout_marginBottom="7dp"
+                android:layout_alignParentBottom="true"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:textColor="?android:attr/textColorSecondary"
+                android:textSize="12sp"
+                android:fontFamily="roboto-regular"
+                android:visibility="gone"/>
+            <SeekBar
+                android:id="@+id/volume_seekbar"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"/>
+        </RelativeLayout>
+
+        <ProgressBar
+            android:id="@+id/volume_indeterminate_progress"
+            style="@*android:style/Widget.Material.ProgressBar.Horizontal"
+            android:layout_width="258dp"
+            android:layout_height="18dp"
+            android:layout_marginStart="68dp"
+            android:layout_marginTop="40dp"
+            android:indeterminate="true"
+            android:indeterminateOnly="true"
+            android:visibility="gone"/>
+
+        <View
+            android:id="@+id/end_divider"
+            android:layout_width="1dp"
+            android:layout_height="36dp"
+            android:layout_marginEnd="68dp"
+            android:layout_gravity="right|center_vertical"
+            android:background="?android:attr/listDivider"
+            android:visibility="gone"/>
+
+        <ImageView
+            android:id="@+id/add_icon"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_gravity="right|center_vertical"
+            android:layout_marginEnd="24dp"
+            android:src="@drawable/ic_add"
+            android:tint="?android:attr/colorAccent"
+            android:visibility="gone"/>
+
+        <CheckBox
+            android:id="@+id/check_box"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_gravity="right|center_vertical"
+            android:layout_marginEnd="24dp"
+            android:button="@drawable/ic_check_box"
+            android:visibility="gone"/>
+    </FrameLayout>
 
     <View
-        android:layout_width="1dp"
-        android:layout_height="36dp"
-        android:layout_marginEnd="68dp"
-        android:layout_gravity="right|center_vertical"
+        android:id="@+id/bottom_divider"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_marginTop="12dp"
+        android:layout_marginBottom="12dp"
+        android:layout_gravity="bottom"
         android:background="?android:attr/listDivider"
         android:visibility="gone"/>
-
-    <ImageView
-        android:id="@+id/end_icon"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:layout_gravity="right|center_vertical"
-        android:layout_marginEnd="24dp"
-        android:visibility="gone"/>
-</FrameLayout>
\ No newline at end of file
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index d1630eb..d26f7ab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -109,34 +109,45 @@
             super.onBind(device, topMargin, bottomMargin);
             final boolean currentlyConnected = isCurrentlyConnected(device);
             if (currentlyConnected) {
-                mConnectedItem = mFrameLayout;
+                mConnectedItem = mContainerLayout;
+            }
+            mBottomDivider.setVisibility(View.GONE);
+            mCheckBox.setVisibility(View.GONE);
+            if (currentlyConnected && mController.isActiveRemoteDevice(device)) {
+                // Init active device layout
+                mDivider.setVisibility(View.VISIBLE);
+                mDivider.setTransitionAlpha(1);
+                mAddIcon.setVisibility(View.VISIBLE);
+                mAddIcon.setTransitionAlpha(1);
+                mAddIcon.setOnClickListener(v -> onEndItemClick());
+            } else {
+                // Init non-active device layout
+                mDivider.setVisibility(View.GONE);
+                mAddIcon.setVisibility(View.GONE);
             }
             if (mController.isTransferring()) {
                 if (device.getState() == MediaDeviceState.STATE_CONNECTING
                         && !mController.hasAdjustVolumeUserRestriction()) {
-                    setTwoLineLayout(device, null /* title */, true /* bFocused */,
-                            false /* showSeekBar*/, true /* showProgressBar */,
-                            false /* showSubtitle */);
+                    setTwoLineLayout(device, true /* bFocused */, false /* showSeekBar*/,
+                            true /* showProgressBar */, false /* showSubtitle */);
                 } else {
                     setSingleLineLayout(getItemTitle(device), false /* bFocused */);
                 }
             } else {
                 // Set different layout for each device
                 if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
-                    setTwoLineLayout(device, null /* title */, false /* bFocused */,
-                            false /* showSeekBar*/, false /* showProgressBar */,
+                    setTwoLineLayout(device, false /* bFocused */,
+                            false /* showSeekBar */, false /* showProgressBar */,
                             true /* showSubtitle */);
                     mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
-                    mFrameLayout.setOnClickListener(v -> onItemClick(v, device));
-                } else if (!mController.hasAdjustVolumeUserRestriction()
-                        && currentlyConnected) {
-                    setTwoLineLayout(device, null /* title */, true /* bFocused */,
-                            true /* showSeekBar*/, false /* showProgressBar */,
-                            false /* showSubtitle */);
+                    mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
+                } else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) {
+                    setTwoLineLayout(device, true /* bFocused */, true /* showSeekBar */,
+                            false /* showProgressBar */, false /* showSubtitle */);
                     initSeekbar(device);
                 } else {
                     setSingleLineLayout(getItemTitle(device), false /* bFocused */);
-                    mFrameLayout.setOnClickListener(v -> onItemClick(v, device));
+                    mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
                 }
             }
         }
@@ -145,13 +156,17 @@
         void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
             super.onBind(customizedItem, topMargin, bottomMargin);
             if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
+                mCheckBox.setVisibility(View.GONE);
+                mDivider.setVisibility(View.GONE);
+                mAddIcon.setVisibility(View.GONE);
+                mBottomDivider.setVisibility(View.GONE);
                 setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
                         false /* bFocused */);
                 final Drawable d = mContext.getDrawable(R.drawable.ic_add);
                 d.setColorFilter(new PorterDuffColorFilter(
                         Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN));
                 mTitleIcon.setImageDrawable(d);
-                mFrameLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
+                mContainerLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
             }
         }
 
@@ -173,5 +188,9 @@
                 mController.launchBluetoothPairing();
             }
         }
+
+        private void onEndItemClick() {
+            mController.launchMediaOutputGroupDialog();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 2d3e77d..536b759 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -24,8 +24,9 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.FrameLayout;
+import android.widget.CheckBox;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.ProgressBar;
 import android.widget.RelativeLayout;
 import android.widget.SeekBar;
@@ -44,19 +45,17 @@
 public abstract class MediaOutputBaseAdapter extends
         RecyclerView.Adapter<MediaOutputBaseAdapter.MediaDeviceBaseViewHolder> {
 
-    private static final String FONT_SELECTED_TITLE = "sans-serif-medium";
-    private static final String FONT_TITLE = "sans-serif";
-
     static final int CUSTOMIZED_ITEM_PAIR_NEW = 1;
+    static final int CUSTOMIZED_ITEM_GROUP = 2;
 
     final MediaOutputController mController;
 
-    private boolean mIsDragging;
     private int mMargin;
     private boolean mIsAnimating;
 
     Context mContext;
     View mHolderView;
+    boolean mIsDragging;
 
     public MediaOutputBaseAdapter(MediaOutputController controller) {
         mController = controller;
@@ -99,27 +98,33 @@
 
         private static final int ANIM_DURATION = 200;
 
-        final FrameLayout mFrameLayout;
+        final LinearLayout mContainerLayout;
         final TextView mTitleText;
         final TextView mTwoLineTitleText;
         final TextView mSubTitleText;
         final ImageView mTitleIcon;
-        final ImageView mEndIcon;
+        final ImageView mAddIcon;
         final ProgressBar mProgressBar;
         final SeekBar mSeekBar;
         final RelativeLayout mTwoLineLayout;
+        final View mDivider;
+        final View mBottomDivider;
+        final CheckBox mCheckBox;
 
         MediaDeviceBaseViewHolder(View view) {
             super(view);
-            mFrameLayout = view.requireViewById(R.id.device_container);
+            mContainerLayout = view.requireViewById(R.id.device_container);
             mTitleText = view.requireViewById(R.id.title);
             mSubTitleText = view.requireViewById(R.id.subtitle);
             mTwoLineLayout = view.requireViewById(R.id.two_line_layout);
             mTwoLineTitleText = view.requireViewById(R.id.two_line_title);
             mTitleIcon = view.requireViewById(R.id.title_icon);
-            mEndIcon = view.requireViewById(R.id.end_icon);
             mProgressBar = view.requireViewById(R.id.volume_indeterminate_progress);
             mSeekBar = view.requireViewById(R.id.volume_seekbar);
+            mDivider = view.requireViewById(R.id.end_divider);
+            mBottomDivider = view.requireViewById(R.id.bottom_divider);
+            mAddIcon = view.requireViewById(R.id.add_icon);
+            mCheckBox = view.requireViewById(R.id.check_box);
         }
 
         void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
@@ -132,11 +137,11 @@
         }
 
         private void setMargin(boolean topMargin, boolean bottomMargin) {
-            ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mFrameLayout
+            ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mContainerLayout
                     .getLayoutParams();
             params.topMargin = topMargin ? mMargin : 0;
             params.bottomMargin = bottomMargin ? mMargin : 0;
-            mFrameLayout.setLayoutParams(params);
+            mContainerLayout.setLayoutParams(params);
         }
 
         void setSingleLineLayout(CharSequence title, boolean bFocused) {
@@ -146,13 +151,26 @@
             mTitleText.setTranslationY(0);
             mTitleText.setText(title);
             if (bFocused) {
-                mTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
+                mTitleText.setTypeface(Typeface.create(mContext.getString(
+                        com.android.internal.R.string.config_headlineFontFamilyMedium),
+                        Typeface.NORMAL));
             } else {
-                mTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+                mTitleText.setTypeface(Typeface.create(mContext.getString(
+                        com.android.internal.R.string.config_headlineFontFamily), Typeface.NORMAL));
             }
         }
 
-        void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
+        void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
+                boolean showProgressBar, boolean showSubtitle) {
+            setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle);
+        }
+
+        void setTwoLineLayout(CharSequence title, boolean bFocused, boolean showSeekBar,
+                boolean showProgressBar, boolean showSubtitle) {
+            setTwoLineLayout(null, title, bFocused, showSeekBar, showProgressBar, showSubtitle);
+        }
+
+        private void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
                 boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) {
             mTitleText.setVisibility(View.GONE);
             mTwoLineLayout.setVisibility(View.VISIBLE);
@@ -168,18 +186,21 @@
             }
 
             if (bFocused) {
-                mTwoLineTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE,
+                mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
+                        com.android.internal.R.string.config_headlineFontFamilyMedium),
                         Typeface.NORMAL));
             } else {
-                mTwoLineTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+                mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
+                        com.android.internal.R.string.config_headlineFontFamily), Typeface.NORMAL));
             }
         }
 
         void initSeekbar(MediaDevice device) {
             mSeekBar.setMax(device.getMaxVolume());
             mSeekBar.setMin(0);
-            if (mSeekBar.getProgress() != device.getCurrentVolume()) {
-                mSeekBar.setProgress(device.getCurrentVolume());
+            final int currentVolume = device.getCurrentVolume();
+            if (mSeekBar.getProgress() != currentVolume) {
+                mSeekBar.setProgress(currentVolume);
             }
             mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                 @Override
@@ -213,7 +234,9 @@
             }
             mIsAnimating = true;
             // Animation for title text
-            toTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
+            toTitleText.setTypeface(Typeface.create(mContext.getString(
+                    com.android.internal.R.string.config_headlineFontFamilyMedium),
+                    Typeface.NORMAL));
             toTitleText.animate()
                     .setDuration(ANIM_DURATION)
                     .translationY(-delta)
@@ -234,7 +257,9 @@
                         public void onAnimationEnd(Animator animation) {
                             final TextView fromTitleText = from.requireViewById(
                                     R.id.two_line_title);
-                            fromTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+                            fromTitleText.setTypeface(Typeface.create(mContext.getString(
+                                    com.android.internal.R.string.config_headlineFontFamily),
+                                    Typeface.NORMAL));
                             fromTitleText.animate()
                                     .setDuration(ANIM_DURATION)
                                     .translationY(delta)
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 2faf56a..dd9a808 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -119,6 +119,8 @@
         // Init device list
         mDevicesRecyclerView.setLayoutManager(mLayoutManager);
         mDevicesRecyclerView.setAdapter(mAdapter);
+        // Init header icon
+        mHeaderIcon.setOnClickListener(v -> onHeaderIconClick());
         // Init bottom buttons
         mDoneButton.setOnClickListener(v -> dismiss());
         mStopButton.setOnClickListener(v -> {
@@ -218,4 +220,7 @@
             dismiss();
         }
     }
+
+    void onHeaderIconClick() {
+    }
 }
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 b1f1bda..b9fa872 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -71,6 +71,8 @@
     private final MediaSessionManager mMediaSessionManager;
     private final ShadeController mShadeController;
     private final ActivityStarter mActivityStarter;
+    private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
+    private final boolean mAboveStatusbar;
     @VisibleForTesting
     final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
 
@@ -82,13 +84,14 @@
 
     @Inject
     public MediaOutputController(@NonNull Context context, String packageName,
-            MediaSessionManager mediaSessionManager, LocalBluetoothManager
+            boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
             lbm, ShadeController shadeController, ActivityStarter starter) {
         mContext = context;
         mPackageName = packageName;
         mMediaSessionManager = mediaSessionManager;
         mShadeController = shadeController;
         mActivityStarter = starter;
+        mAboveStatusbar = aboveStatusbar;
         InfoMediaManager imm = new InfoMediaManager(mContext, packageName, null, lbm);
         mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
     }
@@ -271,6 +274,42 @@
         mMediaDevices.addAll(targetMediaDevices);
     }
 
+    List<MediaDevice> getGroupMediaDevices() {
+        final List<MediaDevice> selectedDevices = getSelectedMediaDevice();
+        final List<MediaDevice> selectableDevices = getSelectableMediaDevice();
+        if (mGroupMediaDevices.isEmpty()) {
+            mGroupMediaDevices.addAll(selectedDevices);
+            mGroupMediaDevices.addAll(selectableDevices);
+            return mGroupMediaDevices;
+        }
+        // To keep the same list order
+        final Collection<MediaDevice> sourceDevices = new ArrayList<>();
+        final Collection<MediaDevice> targetMediaDevices = new ArrayList<>();
+        sourceDevices.addAll(selectedDevices);
+        sourceDevices.addAll(selectableDevices);
+        for (MediaDevice originalDevice : mGroupMediaDevices) {
+            for (MediaDevice newDevice : sourceDevices) {
+                if (TextUtils.equals(originalDevice.getId(), newDevice.getId())) {
+                    targetMediaDevices.add(newDevice);
+                    sourceDevices.remove(newDevice);
+                    break;
+                }
+            }
+        }
+        // Add new devices at the end of list if necessary
+        if (!sourceDevices.isEmpty()) {
+            targetMediaDevices.addAll(sourceDevices);
+        }
+        mGroupMediaDevices.clear();
+        mGroupMediaDevices.addAll(targetMediaDevices);
+
+        return mGroupMediaDevices;
+    }
+
+    void resetGroupMediaDevices() {
+        mGroupMediaDevices.clear();
+    }
+
     void connectDevice(MediaDevice device) {
         ThreadUtils.postOnBackgroundThread(() -> {
             mLocalMediaManager.connectDevice(device);
@@ -309,15 +348,6 @@
         return mLocalMediaManager.getDeselectableMediaDevice();
     }
 
-    boolean isDeviceIncluded(Collection<MediaDevice> deviceCollection, MediaDevice targetDevice) {
-        for (MediaDevice device : deviceCollection) {
-            if (TextUtils.equals(device.getId(), targetDevice.getId())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void adjustSessionVolume(String sessionId, int volume) {
         mLocalMediaManager.adjustSessionVolume(sessionId, volume);
     }
@@ -407,6 +437,16 @@
         mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true);
     }
 
+    void launchMediaOutputDialog() {
+        mCallback.dismissDialog();
+        new MediaOutputDialog(mContext, mAboveStatusbar, this);
+    }
+
+    void launchMediaOutputGroupDialog() {
+        mCallback.dismissDialog();
+        new MediaOutputGroupDialog(mContext, mAboveStatusbar, this);
+    }
+
     boolean isActiveRemoteDevice(@NonNull MediaDevice device) {
         final List<String> features = device.getFeatures();
         return (features.contains(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)
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 4cdca4c..e912b7d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -40,9 +40,8 @@
     /** Creates a [MediaOutputDialog] for the given package. */
     fun create(packageName: String, aboveStatusBar: Boolean) {
         mediaOutputDialog?.dismiss()
-
-        mediaOutputDialog = MediaOutputController(context, packageName, mediaSessionManager, lbm,
-                shadeController, starter).run {
+        mediaOutputDialog = MediaOutputController(context, packageName, aboveStatusBar,
+                mediaSessionManager, lbm, shadeController, starter).run {
             MediaOutputDialog(context, aboveStatusBar, this) }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 2d460aa..5218886 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -25,7 +25,7 @@
 import android.graphics.drawable.Icon;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
-import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.test.filters.SmallTest;
@@ -66,7 +66,7 @@
     public void setUp() {
         mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
         mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
-                .onCreateViewHolder(new FrameLayout(mContext), 0);
+                .onCreateViewHolder(new LinearLayout(mContext), 0);
 
         when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
         when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
@@ -75,6 +75,7 @@
         when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
         when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
         when(mMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(mMediaDevice1);
+        when(mMediaOutputController.isActiveRemoteDevice(mMediaDevice1)).thenReturn(true);
         when(mIconCompat.toIcon(mContext)).thenReturn(mIcon);
         when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1);
         when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1);
@@ -107,6 +108,11 @@
 
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText(
                 R.string.media_output_dialog_pairing_new));
     }
@@ -118,19 +124,41 @@
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
     }
 
     @Test
+    public void onBindViewHolder_bindNonActiveConnectedDevice_verifyView() {
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+    }
+
+    @Test
     public void onBindViewHolder_bindDisconnectedBluetoothDevice_verifyView() {
         when(mMediaDevice2.getDeviceType()).thenReturn(
                 MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
         when(mMediaDevice2.isConnected()).thenReturn(false);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
 
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(
                 mContext.getString(R.string.media_output_dialog_disconnected, TEST_DEVICE_NAME_2));
@@ -142,9 +170,13 @@
                 LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
 
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(mContext.getText(
@@ -162,7 +194,11 @@
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
     }
@@ -174,8 +210,13 @@
                 LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
-        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
     }
 
@@ -183,7 +224,7 @@
     public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
         when(mMediaOutputController.isZeroMode()).thenReturn(true);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
-        mViewHolder.mFrameLayout.performClick();
+        mViewHolder.mContainerLayout.performClick();
 
         verify(mMediaOutputController).launchBluetoothPairing();
     }
@@ -194,7 +235,7 @@
                 LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
-        mViewHolder.mFrameLayout.performClick();
+        mViewHolder.mContainerLayout.performClick();
 
         verify(mMediaOutputController).connectDevice(mMediaDevice2);
     }
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 27b5b7f..4639dcb 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
@@ -68,7 +68,7 @@
 
     @Before
     public void setUp() {
-        mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
+        mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
         mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
                 mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 0dcdecf..0576667484d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -34,6 +34,7 @@
 import android.media.session.MediaController;
 import android.media.session.MediaSessionManager;
 import android.testing.AndroidTestingRunner;
+import android.text.TextUtils;
 
 import androidx.test.filters.SmallTest;
 
@@ -60,6 +61,9 @@
     private static final String TEST_PACKAGE_NAME = "com.test.package.name";
     private static final String TEST_DEVICE_1_ID = "test_device_1_id";
     private static final String TEST_DEVICE_2_ID = "test_device_2_id";
+    private static final String TEST_DEVICE_3_ID = "test_device_3_id";
+    private static final String TEST_DEVICE_4_ID = "test_device_4_id";
+    private static final String TEST_DEVICE_5_ID = "test_device_5_id";
     private static final String TEST_ARTIST = "test_artist";
     private static final String TEST_SONG = "test_song";
     private static final String TEST_SESSION_ID = "test_session_id";
@@ -96,7 +100,7 @@
                 MediaSessionManager.class);
         when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
                 mCachedBluetoothDeviceManager);
-        mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME,
+        mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
         mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
@@ -139,8 +143,8 @@
 
     @Test
     public void start_withoutPackageName_verifyMediaControllerInit() {
-        mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager,
-                mLocalBluetoothManager, mShadeController, mStarter);
+        mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
+                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
 
         mMediaOutputController.start(mCb);
 
@@ -159,8 +163,8 @@
 
     @Test
     public void stop_withoutPackageName_verifyMediaControllerDeinit() {
-        mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager,
-                mLocalBluetoothManager, mShadeController, mStarter);
+        mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
+                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
         mMediaOutputController.start(mCb);
 
         mMediaOutputController.stop();
@@ -345,4 +349,78 @@
 
         assertThat(mMediaOutputController.isZeroMode()).isFalse();
     }
+
+    @Test
+    public void getGroupMediaDevices_differentDeviceOrder_showingSameOrder() {
+        final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
+        final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
+        final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class);
+        final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class);
+        final List<MediaDevice> selectedMediaDevices = new ArrayList<>();
+        final List<MediaDevice> selectableMediaDevices = new ArrayList<>();
+        when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
+        when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
+        when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID);
+        when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID);
+        selectedMediaDevices.add(selectedMediaDevice1);
+        selectedMediaDevices.add(selectedMediaDevice2);
+        selectableMediaDevices.add(selectableMediaDevice1);
+        selectableMediaDevices.add(selectableMediaDevice2);
+        doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice();
+        doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice();
+        final List<MediaDevice> groupMediaDevices = mMediaOutputController.getGroupMediaDevices();
+        // Reset order
+        selectedMediaDevices.clear();
+        selectedMediaDevices.add(selectedMediaDevice2);
+        selectedMediaDevices.add(selectedMediaDevice1);
+        selectableMediaDevices.clear();
+        selectableMediaDevices.add(selectableMediaDevice2);
+        selectableMediaDevices.add(selectableMediaDevice1);
+        final List<MediaDevice> newDevices = mMediaOutputController.getGroupMediaDevices();
+
+        assertThat(newDevices.size()).isEqualTo(groupMediaDevices.size());
+        for (int i = 0; i < groupMediaDevices.size(); i++) {
+            assertThat(TextUtils.equals(groupMediaDevices.get(i).getId(),
+                    newDevices.get(i).getId())).isTrue();
+        }
+    }
+
+    @Test
+    public void getGroupMediaDevices_newDevice_verifyDeviceOrder() {
+        final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
+        final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
+        final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class);
+        final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class);
+        final MediaDevice selectableMediaDevice3 = mock(MediaDevice.class);
+        final List<MediaDevice> selectedMediaDevices = new ArrayList<>();
+        final List<MediaDevice> selectableMediaDevices = new ArrayList<>();
+        when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
+        when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
+        when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID);
+        when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID);
+        when(selectableMediaDevice3.getId()).thenReturn(TEST_DEVICE_5_ID);
+        selectedMediaDevices.add(selectedMediaDevice1);
+        selectedMediaDevices.add(selectedMediaDevice2);
+        selectableMediaDevices.add(selectableMediaDevice1);
+        selectableMediaDevices.add(selectableMediaDevice2);
+        doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice();
+        doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice();
+        final List<MediaDevice> groupMediaDevices = mMediaOutputController.getGroupMediaDevices();
+        // Reset order
+        selectedMediaDevices.clear();
+        selectedMediaDevices.add(selectedMediaDevice2);
+        selectedMediaDevices.add(selectedMediaDevice1);
+        selectableMediaDevices.clear();
+        selectableMediaDevices.add(selectableMediaDevice3);
+        selectableMediaDevices.add(selectableMediaDevice2);
+        selectableMediaDevices.add(selectableMediaDevice1);
+        final List<MediaDevice> newDevices = mMediaOutputController.getGroupMediaDevices();
+
+        assertThat(newDevices.size()).isEqualTo(5);
+        for (int i = 0; i < groupMediaDevices.size(); i++) {
+            assertThat(TextUtils.equals(groupMediaDevices.get(i).getId(),
+                    newDevices.get(i).getId())).isTrue();
+        }
+        assertThat(newDevices.get(4).getId()).isEqualTo(TEST_DEVICE_5_ID);
+    }
 }
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 ca328fb..4b00d9e 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
@@ -64,7 +64,7 @@
 
     @Before
     public void setUp() {
-        mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
+        mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         mMediaOutputDialog = new MediaOutputDialog(mContext, false, mMediaOutputController);