[Audiosharing] Impl audio sharing main switch.
Start/stop broadcast when no eligible buds connected. Flagged with enable_le_audio_sharing Bug: 305620450 Test: Manual Change-Id: I04359c0954e336ceb1a89a7836199b5be0b5e0c5
This commit is contained in:
@@ -38,6 +38,26 @@ import java.util.ArrayList;
|
|||||||
public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
|
public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
|
||||||
private static final String TAG = "AudioSharingDialog";
|
private static final String TAG = "AudioSharingDialog";
|
||||||
|
|
||||||
|
private static final String BUNDLE_KEY_DEVICE_NAMES = "bundle_key_device_names";
|
||||||
|
|
||||||
|
// The host creates an instance of this dialog fragment must implement this interface to receive
|
||||||
|
// event callbacks.
|
||||||
|
public interface DialogEventListener {
|
||||||
|
/**
|
||||||
|
* Called when users click the device item for sharing in the dialog.
|
||||||
|
*
|
||||||
|
* @param position The position of the item clicked.
|
||||||
|
*/
|
||||||
|
void onItemClick(int position);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when users click the cancel button in the dialog.
|
||||||
|
*/
|
||||||
|
void onCancelClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DialogEventListener sListener;
|
||||||
|
|
||||||
private View mRootView;
|
private View mRootView;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -50,40 +70,59 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
|
|||||||
*
|
*
|
||||||
* @param host The Fragment this dialog will be hosted.
|
* @param host The Fragment this dialog will be hosted.
|
||||||
*/
|
*/
|
||||||
public static void show(Fragment host) {
|
public static void show(
|
||||||
|
Fragment host, ArrayList<String> deviceNames, DialogEventListener listener) {
|
||||||
if (!Flags.enableLeAudioSharing()) return;
|
if (!Flags.enableLeAudioSharing()) return;
|
||||||
final FragmentManager manager = host.getChildFragmentManager();
|
final FragmentManager manager = host.getChildFragmentManager();
|
||||||
|
sListener = listener;
|
||||||
if (manager.findFragmentByTag(TAG) == null) {
|
if (manager.findFragmentByTag(TAG) == null) {
|
||||||
final AudioSharingDialogFragment dialog = new AudioSharingDialogFragment();
|
final Bundle bundle = new Bundle();
|
||||||
|
bundle.putStringArrayList(BUNDLE_KEY_DEVICE_NAMES, deviceNames);
|
||||||
|
AudioSharingDialogFragment dialog = new AudioSharingDialogFragment();
|
||||||
|
dialog.setArguments(bundle);
|
||||||
dialog.show(manager, TAG);
|
dialog.show(manager, TAG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
Bundle arguments = requireArguments();
|
||||||
|
ArrayList<String> deviceNames = arguments.getStringArrayList(BUNDLE_KEY_DEVICE_NAMES);
|
||||||
final AlertDialog.Builder builder =
|
final AlertDialog.Builder builder =
|
||||||
new AlertDialog.Builder(getActivity()).setTitle("Share audio");
|
new AlertDialog.Builder(getActivity()).setTitle("Share audio").setCancelable(false);
|
||||||
mRootView =
|
mRootView =
|
||||||
LayoutInflater.from(builder.getContext())
|
LayoutInflater.from(builder.getContext())
|
||||||
.inflate(R.layout.dialog_audio_sharing, /* parent= */ null);
|
.inflate(R.layout.dialog_audio_sharing, /* parent= */ null);
|
||||||
// TODO: use real subtitle according to device count.
|
|
||||||
TextView subTitle1 = mRootView.findViewById(R.id.share_audio_subtitle1);
|
TextView subTitle1 = mRootView.findViewById(R.id.share_audio_subtitle1);
|
||||||
TextView subTitle2 = mRootView.findViewById(R.id.share_audio_subtitle2);
|
TextView subTitle2 = mRootView.findViewById(R.id.share_audio_subtitle2);
|
||||||
subTitle1.setText("2 devices connected");
|
if (deviceNames.isEmpty()) {
|
||||||
subTitle2.setText("placeholder");
|
subTitle1.setVisibility(View.INVISIBLE);
|
||||||
|
subTitle2.setText("To start sharing audio, connect headphones that support LE audio");
|
||||||
|
builder.setNegativeButton(
|
||||||
|
"Close",
|
||||||
|
(dialog, which) -> {
|
||||||
|
sListener.onCancelClick();
|
||||||
|
});
|
||||||
|
} else if (deviceNames.size() == 1) {
|
||||||
|
// TODO: add real impl
|
||||||
|
subTitle1.setText("1 devices connected");
|
||||||
|
subTitle2.setText("placeholder");
|
||||||
|
} else {
|
||||||
|
// TODO: add real impl
|
||||||
|
subTitle1.setText("2 devices connected");
|
||||||
|
subTitle2.setText("placeholder");
|
||||||
|
}
|
||||||
RecyclerView recyclerView = mRootView.findViewById(R.id.btn_list);
|
RecyclerView recyclerView = mRootView.findViewById(R.id.btn_list);
|
||||||
// TODO: use real audio sharing device list.
|
|
||||||
ArrayList<String> devices = new ArrayList<>();
|
|
||||||
devices.add("Buds 1");
|
|
||||||
devices.add("Buds 2");
|
|
||||||
recyclerView.setAdapter(
|
recyclerView.setAdapter(
|
||||||
new AudioSharingDeviceAdapter(
|
new AudioSharingDeviceAdapter(
|
||||||
devices,
|
deviceNames,
|
||||||
(int position) -> {
|
(int position) -> {
|
||||||
// TODO: add on click callback.
|
sListener.onItemClick(position);
|
||||||
}));
|
}));
|
||||||
recyclerView.setLayoutManager(
|
recyclerView.setLayoutManager(
|
||||||
new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
|
new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
|
||||||
return builder.setView(mRootView).create();
|
AlertDialog dialog = builder.setView(mRootView).create();
|
||||||
|
dialog.setCanceledOnTouchOutside(false);
|
||||||
|
return dialog;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.connecteddevice.audiosharing;
|
package com.android.settings.connecteddevice.audiosharing;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothLeBroadcast;
|
||||||
|
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Switch;
|
import android.widget.Switch;
|
||||||
@@ -24,36 +26,116 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
|
||||||
|
import com.android.settings.bluetooth.Utils;
|
||||||
import com.android.settings.core.BasePreferenceController;
|
import com.android.settings.core.BasePreferenceController;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
import com.android.settings.flags.Flags;
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settings.widget.SettingsMainSwitchBar;
|
import com.android.settings.widget.SettingsMainSwitchBar;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
import com.android.settingslib.widget.OnMainSwitchChangeListener;
|
import com.android.settingslib.widget.OnMainSwitchChangeListener;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class AudioSharingSwitchBarController extends BasePreferenceController
|
public class AudioSharingSwitchBarController extends BasePreferenceController
|
||||||
implements DefaultLifecycleObserver, OnMainSwitchChangeListener {
|
implements DefaultLifecycleObserver, OnMainSwitchChangeListener {
|
||||||
private static final String TAG = "AudioSharingSwitchBarCtl";
|
private static final String TAG = "AudioSharingSwitchBarCtl";
|
||||||
private static final String PREF_KEY = "audio_sharing_main_switch";
|
private static final String PREF_KEY = "audio_sharing_main_switch";
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final SettingsMainSwitchBar mSwitchBar;
|
private final SettingsMainSwitchBar mSwitchBar;
|
||||||
|
private final LocalBluetoothManager mBtManager;
|
||||||
|
private final LocalBluetoothLeBroadcast mBroadcast;
|
||||||
|
private final Executor mExecutor;
|
||||||
private DashboardFragment mFragment;
|
private DashboardFragment mFragment;
|
||||||
|
|
||||||
|
private final BluetoothLeBroadcast.Callback mBroadcastCallback =
|
||||||
|
new BluetoothLeBroadcast.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onBroadcastStarted(int reason, int broadcastId) {
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"onBroadcastStarted(), reason = "
|
||||||
|
+ reason
|
||||||
|
+ ", broadcastId = "
|
||||||
|
+ broadcastId);
|
||||||
|
updateSwitch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBroadcastStartFailed(int reason) {
|
||||||
|
Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
|
||||||
|
// TODO: handle broadcast start fail
|
||||||
|
updateSwitch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBroadcastMetadataChanged(
|
||||||
|
int broadcastId, @NonNull BluetoothLeBroadcastMetadata metadata) {
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"onBroadcastMetadataChanged(), broadcastId = "
|
||||||
|
+ broadcastId
|
||||||
|
+ ", metadata = "
|
||||||
|
+ metadata);
|
||||||
|
// TODO: handle add sink if there are connected lea devices.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBroadcastStopped(int reason, int broadcastId) {
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"onBroadcastStopped(), reason = "
|
||||||
|
+ reason
|
||||||
|
+ ", broadcastId = "
|
||||||
|
+ broadcastId);
|
||||||
|
updateSwitch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBroadcastStopFailed(int reason) {
|
||||||
|
Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason);
|
||||||
|
// TODO: handle broadcast stop fail
|
||||||
|
updateSwitch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBroadcastUpdated(int reason, int broadcastId) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBroadcastUpdateFailed(int reason, int broadcastId) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStarted(int reason, int broadcastId) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStopped(int reason, int broadcastId) {}
|
||||||
|
};
|
||||||
|
|
||||||
AudioSharingSwitchBarController(Context context, SettingsMainSwitchBar switchBar) {
|
AudioSharingSwitchBarController(Context context, SettingsMainSwitchBar switchBar) {
|
||||||
super(context, PREF_KEY);
|
super(context, PREF_KEY);
|
||||||
mContext = context;
|
|
||||||
mSwitchBar = switchBar;
|
mSwitchBar = switchBar;
|
||||||
mSwitchBar.setChecked(false);
|
mBtManager = Utils.getLocalBtManager(context);
|
||||||
|
mBroadcast = mBtManager.getProfileManager().getLeAudioBroadcastProfile();
|
||||||
|
mExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
mSwitchBar.setChecked(isBroadcasting());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart(@NonNull LifecycleOwner owner) {
|
public void onStart(@NonNull LifecycleOwner owner) {
|
||||||
mSwitchBar.addOnSwitchChangeListener(this);
|
mSwitchBar.addOnSwitchChangeListener(this);
|
||||||
|
if (mBroadcast != null) {
|
||||||
|
mBroadcast.registerServiceCallBack(mExecutor, mBroadcastCallback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop(@NonNull LifecycleOwner owner) {
|
public void onStop(@NonNull LifecycleOwner owner) {
|
||||||
mSwitchBar.removeOnSwitchChangeListener(this);
|
mSwitchBar.removeOnSwitchChangeListener(this);
|
||||||
|
if (mBroadcast != null) {
|
||||||
|
mBroadcast.unregisterServiceCallBack(mBroadcastCallback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -63,7 +145,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
startAudioSharing();
|
startAudioSharing();
|
||||||
} else {
|
} else {
|
||||||
// TODO: stop sharing
|
stopAudioSharing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,10 +164,53 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void startAudioSharing() {
|
private void startAudioSharing() {
|
||||||
if (mFragment != null) {
|
mSwitchBar.setEnabled(false);
|
||||||
AudioSharingDialogFragment.show(mFragment);
|
if (mBroadcast == null || isBroadcasting()) {
|
||||||
} else {
|
Log.d(TAG, "Already in broadcasting or broadcast not support, ignore!");
|
||||||
Log.w(TAG, "Dialog fail to show due to null fragment.");
|
mSwitchBar.setEnabled(true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
if (mFragment == null) {
|
||||||
|
Log.w(TAG, "Dialog fail to show due to null fragment.");
|
||||||
|
mSwitchBar.setEnabled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ArrayList<String> deviceNames = new ArrayList<>();
|
||||||
|
AudioSharingDialogFragment.show(
|
||||||
|
mFragment,
|
||||||
|
deviceNames,
|
||||||
|
new AudioSharingDialogFragment.DialogEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(int position) {
|
||||||
|
// TODO: handle broadcast based on the dialog device item clicked
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelClick() {
|
||||||
|
mBroadcast.startBroadcast("test", /* language= */ null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopAudioSharing() {
|
||||||
|
mSwitchBar.setEnabled(false);
|
||||||
|
if (mBroadcast == null || !isBroadcasting()) {
|
||||||
|
Log.d(TAG, "Already not broadcasting or broadcast not support, ignore!");
|
||||||
|
mSwitchBar.setEnabled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mBroadcast.stopBroadcast(mBroadcast.getLatestBroadcastId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSwitch() {
|
||||||
|
ThreadUtils.postOnMainThread(
|
||||||
|
() -> {
|
||||||
|
mSwitchBar.setChecked(isBroadcasting());
|
||||||
|
mSwitchBar.setEnabled(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBroadcasting() {
|
||||||
|
return mBroadcast != null && mBroadcast.isEnabled(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user