Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 77c2a331 authored by Santiago Seifert's avatar Santiago Seifert Committed by Android (Google) Code Review
Browse files

Merge "Show the output switcher when no media is playing" into main

parents 6e3bf1ee 9e454698
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -10927,6 +10927,9 @@
    <!-- Title with application label for media output settings. [CHAR LIMIT=NONE] -->
    <string name="media_output_label_title">Play <xliff:g id="label" example="Music Player">%s</xliff:g> on</string>
    <!-- Title for media output settings without media is playing -->
    <string name="media_output_title_without_playing">Audio will play on</string>
    <!-- Summary for media output default settings. (this device) [CHAR LIMIT=30] -->
    <string name="media_output_default_summary">This device</string>
+25 −11
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.settings.media;

import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI;
import static com.android.settingslib.media.flags.Flags.enableOutputSwitcherForSystemRouting;

import android.annotation.ColorInt;
import android.content.Context;
@@ -58,7 +59,12 @@ public class MediaOutputIndicatorSlice implements CustomSliceable {
        }
        final IconCompat icon = IconCompat.createWithResource(mContext,
                com.android.internal.R.drawable.ic_settings_bluetooth);
        final CharSequence title = mContext.getString(R.string.media_output_label_title,
        final int stringRes = enableOutputSwitcherForSystemRouting()
                ? (getWorker().getActiveLocalMediaController() != null
                        ? R.string.media_output_label_title
                        : R.string.media_output_title_without_playing)
                : R.string.media_output_label_title;
        final CharSequence title = mContext.getString(stringRes,
                Utils.getApplicationLabel(mContext, getWorker().getPackageName()));
        final SliceAction primarySliceAction = SliceAction.create(
                getBroadcastIntent(mContext), icon, ListBuilder.ICON_IMAGE, title);
@@ -117,21 +123,24 @@ public class MediaOutputIndicatorSlice implements CustomSliceable {
        // 2. worker is not null
        // 3. Available devices are more than 0
        // 4. The local media session is active and the state is playing.
        //    - if !enableOutputSwitcherForSystemRouting(), (4) will be bypass.
        return getWorker() != null
                && !com.android.settingslib.Utils.isAudioModeOngoingCall(mContext)
                && getWorker().getMediaDevices().size() > 0
                && getWorker().getActiveLocalMediaController() != null;
                && (enableOutputSwitcherForSystemRouting()
                        ? true : 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
        if (enableOutputSwitcherForSystemRouting() && mediaController == null) {
            mContext.sendBroadcast(new Intent()
                    .setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME)
                    .setAction(MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG));
        } else if (mediaController != null) {
            mContext.sendBroadcast(new Intent()
                    .setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME)
                    .setAction(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG)
@@ -139,6 +148,11 @@ public class MediaOutputIndicatorSlice implements CustomSliceable {
                            mediaController.getSessionToken())
                    .putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME,
                            mediaController.getPackageName()));
        } else {
            Log.d(TAG, "No active local media controller");
            return;
        }

        // Dismiss volume panel
        mContext.sendBroadcast(new Intent()
                .setPackage(MediaOutputConstants.SETTINGS_PACKAGE_NAME)
+38 −1
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@ package com.android.settings.sound;

import static android.media.AudioManager.STREAM_DEVICES_CHANGED_ACTION;

import static com.android.settingslib.media.flags.Flags.enableOutputSwitcherForSystemRouting;

import android.annotation.Nullable;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -28,6 +31,8 @@ import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.MediaRouter;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
import android.os.Handler;
import android.os.Looper;
import android.util.FeatureFlagUtils;
@@ -79,6 +84,8 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont
    private final WiredHeadsetBroadcastReceiver mReceiver;
    private final Handler mHandler;
    private LocalBluetoothManager mLocalBluetoothManager;
    @Nullable private MediaSessionManager.OnActiveSessionsChangedListener mSessionListener;
    @Nullable private MediaSessionManager mMediaSessionManager;

    public interface AudioSwitchCallback {
        void onPreferenceDataChanged(ListPreference preference);
@@ -107,6 +114,14 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont
            return;
        }
        mProfileManager = mLocalBluetoothManager.getProfileManager();

        if (enableOutputSwitcherForSystemRouting()) {
            mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
            mSessionListener = new SessionChangeListener();
        } else {
            mMediaSessionManager = null;
            mSessionListener = null;
        }
    }

    /**
@@ -329,13 +344,27 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont
        // Register for misc other intent broadcasts.
        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
        intentFilter.addAction(STREAM_DEVICES_CHANGED_ACTION);

        if (enableOutputSwitcherForSystemRouting()) {
            mContext.registerReceiver(mReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED);
            if (mMediaSessionManager != null) {
                mMediaSessionManager.addOnActiveSessionsChangedListener(
                        mSessionListener, null, mHandler);
            }
        } else {
            mContext.registerReceiver(mReceiver, intentFilter);
        }
    }

    private void unregister() {
        mLocalBluetoothManager.getEventManager().unregisterCallback(this);
        mAudioManager.unregisterAudioDeviceCallback(mAudioManagerAudioDeviceCallback);
        mContext.unregisterReceiver(mReceiver);
        if (enableOutputSwitcherForSystemRouting()) {
            if (mMediaSessionManager != null) {
                mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionListener);
            }
        }
    }

    /** Notifications of audio device connection and disconnection events. */
