Merge "[Audiosharing] Impl audio sharing main switch." into main

This commit is contained in:
Yiyi Shen
2023-11-03 12:27:41 +00:00
committed by Android (Google) Code Review
2 changed files with 186 additions and 22 deletions

View File

@@ -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);
if (deviceNames.isEmpty()) {
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"); subTitle1.setText("2 devices connected");
subTitle2.setText("placeholder"); 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;
} }
} }

View File

@@ -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!");
mSwitchBar.setEnabled(true);
return;
}
if (mFragment == null) {
Log.w(TAG, "Dialog fail to show due to null fragment."); 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);
} }
} }