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

Commit f0f7d1a2 authored by Chelsea Hao's avatar Chelsea Hao Committed by Android (Google) Code Review
Browse files

Merge "Add developer option for le audio sharing ui flow." into main

parents 9a022bcf 97dbd0bb
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -260,6 +260,13 @@
    <string name="bluetooth_disable_leaudio_summary">Disables Bluetooth LE audio feature if the device supports LE audio hardware capabilities.</string>
    <!-- Setting toggle title for switch Bluetooth LE Audio mode. [CHAR LIMIT=40] -->
    <string name="bluetooth_leaudio_mode">Bluetooth LE Audio mode</string>
    <!-- Setting toggle title for enabling Bluetooth LE Audio UI preview. [CHAR LIMIT=none] -->
    <string name="bluetooth_leaudio_broadcast_ui">Enable Bluetooth LE Audio Broadcast UI preview</string>
    <!-- Summary of toggle for enabling Bluetooth LE Audio UI preview. [CHAR LIMIT=none]-->
    <string name="bluetooth_leaudio_broadcast_ui_summary">Enables the LE Audio Sharing UI preview
        including personal audio sharing and private broadcast</string>
    <!-- Setting toggle title for enabling Bluetooth LE Audio toggle in Device Details. [CHAR LIMIT=40] -->
    <string name="bluetooth_show_leaudio_device_details">Show LE audio toggle in Device Details</string>
+5 −0
Original line number Diff line number Diff line
@@ -393,6 +393,11 @@
            android:entries="@array/bluetooth_leaudio_mode"
            android:entryValues="@array/bluetooth_leaudio_mode_values"/>

        <SwitchPreferenceCompat
            android:key="bluetooth_leaudio_broadcast_ui"
            android:title="@string/bluetooth_leaudio_broadcast_ui"
            android:summary="@string/bluetooth_leaudio_broadcast_ui_summary"/>

        <SwitchPreferenceCompat
            android:key="bluetooth_show_leaudio_device_details"
            android:title="@string/bluetooth_show_leaudio_device_details"/>
+20 −18
Original line number Diff line number Diff line
@@ -34,12 +34,10 @@ import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;

import java.util.Objects;

/**
 * Preference controller to control Bluetooth LE audio mode
 */
public class BluetoothLeAudioModePreferenceController
        extends DeveloperOptionsPreferenceController
/** Preference controller to control Bluetooth LE audio mode */
public class BluetoothLeAudioModePreferenceController extends DeveloperOptionsPreferenceController
        implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {

    private static final String PREFERENCE_KEY = "bluetooth_leaudio_mode";
@@ -51,15 +49,13 @@ public class BluetoothLeAudioModePreferenceController

    private final String[] mListValues;
    private final String[] mListSummaries;
    @VisibleForTesting
    @Nullable String mNewMode;
    @VisibleForTesting
    BluetoothAdapter mBluetoothAdapter;
    @VisibleForTesting @Nullable String mNewMode;
    @VisibleForTesting BluetoothAdapter mBluetoothAdapter;

    boolean mChanged = false;

    public BluetoothLeAudioModePreferenceController(@NonNull Context context,
            @Nullable DevelopmentSettingsDashboardFragment fragment) {
    public BluetoothLeAudioModePreferenceController(
            @NonNull Context context, @Nullable DevelopmentSettingsDashboardFragment fragment) {
        super(context);
        mFragment = fragment;
        mBluetoothAdapter = context.getSystemService(BluetoothManager.class).getAdapter();
@@ -69,7 +65,8 @@ public class BluetoothLeAudioModePreferenceController
    }

    @Override
    @NonNull public String getPreferenceKey() {
    @NonNull
    public String getPreferenceKey() {
        return PREFERENCE_KEY;
    }

@@ -125,20 +122,25 @@ public class BluetoothLeAudioModePreferenceController
        }
    }

    /**
     * Called when the RebootDialog confirm is clicked.
     */
    /** Called when the RebootDialog confirm is clicked. */
    public void onRebootDialogConfirmed() {
        if (!mChanged) {
            return;
        }
        SystemProperties.set(LE_AUDIO_DYNAMIC_SWITCHER_MODE_PROPERTY, mNewMode);
        if (mFragment != null && !Objects.equals(mNewMode, "broadcast")) {
            mFragment.onBroadcastDisabled();
        }
    }

    /**
     * Called when the RebootDialog cancel is clicked.
     */
    /** Called when the RebootDialog cancel is clicked. */
    public void onRebootDialogCanceled() {
        mChanged = false;
    }

    public interface OnModeChangeListener {

        /** Called when the broadcast mode is disabled. */
        void onBroadcastDisabled();
    }
}
+144 −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.development;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.content.ContentResolver;
import android.content.Context;
import android.provider.Settings;
import android.sysprop.BluetoothProperties;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.SwitchPreferenceCompat;

