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

Commit 6197f495 authored by SongFerngWang's avatar SongFerngWang
Browse files

[LE_Broadcast]Start broadcast and stop broadcast

- Add start broadcast UI and stop broadcast UI
- Add broadcast start process
- Add broadcast stop process

Bug: 227511707
Test: build pass and manaual test.
atest MediaOutputBaseDialogTest  (PASS)
atest MediaOutputDialogTest  (PASS)

Change-Id: Idd26d2b4e10dfcd00045184ddd2f9815a02ec7ce
parent 04a6e834
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -2283,6 +2283,10 @@
    <string name="media_output_broadcast_code">Password</string>
    <!-- Button for change broadcast name and broadcast code [CHAR LIMIT=60] -->
    <string name="media_output_broadcast_dialog_save">Save</string>
    <!-- The "starting" text when Broadcast is starting [CHAR LIMIT=60] -->
    <string name="media_output_broadcast_starting">Starting&#8230;</string>
    <!-- The button text when Broadcast start failed [CHAR LIMIT=60] -->
    <string name="media_output_broadcast_start_failed">Can\u2019t broadcast</string>

    <!-- Label for clip data when copying the build number off QS [CHAR LIMIT=NONE]-->
    <string name="build_number_clip_data_label">Build number</string>
+153 −28
Original line number Diff line number Diff line
@@ -19,7 +19,10 @@ package com.android.systemui.media.dialog;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;

import android.annotation.NonNull;
import android.app.WallpaperColors;
import android.bluetooth.BluetoothLeBroadcast;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
@@ -59,6 +62,9 @@ import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.statusbar.phone.SystemUIDialog;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * Base dialog for media output UI
 */
@@ -69,6 +75,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
    private static final String EMPTY_TITLE = " ";
    private static final String PREF_NAME = "MediaOutputDialog";
    private static final String PREF_IS_LE_BROADCAST_FIRST_LAUNCH = "PrefIsLeBroadcastFirstLaunch";
    private static final boolean DEBUG = true;

    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
    private final RecyclerView.LayoutManager mLayoutManager;
@@ -91,6 +98,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
    private Button mAppButton;
    private int mListMaxHeight;
    private WallpaperColors mWallpaperColors;
    private Executor mExecutor;

    MediaOutputBaseAdapter mAdapter;

@@ -103,6 +111,79 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
        }
    };

    private final BluetoothLeBroadcast.Callback mBroadcastCallback =
            new BluetoothLeBroadcast.Callback() {
                @Override
                public void onBroadcastStarted(int reason, int broadcastId) {
                    if (DEBUG) {
                        Log.d(TAG, "onBroadcastStarted(), reason = " + reason
                                + ", broadcastId = " + broadcastId);
                    }
                    mMainThreadHandler.post(() -> startLeBroadcastDialog());
                }

                @Override
                public void onBroadcastStartFailed(int reason) {
                    if (DEBUG) {
                        Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
                    }
                    handleLeBroadcastStartFailed();
                }

                @Override
                public void onBroadcastMetadataChanged(int broadcastId,
                        @NonNull BluetoothLeBroadcastMetadata metadata) {
                    if (DEBUG) {
                        Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId
                                + ", metadata = " + metadata);
                    }
                    mMainThreadHandler.post(() -> refresh());
                }

                @Override
                public void onBroadcastStopped(int reason, int broadcastId) {
                    if (DEBUG) {
                        Log.d(TAG, "onBroadcastStopped(), reason = " + reason
                                + ", broadcastId = " + broadcastId);
                    }
                    mMainThreadHandler.post(() -> refresh());
                }

                @Override
                public void onBroadcastStopFailed(int reason) {
                    if (DEBUG) {
                        Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason);
                    }
                    mMainThreadHandler.post(() -> refresh());
                }

                @Override
                public void onBroadcastUpdated(int reason, int broadcastId) {
                    if (DEBUG) {
                        Log.d(TAG, "onBroadcastUpdated(), reason = " + reason
                                + ", broadcastId = " + broadcastId);
                    }
                    mMainThreadHandler.post(() -> refresh());
                }

                @Override
                public void onBroadcastUpdateFailed(int reason, int broadcastId) {
                    if (DEBUG) {
                        Log.d(TAG, "onBroadcastUpdateFailed(), reason = " + reason
                                + ", broadcastId = " + broadcastId);
                    }
                    mMainThreadHandler.post(() -> refresh());
                }

                @Override
                public void onPlaybackStarted(int reason, int broadcastId) {
                }

                @Override
                public void onPlaybackStopped(int reason, int broadcastId) {
                }
            };

    public MediaOutputBaseDialog(Context context, BroadcastSender broadcastSender,
            MediaOutputController mediaOutputController) {
        super(context, R.style.Theme_SystemUI_Dialog_Media);
@@ -114,6 +195,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
        mLayoutManager = new LinearLayoutManager(mContext);
        mListMaxHeight = context.getResources().getDimensionPixelSize(
                R.dimen.media_output_dialog_list_max_height);
        mExecutor = Executors.newSingleThreadExecutor();
    }

    @Override
