Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9c2968ab authored by Tim Peng's avatar Tim Peng
Browse files

Update rule to display output switcher Slice

-Display when Cast device is available
-Add test case

Bug: 150907688
Test: make -j42 RunSettingsRoboTests
Change-Id: I1aa2fbe7b77a0274816af47bbc372eae9d7944c9
parent 5a4257de
Loading
Loading
Loading
Loading
+7 −78
Original line number Original line Diff line number Diff line
@@ -20,51 +20,33 @@ import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDIC


import android.annotation.ColorInt;
import android.annotation.ColorInt;
import android.app.PendingIntent;
import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.media.session.MediaController;
import android.media.session.MediaController;
import android.net.Uri;
import android.net.Uri;
import android.util.Log;


import androidx.core.graphics.drawable.IconCompat;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.SliceAction;
import androidx.slice.builders.SliceAction;


import com.android.internal.util.CollectionUtils;
import com.android.settings.R;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.Utils;
import com.android.settings.slices.CustomSliceable;
import com.android.settings.slices.CustomSliceable;
import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settings.slices.SliceBroadcastReceiver;
import com.android.settings.slices.SliceBroadcastReceiver;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.media.MediaOutputSliceConstants;
import com.android.settingslib.media.MediaOutputSliceConstants;


import java.util.ArrayList;
import java.util.List;

public class MediaOutputIndicatorSlice implements CustomSliceable {
public class MediaOutputIndicatorSlice implements CustomSliceable {


    private static final String TAG = "MediaOutputIndicatorSlice";
    private static final String TAG = "MediaOutputIndSlice";


    private Context mContext;
    private Context mContext;
    private LocalBluetoothManager mLocalBluetoothManager;
    private LocalBluetoothProfileManager mProfileManager;
    private MediaOutputIndicatorWorker mWorker;
    private MediaOutputIndicatorWorker mWorker;


    public MediaOutputIndicatorSlice(Context context) {
    public MediaOutputIndicatorSlice(Context context) {
        mContext = context;
        mContext = context;
        mLocalBluetoothManager = com.android.settings.bluetooth.Utils.getLocalBtManager(context);
        if (mLocalBluetoothManager == null) {
            Log.e(TAG, "Bluetooth is not supported on this device");
            return;
        }
        mProfileManager = mLocalBluetoothManager.getProfileManager();
    }
    }


    @Override
    @Override
@@ -86,7 +68,7 @@ public class MediaOutputIndicatorSlice implements CustomSliceable {
                .addRow(new ListBuilder.RowBuilder()
                .addRow(new ListBuilder.RowBuilder()
                        .setTitle(title)
                        .setTitle(title)
                        .setTitleItem(createEmptyIcon(), ListBuilder.ICON_IMAGE)
                        .setTitleItem(createEmptyIcon(), ListBuilder.ICON_IMAGE)
                        .setSubtitle(findActiveDeviceName())
                        .setSubtitle(getWorker().getCurrentConnectedMediaDevice().getName())
                        .setPrimaryAction(primarySliceAction));
                        .setPrimaryAction(primarySliceAction));
        return listBuilder.build();
        return listBuilder.build();
    }
    }
@@ -146,63 +128,10 @@ public class MediaOutputIndicatorSlice implements CustomSliceable {
        // To decide Slice's visibility.
        // To decide Slice's visibility.
        // Return true if
        // Return true if
        // 1. AudioMode is not in on-going call
        // 1. AudioMode is not in on-going call
        // 2. Bluetooth device is connected
        // 2. worker is not null
        return (!CollectionUtils.isEmpty(getConnectedA2dpDevices())
        // 3. Available devices are more than 1
                || !CollectionUtils.isEmpty(getConnectedHearingAidDevices()))
        return getWorker() != null
                && !com.android.settingslib.Utils.isAudioModeOngoingCall(mContext);
                && !com.android.settingslib.Utils.isAudioModeOngoingCall(mContext)
    }
                && getWorker().getMediaDevices().size() > 1;

    private List<BluetoothDevice> getConnectedA2dpDevices() {
        // Get A2dp devices on states
        // (STATE_CONNECTING, STATE_CONNECTED,  STATE_DISCONNECTING)
        final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
        if (a2dpProfile == null) {
            return new ArrayList<>();
        }
        return a2dpProfile.getConnectedDevices();
    }

