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

Commit 8a3ebe25 authored by chelseahao's avatar chelseahao
Browse files

[Audiosharing] Add button action in detail page.

Bug: 308368124
Test: manual
Change-Id: I44e631cb75af432965d2221e71676146ea1537b6
parent 911d85a9
Loading
Loading
Loading
Loading
+137 −6
Original line number Diff line number Diff line
@@ -16,37 +16,168 @@

package com.android.settings.connecteddevice.audiosharing.audiostreams;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.content.Context;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.bluetooth.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.ActionButtonsPreference;

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

public class AudioStreamButtonController extends BasePreferenceController
        implements DefaultLifecycleObserver {
    private static final String TAG = "AudioStreamButtonController";
    private static final String KEY = "audio_stream_button";
    private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
            new AudioStreamsBroadcastAssistantCallback() {
                @Override
                public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {
                    super.onSourceRemoved(sink, sourceId, reason);
                    updateButton();
                }

                @Override
                public void onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) {
                    super.onSourceRemoveFailed(sink, sourceId, reason);
                    updateButton();
                }

                @Override
                public void onReceiveStateChanged(
                        BluetoothDevice sink,
                        int sourceId,
                        BluetoothLeBroadcastReceiveState state) {
                    super.onReceiveStateChanged(sink, sourceId, state);
                    if (mAudioStreamsHelper.isConnected(state)) {
                        updateButton();
                    }
                }

                @Override
                public void onSourceAddFailed(
                        BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason) {
                    super.onSourceAddFailed(sink, source, reason);
                    updateButton();
                }

                @Override
                public void onSourceLost(int broadcastId) {
                    super.onSourceLost(broadcastId);
                    updateButton();
                }
            };

    private final AudioStreamsRepository mAudioStreamsRepository =
            AudioStreamsRepository.getInstance();
    private final Executor mExecutor;
    private final AudioStreamsHelper mAudioStreamsHelper;
    private final @Nullable LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
    private @Nullable ActionButtonsPreference mPreference;
    private int mBroadcastId = -1;

    public AudioStreamButtonController(Context context, String preferenceKey) {
        super(context, preferenceKey);
        mExecutor = Executors.newSingleThreadExecutor();
        mAudioStreamsHelper = new AudioStreamsHelper(Utils.getLocalBtManager(context));
        mLeBroadcastAssistant = mAudioStreamsHelper.getLeBroadcastAssistant();
    }

    @Override
    public void onStart(@NonNull LifecycleOwner owner) {
        if (mLeBroadcastAssistant == null) {
            Log.w(TAG, "onStart(): LeBroadcastAssistant is null!");
            return;
        }
        mLeBroadcastAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
    }

    @Override
    public void onStop(@NonNull LifecycleOwner owner) {
        if (mLeBroadcastAssistant == null) {
            Log.w(TAG, "onStop(): LeBroadcastAssistant is null!");
            return;
        }
        mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
    }

    @Override
    public final void displayPreference(PreferenceScreen screen) {
        mPreference = screen.findPreference(getPreferenceKey());
        updateButton();
        super.displayPreference(screen);
    }

    private void updateButton() {
        if (mPreference != null) {
            if (mAudioStreamsHelper.getAllConnectedSources().stream()
                    .map(BluetoothLeBroadcastReceiveState::getBroadcastId)
                    .anyMatch(connectedBroadcastId -> connectedBroadcastId == mBroadcastId)) {
                ThreadUtils.postOnMainThread(
                        () -> {
                            if (mPreference != null) {
                                mPreference.setButton1Enabled(true);
            // TODO(chelseahao): update this based on stream connection state
                                mPreference
                    .setButton1Text(R.string.bluetooth_device_context_disconnect)
                    .setButton1Icon(R.drawable.ic_settings_close);
                                        .setButton1Text(
                                                R.string.bluetooth_device_context_disconnect)
                                        .setButton1Icon(R.drawable.ic_settings_close)
                                        .setButton1OnClickListener(
                                                unused -> {
                                                    if (mPreference != null) {
                                                        mPreference.setButton1Enabled(false);
                                                    }
                                                    mAudioStreamsHelper.removeSource(mBroadcastId);
                                                });
                            }
                        });
            } else {
                View.OnClickListener clickToRejoin =
                        unused ->
                                ThreadUtils.postOnBackgroundThread(
                                        () -> {
                                            var metadata =
                                                    mAudioStreamsRepository.getSavedMetadata(
                                                            mContext, mBroadcastId);
                                            if (metadata != null) {
                                                mAudioStreamsHelper.addSource(metadata);
                                                ThreadUtils.postOnMainThread(
                                                        () -> {
                                                            if (mPreference != null) {
                                                                mPreference.setButton1Enabled(
                                                                        false);
                                                            }
                                                        });
                                            }
                                        });
                ThreadUtils.postOnMainThread(
                        () -> {
                            if (mPreference != null) {
                                mPreference.setButton1Enabled(true);
                                mPreference
                                        .setButton1Text(R.string.bluetooth_device_context_connect)
                                        .setButton1Icon(R.drawable.ic_add_24dp)
                                        .setButton1OnClickListener(clickToRejoin);
                            }
                        });
            }
        } else {
            Log.w(TAG, "updateButton(): preference is null!");
        }
        super.displayPreference(screen);
    }

    @Override
