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

Commit 5f9f5852 authored by tim peng's avatar tim peng Committed by Android (Google) Code Review
Browse files

Merge "Add dynamic group operation in output switcher"

parents cde54f7b 56b8cbd9
Loading
Loading
Loading
Loading
+220 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.systemui.media.dialog;

import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SeekBar;

import androidx.annotation.NonNull;

import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;

import java.util.List;

/**
 * Adapter for media output dynamic group dialog.
 */
public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter {

    private static final String TAG = "MediaOutputGroupAdapter";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private final List<MediaDevice> mGroupMediaDevices;

    public MediaOutputGroupAdapter(MediaOutputController controller) {
        super(controller);
        mGroupMediaDevices = controller.getGroupMediaDevices();
    }

    @Override
    public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
            int viewType) {
        super.onCreateViewHolder(viewGroup, viewType);

        return new GroupViewHolder(mHolderView);
    }

    @Override
    public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) {
        // Add "Group"
        if (position == 0) {
            viewHolder.onBind(CUSTOMIZED_ITEM_GROUP, true /* topMargin */,
                    false /* bottomMargin */);
            return;
        }
        // Add available devices
        final int newPosition = position - 1;
        final int size = mGroupMediaDevices.size();
        if (newPosition < size) {
            viewHolder.onBind(mGroupMediaDevices.get(newPosition), false /* topMargin */,
                    newPosition == (size - 1) /* bottomMargin */);
            return;
        }
        if (DEBUG) {
            Log.d(TAG, "Incorrect position: " + position);
        }
    }

    @Override
    public int getItemCount() {
        // Require extra item for group volume operation
        return mGroupMediaDevices.size() + 1;
    }

    @Override
    CharSequence getItemTitle(MediaDevice device) {
        return super.getItemTitle(device);
    }

    class GroupViewHolder extends MediaDeviceBaseViewHolder {

        GroupViewHolder(View view) {
            super(view);
        }

        @Override
        void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
            super.onBind(device, topMargin, bottomMargin);
            mDivider.setVisibility(View.GONE);
            mAddIcon.setVisibility(View.GONE);
            mBottomDivider.setVisibility(View.GONE);
            mCheckBox.setVisibility(View.VISIBLE);
            mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
                onCheckBoxClicked(isChecked, device);
            });
            setTwoLineLayout(device, false /* bFocused */, true /* showSeekBar */,
                    false /* showProgressBar */, false /* showSubtitle*/);
            initSeekbar(device);
            final List<MediaDevice> selectedDevices = mController.getSelectedMediaDevice();
            if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
                mCheckBox.setButtonDrawable(R.drawable.ic_check_box);
                mCheckBox.setChecked(false);
                mCheckBox.setEnabled(true);
            } else if (isDeviceIncluded(selectedDevices, device)) {
                if (selectedDevices.size() == 1 || !isDeviceIncluded(
                        mController.getDeselectableMediaDevice(), device)) {
                    mCheckBox.setButtonDrawable(getDisabledCheckboxDrawable());
                    mCheckBox.setChecked(true);
                    mCheckBox.setEnabled(false);
                } else {
                    mCheckBox.setButtonDrawable(R.drawable.ic_check_box);
                    mCheckBox.setChecked(true);
                    mCheckBox.setEnabled(true);
                }
            }
        }

        @Override
        void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
            super.onBind(customizedItem, topMargin, bottomMargin);
            if (customizedItem == CUSTOMIZED_ITEM_GROUP) {
                setTwoLineLayout(mContext.getText(R.string.media_output_dialog_group),
                        true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */,
                        false /* showSubtitle*/);
                mTitleIcon.setImageDrawable(getSpeakerDrawable());
                mBottomDivider.setVisibility(View.VISIBLE);
                mCheckBox.setVisibility(View.GONE);
                mDivider.setVisibility(View.GONE);
                mAddIcon.setVisibility(View.GONE);
                initSessionSeekbar();
            }
        }

        private void onCheckBoxClicked(boolean isChecked, MediaDevice device) {
            if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
                mController.addDeviceToPlayMedia(device);
            } else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
                    device)) {
                mController.removeDeviceFromPlayMedia(device);
            }
        }

        private void initSessionSeekbar() {
            mSeekBar.setMax(mController.getSessionVolumeMax());
            mSeekBar.setMin(0);
            final int currentVolume = mController.getSessionVolume();
            if (mSeekBar.getProgress() != currentVolume) {
                mSeekBar.setProgress(currentVolume);
            }
            mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                    if (!fromUser) {
                        return;
                    }
                    mController.adjustSessionVolume(progress);
                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
                    mIsDragging = true;
                }

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                    mIsDragging = false;
                }
            });
        }

        private Drawable getDisabledCheckboxDrawable() {
            final Drawable drawable = mContext.getDrawable(R.drawable.ic_check_box_blue_24dp)
                    .mutate();
            final Bitmap checkbox = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                    drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
            final Canvas canvas = new Canvas(checkbox);
            TypedValue value = new TypedValue();
            mContext.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true);
            drawable.setAlpha((int) (value.getFloat() * 255));
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);

            return drawable;
        }

        private Drawable getSpeakerDrawable() {
            final Drawable drawable = mContext.getDrawable(R.drawable.ic_speaker_group_black_24dp)
                    .mutate();
            final ColorStateList list = mContext.getResources().getColorStateList(
                            R.color.advanced_icon_color, mContext.getTheme());
            drawable.setColorFilter(new PorterDuffColorFilter(list.getDefaultColor(),
                    PorterDuff.Mode.SRC_IN));
            return BluetoothUtils.buildAdvancedDrawable(mContext, drawable);
        }

        private boolean isDeviceIncluded(List<MediaDevice> deviceList, MediaDevice targetDevice) {
            for (MediaDevice device : deviceList) {
                if (TextUtils.equals(device.getId(), targetDevice.getId())) {
                    return true;
                }
            }
            return false;
        }
    }
}
+88 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.systemui.media.dialog;

