Add auto on toggle in settings.
Test: atest com.android.settings.bluetooth Bug: b/316822488 b/316985153 Flag: ACONFIG com.android.settingslib.flags.bluetooth_qs_tile_dialog_auto_on_toggle DISABLED Change-Id: Iaa8ce3d3f6e2ffa25d8b7a35b5f55f4774ac4a40
This commit is contained in:
@@ -132,6 +132,8 @@
|
||||
<string name="bluetooth_pairing_pref_title">Pair new device</string>
|
||||
<!-- Keywords for bluetooth pairing item [CHAR LIMIT=30] -->
|
||||
<string name="keywords_add_bt_device">bluetooth</string>
|
||||
<!-- Title for bluetooth auto on toggle [CHAR LIMIT=60] -->
|
||||
<string name="bluetooth_screen_auto_on_title">Automatically turn on again tomorrow</string>
|
||||
|
||||
|
||||
<!-- Button to help user to pair right ear of the hearing aid device. It will show when only one of the hearing aid device set is connected. [CHAR LIMIT=20] -->
|
||||
@@ -1800,8 +1802,12 @@
|
||||
<string name="bluetooth_device_context_pair_connect">Pair & connect</string>
|
||||
<!-- Bluetooth settings. Text displayed when Bluetooth is off and device list is empty [CHAR LIMIT=NONE]-->
|
||||
<string name="bluetooth_empty_list_bluetooth_off">When Bluetooth is turned on, your device can communicate with other nearby Bluetooth devices</string>
|
||||
<!-- Bluetooth settings. Text displayed when Bluetooth is off and device list is empty when auto-on feature is enabled [CHAR LIMIT=NONE]-->
|
||||
<string name="bluetooth_empty_list_bluetooth_off_auto_on_available">When Bluetooth is on, your device can communicate with other nearby Bluetooth devices. Features like Quick Share, Find My Device, and device location use Bluetooth.</string>
|
||||
<!-- Bluetooth settings. Text displayed when Bluetooth is off and bluetooth scanning is turned on [CHAR LIMIT=NONE] -->
|
||||
<string name="bluetooth_scanning_on_info_message">When Bluetooth is turned on, your device can communicate with other nearby Bluetooth devices.\n\nTo improve device experience, apps and services can still scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services. You can change this in Bluetooth scanning settings.</string>
|
||||
<!-- Bluetooth settings. Text displayed when Bluetooth is off and bluetooth scanning is turned on [CHAR LIMIT=NONE] -->
|
||||
<string name="bluetooth_scanning_on_info_message_auto_on_available">When Bluetooth is on, your device can communicate with other nearby Bluetooth devices. Features like Quick Share, Find My Device, and device location use Bluetooth.\n\nApps and services can still scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services. You can change this in Bluetooth scanning settings.</string>
|
||||
<!-- Bluetooth settings. Link text to bring the user to "scanning settings" screen. [CHAR LIMIT=NONE]-->
|
||||
<string name="bluetooth_scan_change">Change</string>
|
||||
|
||||
|
@@ -18,6 +18,11 @@
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/bluetooth_settings_title">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="bluetooth_auto_on_settings_toggle"
|
||||
android:title="@string/bluetooth_screen_auto_on_title"
|
||||
settings:controller="com.android.settings.bluetooth.BluetoothAutoOnPreferenceController"/>
|
||||
|
||||
<Preference
|
||||
android:key="bluetooth_screen_bt_pair_rename_devices"
|
||||
android:title="@string/bluetooth_device_name"
|
||||
|
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.bluetooth;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStart;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStop;
|
||||
import com.android.settingslib.flags.Flags;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
public class BluetoothAutoOnPreferenceController extends TogglePreferenceController
|
||||
implements LifecycleObserver, OnStart, OnStop {
|
||||
private static final String TAG = "BluetoothAutoOnPreferenceController";
|
||||
@VisibleForTesting static final String PREF_KEY = "bluetooth_auto_on_settings_toggle";
|
||||
static final String SETTING_NAME = "bluetooth_automatic_turn_on";
|
||||
static final int UNSET = -1;
|
||||
@VisibleForTesting static final int ENABLED = 1;
|
||||
@VisibleForTesting static final int DISABLED = 0;
|
||||
private final ContentObserver mContentObserver =
|
||||
new ContentObserver(new Handler(/* async= */ true)) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
var unused =
|
||||
ThreadUtils.postOnBackgroundThread(
|
||||
() -> {
|
||||
updateValue();
|
||||
mContext.getMainExecutor()
|
||||
.execute(
|
||||
() -> {
|
||||
if (mPreference != null) {
|
||||
updateState(mPreference);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
private int mAutoOnValue = UNSET;
|
||||
@Nullable private TwoStatePreference mPreference;
|
||||
|
||||
public BluetoothAutoOnPreferenceController(
|
||||
@NonNull Context context, @NonNull String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
mContext.getContentResolver()
|
||||
.registerContentObserver(
|
||||
Settings.Secure.getUriFor(SETTING_NAME),
|
||||
/* notifyForDescendants= */ false,
|
||||
mContentObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
mContext.getContentResolver().unregisterContentObserver(mContentObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (!Flags.bluetoothQsTileDialogAutoOnToggle()) {
|
||||
return UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
updateValue();
|
||||
return mAutoOnValue != UNSET ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(@NonNull PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return PREF_KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return mAutoOnValue == ENABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
if (getAvailabilityStatus() != AVAILABLE) {
|
||||
Log.w(TAG, "Trying to set toggle value while feature not available.");
|
||||
return false;
|
||||
}
|
||||
var unused =
|
||||
ThreadUtils.postOnBackgroundThread(
|
||||
() -> {
|
||||
boolean updated =
|
||||
Settings.Secure.putIntForUser(
|
||||
mContext.getContentResolver(),
|
||||
SETTING_NAME,
|
||||
isChecked ? ENABLED : DISABLED,
|
||||
UserHandle.myUserId());
|
||||
if (updated) {
|
||||
updateValue();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSliceHighlightMenuRes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void updateValue() {
|
||||
mAutoOnValue =
|
||||
Settings.Secure.getIntForUser(
|
||||
mContext.getContentResolver(), SETTING_NAME, UNSET, UserHandle.myUserId());
|
||||
}
|
||||
}
|
@@ -15,8 +15,13 @@
|
||||
*/
|
||||
package com.android.settings.bluetooth;
|
||||
|
||||
import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME;
|
||||
import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
@@ -29,6 +34,7 @@ import com.android.settings.widget.SwitchWidgetController;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStart;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStop;
|
||||
import com.android.settingslib.flags.Flags;
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
|
||||
/**
|
||||
@@ -36,8 +42,11 @@ import com.android.settingslib.widget.FooterPreference;
|
||||
* is delegated to the SwitchWidgetController it uses.
|
||||
*/
|
||||
public class BluetoothSwitchPreferenceController
|
||||
implements LifecycleObserver, OnStart, OnStop,
|
||||
SwitchWidgetController.OnSwitchChangeListener, View.OnClickListener {
|
||||
implements LifecycleObserver,
|
||||
OnStart,
|
||||
OnStop,
|
||||
SwitchWidgetController.OnSwitchChangeListener,
|
||||
View.OnClickListener {
|
||||
|
||||
private BluetoothEnabler mBluetoothEnabler;
|
||||
private RestrictionUtils mRestrictionUtils;
|
||||
@@ -46,18 +55,21 @@ public class BluetoothSwitchPreferenceController
|
||||
private FooterPreference mFooterPreference;
|
||||
private boolean mIsAlwaysDiscoverable;
|
||||
|
||||
@VisibleForTesting
|
||||
AlwaysDiscoverable mAlwaysDiscoverable;
|
||||
@VisibleForTesting AlwaysDiscoverable mAlwaysDiscoverable;
|
||||
|
||||
public BluetoothSwitchPreferenceController(Context context,
|
||||
public BluetoothSwitchPreferenceController(
|
||||
Context context,
|
||||
SwitchWidgetController switchController,
|
||||
FooterPreference footerPreference) {
|
||||
this(context, new RestrictionUtils(), switchController, footerPreference);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public BluetoothSwitchPreferenceController(Context context, RestrictionUtils restrictionUtils,
|
||||
SwitchWidgetController switchController, FooterPreference footerPreference) {
|
||||
public BluetoothSwitchPreferenceController(
|
||||
Context context,
|
||||
RestrictionUtils restrictionUtils,
|
||||
SwitchWidgetController switchController,
|
||||
FooterPreference footerPreference) {
|
||||
mRestrictionUtils = restrictionUtils;
|
||||
mSwitch = switchController;
|
||||
mContext = context;
|
||||
@@ -66,11 +78,13 @@ public class BluetoothSwitchPreferenceController
|
||||
mSwitch.setupView();
|
||||
updateText(mSwitch.isChecked());
|
||||
|
||||
mBluetoothEnabler = new BluetoothEnabler(context,
|
||||
switchController,
|
||||
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(),
|
||||
SettingsEnums.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE,
|
||||
mRestrictionUtils);
|
||||
mBluetoothEnabler =
|
||||
new BluetoothEnabler(
|
||||
context,
|
||||
switchController,
|
||||
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(),
|
||||
SettingsEnums.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE,
|
||||
mRestrictionUtils);
|
||||
mBluetoothEnabler.setToggleCallback(this);
|
||||
mAlwaysDiscoverable = new AlwaysDiscoverable(context);
|
||||
}
|
||||
@@ -97,8 +111,8 @@ public class BluetoothSwitchPreferenceController
|
||||
/**
|
||||
* Set whether the device can be discovered. By default the value will be {@code false}.
|
||||
*
|
||||
* @param isAlwaysDiscoverable {@code true} if the device can be discovered,
|
||||
* otherwise {@code false}
|
||||
* @param isAlwaysDiscoverable {@code true} if the device can be discovered, otherwise {@code
|
||||
* false}
|
||||
*/
|
||||
public void setAlwaysDiscoverable(boolean isAlwaysDiscoverable) {
|
||||
mIsAlwaysDiscoverable = isAlwaysDiscoverable;
|
||||
@@ -119,15 +133,35 @@ public class BluetoothSwitchPreferenceController
|
||||
.launch();
|
||||
}
|
||||
|
||||
@VisibleForTesting void updateText(boolean isChecked) {
|
||||
@VisibleForTesting
|
||||
void updateText(boolean isChecked) {
|
||||
if (!isChecked && Utils.isBluetoothScanningEnabled(mContext)) {
|
||||
mFooterPreference.setTitle(R.string.bluetooth_scanning_on_info_message);
|
||||
if (isAutoOnFeatureAvailable()) {
|
||||
mFooterPreference.setTitle(
|
||||
R.string.bluetooth_scanning_on_info_message_auto_on_available);
|
||||
} else {
|
||||
mFooterPreference.setTitle(R.string.bluetooth_scanning_on_info_message);
|
||||
}
|
||||
mFooterPreference.setLearnMoreText(mContext.getString(R.string.bluetooth_scan_change));
|
||||
mFooterPreference.setLearnMoreAction(v -> onClick(v));
|
||||
} else {
|
||||
mFooterPreference.setTitle(R.string.bluetooth_empty_list_bluetooth_off);
|
||||
if (isAutoOnFeatureAvailable()) {
|
||||
mFooterPreference.setTitle(
|
||||
R.string.bluetooth_empty_list_bluetooth_off_auto_on_available);
|
||||
} else {
|
||||
mFooterPreference.setTitle(R.string.bluetooth_empty_list_bluetooth_off);
|
||||
}
|
||||
mFooterPreference.setLearnMoreText("");
|
||||
mFooterPreference.setLearnMoreAction(null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAutoOnFeatureAvailable() {
|
||||
if (!Flags.bluetoothQsTileDialogAutoOnToggle()) {
|
||||
return false;
|
||||
}
|
||||
return Settings.Secure.getIntForUser(
|
||||
mContext.getContentResolver(), SETTING_NAME, UNSET, UserHandle.myUserId())
|
||||
!= UNSET;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.bluetooth;
|
||||
|
||||
import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.DISABLED;
|
||||
import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.ENABLED;
|
||||
import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.PREF_KEY;
|
||||
import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME;
|
||||
import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET;
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
||||
import static com.android.settingslib.flags.Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothAutoOnPreferenceControllerTest {
|
||||
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||
private Context mContext;
|
||||
private ContentResolver mContentResolver;
|
||||
private BluetoothAutoOnPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mSetFlagsRule.enableFlags(FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mContentResolver = mContext.getContentResolver();
|
||||
mController = new BluetoothAutoOnPreferenceController(mContext, PREF_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailability_valueUnset_returnUnsupported() {
|
||||
Settings.Secure.putInt(mContentResolver, SETTING_NAME, UNSET);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailability_valueSet_returnAvailable() {
|
||||
Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_valueEnabled_returnTrue() {
|
||||
Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
assertThat(mController.isChecked()).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_returnTrue() {
|
||||
Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED);
|
||||
|
||||
mController.setChecked(true);
|
||||
assertThat(mController.isChecked()).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_returnFalse() {
|
||||
Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED);
|
||||
|
||||
mController.setChecked(false);
|
||||
assertThat(mController.isChecked()).isEqualTo(false);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user