Add Media Output Dialog for Output Switcher
-Add MediaOutputBaseDialog to provide common method for different media operations UI
-Add MediaOutputDialog for showing Bluetooth device
-Add resources for background image, style and layout
-Add MediaOutputBaseDialogTest for unit test
Bug: 155822415
Test: atest MediaOutputBaseDialogTest
Merged-In: I3086a4049f240870ca1ad870946d6848e500b561
Change-Id: I3086a4049f240870ca1ad870946d6848e500b561
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_background.xml
new file mode 100644
index 0000000..3ceb0f6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_output_dialog_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ <shape android:shape="rectangle">
+ <corners android:radius="8dp" />
+ <solid android:color="?android:attr/colorBackground" />
+ </shape>
+</inset>
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
new file mode 100644
index 0000000..0229e6e
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/media_output_dialog"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="94dp"
+ android:gravity="start|center_vertical"
+ android:paddingStart="16dp"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/header_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingEnd="16dp"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="16dp"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/header_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:textSize="20sp"/>
+
+ <TextView
+ android:id="@+id/header_subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:fontFamily="roboto-regular"
+ android:textSize="14sp"/>
+
+ </LinearLayout>
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/listDivider"/>
+
+ <LinearLayout
+ android:id="@+id/device_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="12dp"/>
+
+ <include
+ layout="@layout/media_output_list_item"
+ android:id="@+id/group_item_controller"
+ android:visibility="gone"/>
+
+ <View
+ android:id="@+id/group_item_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/listDivider"
+ android:visibility="gone"/>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/list_result"
+ android:scrollbars="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:overScrollMode="never"/>
+
+ <View
+ android:id="@+id/list_bottom_padding"
+ android:layout_width="match_parent"
+ android:layout_height="12dp"/>
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/listDivider"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/stop"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="64dp"
+ android:text="@string/keyboard_key_media_stop"
+ android:visibility="gone"/>
+
+ <Space
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+
+ <Button
+ android:id="@+id/done"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="64dp"
+ android:layout_marginEnd="0dp"
+ android:text="@string/inline_done_button"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 68c2a38..44f0cce 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -400,6 +400,10 @@
<item name="android:windowIsFloating">true</item>
</style>
+ <style name="Theme.SystemUI.Dialog.MediaOutput">
+ <item name="android:windowBackground">@drawable/media_output_dialog_background</item>
+ </style>
+
<style name="QSBorderlessButton">
<item name="android:padding">12dp</item>
<item name="android:background">@drawable/qs_btn_borderless_rect</item>
@@ -789,5 +793,4 @@
* Title: headline, medium 20sp
* Message: body, 16 sp -->
<style name="Theme.ControlsRequestDialog" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert"/>
-
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
new file mode 100644
index 0000000..781bf8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.media.dialog;
+
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settingslib.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+/**
+ * Base dialog for media output UI
+ */
+public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
+ MediaOutputController.Callback {
+
+ private static final String TAG = "MediaOutputDialog";
+
+ private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+ private final RecyclerView.LayoutManager mLayoutManager;
+
+ final Context mContext;
+ final MediaOutputController mMediaOutputController;
+
+ @VisibleForTesting
+ View mDialogView;
+ private TextView mHeaderTitle;
+ private TextView mHeaderSubtitle;
+ private ImageView mHeaderIcon;
+ private RecyclerView mDevicesRecyclerView;
+ private LinearLayout mDeviceListLayout;
+ private Button mDoneButton;
+ private Button mStopButton;
+ private View mListBottomPadding;
+ private int mListMaxHeight;
+
+ MediaOutputBaseAdapter mAdapter;
+ FrameLayout mGroupItemController;
+ View mGroupDivider;
+
+ private final ViewTreeObserver.OnGlobalLayoutListener mDeviceListLayoutListener = () -> {
+ // Set max height for list
+ if (mDeviceListLayout.getHeight() > mListMaxHeight) {
+ ViewGroup.LayoutParams params = mDeviceListLayout.getLayoutParams();
+ params.height = mListMaxHeight;
+ mDeviceListLayout.setLayoutParams(params);
+ }
+ };
+
+ public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) {
+ super(context, R.style.Theme_SystemUI_Dialog_MediaOutput);
+ mContext = context;
+ mMediaOutputController = mediaOutputController;
+ mLayoutManager = new LinearLayoutManager(mContext);
+ mListMaxHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_list_max_height);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mDialogView = LayoutInflater.from(mContext).inflate(R.layout.media_output_dialog, null);
+ final Window window = getWindow();
+ final WindowManager.LayoutParams lp = window.getAttributes();
+ lp.gravity = Gravity.BOTTOM;
+ // Config insets to make sure the layout is above the navigation bar
+ lp.setFitInsetsTypes(statusBars() | navigationBars());
+ lp.setFitInsetsSides(WindowInsets.Side.all());
+ lp.setFitInsetsIgnoringVisibility(true);
+ window.setAttributes(lp);
+ window.setContentView(mDialogView);
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ mHeaderTitle = mDialogView.requireViewById(R.id.header_title);
+ mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
+ mHeaderIcon = mDialogView.requireViewById(R.id.header_icon);
+ mDevicesRecyclerView = mDialogView.requireViewById(R.id.list_result);
+ mGroupItemController = mDialogView.requireViewById(R.id.group_item_controller);
+ mGroupDivider = mDialogView.requireViewById(R.id.group_item_divider);
+ mDeviceListLayout = mDialogView.requireViewById(R.id.device_list);
+ mDoneButton = mDialogView.requireViewById(R.id.done);
+ mStopButton = mDialogView.requireViewById(R.id.stop);
+ mListBottomPadding = mDialogView.requireViewById(R.id.list_bottom_padding);
+
+ mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
+ mDeviceListLayoutListener);
+ // Init device list
+ mDevicesRecyclerView.setLayoutManager(mLayoutManager);
+ mDevicesRecyclerView.setAdapter(mAdapter);
+ // Init bottom buttons
+ mDoneButton.setOnClickListener(v -> dismiss());
+ mStopButton.setOnClickListener(v -> {
+ mMediaOutputController.releaseSession();
+ dismiss();
+ });
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mMediaOutputController.start(this);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mMediaOutputController.stop();
+ }
+
+ @VisibleForTesting
+ void refresh() {
+ // Update header icon
+ final int iconRes = getHeaderIconRes();
+ final IconCompat iconCompat = getHeaderIcon();
+ if (iconRes != 0) {
+ mHeaderIcon.setVisibility(View.VISIBLE);
+ mHeaderIcon.setImageResource(iconRes);
+ } else if (iconCompat != null) {
+ mHeaderIcon.setVisibility(View.VISIBLE);
+ mHeaderIcon.setImageIcon(iconCompat.toIcon(mContext));
+ } else {
+ mHeaderIcon.setVisibility(View.GONE);
+ }
+ if (mHeaderIcon.getVisibility() == View.VISIBLE) {
+ final int size = getHeaderIconSize();
+ mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size, size));
+ }
+ // Update title and subtitle
+ mHeaderTitle.setText(getHeaderText());
+ final CharSequence subTitle = getHeaderSubtitle();
+ if (TextUtils.isEmpty(subTitle)) {
+ mHeaderSubtitle.setVisibility(View.GONE);
+ mHeaderTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
+ } else {
+ mHeaderSubtitle.setVisibility(View.VISIBLE);
+ mHeaderSubtitle.setText(subTitle);
+ mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
+ }
+ if (!mAdapter.isDragging()) {
+ mAdapter.notifyDataSetChanged();
+ }
+ // Add extra padding when device amount is less than 6
+ if (mMediaOutputController.getMediaDevices().size() < 6) {
+ mListBottomPadding.setVisibility(View.VISIBLE);
+ } else {
+ mListBottomPadding.setVisibility(View.GONE);
+ }
+ }
+
+ abstract int getHeaderIconRes();
+
+ abstract IconCompat getHeaderIcon();
+
+ abstract int getHeaderIconSize();
+
+ abstract CharSequence getHeaderText();
+
+ abstract CharSequence getHeaderSubtitle();
+
+ @Override
+ public void onMediaChanged() {
+ mMainThreadHandler.post(() -> refresh());
+ }
+
+ @Override
+ public void onMediaStoppedOrPaused() {
+ if (isShowing()) {
+ dismiss();
+ }
+ }
+
+ @Override
+ public void onRouteChanged() {
+ mMainThreadHandler.post(() -> refresh());
+ }
+
+ @Override
+ public void dismissDialog() {
+ dismiss();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
new file mode 100644
index 0000000..d59971d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.media.dialog;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+
+import androidx.core.graphics.drawable.IconCompat;
+
+import com.android.systemui.R;
+
+import javax.inject.Singleton;
+
+/**
+ * Dialog for media output transferring.
+ */
+@Singleton
+public class MediaOutputDialog extends MediaOutputBaseDialog {
+
+ MediaOutputDialog(Context context, boolean aboveStatusbar, MediaOutputController
+ mediaOutputController) {
+ super(context, mediaOutputController);
+ mAdapter = new MediaOutputAdapter(mMediaOutputController);
+ if (!aboveStatusbar) {
+ getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ }
+ show();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mGroupItemController.setVisibility(View.GONE);
+ mGroupDivider.setVisibility(View.GONE);
+ }
+
+ @Override
+ int getHeaderIconRes() {
+ return 0;
+ }
+
+ @Override
+ IconCompat getHeaderIcon() {
+ return mMediaOutputController.getHeaderIcon();
+ }
+
+ @Override
+ int getHeaderIconSize() {
+ return mContext.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_header_album_icon_size);
+ }
+
+ @Override
+ CharSequence getHeaderText() {
+ return mMediaOutputController.getHeaderTitle();
+ }
+
+ @Override
+ CharSequence getHeaderSubtitle() {
+ return mMediaOutputController.getHeaderSubTitle();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
new file mode 100644
index 0000000..42b21c6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.media.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.media.session.MediaSessionManager;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class MediaOutputBaseDialogTest extends SysuiTestCase {
+
+ private static final String TEST_PACKAGE = "test_package";
+
+ // Mock
+ private MediaOutputBaseAdapter mMediaOutputBaseAdapter = mock(MediaOutputBaseAdapter.class);
+
+ private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
+ private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+ private ShadeController mShadeController = mock(ShadeController.class);
+ private ActivityStarter mStarter = mock(ActivityStarter.class);
+
+ private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
+ private MediaOutputController mMediaOutputController;
+ private int mHeaderIconRes;
+ private IconCompat mIconCompat;
+ private CharSequence mHeaderTitle;
+ private CharSequence mHeaderSubtitle;
+
+ @Before
+ public void setUp() {
+ mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
+ mMediaOutputController);
+ mMediaOutputBaseDialogImpl.onCreate(new Bundle());
+ }
+
+ @Test
+ public void refresh_withIconRes_iconIsVisible() {
+ mHeaderIconRes = 1;
+ mMediaOutputBaseDialogImpl.refresh();
+ final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.header_icon);
+
+ assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void refresh_withIconCompat_iconIsVisible() {
+ mIconCompat = mock(IconCompat.class);
+ mMediaOutputBaseDialogImpl.refresh();
+ final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.header_icon);
+
+ assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void refresh_noIcon_iconLayoutNotVisible() {
+ mHeaderIconRes = 0;
+ mIconCompat = null;
+ mMediaOutputBaseDialogImpl.refresh();
+ final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.header_icon);
+
+ assertThat(view.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void refresh_checkTitle() {
+ mHeaderTitle = "test_string";
+
+ mMediaOutputBaseDialogImpl.refresh();
+ final TextView titleView = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.header_title);
+
+ assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(titleView.getText()).isEqualTo(mHeaderTitle);
+ }
+
+ @Test
+ public void refresh_withSubtitle_checkSubtitle() {
+ mHeaderSubtitle = "test_string";
+
+ mMediaOutputBaseDialogImpl.refresh();
+ final TextView subtitleView = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.header_subtitle);
+
+ assertThat(subtitleView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(subtitleView.getText()).isEqualTo(mHeaderSubtitle);
+ }
+
+ @Test
+ public void refresh_noSubtitle_checkSubtitle() {
+ mMediaOutputBaseDialogImpl.refresh();
+ final TextView subtitleView = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.header_subtitle);
+
+ assertThat(subtitleView.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void refresh_inDragging_notUpdateAdapter() {
+ when(mMediaOutputBaseAdapter.isDragging()).thenReturn(true);
+ mMediaOutputBaseDialogImpl.refresh();
+
+ verify(mMediaOutputBaseAdapter, never()).notifyDataSetChanged();
+ }
+
+ @Test
+ public void refresh_notInDragging_verifyUpdateAdapter() {
+ when(mMediaOutputBaseAdapter.isDragging()).thenReturn(false);
+ mMediaOutputBaseDialogImpl.refresh();
+
+ verify(mMediaOutputBaseAdapter).notifyDataSetChanged();
+ }
+
+ @Test
+ public void refresh_with6Devices_checkBottomPaddingVisibility() {
+ for (int i = 0; i < 6; i++) {
+ mMediaOutputController.mMediaDevices.add(mock(MediaDevice.class));
+ }
+ mMediaOutputBaseDialogImpl.refresh();
+ final View view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.list_bottom_padding);
+
+ assertThat(view.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void refresh_with5Devices_checkBottomPaddingVisibility() {
+ for (int i = 0; i < 5; i++) {
+ mMediaOutputController.mMediaDevices.add(mock(MediaDevice.class));
+ }
+ mMediaOutputBaseDialogImpl.refresh();
+ final View view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.list_bottom_padding);
+
+ assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog {
+
+ MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) {
+ super(context, mediaOutputController);
+
+ mAdapter = mMediaOutputBaseAdapter;
+ }
+
+ int getHeaderIconRes() {
+ return mHeaderIconRes;
+ }
+
+ IconCompat getHeaderIcon() {
+ return mIconCompat;
+ }
+
+ int getHeaderIconSize() {
+ return 10;
+ }
+
+ CharSequence getHeaderText() {
+ return mHeaderTitle;
+ }
+
+ CharSequence getHeaderSubtitle() {
+ return mHeaderSubtitle;
+ }
+ }
+}