diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java index d4803c63699..0a6795f2288 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java @@ -16,10 +16,12 @@ package com.android.settings.connecteddevice.audiosharing; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastAssistant; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.pm.PackageManager; import android.util.Log; @@ -38,9 +40,14 @@ import com.android.settings.core.BasePreferenceController; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.flags.Flags; import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LeAudioProfile; +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfile; +import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -53,11 +60,13 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro "connected_device_audio_sharing_settings"; private final LocalBluetoothManager mLocalBtManager; + private final LocalBluetoothLeBroadcast mBroadcast; private final LocalBluetoothLeBroadcastAssistant mAssistant; private final Executor mExecutor; private PreferenceGroup mPreferenceGroup; private Preference mAudioSharingSettingsPreference; private BluetoothDeviceUpdater mBluetoothDeviceUpdater; + private DashboardFragment mFragment; private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = new BluetoothLeBroadcastAssistant.Callback() { @@ -149,6 +158,7 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro public AudioSharingDevicePreferenceController(Context context) { super(context, KEY); mLocalBtManager = Utils.getLocalBtManager(mContext); + mBroadcast = mLocalBtManager.getProfileManager().getLeAudioBroadcastProfile(); mAssistant = mLocalBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile(); mExecutor = Executors.newSingleThreadExecutor(); } @@ -156,15 +166,15 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro @Override public void onStart(@NonNull LifecycleOwner owner) { if (mLocalBtManager == null) { - Log.e(TAG, "onStart() Bluetooth is not supported on this device"); + Log.d(TAG, "onStart() Bluetooth is not supported on this device"); return; } if (mAssistant == null) { - Log.e(TAG, "onStart() Broadcast assistant is not supported on this device"); + Log.d(TAG, "onStart() Broadcast assistant is not supported on this device"); return; } if (mBluetoothDeviceUpdater == null) { - Log.e(TAG, "onStart() Bluetooth device updater is not initialized"); + Log.d(TAG, "onStart() Bluetooth device updater is not initialized"); return; } mLocalBtManager.getEventManager().registerCallback(this); @@ -176,15 +186,15 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro @Override public void onStop(@NonNull LifecycleOwner owner) { if (mLocalBtManager == null) { - Log.e(TAG, "onStop() Bluetooth is not supported on this device"); + Log.d(TAG, "onStop() Bluetooth is not supported on this device"); return; } if (mAssistant == null) { - Log.e(TAG, "onStop() Broadcast assistant is not supported on this device"); + Log.d(TAG, "onStop() Broadcast assistant is not supported on this device"); return; } if (mBluetoothDeviceUpdater == null) { - Log.e(TAG, "onStop() Bluetooth device updater is not initialized"); + Log.d(TAG, "onStop() Bluetooth device updater is not initialized"); return; } mLocalBtManager.getEventManager().unregisterCallback(this); @@ -244,17 +254,60 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro } } + @Override + public void onProfileConnectionStateChanged( + @NonNull CachedBluetoothDevice cachedDevice, + @ConnectionState int state, + int bluetoothProfile) { + if (state != BluetoothAdapter.STATE_CONNECTED || !cachedDevice.getDevice().isConnected()) { + Log.d(TAG, "Ignore onProfileConnectionStateChanged, not connected state"); + return; + } + List supportedProfiles = cachedDevice.getProfiles(); + boolean isLeAudioSupported = false; + for (LocalBluetoothProfile profile : supportedProfiles) { + if (profile instanceof LeAudioProfile && profile.isEnabled(cachedDevice.getDevice())) { + isLeAudioSupported = true; + } + if (profile.getProfileId() != bluetoothProfile + && profile.getConnectionStatus(cachedDevice.getDevice()) + == BluetoothProfile.STATE_CONNECTED) { + Log.d( + TAG, + "Ignore onProfileConnectionStateChanged, not the first connected profile"); + return; + } + } + // Show stop audio sharing dialog when an ineligible (not le audio) remote device connected + // during a sharing session. + if (isBroadcasting() && !isLeAudioSupported) { + if (mFragment != null) { + AudioSharingStopDialogFragment.show( + mFragment, + cachedDevice.getName(), + () -> { + mBroadcast.stopBroadcast(mBroadcast.getLatestBroadcastId()); + }); + } + } + } + /** * Initialize the controller. * * @param fragment The fragment to provide the context and metrics category for {@link - * AudioSharingBluetoothDeviceUpdater}. + * AudioSharingBluetoothDeviceUpdater} and provide the host for dialogs. */ public void init(DashboardFragment fragment) { + mFragment = fragment; mBluetoothDeviceUpdater = new AudioSharingBluetoothDeviceUpdater( fragment.getContext(), AudioSharingDevicePreferenceController.this, fragment.getMetricsCategory()); } + + private boolean isBroadcasting() { + return mBroadcast != null && mBroadcast.isEnabled(null); + } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java new file mode 100644 index 00000000000..495fad3bdcb --- /dev/null +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 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; + +import android.app.Dialog; +import android.app.settings.SettingsEnums; +import android.os.Bundle; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; + +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settings.flags.Flags; + +public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment { + private static final String TAG = "AudioSharingStopDialog"; + + private static final String BUNDLE_KEY_NEW_DEVICE_NAME = "bundle_key_new_device_name"; + + // 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 stop sharing button in the dialog. */ + void onStopSharingClick(); + } + + private static DialogEventListener sListener; + + @Override + public int getMetricsCategory() { + return SettingsEnums.DIALOG_STOP_AUDIO_SHARING; + } + + /** + * Display the {@link AudioSharingStopDialogFragment} dialog. + * + * @param host The Fragment this dialog will be hosted. + */ + public static void show(Fragment host, String newDeviceName, DialogEventListener listener) { + if (!Flags.enableLeAudioSharing()) return; + final FragmentManager manager = host.getChildFragmentManager(); + sListener = listener; + if (manager.findFragmentByTag(TAG) == null) { + final Bundle bundle = new Bundle(); + bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName); + AudioSharingStopDialogFragment dialog = new AudioSharingStopDialogFragment(); + dialog.setArguments(bundle); + dialog.show(manager, TAG); + } + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Bundle arguments = requireArguments(); + String newDeviceName = arguments.getString(BUNDLE_KEY_NEW_DEVICE_NAME); + final AlertDialog.Builder builder = + new AlertDialog.Builder(getActivity()) + .setTitle("Stop sharing audio?") + .setCancelable(false); + builder.setMessage( + newDeviceName + " is connected, devices in audio sharing will disconnect."); + builder.setPositiveButton( + "Stop sharing", + (dialog, which) -> { + sListener.onStopSharingClick(); + }); + builder.setNegativeButton( + "Cancel", + (dialog, which) -> { + dismiss(); + }); + AlertDialog dialog = builder.create(); + dialog.setCanceledOnTouchOutside(false); + return dialog; + } +}