+89 −3
Original line number Diff line number Diff line
@@ -16,22 +16,64 @@

package com.android.settings.connecteddevice.audiosharing.audiostreams;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.content.Context;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.bluetooth.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.LayoutPreference;

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

import javax.annotation.Nullable;

public class AudioStreamHeaderController extends BasePreferenceController
        implements DefaultLifecycleObserver {
    private static final String TAG = "AudioStreamHeaderController";
    private static final String KEY = "audio_stream_header";
    private final Executor mExecutor;
    private final AudioStreamsHelper mAudioStreamsHelper;
    @Nullable private final LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
    private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
            new AudioStreamsBroadcastAssistantCallback() {
                @Override
                public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {
                    super.onSourceRemoved(sink, sourceId, reason);
                    updateSummary();
                }

                @Override
                public void onSourceLost(int broadcastId) {
                    super.onSourceLost(broadcastId);
                    updateSummary();
                }

                @Override
                public void onReceiveStateChanged(
                        BluetoothDevice sink,
                        int sourceId,
                        BluetoothLeBroadcastReceiveState state) {
                    super.onReceiveStateChanged(sink, sourceId, state);
                    if (mAudioStreamsHelper.isConnected(state)) {
                        updateSummary();
                    }
                }
            };

    private @Nullable EntityHeaderController mHeaderController;
    private @Nullable DashboardFragment mFragment;
    private String mBroadcastName = "";
@@ -39,6 +81,27 @@ public class AudioStreamHeaderController extends BasePreferenceController

    public AudioStreamHeaderController(Context context, String preferenceKey) {
        super(context, preferenceKey);
        mExecutor = Executors.newSingleThreadExecutor();
        mAudioStreamsHelper = new AudioStreamsHelper(Utils.getLocalBtManager(context));
        mLeBroadcastAssistant = mAudioStreamsHelper.getLeBroadcastAssistant();
    }

    @Override
    public void onStart(@NonNull LifecycleOwner owner) {
        if (mLeBroadcastAssistant == null) {
            Log.w(TAG, "onStart(): LeBroadcastAssistant is null!");
            return;
        }
        mLeBroadcastAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
    }

    @Override
    public void onStop(@NonNull LifecycleOwner owner) {
        if (mLeBroadcastAssistant == null) {
            Log.w(TAG, "onStop(): LeBroadcastAssistant is null!");
            return;
        }
        mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
    }

    @Override
@@ -55,14 +118,37 @@ public class AudioStreamHeaderController extends BasePreferenceController
            }
            mHeaderController.setIcon(
                    screen.getContext().getDrawable(R.drawable.ic_bt_audio_sharing));
            // TODO(chelseahao): update this based on stream connection state
            mHeaderController.setSummary("Listening now");
            mHeaderController.done(true);
            screen.addPreference(headerPreference);
            updateSummary();
        }
        super.displayPreference(screen);
    }

    private void updateSummary() {
        var unused =
                ThreadUtils.postOnBackgroundThread(
                        () -> {
                            var latestSummary =
                                    mAudioStreamsHelper.getAllConnectedSources().stream()
                                                    .map(
                                                            BluetoothLeBroadcastReceiveState
                                                                    ::getBroadcastId)
                                                    .anyMatch(
                                                            connectedBroadcastId ->
                                                                    connectedBroadcastId
                                                                            == mBroadcastId)
                                            ? "Listening now"
                                            : "";
                            ThreadUtils.postOnMainThread(
                                    () -> {
                                        if (mHeaderController != null) {
                                            mHeaderController.setSummary(latestSummary);
                                            mHeaderController.done(true);
                                        }
                                    });
                        });
    }

    @Override
    public int getAvailabilityStatus() {
        return AVAILABLE;
+0 −41
Original line number Diff line number Diff line
@@ -24,21 +24,12 @@ import android.util.Log;

import com.android.settingslib.bluetooth.BluetoothUtils;

import java.util.Locale;

public class AudioStreamsBroadcastAssistantCallback
        implements BluetoothLeBroadcastAssistant.Callback {

    private static final String TAG = "AudioStreamsBroadcastAssistantCallback";
    private static final boolean DEBUG = BluetoothUtils.D;

    private final AudioStreamsProgressCategoryController mCategoryController;

    public AudioStreamsBroadcastAssistantCallback(
            AudioStreamsProgressCategoryController audioStreamsProgressCategoryController) {
        mCategoryController = audioStreamsProgressCategoryController;
    }

    @Override
    public void onReceiveStateChanged(
            BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state) {
@@ -52,45 +43,30 @@ public class AudioStreamsBroadcastAssistantCallback
                            + " state: "
                            + state);
        }
        mCategoryController.handleSourceConnected(state);
    }

    @Override
    public void onSearchStartFailed(int reason) {
        Log.w(TAG, "onSearchStartFailed() reason : " + reason);
        mCategoryController.showToast(
                String.format(Locale.US, "Failed to start scanning, reason %d", reason));
    }

    @Override
    public void onSearchStarted(int reason) {
        if (mCategoryController == null) {
            Log.w(TAG, "onSearchStarted() : mCategoryController is null!");
            return;
        }
        if (DEBUG) {
            Log.d(TAG, "onSearchStarted() reason : " + reason);
        }
        mCategoryController.setScanning(true);
    }

    @Override
    public void onSearchStopFailed(int reason) {
        Log.w(TAG, "onSearchStopFailed() reason : " + reason);
        mCategoryController.showToast(
                String.format(Locale.US, "Failed to stop scanning, reason %d", reason));
    }

    @Override
    public void onSearchStopped(int reason) {
        if (mCategoryController == null) {
            Log.w(TAG, "onSearchStopped() : mCategoryController is null!");
            return;
        }
        if (DEBUG) {
            Log.d(TAG, "onSearchStopped() reason : " + reason);
        }
        mCategoryController.setScanning(false);
    }

    @Override
