Loading packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +74 −5 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.systemui.volume; import static android.media.AudioManager.RINGER_MODE_NORMAL; import static com.android.settingslib.flags.Flags.volumeDialogAudioSharingFix; import android.app.ActivityManager; import android.app.KeyguardManager; import android.app.NotificationManager; Loading Loading @@ -59,6 +61,8 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.CaptioningManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.Observer; import com.android.internal.annotations.GuardedBy; Loading @@ -76,6 +80,8 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.RingerModeLiveData; import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.concurrency.ThreadFactory; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.volume.domain.interactor.AudioSharingInteractor; import dalvik.annotation.optimization.NeverCompile; Loading @@ -102,7 +108,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int TOUCH_FEEDBACK_TIMEOUT_MS = 1000; private static final int DYNAMIC_STREAM_START_INDEX = 100; // We only need one dynamic stream for broadcast because at most two headsets are allowed // to join local broadcast in current stage. // It is safe to use 99 as the broadcast stream now. There are only 10+ default audio // streams defined in AudioSystem for now and audio team is in the middle of restructure, // no new default stream is preferred. @VisibleForTesting static final int DYNAMIC_STREAM_BROADCAST = 99; private static final int DYNAMIC_STREAM_REMOTE_START_INDEX = 100; private static final AudioAttributes SONIFICIATION_VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) Loading Loading @@ -145,6 +157,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final State mState = new State(); protected final MediaSessionsCallbacks mMediaSessionsCallbacksW; private final VibratorHelper mVibrator; private final AudioSharingInteractor mAudioSharingInteractor; private final JavaAdapter mJavaAdapter; private final boolean mHasVibrator; private boolean mShowA11yStream; private boolean mShowVolumeDialog; Loading Loading @@ -188,7 +202,9 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa KeyguardManager keyguardManager, ActivityManager activityManager, UserTracker userTracker, DumpManager dumpManager DumpManager dumpManager, AudioSharingInteractor audioSharingInteractor, JavaAdapter javaAdapter ) { mContext = context.getApplicationContext(); mPackageManager = packageManager; Loading @@ -200,6 +216,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mRouter2Manager = MediaRouter2Manager.getInstance(mContext); mMediaSessionsCallbacksW = new MediaSessionsCallbacks(mContext); mMediaSessions = createMediaSessions(mContext, mWorkerLooper, mMediaSessionsCallbacksW); mAudioSharingInteractor = audioSharingInteractor; mJavaAdapter = javaAdapter; mAudio = audioManager; mNoMan = notificationManager; mObserver = new SettingObserver(mWorker); Loading Loading @@ -272,6 +290,12 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } catch (SecurityException e) { Log.w(TAG, "No access to media sessions", e); } if (volumeDialogAudioSharingFix()) { Slog.d(TAG, "Start collect volume changes in audio sharing"); mJavaAdapter.alwaysCollectFlow( mAudioSharingInteractor.getVolume(), this::handleAudioSharingStreamVolumeChanges); } } public void setVolumePolicy(VolumePolicy policy) { Loading Loading @@ -545,7 +569,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mState.activeStream = activeStream; Events.writeEvent(Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream); if (D.BUG) Log.d(TAG, "updateActiveStreamW " + activeStream); final int s = activeStream < DYNAMIC_STREAM_START_INDEX ? activeStream : -1; final int s = activeStream < (volumeDialogAudioSharingFix() ? DYNAMIC_STREAM_BROADCAST : DYNAMIC_STREAM_REMOTE_START_INDEX) ? activeStream : -1; if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s); mAudio.forceVolumeControlStream(s); return true; Loading Loading @@ -726,7 +756,12 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private void onSetStreamVolumeW(int stream, int level) { if (D.BUG) Log.d(TAG, "onSetStreamVolume " + stream + " level=" + level); if (stream >= DYNAMIC_STREAM_START_INDEX) { if (volumeDialogAudioSharingFix() && stream == DYNAMIC_STREAM_BROADCAST) { Slog.d(TAG, "onSetStreamVolumeW set broadcast stream level = " + level); mAudioSharingInteractor.setStreamVolume(level); return; } if (stream >= DYNAMIC_STREAM_REMOTE_START_INDEX) { mMediaSessionsCallbacksW.setStreamVolume(stream, level); return; } Loading Loading @@ -758,6 +793,40 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa DndTile.setVisible(mContext, true); } void handleAudioSharingStreamVolumeChanges(@Nullable Integer volume) { if (volume == null) { if (mState.states.contains(DYNAMIC_STREAM_BROADCAST)) { mState.states.remove(DYNAMIC_STREAM_BROADCAST); Slog.d(TAG, "Remove audio sharing stream"); mCallbacks.onStateChanged(mState); } } else { if (mState.states.contains(DYNAMIC_STREAM_BROADCAST)) { StreamState ss = mState.states.get(DYNAMIC_STREAM_BROADCAST); if (ss.level != volume) { ss.level = volume; Slog.d(TAG, "updateState, audio sharing stream volume = " + volume); mCallbacks.onStateChanged(mState); } } else { StreamState ss = streamStateW(DYNAMIC_STREAM_BROADCAST); ss.dynamic = true; ss.levelMin = mAudioSharingInteractor.getVolumeMin(); ss.levelMax = mAudioSharingInteractor.getVolumeMax(); if (ss.level != volume) { ss.level = volume; } String label = mContext.getString(R.string.audio_sharing_description); if (!Objects.equals(ss.remoteLabel, label)) { ss.name = -1; ss.remoteLabel = label; } Slog.d(TAG, "updateState, new audio sharing stream volume = " + volume); mCallbacks.onStateChanged(mState); } } } private final class VC extends IVolumeController.Stub { private final String TAG = VolumeDialogControllerImpl.TAG + ".VC"; Loading Loading @@ -1256,7 +1325,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa protected final class MediaSessionsCallbacks implements MediaSessions.Callbacks { private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>(); private int mNextStream = DYNAMIC_STREAM_START_INDEX; private int mNextStream = DYNAMIC_STREAM_REMOTE_START_INDEX; private final boolean mVolumeAdjustmentForRemoteGroupSessions; public MediaSessionsCallbacks(Context context) { Loading packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +28 −4 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.internal.jank.InteractionJankMonitor.CUJ_VOLUME_CONTROL; import static com.android.internal.jank.InteractionJankMonitor.Configuration.Builder; import static com.android.settingslib.flags.Flags.volumeDialogAudioSharingFix; import static com.android.systemui.Flags.hapticVolumeSlider; import static com.android.systemui.volume.Events.DISMISS_REASON_POSTURE_CHANGED; import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED; Loading Loading @@ -1678,6 +1679,14 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, return true; } // Always show the stream for audio sharing if it exists. if (volumeDialogAudioSharingFix() && row.ss != null && mContext.getString(R.string.audio_sharing_description) .equals(row.ss.remoteLabel)) { return true; } if (row.defaultStream) { return activeRow.stream == STREAM_RING || activeRow.stream == STREAM_ALARM Loading Loading @@ -1880,10 +1889,25 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, if (!ss.dynamic) continue; mDynamic.put(stream, true); if (findRow(stream) == null) { addRow(stream, if (volumeDialogAudioSharingFix() && mContext.getString(R.string.audio_sharing_description) .equals(ss.remoteLabel)) { addRow( stream, R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, false, true); } else { addRow( stream, com.android.settingslib.R.drawable.ic_volume_remote, com.android.settingslib.R.drawable.ic_volume_remote_mute, true, false, true); true, false, true); } } } Loading packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +88 −10 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.systemui.volume; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; Loading @@ -37,16 +39,19 @@ import android.media.IAudioService; import android.media.session.MediaSession; import android.os.Handler; import android.os.Process; import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper; import android.view.accessibility.AccessibilityManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.flags.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.RingerModeLiveData; Loading @@ -54,7 +59,9 @@ import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.concurrency.FakeThreadFactory; import com.android.systemui.util.concurrency.ThreadFactory; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.volume.domain.interactor.AudioSharingInteractor; import org.junit.Before; import org.junit.Test; Loading @@ -63,6 +70,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Objects; import java.util.concurrent.Executor; @RunWith(AndroidJUnit4.class) Loading Loading @@ -104,6 +112,10 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { private UserTracker mUserTracker; @Mock private DumpManager mDumpManager; @Mock private AudioSharingInteractor mAudioSharingInteractor; @Mock private JavaAdapter mJavaAdapter; @Before Loading @@ -124,11 +136,26 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { mCallback = mock(VolumeDialogControllerImpl.C.class); mThreadFactory.setLooper(TestableLooper.get(this).getLooper()); mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager, mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager, mPackageManager, mWakefullnessLifcycle, mKeyguardManager, mActivityManager, mUserTracker, mDumpManager, mCallback); mVolumeController = new TestableVolumeDialogControllerImpl( mContext, mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager, mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager, mPackageManager, mWakefullnessLifcycle, mKeyguardManager, mActivityManager, mUserTracker, mDumpManager, mCallback, mAudioSharingInteractor, mJavaAdapter); mVolumeController.setEnableDialogs(true, true); } Loading Loading @@ -224,6 +251,41 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { verify(mUserTracker).addCallback(any(UserTracker.Callback.class), any(Executor.class)); } @Test @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX) public void handleAudioSharingStreamVolumeChanges_updateState() { ArgumentCaptor<VolumeDialogController.State> stateCaptor = ArgumentCaptor.forClass(VolumeDialogController.State.class); int broadcastStream = VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST; mVolumeController.handleAudioSharingStreamVolumeChanges(100); verify(mCallback).onStateChanged(stateCaptor.capture()); assertThat(stateCaptor.getValue().states.contains(broadcastStream)).isTrue(); assertThat(stateCaptor.getValue().states.get(broadcastStream).level).isEqualTo(100); mVolumeController.handleAudioSharingStreamVolumeChanges(200); verify(mCallback, times(2)).onStateChanged(stateCaptor.capture()); assertThat(stateCaptor.getValue().states.contains(broadcastStream)).isTrue(); assertThat(stateCaptor.getValue().states.get(broadcastStream).level).isEqualTo(200); mVolumeController.handleAudioSharingStreamVolumeChanges(null); verify(mCallback, times(3)).onStateChanged(stateCaptor.capture()); assertThat(stateCaptor.getValue().states.contains(broadcastStream)).isFalse(); } @Test @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX) public void testSetStreamVolume_setSecondaryDeviceVolume() { mVolumeController.setStreamVolume( VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST, /* level= */ 100); Objects.requireNonNull(TestableLooper.get(this)).processAllMessages(); verify(mAudioSharingInteractor).setStreamVolume(100); } static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl { private final WakefulnessLifecycle.Observer mWakefullessLifecycleObserver; Loading @@ -243,11 +305,27 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { ActivityManager activityManager, UserTracker userTracker, DumpManager dumpManager, C callback) { super(context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager, notificationManager, optionalVibrator, iAudioService, accessibilityManager, packageManager, wakefulnessLifecycle, keyguardManager, activityManager, userTracker, dumpManager); C callback, AudioSharingInteractor audioSharingInteractor, JavaAdapter javaAdapter) { super( context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager, notificationManager, optionalVibrator, iAudioService, accessibilityManager, packageManager, wakefulnessLifecycle, keyguardManager, activityManager, userTracker, dumpManager, audioSharingInteractor, javaAdapter); mCallbacks = callback; ArgumentCaptor<WakefulnessLifecycle.Observer> observerCaptor = Loading packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.media.AudioManager.RINGER_MODE_VIBRATE; import static com.android.systemui.Flags.FLAG_HAPTIC_VOLUME_SLIDER; import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN; import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN; import static com.android.systemui.volume.VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST; import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS; import static junit.framework.Assert.assertEquals; Loading Loading @@ -72,6 +73,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.settingslib.flags.Flags; import com.android.systemui.Prefs; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.AnimatorTestRule; Loading Loading @@ -794,6 +796,38 @@ public class VolumeDialogImplTest extends SysuiTestCase { verify(mVolumeDialogInteractor, atLeastOnce()).onDialogDismissed(); // dismiss by timeout } @Test @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX) public void testDynamicStreamForBroadcast_createRow() { State state = createShellState(); VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState(); ss.dynamic = true; ss.levelMin = 0; ss.levelMax = 255; ss.level = 20; ss.name = -1; ss.remoteLabel = mContext.getString(R.string.audio_sharing_description); state.states.append(DYNAMIC_STREAM_BROADCAST, ss); mDialog.onStateChangedH(state); mTestableLooper.processAllMessages(); ViewGroup volumeDialogRows = mDialog.getDialogView().findViewById(R.id.volume_dialog_rows); assumeNotNull(volumeDialogRows); View broadcastRow = null; final int rowCount = volumeDialogRows.getChildCount(); // we don't make assumptions about the position of the dnd row for (int i = 0; i < rowCount; i++) { View volumeRow = volumeDialogRows.getChildAt(i); if (volumeRow.getId() == DYNAMIC_STREAM_BROADCAST) { broadcastRow = volumeRow; break; } } assertNotNull(broadcastRow); assertEquals(broadcastRow.getVisibility(), View.VISIBLE); } /** * @return true if at least one volume row has the DND icon */ Loading Loading
packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +74 −5 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.systemui.volume; import static android.media.AudioManager.RINGER_MODE_NORMAL; import static com.android.settingslib.flags.Flags.volumeDialogAudioSharingFix; import android.app.ActivityManager; import android.app.KeyguardManager; import android.app.NotificationManager; Loading Loading @@ -59,6 +61,8 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.CaptioningManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.Observer; import com.android.internal.annotations.GuardedBy; Loading @@ -76,6 +80,8 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.RingerModeLiveData; import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.concurrency.ThreadFactory; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.volume.domain.interactor.AudioSharingInteractor; import dalvik.annotation.optimization.NeverCompile; Loading @@ -102,7 +108,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int TOUCH_FEEDBACK_TIMEOUT_MS = 1000; private static final int DYNAMIC_STREAM_START_INDEX = 100; // We only need one dynamic stream for broadcast because at most two headsets are allowed // to join local broadcast in current stage. // It is safe to use 99 as the broadcast stream now. There are only 10+ default audio // streams defined in AudioSystem for now and audio team is in the middle of restructure, // no new default stream is preferred. @VisibleForTesting static final int DYNAMIC_STREAM_BROADCAST = 99; private static final int DYNAMIC_STREAM_REMOTE_START_INDEX = 100; private static final AudioAttributes SONIFICIATION_VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) Loading Loading @@ -145,6 +157,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final State mState = new State(); protected final MediaSessionsCallbacks mMediaSessionsCallbacksW; private final VibratorHelper mVibrator; private final AudioSharingInteractor mAudioSharingInteractor; private final JavaAdapter mJavaAdapter; private final boolean mHasVibrator; private boolean mShowA11yStream; private boolean mShowVolumeDialog; Loading Loading @@ -188,7 +202,9 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa KeyguardManager keyguardManager, ActivityManager activityManager, UserTracker userTracker, DumpManager dumpManager DumpManager dumpManager, AudioSharingInteractor audioSharingInteractor, JavaAdapter javaAdapter ) { mContext = context.getApplicationContext(); mPackageManager = packageManager; Loading @@ -200,6 +216,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mRouter2Manager = MediaRouter2Manager.getInstance(mContext); mMediaSessionsCallbacksW = new MediaSessionsCallbacks(mContext); mMediaSessions = createMediaSessions(mContext, mWorkerLooper, mMediaSessionsCallbacksW); mAudioSharingInteractor = audioSharingInteractor; mJavaAdapter = javaAdapter; mAudio = audioManager; mNoMan = notificationManager; mObserver = new SettingObserver(mWorker); Loading Loading @@ -272,6 +290,12 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } catch (SecurityException e) { Log.w(TAG, "No access to media sessions", e); } if (volumeDialogAudioSharingFix()) { Slog.d(TAG, "Start collect volume changes in audio sharing"); mJavaAdapter.alwaysCollectFlow( mAudioSharingInteractor.getVolume(), this::handleAudioSharingStreamVolumeChanges); } } public void setVolumePolicy(VolumePolicy policy) { Loading Loading @@ -545,7 +569,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mState.activeStream = activeStream; Events.writeEvent(Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream); if (D.BUG) Log.d(TAG, "updateActiveStreamW " + activeStream); final int s = activeStream < DYNAMIC_STREAM_START_INDEX ? activeStream : -1; final int s = activeStream < (volumeDialogAudioSharingFix() ? DYNAMIC_STREAM_BROADCAST : DYNAMIC_STREAM_REMOTE_START_INDEX) ? activeStream : -1; if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s); mAudio.forceVolumeControlStream(s); return true; Loading Loading @@ -726,7 +756,12 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private void onSetStreamVolumeW(int stream, int level) { if (D.BUG) Log.d(TAG, "onSetStreamVolume " + stream + " level=" + level); if (stream >= DYNAMIC_STREAM_START_INDEX) { if (volumeDialogAudioSharingFix() && stream == DYNAMIC_STREAM_BROADCAST) { Slog.d(TAG, "onSetStreamVolumeW set broadcast stream level = " + level); mAudioSharingInteractor.setStreamVolume(level); return; } if (stream >= DYNAMIC_STREAM_REMOTE_START_INDEX) { mMediaSessionsCallbacksW.setStreamVolume(stream, level); return; } Loading Loading @@ -758,6 +793,40 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa DndTile.setVisible(mContext, true); } void handleAudioSharingStreamVolumeChanges(@Nullable Integer volume) { if (volume == null) { if (mState.states.contains(DYNAMIC_STREAM_BROADCAST)) { mState.states.remove(DYNAMIC_STREAM_BROADCAST); Slog.d(TAG, "Remove audio sharing stream"); mCallbacks.onStateChanged(mState); } } else { if (mState.states.contains(DYNAMIC_STREAM_BROADCAST)) { StreamState ss = mState.states.get(DYNAMIC_STREAM_BROADCAST); if (ss.level != volume) { ss.level = volume; Slog.d(TAG, "updateState, audio sharing stream volume = " + volume); mCallbacks.onStateChanged(mState); } } else { StreamState ss = streamStateW(DYNAMIC_STREAM_BROADCAST); ss.dynamic = true; ss.levelMin = mAudioSharingInteractor.getVolumeMin(); ss.levelMax = mAudioSharingInteractor.getVolumeMax(); if (ss.level != volume) { ss.level = volume; } String label = mContext.getString(R.string.audio_sharing_description); if (!Objects.equals(ss.remoteLabel, label)) { ss.name = -1; ss.remoteLabel = label; } Slog.d(TAG, "updateState, new audio sharing stream volume = " + volume); mCallbacks.onStateChanged(mState); } } } private final class VC extends IVolumeController.Stub { private final String TAG = VolumeDialogControllerImpl.TAG + ".VC"; Loading Loading @@ -1256,7 +1325,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa protected final class MediaSessionsCallbacks implements MediaSessions.Callbacks { private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>(); private int mNextStream = DYNAMIC_STREAM_START_INDEX; private int mNextStream = DYNAMIC_STREAM_REMOTE_START_INDEX; private final boolean mVolumeAdjustmentForRemoteGroupSessions; public MediaSessionsCallbacks(Context context) { Loading
packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +28 −4 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.internal.jank.InteractionJankMonitor.CUJ_VOLUME_CONTROL; import static com.android.internal.jank.InteractionJankMonitor.Configuration.Builder; import static com.android.settingslib.flags.Flags.volumeDialogAudioSharingFix; import static com.android.systemui.Flags.hapticVolumeSlider; import static com.android.systemui.volume.Events.DISMISS_REASON_POSTURE_CHANGED; import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED; Loading Loading @@ -1678,6 +1679,14 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, return true; } // Always show the stream for audio sharing if it exists. if (volumeDialogAudioSharingFix() && row.ss != null && mContext.getString(R.string.audio_sharing_description) .equals(row.ss.remoteLabel)) { return true; } if (row.defaultStream) { return activeRow.stream == STREAM_RING || activeRow.stream == STREAM_ALARM Loading Loading @@ -1880,10 +1889,25 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, if (!ss.dynamic) continue; mDynamic.put(stream, true); if (findRow(stream) == null) { addRow(stream, if (volumeDialogAudioSharingFix() && mContext.getString(R.string.audio_sharing_description) .equals(ss.remoteLabel)) { addRow( stream, R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, false, true); } else { addRow( stream, com.android.settingslib.R.drawable.ic_volume_remote, com.android.settingslib.R.drawable.ic_volume_remote_mute, true, false, true); true, false, true); } } } Loading
packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +88 −10 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.systemui.volume; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; Loading @@ -37,16 +39,19 @@ import android.media.IAudioService; import android.media.session.MediaSession; import android.os.Handler; import android.os.Process; import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper; import android.view.accessibility.AccessibilityManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.flags.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.RingerModeLiveData; Loading @@ -54,7 +59,9 @@ import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.concurrency.FakeThreadFactory; import com.android.systemui.util.concurrency.ThreadFactory; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.volume.domain.interactor.AudioSharingInteractor; import org.junit.Before; import org.junit.Test; Loading @@ -63,6 +70,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Objects; import java.util.concurrent.Executor; @RunWith(AndroidJUnit4.class) Loading Loading @@ -104,6 +112,10 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { private UserTracker mUserTracker; @Mock private DumpManager mDumpManager; @Mock private AudioSharingInteractor mAudioSharingInteractor; @Mock private JavaAdapter mJavaAdapter; @Before Loading @@ -124,11 +136,26 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { mCallback = mock(VolumeDialogControllerImpl.C.class); mThreadFactory.setLooper(TestableLooper.get(this).getLooper()); mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager, mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager, mPackageManager, mWakefullnessLifcycle, mKeyguardManager, mActivityManager, mUserTracker, mDumpManager, mCallback); mVolumeController = new TestableVolumeDialogControllerImpl( mContext, mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager, mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager, mPackageManager, mWakefullnessLifcycle, mKeyguardManager, mActivityManager, mUserTracker, mDumpManager, mCallback, mAudioSharingInteractor, mJavaAdapter); mVolumeController.setEnableDialogs(true, true); } Loading Loading @@ -224,6 +251,41 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { verify(mUserTracker).addCallback(any(UserTracker.Callback.class), any(Executor.class)); } @Test @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX) public void handleAudioSharingStreamVolumeChanges_updateState() { ArgumentCaptor<VolumeDialogController.State> stateCaptor = ArgumentCaptor.forClass(VolumeDialogController.State.class); int broadcastStream = VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST; mVolumeController.handleAudioSharingStreamVolumeChanges(100); verify(mCallback).onStateChanged(stateCaptor.capture()); assertThat(stateCaptor.getValue().states.contains(broadcastStream)).isTrue(); assertThat(stateCaptor.getValue().states.get(broadcastStream).level).isEqualTo(100); mVolumeController.handleAudioSharingStreamVolumeChanges(200); verify(mCallback, times(2)).onStateChanged(stateCaptor.capture()); assertThat(stateCaptor.getValue().states.contains(broadcastStream)).isTrue(); assertThat(stateCaptor.getValue().states.get(broadcastStream).level).isEqualTo(200); mVolumeController.handleAudioSharingStreamVolumeChanges(null); verify(mCallback, times(3)).onStateChanged(stateCaptor.capture()); assertThat(stateCaptor.getValue().states.contains(broadcastStream)).isFalse(); } @Test @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX) public void testSetStreamVolume_setSecondaryDeviceVolume() { mVolumeController.setStreamVolume( VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST, /* level= */ 100); Objects.requireNonNull(TestableLooper.get(this)).processAllMessages(); verify(mAudioSharingInteractor).setStreamVolume(100); } static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl { private final WakefulnessLifecycle.Observer mWakefullessLifecycleObserver; Loading @@ -243,11 +305,27 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { ActivityManager activityManager, UserTracker userTracker, DumpManager dumpManager, C callback) { super(context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager, notificationManager, optionalVibrator, iAudioService, accessibilityManager, packageManager, wakefulnessLifecycle, keyguardManager, activityManager, userTracker, dumpManager); C callback, AudioSharingInteractor audioSharingInteractor, JavaAdapter javaAdapter) { super( context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager, notificationManager, optionalVibrator, iAudioService, accessibilityManager, packageManager, wakefulnessLifecycle, keyguardManager, activityManager, userTracker, dumpManager, audioSharingInteractor, javaAdapter); mCallbacks = callback; ArgumentCaptor<WakefulnessLifecycle.Observer> observerCaptor = Loading
packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.media.AudioManager.RINGER_MODE_VIBRATE; import static com.android.systemui.Flags.FLAG_HAPTIC_VOLUME_SLIDER; import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN; import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN; import static com.android.systemui.volume.VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST; import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS; import static junit.framework.Assert.assertEquals; Loading Loading @@ -72,6 +73,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.settingslib.flags.Flags; import com.android.systemui.Prefs; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.AnimatorTestRule; Loading Loading @@ -794,6 +796,38 @@ public class VolumeDialogImplTest extends SysuiTestCase { verify(mVolumeDialogInteractor, atLeastOnce()).onDialogDismissed(); // dismiss by timeout } @Test @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX) public void testDynamicStreamForBroadcast_createRow() { State state = createShellState(); VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState(); ss.dynamic = true; ss.levelMin = 0; ss.levelMax = 255; ss.level = 20; ss.name = -1; ss.remoteLabel = mContext.getString(R.string.audio_sharing_description); state.states.append(DYNAMIC_STREAM_BROADCAST, ss); mDialog.onStateChangedH(state); mTestableLooper.processAllMessages(); ViewGroup volumeDialogRows = mDialog.getDialogView().findViewById(R.id.volume_dialog_rows); assumeNotNull(volumeDialogRows); View broadcastRow = null; final int rowCount = volumeDialogRows.getChildCount(); // we don't make assumptions about the position of the dnd row for (int i = 0; i < rowCount; i++) { View volumeRow = volumeDialogRows.getChildAt(i); if (volumeRow.getId() == DYNAMIC_STREAM_BROADCAST) { broadcastRow = volumeRow; break; } } assertNotNull(broadcastRow); assertEquals(broadcastRow.getVisibility(), View.VISIBLE); } /** * @return true if at least one volume row has the DND icon */ Loading