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

Commit c9eab1d5 authored by Alex Shabalin's avatar Alex Shabalin
Browse files

Consolidate the end area rendering logic.

- Render the end area only once.
- Make sure that either checkbox or the icon is rendered, not both.

Flag: EXEMPT refactor
Bug: 387570618,390021955
Test: atest SystemUIGoogleRoboRNGTests:MediaOutputAdapterScreenshotTest,
    atest SystemUiRoboTests:MediaOutputAdapterTest
Change-Id: I1000adc3c122e100db26b839af2b8a6f921492e0
parent fbcae369
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -246,6 +246,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
        assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.GONE);
        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
    }
@@ -297,6 +298,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.VISIBLE);
    }

    @Test
@@ -312,12 +314,12 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
        assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE);
        assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.VISIBLE);
    }

    @Test
@@ -383,7 +385,9 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);

        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
        assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.GONE);
        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
        assertThat(mViewHolder.mContainerLayout.isFocusable()).isTrue();
+101 −59
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.widget.TextView;

import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.widget.CompoundButtonCompat;
import androidx.recyclerview.widget.RecyclerView;
@@ -184,6 +185,11 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
                                + "]");
            }

            boolean isDeviceGroup = false;
            GroupStatus groupStatus = null;
            OngoingSessionStatus ongoingSessionStatus = null;
            ConnectionState connectionState = ConnectionState.DISCONNECTED;

            if (mCurrentActivePosition == position) {
                mCurrentActivePosition = -1;
            }