@@ -362,4 +391,12 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont
            }
        }
    }

    private class SessionChangeListener
            implements MediaSessionManager.OnActiveSessionsChangedListener {
        @Override
        public void onActiveSessionsChanged(List<MediaController> controllers) {
            updateState(mPreference);
        }
    }
}
+39 −19
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.settings.sound;

import static com.android.settingslib.media.flags.Flags.enableOutputSwitcherForSystemRouting;

import android.annotation.Nullable;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
@@ -46,21 +49,22 @@ import java.util.List;
 */
public class MediaOutputPreferenceController extends AudioSwitchPreferenceController {

    private MediaController mMediaController;
    private static final String TAG = "MediaOutputPreferenceController";
    @Nullable private MediaController mMediaController;
    private MediaSessionManager mMediaSessionManager;

    public MediaOutputPreferenceController(Context context, String key) {
        super(context, key);
        mMediaController = MediaOutputUtils.getActiveLocalMediaController(context.getSystemService(
                MediaSessionManager.class));
        mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
        mMediaController = MediaOutputUtils.getActiveLocalMediaController(mMediaSessionManager);
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);

        if (!Utils.isAudioModeOngoingCall(mContext) && mMediaController != null) {
            mPreference.setVisible(true);
        }
        mPreference.setVisible(!Utils.isAudioModeOngoingCall(mContext)
                && (enableOutputSwitcherForSystemRouting() ? true : mMediaController != null));
    }

    @Override
@@ -70,10 +74,15 @@ public class MediaOutputPreferenceController extends AudioSwitchPreferenceContro
            return;
        }

        if (enableOutputSwitcherForSystemRouting()) {
            mMediaController = MediaOutputUtils.getActiveLocalMediaController(mMediaSessionManager);
        } else {
            if (mMediaController == null) {
                // No active local playback
                return;
            }
        }


        if (Utils.isAudioModeOngoingCall(mContext)) {
            // Ongoing call status, switch entry for media will be disabled.
@@ -95,9 +104,14 @@ public class MediaOutputPreferenceController extends AudioSwitchPreferenceContro
                || (connectedLeAudioDevices != null && !connectedLeAudioDevices.isEmpty()))) {
            activeDevice = findActiveDevice();
        }

        if (mMediaController == null) {
            mPreference.setTitle(mContext.getString(R.string.media_output_title_without_playing));
        } else {
            mPreference.setTitle(mContext.getString(R.string.media_output_label_title,
                    com.android.settings.Utils.getApplicationLabel(mContext,
                    mMediaController.getPackageName())));
        }
        mPreference.setSummary((activeDevice == null) ?
                mContext.getText(R.string.media_output_default_summary) :
                activeDevice.getAlias());
