From 6472c98ffa36c2f7691684652fd0fbd32ff6d88b Mon Sep 17 00:00:00 2001 From: timhypeng Date: Tue, 6 Oct 2020 12:12:53 +0800 Subject: [PATCH] Update rule of launching media output dialog -Do not hide Media Output Dialog in Settings, and let dialog handles -Hide Media output slice panel when launching dialog Bug: 155822415 Test: make -j50 RunSettingsRoboTests Merged-In: I16732f625f100b259d6e53c85db40af0ec1652c5 Change-Id: I16732f625f100b259d6e53c85db40af0ec1652c5 --- .../media/MediaOutputIndicatorSlice.java | 51 +++++++++---------- .../settings/media/RemoteMediaSlice.java | 38 ++++++++++---- .../RemoteVolumeGroupController.java | 11 +--- .../android/settings/panel/VolumePanel.java | 32 ++++++++++-- .../MediaOutputPreferenceController.java | 14 +---- .../media/MediaOutputIndicatorSliceTest.java | 21 ++++++-- 6 files changed, 99 insertions(+), 68 deletions(-) diff --git a/src/com/android/settings/media/MediaOutputIndicatorSlice.java b/src/com/android/settings/media/MediaOutputIndicatorSlice.java index 401a0a61a0c..40ee05bc01b 100644 --- a/src/com/android/settings/media/MediaOutputIndicatorSlice.java +++ b/src/com/android/settings/media/MediaOutputIndicatorSlice.java @@ -16,18 +16,15 @@ package com.android.settings.media; -import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; - import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI; import android.annotation.ColorInt; -import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.media.session.MediaController; import android.net.Uri; -import android.text.TextUtils; +import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.core.graphics.drawable.IconCompat; @@ -63,13 +60,8 @@ public class MediaOutputIndicatorSlice implements CustomSliceable { com.android.internal.R.drawable.ic_settings_bluetooth); final CharSequence title = mContext.getString(R.string.media_output_label_title, Utils.getApplicationLabel(mContext, getWorker().getPackageName())); - final int requestCode = TextUtils.isEmpty(getWorker().getPackageName()) - ? 0 - : getWorker().getPackageName().hashCode(); - final PendingIntent primaryBroadcastIntent = PendingIntent.getBroadcast(mContext, - requestCode, getMediaOutputDialogIntent(), FLAG_UPDATE_CURRENT); - final SliceAction primarySliceAction = SliceAction.createDeeplink( - primaryBroadcastIntent, icon, ListBuilder.ICON_IMAGE, title); + final SliceAction primarySliceAction = SliceAction.create( + getBroadcastIntent(mContext), icon, ListBuilder.ICON_IMAGE, title); @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext); // To set an empty icon to indent the row @@ -83,21 +75,6 @@ public class MediaOutputIndicatorSlice implements CustomSliceable { return listBuilder.build(); } - @VisibleForTesting - Intent getMediaOutputDialogIntent() { - final MediaController mediaController = getWorker().getActiveLocalMediaController(); - final Intent intent = new Intent() - .setPackage(MediaOutputSliceConstants.SYSTEMUI_PACKAGE_NAME) - .setAction(MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG); - if (mediaController != null) { - intent.putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, - mediaController.getSessionToken()); - intent.putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, - mediaController.getPackageName()); - } - return intent; - } - private IconCompat createEmptyIcon() { final Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); return IconCompat.createWithBitmap(bitmap); @@ -140,4 +117,26 @@ public class MediaOutputIndicatorSlice implements CustomSliceable { && getWorker().getMediaDevices().size() > 0 && getWorker().getActiveLocalMediaController() != null; } + + @Override + public void onNotifyChange(Intent intent) { + final MediaController mediaController = getWorker().getActiveLocalMediaController(); + + if (mediaController == null) { + Log.d(TAG, "No active local media controller"); + return; + } + // Launch media output dialog + mContext.sendBroadcast(new Intent() + .setPackage(MediaOutputSliceConstants.SYSTEMUI_PACKAGE_NAME) + .setAction(MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG) + .putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, + mediaController.getSessionToken()) + .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, + mediaController.getPackageName())); + // Dismiss volume panel + mContext.sendBroadcast(new Intent() + .setPackage(MediaOutputSliceConstants.SETTINGS_PACKAGE_NAME) + .setAction(MediaOutputSliceConstants.ACTION_CLOSE_PANEL)); + } } diff --git a/src/com/android/settings/media/RemoteMediaSlice.java b/src/com/android/settings/media/RemoteMediaSlice.java index 1ca33b16baa..5e50b55e98c 100644 --- a/src/com/android/settings/media/RemoteMediaSlice.java +++ b/src/com/android/settings/media/RemoteMediaSlice.java @@ -59,6 +59,9 @@ public class RemoteMediaSlice implements CustomSliceable { private static final String TAG = "RemoteMediaSlice"; private static final String MEDIA_ID = "media_id"; + private static final String ACTION_LAUNCH_DIALOG = "action_launch_dialog"; + private static final String SESSION_INFO = "RoutingSessionInfo"; + private static final String CUSTOMIZED_ACTION = "customized_action"; private final Context mContext; @@ -77,6 +80,20 @@ public class RemoteMediaSlice implements CustomSliceable { final String id = intent.getStringExtra(MEDIA_ID); if (!TextUtils.isEmpty(id)) { getWorker().adjustSessionVolume(id, newPosition); + return; + } + if (TextUtils.equals(ACTION_LAUNCH_DIALOG, intent.getStringExtra(CUSTOMIZED_ACTION))) { + // Launch Media Output Dialog + final RoutingSessionInfo info = intent.getParcelableExtra(SESSION_INFO); + mContext.sendBroadcast(new Intent() + .setPackage(MediaOutputSliceConstants.SYSTEMUI_PACKAGE_NAME) + .setAction(MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG) + .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, + info.getClientPackageName())); + // Dismiss volume panel + mContext.sendBroadcast(new Intent() + .setPackage(MediaOutputSliceConstants.SETTINGS_PACKAGE_NAME) + .setAction(MediaOutputSliceConstants.ACTION_CLOSE_PANEL)); } } @@ -168,19 +185,18 @@ public class RemoteMediaSlice implements CustomSliceable { private SliceAction getMediaOutputDialogAction(RoutingSessionInfo info, boolean isMediaOutputDisabled) { - final Intent intent = new Intent() - .setAction(isMediaOutputDisabled - ? "" : MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG) - .setPackage(MediaOutputSliceConstants.SYSTEMUI_PACKAGE_NAME) - .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, - info.getClientPackageName()); - final IconCompat icon = IconCompat.createWithResource(mContext, - R.drawable.ic_volume_remote); - + final Intent intent = new Intent(getUri().toString()) + .setData(getUri()) + .setClass(mContext, SliceBroadcastReceiver.class) + .putExtra(CUSTOMIZED_ACTION, isMediaOutputDisabled ? "" : ACTION_LAUNCH_DIALOG) + .putExtra(SESSION_INFO, info) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); final PendingIntent primaryBroadcastIntent = PendingIntent.getBroadcast(mContext, - 0 /* requestCode */, intent, 0 /* flags */); + info.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT); final SliceAction primarySliceAction = SliceAction.createDeeplink( - primaryBroadcastIntent, icon, ListBuilder.ICON_IMAGE, + primaryBroadcastIntent, + IconCompat.createWithResource(mContext, R.drawable.ic_volume_remote), + ListBuilder.ICON_IMAGE, mContext.getString(R.string.media_output_label_title, Utils.getApplicationLabel(mContext, info.getClientPackageName()))); return primarySliceAction; diff --git a/src/com/android/settings/notification/RemoteVolumeGroupController.java b/src/com/android/settings/notification/RemoteVolumeGroupController.java index 4dd497f093e..eb72c597d2e 100644 --- a/src/com/android/settings/notification/RemoteVolumeGroupController.java +++ b/src/com/android/settings/notification/RemoteVolumeGroupController.java @@ -32,7 +32,6 @@ import com.android.settings.Utils; import com.android.settings.core.BasePreferenceController; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnDestroy; -import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.settingslib.media.MediaOutputSliceConstants; @@ -46,7 +45,7 @@ import java.util.List; * {@link com.android.settings.notification.RemoteVolumeSeekBarPreference} **/ public class RemoteVolumeGroupController extends BasePreferenceController implements - Preference.OnPreferenceChangeListener, LifecycleObserver, OnDestroy, OnPause, + Preference.OnPreferenceChangeListener, LifecycleObserver, OnDestroy, LocalMediaManager.DeviceCallback { private static final String KEY_REMOTE_VOLUME_GROUP = "remote_media_group"; @@ -97,14 +96,6 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem } } - @Override - public void onPause() { - // Media output dialog should not show when onPause - mContext.sendBroadcast(new Intent() - .setAction(MediaOutputSliceConstants.ACTION_DISMISS_MEDIA_OUTPUT_DIALOG) - .setPackage(MediaOutputSliceConstants.SYSTEMUI_PACKAGE_NAME)); - } - @Override public void onDestroy() { mLocalMediaManager.unregisterCallback(this); diff --git a/src/com/android/settings/panel/VolumePanel.java b/src/com/android/settings/panel/VolumePanel.java index 7c34a7c66bc..b5e807d8fa7 100644 --- a/src/com/android/settings/panel/VolumePanel.java +++ b/src/com/android/settings/panel/VolumePanel.java @@ -17,6 +17,7 @@ package com.android.settings.panel; import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; +import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI; import static com.android.settings.slices.CustomSliceRegistry.REMOTE_MEDIA_SLICE_URI; @@ -26,8 +27,10 @@ import static com.android.settings.slices.CustomSliceRegistry.VOLUME_MEDIA_URI; import static com.android.settings.slices.CustomSliceRegistry.VOLUME_RINGER_URI; import android.app.settings.SettingsEnums; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.net.Uri; import android.provider.Settings; @@ -47,6 +50,17 @@ public class VolumePanel implements PanelContent, LifecycleObserver { private final Context mContext; + private PanelContentCallback mCallback; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (MediaOutputSliceConstants.ACTION_CLOSE_PANEL.equals(intent.getAction())) { + mCallback.forceClose(); + } + } + }; + public static VolumePanel create(Context context) { return new VolumePanel(context); } @@ -55,13 +69,18 @@ public class VolumePanel implements PanelContent, LifecycleObserver { mContext = context.getApplicationContext(); } + /** Invoked when the panel is resumed. */ + @OnLifecycleEvent(ON_RESUME) + public void onResume() { + final IntentFilter filter = new IntentFilter(); + filter.addAction(MediaOutputSliceConstants.ACTION_CLOSE_PANEL); + mContext.registerReceiver(mReceiver, filter); + } + /** Invoked when the panel is paused. */ @OnLifecycleEvent(ON_PAUSE) public void onPause() { - // Media output dialog should not show when onPause - mContext.sendBroadcast(new Intent() - .setAction(MediaOutputSliceConstants.ACTION_DISMISS_MEDIA_OUTPUT_DIALOG) - .setPackage(MediaOutputSliceConstants.SYSTEMUI_PACKAGE_NAME)); + mContext.unregisterReceiver(mReceiver); } @Override @@ -96,4 +115,9 @@ public class VolumePanel implements PanelContent, LifecycleObserver { public int getViewType() { return PanelContent.VIEW_TYPE_SLIDER; } + + @Override + public void registerCallback(PanelContentCallback callback) { + mCallback = callback; + } } \ No newline at end of file diff --git a/src/com/android/settings/sound/MediaOutputPreferenceController.java b/src/com/android/settings/sound/MediaOutputPreferenceController.java index 914a9de0d27..7c3d2b15880 100644 --- a/src/com/android/settings/sound/MediaOutputPreferenceController.java +++ b/src/com/android/settings/sound/MediaOutputPreferenceController.java @@ -33,8 +33,6 @@ import com.android.settings.R; import com.android.settingslib.Utils; import com.android.settingslib.bluetooth.A2dpProfile; import com.android.settingslib.bluetooth.HearingAidProfile; -import com.android.settingslib.core.lifecycle.LifecycleObserver; -import com.android.settingslib.core.lifecycle.events.OnStop; import com.android.settingslib.media.MediaOutputSliceConstants; import java.util.List; @@ -47,8 +45,7 @@ import java.util.List; * - Media stream captured by remote device * - During a call. */ -public class MediaOutputPreferenceController extends AudioSwitchPreferenceController - implements LifecycleObserver, OnStop { +public class MediaOutputPreferenceController extends AudioSwitchPreferenceController { private MediaController mMediaController; @@ -66,15 +63,6 @@ public class MediaOutputPreferenceController extends AudioSwitchPreferenceContro } } - @Override - public void onStop() { - super.onStop(); - // Media output dialog should not show when onStop - mContext.sendBroadcast(new Intent() - .setAction(MediaOutputSliceConstants.ACTION_DISMISS_MEDIA_OUTPUT_DIALOG) - .setPackage(MediaOutputSliceConstants.SYSTEMUI_PACKAGE_NAME)); - } - @Override public void updateState(Preference preference) { if (preference == null) { diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java index 21ab8834619..68848af3ab9 100644 --- a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java +++ b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java @@ -23,6 +23,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -54,6 +56,7 @@ import com.android.settingslib.media.MediaOutputSliceConstants; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -203,12 +206,17 @@ public class MediaOutputIndicatorSliceTest { } @Test - public void getMediaOutputSliceIntent_withActiveLocalMedia_verifyIntentExtra() { + public void onNotifyChange_withActiveLocalMedia_verifyIntentExtra() { when(mMediaController.getSessionToken()).thenReturn(mToken); when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE_NAME); doReturn(mMediaController).when(sMediaOutputIndicatorWorker) .getActiveLocalMediaController(); - final Intent intent = mMediaOutputIndicatorSlice.getMediaOutputDialogIntent(); + ArgumentCaptor argument = ArgumentCaptor.forClass(Intent.class); + + mMediaOutputIndicatorSlice.onNotifyChange(null); + verify(mContext, times(2)).sendBroadcast(argument.capture()); + List intentList = argument.getAllValues(); + Intent intent = intentList.get(0); assertThat(TextUtils.equals(TEST_PACKAGE_NAME, intent.getStringExtra( MediaOutputSliceConstants.EXTRA_PACKAGE_NAME))).isTrue(); @@ -221,10 +229,15 @@ public class MediaOutputIndicatorSliceTest { } @Test - public void getMediaOutputSliceIntent_withoutActiveLocalMedia_verifyIntentExtra() { + public void onNotifyChange_withoutActiveLocalMedia_verifyIntentExtra() { doReturn(mMediaController).when(sMediaOutputIndicatorWorker) .getActiveLocalMediaController(); - final Intent intent = mMediaOutputIndicatorSlice.getMediaOutputDialogIntent(); + ArgumentCaptor argument = ArgumentCaptor.forClass(Intent.class); + + mMediaOutputIndicatorSlice.onNotifyChange(null); + verify(mContext, times(2)).sendBroadcast(argument.capture()); + List intentList = argument.getAllValues(); + Intent intent = intentList.get(0); assertThat(TextUtils.isEmpty(intent.getStringExtra( MediaOutputSliceConstants.EXTRA_PACKAGE_NAME))).isTrue();