Disable the spatializer options for device categories

If the BT device cannot be of category headphones do not show the
spatializer options.

Test: manual & atest BluetoothDetailsSpatialAudioControllerTest
Bug: 297265575
Change-Id: I7f044d40afadec6ccf659ae4969c0d6e2dc8c79e
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsAudioDeviceTypeController.java b/src/com/android/settings/bluetooth/BluetoothDetailsAudioDeviceTypeController.java
index fe0d141..9571767 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsAudioDeviceTypeController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsAudioDeviceTypeController.java
@@ -109,6 +109,7 @@
                         mAudioManager.setBluetoothAudioDeviceCategory(mCachedDevice.getAddress(),
                                 mCachedDevice.getDevice().getType() == DEVICE_TYPE_LE,
                                 Integer.parseInt(value));
+                        mCachedDevice.onAudioDeviceCategoryChanged();
                     }
                 }
                 return true;
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
index bec6b03..c431cee 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
@@ -53,9 +53,7 @@
     @VisibleForTesting
     PreferenceCategory mProfilesContainer;
     @VisibleForTesting
-    AudioDeviceAttributes mAudioDevice;
-
-    private boolean mIsAvailable;
+    AudioDeviceAttributes mAudioDevice = null;
 
     public BluetoothDetailsSpatialAudioController(
             Context context,
@@ -65,13 +63,11 @@
         super(context, fragment, device, lifecycle);
         AudioManager audioManager = context.getSystemService(AudioManager.class);
         mSpatializer = audioManager.getSpatializer();
-        getAvailableDevice();
-
     }
 
     @Override
     public boolean isAvailable() {
-        return mIsAvailable;
+        return mSpatializer.getImmersiveAudioLevel() != SPATIALIZER_IMMERSIVE_LEVEL_NONE;
     }
 
     @Override
@@ -79,15 +75,11 @@
         SwitchPreference switchPreference = (SwitchPreference) preference;
         String key = switchPreference.getKey();
         if (TextUtils.equals(key, KEY_SPATIAL_AUDIO)) {
-            if (switchPreference.isChecked()) {
-                mSpatializer.addCompatibleAudioDevice(mAudioDevice);
-            } else {
-                mSpatializer.removeCompatibleAudioDevice(mAudioDevice);
-            }
-            refresh();
+            updateSpatializerEnabled(switchPreference.isChecked());
+            refreshSpatialAudioEnabled(switchPreference);
             return true;
         } else if (TextUtils.equals(key, KEY_HEAD_TRACKING)) {
-            mSpatializer.setHeadTrackerEnabled(switchPreference.isChecked(), mAudioDevice);
+            updateSpatializerHeadTracking(switchPreference.isChecked());
             return true;
         } else {
             Log.w(TAG, "invalid key name.");
@@ -95,6 +87,26 @@
         }
     }
 
