Merge "Mock method to avoid exception." into main
diff --git a/res/layout/le_audio_bt_entity_header.xml b/res/layout/le_audio_bt_entity_header.xml
index 81911e9..421ee39 100644
--- a/res/layout/le_audio_bt_entity_header.xml
+++ b/res/layout/le_audio_bt_entity_header.xml
@@ -176,5 +176,23 @@
android:padding="@dimen/le_bluetooth_summary_padding"
android:drawablePadding="@dimen/le_bluetooth_summary_drawable_padding"/>
</LinearLayout>
+ <LinearLayout
+ android:id="@+id/bt_battery_mono"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/le_bluetooth_battery_start_margin"
+ android:layout_marginTop="@dimen/le_bluetooth_battery_top_margin"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:visibility="gone"
+ android:focusable="true">
+ <TextView
+ android:id="@+id/bt_battery_mono_summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/le_bluetooth_summary_padding"
+ android:drawablePadding="@dimen/le_bluetooth_summary_drawable_padding"/>
+ </LinearLayout>
</LinearLayout>
</LinearLayout>
diff --git a/res/xml/bluetooth_screen.xml b/res/xml/bluetooth_screen.xml
index e815d44..51cff33 100644
--- a/res/xml/bluetooth_screen.xml
+++ b/res/xml/bluetooth_screen.xml
@@ -16,6 +16,7 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:key="bluetooth_switchbar_screen"
android:title="@string/bluetooth_settings_title">
<SwitchPreferenceCompat
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 363d601..f272c1d 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -17,7 +17,6 @@
package com.android.settings;
import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
-import static com.android.settingslib.media.PhoneMediaDevice.isDesktop;
import android.app.Activity;
import android.app.Dialog;
@@ -187,13 +186,6 @@
/** Returns if catalyst is enabled on current screen. */
public final boolean isCatalystEnabled() {
- // TODO(b/379130874): make Catalyst compatible with desktop device, such as user restriction
- // check.
- Context context = getContext();
- if (context != null && isDesktop(context)) {
- return false;
- }
-
return getPreferenceScreenCreator() != null;
}
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
index 2546d44..52560c9 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
@@ -162,18 +162,13 @@
}
final int side = device.getDeviceSide();
- if (side == HearingAidInfo.DeviceSide.SIDE_LEFT_AND_RIGHT) {
- return mContext.getString(
- R.string.accessibility_hearingaid_left_and_right_side_device_summary, name);
- } else if (side == HearingAidInfo.DeviceSide.SIDE_LEFT) {
+ if (side == HearingAidInfo.DeviceSide.SIDE_LEFT) {
return mContext.getString(
R.string.accessibility_hearingaid_left_side_device_summary, name);
} else if (side == HearingAidInfo.DeviceSide.SIDE_RIGHT) {
return mContext.getString(
R.string.accessibility_hearingaid_right_side_device_summary, name);
}
-
- // Invalid side
return mContext.getString(
R.string.accessibility_hearingaid_active_device_summary, name);
}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
index 32d2a11..d520cd6 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
@@ -144,7 +144,7 @@
@Override
protected void startEnrollmentInternal() {
- super.startEnrollment();
+ super.startEnrollmentInternal();
mPreviewFragment = (FaceEnrollPreviewFragment) getSupportFragmentManager()
.findFragmentByTag(TAG_FACE_PREVIEW);
if (mPreviewFragment == null) {
diff --git a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
index 2524894..4c7c98e 100644
--- a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
@@ -225,6 +225,8 @@
return R.id.bt_battery_left_summary;
} else if (containerId == R.id.bt_battery_right) {
return R.id.bt_battery_right_summary;
+ } else if (containerId == R.id.bt_battery_mono) {
+ return R.id.bt_battery_mono_summary;
}
Log.d(TAG, "No summary resource id. The containerId is " + containerId);
return INVALID_RESOURCE_ID;
@@ -237,6 +239,8 @@
updateBatteryLayout(R.id.bt_battery_left, BluetoothUtils.META_INT_ERROR);
// hide the right
updateBatteryLayout(R.id.bt_battery_right, BluetoothUtils.META_INT_ERROR);
+ // hide the mono
+ updateBatteryLayout(R.id.bt_battery_mono, BluetoothUtils.META_INT_ERROR);
}
private void updateBatteryLayout() {
@@ -261,11 +265,6 @@
int deviceId = leAudioProfile.getAudioLocation(cachedDevice.getDevice());
Log.d(TAG, "LeAudioDevices:" + cachedDevice.getDevice().getAnonymizedAddress()
+ ", deviceId:" + deviceId);
-
- if (deviceId == BluetoothLeAudio.AUDIO_LOCATION_INVALID) {
- Log.d(TAG, "The device does not support the AUDIO_LOCATION.");
- return;
- }
boolean isLeft = (deviceId & LEFT_DEVICE_ID) != 0;
boolean isRight = (deviceId & RIGHT_DEVICE_ID) != 0;
boolean isLeftRight = isLeft && isRight;
@@ -280,6 +279,8 @@
updateBatteryLayout(R.id.bt_battery_left, cachedDevice.getBatteryLevel());
} else if (isRight) {
updateBatteryLayout(R.id.bt_battery_right, cachedDevice.getBatteryLevel());
+ } else if (deviceId == BluetoothLeAudio.AUDIO_LOCATION_MONO) {
+ updateBatteryLayout(R.id.bt_battery_mono, cachedDevice.getBatteryLevel());
} else {
Log.d(TAG, "The device id is other Audio Location. Do nothing.");
}
diff --git a/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java b/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
index 90ef5b9..d3434be 100644
--- a/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
@@ -15,11 +15,16 @@
*/
package com.android.settings.connecteddevice;
+import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -28,6 +33,7 @@
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
+import com.android.settings.bluetooth.AlwaysDiscoverable;
import com.android.settings.bluetooth.BluetoothDeviceRenamePreferenceController;
import com.android.settings.bluetooth.BluetoothSwitchPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
@@ -47,7 +53,6 @@
public class BluetoothDashboardFragment extends DashboardFragment {
private static final String TAG = "BluetoothDashboardFrag";
- private static final String KEY_BLUETOOTH_SCREEN_FOOTER = "bluetooth_screen_footer";
private static final String SLICE_ACTION = "com.android.settings.SEARCH_RESULT_TRAMPOLINE";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -55,6 +60,8 @@
private SettingsMainSwitchBar mSwitchBar;
private BluetoothSwitchPreferenceController mController;
+ private @Nullable AlwaysDiscoverable mAlwaysDiscoverable;
+
@Override
public int getMetricsCategory() {
return SettingsEnums.BLUETOOTH_FRAGMENT;
@@ -78,7 +85,9 @@
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- mFooterPreference = findPreference(KEY_BLUETOOTH_SCREEN_FOOTER);
+ if (!isCatalystEnabled()) {
+ mFooterPreference = findPreference(BluetoothFooterPreference.KEY);
+ }
}
@Override
@@ -90,6 +99,9 @@
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+ if (isCatalystEnabled()) {
+ return;
+ }
String callingAppPackageName = PasswordUtils.getCallingAppPackageName(
getActivity().getActivityToken());
String action = getIntent() != null ? getIntent().getAction() : "";
@@ -101,6 +113,7 @@
SettingsActivity activity = (SettingsActivity) getActivity();
mSwitchBar = activity.getSwitchBar();
mSwitchBar.setTitle(getContext().getString(R.string.bluetooth_main_switch_title));
+ mSwitchBar.getRootView().setAccessibilityDelegate(new MainSwitchAccessibilityDelegate());
mController = new BluetoothSwitchPreferenceController(activity,
new MainSwitchBarController(mSwitchBar), mFooterPreference);
mController.setAlwaysDiscoverable(isAlwaysDiscoverable(callingAppPackageName, action));
@@ -110,8 +123,37 @@
}
}
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (isCatalystEnabled()) {
+ Activity activity = requireActivity();
+ String callingAppPackageName = PasswordUtils.getCallingAppPackageName(
+ activity.getActivityToken());
+ Intent intent = activity.getIntent();
+ String action = intent != null ? intent.getAction() : "";
+ if (DEBUG) {
+ Log.d(TAG, "onActivityCreated() calling package name is : " + callingAppPackageName
+ + ", action : " + action);
+ }
+ if (isAlwaysDiscoverable(callingAppPackageName, action)) {
+ mAlwaysDiscoverable = new AlwaysDiscoverable(activity);
+ mAlwaysDiscoverable.start();
+ }
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (mAlwaysDiscoverable != null) {
+ mAlwaysDiscoverable.stop();
+ mAlwaysDiscoverable = null;
+ }
+ }
+
@VisibleForTesting
- boolean isAlwaysDiscoverable(String callingAppPackageName, String action) {
+ boolean isAlwaysDiscoverable(@Nullable String callingAppPackageName, @Nullable String action) {
return TextUtils.equals(SLICE_ACTION, action) ? false
: TextUtils.equals(Utils.SETTINGS_PACKAGE_NAME, callingAppPackageName)
|| TextUtils.equals(Utils.SYSTEMUI_PACKAGE_NAME, callingAppPackageName);
@@ -127,4 +169,19 @@
public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
return BluetoothDashboardScreen.KEY;
}
+
+ private static final class MainSwitchAccessibilityDelegate extends View.AccessibilityDelegate {
+ @Override
+ public boolean onRequestSendAccessibilityEvent(
+ @NonNull ViewGroup host, @NonNull View view, @NonNull AccessibilityEvent event) {
+ if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+ && (event.getContentChangeTypes()
+ & AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED)
+ != 0) {
+ Log.d(TAG, "Skip accessibility event for CONTENT_CHANGE_TYPE_ENABLED");
+ return false;
+ }
+ return super.onRequestSendAccessibilityEvent(host, view, event);
+ }
+ }
}
diff --git a/src/com/android/settings/connecteddevice/BluetoothDashboardScreen.kt b/src/com/android/settings/connecteddevice/BluetoothDashboardScreen.kt
index 66fd8b1..e6985ca 100644
--- a/src/com/android/settings/connecteddevice/BluetoothDashboardScreen.kt
+++ b/src/com/android/settings/connecteddevice/BluetoothDashboardScreen.kt
@@ -17,7 +17,10 @@
import android.content.Context
import com.android.settings.R
+import com.android.settings.Settings.BluetoothDashboardActivity
import com.android.settings.flags.Flags
+import com.android.settings.utils.makeLaunchIntent
+import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.preferenceHierarchy
import com.android.settingslib.preference.PreferenceScreenCreator
@@ -39,7 +42,15 @@
override fun fragmentClass() = BluetoothDashboardFragment::class.java
- override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {}
+ override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) =
+ makeLaunchIntent(context, BluetoothDashboardActivity::class.java, metadata?.key)
+
+ override fun getPreferenceHierarchy(context: Context) =
+ preferenceHierarchy(this) {
+ val bluetoothDataStore = BluetoothPreference.createDataStore(context)
+ +BluetoothPreference(bluetoothDataStore)
+ +BluetoothFooterPreference(bluetoothDataStore)
+ }
companion object {
const val KEY = "bluetooth_switchbar_screen"
diff --git a/src/com/android/settings/connecteddevice/BluetoothFooterPreference.kt b/src/com/android/settings/connecteddevice/BluetoothFooterPreference.kt
new file mode 100644
index 0000000..72e51a3
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/BluetoothFooterPreference.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.connecteddevice
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settings.bluetooth.Utils
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.location.BluetoothScanningFragment
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceSummaryProvider
+import com.android.settingslib.preference.PreferenceBinding
+import com.android.settingslib.widget.FooterPreference
+
+class BluetoothFooterPreference(private val bluetoothDataStore: BluetoothDataStore) :
+ PreferenceMetadata, PreferenceBinding, PreferenceSummaryProvider {
+
+ override val key: String
+ get() = KEY
+
+ override fun isIndexable(context: Context) = false
+
+ override fun dependencies(context: Context) = arrayOf(BluetoothPreference.KEY)
+
+ override fun intent(context: Context): Intent? = subSettingLauncher(context).toIntent()
+
+ override fun createWidget(context: Context) = FooterPreference(context)
+
+ override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+ super.bind(preference, metadata)
+ preference.isSelectable = false
+ val bluetoothDisabled = bluetoothDataStore.getBoolean(BluetoothPreference.KEY) != true
+ val footerPreference = preference as FooterPreference
+ val context = preference.context
+ if (bluetoothDisabled && Utils.isBluetoothScanningEnabled(context)) {
+ footerPreference.setLearnMoreText(context.getString(R.string.bluetooth_scan_change))
+ footerPreference.setLearnMoreAction { subSettingLauncher(context).launch() }
+ } else {
+ footerPreference.setLearnMoreText("")
+ footerPreference.setLearnMoreAction(null)
+ }
+ }
+
+ private fun subSettingLauncher(context: Context) =
+ SubSettingLauncher(context)
+ .setDestination(BluetoothScanningFragment::class.java.name)
+ .setSourceMetricsCategory(SettingsEnums.BLUETOOTH_FRAGMENT)
+
+ override fun getSummary(context: Context): CharSequence? {
+ val bluetoothDisabled = bluetoothDataStore.getBoolean(BluetoothPreference.KEY) != true
+ val resId =
+ if (bluetoothDisabled && Utils.isBluetoothScanningEnabled(context)) {
+ when (isAutoOnFeatureAvailable()) {
+ true -> R.string.bluetooth_scanning_on_info_message_auto_on_available
+ else -> R.string.bluetooth_scanning_on_info_message
+ }
+ } else {
+ when (isAutoOnFeatureAvailable()) {
+ true -> R.string.bluetooth_empty_list_bluetooth_off_auto_on_available
+ else -> R.string.bluetooth_empty_list_bluetooth_off
+ }
+ }
+ return context.getString(resId)
+ }
+
+ private fun isAutoOnFeatureAvailable() =
+ try {
+ bluetoothDataStore.bluetoothAdapter?.isAutoOnSupported == true
+ } catch (e: Exception) {
+ Log.e(TAG, "isAutoOnSupported failed", e)
+ false
+ }
+
+ companion object {
+ const val KEY = "bluetooth_screen_footer"
+ const val TAG = "BluetoothFooterPreference"
+ }
+}
diff --git a/src/com/android/settings/connecteddevice/BluetoothMainSwitchPreference.kt b/src/com/android/settings/connecteddevice/BluetoothMainSwitchPreference.kt
deleted file mode 100644
index bf80653..0000000
--- a/src/com/android/settings/connecteddevice/BluetoothMainSwitchPreference.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.connecteddevice
-
-import android.bluetooth.BluetoothAdapter
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import com.android.settings.R
-import com.android.settings.widget.MainSwitchBarMetadata
-import com.android.settingslib.datastore.KeyValueStore
-import com.android.settingslib.datastore.NoOpKeyedObservable
-import com.android.settingslib.metadata.PreferenceLifecycleContext
-import com.android.settingslib.metadata.PreferenceLifecycleProvider
-import com.android.settingslib.metadata.ReadWritePermit
-
-class BluetoothMainSwitchPreference(private val bluetoothAdapter: BluetoothAdapter?) :
- MainSwitchBarMetadata, PreferenceLifecycleProvider {
-
- private lateinit var broadcastReceiver: BroadcastReceiver
-
- override val key
- get() = "use_bluetooth"
-
- override val title
- get() = R.string.bluetooth_main_switch_title
-
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
-
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
-
- override fun storage(context: Context) = BluetoothStateStore(bluetoothAdapter)
-
- override fun onStart(context: PreferenceLifecycleContext) {
- broadcastReceiver =
- object : BroadcastReceiver() {
- override fun onReceive(receiverContext: Context, intent: Intent) {
- context.notifyPreferenceChange(key)
- }
- }
- context.registerReceiver(
- broadcastReceiver,
- IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED),
- Context.RECEIVER_EXPORTED_UNAUDITED
- )
- }
-
- override fun onStop(context: PreferenceLifecycleContext) {
- if (::broadcastReceiver.isInitialized) {
- context.unregisterReceiver(broadcastReceiver)
- }
- }
-
- override fun isEnabled(context: Context): Boolean {
- return bluetoothAdapter?.state.let {
- it == BluetoothAdapter.STATE_ON || it == BluetoothAdapter.STATE_OFF
- }
- }
-
- @Suppress("UNCHECKED_CAST")
- class BluetoothStateStore(private val bluetoothAdapter: BluetoothAdapter?) :
- NoOpKeyedObservable<String>(), KeyValueStore {
-
- override fun contains(key: String) = true
-
- override fun <T : Any> getValue(key: String, valueType: Class<T>): T? {
- return (bluetoothAdapter?.state.let {
- it == BluetoothAdapter.STATE_ON || it == BluetoothAdapter.STATE_TURNING_ON
- }) as T
- }
-
- override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
- if (value is Boolean) {
- if (value) {
- bluetoothAdapter?.enable()
- } else {
- bluetoothAdapter?.disable()
- }
- }
- }
- }
-}
diff --git a/src/com/android/settings/connecteddevice/BluetoothPreference.kt b/src/com/android/settings/connecteddevice/BluetoothPreference.kt
new file mode 100644
index 0000000..c9b3953
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/BluetoothPreference.kt
@@ -0,0 +1,175 @@
+/*
+ * 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.connecteddevice
+
+import android.annotation.SuppressLint
+import android.bluetooth.BluetoothAdapter
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserManager
+import android.provider.Settings
+import android.widget.Toast
+import androidx.preference.Preference
+import com.android.settings.PreferenceRestrictionMixin
+import com.android.settings.R
+import com.android.settings.network.SatelliteRepository.Companion.isSatelliteOn
+import com.android.settings.network.SatelliteWarningDialogActivity
+import com.android.settings.widget.MainSwitchBarMetadata
+import com.android.settingslib.WirelessUtils
+import com.android.settingslib.datastore.AbstractKeyedDataObservable
+import com.android.settingslib.datastore.DataChangeReason
+import com.android.settingslib.datastore.KeyValueStore
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.ReadWritePermit
+import com.android.settingslib.metadata.SensitivityLevel
+
+@SuppressLint("MissingPermission")
+class BluetoothPreference(private val bluetoothDataStore: BluetoothDataStore) :
+ MainSwitchBarMetadata, PreferenceRestrictionMixin, Preference.OnPreferenceChangeListener {
+
+ override val key
+ get() = KEY
+
+ override val title
+ get() = R.string.bluetooth_main_switch_title
+
+ override val restrictionKeys: Array<String>
+ get() = arrayOf(UserManager.DISALLOW_BLUETOOTH, UserManager.DISALLOW_CONFIG_BLUETOOTH)
+
+ override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ ReadWritePermit.ALLOW
+
+ override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
+ when {
+ isSatelliteOn(context, 3000) ||
+ (value == true &&
+ !WirelessUtils.isRadioAllowed(context, Settings.Global.RADIO_BLUETOOTH)) ->
+ ReadWritePermit.DISALLOW
+ else -> ReadWritePermit.ALLOW
+ }
+
+ override val sensitivityLevel
+ get() = SensitivityLevel.LOW_SENSITIVITY
+
+ override fun storage(context: Context) = bluetoothDataStore
+
+ override fun isEnabled(context: Context): Boolean {
+ return super<PreferenceRestrictionMixin>.isEnabled(context) &&
+ bluetoothDataStore.bluetoothAdapter?.state.let {
+ it == BluetoothAdapter.STATE_ON || it == BluetoothAdapter.STATE_OFF
+ }
+ }
+
+ override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+ super.bind(preference, metadata)
+ preference.onPreferenceChangeListener = this
+ }
+
+ override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean {
+ val context = preference.context
+
+ if (isSatelliteOn(context, 3000)) {
+ context.startActivity(
+ Intent(context, SatelliteWarningDialogActivity::class.java)
+ .putExtra(
+ SatelliteWarningDialogActivity.EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG,
+ SatelliteWarningDialogActivity.TYPE_IS_BLUETOOTH,
+ )
+ )
+ return false
+ }
+
+ // Show toast message if Bluetooth is not allowed in airplane mode
+ if (
+ newValue == true &&
+ !WirelessUtils.isRadioAllowed(context, Settings.Global.RADIO_BLUETOOTH)
+ ) {
+ Toast.makeText(context, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show()
+ return false
+ }
+
+ return true
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private class BluetoothStorage(
+ private val context: Context,
+ override val bluetoothAdapter: BluetoothAdapter?,
+ ) : AbstractKeyedDataObservable<String>(), BluetoothDataStore {
+
+ private var broadcastReceiver: BroadcastReceiver? = null
+
+ override fun contains(key: String) = key == KEY && bluetoothAdapter != null
+
+ override fun <T : Any> getValue(key: String, valueType: Class<T>): T {
+ return (bluetoothAdapter?.state.let {
+ it == BluetoothAdapter.STATE_ON || it == BluetoothAdapter.STATE_TURNING_ON
+ })
+ as T
+ }
+
+ @Suppress("DEPRECATION")
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ if (value is Boolean) {
+ if (value) {
+ bluetoothAdapter?.enable()
+ } else {
+ bluetoothAdapter?.disable()
+ }
+ }
+ }
+
+ @SuppressLint("WrongConstant")
+ override fun onFirstObserverAdded() {
+ broadcastReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ notifyChange(KEY, DataChangeReason.UPDATE)
+ }
+ }
+ context.registerReceiver(
+ broadcastReceiver,
+ IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED),
+ Context.RECEIVER_EXPORTED_UNAUDITED,
+ )
+ }
+
+ override fun onLastObserverRemoved() {
+ context.unregisterReceiver(broadcastReceiver)
+ }
+ }
+
+ companion object {
+ const val KEY = "use_bluetooth"
+
+ @Suppress("DEPRECATION")
+ fun createDataStore(context: Context) =
+ createDataStore(context, BluetoothAdapter.getDefaultAdapter())
+
+ fun createDataStore(
+ context: Context,
+ bluetoothAdapter: BluetoothAdapter?,
+ ): BluetoothDataStore = BluetoothStorage(context, bluetoothAdapter)
+ }
+}
+
+/** Datastore of the bluetooth preference. */
+interface BluetoothDataStore : KeyValueStore {
+ val bluetoothAdapter: BluetoothAdapter?
+}
diff --git a/src/com/android/settings/network/AirplaneModePreference.kt b/src/com/android/settings/network/AirplaneModePreference.kt
index 758bcfb..e3b7f55 100644
--- a/src/com/android/settings/network/AirplaneModePreference.kt
+++ b/src/com/android/settings/network/AirplaneModePreference.kt
@@ -26,13 +26,13 @@
import android.provider.Settings
import android.telephony.PhoneStateListener
import android.telephony.TelephonyManager
-import android.util.Log
import androidx.annotation.DrawableRes
import androidx.preference.Preference
import com.android.settings.AirplaneModeEnabler
import com.android.settings.PreferenceRestrictionMixin
import com.android.settings.R
import com.android.settings.Utils
+import com.android.settings.network.SatelliteRepository.Companion.isSatelliteOn
import com.android.settingslib.RestrictedSwitchPreference
import com.android.settingslib.datastore.AbstractKeyedDataObservable
import com.android.settingslib.datastore.DataChangeReason
@@ -45,8 +45,6 @@
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.metadata.SensitivityLevel
import com.android.settingslib.metadata.SwitchPreference
-import java.util.concurrent.Executors
-import java.util.concurrent.TimeUnit
// LINT.IfChange
class AirplaneModePreference :
@@ -113,9 +111,7 @@
context.getSystemService(TelephonyManager::class.java)?.let {
phoneStateListener =
object : PhoneStateListener(Looper.getMainLooper()) {
- @Deprecated("Deprecated in Java")
override fun onRadioPowerStateChanged(state: Int) {
- Log.d(TAG, "onRadioPowerStateChanged(), state=$state")
notifyChange(KEY, DataChangeReason.UPDATE)
}
}
@@ -163,17 +159,6 @@
context.getSystemService(TelephonyManager::class.java),
)
- private fun isSatelliteOn(context: Context): Boolean {
- try {
- return SatelliteRepository(context)
- .requestIsSessionStarted(Executors.newSingleThreadExecutor())
- .get(2000, TimeUnit.MILLISECONDS)
- } catch (e: Exception) {
- Log.e(TAG, "Error to get satellite status : $e")
- }
- return false
- }
-
private fun showEcmDialog(context: PreferenceLifecycleContext) {
val intent =
Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null)
@@ -192,7 +177,6 @@
}
companion object {
- const val TAG = "AirplaneModePreference"
const val KEY = Settings.Global.AIRPLANE_MODE_ON
const val DEFAULT_VALUE = false
const val REQUEST_CODE_EXIT_ECM = 1
diff --git a/src/com/android/settings/network/NetworkDashboardScreen.kt b/src/com/android/settings/network/NetworkDashboardScreen.kt
index 15bf590..1ed88c0 100644
--- a/src/com/android/settings/network/NetworkDashboardScreen.kt
+++ b/src/com/android/settings/network/NetworkDashboardScreen.kt
@@ -17,9 +17,12 @@
import android.content.Context
import com.android.settings.R
+import com.android.settings.Settings.NetworkDashboardActivity
import com.android.settings.datausage.DataSaverScreen
import com.android.settings.flags.Flags
+import com.android.settings.utils.makeLaunchIntent
import com.android.settingslib.metadata.PreferenceIconProvider
+import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.preferenceHierarchy
import com.android.settingslib.preference.PreferenceScreenCreator
@@ -44,6 +47,9 @@
override fun fragmentClass() = NetworkDashboardFragment::class.java
+ override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) =
+ makeLaunchIntent(context, NetworkDashboardActivity::class.java, metadata?.key)
+
override fun getPreferenceHierarchy(context: Context) =
preferenceHierarchy(this) {
+MobileNetworkListScreen.KEY order -15
diff --git a/src/com/android/settings/network/SatelliteRepository.kt b/src/com/android/settings/network/SatelliteRepository.kt
index b7c25f4..c70484a 100644
--- a/src/com/android/settings/network/SatelliteRepository.kt
+++ b/src/com/android/settings/network/SatelliteRepository.kt
@@ -26,6 +26,8 @@
import com.google.common.util.concurrent.Futures.immediateFuture
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.Executor
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
@@ -203,5 +205,15 @@
fun setIsSessionStartedForTesting(isEnabled: Boolean) {
this.isSessionStarted = isEnabled
}
+
+ fun isSatelliteOn(context: Context, timeoutMs: Long = 2000): Boolean =
+ try {
+ SatelliteRepository(context)
+ .requestIsSessionStarted(Executors.newSingleThreadExecutor())
+ .get(timeoutMs, TimeUnit.MILLISECONDS)
+ } catch (e: Exception) {
+ Log.e(TAG, "Error to get satellite status : $e")
+ false
+ }
}
}
diff --git a/src/com/android/settings/notification/LockScreenNotificationsPreferencePageFragment.java b/src/com/android/settings/notification/LockScreenNotificationsPreferencePageFragment.java
index ef53e2d..53b1377 100644
--- a/src/com/android/settings/notification/LockScreenNotificationsPreferencePageFragment.java
+++ b/src/com/android/settings/notification/LockScreenNotificationsPreferencePageFragment.java
@@ -33,8 +33,7 @@
@Override
public int getMetricsCategory() {
- //TODO(b/367455695): create a new metrics category
- return SettingsEnums.SETTINGS_LOCK_SCREEN_PREFERENCES;
+ return SettingsEnums.SETTINGS_NOTIFICATIONS_ON_LOCK_SCREEN;
}
@Override
diff --git a/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java b/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
index 4dfb242..254bbfd 100644
--- a/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
@@ -148,7 +148,8 @@
makeSelectorPreference(KEY_ANY,
R.string.zen_mode_from_anyone, null, mIsMessages, true);
makeSelectorPreference(KEY_NONE,
- R.string.zen_mode_none_messages, null, mIsMessages, true);
+ mIsMessages ? R.string.zen_mode_none_messages : R.string.zen_mode_none_calls,
+ null, mIsMessages, true);
}
super.displayPreference(screen);
}
diff --git a/src/com/android/settings/password/PasswordUtils.java b/src/com/android/settings/password/PasswordUtils.java
index 8c8afc2..da2ad63 100644
--- a/src/com/android/settings/password/PasswordUtils.java
+++ b/src/com/android/settings/password/PasswordUtils.java
@@ -109,7 +109,8 @@
public static void setupScreenLockOptionsButton(Context context, View view, Button optButton) {
final LinearLayout headerLayout = view.findViewById(
com.google.android.setupdesign.R.id.sud_layout_header);
- final TextView sucTitleView = headerLayout.findViewById(R.id.suc_layout_title);
+ final TextView sucTitleView = headerLayout.findViewById(
+ com.google.android.setupdesign.R.id.suc_layout_title);
if (headerLayout != null && sucTitleView != null) {
final ViewGroup.MarginLayoutParams layoutTitleParams =
(ViewGroup.MarginLayoutParams) sucTitleView.getLayoutParams();
diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
index dd6a4bb..e474cd3 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
@@ -62,6 +62,7 @@
private final ActivityManager mActivityManager;
private int mErrorCode;
@GuardedBy("this")
+ @Nullable
private UserHandle mUserHandle;
private final KeyguardManager mKeyguardManager;
/** This variable should be accessed via {@link #getProfileBroadcastReceiver()} only. */
@@ -419,12 +420,16 @@
mContext.unregisterReceiver(/* receiver= */ this);
}
+ @GuardedBy("PrivateSpaceMaintainer.this")
@Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class);
if (intent.getAction().equals(Intent.ACTION_PROFILE_REMOVED)) {
// This applies to all profiles getting removed, since there is no way to tell if
// it is a private profile that got removed.
+ if (userHandle.equals(getPrivateProfileHandle())) {
+ mUserHandle = null;
+ }
removeSettingsAllTasks();
unregisterBroadcastReceiver();
return;
diff --git a/src/com/android/settings/restriction/UserRestrictionBindingHelper.kt b/src/com/android/settings/restriction/UserRestrictionBindingHelper.kt
index 16104de..a1329c2 100644
--- a/src/com/android/settings/restriction/UserRestrictionBindingHelper.kt
+++ b/src/com/android/settings/restriction/UserRestrictionBindingHelper.kt
@@ -25,9 +25,9 @@
/** Helper to rebind preference immediately when user restriction is changed. */
class UserRestrictionBindingHelper(
- context: Context,
+ private val context: Context,
private val screenBindingHelper: PreferenceScreenBindingHelper,
-) : AutoCloseable {
+) : KeyedObserver<String>, AutoCloseable {
private val restrictionKeysToPreferenceKeys: Map<String, MutableSet<String>> =
mutableMapOf<String, MutableSet<String>>()
.apply {
@@ -42,27 +42,29 @@
}
.toMap()
- private val userRestrictionObserver: KeyedObserver<String?>?
-
init {
- if (restrictionKeysToPreferenceKeys.isEmpty()) {
- userRestrictionObserver = null
- } else {
- val observer =
- KeyedObserver<String?> { restrictionKey, _ ->
- restrictionKey?.let { notifyRestrictionChanged(it) }
- }
- UserRestrictions.addObserver(context, observer, HandlerExecutor.main)
- userRestrictionObserver = observer
+ val restrictionKeys = restrictionKeysToPreferenceKeys.keys
+ if (restrictionKeys.isNotEmpty()) {
+ val userRestrictions = UserRestrictions.get(context)
+ val executor = HandlerExecutor.main
+ for (restrictionKey in restrictionKeys) {
+ userRestrictions.addObserver(restrictionKey, this, executor)
+ }
}
}
- private fun notifyRestrictionChanged(restrictionKey: String) {
+ override fun onKeyChanged(restrictionKey: String, reason: Int) {
val keys = restrictionKeysToPreferenceKeys[restrictionKey] ?: return
for (key in keys) screenBindingHelper.notifyChange(key, CHANGE_REASON_STATE)
}
override fun close() {
- userRestrictionObserver?.let { UserRestrictions.removeObserver(it) }
+ val restrictionKeys = restrictionKeysToPreferenceKeys.keys
+ if (restrictionKeys.isNotEmpty()) {
+ val userRestrictions = UserRestrictions.get(context)
+ for (restrictionKey in restrictionKeys) {
+ userRestrictions.removeObserver(restrictionKey, this)
+ }
+ }
}
}
diff --git a/src/com/android/settings/restriction/UserRestrictions.kt b/src/com/android/settings/restriction/UserRestrictions.kt
index 1fa6830..880aa5d 100644
--- a/src/com/android/settings/restriction/UserRestrictions.kt
+++ b/src/com/android/settings/restriction/UserRestrictions.kt
@@ -16,68 +16,58 @@
package com.android.settings.restriction
+import android.content.BroadcastReceiver
import android.content.Context
-import android.os.Bundle
-import android.os.IUserRestrictionsListener
+import android.content.Intent
+import android.content.IntentFilter
import android.os.UserManager
-import com.android.settingslib.datastore.KeyedDataObservable
+import com.android.settingslib.datastore.AbstractKeyedDataObservable
+import com.android.settingslib.datastore.DataChangeReason
import com.android.settingslib.datastore.KeyedObserver
import java.util.concurrent.Executor
-import java.util.concurrent.atomic.AtomicBoolean
/** Helper class to monitor user restriction changes. */
-object UserRestrictions {
- private val observable = KeyedDataObservable<String>()
+class UserRestrictions private constructor(private val applicationContext: Context) {
- private val userRestrictionsListener =
- object : IUserRestrictionsListener.Stub() {
- override fun onUserRestrictionsChanged(
- userId: Int,
- newRestrictions: Bundle,
- prevRestrictions: Bundle,
- ) {
- // there is no API to remove listener, do a quick check to avoid unnecessary work
- if (!observable.hasAnyObserver()) return
+ private val observable =
+ object : AbstractKeyedDataObservable<String>() {
+ override fun onFirstObserverAdded() {
+ val intentFilter = IntentFilter()
+ intentFilter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)
+ applicationContext.registerReceiver(broadcastReceiver, intentFilter)
+ }
- val changedKeys = mutableSetOf<String>()
- val keys = newRestrictions.keySet() + prevRestrictions.keySet()
- for (key in keys) {
- if (newRestrictions.getBoolean(key) != prevRestrictions.getBoolean(key)) {
- changedKeys.add(key)
- }
- }
-
- for (key in changedKeys) observable.notifyChange(key, 0)
+ override fun onLastObserverRemoved() {
+ applicationContext.unregisterReceiver(broadcastReceiver)
}
}
- private val listenerAdded = AtomicBoolean()
+ private val broadcastReceiver: BroadcastReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ // there is no way to get the changed keys, just notify all observers
+ observable.notifyChange(DataChangeReason.UPDATE)
+ }
+ }
- fun addObserver(context: Context, observer: KeyedObserver<String?>, executor: Executor) {
- context.addUserRestrictionsListener()
+ fun addObserver(observer: KeyedObserver<String?>, executor: Executor) =
observable.addObserver(observer, executor)
- }
- fun addObserver(
- context: Context,
- key: String,
- observer: KeyedObserver<String>,
- executor: Executor,
- ) {
- context.addUserRestrictionsListener()
+ fun addObserver(key: String, observer: KeyedObserver<String>, executor: Executor) =
observable.addObserver(key, observer, executor)
- }
-
- private fun Context.addUserRestrictionsListener() {
- if (listenerAdded.getAndSet(true)) return
- // surprisingly, there is no way to remove the listener
- applicationContext
- .getSystemService(UserManager::class.java)
- .addUserRestrictionsListener(userRestrictionsListener)
- }
fun removeObserver(observer: KeyedObserver<String?>) = observable.removeObserver(observer)
fun removeObserver(key: String, observer: KeyedObserver<String>) =
observable.removeObserver(key, observer)
+
+ companion object {
+ @Volatile private var instance: UserRestrictions? = null
+
+ fun get(context: Context) =
+ instance
+ ?: synchronized(this) {
+ instance ?: UserRestrictions(context.applicationContext).also { instance = it }
+ }
+ }
}
diff --git a/src/com/android/settings/widget/MainSwitchBarPreference.kt b/src/com/android/settings/widget/MainSwitchBarPreference.kt
index 6ed8877..b3b341c 100644
--- a/src/com/android/settings/widget/MainSwitchBarPreference.kt
+++ b/src/com/android/settings/widget/MainSwitchBarPreference.kt
@@ -17,6 +17,8 @@
package com.android.settings.widget
import android.content.Context
+import android.os.Parcel
+import android.os.Parcelable
import android.widget.CompoundButton
import android.widget.CompoundButton.OnCheckedChangeListener
import androidx.preference.TwoStatePreference
@@ -26,26 +28,32 @@
/** Preference abstraction of the [MainSwitchBar] in settings activity. */
class MainSwitchBarPreference(context: Context, private val metadata: MainSwitchBarMetadata) :
- TwoStatePreference(context), OnCheckedChangeListener {
+ TwoStatePreference(context), OnCheckedChangeListener, MainSwitchBar.PreChangeListener {
- private val mainSwitchBar: MainSwitchBar = (context as SettingsActivity).switchBar
+ // main switch bar might be null when configuration is just changed
+ private val mainSwitchBar: MainSwitchBar?
+ get() = (context as SettingsActivity).switchBar
override fun setTitle(title: CharSequence?) {
- mainSwitchBar.setTitle(title)
+ mainSwitchBar?.setTitle(title)
+ super.setTitle(title)
}
override fun setSummary(summary: CharSequence?) {
- mainSwitchBar.setSummary(summary)
+ mainSwitchBar?.setSummary(summary)
+ super.setSummary(summary)
}
override fun setEnabled(enabled: Boolean) {
- mainSwitchBar.isEnabled = enabled
+ mainSwitchBar?.isEnabled = enabled
+ super.setEnabled(enabled)
}
// Preference.setVisible is final, we cannot override it
fun updateVisibility() {
// always make preference invisible, the UI visibility is reflected on MainSwitchBar
isVisible = false
+ val mainSwitchBar = mainSwitchBar ?: return
if ((metadata as? PreferenceAvailabilityProvider)?.isAvailable(context) != false) {
mainSwitchBar.show()
} else {
@@ -54,6 +62,7 @@
}
override fun setChecked(checked: Boolean) {
+ val mainSwitchBar = mainSwitchBar ?: return
// remove listener to update UI only
mainSwitchBar.removeOnSwitchChangeListener(this)
mainSwitchBar.isChecked = checked
@@ -62,9 +71,13 @@
override fun onAttached() {
super.onAttached()
+ val mainSwitchBar = mainSwitchBar!!
+ mainSwitchBar.setPreChangeListener(this)
mainSwitchBar.addOnSwitchChangeListener(this)
}
+ override fun preChange(isCheck: Boolean) = callChangeListener(isCheck)
+
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
// prevent user from toggling the switch before data store operation is done
isEnabled = false
@@ -73,7 +86,65 @@
}
override fun onDetached() {
+ val mainSwitchBar = mainSwitchBar!!
mainSwitchBar.removeOnSwitchChangeListener(this)
+ mainSwitchBar.setPreChangeListener(null)
super.onDetached()
}
+
+ override fun onSaveInstanceState(): Parcelable =
+ SavedState(super.onSaveInstanceState()!!).also {
+ it.isEnabled = isEnabled
+ it.title = title
+ it.summary = summary
+ it.mainSwitchBarState = mainSwitchBar?.onSaveInstanceState()
+ }
+
+ override fun onRestoreInstanceState(state: Parcelable?) {
+ val savedState = state as SavedState
+ super.onRestoreInstanceState(savedState.superState)
+ isEnabled = savedState.isEnabled
+ title = savedState.title
+ summary = savedState.summary
+ mainSwitchBar?.onRestoreInstanceState(savedState.mainSwitchBarState!!)
+ }
+
+ private class SavedState : BaseSavedState {
+ var isEnabled: Boolean = false
+ var title: CharSequence? = null
+ var summary: CharSequence? = null
+ var mainSwitchBarState: Parcelable? = null
+
+ constructor(source: Parcel) : super(source) {
+ isEnabled = source.readBoolean()
+ title = source.readCharSequence()
+ summary = source.readCharSequence()
+ val stateClass = MainSwitchBar.SavedState::class.java
+ mainSwitchBarState = source.readParcelable(stateClass.classLoader, stateClass)
+ }
+
+ constructor(superState: Parcelable) : super(superState)
+
+ override fun writeToParcel(dest: Parcel, flags: Int) {
+ super.writeToParcel(dest, flags)
+ dest.writeBoolean(isEnabled)
+ dest.writeCharSequence(title)
+ dest.writeCharSequence(summary)
+ dest.writeParcelable(mainSwitchBarState, flags)
+ }
+
+ companion object {
+ @JvmField
+ val CREATOR: Parcelable.Creator<SavedState> =
+ object : Parcelable.Creator<SavedState> {
+ override fun createFromParcel(parcel: Parcel): SavedState {
+ return SavedState(parcel)
+ }
+
+ override fun newArray(size: Int): Array<SavedState?> {
+ return arrayOfNulls(size)
+ }
+ }
+ }
+ }
}
diff --git a/src/com/android/settings/widget/SettingsMainSwitchBar.java b/src/com/android/settings/widget/SettingsMainSwitchBar.java
index e8c6fc9..6bccbd7 100644
--- a/src/com/android/settings/widget/SettingsMainSwitchBar.java
+++ b/src/com/android/settings/widget/SettingsMainSwitchBar.java
@@ -109,7 +109,7 @@
return true;
}
- return mSwitch.performClick();
+ return callPreChangeListener() && mSwitch.performClick();
}
@Override
diff --git a/src/com/android/settings/wifi/WifiSwitchPreference.kt b/src/com/android/settings/wifi/WifiSwitchPreference.kt
index 2a18d3f..ba6fb02 100644
--- a/src/com/android/settings/wifi/WifiSwitchPreference.kt
+++ b/src/com/android/settings/wifi/WifiSwitchPreference.kt
@@ -23,13 +23,12 @@
import android.net.wifi.WifiManager
import android.os.UserManager
import android.provider.Settings
-import android.util.Log
import android.widget.Toast
import androidx.preference.Preference
import androidx.preference.Preference.OnPreferenceChangeListener
import com.android.settings.PreferenceRestrictionMixin
import com.android.settings.R
-import com.android.settings.network.SatelliteRepository
+import com.android.settings.network.SatelliteRepository.Companion.isSatelliteOn
import com.android.settings.network.SatelliteWarningDialogActivity
import com.android.settingslib.RestrictedSwitchPreference
import com.android.settingslib.WirelessUtils
@@ -42,8 +41,6 @@
import com.android.settingslib.metadata.SensitivityLevel
import com.android.settingslib.metadata.SwitchPreference
import com.android.settingslib.preference.SwitchPreferenceBinding
-import java.util.concurrent.Executors
-import java.util.concurrent.TimeUnit
// LINT.IfChange
class WifiSwitchPreference :
@@ -75,7 +72,7 @@
val context = preference.context
// Show dialog and do nothing under satellite mode.
- if (context.isSatelliteOn()) {
+ if (isSatelliteOn(context)) {
context.startActivity(
Intent(context, SatelliteWarningDialogActivity::class.java)
.putExtra(
@@ -100,7 +97,7 @@
override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
when {
- (value == true && !context.isRadioAllowed()) || context.isSatelliteOn() ->
+ (value == true && !context.isRadioAllowed()) || isSatelliteOn(context) ->
ReadWritePermit.DISALLOW
else -> ReadWritePermit.ALLOW
}
@@ -155,22 +152,11 @@
}
companion object {
- const val TAG = "WifiSwitchPreference"
const val KEY = "main_toggle_wifi"
private fun Context.isRadioAllowed() =
WirelessUtils.isRadioAllowed(this, Settings.Global.RADIO_WIFI)
- private fun Context.isSatelliteOn() =
- try {
- SatelliteRepository(this)
- .requestIsSessionStarted(Executors.newSingleThreadExecutor())
- .get(2000, TimeUnit.MILLISECONDS)
- } catch (e: Exception) {
- Log.e(TAG, "Error to get satellite status : $e")
- false
- }
-
private val Intent.wifiState
get() = getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN)
}
diff --git a/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt b/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt
index c185e02..931583a 100644
--- a/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt
+++ b/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt
@@ -100,7 +100,7 @@
.toIntent()
override fun isEnabled(context: Context) =
- wifiHotspotStore.dataSaverStore.getBoolean(DATA_SAVER_KEY) == true &&
+ wifiHotspotStore.dataSaverStore.getBoolean(DATA_SAVER_KEY) != true &&
super<PreferenceRestrictionMixin>.isEnabled(context)
override val restrictionKeys
@@ -193,6 +193,7 @@
super.bind(preference, metadata)
(preference as PrimarySwitchPreference).apply {
isChecked = preferenceDataStore!!.getBoolean(key, false)
+ isSwitchEnabled = isEnabled
}
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
index 97faa63..b8b0699 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
@@ -208,7 +208,24 @@
ShadowLooper.idleMainLooper();
assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
- "TEST_HEARING_AID_BT_DEVICE_NAME / Left and right")).isTrue();
+ "TEST_HEARING_AID_BT_DEVICE_NAME active")).isTrue();
+ }
+
+ @Test
+ public void getSummary_connectedLeAudioHearingAidMonoSide_connectedSummary() {
+ when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
+ HearingAidInfo.DeviceSide.SIDE_MONO);
+ when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(new HashSet<>());
+ when(mHapClientProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
+
+ mPreferenceController.onStart();
+ Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
+ intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHapClient.STATE_CONNECTED);
+ sendIntent(intent);
+ ShadowLooper.idleMainLooper();
+
+ assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
+ "TEST_HEARING_AID_BT_DEVICE_NAME active")).isTrue();
}
@Test
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/BluetoothMainSwitchPreferenceTest.kt b/tests/robotests/src/com/android/settings/connecteddevice/BluetoothPreferenceTest.kt
similarity index 75%
rename from tests/robotests/src/com/android/settings/connecteddevice/BluetoothMainSwitchPreferenceTest.kt
rename to tests/robotests/src/com/android/settings/connecteddevice/BluetoothPreferenceTest.kt
index 15db130..8b739db 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/BluetoothMainSwitchPreferenceTest.kt
+++ b/tests/robotests/src/com/android/settings/connecteddevice/BluetoothPreferenceTest.kt
@@ -31,45 +31,42 @@
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
-class BluetoothMainSwitchPreferenceTest {
+class BluetoothPreferenceTest {
@get:Rule val setFlagsRule = SetFlagsRule()
private val context: Context = ApplicationProvider.getApplicationContext()
private lateinit var bluetoothAdapter: BluetoothAdapter
- private lateinit var bluetoothMainSwitchPreference: BluetoothMainSwitchPreference
+ private lateinit var bluetoothPreference: BluetoothPreference
@Before
fun setUp() {
bluetoothAdapter = spy(BluetoothAdapter.getDefaultAdapter())
whenever(bluetoothAdapter.state).thenReturn(BluetoothAdapter.STATE_ON)
- bluetoothMainSwitchPreference = BluetoothMainSwitchPreference(bluetoothAdapter)
+ bluetoothPreference =
+ BluetoothPreference(BluetoothPreference.createDataStore(context, bluetoothAdapter))
}
@Test
fun isEnabled_bluetoothOn_returnTrue() {
- assertThat(bluetoothMainSwitchPreference.isEnabled(context)).isTrue()
+ assertThat(bluetoothPreference.isEnabled(context)).isTrue()
}
@Test
fun isEnabled_bluetoothTurningOn_returnFalse() {
whenever(bluetoothAdapter.state).thenReturn(BluetoothAdapter.STATE_TURNING_ON)
- assertThat(bluetoothMainSwitchPreference.isEnabled(context)).isFalse()
+ assertThat(bluetoothPreference.isEnabled(context)).isFalse()
}
@Test
fun storageSetOff_turnOff() {
- bluetoothMainSwitchPreference
- .storage(context)
- .setBoolean(bluetoothMainSwitchPreference.key, false)
+ bluetoothPreference.storage(context).setBoolean(bluetoothPreference.key, false)
verify(bluetoothAdapter).disable()
}
@Test
fun storageSetOn_turnOn() {
- bluetoothMainSwitchPreference
- .storage(context)
- .setBoolean(bluetoothMainSwitchPreference.key, true)
+ bluetoothPreference.storage(context).setBoolean(bluetoothPreference.key, true)
verify(bluetoothAdapter).enable()
}
diff --git a/tests/unit/src/com/android/settings/network/telephony/SatelliteSettingsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/SatelliteSettingsPreferenceControllerTest.java
index 477a63f..6e650fd 100644
--- a/tests/unit/src/com/android/settings/network/telephony/SatelliteSettingsPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/SatelliteSettingsPreferenceControllerTest.java
@@ -56,6 +56,7 @@
import org.mockito.junit.MockitoRule;
@RunWith(AndroidJUnit4.class)
+@Ignore("b/382664790")
public class SatelliteSettingsPreferenceControllerTest {
private static final String KEY = "key";
private static final int TEST_SUB_ID = 0;
diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
index bfe4be2..191935f 100644
--- a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
+++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
@@ -36,9 +36,11 @@
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Flags;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
@@ -46,6 +48,7 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
import com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace;
import org.junit.After;
@@ -57,6 +60,9 @@
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
@RunWith(AndroidJUnit4.class)
public class PrivateSpaceMaintainerTest {
private static final String TAG = "PSMaintainerTest";
@@ -506,4 +512,25 @@
/* enabled */ 1,
privateSpaceMaintainer.getPrivateProfileHandle().getIdentifier());
}
+
+ @Test
+ public void profileRemovedFromUserManager_privateSpaceNoLongerExists() {
+ PrivateSpaceMaintainer privateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(
+ mContext);
+ privateSpaceMaintainer.createPrivateSpace();
+ UserHandle privateSpaceUserHandle = privateSpaceMaintainer.getPrivateProfileHandle();
+ assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isTrue();
+ Intent removedIntent = new Intent(Intent.ACTION_PROFILE_REMOVED);
+ assertThat(privateSpaceUserHandle).isNotNull();
+ final BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(mContext,
+ removedIntent.getAction());
+ receiver.register();
+
+ Objects.requireNonNull(mContext.getSystemService(UserManager.class)).removeUser(
+ privateSpaceUserHandle);
+
+ receiver.awaitForBroadcast(TimeUnit.SECONDS.toMillis(10));
+ assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isFalse();
+ }
+
}