import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;

import androidx.core.graphics.drawable.IconCompat;

import com.android.systemui.R;

/**
 * Dialog for media output group.
 */
public class MediaOutputGroupDialog extends MediaOutputBaseDialog {

    MediaOutputGroupDialog(Context context, boolean aboveStatusbar, MediaOutputController
            mediaOutputController) {
        super(context, mediaOutputController);
        mMediaOutputController.resetGroupMediaDevices();
        mAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
        if (!aboveStatusbar) {
            getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
        }
        show();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    int getHeaderIconRes() {
        return R.drawable.ic_arrow_back;
    }

    @Override
    IconCompat getHeaderIcon() {
        return null;
    }

    @Override
    int getHeaderIconSize() {
        return mContext.getResources().getDimensionPixelSize(
                    R.dimen.media_output_dialog_header_back_icon_size);
    }

    @Override
    CharSequence getHeaderText() {
        return mContext.getString(R.string.media_output_dialog_add_output);
    }

    @Override
    CharSequence getHeaderSubtitle() {
        final int size = mMediaOutputController.getSelectedMediaDevice().size();
        if (size == 1) {
            return mContext.getText(R.string.media_output_dialog_single_device);
        }
        return mContext.getString(R.string.media_output_dialog_multiple_devices, size);
    }

    @Override
    int getStopButtonVisibility() {
        return View.VISIBLE;
    }

    @Override
    void onHeaderIconClick() {
        mMediaOutputController.launchMediaOutputDialog();
    }
}
+248 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.systemui.media.dialog;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.graphics.drawable.Icon;
import android.testing.AndroidTestingRunner;
import android.view.View;
import android.widget.LinearLayout;

import androidx.core.graphics.drawable.IconCompat;
import androidx.test.filters.SmallTest;

import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

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

@SmallTest
@RunWith(AndroidTestingRunner.class)
public class MediaOutputGroupAdapterTest extends SysuiTestCase {

    private static final String TEST_DEVICE_NAME_1 = "test_device_name_1";
    private static final String TEST_DEVICE_NAME_2 = "test_device_name_2";
    private static final String TEST_DEVICE_ID_1 = "test_device_id_1";
    private static final String TEST_DEVICE_ID_2 = "test_device_id_2";
    private static final int TEST_VOLUME = 10;
    private static final int TEST_MAX_VOLUME = 50;

    // Mock
    private MediaOutputController mMediaOutputController = mock(MediaOutputController.class);
    private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
    private MediaDevice mMediaDevice2 = mock(MediaDevice.class);
    private Icon mIcon = mock(Icon.class);
    private IconCompat mIconCompat = mock(IconCompat.class);

    private MediaOutputGroupAdapter mGroupAdapter;
    private MediaOutputGroupAdapter.GroupViewHolder mGroupViewHolder;
    private List<MediaDevice> mGroupMediaDevices = new ArrayList<>();
    private List<MediaDevice> mSelectableMediaDevices = new ArrayList<>();
    private List<MediaDevice> mSelectedMediaDevices = new ArrayList<>();
    private List<MediaDevice> mDeselectableMediaDevices = new ArrayList<>();

