diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml index c525ccd1f4d..9017ccc4fd3 100644 --- a/res/xml/sound_settings.xml +++ b/res/xml/sound_settings.xml @@ -21,13 +21,14 @@ settings:keywords="@string/keywords_sounds" settings:initialExpandedChildrenCount="9"> - - + + settings:allowDividerBelow="true" + settings:controller="com.android.settings.notification.RemoteVolumeGroupController"> + controllers = sessionManager.getActiveSessions(null); - for (MediaController mediaController : controllers) { - final MediaController.PlaybackInfo pi = mediaController.getPlaybackInfo(); - if (isRemote(pi)) { - return mediaController.getSessionToken(); - } - } - - // No active remote media at this point - return null; - } - - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - mPreference.setVisible(mActiveToken != null); - if (mMediaController != null) { - updatePreference(mPreference, mActiveToken, mMediaController.getPlaybackInfo()); - } - } - - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) - public void onResume() { - super.onResume(); - mMediaSessions.init(); - } - - @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) - public void onPause() { - super.onPause(); - mMediaSessions.destroy(); - } - - @Override - public int getSliderPosition() { - if (mPreference != null) { - return mPreference.getProgress(); - } - if (mMediaController == null) { - return 0; - } - final MediaController.PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo(); - return playbackInfo != null ? playbackInfo.getCurrentVolume() : 0; - } - - @Override - public boolean setSliderPosition(int position) { - if (mPreference != null) { - mPreference.setProgress(position); - } - if (mMediaController == null) { - return false; - } - mMediaController.setVolumeTo(position, 0); - return true; - } - - @Override - public int getMax() { - if (mPreference != null) { - return mPreference.getMax(); - } - if (mMediaController == null) { - return 0; - } - final MediaController.PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo(); - return playbackInfo != null ? playbackInfo.getMaxVolume() : 0; - } - - @Override - public int getMin() { - if (mPreference != null) { - return mPreference.getMin(); - } - return 0; - } - - @Override - public boolean isSliceable() { - return TextUtils.equals(getPreferenceKey(), KEY_REMOTE_VOLUME); - } - - @Override - public boolean isPublicSlice() { - return true; - } - - @Override - public boolean useDynamicSliceSummary() { - return true; - } - - @Override - public String getPreferenceKey() { - return KEY_REMOTE_VOLUME; - } - - @Override - public int getAudioStream() { - // This can be anything because remote volume controller doesn't rely on it. - return REMOTE_VOLUME; - } - - @Override - public int getMuteIcon() { - return R.drawable.ic_volume_remote_mute; - } - - public static boolean isRemote(MediaController.PlaybackInfo pi) { - return pi != null - && pi.getPlaybackType() == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE; - } - - @Override - public Class getBackgroundWorkerClass() { - return RemoteVolumeSliceWorker.class; - } - - private void updatePreference(VolumeSeekBarPreference seekBarPreference, - MediaSession.Token token, MediaController.PlaybackInfo playbackInfo) { - if (seekBarPreference == null || token == null || playbackInfo == null) { - return; - } - - seekBarPreference.setMax(playbackInfo.getMaxVolume()); - seekBarPreference.setVisible(true); - setSliderPosition(playbackInfo.getCurrentVolume()); - } - - private void updateToken(MediaSession.Token token) { - mActiveToken = token; - if (token != null) { - mMediaController = new MediaController(mContext, mActiveToken); - } else { - mMediaController = null; - } - } - - /** - * Listener for background change to remote volume, which listens callback - * from {@code MediaSessions} - */ - public static class RemoteVolumeSliceWorker extends SliceBackgroundWorker implements - MediaSessions.Callbacks { - - private MediaSessions mMediaSessions; - - public RemoteVolumeSliceWorker(Context context, Uri uri) { - super(context, uri); - mMediaSessions = new MediaSessions(context, Looper.getMainLooper(), this); - } - - @Override - protected void onSlicePinned() { - mMediaSessions.init(); - } - - @Override - protected void onSliceUnpinned() { - mMediaSessions.destroy(); - } - - @Override - public void close() throws IOException { - mMediaSessions = null; - } - - @Override - public void onRemoteUpdate(MediaSession.Token token, String name, - MediaController.PlaybackInfo pi) { - notifySliceChange(); - } - - @Override - public void onRemoteRemoved(MediaSession.Token t) { - notifySliceChange(); - } - - @Override - public void onRemoteVolumeChanged(MediaSession.Token token, int flags) { - notifySliceChange(); - } - } -} diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java index 7d14691fed6..ad11f559b05 100644 --- a/src/com/android/settings/notification/SoundSettings.java +++ b/src/com/android/settings/notification/SoundSettings.java @@ -179,7 +179,6 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult volumeControllers.add(use(RingVolumePreferenceController.class)); volumeControllers.add(use(NotificationVolumePreferenceController.class)); volumeControllers.add(use(CallVolumePreferenceController.class)); - volumeControllers.add(use(RemoteVolumePreferenceController.class)); use(HandsFreeProfileOutputPreferenceController.class).setCallback(listPreference -> onPreferenceDataChanged(listPreference)); @@ -190,6 +189,7 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult controller.setCallback(mVolumeCallback); getSettingsLifecycle().addObserver(controller); } + getSettingsLifecycle().addObserver(use(RemoteVolumeGroupController.class)); } // === Volumes === diff --git a/tests/robotests/src/com/android/settings/notification/RemoteVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/RemoteVolumePreferenceControllerTest.java deleted file mode 100644 index 44fc04f396f..00000000000 --- a/tests/robotests/src/com/android/settings/notification/RemoteVolumePreferenceControllerTest.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -package com.android.settings.notification; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.media.session.ISessionController; -import android.media.session.MediaController; -import android.media.session.MediaSession; -import android.media.session.MediaSessionManager; - -import com.android.settings.R; -import com.android.settings.core.BasePreferenceController; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -import java.util.ArrayList; -import java.util.List; - -@RunWith(RobolectricTestRunner.class) -public class RemoteVolumePreferenceControllerTest { - private static final int CURRENT_POS = 5; - private static final int MAX_POS = 10; - - @Mock - private MediaSessionManager mMediaSessionManager; - @Mock - private MediaController mMediaController; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private ISessionController mStub; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private ISessionController mStub2; - private MediaSession.Token mToken; - private MediaSession.Token mToken2; - private RemoteVolumePreferenceController mController; - private Context mContext; - private List mActiveSessions; - private MediaController.PlaybackInfo mPlaybackInfo; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mContext = spy(RuntimeEnvironment.application); - when(mContext.getSystemService(MediaSessionManager.class)).thenReturn(mMediaSessionManager); - mActiveSessions = new ArrayList<>(); - mActiveSessions.add(mMediaController); - when(mMediaSessionManager.getActiveSessions(null)).thenReturn( - mActiveSessions); - mToken = new MediaSession.Token(mStub); - mToken2 = new MediaSession.Token(mStub2); - - mController = new RemoteVolumePreferenceController(mContext); - mPlaybackInfo = new MediaController.PlaybackInfo( - MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, 0, MAX_POS, CURRENT_POS, null); - when(mMediaController.getPlaybackInfo()).thenReturn(mPlaybackInfo); - when(mMediaController.getSessionToken()).thenReturn(mToken); - } - - @Test - public void getActiveRemoteToken_containRemoteMedia_returnToken() { - when(mMediaController.getPlaybackInfo()).thenReturn( - new MediaController.PlaybackInfo(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, - 0, 0, 0, null)); - assertThat(mController.getActiveRemoteToken(mContext)).isEqualTo(mToken); - } - - @Test - public void getActiveRemoteToken_noRemoteMedia_returnNull() { - when(mMediaController.getPlaybackInfo()).thenReturn( - new MediaController.PlaybackInfo(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, - 0, 0, 0, null)); - assertThat(mController.getActiveRemoteToken(mContext)).isNull(); - } - - @Test - public void isAvailable_returnAvailableUnsearchable() { - assertThat(mController.isAvailable()).isTrue(); - assertThat(mController.getAvailabilityStatus()).isEqualTo( - BasePreferenceController.AVAILABLE_UNSEARCHABLE); - } - - @Test - public void getMuteIcon_returnMuteIcon() { - assertThat(mController.getMuteIcon()).isEqualTo(R.drawable.ic_volume_remote_mute); - } - - @Test - public void getAudioStream_returnRemoteVolume() { - assertThat(mController.getAudioStream()).isEqualTo( - RemoteVolumePreferenceController.REMOTE_VOLUME); - } - - @Test - public void getSliderPosition_controllerNull_returnZero() { - mController.mMediaController = null; - - assertThat(mController.getSliderPosition()).isEqualTo(0); - } - - @Test - public void getSliderPosition_controllerExists_returnValue() { - mController.mMediaController = mMediaController; - - assertThat(mController.getSliderPosition()).isEqualTo(CURRENT_POS); - } - - @Test - public void getMinValue_controllerNull_returnZero() { - mController.mMediaController = null; - - assertThat(mController.getMin()).isEqualTo(0); - } - - @Test - public void getMinValue_controllerExists_returnValue() { - mController.mMediaController = mMediaController; - - assertThat(mController.getMin()).isEqualTo(0); - } - - @Test - public void getMaxValue_controllerNull_returnZero() { - mController.mMediaController = null; - - assertThat(mController.getMax()).isEqualTo(0); - } - - @Test - public void getMaxValue_controllerExists_returnValue() { - mController.mMediaController = mMediaController; - - assertThat(mController.getMax()).isEqualTo(MAX_POS); - } - - @Test - public void setSliderPosition_controllerNull_returnFalse() { - mController.mMediaController = null; - - assertThat(mController.setSliderPosition(CURRENT_POS)).isFalse(); - } - - @Test - public void setSliderPosition_controllerExists_returnTrue() { - mController.mMediaController = mMediaController; - - assertThat(mController.setSliderPosition(CURRENT_POS)).isTrue(); - verify(mMediaController).setVolumeTo(CURRENT_POS, 0 /* flags */); - } - - @Test - public void onRemoteUpdate_firstToken_updateTokenAndPreference() { - mController.mPreference = new VolumeSeekBarPreference(mContext); - mController.mActiveToken = null; - - mController.mCallbacks.onRemoteUpdate(mToken, "token", mPlaybackInfo); - - assertThat(mController.mActiveToken).isEqualTo(mToken); - assertThat(mController.mPreference.isVisible()).isTrue(); - assertThat(mController.mPreference.getMax()).isEqualTo(MAX_POS); - assertThat(mController.mPreference.getProgress()).isEqualTo(CURRENT_POS); - } - - @Test - public void onRemoteUpdate_differentToken_doNothing() { - mController.mActiveToken = mToken; - - mController.mCallbacks.onRemoteUpdate(mToken2, "token2", mPlaybackInfo); - - assertThat(mController.mActiveToken).isEqualTo(mToken); - } - - @Test - public void onRemoteRemoved_tokenRemoved_setInvisible() { - mController.mPreference = new VolumeSeekBarPreference(mContext); - mController.mActiveToken = mToken; - - mController.mCallbacks.onRemoteRemoved(mToken); - - assertThat(mController.mActiveToken).isNull(); - assertThat(mController.mPreference.isVisible()).isFalse(); - } - - @Test - public void onRemoteVolumeChanged_volumeChanged_updateIt() { - mController.mPreference = new VolumeSeekBarPreference(mContext); - mController.mPreference.setMax(MAX_POS); - mController.mActiveToken = mToken; - mController.mMediaController = mMediaController; - - mController.mCallbacks.onRemoteVolumeChanged(mToken, 0 /* flags */); - - assertThat(mController.mPreference.getProgress()).isEqualTo(CURRENT_POS); - } - - @Test - public void isPublicSlice_returnTrue() { - assertThat(mController.isPublicSlice()).isTrue(); - } -} diff --git a/tests/robotests/src/com/android/settings/notification/SoundSettingsTest.java b/tests/robotests/src/com/android/settings/notification/SoundSettingsTest.java index 56d0828ca2c..b6f06f48def 100644 --- a/tests/robotests/src/com/android/settings/notification/SoundSettingsTest.java +++ b/tests/robotests/src/com/android/settings/notification/SoundSettingsTest.java @@ -25,13 +25,13 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.media.AudioManager; -import android.os.Handler; import android.os.UserManager; import android.preference.SeekBarVolumizer; import com.android.settings.R; import com.android.settings.testutils.XmlTestUtils; import com.android.settings.testutils.shadow.ShadowAudioHelper; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowDeviceConfig; import com.android.settings.testutils.shadow.ShadowUserManager; @@ -48,7 +48,8 @@ import java.util.List; public class SoundSettingsTest { @Test - @Config(shadows = {ShadowUserManager.class, ShadowAudioHelper.class, ShadowDeviceConfig.class}) + @Config(shadows = {ShadowUserManager.class, ShadowAudioHelper.class, ShadowDeviceConfig.class, + ShadowBluetoothAdapter.class}) public void getNonIndexableKeys_existInXmlLayout() { final Context context = spy(RuntimeEnvironment.application); AudioManager audioManager = mock(AudioManager.class);