@@ -145,6 +159,11 @@ public class MediaOutputPreferenceController extends AudioSwitchPreferenceContro
    @Override
    public boolean handlePreferenceTreeClick(Preference preference) {
        if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
            if (enableOutputSwitcherForSystemRouting() && mMediaController == null) {
                mContext.sendBroadcast(new Intent()
                        .setAction(MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG)
                        .setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME));
            } else if (mMediaController != null) {
                mContext.sendBroadcast(new Intent()
                        .setAction(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG)
                        .setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME)
@@ -152,6 +171,7 @@ public class MediaOutputPreferenceController extends AudioSwitchPreferenceContro
                                mMediaController.getPackageName())
                        .putExtra(MediaOutputConstants.KEY_MEDIA_SESSION_TOKEN,
                                mMediaController.getSessionToken()));
            }
            return true;
        }
        return false;
+48 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
package com.android.settings.media;

import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI;
import static com.android.settingslib.media.flags.Flags.FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING;

import static com.google.common.truth.Truth.assertThat;

@@ -38,6 +39,7 @@ import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Process;
import android.platform.test.flag.junit.SetFlagsRule;
import android.text.TextUtils;

import androidx.slice.Slice;
@@ -55,6 +57,7 @@ import com.android.settingslib.media.MediaOutputConstants;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -96,6 +99,9 @@ public class MediaOutputIndicatorSliceTest {
    @Mock
    private Drawable mTestDrawable;

    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    private Context mContext;
    private MediaOutputIndicatorSlice mMediaOutputIndicatorSlice;
    private AudioManager mAudioManager;
@@ -254,6 +260,34 @@ public class MediaOutputIndicatorSliceTest {
                MediaOutputConstants.KEY_MEDIA_SESSION_TOKEN) == null).isTrue();
    }

    @Test
    public void onNotifyChange_withoutMediaControllerFlagEnabled_verifyIntentExtra() {
        mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
        doReturn(null).when(sMediaOutputIndicatorWorker)
                .getActiveLocalMediaController();
        ArgumentCaptor<Intent> argument = ArgumentCaptor.forClass(Intent.class);

        mMediaOutputIndicatorSlice.onNotifyChange(null);
        verify(mContext, times(2)).sendBroadcast(argument.capture());
        List<Intent> intentList = argument.getAllValues();
        Intent intent = intentList.get(0);

        assertThat(intent.getAction()).isEqualTo(
                MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG);
        assertThat(TextUtils.equals(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME,
                intent.getPackage())).isTrue();
    }

    @Test
    public void onNotifyChange_withoutMediaControllerFlagDisabled_doNothing() {
        mSetFlagsRule.disableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
        doReturn(null).when(sMediaOutputIndicatorWorker)
                .getActiveLocalMediaController();

        mMediaOutputIndicatorSlice.onNotifyChange(null);
    }


    @Test
    public void isVisible_allConditionMatched_returnTrue() {
        mAudioManager.setMode(AudioManager.MODE_NORMAL);
@@ -268,6 +302,7 @@ public class MediaOutputIndicatorSliceTest {

    @Test
    public void isVisible_noActiveSession_returnFalse() {
        mSetFlagsRule.disableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
        mAudioManager.setMode(AudioManager.MODE_NORMAL);
        mDevices.add(mDevice1);

@@ -278,6 +313,19 @@ public class MediaOutputIndicatorSliceTest {
        assertThat(mMediaOutputIndicatorSlice.isVisible()).isFalse();
    }

    @Test
    public void isVisible_noActiveSession_returnTrue() {
        mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
        mAudioManager.setMode(AudioManager.MODE_NORMAL);
        mDevices.add(mDevice1);

        when(sMediaOutputIndicatorWorker.getMediaDevices()).thenReturn(mDevices);
        doReturn(mMediaController).when(sMediaOutputIndicatorWorker)
                .getActiveLocalMediaController();

        assertThat(mMediaOutputIndicatorSlice.isVisible()).isTrue();
    }

    private void initPackage() {
        mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
        mAppInfo = new ApplicationInfo();
Loading