diff --git a/src/com/android/settings/panel/MediaOutputGroupPanel.java b/src/com/android/settings/panel/MediaOutputGroupPanel.java
new file mode 100644
index 00000000000..fdac4e797b3
--- /dev/null
+++ b/src/com/android/settings/panel/MediaOutputGroupPanel.java
@@ -0,0 +1,159 @@
+/*
+ * 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.settings.panel;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import static com.android.settings.media.MediaOutputSlice.MEDIA_PACKAGE_NAME;
+import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_GROUP_SLICE_URI;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.net.Uri;
+
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+
+import com.android.settings.R;
+import com.android.settingslib.media.LocalMediaManager;
+import com.android.settingslib.media.MediaDevice;
+import com.android.settingslib.media.MediaOutputSliceConstants;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents the Media output group Panel.
+ *
+ *
+ * Displays Media output group item
+ *
+ */
+public class MediaOutputGroupPanel implements PanelContent, LocalMediaManager.DeviceCallback,
+ LifecycleObserver {
+
+ private final Context mContext;
+ private final String mPackageName;
+
+ private PanelContentCallback mCallback;
+ private LocalMediaManager mLocalMediaManager;
+
+ /**
+ * To generate a Media output group Panel instance.
+ *
+ * @param context the context of the caller.
+ * @param packageName media application package name.
+ * @return MediaOutputGroupPanel instance.
+ */
+ public static MediaOutputGroupPanel create(Context context, String packageName) {
+ return new MediaOutputGroupPanel(context, packageName);
+ }
+
+ private MediaOutputGroupPanel(Context context, String packageName) {
+ mContext = context.getApplicationContext();
+ mPackageName = packageName;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mContext.getText(R.string.media_output_group_panel_title);
+ }
+
+ @Override
+ public CharSequence getSubTitle() {
+ final int size = mLocalMediaManager.getSelectedMediaDevice().size();
+ if (size == 1) {
+ return mContext.getText(R.string.media_output_group_panel_single_device_summary);
+ }
+ return mContext.getString(R.string.media_output_group_panel_multiple_devices_summary, size);
+ }
+
+ @Override
+ public IconCompat getIcon() {
+ return IconCompat.createWithResource(mContext, R.drawable.ic_arrow_back).setTint(
+ Color.BLACK);
+ }
+
+ @Override
+ public List getSlices() {
+ final List uris = new ArrayList<>();
+ MEDIA_OUTPUT_GROUP_SLICE_URI =
+ MEDIA_OUTPUT_GROUP_SLICE_URI
+ .buildUpon()
+ .clearQuery()
+ .appendQueryParameter(MEDIA_PACKAGE_NAME, mPackageName)
+ .build();
+ uris.add(MEDIA_OUTPUT_GROUP_SLICE_URI);
+ return uris;
+ }
+
+ @Override
+ public Intent getSeeMoreIntent() {
+ return null;
+ }
+
+ @Override
+ public Intent getHeaderIconIntent() {
+ final Intent intent = new Intent()
+ .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, mPackageName);
+ return intent;
+ }
+
+ @Override
+ public void registerCallback(PanelContentCallback callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * Lifecycle callback to initial {@link LocalMediaManager}
+ */
+ @OnLifecycleEvent(ON_START)
+ public void onStart() {
+ if (mLocalMediaManager == null) {
+ mLocalMediaManager = new LocalMediaManager(mContext, mPackageName, null);
+ }
+ mLocalMediaManager.registerCallback(this);
+ mLocalMediaManager.startScan();
+ }
+
+ /**
+ * Lifecycle callback to de-initial {@link LocalMediaManager}
+ */
+ @OnLifecycleEvent(ON_STOP)
+ public void onStop() {
+ mLocalMediaManager.unregisterCallback(this);
+ mLocalMediaManager.stopScan();
+ }
+
+ @Override
+ public void onDeviceListUpdate(List devices) {
+ if (mCallback != null) {
+ mCallback.onGroupChanged();
+ }
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.PANEL_MEDIA_OUTPUT_GROUP;
+ }
+}
diff --git a/src/com/android/settings/panel/MediaOutputPanel.java b/src/com/android/settings/panel/MediaOutputPanel.java
index 6735375b9cd..0bb1430b245 100644
--- a/src/com/android/settings/panel/MediaOutputPanel.java
+++ b/src/com/android/settings/panel/MediaOutputPanel.java
@@ -66,7 +66,7 @@ public class MediaOutputPanel implements PanelContent, LocalMediaManager.DeviceC
private final Context mContext;
private final String mPackageName;
- private PanelCustomizedButtonCallback mCallback;
+ private PanelContentCallback mCallback;
private boolean mIsCustomizedButtonUsed = true;
@VisibleForTesting
@@ -190,7 +190,7 @@ public class MediaOutputPanel implements PanelContent, LocalMediaManager.DeviceC
}
@Override
- public void registerCallback(PanelCustomizedButtonCallback callback) {
+ public void registerCallback(PanelContentCallback callback) {
mCallback = callback;
}
diff --git a/src/com/android/settings/panel/PanelContent.java b/src/com/android/settings/panel/PanelContent.java
index 352a304e24d..0f60e29a8ca 100644
--- a/src/com/android/settings/panel/PanelContent.java
+++ b/src/com/android/settings/panel/PanelContent.java
@@ -100,5 +100,5 @@ public interface PanelContent extends Instrumentable {
*
* @param callback the callback to add.
*/
- default void registerCallback(PanelCustomizedButtonCallback callback) {}
+ default void registerCallback(PanelContentCallback callback) {}
}
diff --git a/src/com/android/settings/panel/PanelCustomizedButtonCallback.java b/src/com/android/settings/panel/PanelContentCallback.java
similarity index 75%
rename from src/com/android/settings/panel/PanelCustomizedButtonCallback.java
rename to src/com/android/settings/panel/PanelContentCallback.java
index 8b4290ff1d3..ce8ea223379 100644
--- a/src/com/android/settings/panel/PanelCustomizedButtonCallback.java
+++ b/src/com/android/settings/panel/PanelContentCallback.java
@@ -16,14 +16,20 @@
package com.android.settings.panel;
/**
- * PanelCustomizedButtonCallback provides a callback interface for {@link PanelFragment} to receive
+ * PanelContentCallback provides a callback interface for {@link PanelFragment} to receive
* events from {@link PanelContent}.
*/
-public interface PanelCustomizedButtonCallback {
+public interface PanelContentCallback {
/**
* It will be called when customized button state is changed. For example, custom button
* would be hidden for specific behavior.
*/
void onCustomizedButtonStateChanged();
+
+ /**
+ * It will be called when group content is changed. For example, to add/remove a device into
+ * a group
+ */
+ void onGroupChanged();
}
diff --git a/src/com/android/settings/panel/PanelFragment.java b/src/com/android/settings/panel/PanelFragment.java
index 31e2ac6ee55..0e41421644f 100644
--- a/src/com/android/settings/panel/PanelFragment.java
+++ b/src/com/android/settings/panel/PanelFragment.java
@@ -411,7 +411,7 @@ public class PanelFragment extends Fragment {
};
}
- class LocalPanelCallback implements PanelCustomizedButtonCallback {
+ class LocalPanelCallback implements PanelContentCallback {
@Override
public void onCustomizedButtonStateChanged() {
@@ -420,5 +420,12 @@ public class PanelFragment extends Fragment {
mPanel.isCustomizedButtonUsed() ? View.VISIBLE : View.GONE);
});
}
+
+ @Override
+ public void onGroupChanged() {
+ ThreadUtils.postOnMainThread(() -> {
+ mHeaderSubtitle.setText(mPanel.getSubTitle());
+ });
+ }
}
}
diff --git a/tests/robotests/src/com/android/settings/panel/MediaOutputGroupPanelTest.java b/tests/robotests/src/com/android/settings/panel/MediaOutputGroupPanelTest.java
new file mode 100644
index 00000000000..632d10c1ae4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/panel/MediaOutputGroupPanelTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.settings.panel;
+
+import static com.android.settings.media.MediaOutputSlice.MEDIA_PACKAGE_NAME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.Uri;
+
+import com.android.settings.slices.CustomSliceRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class MediaOutputGroupPanelTest {
+
+ private static final String TEST_PACKAGENAME = "com.test.packagename";
+
+ private MediaOutputGroupPanel mPanel;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ when(mContext.getApplicationContext()).thenReturn(mContext);
+ mPanel = MediaOutputGroupPanel.create(mContext, TEST_PACKAGENAME);
+ }
+
+ @Test
+ public void getSlices_containsNecessarySlices() {
+ final List uris = mPanel.getSlices();
+
+ assertThat(uris).containsExactly(CustomSliceRegistry.MEDIA_OUTPUT_GROUP_SLICE_URI);
+ }
+
+ @Test
+ public void getSlices_verifyPackageName_isEqual() {
+ final List uris = mPanel.getSlices();
+
+ assertThat(uris.get(0).getQueryParameter(MEDIA_PACKAGE_NAME)).isEqualTo(TEST_PACKAGENAME);
+ }
+
+ @Test
+ public void getSeeMoreIntent_isNull() {
+ assertThat(mPanel.getSeeMoreIntent()).isNull();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/panel/MediaOutputPanelTest.java b/tests/robotests/src/com/android/settings/panel/MediaOutputPanelTest.java
index ea77485524f..b95c9d0701c 100644
--- a/tests/robotests/src/com/android/settings/panel/MediaOutputPanelTest.java
+++ b/tests/robotests/src/com/android/settings/panel/MediaOutputPanelTest.java
@@ -66,7 +66,7 @@ public class MediaOutputPanelTest {
@Mock
private LocalMediaManager mLocalMediaManager;
@Mock
- private PanelCustomizedButtonCallback mCallback;
+ private PanelContentCallback mCallback;
private MediaOutputPanel mPanel;
private Context mContext;