@@ -106,8 +82,6 @@ public class AudioStreamsBroadcastAssistantCallback
                            + " reason: "
                            + reason);
        }
        mCategoryController.showToast(
                String.format(Locale.US, "Failed to join broadcast, reason %d", reason));
    }

    @Override
@@ -126,14 +100,9 @@ public class AudioStreamsBroadcastAssistantCallback

    @Override
    public void onSourceFound(BluetoothLeBroadcastMetadata source) {
        if (mCategoryController == null) {
            Log.w(TAG, "onSourceFound() : mCategoryController is null!");
            return;
        }
        if (DEBUG) {
            Log.d(TAG, "onSourceFound() broadcastId : " + source.getBroadcastId());
        }
        mCategoryController.handleSourceFound(source);
    }

    @Override
@@ -141,7 +110,6 @@ public class AudioStreamsBroadcastAssistantCallback
        if (DEBUG) {
            Log.d(TAG, "onSourceLost() broadcastId : " + broadcastId);
        }
        mCategoryController.handleSourceLost(broadcastId);
    }

    @Override
@@ -153,12 +121,6 @@ public class AudioStreamsBroadcastAssistantCallback
    @Override
    public void onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) {
        Log.w(TAG, "onSourceRemoveFailed() sourceId : " + sourceId + " reason : " + reason);
        mCategoryController.showToast(
                String.format(
                        Locale.US,
                        "Failed to remove source %d for sink %s",
                        sourceId,
                        sink.getAddress()));
    }

    @Override
