Loading packages/SystemUI/res/layout/media_output_list_group_divider.xml 0 → 100644 +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 packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java 0 → 100644 +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; } } packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +81 −17 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 { Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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); } } } packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +6 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading @@ -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); Loading packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +115 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -212,6 +214,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, synchronized (mMediaDevicesLock) { mCachedMediaDevices.clear(); mMediaDevices.clear(); mMediaItemList.clear(); } mNearbyDeviceInfoMap.clear(); if (mNearbyMediaDevicesManager != null) { Loading Loading @@ -262,6 +265,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, synchronized (mMediaDevicesLock) { mCachedMediaDevices.clear(); mMediaDevices.clear(); mMediaItemList.clear(); } if (mNearbyMediaDevicesManager != null) { mNearbyMediaDevicesManager.unregisterNearbyDevicesCallback(this); Loading @@ -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() { Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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()); Loading Loading @@ -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())) { Loading Loading @@ -670,6 +766,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, return mMediaDevices; } public List<MediaItem> getMediaItemList() { return mMediaItemList; } MediaDevice getCurrentConnectedMediaDevice() { return mLocalMediaManager.getCurrentConnectedDevice(); } Loading Loading @@ -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 Loading
packages/SystemUI/res/layout/media_output_list_group_divider.xml 0 → 100644 +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
packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java 0 → 100644 +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; } }
packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +81 −17 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 { Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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); } } }
packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +6 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading @@ -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); Loading
packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +115 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -212,6 +214,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, synchronized (mMediaDevicesLock) { mCachedMediaDevices.clear(); mMediaDevices.clear(); mMediaItemList.clear(); } mNearbyDeviceInfoMap.clear(); if (mNearbyMediaDevicesManager != null) { Loading Loading @@ -262,6 +265,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, synchronized (mMediaDevicesLock) { mCachedMediaDevices.clear(); mMediaDevices.clear(); mMediaItemList.clear(); } if (mNearbyMediaDevicesManager != null) { mNearbyMediaDevicesManager.unregisterNearbyDevicesCallback(this); Loading @@ -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() { Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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()); Loading Loading @@ -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())) { Loading Loading @@ -670,6 +766,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, return mMediaDevices; } public List<MediaItem> getMediaItemList() { return mMediaItemList; } MediaDevice getCurrentConnectedMediaDevice() { return mLocalMediaManager.getCurrentConnectedDevice(); } Loading Loading @@ -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