Enhancement for group operation in output switcher

-Add new group components in xml layout and assets
-Adjust view objects in adapter for new layout
-Add aboveStatusbar in MediaOutputControllter to keep the dialog style
when switching
-Add getGroupMediaDevices() in MediaOutputController to provide available
device list for group operation
-Update test cases

Bug: 155822415
Test: atest MediaOutputAdapterTest MediaOutputControllerTest MediaOutputBaseDialogTest MediaOutputDialogTest
Merged-In: If74bf2efb89a8ece61f4e0cf13e4dcfa30cb0a8a
Change-Id: If74bf2efb89a8ece61f4e0cf13e4dcfa30cb0a8a
This commit is contained in:
timhypeng
2020-10-19 09:26:30 +08:00
committed by tim peng
parent 07e1fa7bcc
commit 3de24ae2c5
14 changed files with 479 additions and 136 deletions

View File

@@ -0,0 +1,26 @@
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/checked"
android:state_checked="true"
android:drawable="@drawable/ic_check_box_blue_24dp" />
<item
android:id="@+id/unchecked"
android:state_checked="false"
android:drawable="@drawable/ic_check_box_outline_24dp" />
</selector>

View File

@@ -0,0 +1,26 @@
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"
android:fillColor="#4285F4"/>
</vector>

View File

@@ -0,0 +1,26 @@
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"
android:fillColor="#757575"/>
</vector>

View File

@@ -0,0 +1,31 @@
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
android:fillColor="#000000"/>
<path
android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
android:fillColor="#000000"/>
<path
android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
android:fillColor="#000000"/>
</vector>

View File

@@ -15,97 +15,124 @@
~ limitations under the License. ~ limitations under the License.
--> -->
<FrameLayout <LinearLayout
android:id="@+id/device_container"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/device_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="64dp"> android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout <FrameLayout
android:layout_width="36dp" android:layout_width="match_parent"
android:layout_height="36dp" android:layout_height="64dp">
android:layout_gravity="center_vertical"
android:layout_marginStart="16dp">
<ImageView
android:id="@+id/title_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
<TextView <FrameLayout
android:id="@+id/title" android:layout_width="36dp"
android:layout_width="wrap_content" android:layout_height="36dp"
android:layout_height="wrap_content" android:layout_gravity="center_vertical"
android:layout_gravity="center_vertical" android:layout_marginStart="16dp">
android:layout_marginStart="68dp" <ImageView
android:ellipsize="end" android:id="@+id/title_icon"
android:maxLines="1" android:layout_width="wrap_content"
android:textColor="?android:attr/textColorPrimary" android:layout_height="wrap_content"
android:textSize="14sp"/> android:layout_gravity="center"/>
</FrameLayout>
<RelativeLayout
android:id="@+id/two_line_layout"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="52dp"
android:layout_marginEnd="69dp"
android:layout_marginTop="10dp">
<TextView <TextView
android:id="@+id/two_line_title" android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_gravity="center_vertical"
android:layout_marginEnd="15dp" android:layout_marginStart="68dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp"/> android:textSize="14sp"/>
<TextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="15dp"
android:layout_marginBottom="7dp"
android:layout_alignParentBottom="true"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorSecondary"
android:textSize="12sp"
android:fontFamily="roboto-regular"
android:visibility="gone"/>
<SeekBar
android:id="@+id/volume_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
<ProgressBar <RelativeLayout
android:id="@+id/volume_indeterminate_progress" android:id="@+id/two_line_layout"
style="@*android:style/Widget.Material.ProgressBar.Horizontal" android:layout_width="wrap_content"
android:layout_width="258dp" android:layout_height="48dp"
android:layout_height="18dp" android:layout_marginStart="52dp"
android:layout_marginStart="68dp" android:layout_marginEnd="69dp"
android:layout_marginTop="40dp" android:layout_marginTop="10dp">
android:indeterminate="true" <TextView
android:indeterminateOnly="true" android:id="@+id/two_line_title"
android:visibility="gone"/> android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="15dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp"/>
<TextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="15dp"
android:layout_marginBottom="7dp"
android:layout_alignParentBottom="true"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorSecondary"
android:textSize="12sp"
android:fontFamily="roboto-regular"
android:visibility="gone"/>
<SeekBar
android:id="@+id/volume_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
<ProgressBar
android:id="@+id/volume_indeterminate_progress"
style="@*android:style/Widget.Material.ProgressBar.Horizontal"
android:layout_width="258dp"
android:layout_height="18dp"
android:layout_marginStart="68dp"
android:layout_marginTop="40dp"
android:indeterminate="true"
android:indeterminateOnly="true"
android:visibility="gone"/>
<View
android:id="@+id/end_divider"
android:layout_width="1dp"
android:layout_height="36dp"
android:layout_marginEnd="68dp"
android:layout_gravity="right|center_vertical"
android:background="?android:attr/listDivider"
android:visibility="gone"/>
<ImageView
android:id="@+id/add_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="right|center_vertical"
android:layout_marginEnd="24dp"
android:src="@drawable/ic_add"
android:tint="?android:attr/colorAccent"
android:visibility="gone"/>
<CheckBox
android:id="@+id/check_box"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="right|center_vertical"
android:layout_marginEnd="24dp"
android:button="@drawable/ic_check_box"
android:visibility="gone"/>
</FrameLayout>
<View <View
android:layout_width="1dp" android:id="@+id/bottom_divider"
android:layout_height="36dp" android:layout_width="match_parent"
android:layout_marginEnd="68dp" android:layout_height="1dp"
android:layout_gravity="right|center_vertical" android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:layout_gravity="bottom"
android:background="?android:attr/listDivider" android:background="?android:attr/listDivider"
android:visibility="gone"/> android:visibility="gone"/>
</LinearLayout>
<ImageView
android:id="@+id/end_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="right|center_vertical"
android:layout_marginEnd="24dp"
android:visibility="gone"/>
</FrameLayout>

