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

Commit a6eb5b7b authored by Shaowei Shen's avatar Shaowei Shen Committed by Automerger Merge Worker
Browse files

Merge "[Output Switcher] [Group by Type] Add MediaItem to store data" into...

Merge "[Output Switcher] [Group by Type] Add MediaItem to store data" into tm-qpr-dev am: f537e319 am: 36bb7c31

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20641148



Change-Id: I5f74a6a255b0c49c33598b9141a47607cd918c4a
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 78dfbc18 36bb7c31
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2022 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.
  -->

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/device_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="36dp"
        android:layout_gravity="center_vertical|start"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="56dp"
        android:ellipsize="end"
        android:maxLines="1"
        android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
        android:textSize="16sp"/>
</LinearLayout>
 No newline at end of file
+97 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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 androidx.annotation.IntDef;

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

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Optional;

/**
 * MediaItem represents an item in OutputSwitcher list (could be a MediaDevice, group divider or
 * connect new device item).
 */
public class MediaItem {
    private final Optional<MediaDevice> mMediaDeviceOptional;
    private final String mTitle;
    @MediaItemType
    private final int mMediaItemType;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({
            MediaItemType.TYPE_DEVICE,
            MediaItemType.TYPE_GROUP_DIVIDER,
            MediaItemType.TYPE_PAIR_NEW_DEVICE})
    public @interface MediaItemType {
        int TYPE_DEVICE = 0;
        int TYPE_GROUP_DIVIDER = 1;
        int TYPE_PAIR_NEW_DEVICE = 2;
    }

    public MediaItem() {
        this.mMediaDeviceOptional = Optional.empty();
        this.mTitle = null;
        this.mMediaItemType = MediaItemType.TYPE_PAIR_NEW_DEVICE;
    }

    public MediaItem(String title, int mediaItemType) {
        this.mMediaDeviceOptional = Optional.empty();
        this.mTitle = title;
        this.mMediaItemType = mediaItemType;
    }

    public MediaItem(MediaDevice mediaDevice) {
        this.mMediaDeviceOptional = Optional.of(mediaDevice);
        this.mTitle = mediaDevice.getName();
        this.mMediaItemType = MediaItemType.TYPE_DEVICE;
    }

    public Optional<MediaDevice> getMediaDevice() {
        return mMediaDeviceOptional;
    }

    /**
     * Get layout id based on media item Type.
     */
    public static int getMediaLayoutId(int mediaItemType) {
        switch (mediaItemType) {
            case MediaItemType.TYPE_DEVICE:
            case MediaItemType.TYPE_PAIR_NEW_DEVICE:
                return R.layout.media_output_list_item_advanced;
            case MediaItemType.TYPE_GROUP_DIVIDER:
            default:
                return R.layout.media_output_list_group_divider;
        }
    }

    public String getTitle() {
        return mTitle;
    }

    public boolean isMutingExpectedDevice() {
        return mMediaDeviceOptional.isPresent()
                && mMediaDeviceOptional.get().isMutingExpectedDevice();
    }

    public int getMediaItemType() {
        return mMediaItemType;
    }
}
+81 −17
Original line number Diff line number Diff line
@@ -24,9 +24,11 @@ import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.core.widget.CompoundButtonCompat;
import androidx.recyclerview.widget.RecyclerView;