@@ -171,11 +253,18 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
    public void onStart() {
        super.onStart();
        mMediaOutputController.start(this);
        if(isBroadcastSupported()) {
            mMediaOutputController.registerLeBroadcastServiceCallBack(mExecutor,
                    mBroadcastCallback);
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if(isBroadcastSupported()) {
            mMediaOutputController.unregisterLeBroadcastServiceCallBack(mBroadcastCallback);
        }
        mMediaOutputController.stop();
    }

@@ -254,35 +343,12 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
                mAdapter.notifyDataSetChanged();
            }
        }
        // Show when remote media session is available
        // Show when remote media session is available or
        //      when the device supports BT LE audio + media is playing
        mStopButton.setVisibility(getStopButtonVisibility());
        if (isBroadcastSupported() && mMediaOutputController.isPlaying()) {
            mStopButton.setText(R.string.media_output_broadcast);
            mStopButton.setOnClickListener(v -> {
                SharedPreferences sharedPref = mContext.getSharedPreferences(PREF_NAME,
                        Context.MODE_PRIVATE);

                if (sharedPref != null
                        && sharedPref.getBoolean(PREF_IS_LE_BROADCAST_FIRST_LAUNCH, true)) {
                    Log.d(TAG, "PREF_IS_LE_BROADCAST_FIRST_LAUNCH: true");

                    mMediaOutputController.launchLeBroadcastNotifyDialog(mDialogView,
                            mBroadcastSender,
                            MediaOutputController.BroadcastNotifyDialog.ACTION_FIRST_LAUNCH);
                    SharedPreferences.Editor editor = sharedPref.edit();
                    editor.putBoolean(PREF_IS_LE_BROADCAST_FIRST_LAUNCH, false);
                    editor.apply();
                } else {
                    mMediaOutputController.launchMediaOutputBroadcastDialog(mDialogView,
                            mBroadcastSender);
                }
            });
        } else {
            mStopButton.setOnClickListener(v -> {
                mMediaOutputController.releaseSession();
                dismiss();
            });
        }
        mStopButton.setEnabled(true);
        mStopButton.setText(getStopButtonText());
        mStopButton.setOnClickListener(v -> onStopButtonClick());
    }

    private Drawable resizeDrawable(Drawable drawable, int size) {
@@ -301,6 +367,56 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
                Bitmap.createScaledBitmap(bitmap, size, size, false));
    }

    protected void handleLeBroadcastStartFailed() {
        mStopButton.setText(R.string.media_output_broadcast_start_failed);
        mStopButton.setEnabled(false);
        mMainThreadHandler.postDelayed(() -> refresh(), 3000);
    }

    protected void startLeBroadcast() {
        mStopButton.setText(R.string.media_output_broadcast_starting);
        mStopButton.setEnabled(false);
        if (!mMediaOutputController.startBluetoothLeBroadcast()) {
            // If the system can't execute "broadcast start", then UI shows the error.
            handleLeBroadcastStartFailed();
        }
    }

    protected boolean startLeBroadcastDialogForFirstTime(){
        SharedPreferences sharedPref = mContext.getSharedPreferences(PREF_NAME,
                Context.MODE_PRIVATE);
        if (sharedPref != null
                && sharedPref.getBoolean(PREF_IS_LE_BROADCAST_FIRST_LAUNCH, true)) {
            Log.d(TAG, "PREF_IS_LE_BROADCAST_FIRST_LAUNCH: true");

            mMediaOutputController.launchLeBroadcastNotifyDialog(mDialogView,
                    mBroadcastSender,
                    MediaOutputController.BroadcastNotifyDialog.ACTION_FIRST_LAUNCH,
                    (d, w) -> {
                        startLeBroadcast();
                    });
            SharedPreferences.Editor editor = sharedPref.edit();
            editor.putBoolean(PREF_IS_LE_BROADCAST_FIRST_LAUNCH, false);
            editor.apply();
            return true;
        }
        return false;
    }

    protected void startLeBroadcastDialog() {
        mMediaOutputController.launchMediaOutputBroadcastDialog(mDialogView,
                mBroadcastSender);
        refresh();
    }

    protected void stopLeBroadcast() {
        mStopButton.setEnabled(false);
        if (!mMediaOutputController.stopBluetoothLeBroadcast()) {
            // If the system can't execute "broadcast stop", then UI does refresh.
            mMainThreadHandler.post(() -> refresh());
        }
    }

    abstract Drawable getAppSourceIcon();

    abstract int getHeaderIconRes();