View File

@@ -109,34 +109,45 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
super.onBind(device, topMargin, bottomMargin); super.onBind(device, topMargin, bottomMargin);
final boolean currentlyConnected = isCurrentlyConnected(device); final boolean currentlyConnected = isCurrentlyConnected(device);
if (currentlyConnected) { if (currentlyConnected) {
mConnectedItem = mFrameLayout; mConnectedItem = mContainerLayout;
}
mBottomDivider.setVisibility(View.GONE);
mCheckBox.setVisibility(View.GONE);
if (currentlyConnected && mController.isActiveRemoteDevice(device)) {
// Init active device layout
mDivider.setVisibility(View.VISIBLE);
mDivider.setTransitionAlpha(1);
mAddIcon.setVisibility(View.VISIBLE);
mAddIcon.setTransitionAlpha(1);
mAddIcon.setOnClickListener(v -> onEndItemClick());
} else {
// Init non-active device layout
mDivider.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
} }
if (mController.isTransferring()) { if (mController.isTransferring()) {
if (device.getState() == MediaDeviceState.STATE_CONNECTING if (device.getState() == MediaDeviceState.STATE_CONNECTING
&& !mController.hasAdjustVolumeUserRestriction()) { && !mController.hasAdjustVolumeUserRestriction()) {
setTwoLineLayout(device, null /* title */, true /* bFocused */, setTwoLineLayout(device, true /* bFocused */, false /* showSeekBar*/,
false /* showSeekBar*/, true /* showProgressBar */, true /* showProgressBar */, false /* showSubtitle */);
false /* showSubtitle */);
} else { } else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */); setSingleLineLayout(getItemTitle(device), false /* bFocused */);
} }
} else { } else {
// Set different layout for each device // Set different layout for each device
if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) { if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
setTwoLineLayout(device, null /* title */, false /* bFocused */, setTwoLineLayout(device, false /* bFocused */,
false /* showSeekBar*/, false /* showProgressBar */, false /* showSeekBar */, false /* showProgressBar */,
true /* showSubtitle */); true /* showSubtitle */);
mSubTitleText.setText(R.string.media_output_dialog_connect_failed); mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
mFrameLayout.setOnClickListener(v -> onItemClick(v, device)); mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
} else if (!mController.hasAdjustVolumeUserRestriction() } else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) {
&& currentlyConnected) { setTwoLineLayout(device, true /* bFocused */, true /* showSeekBar */,
setTwoLineLayout(device, null /* title */, true /* bFocused */, false /* showProgressBar */, false /* showSubtitle */);
true /* showSeekBar*/, false /* showProgressBar */,
false /* showSubtitle */);
initSeekbar(device); initSeekbar(device);
} else { } else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */); setSingleLineLayout(getItemTitle(device), false /* bFocused */);
mFrameLayout.setOnClickListener(v -> onItemClick(v, device)); mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
} }
} }
} }
@@ -145,13 +156,17 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) { void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
super.onBind(customizedItem, topMargin, bottomMargin); super.onBind(customizedItem, topMargin, bottomMargin);
if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) { if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
mCheckBox.setVisibility(View.GONE);
mDivider.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
mBottomDivider.setVisibility(View.GONE);
setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new), setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
false /* bFocused */); false /* bFocused */);
final Drawable d = mContext.getDrawable(R.drawable.ic_add); final Drawable d = mContext.getDrawable(R.drawable.ic_add);
d.setColorFilter(new PorterDuffColorFilter( d.setColorFilter(new PorterDuffColorFilter(
Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN)); Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN));
mTitleIcon.setImageDrawable(d); mTitleIcon.setImageDrawable(d);
mFrameLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW)); mContainerLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
} }
} }
@@ -173,5 +188,9 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mController.launchBluetoothPairing(); mController.launchBluetoothPairing();
} }
} }
private void onEndItemClick() {
mController.launchMediaOutputGroupDialog();
}
} }
} }