    @Before
    public void setUp() {
        when(mMediaOutputController.getGroupMediaDevices()).thenReturn(mGroupMediaDevices);
        when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
        when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
        when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(mSelectableMediaDevices);
        when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mSelectedMediaDevices);
        when(mMediaOutputController.getDeselectableMediaDevice()).thenReturn(
                mDeselectableMediaDevices);
        when(mIconCompat.toIcon(mContext)).thenReturn(mIcon);
        when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1);
        when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1);
        when(mMediaDevice2.getName()).thenReturn(TEST_DEVICE_NAME_2);
        when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_ID_2);
        mGroupMediaDevices.add(mMediaDevice1);
        mGroupMediaDevices.add(mMediaDevice2);
        mSelectedMediaDevices.add(mMediaDevice1);
        mSelectableMediaDevices.add(mMediaDevice2);
        mDeselectableMediaDevices.add(mMediaDevice1);

        mGroupAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
        mGroupViewHolder = (MediaOutputGroupAdapter.GroupViewHolder) mGroupAdapter
                .onCreateViewHolder(new LinearLayout(mContext), 0);
    }

    @Test
    public void onBindViewHolder_verifyGroupItem() {
        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 0);

        assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(mContext.getText(
                R.string.media_output_dialog_group));
    }

    @Test
    public void onBindViewHolder_singleSelectedDevice_verifyView() {
        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);

        assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
        assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
        // Disabled checkBox
        assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isFalse();
    }

    @Test
    public void onBindViewHolder_multipleSelectedDevice_verifyView() {
        mSelectedMediaDevices.clear();
        mSelectedMediaDevices.add(mMediaDevice1);
        mSelectedMediaDevices.add(mMediaDevice2);
        mDeselectableMediaDevices.clear();
        mDeselectableMediaDevices.add(mMediaDevice1);
        mDeselectableMediaDevices.add(mMediaDevice2);
        mSelectableMediaDevices.clear();

        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);

        assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
        assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
        // Enabled checkBox
        assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isTrue();
    }

    @Test
    public void onBindViewHolder_notDeselectedDevice_verifyView() {
        mSelectedMediaDevices.clear();
        mSelectedMediaDevices.add(mMediaDevice1);
        mSelectedMediaDevices.add(mMediaDevice2);
        mDeselectableMediaDevices.clear();
        mDeselectableMediaDevices.add(mMediaDevice1);
        mSelectableMediaDevices.clear();

        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);

        assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
        assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
        // Disabled checkBox
        assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isFalse();
    }

    @Test
    public void onBindViewHolder_selectableDevice_verifyCheckBox() {
        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);

        assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
        assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
        assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mGroupViewHolder.mCheckBox.isChecked()).isFalse();
        // Enabled checkBox
        assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isTrue();
    }

    @Test
    public void onBindViewHolder_verifySessionVolume() {
        when(mMediaOutputController.getSessionVolume()).thenReturn(TEST_VOLUME);
        when(mMediaOutputController.getSessionVolumeMax()).thenReturn(TEST_MAX_VOLUME);

        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 0);

        assertThat(mGroupViewHolder.mSeekBar.getProgress()).isEqualTo(TEST_VOLUME);
        assertThat(mGroupViewHolder.mSeekBar.getMax()).isEqualTo(TEST_MAX_VOLUME);
    }

    @Test
    public void onBindViewHolder_verifyDeviceVolume() {
        when(mMediaDevice1.getCurrentVolume()).thenReturn(TEST_VOLUME);
        when(mMediaDevice1.getMaxVolume()).thenReturn(TEST_MAX_VOLUME);

        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);

        assertThat(mGroupViewHolder.mSeekBar.getProgress()).isEqualTo(TEST_VOLUME);
        assertThat(mGroupViewHolder.mSeekBar.getMax()).isEqualTo(TEST_MAX_VOLUME);
    }

    @Test
    public void clickSelectedDevice_verifyRemoveDeviceFromPlayMedia() {
        mSelectedMediaDevices.clear();
        mSelectedMediaDevices.add(mMediaDevice1);
        mSelectedMediaDevices.add(mMediaDevice2);
        mDeselectableMediaDevices.clear();
        mDeselectableMediaDevices.add(mMediaDevice1);
        mDeselectableMediaDevices.add(mMediaDevice2);
        mSelectableMediaDevices.clear();

        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
        mGroupViewHolder.mCheckBox.performClick();

        verify(mMediaOutputController).removeDeviceFromPlayMedia(mMediaDevice1);
    }

    @Test
    public void clickSelectabelDevice_verifyAddDeviceToPlayMedia() {
        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);

        mGroupViewHolder.mCheckBox.performClick();

        verify(mMediaOutputController).addDeviceToPlayMedia(mMediaDevice2);
    }
}
+104 −0

File added.

Preview size limit exceeded, changes collapsed.