import com.android.settingslib.media.LocalMediaManager.MediaDeviceState;
import com.android.settingslib.media.MediaDevice;
@@ -48,29 +50,68 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
    }

    @Override
    public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
            int viewType) {
        super.onCreateViewHolder(viewGroup, viewType);
        if (mController.isAdvancedLayoutSupported()) {
            switch (viewType) {
                case MediaItem.MediaItemType.TYPE_GROUP_DIVIDER:
                    return new MediaGroupDividerViewHolder(mHolderView);
                case MediaItem.MediaItemType.TYPE_PAIR_NEW_DEVICE:
                case MediaItem.MediaItemType.TYPE_DEVICE:
                default:
                    return new MediaDeviceViewHolder(mHolderView);
            }
        } else {
            return new MediaDeviceViewHolder(mHolderView);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) {
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
        if (mController.isAdvancedLayoutSupported()) {
            MediaItem currentMediaItem = mController.getMediaItemList().get(position);
            switch (currentMediaItem.getMediaItemType()) {
                case MediaItem.MediaItemType.TYPE_GROUP_DIVIDER:
                    ((MediaGroupDividerViewHolder) viewHolder).onBind(currentMediaItem.getTitle());
                    break;
                case MediaItem.MediaItemType.TYPE_PAIR_NEW_DEVICE:
                    ((MediaDeviceViewHolder) viewHolder).onBind(CUSTOMIZED_ITEM_PAIR_NEW);
                    break;
                case MediaItem.MediaItemType.TYPE_DEVICE:
                    ((MediaDeviceViewHolder) viewHolder).onBind(
                            currentMediaItem.getMediaDevice().get(),
                            position);
                    break;
                default:
                    Log.d(TAG, "Incorrect position: " + position);
            }
        } else {
            final int size = mController.getMediaDevices().size();
            if (position == size) {
            viewHolder.onBind(CUSTOMIZED_ITEM_PAIR_NEW, false /* topMargin */,
                    true /* bottomMargin */);
                ((MediaDeviceViewHolder) viewHolder).onBind(CUSTOMIZED_ITEM_PAIR_NEW);
            } else if (position < size) {
            viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position),
                    position == 0 /* topMargin */, position == (size - 1) /* bottomMargin */,
                ((MediaDeviceViewHolder) viewHolder).onBind(
                        ((List<MediaDevice>) (mController.getMediaDevices())).get(position),
                        position);
            } else if (DEBUG) {
                Log.d(TAG, "Incorrect position: " + position);
            }
        }
    }

    @Override
    public long getItemId(int position) {
        if (mController.isAdvancedLayoutSupported()) {
            if (position >= mController.getMediaItemList().size()) {
                Log.d(TAG, "Incorrect position for item id: " + position);
                return position;
            }
            MediaItem currentMediaItem = mController.getMediaItemList().get(position);
            return currentMediaItem.getMediaDevice().isPresent()
                    ? currentMediaItem.getMediaDevice().get().getId().hashCode()
                    : position;
        }
        final int size = mController.getMediaDevices().size();
        if (position == size) {
            return -1;
@@ -83,10 +124,19 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
        return position;
    }

    @Override
    public int getItemViewType(int position) {
        return mController.isAdvancedLayoutSupported()
                ? mController.getMediaItemList().get(position).getMediaItemType()
                : super.getItemViewType(position);
    }

    @Override
    public int getItemCount() {
        // Add extra one for "pair new"
        return mController.getMediaDevices().size() + 1;
        return mController.isAdvancedLayoutSupported()
                ? mController.getMediaItemList().size()
                : mController.getMediaDevices().size() + 1;
    }

    class MediaDeviceViewHolder extends MediaDeviceBaseViewHolder {
@@ -96,8 +146,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
        }

        @Override
        void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
            super.onBind(device, topMargin, bottomMargin, position);
        void onBind(MediaDevice device, int position) {
            super.onBind(device, position);
            boolean isMutingExpectedDeviceExist = mController.hasMutingExpectedDevice();
            final boolean currentlyConnected = isCurrentlyConnected(device);
            boolean isCurrentSeekbarInvisible = mSeekBar.getVisibility() == View.GONE;
@@ -261,7 +311,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
        }

        @Override
        void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
        void onBind(int customizedItem) {
            if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
                mTitleText.setTextColor(mController.getColorItemContent());
                mCheckBox.setVisibility(View.GONE);
@@ -318,4 +368,18 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
                            : R.string.accessibility_cast_name, device.getName()));
        }
    }

    class MediaGroupDividerViewHolder extends RecyclerView.ViewHolder {
        final TextView mTitleText;

        MediaGroupDividerViewHolder(@NonNull View itemView) {
            super(itemView);
            mTitleText = itemView.requireViewById(R.id.title);
        }

        void onBind(String groupDividerTitle) {
            mTitleText.setTextColor(mController.getColorItemContent());
            mTitleText.setText(groupDividerTitle);
        }
    }
}
+6 −5
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ import java.util.List;
 * Base adapter for media output dialog.
 */