@@ -166,8 +128,5 @@ public class AudioStreamsBroadcastAssistantCallback
        if (DEBUG) {
            Log.d(TAG, "onSourceRemoved() sourceId : " + sourceId + " reason : " + reason);
        }
        mCategoryController.showToast(
                String.format(
                        Locale.US, "Source %d removed for sink %s", sourceId, sink.getAddress()));
    }
}
+119 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.settings.connecteddevice.audiosharing.audiostreams;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.util.Log;

import java.util.Locale;

public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastAssistantCallback {
    private static final String TAG = "AudioStreamsProgressCategoryCallback";

    private final AudioStreamsProgressCategoryController mCategoryController;

    public AudioStreamsProgressCategoryCallback(
            AudioStreamsProgressCategoryController audioStreamsProgressCategoryController) {
        mCategoryController = audioStreamsProgressCategoryController;
    }

    @Override
    public void onReceiveStateChanged(
            BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state) {
        super.onReceiveStateChanged(sink, sourceId, state);
        mCategoryController.handleSourceConnected(state);
    }

    @Override
    public void onSearchStartFailed(int reason) {
        super.onSearchStartFailed(reason);
        mCategoryController.showToast(
                String.format(Locale.US, "Failed to start scanning, reason %d", reason));
    }

    @Override
    public void onSearchStarted(int reason) {
        super.onSearchStarted(reason);
        if (mCategoryController == null) {
            Log.w(TAG, "onSearchStarted() : mCategoryController is null!");
            return;
        }
        mCategoryController.setScanning(true);
    }

    @Override
    public void onSearchStopFailed(int reason) {
        super.onSearchStopFailed(reason);
        mCategoryController.showToast(
                String.format(Locale.US, "Failed to stop scanning, reason %d", reason));
    }

    @Override
    public void onSearchStopped(int reason) {
        super.onSearchStopped(reason);
        if (mCategoryController == null) {
            Log.w(TAG, "onSearchStopped() : mCategoryController is null!");
            return;
        }
        mCategoryController.setScanning(false);
    }

    @Override
    public void onSourceAddFailed(
            BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason) {
        super.onSourceAddFailed(sink, source, reason);
        mCategoryController.showToast(
                String.format(Locale.US, "Failed to join broadcast, reason %d", reason));
    }

    @Override
    public void onSourceFound(BluetoothLeBroadcastMetadata source) {
        super.onSourceFound(source);
        if (mCategoryController == null) {
            Log.w(TAG, "onSourceFound() : mCategoryController is null!");
            return;
        }
        mCategoryController.handleSourceFound(source);
    }

    @Override
    public void onSourceLost(int broadcastId) {
        super.onSourceLost(broadcastId);
        mCategoryController.handleSourceLost(broadcastId);
    }

    @Override
    public void onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) {
        super.onSourceRemoveFailed(sink, sourceId, reason);
        mCategoryController.showToast(
                String.format(
                        Locale.US,
                        "Failed to remove source %d for sink %s",
                        sourceId,
                        sink.getAddress()));
    }

    @Override
    public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {
        super.onSourceRemoved(sink, sourceId, reason);
        mCategoryController.showToast(
                String.format(
                        Locale.US, "Source %d removed for sink %s", sourceId, sink.getAddress()));
    }
}
+22 −4

File changed.

Preview size limit exceeded, changes collapsed.

Loading