View File

@@ -24,8 +24,9 @@ import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.CheckBox;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.SeekBar; import android.widget.SeekBar;
@@ -44,19 +45,17 @@ import com.android.systemui.R;
public abstract class MediaOutputBaseAdapter extends public abstract class MediaOutputBaseAdapter extends
RecyclerView.Adapter<MediaOutputBaseAdapter.MediaDeviceBaseViewHolder> { RecyclerView.Adapter<MediaOutputBaseAdapter.MediaDeviceBaseViewHolder> {
private static final String FONT_SELECTED_TITLE = "sans-serif-medium";
private static final String FONT_TITLE = "sans-serif";
static final int CUSTOMIZED_ITEM_PAIR_NEW = 1; static final int CUSTOMIZED_ITEM_PAIR_NEW = 1;
static final int CUSTOMIZED_ITEM_GROUP = 2;
final MediaOutputController mController; final MediaOutputController mController;
private boolean mIsDragging;
private int mMargin; private int mMargin;
private boolean mIsAnimating; private boolean mIsAnimating;
Context mContext; Context mContext;
View mHolderView; View mHolderView;
boolean mIsDragging;
public MediaOutputBaseAdapter(MediaOutputController controller) { public MediaOutputBaseAdapter(MediaOutputController controller) {
mController = controller; mController = controller;
@@ -99,27 +98,33 @@ public abstract class MediaOutputBaseAdapter extends
private static final int ANIM_DURATION = 200; private static final int ANIM_DURATION = 200;
final FrameLayout mFrameLayout; final LinearLayout mContainerLayout;
final TextView mTitleText; final TextView mTitleText;
final TextView mTwoLineTitleText; final TextView mTwoLineTitleText;
final TextView mSubTitleText; final TextView mSubTitleText;
final ImageView mTitleIcon; final ImageView mTitleIcon;
final ImageView mEndIcon; final ImageView mAddIcon;
final ProgressBar mProgressBar; final ProgressBar mProgressBar;
final SeekBar mSeekBar; final SeekBar mSeekBar;
final RelativeLayout mTwoLineLayout; final RelativeLayout mTwoLineLayout;
final View mDivider;
final View mBottomDivider;
final CheckBox mCheckBox;
MediaDeviceBaseViewHolder(View view) { MediaDeviceBaseViewHolder(View view) {
super(view); super(view);
mFrameLayout = view.requireViewById(R.id.device_container); mContainerLayout = view.requireViewById(R.id.device_container);
mTitleText = view.requireViewById(R.id.title); mTitleText = view.requireViewById(R.id.title);
mSubTitleText = view.requireViewById(R.id.subtitle); mSubTitleText = view.requireViewById(R.id.subtitle);
mTwoLineLayout = view.requireViewById(R.id.two_line_layout); mTwoLineLayout = view.requireViewById(R.id.two_line_layout);
mTwoLineTitleText = view.requireViewById(R.id.two_line_title); mTwoLineTitleText = view.requireViewById(R.id.two_line_title);
mTitleIcon = view.requireViewById(R.id.title_icon); mTitleIcon = view.requireViewById(R.id.title_icon);
mEndIcon = view.requireViewById(R.id.end_icon);
mProgressBar = view.requireViewById(R.id.volume_indeterminate_progress); mProgressBar = view.requireViewById(R.id.volume_indeterminate_progress);
mSeekBar = view.requireViewById(R.id.volume_seekbar); mSeekBar = view.requireViewById(R.id.volume_seekbar);
mDivider = view.requireViewById(R.id.end_divider);
mBottomDivider = view.requireViewById(R.id.bottom_divider);
mAddIcon = view.requireViewById(R.id.add_icon);
mCheckBox = view.requireViewById(R.id.check_box);
} }
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) { void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
@@ -132,11 +137,11 @@ public abstract class MediaOutputBaseAdapter extends
} }
private void setMargin(boolean topMargin, boolean bottomMargin) { private void setMargin(boolean topMargin, boolean bottomMargin) {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mFrameLayout ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mContainerLayout
.getLayoutParams(); .getLayoutParams();
params.topMargin = topMargin ? mMargin : 0; params.topMargin = topMargin ? mMargin : 0;
params.bottomMargin = bottomMargin ? mMargin : 0; params.bottomMargin = bottomMargin ? mMargin : 0;
mFrameLayout.setLayoutParams(params); mContainerLayout.setLayoutParams(params);
} }
void setSingleLineLayout(CharSequence title, boolean bFocused) { void setSingleLineLayout(CharSequence title, boolean bFocused) {
@@ -146,13 +151,26 @@ public abstract class MediaOutputBaseAdapter extends
mTitleText.setTranslationY(0); mTitleText.setTranslationY(0);
mTitleText.setText(title); mTitleText.setText(title);
if (bFocused) { if (bFocused) {
mTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL)); mTitleText.setTypeface(Typeface.create(mContext.getString(
com.android.internal.R.string.config_headlineFontFamilyMedium),
Typeface.NORMAL));
} else { } else {
mTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL)); mTitleText.setTypeface(Typeface.create(mContext.getString(
com.android.internal.R.string.config_headlineFontFamily), Typeface.NORMAL));
} }
} }
void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused, void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
boolean showProgressBar, boolean showSubtitle) {
setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle);
}
void setTwoLineLayout(CharSequence title, boolean bFocused, boolean showSeekBar,
boolean showProgressBar, boolean showSubtitle) {
setTwoLineLayout(null, title, bFocused, showSeekBar, showProgressBar, showSubtitle);
}
private void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) { boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) {
mTitleText.setVisibility(View.GONE); mTitleText.setVisibility(View.GONE);
mTwoLineLayout.setVisibility(View.VISIBLE); mTwoLineLayout.setVisibility(View.VISIBLE);
@@ -168,18 +186,21 @@ public abstract class MediaOutputBaseAdapter extends
} }
if (bFocused) { if (bFocused) {
mTwoLineTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
com.android.internal.R.string.config_headlineFontFamilyMedium),
Typeface.NORMAL)); Typeface.NORMAL));
} else { } else {
mTwoLineTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL)); mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
com.android.internal.R.string.config_headlineFontFamily), Typeface.NORMAL));
} }
} }
void initSeekbar(MediaDevice device) { void initSeekbar(MediaDevice device) {
mSeekBar.setMax(device.getMaxVolume()); mSeekBar.setMax(device.getMaxVolume());
mSeekBar.setMin(0); mSeekBar.setMin(0);
if (mSeekBar.getProgress() != device.getCurrentVolume()) { final int currentVolume = device.getCurrentVolume();
mSeekBar.setProgress(device.getCurrentVolume()); if (mSeekBar.getProgress() != currentVolume) {
mSeekBar.setProgress(currentVolume);
} }
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override @Override
@@ -213,7 +234,9 @@ public abstract class MediaOutputBaseAdapter extends
} }
mIsAnimating = true; mIsAnimating = true;
// Animation for title text // Animation for title text
toTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL)); toTitleText.setTypeface(Typeface.create(mContext.getString(
com.android.internal.R.string.config_headlineFontFamilyMedium),
Typeface.NORMAL));
toTitleText.animate() toTitleText.animate()
.setDuration(ANIM_DURATION) .setDuration(ANIM_DURATION)
.translationY(-delta) .translationY(-delta)
@@ -234,7 +257,9 @@ public abstract class MediaOutputBaseAdapter extends
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
final TextView fromTitleText = from.requireViewById( final TextView fromTitleText = from.requireViewById(
R.id.two_line_title); R.id.two_line_title);
fromTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL)); fromTitleText.setTypeface(Typeface.create(mContext.getString(
com.android.internal.R.string.config_headlineFontFamily),
Typeface.NORMAL));
fromTitleText.animate() fromTitleText.animate()
.setDuration(ANIM_DURATION) .setDuration(ANIM_DURATION)
.translationY(delta) .translationY(delta)

