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;