public abstract class MediaOutputBaseAdapter extends
        RecyclerView.Adapter<MediaOutputBaseAdapter.MediaDeviceBaseViewHolder> {
        RecyclerView.Adapter<RecyclerView.ViewHolder> {

    static final int CUSTOMIZED_ITEM_PAIR_NEW = 1;
    static final int CUSTOMIZED_ITEM_GROUP = 2;
@@ -80,11 +80,12 @@ public abstract class MediaOutputBaseAdapter extends
    }

    @Override
    public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
            int viewType) {
        mContext = viewGroup.getContext();
        mHolderView = LayoutInflater.from(mContext).inflate(
                mController.isAdvancedLayoutSupported() ? R.layout.media_output_list_item_advanced
                mController.isAdvancedLayoutSupported() ? MediaItem.getMediaLayoutId(
                        viewType) /*R.layout.media_output_list_item_advanced*/
                        : R.layout.media_output_list_item, viewGroup, false);

        return null;
@@ -175,7 +176,7 @@ public abstract class MediaOutputBaseAdapter extends
            initAnimator();
        }

        void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
        void onBind(MediaDevice device, int position) {
            mDeviceId = device.getId();
            mCheckBox.setVisibility(View.GONE);
            mStatusIcon.setVisibility(View.GONE);
@@ -196,7 +197,7 @@ public abstract class MediaOutputBaseAdapter extends
                            PorterDuff.Mode.SRC_IN));
        }

        abstract void onBind(int customizedItem, boolean topMargin, boolean bottomMargin);
        abstract void onBind(int customizedItem);

        void setSingleLineLayout(CharSequence title) {
            setSingleLineLayout(title, false, false, false, false);
+115 −5
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

import javax.inject.Inject;

@@ -121,6 +122,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
    @VisibleForTesting
    final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
    final List<MediaDevice> mCachedMediaDevices = new CopyOnWriteArrayList<>();
    private final List<MediaItem> mMediaItemList = new CopyOnWriteArrayList<>();
    private final AudioManager mAudioManager;
    private final PowerExemptionManager mPowerExemptionManager;
    private final KeyguardManager mKeyGuardManager;
@@ -212,6 +214,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
        synchronized (mMediaDevicesLock) {
            mCachedMediaDevices.clear();
            mMediaDevices.clear();
            mMediaItemList.clear();
        }
        mNearbyDeviceInfoMap.clear();
        if (mNearbyMediaDevicesManager != null) {
@@ -262,6 +265,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
        synchronized (mMediaDevicesLock) {
            mCachedMediaDevices.clear();
            mMediaDevices.clear();
            mMediaItemList.clear();
        }
        if (mNearbyMediaDevicesManager != null) {
            mNearbyMediaDevicesManager.unregisterNearbyDevicesCallback(this);
@@ -287,8 +291,12 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
    public void onSelectedDeviceStateChanged(MediaDevice device,
            @LocalMediaManager.MediaDeviceState int state) {
        mCallback.onRouteChanged();
        if (isAdvancedLayoutSupported()) {
            mMetricLogger.logOutputItemSuccess(device.toString(), new ArrayList<>(mMediaItemList));
        } else {
            mMetricLogger.logOutputSuccess(device.toString(), new ArrayList<>(mMediaDevices));
        }
    }

    @Override
    public void onDeviceAttributesChanged() {
@@ -298,8 +306,12 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
    @Override
    public void onRequestFailed(int reason) {
        mCallback.onRouteChanged();
        if (isAdvancedLayoutSupported()) {
            mMetricLogger.logOutputItemFailure(new ArrayList<>(mMediaItemList), reason);
        } else {
            mMetricLogger.logOutputFailure(new ArrayList<>(mMediaDevices), reason);
        }
    }

    /**
     * Checks if there's any muting expected device exist
@@ -318,6 +330,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
        try {
            synchronized (mMediaDevicesLock) {
                mMediaDevices.removeIf(MediaDevice::isMutingExpectedDevice);
                mMediaItemList.removeIf((MediaItem::isMutingExpectedDevice));
            }
            mAudioManager.cancelMuteAwaitConnection(mAudioManager.getMutingExpectedDevice());
        } catch (Exception e) {
@@ -547,6 +560,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
    }

    private void buildMediaDevices(List<MediaDevice> devices) {
        if (isAdvancedLayoutSupported()) {
            buildMediaItems(devices);
        } else {
            buildDefaultMediaDevices(devices);
        }
    }

    private void buildDefaultMediaDevices(List<MediaDevice> devices) {
        synchronized (mMediaDevicesLock) {
            attachRangeInfo(devices);
            Collections.sort(devices, Comparator.naturalOrder());
@@ -603,6 +624,81 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
        }
    }

    private void buildMediaItems(List<MediaDevice> devices) {
        synchronized (mMediaDevicesLock) {
            //TODO(b/257851968): do the organization only when there's no suggested sorted order
            // we get from application
            attachRangeInfo(devices);
            Collections.sort(devices, Comparator.naturalOrder());
            // For the first time building list, to make sure the top device is the connected
            // device.
            if (mMediaItemList.isEmpty()) {
                boolean needToHandleMutingExpectedDevice =
                        hasMutingExpectedDevice() && !isCurrentConnectedDeviceRemote();
                final MediaDevice connectedMediaDevice =
                        needToHandleMutingExpectedDevice ? null
                                : getCurrentConnectedMediaDevice();
                if (connectedMediaDevice == null) {
                    if (DEBUG) {
                        Log.d(TAG, "No connected media device or muting expected device exist.");
                    }
                    if (needToHandleMutingExpectedDevice) {
                        for (MediaDevice device : devices) {
                            if (device.isMutingExpectedDevice()) {
                                mMediaItemList.add(0, new MediaItem(device));
                            } else {
                                mMediaItemList.add(new MediaItem(device));
                            }
                        }
                    } else {
                        mMediaItemList.addAll(
                                devices.stream().map(MediaItem::new).collect(Collectors.toList()));
                    }

                    categorizeMediaItems();
                    return;
                }
                // selected device exist
                for (MediaDevice device : devices) {
                    if (TextUtils.equals(device.getId(), connectedMediaDevice.getId())) {
                        mMediaItemList.add(0, new MediaItem(device));
                    } else {
                        mMediaItemList.add(new MediaItem(device));
                    }
                }
                categorizeMediaItems();
                return;
            }
            // To keep the same list order
            final List<MediaDevice> targetMediaDevices = new ArrayList<>();
            for (MediaItem originalMediaItem : mMediaItemList) {
                for (MediaDevice newDevice : devices) {
                    if (originalMediaItem.getMediaDevice().isPresent()
                            && TextUtils.equals(originalMediaItem.getMediaDevice().get().getId(),
                            newDevice.getId())) {
                        targetMediaDevices.add(newDevice);
                        break;
                    }
                }
            }
            if (targetMediaDevices.size() != devices.size()) {
                devices.removeAll(targetMediaDevices);
                targetMediaDevices.addAll(devices);
            }
            mMediaItemList.clear();
            mMediaItemList.addAll(
                    targetMediaDevices.stream().map(MediaItem::new).collect(Collectors.toList()));
            categorizeMediaItems();
        }
    }

    private void categorizeMediaItems() {
        synchronized (mMediaDevicesLock) {
            //TODO(255124239): do the categorization here
            mMediaItemList.add(new MediaItem());
        }
    }

    private void attachRangeInfo(List<MediaDevice> devices) {
        for (MediaDevice mediaDevice : devices) {
            if (mNearbyDeviceInfoMap.containsKey(mediaDevice.getId())) {
@@ -670,6 +766,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
        return mMediaDevices;
    }

    public List<MediaItem> getMediaItemList() {
        return mMediaItemList;
    }

    MediaDevice getCurrentConnectedMediaDevice() {
        return mLocalMediaManager.getCurrentConnectedDevice();
    }
@@ -749,12 +849,22 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,

    boolean isAnyDeviceTransferring() {
        synchronized (mMediaDevicesLock) {
            if (isAdvancedLayoutSupported()) {
                for (MediaItem mediaItem : mMediaItemList) {
                    if (mediaItem.getMediaDevice().isPresent()
                            && mediaItem.getMediaDevice().get().getState()
                            == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) {
                        return true;
                    }
                }
            } else {
                for (MediaDevice device : mMediaDevices) {
                    if (device.getState() == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

Loading