Merge "[Output switcher] Filter disconnected device" into tm-qpr-dev
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index d6d7304..281501e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -22,6 +22,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
 import android.media.RoutingSessionInfo;
 import android.os.Build;
 import android.text.TextUtils;
@@ -83,6 +84,7 @@
     private InfoMediaManager mInfoMediaManager;
     private String mPackageName;
     private MediaDevice mOnTransferBluetoothDevice;
+    private AudioManager mAudioManager;
 
     @VisibleForTesting
     List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
@@ -126,6 +128,7 @@
         mPackageName = packageName;
         mLocalBluetoothManager =
                 LocalBluetoothManager.getInstance(context, /* onInitCallback= */ null);
+        mAudioManager = context.getSystemService(AudioManager.class);
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
         if (mLocalBluetoothManager == null) {
             Log.e(TAG, "Bluetooth is not supported on this device");
@@ -148,6 +151,7 @@
         mInfoMediaManager = infoMediaManager;
         mPackageName = packageName;
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        mAudioManager = context.getSystemService(AudioManager.class);
     }
 
     /**
@@ -527,13 +531,16 @@
             synchronized (mMediaDevicesLock) {
                 mMediaDevices.clear();
                 mMediaDevices.addAll(devices);
-                // Add disconnected bluetooth devices only when phone output device is available.
+                // Add muting expected bluetooth devices only when phone output device is available.
                 for (MediaDevice device : devices) {
                     final int type = device.getDeviceType();
                     if (type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE
                             || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE
                             || type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE) {
-                        mMediaDevices.addAll(buildDisconnectedBluetoothDevice());
+                        MediaDevice mutingExpectedDevice = getMutingExpectedDevice();
+                        if (mutingExpectedDevice != null) {
+                            mMediaDevices.add(mutingExpectedDevice);
+                        }
                         break;
                     }
                 }
@@ -552,6 +559,34 @@
             }
         }
 
+        private MediaDevice getMutingExpectedDevice() {
+            if (mBluetoothAdapter == null
+                    || mAudioManager.getMutingExpectedDevice() == null) {
+                Log.w(TAG, "BluetoothAdapter is null or muting expected device not exist");
+                return null;
+            }
+            final List<BluetoothDevice> bluetoothDevices =
+                    mBluetoothAdapter.getMostRecentlyConnectedDevices();
+            final CachedBluetoothDeviceManager cachedDeviceManager =
+                    mLocalBluetoothManager.getCachedDeviceManager();
+            for (BluetoothDevice device : bluetoothDevices) {
+                final CachedBluetoothDevice cachedDevice =
+                        cachedDeviceManager.findDevice(device);
+                if (isBondedMediaDevice(cachedDevice) && isMutingExpectedDevice(cachedDevice)) {
+                    return new BluetoothMediaDevice(mContext,
+                            cachedDevice,
+                            null, null, mPackageName);
+                }
+            }
+            return null;
+        }
+
+        private boolean isMutingExpectedDevice(CachedBluetoothDevice cachedDevice) {
+            return mAudioManager.getMutingExpectedDevice() != null
+                    && cachedDevice.getAddress().equals(
+                    mAudioManager.getMutingExpectedDevice().getAddress());
+        }
+
         private List<MediaDevice> buildDisconnectedBluetoothDevice() {
             if (mBluetoothAdapter == null) {
                 Log.w(TAG, "buildDisconnectedBluetoothDevice() BluetoothAdapter is null");
@@ -595,6 +630,13 @@
             return new ArrayList<>(mDisconnectedMediaDevices);
         }
 
+        private boolean isBondedMediaDevice(CachedBluetoothDevice cachedDevice) {
+            return cachedDevice != null
+                    && cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
+                    && !cachedDevice.isConnected()
+                    && isMediaDevice(cachedDevice);
+        }
+
         private boolean isMediaDevice(CachedBluetoothDevice device) {
             for (LocalBluetoothProfile profile : device.getConnectableProfiles()) {
                 if (profile instanceof A2dpProfile || profile instanceof HearingAidProfile ||
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cdb4f44..01b9194 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2297,8 +2297,8 @@
     <string name="media_output_dialog_disconnected">(disconnected)</string>
     <!-- Summary for connecting error message [CHAR LIMIT=NONE] -->
     <string name="media_output_dialog_connect_failed">Can\'t switch. Tap to try again.</string>
-    <!-- Title for pairing item [CHAR LIMIT=60] -->
-    <string name="media_output_dialog_pairing_new">Pair new device</string>
+    <!-- Title for connecting item [CHAR LIMIT=60] -->
+    <string name="media_output_dialog_pairing_new">Connect a device</string>
     <!-- Title for launch app [CHAR LIMIT=60] -->
     <string name="media_output_dialog_launch_app_text">To cast this session, please open the app.</string>
     <!-- App name when can't get app name [CHAR LIMIT=60] -->
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 ec4c4e6..e9b6af4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -60,7 +60,7 @@
     @Override
     public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) {
         final int size = mController.getMediaDevices().size();
-        if (position == size && mController.isZeroMode()) {
+        if (position == size) {
             viewHolder.onBind(CUSTOMIZED_ITEM_PAIR_NEW, false /* topMargin */,
                     true /* bottomMargin */);
         } else if (position < size) {
@@ -75,7 +75,7 @@
     @Override
     public long getItemId(int position) {
         final int size = mController.getMediaDevices().size();
-        if (position == size && mController.isZeroMode()) {
+        if (position == size) {
             return -1;
         } else if (position < size) {
             return ((List<MediaDevice>) (mController.getMediaDevices()))
@@ -88,11 +88,8 @@
 
     @Override
     public int getItemCount() {
-        if (mController.isZeroMode()) {
-            // Add extra one for "pair new" or dynamic group
-            return mController.getMediaDevices().size() + 1;
-        }
-        return mController.getMediaDevices().size();
+        // Add extra one for "pair new"
+        return mController.getMediaDevices().size() + 1;
     }
 
     class MediaDeviceViewHolder extends MediaDeviceBaseViewHolder {
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 247ffa7..7e3275d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.media.dialog;
 
-import static android.provider.Settings.ACTION_BLUETOOTH_PAIRING_SETTINGS;
+import static android.provider.Settings.ACTION_BLUETOOTH_SETTINGS;
 
 import android.annotation.CallbackExecutor;
 import android.app.AlertDialog;
@@ -300,6 +300,9 @@
             return;
         }
         try {
+            synchronized (mMediaDevicesLock) {
+                mMediaDevices.removeIf(MediaDevice::isMutingExpectedDevice);
+            }
             mAudioManager.cancelMuteAwaitConnection(mAudioManager.getMutingExpectedDevice());
         } catch (Exception e) {
             Log.d(TAG, "Unable to cancel mute await connection");
@@ -711,22 +714,6 @@
         return false;
     }
 
-    boolean isZeroMode() {
-        synchronized (mMediaDevicesLock) {
-            if (mMediaDevices.size() == 1) {
-                final MediaDevice device = mMediaDevices.iterator().next();
-                // Add "pair new" only when local output device exists
-                final int type = device.getDeviceType();
-                if (type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE
-                        || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE
-                        || type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-
     void launchBluetoothPairing(View view) {
         ActivityLaunchAnimator.Controller controller =
                 mDialogLaunchAnimator.createActivityLaunchController(view);
@@ -736,7 +723,7 @@
         }
 
         Intent launchIntent =
-                new Intent(ACTION_BLUETOOTH_PAIRING_SETTINGS)
+                new Intent(ACTION_BLUETOOTH_SETTINGS)
                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
         final Intent deepLinkIntent =
                 new Intent(Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
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 2261829..568e0cb 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
@@ -78,7 +78,6 @@
 
         when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
         when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
-        when(mMediaOutputController.isZeroMode()).thenReturn(false);
         when(mMediaOutputController.isTransferring()).thenReturn(false);
         when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
         when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
@@ -98,28 +97,12 @@
     }
 
     @Test
-    public void getItemCount_nonZeroMode_isDeviceSize() {
-        assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size());
-    }
-
-    @Test
-    public void getItemCount_zeroMode_containExtraOneForPairNew() {
-        when(mMediaOutputController.isZeroMode()).thenReturn(true);
-
+    public void getItemCount_containExtraOneForPairNew() {
         assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size() + 1);
     }
 
     @Test
-    public void getItemCount_withDynamicGroup_containExtraOneForGroup() {
-        when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
-        when(mMediaOutputController.isZeroMode()).thenReturn(false);
-
-        assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size());
-    }
-
-    @Test
-    public void onBindViewHolder_zeroMode_bindPairNew_verifyView() {
-        when(mMediaOutputController.isZeroMode()).thenReturn(true);
+    public void onBindViewHolder_bindPairNew_verifyView() {
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
 
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -133,7 +116,6 @@
     @Test
     public void onBindViewHolder_bindGroup_withSessionName_verifyView() {
         when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
-        when(mMediaOutputController.isZeroMode()).thenReturn(false);
         when(mMediaOutputController.getSessionName()).thenReturn(TEST_SESSION_NAME);
         mMediaOutputAdapter.getItemCount();
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
@@ -148,7 +130,6 @@
     @Test
     public void onBindViewHolder_bindGroup_noSessionName_verifyView() {
         when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
-        when(mMediaOutputController.isZeroMode()).thenReturn(false);
         when(mMediaOutputController.getSessionName()).thenReturn(null);
         mMediaOutputAdapter.getItemCount();
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
@@ -257,7 +238,6 @@
 
     @Test
     public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
-        when(mMediaOutputController.isZeroMode()).thenReturn(true);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
         mViewHolder.mContainerLayout.performClick();
 
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 2bf5f0f..751c895 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
@@ -383,62 +383,6 @@
     }
 
     @Test
-    public void isZeroMode_onlyFromPhoneOutput_returnTrue() {
-        // Multiple available devices
-        assertThat(mMediaOutputController.isZeroMode()).isFalse();
-        when(mMediaDevice1.getDeviceType()).thenReturn(
-                MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
-        mMediaDevices.clear();
-        mMediaDevices.add(mMediaDevice1);
-        mMediaOutputController.start(mCb);
-        mMediaOutputController.onDeviceListUpdate(mMediaDevices);
-
-        assertThat(mMediaOutputController.isZeroMode()).isTrue();
-
-        when(mMediaDevice1.getDeviceType()).thenReturn(
-                MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE);
-
-        assertThat(mMediaOutputController.isZeroMode()).isTrue();
-
-        when(mMediaDevice1.getDeviceType()).thenReturn(
-                MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE);
-
-        assertThat(mMediaOutputController.isZeroMode()).isTrue();
-    }
-
-    @Test
-    public void isZeroMode_notFromPhoneOutput_returnFalse() {
-        when(mMediaDevice1.getDeviceType()).thenReturn(
-                MediaDevice.MediaDeviceType.TYPE_UNKNOWN);
-        mMediaDevices.clear();
-        mMediaDevices.add(mMediaDevice1);
-        mMediaOutputController.start(mCb);
-        mMediaOutputController.onDeviceListUpdate(mMediaDevices);
-
-        assertThat(mMediaOutputController.isZeroMode()).isFalse();
-
-        when(mMediaDevice1.getDeviceType()).thenReturn(
-                MediaDevice.MediaDeviceType.TYPE_FAST_PAIR_BLUETOOTH_DEVICE);
-
-        assertThat(mMediaOutputController.isZeroMode()).isFalse();
-
-        when(mMediaDevice1.getDeviceType()).thenReturn(
-                MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
-
-        assertThat(mMediaOutputController.isZeroMode()).isFalse();
-
-        when(mMediaDevice1.getDeviceType()).thenReturn(
-                MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE);
-
-        assertThat(mMediaOutputController.isZeroMode()).isFalse();
-
-        when(mMediaDevice1.getDeviceType()).thenReturn(
-                MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE);
-
-        assertThat(mMediaOutputController.isZeroMode()).isFalse();
-    }
-
-    @Test
     public void getGroupMediaDevices_differentDeviceOrder_showingSameOrder() {
         final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
         final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);