diff --git a/src/com/android/settings/media/MediaDeviceUpdateWorker.java b/src/com/android/settings/media/MediaDeviceUpdateWorker.java index f67afdc3943..21d8237a7cd 100644 --- a/src/com/android/settings/media/MediaDeviceUpdateWorker.java +++ b/src/com/android/settings/media/MediaDeviceUpdateWorker.java @@ -16,8 +16,15 @@ package com.android.settings.media; +import static android.media.AudioManager.STREAM_DEVICES_CHANGED_ACTION; + +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; import android.net.Uri; +import android.text.TextUtils; import androidx.annotation.VisibleForTesting; @@ -37,6 +44,7 @@ public class MediaDeviceUpdateWorker extends SliceBackgroundWorker private final Context mContext; private final List mMediaDevices = new ArrayList<>(); + private final DevicesChangedBroadcastReceiver mReceiver; private String mPackageName; @@ -46,6 +54,7 @@ public class MediaDeviceUpdateWorker extends SliceBackgroundWorker public MediaDeviceUpdateWorker(Context context, Uri uri) { super(context, uri); mContext = context; + mReceiver = new DevicesChangedBroadcastReceiver(); } public void setPackageName(String packageName) { @@ -60,12 +69,15 @@ public class MediaDeviceUpdateWorker extends SliceBackgroundWorker } mLocalMediaManager.registerCallback(this); + final IntentFilter intentFilter = new IntentFilter(STREAM_DEVICES_CHANGED_ACTION); + mContext.registerReceiver(mReceiver, intentFilter); mLocalMediaManager.startScan(); } @Override protected void onSliceUnpinned() { mLocalMediaManager.unregisterCallback(this); + mContext.unregisterReceiver(mReceiver); mLocalMediaManager.stopScan(); } @@ -107,4 +119,14 @@ public class MediaDeviceUpdateWorker extends SliceBackgroundWorker public MediaDevice getCurrentConnectedMediaDevice() { return mLocalMediaManager.getCurrentConnectedDevice(); } + + private class DevicesChangedBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (TextUtils.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION, action)) { + notifySliceChange(); + } + } + } } diff --git a/src/com/android/settings/media/MediaOutputIndicatorSlice.java b/src/com/android/settings/media/MediaOutputIndicatorSlice.java index 0ffa9ff2006..75213c07eda 100644 --- a/src/com/android/settings/media/MediaOutputIndicatorSlice.java +++ b/src/com/android/settings/media/MediaOutputIndicatorSlice.java @@ -24,7 +24,6 @@ import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; import android.net.Uri; -import android.telephony.TelephonyManager; import android.util.Log; import androidx.core.graphics.drawable.IconCompat; @@ -115,13 +114,11 @@ public class MediaOutputIndicatorSlice implements CustomSliceable { private boolean isVisible() { // To decide Slice's visibility. // Return true if - // 1. phone is not in ongoing call mode + // 1. AudioMode is not in on-going call // 2. Bluetooth device is connected - final TelephonyManager telephonyManager = - (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); - return telephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE - && (!CollectionUtils.isEmpty(getConnectedA2dpDevices()) - || !CollectionUtils.isEmpty(getConnectedHearingAidDevices())); + return (!CollectionUtils.isEmpty(getConnectedA2dpDevices()) + || !CollectionUtils.isEmpty(getConnectedHearingAidDevices())) + && !com.android.settingslib.Utils.isAudioModeOngoingCall(mContext); } private List getConnectedA2dpDevices() { diff --git a/src/com/android/settings/media/MediaOutputIndicatorWorker.java b/src/com/android/settings/media/MediaOutputIndicatorWorker.java index 7b0b3dde413..6498dd6f4ce 100644 --- a/src/com/android/settings/media/MediaOutputIndicatorWorker.java +++ b/src/com/android/settings/media/MediaOutputIndicatorWorker.java @@ -16,9 +16,16 @@ package com.android.settings.media; +import static android.media.AudioManager.STREAM_DEVICES_CHANGED_ACTION; + import android.bluetooth.BluetoothProfile; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; import android.net.Uri; +import android.text.TextUtils; import android.util.Log; import com.android.settings.bluetooth.Utils; @@ -36,10 +43,15 @@ public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements private static final String TAG = "MediaOutputIndicatorWorker"; + private final DevicesChangedBroadcastReceiver mReceiver; + private final Context mContext; + private LocalBluetoothManager mLocalBluetoothManager; public MediaOutputIndicatorWorker(Context context, Uri uri) { super(context, uri); + mReceiver = new DevicesChangedBroadcastReceiver(); + mContext = context; } @Override @@ -49,6 +61,8 @@ public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements Log.e(TAG, "Bluetooth is not supported on this device"); return; } + final IntentFilter intentFilter = new IntentFilter(STREAM_DEVICES_CHANGED_ACTION); + mContext.registerReceiver(mReceiver, intentFilter); mLocalBluetoothManager.getEventManager().registerCallback(this); } @@ -59,10 +73,11 @@ public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements return; } mLocalBluetoothManager.getEventManager().unregisterCallback(this); + mContext.unregisterReceiver(mReceiver); } @Override - public void close() throws IOException { + public void close() { mLocalBluetoothManager = null; } @@ -84,4 +99,14 @@ public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements public void onAudioModeChanged() { notifySliceChange(); } + + private class DevicesChangedBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (TextUtils.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION, action)) { + notifySliceChange(); + } + } + } } diff --git a/src/com/android/settings/media/MediaOutputSlice.java b/src/com/android/settings/media/MediaOutputSlice.java index 734c31e5b4a..8086c413edf 100644 --- a/src/com/android/settings/media/MediaOutputSlice.java +++ b/src/com/android/settings/media/MediaOutputSlice.java @@ -24,7 +24,6 @@ import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -197,15 +196,13 @@ public class MediaOutputSlice implements CustomSliceable { private boolean isVisible() { // To decide Slice's visibility. // Return true if - // 1. phone is not in ongoing call mode + // 1. AudioMode is not in on-going call // 2. worker is not null // 3. Bluetooth is enabled - final TelephonyManager telephonyManager = - (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - return telephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE - && adapter.isEnabled() + return adapter.isEnabled() + && !com.android.settingslib.Utils.isAudioModeOngoingCall(mContext) && getWorker() != null; } } diff --git a/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java b/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java index 28bf4e9053b..2e9fa30498d 100644 --- a/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java +++ b/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java @@ -24,17 +24,23 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.media.AudioManager; import android.net.Uri; +import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadows.ShadowApplication; import java.util.ArrayList; import java.util.List; @@ -54,12 +60,15 @@ public class MediaDeviceUpdateWorkerTest { private Context mContext; private MediaDevice mMediaDevice1; private MediaDevice mMediaDevice2; + private ShadowApplication mShadowApplication; @Before public void setUp() { + MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); mMediaDeviceUpdateWorker = new MediaDeviceUpdateWorker(mContext, URI); mResolver = mock(ContentResolver.class); + mShadowApplication = ShadowApplication.getInstance(); mMediaDevice1 = mock(MediaDevice.class); when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID); @@ -129,4 +138,17 @@ public class MediaDeviceUpdateWorkerTest { assertThat(devices.size()).isEqualTo(newDevices.size()); } + + @Test + public void onReceive_shouldNotifyChange() { + mMediaDeviceUpdateWorker.mLocalMediaManager = mock(LocalMediaManager.class); + + mMediaDeviceUpdateWorker.onSlicePinned(); + final Intent intent = new Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION); + for (BroadcastReceiver receiver : mShadowApplication.getReceiversForIntent(intent)) { + receiver.onReceive(mContext, intent); + } + + verify(mResolver).notifyChange(URI, null); + } } diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java index c5584d0cb54..ca9c6b9f0c2 100644 --- a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java +++ b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java @@ -26,7 +26,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.content.Context; -import android.telephony.TelephonyManager; +import android.media.AudioManager; import androidx.slice.Slice; import androidx.slice.SliceMetadata; @@ -48,15 +48,12 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import org.robolectric.shadow.api.Shadow; -import org.robolectric.shadows.ShadowTelephonyManager; import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowBluetoothUtils.class, - ShadowTelephonyManager.class}) +@Config(shadows = {ShadowBluetoothUtils.class}) public class MediaOutputIndicatorSliceTest { private static final String TEST_A2DP_DEVICE_NAME = "Test_A2DP_BT_Device_NAME"; @@ -80,14 +77,14 @@ public class MediaOutputIndicatorSliceTest { private Context mContext; private List mDevicesList; private MediaOutputIndicatorSlice mMediaOutputIndicatorSlice; - private ShadowTelephonyManager mShadowTelephonyManager; + private AudioManager mAudioManager; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); - mShadowTelephonyManager = Shadow.extract(mContext.getSystemService( - Context.TELEPHONY_SERVICE)); + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + mAudioManager.setMode(AudioManager.MODE_NORMAL); // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); @@ -160,28 +157,28 @@ public class MediaOutputIndicatorSliceTest { } @Test - public void getSlice_callStateIdle_available() { + public void getSlice_audioModeIsInCommunication_returnNull() { mDevicesList.add(mA2dpDevice); when(mA2dpProfile.getConnectedDevices()).thenReturn(mDevicesList); - mShadowTelephonyManager.setCallState(TelephonyManager.CALL_STATE_IDLE); - - assertThat(mMediaOutputIndicatorSlice.getSlice()).isNotNull(); - } - - @Test - public void getSlice_callStateRinging_returnNull() { - mDevicesList.add(mA2dpDevice); - when(mA2dpProfile.getConnectedDevices()).thenReturn(mDevicesList); - mShadowTelephonyManager.setCallState(TelephonyManager.CALL_STATE_RINGING); + mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); assertThat(mMediaOutputIndicatorSlice.getSlice()).isNull(); } @Test - public void getSlice_callStateOffHook_returnNull() { + public void getSlice_audioModeIsRingtone_returnNull() { mDevicesList.add(mA2dpDevice); when(mA2dpProfile.getConnectedDevices()).thenReturn(mDevicesList); - mShadowTelephonyManager.setCallState(TelephonyManager.CALL_STATE_OFFHOOK); + mAudioManager.setMode(AudioManager.MODE_RINGTONE); + + assertThat(mMediaOutputIndicatorSlice.getSlice()).isNull(); + } + + @Test + public void getSlice_audioModeIsInCall_returnNull() { + mDevicesList.add(mA2dpDevice); + when(mA2dpProfile.getConnectedDevices()).thenReturn(mDevicesList); + mAudioManager.setMode(AudioManager.MODE_IN_CALL); assertThat(mMediaOutputIndicatorSlice.getSlice()).isNull(); } diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorWorkerTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorWorkerTest.java index 3671d819f83..78658fdc4ed 100644 --- a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorWorkerTest.java +++ b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorWorkerTest.java @@ -16,11 +16,19 @@ package com.android.settings.media; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; import android.net.Uri; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; @@ -35,6 +43,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothUtils.class}) @@ -47,20 +56,27 @@ public class MediaOutputIndicatorWorkerTest { private LocalBluetoothManager mLocalBluetoothManager; private Context mContext; private MediaOutputIndicatorWorker mMediaDeviceUpdateWorker; + private ShadowApplication mShadowApplication; + private ContentResolver mResolver; @Before public void setUp() { MockitoAnnotations.initMocks(this); + mShadowApplication = ShadowApplication.getInstance(); mContext = spy(RuntimeEnvironment.application); ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager; when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager); mMediaDeviceUpdateWorker = new MediaOutputIndicatorWorker(mContext, URI); + + mResolver = mock(ContentResolver.class); + doReturn(mResolver).when(mContext).getContentResolver(); } @Test public void onSlicePinned_registerCallback() { mMediaDeviceUpdateWorker.onSlicePinned(); verify(mBluetoothEventManager).registerCallback(mMediaDeviceUpdateWorker); + verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); } @Test @@ -68,5 +84,18 @@ public class MediaOutputIndicatorWorkerTest { mMediaDeviceUpdateWorker.onSlicePinned(); mMediaDeviceUpdateWorker.onSliceUnpinned(); verify(mBluetoothEventManager).unregisterCallback(mMediaDeviceUpdateWorker); + verify(mContext).unregisterReceiver(any(BroadcastReceiver.class)); + } + + @Test + public void onReceive_shouldNotifyChange() { + mMediaDeviceUpdateWorker.onSlicePinned(); + + final Intent intent = new Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION); + for (BroadcastReceiver receiver : mShadowApplication.getReceiversForIntent(intent)) { + receiver.onReceive(mContext, intent); + } + + verify(mResolver).notifyChange(URI, null); } } diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java index ac37a7f7bf2..f01ef0e17b6 100644 --- a/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java +++ b/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java @@ -34,7 +34,7 @@ import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; -import android.telephony.TelephonyManager; +import android.media.AudioManager; import androidx.slice.Slice; import androidx.slice.SliceMetadata; @@ -56,13 +56,12 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; -import org.robolectric.shadows.ShadowTelephonyManager; import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowBluetoothAdapter.class, ShadowTelephonyManager.class}) +@Config(shadows = {ShadowBluetoothAdapter.class}) public class MediaOutputSliceTest { private static final String TEST_PACKAGE_NAME = "com.fake.android.music"; @@ -82,21 +81,20 @@ public class MediaOutputSliceTest { private MediaOutputSlice mMediaOutputSlice; private MediaDeviceUpdateWorker mMediaDeviceUpdateWorker; private ShadowBluetoothAdapter mShadowBluetoothAdapter; - private ShadowTelephonyManager mShadowTelephonyManager; + private AudioManager mAudioManager; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); - mShadowTelephonyManager = Shadow.extract(mContext.getSystemService( - Context.TELEPHONY_SERVICE)); + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + mAudioManager.setMode(AudioManager.MODE_NORMAL); // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); // Setup BluetoothAdapter mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); mShadowBluetoothAdapter.setEnabled(true); - mShadowTelephonyManager.setCallState(TelephonyManager.CALL_STATE_IDLE); mMediaOutputSlice = new MediaOutputSlice(mContext); mMediaDeviceUpdateWorker = new MediaDeviceUpdateWorker(mContext, MEDIA_OUTPUT_SLICE_URI); @@ -131,8 +129,8 @@ public class MediaOutputSliceTest { } @Test - public void getSlice_callStateRinging_shouldReturnZeroRow() { - mShadowTelephonyManager.setCallState(TelephonyManager.CALL_STATE_RINGING); + public void getSlice_audioModeIsOngoingCall_shouldReturnZeroRow() { + mAudioManager.setMode(AudioManager.MODE_IN_CALL); final Slice slice = mMediaOutputSlice.getSlice();