    private List<BluetoothDevice> getConnectedHearingAidDevices() {
        // Get hearing aid profile devices on states
        // (STATE_CONNECTING, STATE_CONNECTED,  STATE_DISCONNECTING)
        final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
        if (hapProfile == null) {
            return new ArrayList<>();
        }

        return hapProfile.getConnectedDevices();
    }

    private CharSequence findActiveDeviceName() {
        // Return Hearing Aid device name if it is active
        BluetoothDevice activeDevice = findActiveHearingAidDevice();
        if (activeDevice != null) {
            return activeDevice.getAlias();
        }
        // Return A2DP device name if it is active
        final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
        if (a2dpProfile != null) {
            activeDevice = a2dpProfile.getActiveDevice();
            if (activeDevice != null) {
                return activeDevice.getAlias();
            }
        }
        // No active device, return default summary
        return mContext.getText(R.string.media_output_default_summary);
    }

    private BluetoothDevice findActiveHearingAidDevice() {
        final HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
        if (hearingAidProfile == null) {
            return null;
        }

        final List<BluetoothDevice> activeDevices = hearingAidProfile.getActiveDevices();
        for (BluetoothDevice btDevice : activeDevices) {
            if (btDevice != null) {
                return btDevice;
            }
        }
        return null;
    }
    }
}
}
+62 −18
Original line number Original line Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.settings.media;


import static android.media.AudioManager.STREAM_DEVICES_CHANGED_ACTION;
import static android.media.AudioManager.STREAM_DEVICES_CHANGED_ACTION;


import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
@@ -36,21 +35,33 @@ import androidx.annotation.Nullable;
import com.android.settings.bluetooth.Utils;
import com.android.settings.bluetooth.Utils;
import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;

import com.google.common.annotations.VisibleForTesting;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;


/**
/**
 * Listener for background change from {@code BluetoothCallback} to update media output indicator.
 * Listener for background change from {@code BluetoothCallback} to update media output indicator.
 */
 */
