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

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

Change-Id: I5ab87cbd0dc822af5a915b80e1c14466237cd310
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Shaowei Shen
2022-12-12 15:14:27 +00:00
committed by Automerger Merge Worker
8 changed files with 552 additions and 28 deletions

View File

@@ -0,0 +1,35 @@
<?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>

View File

@@ -0,0 +1,97 @@
/*
* 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;
}
}

View File

@@ -24,9 +24,11 @@ import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.widget.CompoundButtonCompat; import androidx.core.widget.CompoundButtonCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settingslib.media.LocalMediaManager.MediaDeviceState; import com.android.settingslib.media.LocalMediaManager.MediaDeviceState;
import com.android.settingslib.media.MediaDevice; import com.android.settingslib.media.MediaDevice;
@@ -48,29 +50,68 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
} }
@Override @Override
public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
int viewType) { int viewType) {
super.onCreateViewHolder(viewGroup, viewType); super.onCreateViewHolder(viewGroup, viewType);
return new MediaDeviceViewHolder(mHolderView); 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 @Override
public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) { public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
final int size = mController.getMediaDevices().size(); if (mController.isAdvancedLayoutSupported()) {
if (position == size) { MediaItem currentMediaItem = mController.getMediaItemList().get(position);
viewHolder.onBind(CUSTOMIZED_ITEM_PAIR_NEW, false /* topMargin */, switch (currentMediaItem.getMediaItemType()) {
true /* bottomMargin */); case MediaItem.MediaItemType.TYPE_GROUP_DIVIDER:
} else if (position < size) { ((MediaGroupDividerViewHolder) viewHolder).onBind(currentMediaItem.getTitle());
viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position), break;
position == 0 /* topMargin */, position == (size - 1) /* bottomMargin */, case MediaItem.MediaItemType.TYPE_PAIR_NEW_DEVICE:
position); ((MediaDeviceViewHolder) viewHolder).onBind(CUSTOMIZED_ITEM_PAIR_NEW);
} else if (DEBUG) { break;
Log.d(TAG, "Incorrect position: " + position); 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) {
((MediaDeviceViewHolder) viewHolder).onBind(CUSTOMIZED_ITEM_PAIR_NEW);
} else if (position < size) {
((MediaDeviceViewHolder) viewHolder).onBind(
((List<MediaDevice>) (mController.getMediaDevices())).get(position),
position);
} else if (DEBUG) {
Log.d(TAG, "Incorrect position: " + position);
}
} }
} }
@Override @Override
public long getItemId(int position) { 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(); final int size = mController.getMediaDevices().size();
if (position == size) { if (position == size) {
return -1; return -1;
@@ -83,10 +124,19 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
return position; return position;
} }
@Override
public int getItemViewType(int position) {
return mController.isAdvancedLayoutSupported()
? mController.getMediaItemList().get(position).getMediaItemType()
: super.getItemViewType(position);
}
@Override @Override
public int getItemCount() { public int getItemCount() {
// Add extra one for "pair new" // 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 { class MediaDeviceViewHolder extends MediaDeviceBaseViewHolder {
@@ -96,8 +146,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
} }
@Override @Override
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) { void onBind(MediaDevice device, int position) {
super.onBind(device, topMargin, bottomMargin, position); super.onBind(device, position);
boolean isMutingExpectedDeviceExist = mController.hasMutingExpectedDevice(); boolean isMutingExpectedDeviceExist = mController.hasMutingExpectedDevice();
final boolean currentlyConnected = isCurrentlyConnected(device); final boolean currentlyConnected = isCurrentlyConnected(device);
boolean isCurrentSeekbarInvisible = mSeekBar.getVisibility() == View.GONE; boolean isCurrentSeekbarInvisible = mSeekBar.getVisibility() == View.GONE;
@@ -261,7 +311,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
} }
@Override @Override
void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) { void onBind(int customizedItem) {
if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) { if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
mTitleText.setTextColor(mController.getColorItemContent()); mTitleText.setTextColor(mController.getColorItemContent());
mCheckBox.setVisibility(View.GONE); mCheckBox.setVisibility(View.GONE);
@@ -318,4 +368,18 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
: R.string.accessibility_cast_name, device.getName())); : 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);
}
}
} }

View File