+    private void updateSpatializerEnabled(boolean enabled)  {
+        if (mAudioDevice == null) {
+            Log.w(TAG, "cannot update spatializer enabled for null audio device.");
+            return;
+        }
+        if (enabled) {
+            mSpatializer.addCompatibleAudioDevice(mAudioDevice);
+        } else {
+            mSpatializer.removeCompatibleAudioDevice(mAudioDevice);
+        }
+    }
+
+    private void updateSpatializerHeadTracking(boolean enabled)  {
+        if (mAudioDevice == null) {
+            Log.w(TAG, "cannot update spatializer head tracking for null audio device.");
+            return;
+        }
+        mSpatializer.setHeadTrackerEnabled(enabled, mAudioDevice);
+    }
+
     @Override
     public String getPreferenceKey() {
         return KEY_SPATIAL_AUDIO_GROUP;
@@ -109,15 +121,30 @@
     @Override
     protected void refresh() {
         if (mAudioDevice == null) {
-            return;
+            getAvailableDevice();
         }
 
         SwitchPreference spatialAudioPref = mProfilesContainer.findPreference(KEY_SPATIAL_AUDIO);
-        if (spatialAudioPref == null) {
+        if (spatialAudioPref == null && mAudioDevice != null) {
             spatialAudioPref = createSpatialAudioPreference(mProfilesContainer.getContext());
             mProfilesContainer.addPreference(spatialAudioPref);
+        } else if (mAudioDevice == null || !mSpatializer.isAvailableForDevice(mAudioDevice)) {
+            if (spatialAudioPref != null) {
+                mProfilesContainer.removePreference(spatialAudioPref);
+            }
+            final SwitchPreference headTrackingPref =
+                    mProfilesContainer.findPreference(KEY_HEAD_TRACKING);
+            if (headTrackingPref != null) {
+                mProfilesContainer.removePreference(headTrackingPref);
+            }
+            mAudioDevice = null;
+            return;
         }
 
+        refreshSpatialAudioEnabled(spatialAudioPref);
+    }
+
+    private void refreshSpatialAudioEnabled(SwitchPreference spatialAudioPref) {
         boolean isSpatialAudioOn = mSpatializer.getCompatibleAudioDevices().contains(mAudioDevice);
         Log.d(TAG, "refresh() isSpatialAudioOn : " + isSpatialAudioOn);
         spatialAudioPref.setChecked(isSpatialAudioOn);
@@ -127,9 +154,13 @@
             headTrackingPref = createHeadTrackingPreference(mProfilesContainer.getContext());
             mProfilesContainer.addPreference(headTrackingPref);
         }
+        refreshHeadTracking(spatialAudioPref, headTrackingPref);
+    }
 
+    private void refreshHeadTracking(SwitchPreference spatialAudioPref,
+                                     SwitchPreference headTrackingPref) {
         boolean isHeadTrackingAvailable =
-                isSpatialAudioOn && mSpatializer.hasHeadTracker(mAudioDevice);
+                spatialAudioPref.isChecked() && mSpatializer.hasHeadTracker(mAudioDevice);
         Log.d(TAG, "refresh() has head tracker : " + mSpatializer.hasHeadTracker(mAudioDevice));
         headTrackingPref.setVisible(isHeadTrackingAvailable);
         if (isHeadTrackingAvailable) {
@@ -158,14 +189,6 @@
     }
 
     private void getAvailableDevice() {
-        if (mSpatializer.getImmersiveAudioLevel() == SPATIALIZER_IMMERSIVE_LEVEL_NONE) {
-            mIsAvailable = false;
-            mAudioDevice = null;
-            Log.d(TAG, "getAvailableDevice() ignored: spatializer not supported");
-
-            return;
-        }
-
         AudioDeviceAttributes a2dpDevice = new AudioDeviceAttributes(
                 AudioDeviceAttributes.ROLE_OUTPUT,
                 AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
@@ -187,7 +210,6 @@
                 AudioDeviceInfo.TYPE_HEARING_AID,
                 mCachedDevice.getAddress());
 
-        mIsAvailable = true;
         if (mSpatializer.isAvailableForDevice(bleHeadsetDevice)) {
             mAudioDevice = bleHeadsetDevice;
         } else if (mSpatializer.isAvailableForDevice(bleSpeakerDevice)) {
@@ -196,20 +218,20 @@
             mAudioDevice = bleBroadcastDevice;
         } else if (mSpatializer.isAvailableForDevice(a2dpDevice)) {
             mAudioDevice = a2dpDevice;
-        } else {
-            mIsAvailable = mSpatializer.isAvailableForDevice(hearingAidDevice);
+        } else if (mSpatializer.isAvailableForDevice(hearingAidDevice)) {
             mAudioDevice = hearingAidDevice;
+        } else {
+            mAudioDevice = null;
         }
 
         Log.d(TAG, "getAvailableDevice() device : "
                 + mCachedDevice.getDevice().getAnonymizedAddress()
-                + ", type : " + mAudioDevice.getType()
-                + ", is available : " + mIsAvailable);
+                + ", is available : " + (mAudioDevice != null)
+                + ", type : " + (mAudioDevice == null ? "no type" : mAudioDevice.getType()));
     }
 
     @VisibleForTesting
     void setAvailableDevice(AudioDeviceAttributes audioDevice) {
         mAudioDevice = audioDevice;
-        mIsAvailable = mSpatializer.isAvailableForDevice(audioDevice);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
index 1f0adcf..ce5631f 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.bluetooth;
 
+import static android.media.Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL;
+import static android.media.Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.spy;
@@ -62,6 +65,8 @@
     @Mock
     private BluetoothDevice mBluetoothDevice;
 
+    private AudioDeviceAttributes mAvailableDevice;
+
     private BluetoothDetailsSpatialAudioController mController;
     private SwitchPreference mSpatialAudioPref;
     private SwitchPreference mHeadTrackingPref;
@@ -86,94 +91,32 @@
 
         when(mProfilesContainer.findPreference(KEY_SPATIAL_AUDIO)).thenReturn(mSpatialAudioPref);
         when(mProfilesContainer.findPreference(KEY_HEAD_TRACKING)).thenReturn(mHeadTrackingPref);
-    }
 
-    @Test
-    public void isAvailable_spatialAudioSupportA2dpDevice_returnsTrue() {
-        AudioDeviceAttributes a2dpDevice = new AudioDeviceAttributes(
+        mAvailableDevice = new AudioDeviceAttributes(
                 AudioDeviceAttributes.ROLE_OUTPUT,
                 AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
                 MAC_ADDRESS);
-        when(mSpatializer.isAvailableForDevice(a2dpDevice)).thenReturn(true);
-
-        mController.setAvailableDevice(a2dpDevice);
-
-        assertThat(mController.isAvailable()).isTrue();
-        assertThat(mController.mAudioDevice.getType())
-                .isEqualTo(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP);
     }
 
     @Test
-    public void isAvailable_spatialAudioSupportBleHeadsetDevice_returnsTrue() {
-        AudioDeviceAttributes bleHeadsetDevice = new AudioDeviceAttributes(
-                AudioDeviceAttributes.ROLE_OUTPUT,
-                AudioDeviceInfo.TYPE_BLE_HEADSET,
-                MAC_ADDRESS);
-        when(mSpatializer.isAvailableForDevice(bleHeadsetDevice)).thenReturn(true);
-
-        mController.setAvailableDevice(bleHeadsetDevice);
-
-        assertThat(mController.isAvailable()).isTrue();
-        assertThat(mController.mAudioDevice.getType())
-                .isEqualTo(AudioDeviceInfo.TYPE_BLE_HEADSET);
-    }
-
-    @Test
-    public void isAvailable_spatialAudioSupportBleSpeakerDevice_returnsTrue() {
-        AudioDeviceAttributes bleSpeakerDevice = new AudioDeviceAttributes(
-                AudioDeviceAttributes.ROLE_OUTPUT,
-                AudioDeviceInfo.TYPE_BLE_SPEAKER,
-                MAC_ADDRESS);
-        when(mSpatializer.isAvailableForDevice(bleSpeakerDevice)).thenReturn(true);
-
-        mController.setAvailableDevice(bleSpeakerDevice);
-
-        assertThat(mController.isAvailable()).isTrue();
-        assertThat(mController.mAudioDevice.getType())
-                .isEqualTo(AudioDeviceInfo.TYPE_BLE_SPEAKER);
-    }
-
-    @Test
-    public void isAvailable_spatialAudioSupportBleBroadcastDevice_returnsTrue() {
-        AudioDeviceAttributes bleBroadcastDevice = new AudioDeviceAttributes(
-                AudioDeviceAttributes.ROLE_OUTPUT,
-                AudioDeviceInfo.TYPE_BLE_BROADCAST,
-                MAC_ADDRESS);
-        when(mSpatializer.isAvailableForDevice(bleBroadcastDevice)).thenReturn(true);
-
-        mController.setAvailableDevice(bleBroadcastDevice);
-
-        assertThat(mController.isAvailable()).isTrue();
-        assertThat(mController.mAudioDevice.getType())
-                .isEqualTo(AudioDeviceInfo.TYPE_BLE_BROADCAST);
-    }
-
-    @Test
-    public void isAvailable_spatialAudioSupportHearingAidDevice_returnsTrue() {
-        AudioDeviceAttributes hearingAidDevice = new AudioDeviceAttributes(
-                AudioDeviceAttributes.ROLE_OUTPUT,
-                AudioDeviceInfo.TYPE_HEARING_AID,
-                MAC_ADDRESS);
-        when(mSpatializer.isAvailableForDevice(hearingAidDevice)).thenReturn(true);
-
-        mController.setAvailableDevice(hearingAidDevice);
-
-        assertThat(mController.isAvailable()).isTrue();
-        assertThat(mController.mAudioDevice.getType())
-                .isEqualTo(AudioDeviceInfo.TYPE_HEARING_AID);
-    }
-
-    @Test
-    public void isAvailable_spatialAudioNotSupported_returnsFalse() {
+    public void isAvailable_forSpatializerWithLevelNone_returnsFalse() {
+        when(mSpatializer.getImmersiveAudioLevel()).thenReturn(SPATIALIZER_IMMERSIVE_LEVEL_NONE);
         assertThat(mController.isAvailable()).isFalse();
-        assertThat(mController.mAudioDevice.getType())
-                .isEqualTo(AudioDeviceInfo.TYPE_HEARING_AID);
+    }
+
+    @Test
+    public void isAvailable_forSpatializerWithLevelNotNone_returnsTrue() {
+        when(mSpatializer.getImmersiveAudioLevel()).thenReturn(
+                SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL);
+        assertThat(mController.isAvailable()).isTrue();
     }
 
     @Test
     public void refresh_spatialAudioIsTurnedOn_checksSpatialAudioPreference() {
         List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
+        mController.setAvailableDevice(mAvailableDevice);
         compatibleAudioDevices.add(mController.mAudioDevice);
+        when(mSpatializer.isAvailableForDevice(mController.mAudioDevice)).thenReturn(true);
         when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
 
         mController.refresh();
@@ -207,13 +150,14 @@
     public void
             refresh_spatialAudioOnAndHeadTrackingIsNotAvailable_hidesHeadTrackingPreference() {
         List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
+        mController.setAvailableDevice(mAvailableDevice);
         compatibleAudioDevices.add(mController.mAudioDevice);
         when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
         when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(false);
 
         mController.refresh();
 
-        assertThat(mHeadTrackingPref.isVisible()).isFalse();
+        verify(mProfilesContainer).removePreference(mHeadTrackingPref);
     }
 
     @Test
@@ -223,14 +167,16 @@
 
         mController.refresh();
 
-        assertThat(mHeadTrackingPref.isVisible()).isFalse();
+        verify(mProfilesContainer).removePreference(mHeadTrackingPref);
     }
 
     @Test
     public void refresh_headTrackingIsTurnedOn_checksHeadTrackingPreference() {
         List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
+        mController.setAvailableDevice(mAvailableDevice);
         compatibleAudioDevices.add(mController.mAudioDevice);
         when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
+        when(mSpatializer.isAvailableForDevice(mController.mAudioDevice)).thenReturn(true);
         when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(true);
         when(mSpatializer.isHeadTrackerEnabled(mController.mAudioDevice)).thenReturn(true);
 
@@ -242,8 +188,10 @@
     @Test
     public void refresh_headTrackingIsTurnedOff_unchecksHeadTrackingPreference() {
         List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
+        mController.setAvailableDevice(mAvailableDevice);
         compatibleAudioDevices.add(mController.mAudioDevice);
         when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
+        when(mSpatializer.isAvailableForDevice(mController.mAudioDevice)).thenReturn(true);
         when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(true);
         when(mSpatializer.isHeadTrackerEnabled(mController.mAudioDevice)).thenReturn(false);
 
@@ -254,6 +202,7 @@
 
     @Test
     public void turnedOnSpatialAudio_invokesAddCompatibleAudioDevice() {
+        mController.setAvailableDevice(mAvailableDevice);
         mSpatialAudioPref.setChecked(true);
         mController.onPreferenceClick(mSpatialAudioPref);
         verify(mSpatializer).addCompatibleAudioDevice(mController.mAudioDevice);
@@ -261,6 +210,7 @@
 
     @Test
     public void turnedOffSpatialAudio_invokesRemoveCompatibleAudioDevice() {
+        mController.setAvailableDevice(mAvailableDevice);
         mSpatialAudioPref.setChecked(false);
         mController.onPreferenceClick(mSpatialAudioPref);
         verify(mSpatializer).removeCompatibleAudioDevice(mController.mAudioDevice);
@@ -268,6 +218,7 @@
 
     @Test
     public void turnedOnHeadTracking_invokesSetHeadTrackerEnabled_setsTrue() {
+        mController.setAvailableDevice(mAvailableDevice);
         mHeadTrackingPref.setChecked(true);
         mController.onPreferenceClick(mHeadTrackingPref);
         verify(mSpatializer).setHeadTrackerEnabled(true, mController.mAudioDevice);
@@ -275,6 +226,7 @@
 
     @Test
     public void turnedOffHeadTracking_invokesSetHeadTrackerEnabled_setsFalse() {
+        mController.setAvailableDevice(mAvailableDevice);
         mHeadTrackingPref.setChecked(false);
         mController.onPreferenceClick(mHeadTrackingPref);
         verify(mSpatializer).setHeadTrackerEnabled(false, mController.mAudioDevice);