public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements BluetoothCallback {
public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements BluetoothCallback,
        LocalMediaManager.DeviceCallback {


    private static final String TAG = "MediaOutputIndicatorWorker";
    private static final String TAG = "MediaOutputIndWorker";


    private final DevicesChangedBroadcastReceiver mReceiver;
    private final DevicesChangedBroadcastReceiver mReceiver;
    private final Context mContext;
    private final Context mContext;
    private final Collection<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();


    private LocalBluetoothManager mLocalBluetoothManager;
    private LocalBluetoothManager mLocalBluetoothManager;


    @VisibleForTesting
    LocalMediaManager mLocalMediaManager;

    public MediaOutputIndicatorWorker(Context context, Uri uri) {
    public MediaOutputIndicatorWorker(Context context, Uri uri) {
        super(context, uri);
        super(context, uri);
        mReceiver = new DevicesChangedBroadcastReceiver();
        mReceiver = new DevicesChangedBroadcastReceiver();
@@ -59,6 +70,7 @@ public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements


    @Override
    @Override
    protected void onSlicePinned() {
    protected void onSlicePinned() {
        mMediaDevices.clear();
        mLocalBluetoothManager = Utils.getLocalBtManager(getContext());
        mLocalBluetoothManager = Utils.getLocalBtManager(getContext());
        if (mLocalBluetoothManager == null) {
        if (mLocalBluetoothManager == null) {
            Log.e(TAG, "Bluetooth is not supported on this device");
            Log.e(TAG, "Bluetooth is not supported on this device");
@@ -67,10 +79,25 @@ public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements
        final IntentFilter intentFilter = new IntentFilter(STREAM_DEVICES_CHANGED_ACTION);
        final IntentFilter intentFilter = new IntentFilter(STREAM_DEVICES_CHANGED_ACTION);
        mContext.registerReceiver(mReceiver, intentFilter);
        mContext.registerReceiver(mReceiver, intentFilter);
        mLocalBluetoothManager.getEventManager().registerCallback(this);
        mLocalBluetoothManager.getEventManager().registerCallback(this);

        if (mLocalMediaManager == null) {
            final MediaController controller = getActiveLocalMediaController();
            String packageName = null;
            if (controller != null) {
                packageName = controller.getPackageName();
            }
            mLocalMediaManager = new LocalMediaManager(mContext, packageName, null);
        }

        mLocalMediaManager.registerCallback(this);
        mLocalMediaManager.startScan();
    }
    }


    @Override
    @Override
    protected void onSliceUnpinned() {
    protected void onSliceUnpinned() {
        mLocalMediaManager.unregisterCallback(this);
        mLocalMediaManager.stopScan();

        if (mLocalBluetoothManager == null) {
        if (mLocalBluetoothManager == null) {
            Log.e(TAG, "Bluetooth is not supported on this device");
            Log.e(TAG, "Bluetooth is not supported on this device");
            return;
            return;
@@ -82,20 +109,7 @@ public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements
    @Override
    @Override
    public void close() {
    public void close() {
        mLocalBluetoothManager = null;
        mLocalBluetoothManager = null;
    }
        mLocalMediaManager = null;

    @Override
    public void onBluetoothStateChanged(int bluetoothState) {
        // To handle the case that Bluetooth on and no connected devices
        notifySliceChange();
    }

    @Override
    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
        if (bluetoothProfile == BluetoothProfile.A2DP ||
                bluetoothProfile == BluetoothProfile.HEARING_AID) {
            notifySliceChange();
        }
    }
    }


    @Override
    @Override
@@ -124,6 +138,36 @@ public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements
        }
        }
        return null;
        return null;
    }
    }

    @Override
    public void onDeviceListUpdate(List<MediaDevice> devices) {
        buildMediaDevices(devices);
        notifySliceChange();
    }

    private void buildMediaDevices(List<MediaDevice> devices) {
        mMediaDevices.clear();
        mMediaDevices.addAll(devices);
    }

    @Override
    public void onSelectedDeviceStateChanged(MediaDevice device, int state) {
        notifySliceChange();
    }

    @Override
    public void onDeviceAttributesChanged() {
        notifySliceChange();
    }

    Collection<MediaDevice> getMediaDevices() {
        return mMediaDevices;
    }

    MediaDevice getCurrentConnectedMediaDevice() {
        return mLocalMediaManager.getCurrentConnectedDevice();
    }

    private class DevicesChangedBroadcastReceiver extends BroadcastReceiver {
    private class DevicesChangedBroadcastReceiver extends BroadcastReceiver {
        @Override
        @Override
        public void onReceive(Context context, Intent intent) {
        public void onReceive(Context context, Intent intent) {
+4 −6
Original line number Original line Diff line number Diff line
@@ -22,7 +22,6 @@ import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_SLICE_URI;
import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_SLICE_URI;


import android.app.PendingIntent;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Color;
@@ -246,11 +245,10 @@ public class MediaOutputSlice implements CustomSliceable {
        // Return true if
        // Return true if
        // 1. AudioMode is not in on-going call
        // 1. AudioMode is not in on-going call
        // 2. worker is not null
        // 2. worker is not null
        // 3. Bluetooth is enabled
        // 3. Available devices are more than 1
        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
        return getWorker() != null

        return adapter.isEnabled()
                && !com.android.settingslib.Utils.isAudioModeOngoingCall(mContext)
                && !com.android.settingslib.Utils.isAudioModeOngoingCall(mContext)
                && getWorker() != null;
                && getWorker().getMediaDevices().size() > 1;

    }
    }
}
}
+50 −81
Original line number Original line Diff line number Diff line
@@ -26,11 +26,9 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSession;
@@ -46,14 +44,12 @@ import androidx.slice.widget.SliceLiveData;
import com.android.settings.R;
import com.android.settings.R;
import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.media.MediaOutputSliceConstants;
import com.android.settingslib.media.MediaOutputSliceConstants;


import org.junit.Before;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentCaptor;
@@ -73,31 +69,28 @@ import java.util.List;
        MediaOutputIndicatorSliceTest.ShadowSliceBackgroundWorker.class})
        MediaOutputIndicatorSliceTest.ShadowSliceBackgroundWorker.class})
public class MediaOutputIndicatorSliceTest {
public class MediaOutputIndicatorSliceTest {


    private static final String TEST_A2DP_DEVICE_NAME = "Test_A2DP_BT_Device_NAME";
    private static final String TEST_DEVICE_1_NAME = "test_device_1_name";
    private static final String TEST_HAP_DEVICE_NAME = "Test_HAP_BT_Device_NAME";
    private static final String TEST_DEVICE_2_NAME = "test_device_2_name";
    private static final String TEST_A2DP_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
    private static final String TEST_HAP_DEVICE_ADDRESS = "00:B2:B2:B2:B2:B2";
    private static final String TEST_PACKAGE_NAME = "com.test";
    private static final String TEST_PACKAGE_NAME = "com.test";


    private static MediaOutputIndicatorWorker sMediaOutputIndicatorWorker;
    private static MediaOutputIndicatorWorker sMediaOutputIndicatorWorker;


    private final List<MediaDevice> mDevices = new ArrayList<>();

    @Mock
    @Mock
    private A2dpProfile mA2dpProfile;
    private LocalBluetoothManager mLocalBluetoothManager;
    @Mock
    @Mock
    private HearingAidProfile mHearingAidProfile;
    private MediaController mMediaController;
    @Mock
    @Mock
    private LocalBluetoothManager mLocalBluetoothManager;
    private LocalMediaManager mLocalMediaManager;
    @Mock
    @Mock
    private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
    private MediaDevice mDevice1;
    @Mock
    @Mock
    private MediaController mMediaController;
    private MediaDevice mDevice2;
    @Mock
    private Drawable mTestDrawable;


    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothDevice mA2dpDevice;
    private BluetoothDevice mHapDevice;
    private BluetoothManager mBluetoothManager;
    private Context mContext;
    private Context mContext;
    private List<BluetoothDevice> mDevicesList;
    private MediaOutputIndicatorSlice mMediaOutputIndicatorSlice;
    private MediaOutputIndicatorSlice mMediaOutputIndicatorSlice;
    private AudioManager mAudioManager;
    private AudioManager mAudioManager;
    private MediaSession.Token mToken;
    private MediaSession.Token mToken;
@@ -107,7 +100,6 @@ public class MediaOutputIndicatorSliceTest {
        MockitoAnnotations.initMocks(this);
        MockitoAnnotations.initMocks(this);
        mContext = spy(RuntimeEnvironment.application);
        mContext = spy(RuntimeEnvironment.application);
        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        mAudioManager.setMode(AudioManager.MODE_NORMAL);
        sMediaOutputIndicatorWorker = spy(new MediaOutputIndicatorWorker(mContext,
        sMediaOutputIndicatorWorker = spy(new MediaOutputIndicatorWorker(mContext,
                MEDIA_OUTPUT_INDICATOR_SLICE_URI));
                MEDIA_OUTPUT_INDICATOR_SLICE_URI));
        mToken = new MediaSession.Token(Process.myUid(), null);
        mToken = new MediaSession.Token(Process.myUid(), null);
@@ -115,109 +107,86 @@ public class MediaOutputIndicatorSliceTest {
        SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
        SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
        // Setup Bluetooth environment
        // Setup Bluetooth environment
        ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
        ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
        mBluetoothManager = new BluetoothManager(mContext);
        // Setup mock devices
        mBluetoothAdapter = mBluetoothManager.getAdapter();
        when(mDevice1.getName()).thenReturn(TEST_DEVICE_1_NAME);
        when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
        when(mDevice1.getIcon()).thenReturn(mTestDrawable);
        when(mLocalBluetoothProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
        when(mDevice1.getMaxVolume()).thenReturn(100);
        when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
        when(mDevice1.isConnected()).thenReturn(true);

        when(mDevice2.getName()).thenReturn(TEST_DEVICE_2_NAME);
        // Setup A2dp device
        when(mDevice2.getIcon()).thenReturn(mTestDrawable);
        mA2dpDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_A2DP_DEVICE_ADDRESS));
        when(mDevice2.getMaxVolume()).thenReturn(100);
        when(mA2dpDevice.getName()).thenReturn(TEST_A2DP_DEVICE_NAME);
        when(mDevice2.isConnected()).thenReturn(false);
        when(mA2dpDevice.isConnected()).thenReturn(true);
        // Setup HearingAid device
        mHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_HAP_DEVICE_ADDRESS));
        when(mHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME);
        when(mHapDevice.isConnected()).thenReturn(true);


        mMediaOutputIndicatorSlice = new MediaOutputIndicatorSlice(mContext);
        mMediaOutputIndicatorSlice = new MediaOutputIndicatorSlice(mContext);
        mDevicesList = new ArrayList<>();
    }
    }


    @Test
    @Test
    public void getSlice_noConnectedDevice_returnErrorSlice() {
    public void getSlice_withConnectedDevice_verifyMetadata() {
        mDevicesList.clear();
        mDevices.add(mDevice1);
        when(mA2dpProfile.getConnectedDevices()).thenReturn(mDevicesList);
        mDevices.add(mDevice2);
        when(sMediaOutputIndicatorWorker.getMediaDevices()).thenReturn(mDevices);
        doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
        mAudioManager.setMode(AudioManager.MODE_NORMAL);


        final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
        final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
        assertThat(metadata.isErrorSlice()).isTrue();
    }


    @Test
    public void getSlice_noActiveDevice_verifyDefaultName() {
        mDevicesList.add(mA2dpDevice);
        when(mA2dpProfile.getConnectedDevices()).thenReturn(mDevicesList);
        when(mA2dpProfile.getActiveDevice()).thenReturn(null);

        // Verify slice title and subtitle
        final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
        assertThat(metadata.getTitle()).isEqualTo(mContext.getText(R.string.media_output_title));
        assertThat(metadata.getTitle()).isEqualTo(mContext.getText(R.string.media_output_title));
        assertThat(metadata.getSubtitle()).isEqualTo(mContext.getText(
        assertThat(metadata.getSubtitle()).isEqualTo(TEST_DEVICE_1_NAME);
                R.string.media_output_default_summary));
        assertThat(metadata.isErrorSlice()).isFalse();
        assertThat(metadata.isErrorSlice()).isFalse();
    }
    }


    @Test
    @Test
    @Ignore
    public void getSlice_noConnectedDevice_returnErrorSlice() {
    public void getSlice_A2dpDeviceActive_verifyName() {
        mDevices.clear();
        mDevicesList.add(mA2dpDevice);
        when(sMediaOutputIndicatorWorker.getMediaDevices()).thenReturn(mDevices);
        when(mA2dpProfile.getConnectedDevices()).thenReturn(mDevicesList);
        mAudioManager.setMode(AudioManager.MODE_NORMAL);
        when(mA2dpProfile.getActiveDevice()).thenReturn(mA2dpDevice);


        final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
        final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
        assertThat(metadata.getTitle()).isEqualTo(mContext.getText(R.string.media_output_title));
        assertThat(metadata.getSubtitle()).isEqualTo(TEST_A2DP_DEVICE_NAME);
        assertThat(metadata.isErrorSlice()).isFalse();
    }

    @Test
    @Ignore
    public void getSlice_HADeviceActive_verifyName() {
        mDevicesList.add(mHapDevice);
        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mDevicesList);
        when(mHearingAidProfile.getActiveDevices()).thenReturn(mDevicesList);


        // Verify slice title and subtitle
        assertThat(metadata.isErrorSlice()).isTrue();
        final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
        assertThat(metadata.getTitle()).isEqualTo(mContext.getText(R.string.media_output_title));
        assertThat(metadata.getSubtitle()).isEqualTo(TEST_HAP_DEVICE_NAME);
        assertThat(metadata.isErrorSlice()).isFalse();
    }
    }


    @Test
    @Test
    public void getSlice_audioModeIsInCommunication_returnErrorSlice() {
    public void getSlice_audioModeIsInCommunication_returnErrorSlice() {
        mDevicesList.add(mA2dpDevice);
        mDevices.add(mDevice1);
        when(mA2dpProfile.getConnectedDevices()).thenReturn(mDevicesList);
        mDevices.add(mDevice2);
        when(sMediaOutputIndicatorWorker.getMediaDevices()).thenReturn(mDevices);
        doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
        mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
        mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);


        final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
        final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);

        assertThat(metadata.isErrorSlice()).isTrue();
        assertThat(metadata.isErrorSlice()).isTrue();
    }
    }


    @Test
    @Test
    public void getSlice_audioModeIsRingtone_returnErrorSlice() {
    public void getSlice_audioModeIsRingtone_returnErrorSlice() {
        mDevicesList.add(mA2dpDevice);
        mDevices.add(mDevice1);
        when(mA2dpProfile.getConnectedDevices()).thenReturn(mDevicesList);
        mDevices.add(mDevice2);
        when(sMediaOutputIndicatorWorker.getMediaDevices()).thenReturn(mDevices);
        doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
        mAudioManager.setMode(AudioManager.MODE_RINGTONE);
        mAudioManager.setMode(AudioManager.MODE_RINGTONE);


        final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
        final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);

        assertThat(metadata.isErrorSlice()).isTrue();
        assertThat(metadata.isErrorSlice()).isTrue();
    }
    }


    @Test
    @Test
    public void getSlice_audioModeIsInCall_returnErrorSlice() {
    public void getSlice_audioModeIsInCall_returnErrorSlice() {
        mDevicesList.add(mA2dpDevice);
        mDevices.add(mDevice1);
        when(mA2dpProfile.getConnectedDevices()).thenReturn(mDevicesList);
        mDevices.add(mDevice2);
        when(sMediaOutputIndicatorWorker.getMediaDevices()).thenReturn(mDevices);
        doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
        mAudioManager.setMode(AudioManager.MODE_IN_CALL);


        final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
        final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);

        assertThat(metadata.isErrorSlice()).isTrue();
        assertThat(metadata.isErrorSlice()).isTrue();
    }
    }