@@ -58,7 +58,7 @@ import java.util.List;
* Base adapter for media output dialog. * Base adapter for media output dialog.
*/ */
public abstract class MediaOutputBaseAdapter extends 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_PAIR_NEW = 1;
static final int CUSTOMIZED_ITEM_GROUP = 2; static final int CUSTOMIZED_ITEM_GROUP = 2;
@@ -80,11 +80,12 @@ public abstract class MediaOutputBaseAdapter extends
} }
@Override @Override
public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
int viewType) { int viewType) {
mContext = viewGroup.getContext(); mContext = viewGroup.getContext();
mHolderView = LayoutInflater.from(mContext).inflate( 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); : R.layout.media_output_list_item, viewGroup, false);
return null; return null;
@@ -175,7 +176,7 @@ public abstract class MediaOutputBaseAdapter extends
initAnimator(); initAnimator();
} }
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) { void onBind(MediaDevice device, int position) {
mDeviceId = device.getId(); mDeviceId = device.getId();
mCheckBox.setVisibility(View.GONE); mCheckBox.setVisibility(View.GONE);
mStatusIcon.setVisibility(View.GONE); mStatusIcon.setVisibility(View.GONE);
@@ -196,7 +197,7 @@ public abstract class MediaOutputBaseAdapter extends
PorterDuff.Mode.SRC_IN)); PorterDuff.Mode.SRC_IN));
} }
abstract void onBind(int customizedItem, boolean topMargin, boolean bottomMargin); abstract void onBind(int customizedItem);
void setSingleLineLayout(CharSequence title) { void setSingleLineLayout(CharSequence title) {
setSingleLineLayout(title, false, false, false, false); setSingleLineLayout(title, false, false, false, false);

View File

@@ -93,6 +93,7 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
@@ -121,6 +122,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
@VisibleForTesting @VisibleForTesting
final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>(); final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
final List<MediaDevice> mCachedMediaDevices = new CopyOnWriteArrayList<>(); final List<MediaDevice> mCachedMediaDevices = new CopyOnWriteArrayList<>();
private final List<MediaItem> mMediaItemList = new CopyOnWriteArrayList<>();
private final AudioManager mAudioManager; private final AudioManager mAudioManager;
private final PowerExemptionManager mPowerExemptionManager; private final PowerExemptionManager mPowerExemptionManager;
private final KeyguardManager mKeyGuardManager; private final KeyguardManager mKeyGuardManager;
@@ -212,6 +214,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
synchronized (mMediaDevicesLock) { synchronized (mMediaDevicesLock) {
mCachedMediaDevices.clear(); mCachedMediaDevices.clear();
mMediaDevices.clear(); mMediaDevices.clear();
mMediaItemList.clear();
} }
mNearbyDeviceInfoMap.clear(); mNearbyDeviceInfoMap.clear();
if (mNearbyMediaDevicesManager != null) { if (mNearbyMediaDevicesManager != null) {
@@ -262,6 +265,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
synchronized (mMediaDevicesLock) { synchronized (mMediaDevicesLock) {
mCachedMediaDevices.clear(); mCachedMediaDevices.clear();
mMediaDevices.clear(); mMediaDevices.clear();
mMediaItemList.clear();
} }
if (mNearbyMediaDevicesManager != null) { if (mNearbyMediaDevicesManager != null) {
mNearbyMediaDevicesManager.unregisterNearbyDevicesCallback(this); mNearbyMediaDevicesManager.unregisterNearbyDevicesCallback(this);
@@ -287,7 +291,11 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
public void onSelectedDeviceStateChanged(MediaDevice device, public void onSelectedDeviceStateChanged(MediaDevice device,
@LocalMediaManager.MediaDeviceState int state) { @LocalMediaManager.MediaDeviceState int state) {
mCallback.onRouteChanged(); mCallback.onRouteChanged();
mMetricLogger.logOutputSuccess(device.toString(), new ArrayList<>(mMediaDevices)); if (isAdvancedLayoutSupported()) {
mMetricLogger.logOutputItemSuccess(device.toString(), new ArrayList<>(mMediaItemList));
} else {
mMetricLogger.logOutputSuccess(device.toString(), new ArrayList<>(mMediaDevices));
}
} }
@Override @Override
@@ -298,7 +306,11 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
@Override @Override
public void onRequestFailed(int reason) { public void onRequestFailed(int reason) {
mCallback.onRouteChanged(); mCallback.onRouteChanged();
mMetricLogger.logOutputFailure(new ArrayList<>(mMediaDevices), reason); if (isAdvancedLayoutSupported()) {
mMetricLogger.logOutputItemFailure(new ArrayList<>(mMediaItemList), reason);
} else {
mMetricLogger.logOutputFailure(new ArrayList<>(mMediaDevices), reason);
}
} }
/** /**
@@ -318,6 +330,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
try { try {
synchronized (mMediaDevicesLock) { synchronized (mMediaDevicesLock) {
mMediaDevices.removeIf(MediaDevice::isMutingExpectedDevice); mMediaDevices.removeIf(MediaDevice::isMutingExpectedDevice);
mMediaItemList.removeIf((MediaItem::isMutingExpectedDevice));
} }
mAudioManager.cancelMuteAwaitConnection(mAudioManager.getMutingExpectedDevice()); mAudioManager.cancelMuteAwaitConnection(mAudioManager.getMutingExpectedDevice());
} catch (Exception e) { } catch (Exception e) {
@@ -547,6 +560,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
} }
private void buildMediaDevices(List<MediaDevice> devices) { private void buildMediaDevices(List<MediaDevice> devices) {
if (isAdvancedLayoutSupported()) {
buildMediaItems(devices);
} else {
buildDefaultMediaDevices(devices);
}
}
private void buildDefaultMediaDevices(List<MediaDevice> devices) {
synchronized (mMediaDevicesLock) { synchronized (mMediaDevicesLock) {
attachRangeInfo(devices); attachRangeInfo(devices);
Collections.sort(devices, Comparator.naturalOrder()); 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) { private void attachRangeInfo(List<MediaDevice> devices) {
for (MediaDevice mediaDevice : devices) { for (MediaDevice mediaDevice : devices) {
if (mNearbyDeviceInfoMap.containsKey(mediaDevice.getId())) { if (mNearbyDeviceInfoMap.containsKey(mediaDevice.getId())) {
@@ -670,6 +766,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
return mMediaDevices; return mMediaDevices;
} }
public List<MediaItem> getMediaItemList() {
return mMediaItemList;
}
MediaDevice getCurrentConnectedMediaDevice() { MediaDevice getCurrentConnectedMediaDevice() {
return mLocalMediaManager.getCurrentConnectedDevice(); return mLocalMediaManager.getCurrentConnectedDevice();
} }
@@ -749,9 +849,19 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
boolean isAnyDeviceTransferring() { boolean isAnyDeviceTransferring() {
synchronized (mMediaDevicesLock) { synchronized (mMediaDevicesLock) {
for (MediaDevice device : mMediaDevices) { if (isAdvancedLayoutSupported()) {
if (device.getState() == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) { for (MediaItem mediaItem : mMediaItemList) {
return true; 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;
}
} }
} }
} }

View File

@@ -95,6 +95,31 @@ public class MediaOutputMetricLogger {
mAppliedDeviceCountWithinRemoteGroup); mAppliedDeviceCountWithinRemoteGroup);
} }
/**
* Do the metric logging of content switching success.
* @param selectedDeviceType string representation of the target media device
* @param deviceItemList media item list for device count updating
*/
public void logOutputItemSuccess(String selectedDeviceType, List<MediaItem> deviceItemList) {
if (DEBUG) {
Log.d(TAG, "logOutputSuccess - selected device: " + selectedDeviceType);
}
updateLoggingMediaItemCount(deviceItemList);
SysUiStatsLog.write(
SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED,
getLoggingDeviceType(mSourceDevice, true),
getLoggingDeviceType(mTargetDevice, false),
SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__OK,
SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NO_ERROR,
getLoggingPackageName(),
mWiredDeviceCount,
mConnectedBluetoothDeviceCount,
mRemoteDeviceCount,
mAppliedDeviceCountWithinRemoteGroup);
}
/** /**
* Do the metric logging of volume adjustment. * Do the metric logging of volume adjustment.
* @param source the device been adjusted * @param source the device been adjusted
@@ -166,6 +191,31 @@ public class MediaOutputMetricLogger {
mAppliedDeviceCountWithinRemoteGroup); mAppliedDeviceCountWithinRemoteGroup);
} }
/**
* Do the metric logging of content switching failure.
* @param deviceItemList media item list for device count updating
* @param reason the reason of content switching failure
*/
public void logOutputItemFailure(List<MediaItem> deviceItemList, int reason) {
if (DEBUG) {
Log.e(TAG, "logRequestFailed - " + reason);
}
updateLoggingMediaItemCount(deviceItemList);
SysUiStatsLog.write(
SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED,
getLoggingDeviceType(mSourceDevice, true),
getLoggingDeviceType(mTargetDevice, false),
SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__ERROR,
getLoggingSwitchOpSubResult(reason),
getLoggingPackageName(),
mWiredDeviceCount,
mConnectedBluetoothDeviceCount,
mRemoteDeviceCount,
mAppliedDeviceCountWithinRemoteGroup);
}
private void updateLoggingDeviceCount(List<MediaDevice> deviceList) { private void updateLoggingDeviceCount(List<MediaDevice> deviceList) {
mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0; mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0;
mAppliedDeviceCountWithinRemoteGroup = 0; mAppliedDeviceCountWithinRemoteGroup = 0;
@@ -196,6 +246,37 @@ public class MediaOutputMetricLogger {
} }
} }
private void updateLoggingMediaItemCount(List<MediaItem> deviceItemList) {
mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0;
mAppliedDeviceCountWithinRemoteGroup = 0;
for (MediaItem mediaItem: deviceItemList) {
if (mediaItem.getMediaDevice().isPresent()
&& mediaItem.getMediaDevice().get().isConnected()) {
switch (mediaItem.getMediaDevice().get().getDeviceType()) {
case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE:
case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE:
mWiredDeviceCount++;
break;
case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE:
mConnectedBluetoothDeviceCount++;
break;
case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE:
case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE:
mRemoteDeviceCount++;
break;
default:
}
}
}
if (DEBUG) {
Log.d(TAG, "connected devices:" + " wired: " + mWiredDeviceCount
+ " bluetooth: " + mConnectedBluetoothDeviceCount
+ " remote: " + mRemoteDeviceCount);
}
}
private int getLoggingDeviceType(MediaDevice device, boolean isSourceDevice) { private int getLoggingDeviceType(MediaDevice device, boolean isSourceDevice) {
if (device == null) { if (device == null) {
return isSourceDevice return isSourceDevice

View File

@@ -46,6 +46,7 @@ import org.junit.runner.RunWith;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
@@ -71,10 +72,13 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
private MediaOutputAdapter mMediaOutputAdapter; private MediaOutputAdapter mMediaOutputAdapter;
private MediaOutputAdapter.MediaDeviceViewHolder mViewHolder; private MediaOutputAdapter.MediaDeviceViewHolder mViewHolder;
private List<MediaDevice> mMediaDevices = new ArrayList<>(); private List<MediaDevice> mMediaDevices = new ArrayList<>();
private List<MediaItem> mMediaItems = new ArrayList<>();
MediaOutputSeekbar mSpyMediaOutputSeekbar; MediaOutputSeekbar mSpyMediaOutputSeekbar;
@Before @Before
public void setUp() { public void setUp() {
when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(false);
when(mMediaOutputController.getMediaItemList()).thenReturn(mMediaItems);
when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices); when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false); when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(false); when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(false);
@@ -82,7 +86,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat); when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
when(mMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(mMediaDevice1); when(mMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(mMediaDevice1);
when(mMediaOutputController.isActiveRemoteDevice(mMediaDevice1)).thenReturn(true); when(mMediaOutputController.isActiveRemoteDevice(mMediaDevice1)).thenReturn(true);
when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(false);
when(mIconCompat.toIcon(mContext)).thenReturn(mIcon); when(mIconCompat.toIcon(mContext)).thenReturn(mIcon);
when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1); when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1);
when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1); when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1);
@@ -94,6 +97,8 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED); LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
mMediaDevices.add(mMediaDevice1); mMediaDevices.add(mMediaDevice1);
mMediaDevices.add(mMediaDevice2); mMediaDevices.add(mMediaDevice2);
mMediaItems.add(new MediaItem(mMediaDevice1));
mMediaItems.add(new MediaItem(mMediaDevice2));
mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController); mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
@@ -118,6 +123,26 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mMediaOutputAdapter.getItemId(invalidPosition)).isEqualTo(invalidPosition); assertThat(mMediaOutputAdapter.getItemId(invalidPosition)).isEqualTo(invalidPosition);
} }
@Test
public void advanced_getItemCount_returnsMediaItemSize() {
when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaItems.size());
}
@Test
public void advanced_getItemId_validPosition_returnCorrespondingId() {
when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
assertThat(mMediaOutputAdapter.getItemId(0)).isEqualTo(mMediaItems.get(
0).getMediaDevice().get().getId().hashCode());
}
@Test
public void advanced_getItemId_invalidPosition_returnPosition() {
when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
int invalidPosition = mMediaItems.size() + 1;
assertThat(mMediaOutputAdapter.getItemId(invalidPosition)).isEqualTo(invalidPosition);
}
@Test @Test
public void onBindViewHolder_bindPairNew_verifyView() { public void onBindViewHolder_bindPairNew_verifyView() {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
@@ -158,6 +183,63 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
} }
@Test
public void advanced_onBindViewHolder_bindPairNew_verifyView() {
when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
.onCreateViewHolder(new LinearLayout(mContext), 0);
mMediaItems.add(new MediaItem());
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText(
R.string.media_output_dialog_pairing_new));
}
@Test
public void advanced_onBindViewHolder_bindGroup_withSessionName_verifyView() {
when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(
mMediaItems.stream().map((item) -> item.getMediaDevice().get()).collect(
Collectors.toList()));
when(mMediaOutputController.getSessionName()).thenReturn(TEST_SESSION_NAME);
mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
.onCreateViewHolder(new LinearLayout(mContext), 0);
mMediaOutputAdapter.getItemCount();
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
public void advanced_onBindViewHolder_bindGroup_noSessionName_verifyView() {
when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(
mMediaItems.stream().map((item) -> item.getMediaDevice().get()).collect(
Collectors.toList()));
when(mMediaOutputController.getSessionName()).thenReturn(null);
mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
.onCreateViewHolder(new LinearLayout(mContext), 0);
mMediaOutputAdapter.getItemCount();
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test @Test
public void onBindViewHolder_bindConnectedDevice_verifyView() { public void onBindViewHolder_bindConnectedDevice_verifyView() {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
@@ -375,6 +457,19 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
verify(mMediaOutputController).launchBluetoothPairing(mViewHolder.mContainerLayout); verify(mMediaOutputController).launchBluetoothPairing(mViewHolder.mContainerLayout);
} }
@Test
public void advanced_onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
.onCreateViewHolder(new LinearLayout(mContext), 0);
mMediaItems.add(new MediaItem());
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
mViewHolder.mContainerLayout.performClick();
verify(mMediaOutputController).launchBluetoothPairing(mViewHolder.mContainerLayout);
}
@Test @Test
public void onItemClick_clickDevice_verifyConnectDevice() { public void onItemClick_clickDevice_verifyConnectDevice() {
assertThat(mMediaDevice2.getState()).isEqualTo( assertThat(mMediaDevice2.getState()).isEqualTo(
@@ -457,6 +552,26 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse(); assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
} }
@Test
public void advanced_onItemClick_onGroupActionTriggered_verifySeekbarDisabled() {
when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(
mMediaItems.stream().map((item) -> item.getMediaDevice().get()).collect(
Collectors.toList()));
mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
.onCreateViewHolder(new LinearLayout(mContext), 0);
List<MediaDevice> selectableDevices = new ArrayList<>();
selectableDevices.add(mMediaDevice1);
when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices);
when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(true);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
mViewHolder.mContainerLayout.performClick();
assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
}
@Test @Test
public void onBindViewHolder_volumeControlChangeToEnabled_enableSeekbarAgain() { public void onBindViewHolder_volumeControlChangeToEnabled_enableSeekbarAgain() {
when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(false); when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(false);

View File

@@ -63,6 +63,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager; import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -144,6 +145,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
mNotifCollection, mDialogLaunchAnimator, mNotifCollection, mDialogLaunchAnimator,
Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager, Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags); mKeyguardManager, mFlags);
when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(false);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager); mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager; mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
MediaDescription.Builder builder = new MediaDescription.Builder(); MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -281,6 +283,25 @@ public class MediaOutputControllerTest extends SysuiTestCase {
verify(mCb).onDeviceListChanged(); verify(mCb).onDeviceListChanged();
} }
@Test
public void advanced_onDeviceListUpdate_verifyDeviceListCallback() {
when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true);
mMediaOutputController.start(mCb);
reset(mCb);
mMediaOutputController.onDeviceListUpdate(mMediaDevices);
final List<MediaDevice> devices = new ArrayList<>();
for (MediaItem item : mMediaOutputController.getMediaItemList()) {
if (item.getMediaDevice().isPresent()) {
devices.add(item.getMediaDevice().get());
}
}
assertThat(devices.containsAll(mMediaDevices)).isTrue();
assertThat(devices.size()).isEqualTo(mMediaDevices.size());
verify(mCb).onDeviceListChanged();
}
@Test @Test
public void onDeviceListUpdate_isRefreshing_updatesNeedRefreshToTrue() { public void onDeviceListUpdate_isRefreshing_updatesNeedRefreshToTrue() {
mMediaOutputController.start(mCb); mMediaOutputController.start(mCb);