@@ -315,6 +431,15 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements

    abstract int getStopButtonVisibility();

    public CharSequence getStopButtonText() {
        return mContext.getText(R.string.keyboard_key_media_stop);
    }

    public void onStopButtonClick() {
        mMediaOutputController.releaseSession();
        dismiss();
    }

    public boolean isBroadcastSupported() {
        return false;
    }
+5 −2
Original line number Diff line number Diff line
@@ -142,8 +142,11 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog {

        mBroadcastNotify = getDialogView().requireViewById(R.id.broadcast_info);
        mBroadcastNotify.setOnClickListener(v -> {
            mMediaOutputController.launchLeBroadcastNotifyDialog(null, null,
                    MediaOutputController.BroadcastNotifyDialog.ACTION_BROADCAST_INFO_ICON);
            mMediaOutputController.launchLeBroadcastNotifyDialog(
                    /* view= */ null,
                    /* broadcastSender= */ null,
                    MediaOutputController.BroadcastNotifyDialog.ACTION_BROADCAST_INFO_ICON,
                    /* onClickListener= */ null);
        });
        mBroadcastName = getDialogView().requireViewById(R.id.broadcast_name_summary);
        mBroadcastNameEdit = getDialogView().requireViewById(R.id.broadcast_name_edit);
+64 −17
Original line number Diff line number Diff line
@@ -18,10 +18,13 @@ package com.android.systemui.media.dialog;

import static android.provider.Settings.ACTION_BLUETOOTH_PAIRING_SETTINGS;

import android.annotation.CallbackExecutor;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.WallpaperColors;
import android.bluetooth.BluetoothLeBroadcast;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -58,7 +61,7 @@ import androidx.mediarouter.media.MediaRouterParams;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.BluetoothMediaDevice;
import com.android.settingslib.media.InfoMediaManager;
@@ -83,6 +86,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;

import javax.inject.Inject;

@@ -642,17 +646,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
    }

    void launchLeBroadcastNotifyDialog(View mediaOutputDialog, BroadcastSender broadcastSender,
            BroadcastNotifyDialog action) {
            BroadcastNotifyDialog action, final DialogInterface.OnClickListener listener) {
        final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
        switch (action) {
            case ACTION_FIRST_LAUNCH:
                builder.setTitle(R.string.media_output_first_broadcast_title);
                builder.setMessage(R.string.media_output_first_notify_broadcast_message);
                builder.setNegativeButton(android.R.string.cancel, null);
                builder.setPositiveButton(R.string.media_output_broadcast,
                        (d, w) -> {
                            launchMediaOutputBroadcastDialog(mediaOutputDialog, broadcastSender);
                        });
                builder.setPositiveButton(R.string.media_output_broadcast, listener);
                break;
            case ACTION_BROADCAST_INFO_ICON:
                builder.setTitle(R.string.media_output_broadcast);
@@ -685,19 +686,65 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
                || features.contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK));
    }

    boolean isBluetoothLeDevice(@NonNull MediaDevice device) {
        if (device instanceof BluetoothMediaDevice) {
            final CachedBluetoothDevice cachedDevice =
                    ((BluetoothMediaDevice) device).getCachedDevice();
            boolean isConnectedLeAudioDevice =
                    (cachedDevice != null) ? cachedDevice.isConnectedLeAudioDevice() : false;
            if (DEBUG) {
                Log.d(TAG, "isConnectedLeAudioDevice=" + isConnectedLeAudioDevice);
    boolean isBroadcastSupported() {
        LocalBluetoothLeBroadcast broadcast =
                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
        return broadcast != null ? true : false;
    }

    boolean isBluetoothLeBroadcastEnabled() {
        LocalBluetoothLeBroadcast broadcast =
                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
        if (broadcast == null) {
            return false;
        }
            return isConnectedLeAudioDevice;
        return broadcast.isEnabled(null);
    }

    boolean startBluetoothLeBroadcast() {
        LocalBluetoothLeBroadcast broadcast =
                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
        if (broadcast == null) {
            Log.d(TAG, "The broadcast profile is null");
            return false;
        }
        broadcast.startBroadcast(getAppSourceName(), /*language*/ null);
        return true;
    }

    boolean stopBluetoothLeBroadcast() {
        LocalBluetoothLeBroadcast broadcast =
                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
        if (broadcast == null) {
            Log.d(TAG, "The broadcast profile is null");
            return false;
        }
        broadcast.stopLatestBroadcast();
        return true;
    }

    void registerLeBroadcastServiceCallBack(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull BluetoothLeBroadcast.Callback callback) {
        LocalBluetoothLeBroadcast broadcast =
                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
        if (broadcast == null) {
            Log.d(TAG, "The broadcast profile is null");
            return;
        }
        broadcast.registerServiceCallBack(executor, callback);
    }

    void unregisterLeBroadcastServiceCallBack(
            @NonNull BluetoothLeBroadcast.Callback callback) {
        LocalBluetoothLeBroadcast broadcast =
                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
        if (broadcast == null) {
            Log.d(TAG, "The broadcast profile is null");
            return;
        }
        broadcast.unregisterServiceCallBack(callback);
    }

    private boolean isPlayBackInfoLocal() {
        return mMediaController != null
+29 −7
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import androidx.core.graphics.drawable.IconCompat;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.SysUISingleton;
@@ -93,18 +92,41 @@ public class MediaOutputDialog extends MediaOutputBaseDialog {
            isActiveRemoteDevice = mMediaOutputController.isActiveRemoteDevice(
                    mMediaOutputController.getCurrentConnectedMediaDevice());
        }
        boolean isBroadCastSupported = isBroadcastSupported();
        boolean showBroadcastButton = isBroadcastSupported() && mMediaOutputController.isPlaying();

        return (isActiveRemoteDevice || isBroadCastSupported) ? View.VISIBLE : View.GONE;
        return (isActiveRemoteDevice || showBroadcastButton) ? View.VISIBLE : View.GONE;
    }

    @Override
    public boolean isBroadcastSupported() {
        MediaDevice device = mMediaOutputController.getCurrentConnectedMediaDevice();
        if (device == null) {
            return false;
        return mMediaOutputController.isBroadcastSupported();
    }

    @Override
    public CharSequence getStopButtonText() {
        int resId = R.string.keyboard_key_media_stop;
        if (isBroadcastSupported() && mMediaOutputController.isPlaying()
                && !mMediaOutputController.isBluetoothLeBroadcastEnabled()) {
            resId = R.string.media_output_broadcast;
        }
        return mContext.getText(resId);
    }

    @Override
    public void onStopButtonClick() {
        if (isBroadcastSupported() && mMediaOutputController.isPlaying()) {
            if (!mMediaOutputController.isBluetoothLeBroadcastEnabled()) {
                if (startLeBroadcastDialogForFirstTime()) {
                    return;
                }
                startLeBroadcast();
            } else {
                stopLeBroadcast();
            }
        } else {
            mMediaOutputController.releaseSession();
            dismiss();
        }
        return mMediaOutputController.isBluetoothLeDevice(device);
    }

    @VisibleForTesting
Loading