diff options
author | 2024-09-17 00:40:37 +0000 | |
---|---|---|
committer | 2024-09-17 00:40:37 +0000 | |
commit | 5ef5012f612019856551f1075734f524f143bbc0 (patch) | |
tree | ab111b52e2751f1ffb006cdd14d203505a14860f | |
parent | 5a7e18f09d20b1ea1215e5681b1ff9e6f1a317ce (diff) |
Hook InputRouteManager into AudioSwitchController
AudioSwitchController listens to input devices changes, maintains a list
of input devices and displays them when the input routing flag is on.
Change-Id: Iebf9c50cbcb60fb9361d3f9a45016e12032326dc
Bug: b/355684672, b/357122624
Test atest AudioSwitchControllerTest
Flag: com.android.media.flags.enable_audio_input_device_routing_and_volume_control
4 files changed, 138 insertions, 14 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java index 548eb3fd4b8f..874e03012ae2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java @@ -57,7 +57,7 @@ public final class InputRouteManager { } }; - /* package */ InputRouteManager(@NonNull Context context, @NonNull AudioManager audioManager) { + public InputRouteManager(@NonNull Context context, @NonNull AudioManager audioManager) { mContext = context; mAudioManager = audioManager; Handler handler = new Handler(context.getMainLooper()); diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index f9c2aef5f070..ba3822bd3c23 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -3114,6 +3114,10 @@ <string name="media_output_group_title_speakers_and_displays">Speakers & Displays</string> <!-- Title for Suggested Devices group. [CHAR LIMIT=NONE] --> <string name="media_output_group_title_suggested_device">Suggested Devices</string> + <!-- Title for input device group. [CHAR LIMIT=NONE] --> + <string name="media_input_group_title">Input</string> + <!-- Title for output device group. [CHAR LIMIT=NONE] --> + <string name="media_output_group_title">Output</string> <!-- Summary for end session dialog. [CHAR LIMIT=NONE] --> <string name="media_output_end_session_dialog_summary">Stop your shared session to move media to another device</string> <!-- Button text for stopping session [CHAR LIMIT=60] --> diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java index 72582a98a177..2cbc75755cfd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java @@ -77,6 +77,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastMetadata; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.media.InfoMediaManager; +import com.android.settingslib.media.InputRouteManager; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.settingslib.media.flags.Flags; @@ -138,10 +139,12 @@ public class MediaSwitchingController private final DialogTransitionAnimator mDialogTransitionAnimator; private final CommonNotifCollection mNotifCollection; protected final Object mMediaDevicesLock = new Object(); + protected final Object mInputMediaDevicesLock = new Object(); @VisibleForTesting final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>(); final List<MediaDevice> mCachedMediaDevices = new CopyOnWriteArrayList<>(); - private final List<MediaItem> mMediaItemList = new CopyOnWriteArrayList<>(); + private final List<MediaItem> mOutputMediaItemList = new CopyOnWriteArrayList<>(); + private final List<MediaItem> mInputMediaItemList = new CopyOnWriteArrayList<>(); private final AudioManager mAudioManager; private final PowerExemptionManager mPowerExemptionManager; private final KeyguardManager mKeyGuardManager; @@ -154,6 +157,7 @@ public class MediaSwitchingController @VisibleForTesting boolean mNeedRefresh = false; private MediaController mMediaController; + private InputRouteManager mInputRouteManager; @VisibleForTesting Callback mCallback; @VisibleForTesting @@ -182,6 +186,18 @@ public class MediaSwitchingController ACTION_BROADCAST_INFO_ICON } + @VisibleForTesting + final InputRouteManager.InputDeviceCallback mInputDeviceCallback = + new InputRouteManager.InputDeviceCallback() { + @Override + public void onInputDeviceListUpdated(@NonNull List<MediaDevice> devices) { + synchronized (mInputMediaDevicesLock) { + buildInputMediaItems(devices); + mCallback.onDeviceListChanged(); + } + } + }; + @AssistedInject public MediaSwitchingController( Context context, @@ -242,6 +258,10 @@ public class MediaSwitchingController R.dimen.media_output_dialog_default_margin_end); mItemMarginEndSelectable = (int) mContext.getResources().getDimension( R.dimen.media_output_dialog_selectable_margin_end); + + if (enableInputRouting()) { + mInputRouteManager = new InputRouteManager(mContext, audioManager); + } } @AssistedFactory @@ -254,7 +274,7 @@ public class MediaSwitchingController protected void start(@NonNull Callback cb) { synchronized (mMediaDevicesLock) { mCachedMediaDevices.clear(); - mMediaItemList.clear(); + mOutputMediaItemList.clear(); } mNearbyDeviceInfoMap.clear(); if (mNearbyMediaDevicesManager != null) { @@ -278,6 +298,10 @@ public class MediaSwitchingController mCallback = cb; mLocalMediaManager.registerCallback(this); mLocalMediaManager.startScan(); + + if (enableInputRouting()) { + mInputRouteManager.registerCallback(mInputDeviceCallback); + } } boolean shouldShowLaunchSection() { @@ -301,12 +325,19 @@ public class MediaSwitchingController mLocalMediaManager.stopScan(); synchronized (mMediaDevicesLock) { mCachedMediaDevices.clear(); - mMediaItemList.clear(); + mOutputMediaItemList.clear(); } if (mNearbyMediaDevicesManager != null) { mNearbyMediaDevicesManager.unregisterNearbyDevicesCallback(this); } mNearbyDeviceInfoMap.clear(); + + if (enableInputRouting()) { + mInputRouteManager.unregisterCallback(mInputDeviceCallback); + synchronized (mInputMediaDevicesLock) { + mInputMediaItemList.clear(); + } + } } private MediaController getMediaController() { @@ -336,7 +367,7 @@ public class MediaSwitchingController @Override public void onDeviceListUpdate(List<MediaDevice> devices) { - boolean isListEmpty = mMediaItemList.isEmpty(); + boolean isListEmpty = mOutputMediaItemList.isEmpty(); if (isListEmpty || !mIsRefreshing) { buildMediaItems(devices); mCallback.onDeviceListChanged(); @@ -353,7 +384,8 @@ public class MediaSwitchingController public void onSelectedDeviceStateChanged(MediaDevice device, @LocalMediaManager.MediaDeviceState int state) { mCallback.onRouteChanged(); - mMetricLogger.logOutputItemSuccess(device.toString(), new ArrayList<>(mMediaItemList)); + mMetricLogger.logOutputItemSuccess( + device.toString(), new ArrayList<>(mOutputMediaItemList)); } @Override @@ -364,7 +396,7 @@ public class MediaSwitchingController @Override public void onRequestFailed(int reason) { mCallback.onRouteChanged(); - mMetricLogger.logOutputItemFailure(new ArrayList<>(mMediaItemList), reason); + mMetricLogger.logOutputItemFailure(new ArrayList<>(mOutputMediaItemList), reason); } /** @@ -383,7 +415,7 @@ public class MediaSwitchingController } try { synchronized (mMediaDevicesLock) { - mMediaItemList.removeIf((MediaItem::isMutingExpectedDevice)); + mOutputMediaItemList.removeIf((MediaItem::isMutingExpectedDevice)); } mAudioManager.cancelMuteAwaitConnection(mAudioManager.getMutingExpectedDevice()); } catch (Exception e) { @@ -639,9 +671,9 @@ public class MediaSwitchingController private void buildMediaItems(List<MediaDevice> devices) { synchronized (mMediaDevicesLock) { - List<MediaItem> updatedMediaItems = buildMediaItems(mMediaItemList, devices); - mMediaItemList.clear(); - mMediaItemList.addAll(updatedMediaItems); + List<MediaItem> updatedMediaItems = buildMediaItems(mOutputMediaItemList, devices); + mOutputMediaItemList.clear(); + mOutputMediaItemList.addAll(updatedMediaItems); } } @@ -715,6 +747,19 @@ public class MediaSwitchingController } } + private boolean enableInputRouting() { + return com.android.media.flags.Flags.enableAudioInputDeviceRoutingAndVolumeControl(); + } + + private void buildInputMediaItems(List<MediaDevice> devices) { + synchronized (mInputMediaDevicesLock) { + List<MediaItem> updatedInputMediaItems = + devices.stream().map(MediaItem::createDeviceMediaItem).toList(); + mInputMediaItemList.clear(); + mInputMediaItemList.addAll(updatedInputMediaItems); + } + } + /** * Initial categorization of current devices, will not be called for updates to the devices * list. @@ -779,7 +824,6 @@ public class MediaSwitchingController mediaDevice.setRangeZone(mNearbyDeviceInfoMap.get(mediaDevice.getId())); } } - } boolean isCurrentConnectedDeviceRemote() { @@ -838,8 +882,31 @@ public class MediaSwitchingController }); } + private void addInputDevices(List<MediaItem> mediaItems) { + mediaItems.add( + MediaItem.createGroupDividerMediaItem( + mContext.getString(R.string.media_input_group_title))); + mediaItems.addAll(mInputMediaItemList); + } + + private void addOutputDevices(List<MediaItem> mediaItems) { + mediaItems.add( + MediaItem.createGroupDividerMediaItem( + mContext.getString(R.string.media_output_group_title))); + mediaItems.addAll(mOutputMediaItemList); + } + public List<MediaItem> getMediaItemList() { - return mMediaItemList; + // If input routing is not enabled, only return output media items. + if (!enableInputRouting()) { + return mOutputMediaItemList; + } + + // If input routing is enabled, return both output and input media items. + List<MediaItem> mediaItems = new ArrayList<>(); + addOutputDevices(mediaItems); + addInputDevices(mediaItems); + return mediaItems; } public MediaDevice getCurrentConnectedMediaDevice() { @@ -922,7 +989,7 @@ public class MediaSwitchingController public boolean isAnyDeviceTransferring() { synchronized (mMediaDevicesLock) { - for (MediaItem mediaItem : mMediaItemList) { + for (MediaItem mediaItem : mOutputMediaItemList) { if (mediaItem.getMediaDevice().isPresent() && mediaItem.getMediaDevice().get().getState() == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java index 4a54a9077508..d3e20c6e39b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java @@ -43,6 +43,7 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.media.AudioDeviceAttributes; +import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.MediaDescription; import android.media.MediaMetadata; @@ -58,6 +59,7 @@ import android.os.Bundle; import android.os.PowerExemptionManager; import android.os.RemoteException; import android.os.UserHandle; +import android.platform.test.annotations.EnableFlags; import android.service.notification.StatusBarNotification; import android.testing.TestableLooper; import android.text.TextUtils; @@ -67,8 +69,10 @@ import androidx.core.graphics.drawable.IconCompat; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.media.flags.Flags; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.media.InputMediaDevice; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.systemui.SysuiTestCase; @@ -530,6 +534,55 @@ public class MediaSwitchingControllerTest extends SysuiTestCase { verify(mCb).onDeviceListChanged(); } + @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @Test + public void onInputDeviceListUpdate_verifyDeviceListCallback() { + AudioDeviceInfo[] audioDeviceInfos = {}; + when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) + .thenReturn(audioDeviceInfos); + mMediaSwitchingController.start(mCb); + + // Output devices have changed. + mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); + + final int MAX_VOLUME = 1; + final int CURRENT_VOLUME = 0; + final boolean IS_VOLUME_FIXED = true; + final MediaDevice mediaDevice3 = + InputMediaDevice.create( + mContext, + TEST_DEVICE_3_ID, + AudioDeviceInfo.TYPE_BUILTIN_MIC, + MAX_VOLUME, + CURRENT_VOLUME, + IS_VOLUME_FIXED); + final MediaDevice mediaDevice4 = + InputMediaDevice.create( + mContext, + TEST_DEVICE_4_ID, + AudioDeviceInfo.TYPE_WIRED_HEADSET, + MAX_VOLUME, + CURRENT_VOLUME, + IS_VOLUME_FIXED); + final List<MediaDevice> inputDevices = new ArrayList<>(); + inputDevices.add(mediaDevice3); + inputDevices.add(mediaDevice4); + + // Input devices have changed. + mMediaSwitchingController.mInputDeviceCallback.onInputDeviceListUpdated(inputDevices); + + final List<MediaDevice> devices = new ArrayList<>(); + for (MediaItem item : mMediaSwitchingController.getMediaItemList()) { + if (item.getMediaDevice().isPresent()) { + devices.add(item.getMediaDevice().get()); + } + } + + assertThat(devices).containsAtLeastElementsIn(mMediaDevices); + assertThat(devices).hasSize(mMediaDevices.size() + inputDevices.size()); + verify(mCb, atLeastOnce()).onDeviceListChanged(); + } + @Test public void advanced_categorizeMediaItems_withSuggestedDevice_verifyDeviceListSize() { when(mMediaDevice1.isSuggestedDevice()).thenReturn(true); |