import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
import com.android.settingslib.flags.Flags;
import com.android.settingslib.utils.ThreadUtils;

/** Preference controller to enable / disable the Bluetooth LE audio sharing UI flow */
public class BluetoothLeAudioUiPreferenceController extends DeveloperOptionsPreferenceController
        implements Preference.OnPreferenceChangeListener,
                PreferenceControllerMixin,
                BluetoothLeAudioModePreferenceController.OnModeChangeListener {
    private static final String TAG = "BluetoothLeAudioUiPreferenceController";
    private static final String PREFERENCE_KEY = "bluetooth_leaudio_broadcast_ui";

    @VisibleForTesting
    static final String VALUE_KEY = "bluetooth_le_audio_sharing_ui_preview_enabled";

    @VisibleForTesting static final int VALUE_OFF = 0;
    @VisibleForTesting static final int VALUE_ON = 1;
    @VisibleForTesting static final int VALUE_UNSET = -1;
    @Nullable private final DevelopmentSettingsDashboardFragment mFragment;
    private final BluetoothAdapter mBluetoothAdapter;
    private boolean mCurrentSettingsValue = false;
    private boolean mShouldToggleCurrentValue = false;

    public BluetoothLeAudioUiPreferenceController(
            @NonNull Context context, @Nullable DevelopmentSettingsDashboardFragment fragment) {
        super(context);
        mFragment = fragment;
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    @Override
    public boolean isAvailable() {
        return Flags.audioSharingDeveloperOption()
                && BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false)
                && BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false);
    }

    @Override
    public boolean onPreferenceChange(@NonNull Preference preference, @Nullable Object newValue) {
        if (mFragment != null && newValue != null && (boolean) newValue != mCurrentSettingsValue) {
            mShouldToggleCurrentValue = true;
            BluetoothRebootDialog.show(mFragment);
        }
        return false;
    }

    @Override
    public void updateState(@NonNull Preference preference) {
        if (mBluetoothAdapter == null) {
            return;
        }
        var unused = ThreadUtils.postOnBackgroundThread(
                () -> {
                    boolean shouldEnable =
                            mBluetoothAdapter.isEnabled()
                                    && mBluetoothAdapter.isLeAudioBroadcastSourceSupported()
                                            == BluetoothStatusCodes.FEATURE_SUPPORTED
                                    && mBluetoothAdapter.isLeAudioBroadcastAssistantSupported()
                                            == BluetoothStatusCodes.FEATURE_SUPPORTED;
                    boolean valueOn =
                            Settings.Global.getInt(
                                            mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET)
                                    == VALUE_ON;
                    mContext.getMainExecutor()
                            .execute(
                                    () -> {
                                        if (!shouldEnable && valueOn) {
                                            Log.e(
                                                    TAG,
                                                    "Error state: toggle disabled but current"
                                                            + " settings value is true.");
                                        }
                                        mCurrentSettingsValue = valueOn;
                                        preference.setEnabled(shouldEnable);
                                        ((SwitchPreferenceCompat) preference).setChecked(valueOn);
                                    });
                });
    }

    @Override
    public @NonNull String getPreferenceKey() {
        return PREFERENCE_KEY;
    }

    /** Called when the RebootDialog confirm is clicked. */
    public void onRebootDialogConfirmed() {
        if (isAvailable() && mShouldToggleCurrentValue) {
            // Blocking, ensure reboot happens after value is saved.
            Log.d(TAG, "onRebootDialogConfirmed(): setting value to " + !mCurrentSettingsValue);
            toggleSetting(mContext.getContentResolver(), !mCurrentSettingsValue);
        }
    }

    /** Called when the RebootDialog cancel is clicked. */
    public void onRebootDialogCanceled() {
        mShouldToggleCurrentValue = false;
    }

    @Override
    public void onBroadcastDisabled() {
        if (isAvailable() && mCurrentSettingsValue) {
            Log.d(TAG, "onBroadcastDisabled(): setting value to false");
            // Blocking, ensure reboot happens after value is saved.
            toggleSetting(mContext.getContentResolver(), false);
        }
    }

    private static void toggleSetting(ContentResolver contentResolver, boolean valueOn) {
        Settings.Global.putInt(contentResolver, VALUE_KEY, valueOn ? VALUE_ON : VALUE_OFF);
    }
}
+21 −1
Original line number Diff line number Diff line
@@ -99,7 +99,9 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
        AdbClearKeysDialogHost, LogPersistDialogHost,
        BluetoothRebootDialog.OnRebootDialogListener,
        AbstractBluetoothPreferenceController.Callback,
        NfcRebootDialog.OnNfcRebootDialogConfirmedListener, BluetoothSnoopLogHost {
        NfcRebootDialog.OnNfcRebootDialogConfirmedListener,
        BluetoothSnoopLogHost,
        BluetoothLeAudioModePreferenceController.OnModeChangeListener {

    private static final String TAG = "DevSettingsDashboard";
    @VisibleForTesting static final int REQUEST_BIOMETRIC_PROMPT = 100;
@@ -498,6 +500,10 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
                getDevelopmentOptionsController(
                        BluetoothLeAudioModePreferenceController.class);
        leAudioModeController.onRebootDialogConfirmed();

        final BluetoothLeAudioUiPreferenceController leAudioUiController =
                getDevelopmentOptionsController(BluetoothLeAudioUiPreferenceController.class);
        leAudioUiController.onRebootDialogConfirmed();
    }

    @Override
@@ -520,6 +526,10 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
                getDevelopmentOptionsController(
                        BluetoothLeAudioModePreferenceController.class);
        leAudioModeController.onRebootDialogCanceled();

        final BluetoothLeAudioUiPreferenceController leAudioUiController =
                getDevelopmentOptionsController(BluetoothLeAudioUiPreferenceController.class);
        leAudioUiController.onRebootDialogCanceled();
    }

    @Override
@@ -741,6 +751,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
        controllers.add(new BluetoothMapVersionPreferenceController(context));
        controllers.add(new BluetoothLeAudioPreferenceController(context, fragment));
        controllers.add(new BluetoothLeAudioModePreferenceController(context, fragment));
        controllers.add(new BluetoothLeAudioUiPreferenceController(context, fragment));
        controllers.add(new BluetoothLeAudioDeviceDetailsPreferenceController(context));
        controllers.add(new BluetoothLeAudioAllowListPreferenceController(context));
        controllers.add(new BluetoothA2dpHwOffloadPreferenceController(context, fragment));
@@ -858,6 +869,15 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
        }
    }

    @Override
    public void onBroadcastDisabled() {
        for (AbstractPreferenceController controller : mPreferenceControllers) {
            if (controller instanceof BluetoothLeAudioUiPreferenceController) {
                ((BluetoothLeAudioUiPreferenceController) controller).onBroadcastDisabled();
            }
        }
    }

    /**
     * For Search.
     */
Loading