Merge "Show "Stream paused" when hysteresis mode in UMO." into main
This commit is contained in:
@@ -14035,7 +14035,7 @@
|
|||||||
<!-- The preference summary when add source succeed [CHAR LIMIT=NONE] -->
|
<!-- The preference summary when add source succeed [CHAR LIMIT=NONE] -->
|
||||||
<string name="audio_streams_listening_now">Listening now</string>
|
<string name="audio_streams_listening_now">Listening now</string>
|
||||||
<!-- The preference summary when source is present on sinks [CHAR LIMIT=NONE] -->
|
<!-- The preference summary when source is present on sinks [CHAR LIMIT=NONE] -->
|
||||||
<string name="audio_streams_present_now">Paused by host</string>
|
<string name="audio_streams_present_now">Stream paused</string>
|
||||||
<!-- Le audio streams service notification leave broadcast text [CHAR LIMIT=NONE] -->
|
<!-- Le audio streams service notification leave broadcast text [CHAR LIMIT=NONE] -->
|
||||||
<string name="audio_streams_media_service_notification_leave_broadcast_text">Stop listening</string>
|
<string name="audio_streams_media_service_notification_leave_broadcast_text">Stop listening</string>
|
||||||
<!-- Le audio streams no le device dialog title [CHAR LIMIT=NONE] -->
|
<!-- Le audio streams no le device dialog title [CHAR LIMIT=NONE] -->
|
||||||
|
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.PAUSED;
|
||||||
|
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.STREAMING;
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
@@ -23,6 +26,7 @@ import android.app.Service;
|
|||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||||
import android.bluetooth.BluetoothProfile;
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.bluetooth.BluetoothVolumeControl;
|
import android.bluetooth.BluetoothVolumeControl;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -64,7 +68,8 @@ public class AudioStreamMediaService extends Service {
|
|||||||
static final String DEVICES = "audio_stream_media_service_devices";
|
static final String DEVICES = "audio_stream_media_service_devices";
|
||||||
private static final String TAG = "AudioStreamMediaService";
|
private static final String TAG = "AudioStreamMediaService";
|
||||||
private static final int NOTIFICATION_ID = 1;
|
private static final int NOTIFICATION_ID = 1;
|
||||||
private static final int BROADCAST_CONTENT_TEXT = R.string.audio_streams_listening_now;
|
private static final int BROADCAST_LISTENING_NOW_TEXT = R.string.audio_streams_listening_now;
|
||||||
|
private static final int BROADCAST_STREAM_PAUSED_TEXT = R.string.audio_streams_present_now;
|
||||||
@VisibleForTesting static final String LEAVE_BROADCAST_ACTION = "leave_broadcast_action";
|
@VisibleForTesting static final String LEAVE_BROADCAST_ACTION = "leave_broadcast_action";
|
||||||
private static final String LEAVE_BROADCAST_TEXT = "Leave Broadcast";
|
private static final String LEAVE_BROADCAST_TEXT = "Leave Broadcast";
|
||||||
private static final String CHANNEL_ID = "bluetooth_notification_channel";
|
private static final String CHANNEL_ID = "bluetooth_notification_channel";
|
||||||
@@ -94,11 +99,22 @@ public class AudioStreamMediaService extends Service {
|
|||||||
LEAVE_BROADCAST_ACTION,
|
LEAVE_BROADCAST_ACTION,
|
||||||
LEAVE_BROADCAST_TEXT,
|
LEAVE_BROADCAST_TEXT,
|
||||||
com.android.settings.R.drawable.ic_clear);
|
com.android.settings.R.drawable.ic_clear);
|
||||||
|
private final PlaybackState.Builder mPlayStateHysteresisBuilder =
|
||||||
|
new PlaybackState.Builder()
|
||||||
|
.setState(
|
||||||
|
PlaybackState.STATE_STOPPED,
|
||||||
|
STATIC_PLAYBACK_POSITION,
|
||||||
|
ZERO_PLAYBACK_SPEED)
|
||||||
|
.addCustomAction(
|
||||||
|
LEAVE_BROADCAST_ACTION,
|
||||||
|
LEAVE_BROADCAST_TEXT,
|
||||||
|
com.android.settings.R.drawable.ic_clear);
|
||||||
|
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider =
|
private final MetricsFeatureProvider mMetricsFeatureProvider =
|
||||||
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
|
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
|
||||||
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
||||||
private final AtomicBoolean mIsMuted = new AtomicBoolean(false);
|
private final AtomicBoolean mIsMuted = new AtomicBoolean(false);
|
||||||
|
private final AtomicBoolean mIsHysteresis = new AtomicBoolean(false);
|
||||||
// Set 25 as default as the volume range from `VolumeControlProfile` is from 0 to 255.
|
// Set 25 as default as the volume range from `VolumeControlProfile` is from 0 to 255.
|
||||||
// If the initial volume from `onDeviceVolumeChanged` is larger than zero (not muted), we will
|
// If the initial volume from `onDeviceVolumeChanged` is larger than zero (not muted), we will
|
||||||
// override this value. Otherwise, we raise the volume to 25 when the play button is clicked.
|
// override this value. Otherwise, we raise the volume to 25 when the play button is clicked.
|
||||||
@@ -255,6 +271,9 @@ public class AudioStreamMediaService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private PlaybackState getPlaybackState() {
|
private PlaybackState getPlaybackState() {
|
||||||
|
if (mIsHysteresis.get()) {
|
||||||
|
return mPlayStateHysteresisBuilder.build();
|
||||||
|
}
|
||||||
return mIsMuted.get() ? mPlayStatePausingBuilder.build() : mPlayStatePlayingBuilder.build();
|
return mIsMuted.get() ? mPlayStatePausingBuilder.build() : mPlayStatePlayingBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +302,9 @@ public class AudioStreamMediaService extends Service {
|
|||||||
new Notification.Builder(this, CHANNEL_ID)
|
new Notification.Builder(this, CHANNEL_ID)
|
||||||
.setSmallIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing)
|
.setSmallIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing)
|
||||||
.setStyle(mediaStyle)
|
.setStyle(mediaStyle)
|
||||||
.setContentText(getString(BROADCAST_CONTENT_TEXT))
|
.setContentText(getString(
|
||||||
|
mIsHysteresis.get() ? BROADCAST_STREAM_PAUSED_TEXT :
|
||||||
|
BROADCAST_LISTENING_NOW_TEXT))
|
||||||
.setSilent(true);
|
.setSilent(true);
|
||||||
return notificationBuilder.build();
|
return notificationBuilder.build();
|
||||||
}
|
}
|
||||||
@@ -307,6 +328,38 @@ public class AudioStreamMediaService extends Service {
|
|||||||
handleRemoveSource();
|
handleRemoveSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceiveStateChanged(
|
||||||
|
BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state) {
|
||||||
|
super.onReceiveStateChanged(sink, sourceId, state);
|
||||||
|
if (!mHysteresisModeFixAvailable || mDevices == null || !mDevices.contains(sink)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var sourceState = LocalBluetoothLeBroadcastAssistant.getLocalSourceState(state);
|
||||||
|
boolean streaming = sourceState == STREAMING;
|
||||||
|
boolean paused = sourceState == PAUSED;
|
||||||
|
// Exit early if the state is neither streaming nor paused
|
||||||
|
if (!streaming && !paused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Atomically update mIsHysteresis if its current value is not the current paused state
|
||||||
|
if (mIsHysteresis.compareAndSet(!paused, paused)) {
|
||||||
|
synchronized (mLocalSessionLock) {
|
||||||
|
if (mLocalSession == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mLocalSession.setPlaybackState(getPlaybackState());
|
||||||
|
if (mNotificationManager != null) {
|
||||||
|
mNotificationManager.notify(
|
||||||
|
NOTIFICATION_ID,
|
||||||
|
buildNotification(mLocalSession.getSessionToken())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Log.d(TAG, "updating hysteresis mode to : " + paused);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleRemoveSource() {
|
private void handleRemoveSource() {
|
||||||
if (mAudioStreamsHelper != null
|
if (mAudioStreamsHelper != null
|
||||||
&& !mAudioStreamsHelper.getConnectedBroadcastIdAndState(
|
&& !mAudioStreamsHelper.getConnectedBroadcastIdAndState(
|
||||||
|
@@ -40,6 +40,7 @@ import android.app.NotificationManager;
|
|||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||||
import android.bluetooth.BluetoothProfile;
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.bluetooth.BluetoothStatusCodes;
|
import android.bluetooth.BluetoothStatusCodes;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -86,6 +87,7 @@ import org.robolectric.shadow.api.Shadow;
|
|||||||
import org.robolectric.util.ReflectionHelpers;
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
@@ -99,11 +101,13 @@ import java.util.Set;
|
|||||||
public class AudioStreamMediaServiceTest {
|
public class AudioStreamMediaServiceTest {
|
||||||
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
|
private static final String DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
|
||||||
private static final String CHANNEL_ID = "bluetooth_notification_channel";
|
private static final String CHANNEL_ID = "bluetooth_notification_channel";
|
||||||
private static final String DEVICE_NAME = "name";
|
private static final String DEVICE_NAME = "name";
|
||||||
@Mock private Resources mResources;
|
@Mock private Resources mResources;
|
||||||
@Mock private LocalBluetoothManager mLocalBtManager;
|
@Mock private LocalBluetoothManager mLocalBtManager;
|
||||||
@Mock private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
|
@Mock private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
|
||||||
|
@Mock private BluetoothLeBroadcastReceiveState mBroadcastReceiveState;
|
||||||
@Mock private AudioStreamsHelper mAudioStreamsHelper;
|
@Mock private AudioStreamsHelper mAudioStreamsHelper;
|
||||||
@Mock private NotificationManager mNotificationManager;
|
@Mock private NotificationManager mNotificationManager;
|
||||||
@Mock private MediaSessionManager mMediaSessionManager;
|
@Mock private MediaSessionManager mMediaSessionManager;
|
||||||
@@ -304,6 +308,63 @@ public class AudioStreamMediaServiceTest {
|
|||||||
verify(mAudioStreamMediaService).stopSelf();
|
verify(mAudioStreamMediaService).stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void assistantCallback_onReceiveStateChanged_connected_doNothing() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
|
||||||
|
mAudioStreamMediaService.onCreate();
|
||||||
|
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
|
||||||
|
|
||||||
|
assertThat(mAudioStreamMediaService.mBroadcastAssistantCallback).isNotNull();
|
||||||
|
List<Long> bisSyncState = new ArrayList<>();
|
||||||
|
bisSyncState.add(1L);
|
||||||
|
when(mBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState);
|
||||||
|
when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
|
||||||
|
when(mBroadcastReceiveState.getSourceDevice()).thenReturn(mDevice);
|
||||||
|
|
||||||
|
mAudioStreamMediaService.mBroadcastAssistantCallback.onReceiveStateChanged(
|
||||||
|
mDevice, /* sourceId= */ 0, /* state= */ mBroadcastReceiveState);
|
||||||
|
|
||||||
|
verify(mNotificationManager, never()).notify(anyInt(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void assistantCallback_onReceiveStateChanged_hysteresis_updateNotification() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
|
||||||
|
mAudioStreamMediaService.onCreate();
|
||||||
|
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
|
||||||
|
|
||||||
|
assertThat(mAudioStreamMediaService.mBroadcastAssistantCallback).isNotNull();
|
||||||
|
when(mBroadcastReceiveState.getBisSyncState()).thenReturn(new ArrayList<>());
|
||||||
|
when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
|
||||||
|
when(mBroadcastReceiveState.getSourceDevice()).thenReturn(mDevice);
|
||||||
|
|
||||||
|
mAudioStreamMediaService.mBroadcastAssistantCallback.onReceiveStateChanged(
|
||||||
|
mDevice, /* sourceId= */ 0, /* state= */ mBroadcastReceiveState);
|
||||||
|
|
||||||
|
verify(mNotificationManager).notify(anyInt(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void assistantCallback_onReceiveStateChanged_hysteresis_flagOff_doNothing() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
|
||||||
|
mAudioStreamMediaService.onCreate();
|
||||||
|
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
|
||||||
|
|
||||||
|
assertThat(mAudioStreamMediaService.mBroadcastAssistantCallback).isNotNull();
|
||||||
|
mAudioStreamMediaService.mBroadcastAssistantCallback.onReceiveStateChanged(
|
||||||
|
mDevice, /* sourceId= */ 0, /* state= */ mBroadcastReceiveState);
|
||||||
|
|
||||||
|
verify(mBroadcastReceiveState, never()).getBisSyncState();
|
||||||
|
verify(mBroadcastReceiveState, never()).getSourceDevice();
|
||||||
|
verify(mNotificationManager, never()).notify(anyInt(), any());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bluetoothCallback_onBluetoothOff_stopSelf() {
|
public void bluetoothCallback_onBluetoothOff_stopSelf() {
|
||||||
mAudioStreamMediaService.onCreate();
|
mAudioStreamMediaService.onCreate();
|
||||||
|
Reference in New Issue
Block a user