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

Commit f6e7157e authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 13039724 from a7caa23d to 25Q2-release

Change-Id: I9fc10105bf69586473a7927e11b9b05490511e91
parents 69b38191 a7caa23d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -10399,7 +10399,7 @@
    <!-- Body text of prompt dialog app can invoke to turn off optimization [CHAR LIMIT=NONE] -->
    <string name="high_power_prompt_body">
        Allowing <xliff:g id="app_name" example="Settings">%1$s</xliff:g> to always run in the background may reduce battery life.
        \n\nYou can change this later from Settings > Apps.</string>
        \n\nYou can change this later from Settings -> Apps.</string>
    <!-- Summary of power usage for an app [CHAR LIMIT=NONE] -->
    <string name="battery_summary"><xliff:g id="percentage" example="2">%1$s</xliff:g> use since last full charge</string>
    <!-- Summary of power usage for an app within past 24 hr[CHAR LIMIT=NONE] -->
+9 −0
Original line number Diff line number Diff line
@@ -17,9 +17,12 @@
package com.android.settings.accessibility

import android.annotation.DrawableRes
import android.app.settings.SettingsEnums.ACTION_REMOVE_ANIMATION
import android.content.Context
import android.provider.Settings
import com.android.settings.PreferenceActionMetricsProvider
import com.android.settings.R
import com.android.settings.contract.KEY_REMOVE_ANIMATION
import com.android.settingslib.datastore.HandlerExecutor
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.KeyedObserver
@@ -37,6 +40,7 @@ class RemoveAnimationsPreference :
        R.string.accessibility_disable_animations,
        R.string.accessibility_disable_animations_summary,
    ),
    PreferenceActionMetricsProvider,
    PreferenceLifecycleProvider {

    private var mSettingsKeyedObserver: KeyedObserver<String>? = null
@@ -44,6 +48,11 @@ class RemoveAnimationsPreference :
    override val icon: Int
        @DrawableRes get() = R.drawable.ic_accessibility_animation

    override val preferenceActionMetrics: Int
        get() = ACTION_REMOVE_ANIMATION

    override fun tags(context: Context) = arrayOf(KEY_REMOVE_ANIMATION)

    override fun onStart(context: PreferenceLifecycleContext) {
        val observer = KeyedObserver<String> { _, _ -> context.notifyPreferenceChange(KEY) }
        mSettingsKeyedObserver = observer
+9 −0
Original line number Diff line number Diff line
@@ -15,13 +15,16 @@
 */
package com.android.settings.accessibility

import android.app.settings.SettingsEnums.ACTION_VIBRATION_HAPTICS
import android.content.Context
import android.os.VibrationAttributes
import android.os.Vibrator
import android.provider.Settings
import android.widget.CompoundButton
import android.widget.CompoundButton.OnCheckedChangeListener
import com.android.settings.PreferenceActionMetricsProvider
import com.android.settings.R
import com.android.settings.contract.KEY_VIBRATION_HAPTICS
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.KeyedObservableDelegate
import com.android.settingslib.datastore.SettingsStore
@@ -39,6 +42,7 @@ class VibrationMainSwitchPreference :
        key = Settings.System.VIBRATE_ON,
        title = R.string.accessibility_vibration_primary_switch_title,
    ),
    PreferenceActionMetricsProvider,
    PreferenceLifecycleProvider,
    OnCheckedChangeListener {
    override val keywords: Int
@@ -46,6 +50,11 @@ class VibrationMainSwitchPreference :

    lateinit var vibrator: Vibrator

    override val preferenceActionMetrics: Int
        get() = ACTION_VIBRATION_HAPTICS

    override fun tags(context: Context) = arrayOf(KEY_VIBRATION_HAPTICS)

    override fun storage(context: Context): KeyValueStore =
        VibrationMainSwitchToggleStorage(SettingsSystemStore.get(context))

+81 −38
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HearingAidStatsLogUtils;
import com.android.settingslib.flags.Flags;
import com.android.settingslib.utils.ThreadUtils;

import com.google.common.collect.ImmutableList;
@@ -62,6 +63,7 @@ import java.util.concurrent.TimeUnit;
public abstract class BluetoothDevicePairingDetailBase extends DeviceListPreferenceFragment {
    private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(15);
    private static final int AUTO_DISMISS_MESSAGE_ID = 1001;
    private static final int AUTO_FINISH_MESSAGE_ID = 1002;
    private static final ImmutableList<Integer> AUDIO_SHARING_PROFILES = ImmutableList.of(
            BluetoothProfile.LE_AUDIO,
            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, BluetoothProfile.VOLUME_CONTROL);
@@ -77,7 +79,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
    @Nullable
    ProgressDialogFragment mProgressDialog = null;
    @VisibleForTesting
    boolean mShouldTriggerAudioSharingShareThenPairFlow = false;
    boolean mShouldTriggerShareThenPairFlow = false;
    private CopyOnWriteArrayList<BluetoothDevice> mDevicesWithMetadataChangedListener =
            new CopyOnWriteArrayList<>();

@@ -89,7 +91,8 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
    // onDeviceBondStateChanged(BOND_BONDED), BluetoothDevicePreference's summary has already
    // change from "Pairing..." to empty since it listens to metadata changes happens earlier.
    //
    // In share then pair flow, we have to wait on this page till the device is connected.
    // In pairing flow during audio sharing, we have to wait on this page till the device is
    // connected to check the device type and handle extra logic for audio sharing.
    // The BluetoothDevicePreference summary will be blank for seconds between "Pairing..." and
    // "Connecting..." To help users better understand the process, we listen to metadata change
    // as well and show a progress dialog with "Connecting to ...." once BluetoothDevice.getState()
@@ -100,10 +103,11 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
                public void onMetadataChanged(@NonNull BluetoothDevice device, int key,
                        @Nullable byte[] value) {
                    Log.d(getLogTag(), "onMetadataChanged device = " + device + ", key  = " + key);
                    if (mShouldTriggerAudioSharingShareThenPairFlow && mProgressDialog == null
                    if ((mShouldTriggerShareThenPairFlow || shouldSetTempBondMetadata())
                            && mProgressDialog == null
                            && device.getBondState() == BluetoothDevice.BOND_BONDED
                            && mSelectedList.contains(device)) {
                        triggerAudioSharingShareThenPairFlow(device);
                        handleDeviceBondedInAudioSharing(device);
                        // Once device is bonded, remove the listener
                        removeOnMetadataChangedListener(device);
                    }
@@ -133,7 +137,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
            return;
        }
        updateBluetooth();
        mShouldTriggerAudioSharingShareThenPairFlow = shouldTriggerAudioSharingShareThenPairFlow();
        mShouldTriggerShareThenPairFlow = shouldTriggerShareThenPairFlow();
    }

    @Override
@@ -177,11 +181,12 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere

    @Override
    public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
        boolean shouldSetTempBond = shouldSetTempBondMetadata();
        if (bondState == BluetoothDevice.BOND_BONDED) {
            if (cachedDevice != null && mShouldTriggerAudioSharingShareThenPairFlow) {
            if (cachedDevice != null && (mShouldTriggerShareThenPairFlow || shouldSetTempBond)) {
                BluetoothDevice device = cachedDevice.getDevice();
                if (device != null && mSelectedList.contains(device)) {
                    triggerAudioSharingShareThenPairFlow(device);
                    handleDeviceBondedInAudioSharing(device);
                    removeOnMetadataChangedListener(device);
                    return;
                }
@@ -190,7 +195,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
            finish();
            return;
        } else if (bondState == BluetoothDevice.BOND_BONDING) {
            if (mShouldTriggerAudioSharingShareThenPairFlow && cachedDevice != null) {
            if ((mShouldTriggerShareThenPairFlow || shouldSetTempBond) && cachedDevice != null) {
                BluetoothDevice device = cachedDevice.getDevice();
                if (device != null && mSelectedList.contains(device)) {
                    addOnMetadataChangedListener(device);
@@ -203,7 +208,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
                    pageId);
            HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice);
        } else if (bondState == BluetoothDevice.BOND_NONE) {
            if (mShouldTriggerAudioSharingShareThenPairFlow && cachedDevice != null) {
            if ((mShouldTriggerShareThenPairFlow || shouldSetTempBond) && cachedDevice != null) {
                BluetoothDevice device = cachedDevice.getDevice();
                if (device != null && mSelectedList.contains(device)) {
                    removeOnMetadataChangedListener(device);
@@ -233,21 +238,29 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
            final BluetoothDevice device = cachedDevice.getDevice();
            if (device != null
                    && mSelectedList.contains(device)) {
                var unused = ThreadUtils.postOnBackgroundThread(() -> {
                    if (BluetoothUtils.isAudioSharingUIAvailable(getContext())) {
                    if (mShouldTriggerAudioSharingShareThenPairFlow
                        if ((mShouldTriggerShareThenPairFlow || shouldSetTempBondMetadata())
                                && state == BluetoothAdapter.STATE_CONNECTED
                                && device.equals(mJustBonded)
                                && AUDIO_SHARING_PROFILES.contains(bluetoothProfile)
                                && isReadyForAudioSharing(cachedDevice, bluetoothProfile)) {
                        Log.d(getLogTag(),
                                "onProfileConnectionStateChanged, ready for audio sharing");
                            Log.d(getLogTag(), "onProfileConnectionStateChanged, lea eligible");
                            dismissConnectingDialog();
                            BluetoothUtils.setTemporaryBondMetadata(device);
                            if (mShouldTriggerShareThenPairFlow) {
                                mHandler.removeMessages(AUTO_DISMISS_MESSAGE_ID);
                        finishFragmentWithResultForAudioSharing(device);
                                postOnMainThread(() ->
                                        finishFragmentWithResultForAudioSharing(device));
                            } else {
                                mHandler.removeMessages(AUTO_FINISH_MESSAGE_ID);
                                postOnMainThread(() -> finish());
                            }
                        }
                    } else {
                    finish();
                        postOnMainThread(() -> finish());
                    }
                });
            } else {
                onDeviceDeleted(cachedDevice);
            }
@@ -314,7 +327,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
    }

    @VisibleForTesting
    boolean shouldTriggerAudioSharingShareThenPairFlow() {
    boolean shouldTriggerShareThenPairFlow() {
        if (BluetoothUtils.isAudioSharingUIAvailable(getContext())) {
            Activity activity = getActivity();
            Intent intent = activity == null ? null : activity.getIntent();
@@ -328,6 +341,16 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
        return false;
    }

    private boolean shouldSetTempBondMetadata() {
        return Flags.enableTemporaryBondDevicesUi()
                && BluetoothUtils.isAudioSharingUIAvailable(getContext())
                && BluetoothUtils.isBroadcasting(mLocalManager)
                && mLocalManager != null
                && mLocalManager.getCachedDeviceManager() != null
                && mLocalManager.getProfileManager().getLeAudioBroadcastAssistantProfile() != null
                && !Utils.shouldBlockPairingInAudioSharing(mLocalManager);
    }

    private boolean isReadyForAudioSharing(@NonNull CachedBluetoothDevice cachedDevice,
            int justConnectedProfile) {
        for (int profile : AUDIO_SHARING_PROFILES) {
@@ -382,11 +405,10 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
        });
    }

    private void triggerAudioSharingShareThenPairFlow(
            @NonNull BluetoothDevice device) {
    private void handleDeviceBondedInAudioSharing(@Nullable BluetoothDevice device) {
        var unused = ThreadUtils.postOnBackgroundThread(() -> {
            if (mJustBonded != null) {
                Log.d(getLogTag(), "Skip triggerAudioSharingShareThenPairFlow, already done");
                Log.d(getLogTag(), "Skip handleDeviceBondedInAudioSharing, already done");
                return;
            }
            mJustBonded = device;
@@ -395,18 +417,39 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
            String deviceName = TextUtils.isEmpty(aliasName) ? device.getAddress()
                    : aliasName;
            showConnectingDialog(deviceName);
            // Wait for AUTO_DISMISS_TIME_THRESHOLD_MS and check if the paired device supports audio
            // sharing.
            if (mShouldTriggerShareThenPairFlow) {
                // For share then pair flow, we have strong signal that users wish to pair new
                // device to join sharing.
                // So we wait for AUTO_DISMISS_TIME_THRESHOLD_MS, if we find that the bonded device
                // is lea in onProfileConnectionStateChanged, we finish the activity, set the device
                // as temp bond and auto add source; otherwise, show dialog to notify that the
                // device is incompatible for audio sharing.
                if (!mHandler.hasMessages(AUTO_DISMISS_MESSAGE_ID)) {
                    mHandler.postDelayed(() ->
                            postOnMainThread(
                                    () -> {
                                    Log.d(getLogTag(), "Show incompatible dialog when timeout");
                                        Log.d(getLogTag(),
                                                "Show incompatible dialog when timeout");
                                        dismissConnectingDialog();
                                    AudioSharingIncompatibleDialogFragment.show(this, deviceName,
                                        AudioSharingIncompatibleDialogFragment.show(this,
                                                deviceName,
                                                () -> finish());
                                    }), AUTO_DISMISS_MESSAGE_ID, AUTO_DISMISS_TIME_THRESHOLD_MS);
                }
            } else {
                // For other pairing request during audio sharing with sinks < 2, we wait for
                // AUTO_DISMISS_TIME_THRESHOLD_MS, if we find that the bonded device is lea in
                // onProfileConnectionStateChanged, we finish the activity and set the device as
                // temp bond; otherwise, we just finish the activity.
                if (!mHandler.hasMessages(AUTO_FINISH_MESSAGE_ID)) {
                    mHandler.postDelayed(() ->
                            postOnMainThread(
                                    () -> {
                                        Log.d(getLogTag(), "Finish activity when timeout");
                                        finish();
                                    }), AUTO_FINISH_MESSAGE_ID, AUTO_DISMISS_TIME_THRESHOLD_MS);
                }
            }
        });
    }

+31 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;

@@ -42,17 +43,21 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothUtils.ErrorListener;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
import com.android.settingslib.utils.ThreadUtils;

import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.stream.Collectors;

/**
 * Utils is a helper class that contains constants for various
@@ -293,4 +298,30 @@ public final class Utils {
            ThreadUtils.postOnMainThread(runnable);
        });
    }

    /**
     * Check if need to block pairing during audio sharing
     *
     * @param localBtManager {@link LocalBluetoothManager}
     * @return if need to block pairing during audio sharing
     */
    public static boolean shouldBlockPairingInAudioSharing(
            @NonNull LocalBluetoothManager localBtManager) {
        if (!BluetoothUtils.isBroadcasting(localBtManager)) return false;
        LocalBluetoothLeBroadcastAssistant assistant =
                localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
        CachedBluetoothDeviceManager deviceManager = localBtManager.getCachedDeviceManager();
        List<BluetoothDevice> connectedDevices =
                assistant == null ? ImmutableList.of() : assistant.getAllConnectedDevices();
        // Block the pairing if there is ongoing audio sharing session and
        // a) there is already one temp bond sink connected
        // or b) there are already two sinks joining the audio sharing
        return assistant != null && deviceManager != null
                && (connectedDevices.stream().anyMatch(BluetoothUtils::isTemporaryBondDevice)
                || connectedDevices.stream().filter(
                        d -> BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(d,
                                localBtManager))
                .map(d -> BluetoothUtils.getGroupId(deviceManager.findDevice(d))).collect(
                        Collectors.toSet()).size() >= 2);
    }
}
Loading