View File

@@ -119,6 +119,8 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
// Init device list // Init device list
mDevicesRecyclerView.setLayoutManager(mLayoutManager); mDevicesRecyclerView.setLayoutManager(mLayoutManager);
mDevicesRecyclerView.setAdapter(mAdapter); mDevicesRecyclerView.setAdapter(mAdapter);
// Init header icon
mHeaderIcon.setOnClickListener(v -> onHeaderIconClick());
// Init bottom buttons // Init bottom buttons
mDoneButton.setOnClickListener(v -> dismiss()); mDoneButton.setOnClickListener(v -> dismiss());
mStopButton.setOnClickListener(v -> { mStopButton.setOnClickListener(v -> {
@@ -218,4 +220,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
dismiss(); dismiss();
} }
} }
void onHeaderIconClick() {
}
} }

View File

@@ -71,6 +71,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
private final MediaSessionManager mMediaSessionManager; private final MediaSessionManager mMediaSessionManager;
private final ShadeController mShadeController; private final ShadeController mShadeController;
private final ActivityStarter mActivityStarter; private final ActivityStarter mActivityStarter;
private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
private final boolean mAboveStatusbar;
@VisibleForTesting @VisibleForTesting
final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>(); final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
@@ -82,13 +84,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
@Inject @Inject
public MediaOutputController(@NonNull Context context, String packageName, public MediaOutputController(@NonNull Context context, String packageName,
MediaSessionManager mediaSessionManager, LocalBluetoothManager boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
lbm, ShadeController shadeController, ActivityStarter starter) { lbm, ShadeController shadeController, ActivityStarter starter) {
mContext = context; mContext = context;
mPackageName = packageName; mPackageName = packageName;
mMediaSessionManager = mediaSessionManager; mMediaSessionManager = mediaSessionManager;
mShadeController = shadeController; mShadeController = shadeController;
mActivityStarter = starter; mActivityStarter = starter;
mAboveStatusbar = aboveStatusbar;
InfoMediaManager imm = new InfoMediaManager(mContext, packageName, null, lbm); InfoMediaManager imm = new InfoMediaManager(mContext, packageName, null, lbm);
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName); mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
} }
@@ -271,6 +274,42 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
mMediaDevices.addAll(targetMediaDevices); mMediaDevices.addAll(targetMediaDevices);
} }
List<MediaDevice> getGroupMediaDevices() {
final List<MediaDevice> selectedDevices = getSelectedMediaDevice();
final List<MediaDevice> selectableDevices = getSelectableMediaDevice();
if (mGroupMediaDevices.isEmpty()) {
mGroupMediaDevices.addAll(selectedDevices);
mGroupMediaDevices.addAll(selectableDevices);
return mGroupMediaDevices;
}
// To keep the same list order
final Collection<MediaDevice> sourceDevices = new ArrayList<>();
final Collection<MediaDevice> targetMediaDevices = new ArrayList<>();
sourceDevices.addAll(selectedDevices);
sourceDevices.addAll(selectableDevices);
for (MediaDevice originalDevice : mGroupMediaDevices) {
for (MediaDevice newDevice : sourceDevices) {
if (TextUtils.equals(originalDevice.getId(), newDevice.getId())) {
targetMediaDevices.add(newDevice);
sourceDevices.remove(newDevice);
break;
}
}
}
// Add new devices at the end of list if necessary
if (!sourceDevices.isEmpty()) {
targetMediaDevices.addAll(sourceDevices);
}
mGroupMediaDevices.clear();
mGroupMediaDevices.addAll(targetMediaDevices);
return mGroupMediaDevices;
}
void resetGroupMediaDevices() {
mGroupMediaDevices.clear();
}
void connectDevice(MediaDevice device) { void connectDevice(MediaDevice device) {
ThreadUtils.postOnBackgroundThread(() -> { ThreadUtils.postOnBackgroundThread(() -> {
mLocalMediaManager.connectDevice(device); mLocalMediaManager.connectDevice(device);
@@ -309,15 +348,6 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
return mLocalMediaManager.getDeselectableMediaDevice(); return mLocalMediaManager.getDeselectableMediaDevice();
} }
boolean isDeviceIncluded(Collection<MediaDevice> deviceCollection, MediaDevice targetDevice) {
for (MediaDevice device : deviceCollection) {
if (TextUtils.equals(device.getId(), targetDevice.getId())) {
return true;
}
}
return false;
}
void adjustSessionVolume(String sessionId, int volume) { void adjustSessionVolume(String sessionId, int volume) {
mLocalMediaManager.adjustSessionVolume(sessionId, volume); mLocalMediaManager.adjustSessionVolume(sessionId, volume);
} }
@@ -407,6 +437,16 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true); mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true);
} }
void launchMediaOutputDialog() {
mCallback.dismissDialog();
new MediaOutputDialog(mContext, mAboveStatusbar, this);
}
void launchMediaOutputGroupDialog() {
mCallback.dismissDialog();
new MediaOutputGroupDialog(mContext, mAboveStatusbar, this);
}
boolean isActiveRemoteDevice(@NonNull MediaDevice device) { boolean isActiveRemoteDevice(@NonNull MediaDevice device) {
final List<String> features = device.getFeatures(); final List<String> features = device.getFeatures();
return (features.contains(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK) return (features.contains(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)

View File

@@ -40,9 +40,8 @@ class MediaOutputDialogFactory @Inject constructor(
/** Creates a [MediaOutputDialog] for the given package. */ /** Creates a [MediaOutputDialog] for the given package. */
fun create(packageName: String, aboveStatusBar: Boolean) { fun create(packageName: String, aboveStatusBar: Boolean) {
mediaOutputDialog?.dismiss() mediaOutputDialog?.dismiss()
mediaOutputDialog = MediaOutputController(context, packageName, aboveStatusBar,
mediaOutputDialog = MediaOutputController(context, packageName, mediaSessionManager, lbm, mediaSessionManager, lbm, shadeController, starter).run {
shadeController, starter).run {
MediaOutputDialog(context, aboveStatusBar, this) } MediaOutputDialog(context, aboveStatusBar, this) }
} }

View File

@@ -25,7 +25,7 @@ import static org.mockito.Mockito.when;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.view.View; import android.view.View;
import android.widget.FrameLayout; import android.widget.LinearLayout;
import androidx.core.graphics.drawable.IconCompat; import androidx.core.graphics.drawable.IconCompat;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
@@ -66,7 +66,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
public void setUp() { public void setUp() {
mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController); mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
.onCreateViewHolder(new FrameLayout(mContext), 0); .onCreateViewHolder(new LinearLayout(mContext), 0);
when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices); when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false); when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
@@ -75,6 +75,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat); when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
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(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);
@@ -107,6 +108,11 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText( assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText(
R.string.media_output_dialog_pairing_new)); R.string.media_output_dialog_pairing_new));
} }
@@ -118,11 +124,29 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1); assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
} }
@Test
public void onBindViewHolder_bindNonActiveConnectedDevice_verifyView() {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
}
@Test @Test
public void onBindViewHolder_bindDisconnectedBluetoothDevice_verifyView() { public void onBindViewHolder_bindDisconnectedBluetoothDevice_verifyView() {
when(mMediaDevice2.getDeviceType()).thenReturn( when(mMediaDevice2.getDeviceType()).thenReturn(
@@ -130,7 +154,11 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
when(mMediaDevice2.isConnected()).thenReturn(false); when(mMediaDevice2.isConnected()).thenReturn(false);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo( assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(
mContext.getString(R.string.media_output_dialog_disconnected, TEST_DEVICE_NAME_2)); mContext.getString(R.string.media_output_dialog_disconnected, TEST_DEVICE_NAME_2));
@@ -142,9 +170,13 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED); LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(mContext.getText( assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(mContext.getText(
@@ -162,7 +194,11 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1); assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
} }
@@ -174,8 +210,13 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
LocalMediaManager.MediaDeviceState.STATE_CONNECTING); LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1); assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
} }
@@ -183,7 +224,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() { public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
when(mMediaOutputController.isZeroMode()).thenReturn(true); when(mMediaOutputController.isZeroMode()).thenReturn(true);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
mViewHolder.mFrameLayout.performClick(); mViewHolder.mContainerLayout.performClick();
verify(mMediaOutputController).launchBluetoothPairing(); verify(mMediaOutputController).launchBluetoothPairing();
} }
@@ -194,7 +235,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED); LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
mViewHolder.mFrameLayout.performClick(); mViewHolder.mContainerLayout.performClick();
verify(mMediaOutputController).connectDevice(mMediaDevice2); verify(mMediaOutputController).connectDevice(mMediaDevice2);
} }