@@ -194,11 +200,11 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
            if (mController.isAnyDeviceTransferring()) {
                if (device.getState() == MediaDeviceState.STATE_CONNECTING
                        && !mController.hasAdjustVolumeUserRestriction()) {
                    connectionState = ConnectionState.CONNECTING;
                    setUpDeviceIcon(device);
                    updateProgressBarColor();
                    setSingleLineLayout(device.getName(), false /* showSeekBar*/,
                            true /* showProgressBar */, false /* showCheckBox */,
                            false /* showEndTouchArea */);
                            true /* showProgressBar */);
                } else {
                    setUpDeviceIcon(device);
                    setSingleLineLayout(device.getName());
@@ -217,47 +223,38 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
                        && isDeviceIncluded(mController.getSelectedMediaDevice(), device)) {
                    if (!mediaItem.isFirstDeviceInGroup()) {
                        mItemLayout.setVisibility(View.GONE);
                        mEndTouchArea.setVisibility(View.GONE);
                        return;
                    } else {
                        isDeviceGroup = true;
                        String sessionName = mController.getSessionName().toString();
                        updateUnmutedVolumeIcon(null);
                        updateEndClickAreaWithIcon(
                                v -> {
                                    mShouldGroupSelectedMediaItems = false;
                                    notifyDataSetChanged();
                                },
                                R.drawable.media_output_item_expand_group,
                                R.string.accessibility_expand_group);
                        disableFocusPropertyForView(mContainerLayout);
                        setUpContentDescriptionForView(mSeekBar, mContext.getString(
                                R.string.accessibility_cast_name, sessionName));
                        setSingleLineLayout(sessionName, true /* showSeekBar */,
                                false /* showProgressBar */, false /* showCheckBox */,
                                true /* showEndTouchArea */);
                                false /* showProgressBar */);
                        initGroupSeekbar(isCurrentSeekbarInvisible);
                    }
                } else if (device.hasSubtext()) {
                    boolean isActiveWithOngoingSession =
                            device.hasOngoingSession() && (currentlyConnected || isSelected);
                    boolean isHost = device.isHostForOngoingSession()
                            && isActiveWithOngoingSession;
                    if (isActiveWithOngoingSession) {
                        mCurrentActivePosition = position;
                        updateUnmutedVolumeIcon(device);
                        mSubTitleText.setText(device.getSubtextString());
                        updateContentAlpha(DEVICE_CONNECTED_ALPHA);
                        updateEndClickAreaAsSessionEditing(device,
                                isHost ? R.drawable.media_output_status_edit_session
                                        : R.drawable.ic_sound_bars_anim);
                        ongoingSessionStatus = new OngoingSessionStatus(
                                device.isHostForOngoingSession());
                        setTwoLineLayout(device.getName() /* title */,
                                true /* showSeekBar */, false /* showProgressBar */,
                                true /* showSubtitle */, false /* showStatus */,
                                true /* showEndTouchArea */);
                                true /* showSubtitle */, false /* showStatus */);
                        connectionState = ConnectionState.CONNECTED;
                        initSeekbar(device, isCurrentSeekbarInvisible);
                    } else {
                        if (currentlyConnected) {
                            mCurrentActivePosition = position;
                            updateUnmutedVolumeIcon(device);
                            connectionState = ConnectionState.CONNECTED;
                        } else {
                            setUpDeviceIcon(device);
                        }
@@ -288,22 +285,22 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
                            false /* showProgressBar */, true /* showSubtitle */,
                            true /* showStatus */);
                } else if (device.getState() == MediaDeviceState.STATE_GROUPING) {
                    connectionState = ConnectionState.CONNECTING;
                    setUpDeviceIcon(device);
                    updateProgressBarColor();
                    setSingleLineLayout(device.getName(), false /* showSeekBar*/,
                            true /* showProgressBar */, false /* showCheckBox */,
                            false /* showEndTouchArea */);
                            true /* showProgressBar */);
                } else if (mController.getSelectedMediaDevice().size() > 1 && isSelected) {
                    // selected device in group
                    boolean showEndArea =
                            !Flags.enableOutputSwitcherSessionGrouping() || isDeselectable;
                    updateUnmutedVolumeIcon(device);
                    updateEndAreaForGroupCheckbox(device, true /* isSelected */, isDeselectable);
                    groupStatus = new GroupStatus(
                            true /* selected */,
                            isDeselectable /* deselectable */);
                    disableFocusPropertyForView(mContainerLayout);
                    setUpContentDescriptionForView(mSeekBar, device);
                    setSingleLineLayout(device.getName(), true /* showSeekBar */,
                            false /* showProgressBar */, true /* showCheckBox */,
                            showEndArea /* showEndTouchArea */);
                            false /* showProgressBar */);
                    connectionState = ConnectionState.CONNECTED;
                    initSeekbar(device, isCurrentSeekbarInvisible);
                } else if (!mController.hasAdjustVolumeUserRestriction()
                        && currentlyConnected) {
@@ -317,26 +314,25 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
                    } else if (device.hasOngoingSession()) {
                        mCurrentActivePosition = position;
                        updateUnmutedVolumeIcon(device);
                        updateEndClickAreaAsSessionEditing(device, device.isHostForOngoingSession()
                                ? R.drawable.media_output_status_edit_session
                                : R.drawable.ic_sound_bars_anim);
                        mEndClickIcon.setVisibility(View.VISIBLE);
                        ongoingSessionStatus = new OngoingSessionStatus(
                                device.isHostForOngoingSession());
                        setSingleLineLayout(device.getName(), true /* showSeekBar */,
                                false /* showProgressBar */, false /* showCheckBox */,
                                true /* showEndTouchArea */);
                                false /* showProgressBar */);
                        connectionState = ConnectionState.CONNECTED;
                        initSeekbar(device, isCurrentSeekbarInvisible);
                    } else if (mController.isCurrentConnectedDeviceRemote()
                            && !mController.getSelectableMediaDevice().isEmpty()) {
                        //If device is connected and there's other selectable devices, layout as
                        // one of selected devices.
                        updateUnmutedVolumeIcon(device);
                        updateEndAreaForGroupCheckbox(device, true /* isSelected */,
                                isDeselectable);
                        groupStatus = new GroupStatus(
                                true /* selected */,
                                isDeselectable /* isDeselectable */);
                        disableFocusPropertyForView(mContainerLayout);
                        setUpContentDescriptionForView(mSeekBar, device);
                        setSingleLineLayout(device.getName(), true /* showSeekBar */,
                                false /* showProgressBar */, true /* showCheckBox */,
                                true /* showEndTouchArea */);
                                false /* showProgressBar */);
                        connectionState = ConnectionState.CONNECTED;
                        initSeekbar(device, isCurrentSeekbarInvisible);
                    } else {
                        updateUnmutedVolumeIcon(device);
@@ -344,23 +340,21 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
                        setUpContentDescriptionForView(mSeekBar, device);
                        mCurrentActivePosition = position;
                        setSingleLineLayout(device.getName(), true /* showSeekBar */,
                                false /* showProgressBar */, false /* showCheckBox */,
                                false /* showEndTouchArea */);
                                false /* showProgressBar */);
                        connectionState = ConnectionState.CONNECTED;
                        initSeekbar(device, isCurrentSeekbarInvisible);
                    }
                } else if (isSelectable) {
                    //groupable device
                    setUpDeviceIcon(device);
                    updateEndAreaForGroupCheckbox(device, false /* isSelected */,
                            true /* isDeselectable */);
                    groupStatus = new GroupStatus(false /* selected */, true /* deselectable */);
                    if (!Flags.disableTransferWhenAppsDoNotSupport()
                            || isTransferable
                            || hasRouteListingPreferenceItem) {
                        updateFullItemClickListener(v -> onItemClick(v, device));
                    }
                    setSingleLineLayout(device.getName(), false /* showSeekBar */,
                            false /* showProgressBar */, true /* showCheckBox */,
                            true /* showEndTouchArea */);
                            false /* showProgressBar */);
                } else {
                    setUpDeviceIcon(device);
                    setSingleLineLayout(device.getName());
@@ -379,6 +373,44 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
                                    ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
                }
            }

            if (isDeviceGroup) {
                updateEndAreaForDeviceGroup();
            } else {
                updateEndArea(device, connectionState, groupStatus, ongoingSessionStatus);
            }
        }

        /** Renders the right side round pill button / checkbox. */
        private void updateEndArea(@NonNull MediaDevice device, ConnectionState connectionState,
                @Nullable GroupStatus groupStatus,
                @Nullable OngoingSessionStatus ongoingSessionStatus) {
            boolean showEndArea = false;
            boolean isCheckbox = false;
            // If both group status and the ongoing session status are present, only the ongoing
            // session controls are displayed. The current layout design doesn't allow both group
            // and ongoing session controls to be rendered simultaneously.
            if (ongoingSessionStatus != null && connectionState == ConnectionState.CONNECTED) {
                showEndArea = true;
                updateEndAreaForOngoingSession(device, ongoingSessionStatus.host());
            } else if (groupStatus != null && shouldShowGroupCheckbox(groupStatus)) {
                showEndArea = true;
                isCheckbox = true;
                updateEndAreaForGroupCheckBox(device, groupStatus);
            }
            updateEndAreaVisibility(showEndArea, isCheckbox);
        }

        private boolean shouldShowGroupCheckbox(@NonNull GroupStatus groupStatus) {
            if (Flags.enableOutputSwitcherSessionGrouping()) {
                return isGroupCheckboxEnabled(groupStatus);
            }
            return true;
        }

        private boolean isGroupCheckboxEnabled(@NonNull GroupStatus groupStatus) {
            boolean disabled = groupStatus.selected() && !groupStatus.deselectable();
            return !disabled;
        }

        public void setCheckBoxColor(CheckBox checkBox, int color) {
@@ -395,14 +427,26 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
            mStatusIcon.setAlpha(alphaValue);
        }

        private void updateEndClickAreaAsSessionEditing(MediaDevice device, @DrawableRes int id) {
            updateEndClickAreaWithIcon(
        private void updateEndAreaForDeviceGroup() {
            updateEndAreaWithIcon(
                    v -> {
                        mShouldGroupSelectedMediaItems = false;
                        notifyDataSetChanged();
                    },
                    R.drawable.media_output_item_expand_group,
                    R.string.accessibility_expand_group);
            updateEndAreaVisibility(true /* showEndArea */, false /* isCheckbox */);
        }

        private void updateEndAreaForOngoingSession(@NonNull MediaDevice device, boolean isHost) {
            updateEndAreaWithIcon(
                    v -> mController.tryToLaunchInAppRoutingIntent(device.getId(), v),
                    id,
                    isHost ? R.drawable.media_output_status_edit_session
                            : R.drawable.ic_sound_bars_anim,
                    R.string.accessibility_open_application);
        }

        private void updateEndClickAreaWithIcon(View.OnClickListener clickListener,
        private void updateEndAreaWithIcon(View.OnClickListener clickListener,
                @DrawableRes int iconDrawableId,
                @StringRes int accessibilityStringId) {
            updateEndAreaColor(mController.getColorSeekbarProgress());
@@ -454,22 +498,20 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
                    ColorStateList.valueOf(mController.getColorItemContent()));
        }

        public void updateEndAreaForGroupCheckbox(MediaDevice device, boolean isSelected,
                boolean isDeselectable) {
            mEndTouchArea.setOnClickListener(null);
        public void updateEndAreaForGroupCheckBox(@NonNull MediaDevice device,
                @NonNull GroupStatus groupStatus) {
            boolean isEnabled = isGroupCheckboxEnabled(groupStatus);
            mEndTouchArea.setOnClickListener(
                    isDeselectable ? (v) -> mCheckBox.performClick() : null);
            mEndTouchArea.setImportantForAccessibility(
                    View.IMPORTANT_FOR_ACCESSIBILITY_YES);
            updateEndAreaColor(isSelected ? mController.getColorSeekbarProgress()
                    isEnabled ? (v) -> mCheckBox.performClick() : null);
            mEndTouchArea.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
            updateEndAreaColor(groupStatus.selected() ? mController.getColorSeekbarProgress()
                    : mController.getColorItemBackground());
            setUpContentDescriptionForView(mEndTouchArea, device);
            mCheckBox.setOnCheckedChangeListener(null);
            mCheckBox.setChecked(isSelected);
            mCheckBox.setChecked(groupStatus.selected());
            mCheckBox.setOnCheckedChangeListener(
                    isDeselectable ? (buttonView, isChecked) -> onGroupActionTriggered(!isSelected,
                            device) : null);
            mCheckBox.setEnabled(isDeselectable);
                    isEnabled ? (buttonView, isChecked) -> onGroupActionTriggered(
                            !groupStatus.selected(), device) : null);
            mCheckBox.setEnabled(isEnabled);
            setCheckBoxColor(mCheckBox, mController.getColorItemContent());
        }

@@ -546,7 +588,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
            view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
        }

        private void setUpContentDescriptionForView(View view, MediaDevice device) {
        private void setUpContentDescriptionForView(View view, @NonNull MediaDevice device) {
            setUpContentDescriptionForView(
                    view,
                    mContext.getString(device.getDeviceType()
+22 −18
Original line number Diff line number Diff line
@@ -56,6 +56,16 @@ import java.util.List;
public abstract class MediaOutputBaseAdapter extends
        RecyclerView.Adapter<RecyclerView.ViewHolder> {

    record OngoingSessionStatus(boolean host) {}

    record GroupStatus(Boolean selected, Boolean deselectable) {}

    enum ConnectionState {
        CONNECTED,
        CONNECTING,
        DISCONNECTED,
    }

    protected final MediaSwitchingController mController;

    private static final int UNMUTE_DEFAULT_VOLUME = 2;
@@ -175,6 +185,7 @@ public abstract class MediaOutputBaseAdapter extends
            mCheckBox.setVisibility(View.GONE);
            mStatusIcon.setVisibility(View.GONE);
            mEndTouchArea.setVisibility(View.GONE);
            mEndClickIcon.setVisibility(View.GONE);
            mEndTouchArea.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
            mContainerLayout.setOnClickListener(null);
            mContainerLayout.setContentDescription(null);
@@ -187,11 +198,10 @@ public abstract class MediaOutputBaseAdapter extends
        }

        void setSingleLineLayout(CharSequence title) {
            setSingleLineLayout(title, false, false, false, false);
            setSingleLineLayout(title, false, false);
        }

        void setSingleLineLayout(CharSequence title, boolean showSeekBar,
                boolean showProgressBar, boolean showCheckBox, boolean showEndTouchArea) {
        void setSingleLineLayout(CharSequence title, boolean showSeekBar, boolean showProgressBar) {
            boolean isActive = showSeekBar || showProgressBar;
            if (!mCornerAnimator.isRunning()) {
                final Drawable backgroundDrawable =
@@ -216,22 +226,11 @@ public abstract class MediaOutputBaseAdapter extends
                mSeekBar.resetVolume();
            }
            mTitleText.setText(title);
            mCheckBox.setVisibility(showCheckBox ? View.VISIBLE : View.GONE);
            mEndTouchArea.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
            if (Flags.enableOutputSwitcherSessionGrouping()) {
                mEndClickIcon.setVisibility(
                        !showCheckBox && showEndTouchArea ? View.VISIBLE : View.GONE);
            }
        }

        void setTwoLineLayout(CharSequence title, boolean showSeekBar,
                boolean showProgressBar, boolean showSubtitle, boolean showStatus) {
            setTwoLineLayout(title, showSeekBar, showProgressBar, showSubtitle, showStatus, false);
        }

        void setTwoLineLayout(CharSequence title,
                boolean showSeekBar, boolean showProgressBar, boolean showSubtitle,
                boolean showStatus , boolean showEndTouchArea) {
                boolean showStatus) {
            mStatusIcon.setVisibility(showStatus ? View.VISIBLE : View.GONE);
            mSeekBar.setAlpha(1);
            mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
@@ -246,15 +245,20 @@ public abstract class MediaOutputBaseAdapter extends
            if (showSeekBar) {
                updateSeekbarProgressBackground();
            }
            //update end click area by isActive
            mEndTouchArea.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
            mEndClickIcon.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
            mItemLayout.setBackground(backgroundDrawable);
            mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
            mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
            mTitleText.setText(title);
        }

        protected void updateEndAreaVisibility(boolean showEndTouchArea, boolean isCheckbox) {
            mEndTouchArea.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
            if (showEndTouchArea) {
                mCheckBox.setVisibility(isCheckbox ? View.VISIBLE : View.GONE);
                mEndClickIcon.setVisibility(!isCheckbox ? View.VISIBLE : View.GONE);
            }
        }

        void updateSeekbarProgressBackground() {
            final ClipDrawable clipDrawable =
                    (ClipDrawable) ((LayerDrawable) mSeekBar.getProgressDrawable())