diff --git a/src/com/android/settings/media/MediaOutputIndicatorSlice.java b/src/com/android/settings/media/MediaOutputIndicatorSlice.java index 305c7df1913..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,14 +60,9 @@ 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 primaryActionIntent = PendingIntent.getActivity(mContext, - requestCode, - getMediaOutputSliceIntent(), FLAG_UPDATE_CURRENT); - final SliceAction primarySliceAction = SliceAction.createDeeplink( - primaryActionIntent, 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 final ListBuilder listBuilder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY) @@ -83,22 +75,6 @@ public class MediaOutputIndicatorSlice implements CustomSliceable { return listBuilder.build(); } - @VisibleForTesting - Intent getMediaOutputSliceIntent() { - final MediaController mediaController = getWorker().getActiveLocalMediaController(); - final Intent intent = new Intent() - .setPackage(Utils.SETTINGS_PACKAGE_NAME) - .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - 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); @@ -141,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 9ed6f4ebd0d..debc0ecc164 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)); } } @@ -133,8 +150,7 @@ public class RemoteMediaSlice implements CustomSliceable { .setTitle(isMediaOutputDisabled ? spannableTitle : outputTitle) .setSubtitle(info.getName()) .setTitleItem(emptyIcon, ListBuilder.ICON_IMAGE) - .setPrimaryAction(getMediaOutputSliceAction( - info.getClientPackageName(), isMediaOutputDisabled))); + .setPrimaryAction(getMediaOutputDialogAction(info, isMediaOutputDisabled))); } return listBuilder.build(); } @@ -167,23 +183,22 @@ public class RemoteMediaSlice implements CustomSliceable { return primarySliceAction; } - private SliceAction getMediaOutputSliceAction( - String packageName, boolean isMediaOutputDisabled) { - final Intent intent = new Intent() - .setAction(isMediaOutputDisabled - ? "" - : MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, packageName); - final IconCompat icon = IconCompat.createWithResource(mContext, - R.drawable.ic_volume_remote); - final int requestCode = TextUtils.isEmpty(packageName) ? 0 : packageName.hashCode(); - final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext, - requestCode, intent, 0 /* flags */); + private SliceAction getMediaOutputDialogAction(RoutingSessionInfo info, + boolean isMediaOutputDisabled) { + 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, + info.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT); final SliceAction primarySliceAction = SliceAction.createDeeplink( - primaryActionIntent, 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, packageName))); + 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 320862c6075..251cfa5cbf6 100644 --- a/src/com/android/settings/notification/RemoteVolumeGroupController.java +++ b/src/com/android/settings/notification/RemoteVolumeGroupController.java @@ -159,11 +159,11 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem if (TextUtils.equals(info.getId(), preference.getKey().substring(SWITCHER_PREFIX.length()))) { final Intent intent = new Intent() - .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .setAction(MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG) + .setPackage(MediaOutputSliceConstants.SYSTEMUI_PACKAGE_NAME) .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, info.getClientPackageName()); - mContext.startActivity(intent); + mContext.sendBroadcast(intent); return true; } } diff --git a/src/com/android/settings/panel/VolumePanel.java b/src/com/android/settings/panel/VolumePanel.java index d45bfd1f5e1..b5e807d8fa7 100644 --- a/src/com/android/settings/panel/VolumePanel.java +++ b/src/com/android/settings/panel/VolumePanel.java @@ -16,6 +16,9 @@ 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; import static com.android.settings.slices.CustomSliceRegistry.VOLUME_ALARM_URI; @@ -24,20 +27,40 @@ 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; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; + import com.android.settings.R; +import com.android.settingslib.media.MediaOutputSliceConstants; import java.util.ArrayList; import java.util.List; -public class VolumePanel implements PanelContent { +/** + * Panel data class for Volume settings. + */ +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); } @@ -46,6 +69,20 @@ public class VolumePanel implements PanelContent { 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() { + mContext.unregisterReceiver(mReceiver); + } + @Override public CharSequence getTitle() { return mContext.getText(R.string.sound_settings); @@ -78,4 +115,9 @@ public class VolumePanel implements PanelContent { 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 da92b2be424..7c3d2b15880 100644 --- a/src/com/android/settings/sound/MediaOutputPreferenceController.java +++ b/src/com/android/settings/sound/MediaOutputPreferenceController.java @@ -133,10 +133,13 @@ public class MediaOutputPreferenceController extends AudioSwitchPreferenceContro @Override public boolean handlePreferenceTreeClick(Preference preference) { if (TextUtils.equals(preference.getKey(), getPreferenceKey())) { - final Intent intent = new Intent() - .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); + mContext.sendBroadcast(new Intent() + .setAction(MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG) + .setPackage(MediaOutputSliceConstants.SYSTEMUI_PACKAGE_NAME) + .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, + mMediaController.getPackageName()) + .putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, + mMediaController.getSessionToken())); return true; } return false; diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java index 28620e97214..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,31 +206,45 @@ 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.getMediaOutputSliceIntent(); + 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(); - assertThat(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT).isEqualTo(intent.getAction()); - assertThat(TextUtils.equals(Utils.SETTINGS_PACKAGE_NAME, intent.getPackage())).isTrue(); + assertThat(MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG).isEqualTo( + intent.getAction()); + assertThat(TextUtils.equals(MediaOutputSliceConstants.SYSTEMUI_PACKAGE_NAME, + intent.getPackage())).isTrue(); assertThat(mToken == intent.getExtras().getParcelable( MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN)).isTrue(); } @Test - public void getMediaOutputSliceIntent_withoutActiveLocalMedia_verifyIntentExtra() { + public void onNotifyChange_withoutActiveLocalMedia_verifyIntentExtra() { doReturn(mMediaController).when(sMediaOutputIndicatorWorker) .getActiveLocalMediaController(); - final Intent intent = mMediaOutputIndicatorSlice.getMediaOutputSliceIntent(); + 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(); - assertThat(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT).isEqualTo(intent.getAction()); - assertThat(TextUtils.equals(Utils.SETTINGS_PACKAGE_NAME, intent.getPackage())).isTrue(); + assertThat(MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG).isEqualTo( + intent.getAction()); + assertThat(TextUtils.equals(MediaOutputSliceConstants.SYSTEMUI_PACKAGE_NAME, + intent.getPackage())).isTrue(); assertThat(intent.getExtras().getParcelable( MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN) == null).isTrue(); } diff --git a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java index 8225c69b0b9..eb84a4fb3fb 100644 --- a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java @@ -300,9 +300,9 @@ public class MediaOutputPreferenceControllerTest { mPreference.setKey(TEST_KEY); mController.handlePreferenceTreeClick(mPreference); - verify(mContext).startActivity(intentCaptor.capture()); + verify(mContext).sendBroadcast(intentCaptor.capture()); assertThat(intentCaptor.getValue().getAction()) - .isEqualTo(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT); + .isEqualTo(MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG); } /**