diff --git a/core/api/current.txt b/core/api/current.txt index 002bd74580cce..e29e3db67db6b 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -10392,6 +10392,7 @@ package android.content { field public static final String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE"; field public static final String ACTION_SET_WALLPAPER = "android.intent.action.SET_WALLPAPER"; field public static final String ACTION_SHOW_APP_INFO = "android.intent.action.SHOW_APP_INFO"; + field public static final String ACTION_SHOW_OUTPUT_SWITCHER = "android.intent.action.SHOW_OUTPUT_SWITCHER"; field public static final String ACTION_SHOW_WORK_APPS = "android.intent.action.SHOW_WORK_APPS"; field public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN"; field public static final String ACTION_SYNC = "android.intent.action.SYNC"; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index f2ebec6306f80..9d82274d3f995 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3560,6 +3560,17 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON"; + /** + * Broadcast action: Launch System output switcher. Includes a single extra field, + * {@link #EXTRA_PACKAGE_NAME}, which specifies the package name of the calling app + * so that the system can get the corresponding MediaSession for the output switcher. + * + * @see #EXTRA_PACKAGE_NAME + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_SHOW_OUTPUT_SWITCHER = + "android.intent.action.SHOW_OUTPUT_SWITCHER"; + /** * Broadcast Action: The "Camera Button" was pressed. Includes a single * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 61df65aff8d7b..47771aa3c774b 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -971,10 +971,11 @@ - + + diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt index dd9d35bf20213..55fce592a4097 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt @@ -35,25 +35,40 @@ class MediaOutputDialogReceiver @Inject constructor( private val mediaOutputBroadcastDialogFactory: MediaOutputBroadcastDialogFactory ) : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - if (TextUtils.equals(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG, - intent.action)) { - val packageName: String? = - intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME) - if (!TextUtils.isEmpty(packageName)) { - mediaOutputDialogFactory.create(packageName!!, false) - } else if (DEBUG) { - Log.e(TAG, "Unable to launch media output dialog. Package name is empty.") + when { + TextUtils.equals(Intent.ACTION_SHOW_OUTPUT_SWITCHER, intent.action) -> { + val packageName: String? = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME) + launchMediaOutputDialogIfPossible(packageName) } - } else if (TextUtils.equals( - MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG, - intent.action)) { - val packageName: String? = + TextUtils.equals( + MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG, intent.action) -> { + val packageName: String? = intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME) - if (!TextUtils.isEmpty(packageName)) { - mediaOutputBroadcastDialogFactory.create(packageName!!, false) - } else if (DEBUG) { - Log.e(TAG, "Unable to launch media output broadcast dialog. Package name is empty.") + launchMediaOutputDialogIfPossible(packageName) + } + TextUtils.equals( + MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG, + intent.action) -> { + val packageName: String? = + intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME) + launchMediaOutputBroadcastDialogIfPossible(packageName) } } } + + private fun launchMediaOutputDialogIfPossible(packageName: String?) { + if (!packageName.isNullOrEmpty()) { + mediaOutputDialogFactory.create(packageName, false) + } else if (DEBUG) { + Log.e(TAG, "Unable to launch media output dialog. Package name is empty.") + } + } + + private fun launchMediaOutputBroadcastDialogIfPossible(packageName: String?) { + if (!packageName.isNullOrEmpty()) { + mediaOutputBroadcastDialogFactory.create(packageName, false) + } else if (DEBUG) { + Log.e(TAG, "Unable to launch media output broadcast dialog. Package name is empty.") + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java new file mode 100644 index 0000000000000..771b986d1c1ea --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java @@ -0,0 +1,168 @@ +/* + * 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 org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Intent; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.settingslib.media.MediaOutputConstants; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class MediaOutputDialogReceiverTest extends SysuiTestCase { + + private MediaOutputDialogReceiver mMediaOutputDialogReceiver; + + private final MediaOutputDialogFactory mMockMediaOutputDialogFactory = + mock(MediaOutputDialogFactory.class); + + private final MediaOutputBroadcastDialogFactory mMockMediaOutputBroadcastDialogFactory = + mock(MediaOutputBroadcastDialogFactory.class); + + @Before + public void setup() { + mMediaOutputDialogReceiver = new MediaOutputDialogReceiver(mMockMediaOutputDialogFactory, + mMockMediaOutputBroadcastDialogFactory); + } + + @Test + public void showOutputSwitcher_ExtraPackageName_DialogFactoryCalled() { + Intent intent = new Intent(Intent.ACTION_SHOW_OUTPUT_SWITCHER); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, getContext().getPackageName()); + mMediaOutputDialogReceiver.onReceive(getContext(), intent); + + verify(mMockMediaOutputDialogFactory, times(1)) + .create(getContext().getPackageName(), false, null); + verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any()); + } + + @Test + public void showOutputSwitcher_WrongExtraKey_DialogFactoryNotCalled() { + Intent intent = new Intent(Intent.ACTION_SHOW_OUTPUT_SWITCHER); + intent.putExtra("Wrong Package Name Key", getContext().getPackageName()); + mMediaOutputDialogReceiver.onReceive(getContext(), intent); + + verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any()); + verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any()); + } + + @Test + public void showOutputSwitcher_NoExtra_DialogFactoryNotCalled() { + Intent intent = new Intent(Intent.ACTION_SHOW_OUTPUT_SWITCHER); + mMediaOutputDialogReceiver.onReceive(getContext(), intent); + + verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any()); + verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any()); + } + + @Test + public void launchMediaOutputDialog_ExtraPackageName_DialogFactoryCalled() { + Intent intent = new Intent(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG); + intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName()); + mMediaOutputDialogReceiver.onReceive(getContext(), intent); + + verify(mMockMediaOutputDialogFactory, times(1)) + .create(getContext().getPackageName(), false, null); + verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any()); + } + + @Test + public void launchMediaOutputDialog_WrongExtraKey_DialogFactoryNotCalled() { + Intent intent = new Intent(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG); + intent.putExtra("Wrong Package Name Key", getContext().getPackageName()); + mMediaOutputDialogReceiver.onReceive(getContext(), intent); + + verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any()); + verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any()); + } + + @Test + public void launchMediaOutputDialog_NoExtra_DialogFactoryNotCalled() { + Intent intent = new Intent(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG); + mMediaOutputDialogReceiver.onReceive(getContext(), intent); + + verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any()); + verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any()); + } + + @Test + public void launchMediaOutputBroadcastDialog_ExtraPackageName_BroadcastDialogFactoryCalled() { + Intent intent = new Intent( + MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG); + intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName()); + mMediaOutputDialogReceiver.onReceive(getContext(), intent); + + verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any()); + verify(mMockMediaOutputBroadcastDialogFactory, times(1)) + .create(getContext().getPackageName(), false, null); + } + + @Test + public void launchMediaOutputBroadcastDialog_WrongExtraKey_DialogBroadcastFactoryNotCalled() { + Intent intent = new Intent( + MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG); + intent.putExtra("Wrong Package Name Key", getContext().getPackageName()); + mMediaOutputDialogReceiver.onReceive(getContext(), intent); + + verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any()); + verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any()); + } + + @Test + public void launchMediaOutputBroadcastDialog_NoExtra_BroadcastDialogFactoryNotCalled() { + Intent intent = new Intent( + MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG); + mMediaOutputDialogReceiver.onReceive(getContext(), intent); + + verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any()); + verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any()); + } + + @Test + public void unKnownAction_ExtraPackageName_FactoriesNotCalled() { + Intent intent = new Intent("UnKnown Action"); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, getContext().getPackageName()); + intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName()); + mMediaOutputDialogReceiver.onReceive(getContext(), intent); + + verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any()); + verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any()); + } + + @Test + public void unKnownActionAnd_NoExtra_FactoriesNotCalled() { + Intent intent = new Intent("UnKnown Action"); + mMediaOutputDialogReceiver.onReceive(getContext(), intent); + + verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any()); + verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any()); + } +}