+19 −3
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


@@ -38,9 +39,11 @@ import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.media.session.PlaybackState;
import android.net.Uri;
import android.net.Uri;


import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.LocalMediaManager;


import org.junit.Before;
import org.junit.Before;
import org.junit.Test;
import org.junit.Test;
@@ -56,7 +59,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.List;


@RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothUtils.class})
@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class})
public class MediaOutputIndicatorWorkerTest {
public class MediaOutputIndicatorWorkerTest {
    private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
    private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");


@@ -68,6 +71,8 @@ public class MediaOutputIndicatorWorkerTest {
    private MediaSessionManager mMediaSessionManager;
    private MediaSessionManager mMediaSessionManager;
    @Mock
    @Mock
    private MediaController mMediaController;
    private MediaController mMediaController;
    @Mock
    private LocalMediaManager mLocalMediaManager;


    private Context mContext;
    private Context mContext;
    private MediaOutputIndicatorWorker mMediaOutputIndicatorWorker;
    private MediaOutputIndicatorWorker mMediaOutputIndicatorWorker;
@@ -95,29 +100,40 @@ public class MediaOutputIndicatorWorkerTest {


    @Test
    @Test
    public void onSlicePinned_registerCallback() {
    public void onSlicePinned_registerCallback() {
        mMediaOutputIndicatorWorker.mLocalMediaManager = mLocalMediaManager;
        mMediaOutputIndicatorWorker.onSlicePinned();
        mMediaOutputIndicatorWorker.onSlicePinned();

        verify(mBluetoothEventManager).registerCallback(mMediaOutputIndicatorWorker);
        verify(mBluetoothEventManager).registerCallback(mMediaOutputIndicatorWorker);
        verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
        verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
        verify(mLocalMediaManager).registerCallback(mMediaOutputIndicatorWorker);
        verify(mLocalMediaManager).startScan();
    }
    }


    @Test
    @Test
    public void onSliceUnpinned_unRegisterCallback() {
    public void onSliceUnpinned_unRegisterCallback() {
        mMediaOutputIndicatorWorker.mLocalMediaManager = mLocalMediaManager;
        mMediaOutputIndicatorWorker.onSlicePinned();
        mMediaOutputIndicatorWorker.onSlicePinned();
        mMediaOutputIndicatorWorker.onSliceUnpinned();
        mMediaOutputIndicatorWorker.onSliceUnpinned();

        verify(mBluetoothEventManager).unregisterCallback(mMediaOutputIndicatorWorker);
        verify(mBluetoothEventManager).unregisterCallback(mMediaOutputIndicatorWorker);
        verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
        verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
        verify(mLocalMediaManager).unregisterCallback(mMediaOutputIndicatorWorker);
        verify(mLocalMediaManager).stopScan();
    }
    }


    @Test
    @Test
    public void onReceive_shouldNotifyChange() {
    public void onReceive_shouldNotifyChange() {
        mMediaOutputIndicatorWorker.onSlicePinned();
        mMediaOutputIndicatorWorker.onSlicePinned();
        // onSlicePinned will registerCallback() and get first callback. Callback triggers this at
        // the first time.
        verify(mResolver, times(1)).notifyChange(URI, null);


        final Intent intent = new Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
        final Intent intent = new Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
        for (BroadcastReceiver receiver : mShadowApplication.getReceiversForIntent(intent)) {
        for (BroadcastReceiver receiver : mShadowApplication.getReceiversForIntent(intent)) {
            receiver.onReceive(mContext, intent);
            receiver.onReceive(mContext, intent);
        }
        }

        // Intent receiver triggers notifyChange() again
        verify(mResolver).notifyChange(URI, null);
        verify(mResolver, times(2)).notifyChange(URI, null /* observer */);
    }
    }


    @Test
    @Test