Loading src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java +60 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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() { Loading Loading @@ -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(); } Loading @@ -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); Loading @@ -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); Loading Loading @@ -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<LocalBluetoothProfile> 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); } } src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java 0 → 100644 +91 −0 Original line number Diff line number Diff line /* * 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; } } Loading
src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java +60 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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() { Loading Loading @@ -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(); } Loading @@ -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); Loading @@ -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); Loading Loading @@ -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<LocalBluetoothProfile> 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); } }
src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java 0 → 100644 +91 −0 Original line number Diff line number Diff line /* * 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; } }