View File

@@ -68,7 +68,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
@Before @Before
public void setUp() { public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter); mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
mMediaOutputController); mMediaOutputController);

View File

@@ -34,6 +34,7 @@ import android.media.RoutingSessionInfo;
import android.media.session.MediaController; import android.media.session.MediaController;
import android.media.session.MediaSessionManager; import android.media.session.MediaSessionManager;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.text.TextUtils;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
@@ -60,6 +61,9 @@ public class MediaOutputControllerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "com.test.package.name"; private static final String TEST_PACKAGE_NAME = "com.test.package.name";
private static final String TEST_DEVICE_1_ID = "test_device_1_id"; private static final String TEST_DEVICE_1_ID = "test_device_1_id";
private static final String TEST_DEVICE_2_ID = "test_device_2_id"; private static final String TEST_DEVICE_2_ID = "test_device_2_id";
private static final String TEST_DEVICE_3_ID = "test_device_3_id";
private static final String TEST_DEVICE_4_ID = "test_device_4_id";
private static final String TEST_DEVICE_5_ID = "test_device_5_id";
private static final String TEST_ARTIST = "test_artist"; private static final String TEST_ARTIST = "test_artist";
private static final String TEST_SONG = "test_song"; private static final String TEST_SONG = "test_song";
private static final String TEST_SESSION_ID = "test_session_id"; private static final String TEST_SESSION_ID = "test_session_id";
@@ -96,7 +100,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
MediaSessionManager.class); MediaSessionManager.class);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn( when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
mCachedBluetoothDeviceManager); mCachedBluetoothDeviceManager);
mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter); mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager); mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager; mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
@@ -139,8 +143,8 @@ public class MediaOutputControllerTest extends SysuiTestCase {
@Test @Test
public void start_withoutPackageName_verifyMediaControllerInit() { public void start_withoutPackageName_verifyMediaControllerInit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager, mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mLocalBluetoothManager, mShadeController, mStarter); mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
mMediaOutputController.start(mCb); mMediaOutputController.start(mCb);
@@ -159,8 +163,8 @@ public class MediaOutputControllerTest extends SysuiTestCase {
@Test @Test
public void stop_withoutPackageName_verifyMediaControllerDeinit() { public void stop_withoutPackageName_verifyMediaControllerDeinit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager, mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mLocalBluetoothManager, mShadeController, mStarter); mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
mMediaOutputController.start(mCb); mMediaOutputController.start(mCb);
mMediaOutputController.stop(); mMediaOutputController.stop();
@@ -345,4 +349,78 @@ public class MediaOutputControllerTest extends SysuiTestCase {
assertThat(mMediaOutputController.isZeroMode()).isFalse(); assertThat(mMediaOutputController.isZeroMode()).isFalse();
} }
@Test
public void getGroupMediaDevices_differentDeviceOrder_showingSameOrder() {
final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class);
final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class);
final List<MediaDevice> selectedMediaDevices = new ArrayList<>();
final List<MediaDevice> selectableMediaDevices = new ArrayList<>();
when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID);
when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID);
selectedMediaDevices.add(selectedMediaDevice1);
selectedMediaDevices.add(selectedMediaDevice2);
selectableMediaDevices.add(selectableMediaDevice1);
selectableMediaDevices.add(selectableMediaDevice2);
doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice();
doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice();
final List<MediaDevice> groupMediaDevices = mMediaOutputController.getGroupMediaDevices();
// Reset order
selectedMediaDevices.clear();
selectedMediaDevices.add(selectedMediaDevice2);
selectedMediaDevices.add(selectedMediaDevice1);
selectableMediaDevices.clear();
selectableMediaDevices.add(selectableMediaDevice2);
selectableMediaDevices.add(selectableMediaDevice1);
final List<MediaDevice> newDevices = mMediaOutputController.getGroupMediaDevices();
assertThat(newDevices.size()).isEqualTo(groupMediaDevices.size());
for (int i = 0; i < groupMediaDevices.size(); i++) {
assertThat(TextUtils.equals(groupMediaDevices.get(i).getId(),
newDevices.get(i).getId())).isTrue();
}
}
@Test
public void getGroupMediaDevices_newDevice_verifyDeviceOrder() {
final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class);
final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class);
final MediaDevice selectableMediaDevice3 = mock(MediaDevice.class);
final List<MediaDevice> selectedMediaDevices = new ArrayList<>();
final List<MediaDevice> selectableMediaDevices = new ArrayList<>();
when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID);
when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID);
when(selectableMediaDevice3.getId()).thenReturn(TEST_DEVICE_5_ID);
selectedMediaDevices.add(selectedMediaDevice1);
selectedMediaDevices.add(selectedMediaDevice2);
selectableMediaDevices.add(selectableMediaDevice1);
selectableMediaDevices.add(selectableMediaDevice2);
doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice();
doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice();
final List<MediaDevice> groupMediaDevices = mMediaOutputController.getGroupMediaDevices();
// Reset order
selectedMediaDevices.clear();
selectedMediaDevices.add(selectedMediaDevice2);
selectedMediaDevices.add(selectedMediaDevice1);
selectableMediaDevices.clear();
selectableMediaDevices.add(selectableMediaDevice3);
selectableMediaDevices.add(selectableMediaDevice2);
selectableMediaDevices.add(selectableMediaDevice1);
final List<MediaDevice> newDevices = mMediaOutputController.getGroupMediaDevices();
assertThat(newDevices.size()).isEqualTo(5);
for (int i = 0; i < groupMediaDevices.size(); i++) {
assertThat(TextUtils.equals(groupMediaDevices.get(i).getId(),
newDevices.get(i).getId())).isTrue();
}
assertThat(newDevices.get(4).getId()).isEqualTo(TEST_DEVICE_5_ID);
}
} }

View File

@@ -64,7 +64,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
@Before @Before
public void setUp() { public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter); mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager; mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = new MediaOutputDialog(mContext, false, mMediaOutputController); mMediaOutputDialog = new MediaOutputDialog(mContext, false, mMediaOutputController);