Merge "Inline shouldDisableMediaOutput into Settings app" into main
diff --git a/res-product/values/strings.xml b/res-product/values/strings.xml
index 4736d22..3ce29d6 100644
--- a/res-product/values/strings.xml
+++ b/res-product/values/strings.xml
@@ -738,9 +738,4 @@
<string name="daltonizer_feature_summary" product="default">Adjust how colors display on your phone</string>
<!-- The daltonizer feature summary display as a subtext as an item in a list. -->
<string name="daltonizer_feature_summary" product="tablet">Adjust how colors display on your tablet</string>
-
- <!-- Warning text about the visibility of device name on phone. [CHAR LIMIT=NONE] -->
- <string name="about_phone_device_name_warning" product="default">Your device name is visible to apps on your phone. It may also be seen by other people when you connect to Bluetooth devices, connect to a Wi-Fi network or set up a Wi-Fi hotspot.</string>
- <!-- Warning text about the visibility of device name on tablet. [CHAR LIMIT=NONE] -->
- <string name="about_phone_device_name_warning" product="tablet">Your device name is visible to apps on your tablet. It may also be seen by other people when you connect to Bluetooth devices, connect to a Wi-Fi network or set up a Wi-Fi hotspot.</string>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 51cfeb7..f20bce0 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -545,6 +545,9 @@
<!-- Whether to show Smooth Display feature in Settings Options -->
<bool name="config_show_smooth_display">false</bool>
+ <!-- Whether to show Stay awake on fold feature in Settings Options -->
+ <bool name="config_stay_awake_on_fold">false</bool>
+
<!-- Whether to show emergency settings in top-level Settings -->
<bool name="config_show_emergency_settings">true</bool>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ba4ff3c..177a5cb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2378,6 +2378,10 @@
<string name="display_white_balance_title">Display white balance</string>
<!-- Display settings screen, display white balance settings summary [CHAR LIMIT=NONE] -->
<string name="display_white_balance_summary"></string>
+ <!-- Display settings screen, setting name to enable staying awake on fold [CHAR LIMIT=30] -->
+ <string name="stay_awake_on_fold_title">Stay unlocked on fold</string>
+ <!-- Display settings screen, setting summary to enable staying awake on fold [CHAR LIMIT=NONE] -->
+ <string name="stay_awake_on_fold_summary">Keep front display unlocked when folded until screen timeout</string>
<!-- Display settings screen, peak refresh rate settings title [CHAR LIMIT=30] -->
<string name="peak_refresh_rate_title">Smooth Display</string>
<!-- Display settings screen, peak refresh rate settings summary [CHAR LIMIT=NONE] -->
@@ -7061,6 +7065,9 @@
<string name="keywords_app_pinning">screen pinning</string>
<string name="keywords_profile_challenge">work challenge, work, profile</string>
<string name="keywords_unification">work profile, managed profile, unify, unification, work, profile</string>
+ <string name="keywords_stay_awake_on_lock">
+ awake, sleep, do not lock, stay unlocked on fold, folding, closing, fold, close, screen off
+ </string>
<string name="keywords_gesture">gestures</string>
<string name="keywords_wallet">wallet</string>
<string name="keywords_payment_settings">pay, tap, payments</string>
@@ -12211,4 +12218,6 @@
<!-- Summary of the Live Caption enabled state. -->
<string name="live_caption_enabled">On</string>
+ <!-- Warning text about the visibility of device name. [CHAR LIMIT=NONE] -->
+ <string name="about_phone_device_name_warning">Your device name is visible to apps you installed. It may also be seen by other people when you connect to Bluetooth devices, connect to a Wi-Fi network or set up a Wi-Fi hotspot.</string>
</resources>
diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml
index ad5236e..f94ba70 100644
--- a/res/xml/display_settings.xml
+++ b/res/xml/display_settings.xml
@@ -48,6 +48,13 @@
settings:keywords="@string/keywords_ambient_display_screen"
settings:controller="com.android.settings.security.screenlock.LockScreenPreferenceController"/>
+ <SwitchPreference
+ android:key="stay_awake_on_fold"
+ android:title="@string/stay_awake_on_fold_title"
+ android:summary="@string/stay_awake_on_fold_summary"
+ settings:keywords="@string/keywords_stay_awake_on_lock"
+ settings:controller="com.android.settings.display.StayAwakeOnFoldPreferenceController"/>
+
<com.android.settingslib.RestrictedPreference
android:key="screen_timeout"
android:title="@string/screen_timeout"
diff --git a/res/xml/stylus_usi_details_fragment.xml b/res/xml/stylus_usi_details_fragment.xml
index 8a1d036..639c284 100644
--- a/res/xml/stylus_usi_details_fragment.xml
+++ b/res/xml/stylus_usi_details_fragment.xml
@@ -30,4 +30,7 @@
<PreferenceCategory
android:key="device_stylus"/>
+ <PreferenceCategory
+ android:key="stylus_usb_firmware"
+ settings:controller="com.android.settings.connecteddevice.stylus.StylusUsbFirmwareController"/>
</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/SetFullBackupPassword.java b/src/com/android/settings/SetFullBackupPassword.java
index b6a03d5..833ba99 100644
--- a/src/com/android/settings/SetFullBackupPassword.java
+++ b/src/com/android/settings/SetFullBackupPassword.java
@@ -25,6 +25,7 @@
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
@@ -80,6 +81,7 @@
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
diff --git a/src/com/android/settings/biometrics/face/FaceUpdater.java b/src/com/android/settings/biometrics/face/FaceUpdater.java
index 3a1f77c..57c1195 100644
--- a/src/com/android/settings/biometrics/face/FaceUpdater.java
+++ b/src/com/android/settings/biometrics/face/FaceUpdater.java
@@ -50,8 +50,9 @@
/** Wrapper around the {@link FaceManager#enroll} method. */
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
FaceManager.EnrollmentCallback callback, int[] disabledFeatures) {
- mFaceManager.enroll(userId, hardwareAuthToken, cancel,
- new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures);
+ this.enroll(userId, hardwareAuthToken, cancel,
+ new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures,
+ null, false);
}
/** Wrapper around the {@link FaceManager#enroll} method. */
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index 47bd436..ba2786e 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -69,7 +69,7 @@
private static final String ENABLE_DUAL_MODE_AUDIO =
"persist.bluetooth.enable_dual_mode_audio";
private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
- private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = false;
+ private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = true;
private LocalBluetoothManager mManager;
private LocalBluetoothProfileManager mProfileManager;
diff --git a/src/com/android/settings/connecteddevice/stylus/StylusFeatureProvider.java b/src/com/android/settings/connecteddevice/stylus/StylusFeatureProvider.java
new file mode 100644
index 0000000..cd9e981
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/stylus/StylusFeatureProvider.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.stylus;
+
+import android.content.Context;
+import android.hardware.usb.UsbDevice;
+
+import androidx.preference.Preference;
+
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+/** FeatureProvider for USB settings **/
+public interface StylusFeatureProvider {
+
+ /**
+ * Returns whether the current attached USB device allows firmware updates.
+ *
+ * @param usbDevice The USB device to check
+ */
+ boolean isUsbFirmwareUpdateEnabled(UsbDevice usbDevice);
+
+ /**
+ * Returns a list of preferences for the connected USB device if exists. If not, returns
+ * null. If an update is not available but firmware update feature is enabled for the device,
+ * the list will contain only the preference showing the current firmware version.
+ *
+ * @param context The context
+ */
+ @Nullable
+ List<Preference> getUsbFirmwareUpdatePreferences(Context context);
+}
diff --git a/src/com/android/settings/connecteddevice/stylus/StylusFeatureProviderImpl.java b/src/com/android/settings/connecteddevice/stylus/StylusFeatureProviderImpl.java
new file mode 100644
index 0000000..ab9d2ce
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/stylus/StylusFeatureProviderImpl.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.stylus;
+
+import android.content.Context;
+import android.hardware.usb.UsbDevice;
+
+import androidx.preference.Preference;
+
+import java.util.List;
+
+
+/** Default implementation for StylusFeatureProvider **/
+public class StylusFeatureProviderImpl implements StylusFeatureProvider {
+ @Override
+ public boolean isUsbFirmwareUpdateEnabled(UsbDevice usbDevice) {
+ return false;
+ }
+
+ @Override
+ public List<Preference> getUsbFirmwareUpdatePreferences(Context context) {
+ return null;
+ }
+}
diff --git a/src/com/android/settings/connecteddevice/stylus/StylusUsbFirmwareController.java b/src/com/android/settings/connecteddevice/stylus/StylusUsbFirmwareController.java
new file mode 100644
index 0000000..0f79d3e
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/stylus/StylusUsbFirmwareController.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.stylus;
+
+import android.content.Context;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Preference controller for stylus firmware updates via USB **/
+public class StylusUsbFirmwareController extends BasePreferenceController
+ implements LifecycleObserver, OnStart, OnStop {
+ private static final String TAG = StylusUsbFirmwareController.class.getSimpleName();
+ @Nullable
+ private UsbDevice mStylusUsbDevice;
+ private final UsbStylusBroadcastReceiver mUsbStylusBroadcastReceiver;
+
+ private PreferenceScreen mPreferenceScreen;
+ private PreferenceCategory mPreference;
+
+ @VisibleForTesting
+ UsbStylusBroadcastReceiver.UsbStylusConnectionListener mUsbConnectionListener =
+ (stylusUsbDevice, attached) -> {
+ refresh();
+ };
+
+ public StylusUsbFirmwareController(Context context, String key) {
+ super(context, key);
+ mUsbStylusBroadcastReceiver = new UsbStylusBroadcastReceiver(context,
+ mUsbConnectionListener);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ mPreferenceScreen = screen;
+ refresh();
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ // always available, preferences will be added or
+ // removed according to the connected usb device
+ return AVAILABLE;
+ }
+
+ private void refresh() {
+ if (mPreferenceScreen == null) return;
+
+ UsbDevice device = getStylusUsbDevice();
+ if (device == mStylusUsbDevice) {
+ return;
+ }
+ mStylusUsbDevice = device;
+ mPreference = mPreferenceScreen.findPreference(getPreferenceKey());
+ if (mPreference != null) {
+ mPreferenceScreen.removePreference(mPreference);
+ }
+ if (hasUsbStylusFirmwareUpdateFeature(mStylusUsbDevice)) {
+ StylusFeatureProvider featureProvider = FeatureFactory.getFactory(
+ mContext).getStylusFeatureProvider();
+ List<Preference> preferences =
+ featureProvider.getUsbFirmwareUpdatePreferences(mContext);
+
+ if (preferences != null) {
+ mPreference = new PreferenceCategory(mContext);
+ mPreference.setKey(getPreferenceKey());
+ mPreferenceScreen.addPreference(mPreference);
+
+ for (Preference preference : preferences) {
+ mPreference.addPreference(preference);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onStart() {
+ mUsbStylusBroadcastReceiver.register();
+ }
+
+ @Override
+ public void onStop() {
+ mUsbStylusBroadcastReceiver.unregister();
+ }
+
+ private UsbDevice getStylusUsbDevice() {
+ UsbManager usbManager = mContext.getSystemService(UsbManager.class);
+
+ if (usbManager == null) {
+ return null;
+ }
+
+ List<UsbDevice> devices = new ArrayList<>(usbManager.getDeviceList().values());
+ if (devices.isEmpty()) {
+ return null;
+ }
+
+ UsbDevice usbDevice = devices.get(0);
+ if (hasUsbStylusFirmwareUpdateFeature(usbDevice)) {
+ return usbDevice;
+ }
+ return null;
+ }
+
+ static boolean hasUsbStylusFirmwareUpdateFeature(UsbDevice usbDevice) {
+ if (usbDevice == null) return false;
+
+ StylusFeatureProvider featureProvider = FeatureFactory.getFactory(
+ FeatureFactory.getAppContext()).getStylusFeatureProvider();
+
+ return featureProvider.isUsbFirmwareUpdateEnabled(usbDevice);
+ }
+}
diff --git a/src/com/android/settings/connecteddevice/stylus/StylusUsiDetailsFragment.java b/src/com/android/settings/connecteddevice/stylus/StylusUsiDetailsFragment.java
index 5e68a53..ea9781e 100644
--- a/src/com/android/settings/connecteddevice/stylus/StylusUsiDetailsFragment.java
+++ b/src/com/android/settings/connecteddevice/stylus/StylusUsiDetailsFragment.java
@@ -54,7 +54,6 @@
}
}
-
@Override
public int getMetricsCategory() {
return SettingsEnums.USI_DEVICE_DETAILS;
diff --git a/src/com/android/settings/connecteddevice/stylus/UsbStylusBroadcastReceiver.java b/src/com/android/settings/connecteddevice/stylus/UsbStylusBroadcastReceiver.java
new file mode 100644
index 0000000..8c8b4fd
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/stylus/UsbStylusBroadcastReceiver.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.stylus;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+
+/** Broadcast receiver for styluses connected via USB **/
+public class UsbStylusBroadcastReceiver extends BroadcastReceiver {
+ private Context mContext;
+ private UsbStylusConnectionListener mUsbConnectionListener;
+ private boolean mListeningToUsbEvents;
+
+ public UsbStylusBroadcastReceiver(Context context,
+ UsbStylusConnectionListener usbConnectionListener) {
+ mContext = context;
+ mUsbConnectionListener = usbConnectionListener;
+ }
+
+ /** Registers the receiver. **/
+ public void register() {
+ if (!mListeningToUsbEvents) {
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ final Intent intent = mContext.registerReceiver(this, intentFilter);
+ if (intent != null) {
+ onReceive(mContext, intent);
+ }
+ mListeningToUsbEvents = true;
+ }
+ }
+
+ /** Unregisters the receiver. **/
+ public void unregister() {
+ if (mListeningToUsbEvents) {
+ mContext.unregisterReceiver(this);
+ mListeningToUsbEvents = false;
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice.class);
+ if (StylusUsbFirmwareController.hasUsbStylusFirmwareUpdateFeature(usbDevice)) {
+ mUsbConnectionListener.onUsbStylusConnectionChanged(usbDevice,
+ intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED));
+ }
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when stylus usb connection is changed.
+ */
+ interface UsbStylusConnectionListener {
+ void onUsbStylusConnectionChanged(UsbDevice device, boolean connected);
+ }
+}
diff --git a/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java b/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java
index 9545728..298ced0 100644
--- a/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java
+++ b/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java
@@ -40,7 +40,7 @@
private static final String PREFERENCE_KEY = "bluetooth_show_leaudio_device_details";
private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
- private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = false;
+ private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = true;
static int sLeAudioSupportedStateCache = BluetoothStatusCodes.ERROR_UNKNOWN;
@VisibleForTesting
diff --git a/src/com/android/settings/display/StayAwakeOnFoldPreferenceController.java b/src/com/android/settings/display/StayAwakeOnFoldPreferenceController.java
new file mode 100644
index 0000000..9df48f3
--- /dev/null
+++ b/src/com/android/settings/display/StayAwakeOnFoldPreferenceController.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.display;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * A preference controller for the "Stay unlocked on fold" setting.
+ *
+ * This preference controller allows users to control whether or not the device
+ * stays awake when it is folded. When this setting is enabled, the device will
+ * stay awake even if the device is folded.
+ *
+ * @link android.provider.Settings.System#STAY_AWAKE_ON_FOLD
+ */
+public class StayAwakeOnFoldPreferenceController extends TogglePreferenceController {
+
+ private final Resources mResources;
+
+ public StayAwakeOnFoldPreferenceController(Context context, String key) {
+ this(context, key, context.getResources());
+ }
+
+ public StayAwakeOnFoldPreferenceController(Context context, String key, Resources resources) {
+ super(context, key);
+ mResources = resources;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mResources.getBoolean(R.bool.config_stay_awake_on_fold) ? AVAILABLE
+ : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return Settings.System.getInt(
+ mContext.getContentResolver(),
+ Settings.System.STAY_AWAKE_ON_FOLD,
+ 0) == 1;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ final int stayUnlockedOnFold = isChecked ? 1 : 0;
+
+ return Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.STAY_AWAKE_ON_FOLD, stayUnlockedOnFold);
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_display;
+ }
+
+}
diff --git a/src/com/android/settings/localepicker/LocaleDialogFragment.java b/src/com/android/settings/localepicker/LocaleDialogFragment.java
index f54446a..6c37e38 100644
--- a/src/com/android/settings/localepicker/LocaleDialogFragment.java
+++ b/src/com/android/settings/localepicker/LocaleDialogFragment.java
@@ -16,6 +16,8 @@
package com.android.settings.localepicker;
+import static android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT;
+
import android.app.Activity;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
@@ -23,15 +25,17 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.FragmentManager;
import com.android.internal.app.LocaleStore;
import com.android.settings.R;
@@ -53,6 +57,12 @@
static final String ARG_SHOW_DIALOG = "arg_show_dialog";
private boolean mShouldKeepDialog;
+ private AlertDialog mAlertDialog;
+ private OnBackInvokedDispatcher mBackDispatcher;
+
+ private OnBackInvokedCallback mBackCallback = () -> {
+ Log.d(TAG, "Do not back to previous page if the dialog is displaying.");
+ };
public static LocaleDialogFragment newInstance() {
return new LocaleDialogFragment();
@@ -108,9 +118,15 @@
if (!dialogContent.mNegativeButton.isEmpty()) {
builder.setNegativeButton(dialogContent.mNegativeButton, controller);
}
- AlertDialog alertDialog = builder.create();
- alertDialog.setCanceledOnTouchOutside(false);
- return alertDialog;
+ mAlertDialog = builder.create();
+ getOnBackInvokedDispatcher().registerOnBackInvokedCallback(PRIORITY_DEFAULT, mBackCallback);
+ mAlertDialog.setCanceledOnTouchOutside(false);
+ mAlertDialog.setOnDismissListener(dialogInterface -> {
+ mAlertDialog.getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(
+ mBackCallback);
+ });
+
+ return mAlertDialog;
}
private static void setDialogTitle(View root, String content) {
@@ -130,6 +146,25 @@
}
@VisibleForTesting
+ public OnBackInvokedCallback getBackInvokedCallback() {
+ return mBackCallback;
+ }
+
+ @VisibleForTesting
+ public void setBackDispatcher(OnBackInvokedDispatcher dispatcher) {
+ mBackDispatcher = dispatcher;
+ }
+
+ @VisibleForTesting
+ public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+ if (mBackDispatcher != null) {
+ return mBackDispatcher;
+ } else {
+ return mAlertDialog.getOnBackInvokedDispatcher();
+ }
+ }
+
+ @VisibleForTesting
LocaleDialogController getLocaleDialogController(Context context,
LocaleDialogFragment dialogFragment, LocaleListEditor parentFragment) {
return new LocaleDialogController(context, dialogFragment, parentFragment);
@@ -155,11 +190,6 @@
mParent = parentFragment;
}
- LocaleDialogController(@NonNull LocaleDialogFragment dialogFragment,
- LocaleListEditor parent) {
- this(dialogFragment.getContext(), dialogFragment, parent);
- }
-
@Override
public void onClick(DialogInterface dialog, int which) {
if (mDialogType == DIALOG_CONFIRM_SYSTEM_DEFAULT) {
diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
index 3d7976a..f703c83 100644
--- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
+++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
@@ -395,10 +395,13 @@
// drag locale's original position to the top.
mDragLocale = (LocaleStore.LocaleInfo) savedInstanceState.getSerializable(
CFGKEY_DRAG_LOCALE);
- mFeedItemList.removeIf(
- localeInfo -> TextUtils.equals(localeInfo.getId(), mDragLocale.getId()));
- mFeedItemList.add(0, mDragLocale);
- notifyItemRangeChanged(0, mFeedItemList.size());
+ if (mDragLocale != null) {
+ mFeedItemList.removeIf(
+ localeInfo -> TextUtils.equals(localeInfo.getId(),
+ mDragLocale.getId()));
+ mFeedItemList.add(0, mDragLocale);
+ notifyItemRangeChanged(0, mFeedItemList.size());
+ }
}
}
}
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index ee5e672..824a8fd 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -456,7 +456,13 @@
// to remove.
mRemoveMode = false;
mShowingRemoveDialog = false;
+ LocaleStore.LocaleInfo firstLocale =
+ mAdapter.getFeedItemList().get(0);
mAdapter.removeChecked();
+ boolean isFirstRemoved =
+ firstLocale != mAdapter.getFeedItemList().get(0);
+ showConfirmDialog(isFirstRemoved, isFirstRemoved ? firstLocale
+ : mAdapter.getFeedItemList().get(0));
setRemoveMode(false);
}
})
@@ -522,22 +528,27 @@
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP
|| event.getAction() == MotionEvent.ACTION_CANCEL) {
- LocaleStore.LocaleInfo localeInfo = mAdapter.getFeedItemList().get(0);
- if (!localeInfo.getLocale().equals(LocalePicker.getLocales().get(0))) {
- final LocaleDialogFragment localeDialogFragment =
- LocaleDialogFragment.newInstance();
- Bundle args = new Bundle();
- args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
- args.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE, localeInfo);
- localeDialogFragment.setArguments(args);
- localeDialogFragment.show(mFragmentManager, TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT);
- } else {
- mAdapter.doTheUpdate();
- }
+ showConfirmDialog(false, mAdapter.getFeedItemList().get(0));
}
return false;
}
+ private void showConfirmDialog(boolean isFirstRemoved, LocaleStore.LocaleInfo localeInfo) {
+ Locale currentSystemLocale = LocalePicker.getLocales().get(0);
+ if (!localeInfo.getLocale().equals(currentSystemLocale)) {
+ final LocaleDialogFragment localeDialogFragment =
+ LocaleDialogFragment.newInstance();
+ Bundle args = new Bundle();
+ args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
+ args.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE,
+ isFirstRemoved ? LocaleStore.getLocaleInfo(currentSystemLocale) : localeInfo);
+ localeDialogFragment.setArguments(args);
+ localeDialogFragment.show(mFragmentManager, TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT);
+ } else {
+ mAdapter.doTheUpdate();
+ }
+ }
+
// Hide the "Remove" menu if there is only one locale in the list, show it otherwise
// This is called when the menu is first created, and then one add / remove locale
private void updateVisibilityOfRemoveMenu() {
diff --git a/src/com/android/settings/overlay/FeatureFactory.kt b/src/com/android/settings/overlay/FeatureFactory.kt
index 9b1f0f4..f38b5da 100644
--- a/src/com/android/settings/overlay/FeatureFactory.kt
+++ b/src/com/android/settings/overlay/FeatureFactory.kt
@@ -23,6 +23,7 @@
import com.android.settings.biometrics.face.FaceFeatureProvider
import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider
import com.android.settings.bluetooth.BluetoothFeatureProvider
+import com.android.settings.connecteddevice.stylus.StylusFeatureProvider
import com.android.settings.dashboard.DashboardFeatureProvider
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider
import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider
@@ -145,6 +146,11 @@
*/
abstract val keyboardSettingsFeatureProvider: KeyboardSettingsFeatureProvider
+ /**
+ * Retrieves implementation for stylus feature.
+ */
+ abstract val stylusFeatureProvider: StylusFeatureProvider
+
companion object {
private var _factory: FeatureFactory? = null
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.kt b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
index f627c4f..67671fb 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.kt
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
@@ -33,6 +33,8 @@
import com.android.settings.bluetooth.BluetoothFeatureProvider
import com.android.settings.bluetooth.BluetoothFeatureProviderImpl
import com.android.settings.connecteddevice.dock.DockUpdaterFeatureProviderImpl
+import com.android.settings.connecteddevice.stylus.StylusFeatureProvider
+import com.android.settings.connecteddevice.stylus.StylusFeatureProviderImpl
import com.android.settings.core.instrumentation.SettingsMetricsFeatureProvider
import com.android.settings.dashboard.DashboardFeatureProviderImpl
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider
@@ -173,4 +175,8 @@
override val keyboardSettingsFeatureProvider: KeyboardSettingsFeatureProvider by lazy {
KeyboardSettingsFeatureProviderImpl()
}
+
+ override val stylusFeatureProvider: StylusFeatureProvider by lazy {
+ StylusFeatureProviderImpl()
+ }
}
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index 4c4795c..0bf1255 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -33,6 +33,7 @@
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_IS_CALLING_APP_ADMIN;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW;
import android.app.Activity;
import android.app.Dialog;
@@ -795,6 +796,9 @@
if (getIntent().getBooleanExtra(EXTRA_SHOW_OPTIONS_BUTTON, false)) {
intent.putExtra(EXTRA_SHOW_OPTIONS_BUTTON, chooseLockSkipped);
}
+ if (getIntent().getBooleanExtra(EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false)) {
+ intent.putExtra(EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, true);
+ }
intent.putExtra(EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS, getIntent().getExtras());
// If the caller requested Gatekeeper Password Handle to be returned, we assume it
// came from biometric enrollment. onActivityResult will put the LockSettingsService
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index e591a21..6066efb 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -65,7 +65,6 @@
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
-import android.util.Pair;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -89,7 +88,6 @@
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.PasswordValidationError;
import com.android.internal.widget.TextViewInputDisabler;
-import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SetupWizardUtils;
@@ -236,6 +234,7 @@
private LockscreenCredential mCurrentCredential;
private LockscreenCredential mChosenPassword;
private boolean mRequestGatekeeperPassword;
+ private boolean mRequestWriteRepairModePassword;
private ImeAwareEditText mPasswordEntry;
private TextViewInputDisabler mPasswordEntryInputDisabler;
@@ -565,6 +564,8 @@
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
mRequestGatekeeperPassword = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, false);
+ mRequestWriteRepairModePassword = intent.getBooleanExtra(
+ ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false);
if (savedInstanceState == null) {
updateStage(Stage.Introduction);
if (confirmCredentials) {
@@ -574,6 +575,7 @@
.setTitle(getString(R.string.unlock_set_unlock_launch_picker_title))
.setReturnCredentials(true)
.setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword)
+ .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword)
.setUserId(mUserId)
.show();
}
@@ -1034,7 +1036,10 @@
setNextEnabled(false);
mSaveAndFinishWorker = new SaveAndFinishWorker();
- mSaveAndFinishWorker.setListener(this);
+ mSaveAndFinishWorker
+ .setListener(this)
+ .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword)
+ .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword);
getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
FRAGMENT_TAG_SAVE_AND_FINISH).commit();
@@ -1054,7 +1059,7 @@
(mAutoPinConfirmOption != null && mAutoPinConfirmOption.isChecked()),
mUserId);
- mSaveAndFinishWorker.start(mLockPatternUtils, mRequestGatekeeperPassword,
+ mSaveAndFinishWorker.start(mLockPatternUtils,
mChosenPassword, mCurrentCredential, mUserId);
}
@@ -1107,50 +1112,4 @@
}
}
}
-
- public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
-
- private LockscreenCredential mChosenPassword;
- private LockscreenCredential mCurrentCredential;
-
- public void start(LockPatternUtils utils, boolean requestGatekeeperPassword,
- LockscreenCredential chosenPassword, LockscreenCredential currentCredential,
- int userId) {
- prepare(utils, requestGatekeeperPassword, userId);
-
- mChosenPassword = chosenPassword;
- mCurrentCredential = currentCredential != null ? currentCredential
- : LockscreenCredential.createNone();
- mUserId = userId;
-
- start();
- }
-
- @Override
- protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
- final boolean success = mUtils.setLockCredential(
- mChosenPassword, mCurrentCredential, mUserId);
- if (success) {
- unifyProfileCredentialIfRequested();
- }
- Intent result = null;
- if (success && mRequestGatekeeperPassword) {
- // If a Gatekeeper Password was requested, invoke the LockSettingsService code
- // path to return a Gatekeeper Password based on the credential that the user
- // chose. This should only be run if the credential was successfully set.
- final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenPassword,
- mUserId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE);
-
- if (!response.isMatched() || !response.containsGatekeeperPasswordHandle()) {
- Log.e(TAG, "critical: bad response or missing GK PW handle for known good"
- + " password: " + response.toString());
- }
-
- result = new Intent();
- result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
- response.getGatekeeperPasswordHandle());
- }
- return Pair.create(success, result);
- }
- }
}
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index a5d04cc..7569c15 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -34,7 +34,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
-import android.util.Pair;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -53,7 +52,6 @@
import com.android.internal.widget.LockPatternView.Cell;
import com.android.internal.widget.LockPatternView.DisplayMode;
import com.android.internal.widget.LockscreenCredential;
-import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SetupWizardUtils;
@@ -206,6 +204,7 @@
private LockscreenCredential mCurrentCredential;
private boolean mRequestGatekeeperPassword;
+ private boolean mRequestWriteRepairModePassword;
protected TextView mHeaderText;
protected LockPatternView mLockPatternView;
protected TextView mFooterText;
@@ -563,6 +562,8 @@
intent.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
mRequestGatekeeperPassword = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, false);
+ mRequestWriteRepairModePassword = intent.getBooleanExtra(
+ ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false);
if (savedInstanceState == null) {
if (confirmCredentials) {
@@ -576,6 +577,7 @@
.setTitle(getString(R.string.unlock_set_unlock_launch_picker_title))
.setReturnCredentials(true)
.setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword)
+ .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword)
.setUserId(mUserId)
.show();
@@ -827,7 +829,10 @@
setRightButtonEnabled(false);
mSaveAndFinishWorker = new SaveAndFinishWorker();
- mSaveAndFinishWorker.setListener(this);
+ mSaveAndFinishWorker
+ .setListener(this)
+ .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword)
+ .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword);
getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
FRAGMENT_TAG_SAVE_AND_FINISH).commit();
@@ -843,7 +848,7 @@
profileCredential);
}
}
- mSaveAndFinishWorker.start(mLockPatternUtils, mRequestGatekeeperPassword,
+ mSaveAndFinishWorker.start(mLockPatternUtils,
mChosenPattern, mCurrentCredential, mUserId);
}
@@ -867,51 +872,4 @@
getActivity().finish();
}
}
-
- public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
-
- private LockscreenCredential mChosenPattern;
- private LockscreenCredential mCurrentCredential;
-
- public void start(LockPatternUtils utils, boolean requestGatekeeperPassword,
- LockscreenCredential chosenPattern, LockscreenCredential currentCredential,
- int userId) {
- prepare(utils, requestGatekeeperPassword, userId);
-
- mCurrentCredential = currentCredential != null ? currentCredential
- : LockscreenCredential.createNone();
- mChosenPattern = chosenPattern;
- mUserId = userId;
-
- start();
- }
-
- @Override
- protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
- final int userId = mUserId;
- final boolean success = mUtils.setLockCredential(mChosenPattern, mCurrentCredential,
- userId);
- if (success) {
- unifyProfileCredentialIfRequested();
- }
- Intent result = null;
- if (success && mRequestGatekeeperPassword) {
- // If a Gatekeeper Password was requested, invoke the LockSettingsService code
- // path to return a Gatekeeper Password based on the credential that the user
- // chose. This should only be run if the credential was successfully set.
- final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenPattern,
- userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE);
-
- if (!response.isMatched() || !response.containsGatekeeperPasswordHandle()) {
- Log.e(TAG, "critical: bad response or missing GK PW handle for known good"
- + " pattern: " + response.toString());
- }
-
- result = new Intent();
- result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
- response.getGatekeeperPasswordHandle());
- }
- return Pair.create(success, result);
- }
- }
}
diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java
index 9533314..e5fc550 100644
--- a/src/com/android/settings/password/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java
@@ -73,6 +73,8 @@
public static final String EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle";
public static final String EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW =
"request_write_repair_mode_pw";
+ public static final String EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL =
+ "wrote_repair_mode_credential";
/**
* When EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL and EXTRA_KEY_UNIFICATION_PROFILE_ID are
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index c6022b5..8d0ff14 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -125,7 +125,7 @@
public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
implements OnClickListener, OnEditorActionListener,
- CredentialCheckResultTracker.Listener, SaveChosenLockWorkerBase.Listener,
+ CredentialCheckResultTracker.Listener, SaveAndFinishWorker.Listener,
RemoteLockscreenValidationFragment.Listener {
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
private ImeAwareEditText mPasswordEntry;
@@ -633,15 +633,15 @@
if (mCheckBox.isChecked() && mRemoteLockscreenValidationFragment
.getLockscreenCredential() != null) {
Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
- ChooseLockPassword.SaveAndFinishWorker saveAndFinishWorker =
- new ChooseLockPassword.SaveAndFinishWorker();
+ SaveAndFinishWorker saveAndFinishWorker = new SaveAndFinishWorker();
getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
.commit();
getFragmentManager().executePendingTransactions();
- saveAndFinishWorker.setListener(this);
+ saveAndFinishWorker
+ .setListener(this)
+ .setRequestGatekeeperPasswordHandle(true);
saveAndFinishWorker.start(
mLockPatternUtils,
- /* requestGatekeeperPassword= */ true,
mRemoteLockscreenValidationFragment.getLockscreenCredential(),
/* currentCredential= */ null,
mEffectiveUserId);
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index a2bcb5a..ffd7c64 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -93,7 +93,7 @@
public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment
implements AppearAnimationCreator<Object>, CredentialCheckResultTracker.Listener,
- SaveChosenLockWorkerBase.Listener, RemoteLockscreenValidationFragment.Listener {
+ SaveAndFinishWorker.Listener, RemoteLockscreenValidationFragment.Listener {
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
@@ -630,15 +630,15 @@
if (mCheckBox.isChecked() && mRemoteLockscreenValidationFragment
.getLockscreenCredential() != null) {
Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
- ChooseLockPattern.SaveAndFinishWorker saveAndFinishWorker =
- new ChooseLockPattern.SaveAndFinishWorker();
+ SaveAndFinishWorker saveAndFinishWorker = new SaveAndFinishWorker();
getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
.commit();
getFragmentManager().executePendingTransactions();
- saveAndFinishWorker.setListener(this);
+ saveAndFinishWorker
+ .setListener(this)
+ .setRequestGatekeeperPasswordHandle(true);
saveAndFinishWorker.start(
mLockPatternUtils,
- /* requestGatekeeperPassword= */ true,
mRemoteLockscreenValidationFragment.getLockscreenCredential(),
/* currentCredential= */ null,
mEffectiveUserId);
diff --git a/src/com/android/settings/password/SaveAndFinishWorker.java b/src/com/android/settings/password/SaveAndFinishWorker.java
new file mode 100644
index 0000000..df679e5
--- /dev/null
+++ b/src/com/android/settings/password/SaveAndFinishWorker.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2015 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.password;
+
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Pair;
+import android.widget.Toast;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.Fragment;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.settings.R;
+import com.android.settings.safetycenter.LockScreenSafetySource;
+
+/**
+ * An invisible retained worker fragment to track the AsyncWork that saves (and optionally
+ * verifies if a challenge is given) the chosen lock credential (pattern/pin/password).
+ */
+public class SaveAndFinishWorker extends Fragment {
+ private static final String TAG = "SaveAndFinishWorker";
+
+ private Listener mListener;
+ private boolean mFinished;
+ private Intent mResultData;
+
+ private LockPatternUtils mUtils;
+ private boolean mRequestGatekeeperPassword;
+ private boolean mRequestWriteRepairModePassword;
+ private boolean mWasSecureBefore;
+ private int mUserId;
+ private int mUnificationProfileId = UserHandle.USER_NULL;
+ private LockscreenCredential mUnificationProfileCredential;
+ private LockscreenCredential mChosenCredential;
+ private LockscreenCredential mCurrentCredential;
+
+ private boolean mBlocking;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ }
+
+ public SaveAndFinishWorker setListener(Listener listener) {
+ if (mListener == listener) {
+ return this;
+ }
+
+ mListener = listener;
+ if (mFinished && mListener != null) {
+ mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
+ }
+ return this;
+ }
+
+ @VisibleForTesting
+ void prepare(LockPatternUtils utils, LockscreenCredential chosenCredential,
+ LockscreenCredential currentCredential, int userId) {
+ mUtils = utils;
+ mUserId = userId;
+ // This will be a no-op for non managed profiles.
+ mWasSecureBefore = mUtils.isSecure(mUserId);
+ mFinished = false;
+ mResultData = null;
+
+ mChosenCredential = chosenCredential;
+ mCurrentCredential = currentCredential != null ? currentCredential
+ : LockscreenCredential.createNone();
+ }
+
+ public void start(LockPatternUtils utils, LockscreenCredential chosenCredential,
+ LockscreenCredential currentCredential, int userId) {
+ prepare(utils, chosenCredential, currentCredential, userId);
+ if (mBlocking) {
+ finish(saveAndVerifyInBackground().second);
+ } else {
+ new Task().execute();
+ }
+ }
+
+ /**
+ * Executes the save and verify work in background.
+ * @return pair where the first is a boolean confirming whether the change was successful or not
+ * and second is the Intent which has the challenge token or is null.
+ */
+ @VisibleForTesting
+ Pair<Boolean, Intent> saveAndVerifyInBackground() {
+ final int userId = mUserId;
+ if (!mUtils.setLockCredential(mChosenCredential, mCurrentCredential, userId)) {
+ return Pair.create(false, null);
+ }
+
+ unifyProfileCredentialIfRequested();
+
+ @LockPatternUtils.VerifyFlag int flags = 0;
+ if (mRequestGatekeeperPassword) {
+ // If a Gatekeeper Password was requested, invoke the LockSettingsService code
+ // path to return a Gatekeeper Password based on the credential that the user
+ // chose. This should only be run if the credential was successfully set.
+ flags |= LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
+ }
+ if (mRequestWriteRepairModePassword) {
+ flags |= LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
+ }
+ if (flags == 0) {
+ return Pair.create(true, null);
+ }
+
+ Intent result = new Intent();
+ final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenCredential,
+ userId, flags);
+ if (response.isMatched()) {
+ if (mRequestGatekeeperPassword && response.containsGatekeeperPasswordHandle()) {
+ result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
+ response.getGatekeeperPasswordHandle());
+ } else if (mRequestGatekeeperPassword) {
+ Log.e(TAG, "critical: missing GK PW handle for known good credential: " + response);
+ }
+ } else {
+ Log.e(TAG, "critical: bad response for known good credential: " + response);
+ }
+ if (mRequestWriteRepairModePassword) {
+ // Notify the caller if repair mode credential is saved successfully
+ result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL,
+ response.isMatched());
+ }
+
+ return Pair.create(true, result);
+ }
+
+ private void finish(Intent resultData) {
+ mFinished = true;
+ mResultData = resultData;
+ if (mListener != null) {
+ mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
+ }
+ if (mUnificationProfileCredential != null) {
+ mUnificationProfileCredential.zeroize();
+ }
+ LockScreenSafetySource.onLockScreenChange(getContext());
+ }
+
+ public SaveAndFinishWorker setRequestGatekeeperPasswordHandle(boolean value) {
+ mRequestGatekeeperPassword = value;
+ return this;
+ }
+
+ public SaveAndFinishWorker setRequestWriteRepairModePassword(boolean value) {
+ mRequestWriteRepairModePassword = value;
+ return this;
+ }
+
+ public SaveAndFinishWorker setBlocking(boolean blocking) {
+ mBlocking = blocking;
+ return this;
+ }
+
+ public SaveAndFinishWorker setProfileToUnify(
+ int profileId, LockscreenCredential credential) {
+ mUnificationProfileId = profileId;
+ mUnificationProfileCredential = credential.duplicate();
+ return this;
+ }
+
+ private void unifyProfileCredentialIfRequested() {
+ if (mUnificationProfileId != UserHandle.USER_NULL) {
+ mUtils.setSeparateProfileChallengeEnabled(mUnificationProfileId, false,
+ mUnificationProfileCredential);
+ }
+ }
+
+ private class Task extends AsyncTask<Void, Void, Pair<Boolean, Intent>> {
+
+ @Override
+ protected Pair<Boolean, Intent> doInBackground(Void... params){
+ return saveAndVerifyInBackground();
+ }
+
+ @Override
+ protected void onPostExecute(Pair<Boolean, Intent> resultData) {
+ if (!resultData.first) {
+ Toast.makeText(getContext(), R.string.lockpassword_credential_changed,
+ Toast.LENGTH_LONG).show();
+ }
+ finish(resultData.second);
+ }
+ }
+
+ interface Listener {
+ void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData);
+ }
+}
diff --git a/src/com/android/settings/password/SaveChosenLockWorkerBase.java b/src/com/android/settings/password/SaveChosenLockWorkerBase.java
deleted file mode 100644
index 4864941..0000000
--- a/src/com/android/settings/password/SaveChosenLockWorkerBase.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2015 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.password;
-
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.Pair;
-import android.widget.Toast;
-
-import androidx.fragment.app.Fragment;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockscreenCredential;
-import com.android.settings.R;
-import com.android.settings.safetycenter.LockScreenSafetySource;
-
-/**
- * An invisible retained worker fragment to track the AsyncWork that saves (and optionally
- * verifies if a challenge is given) the chosen lock credential (pattern/pin/password).
- */
-abstract class SaveChosenLockWorkerBase extends Fragment {
-
- private Listener mListener;
- private boolean mFinished;
- private Intent mResultData;
-
- protected LockPatternUtils mUtils;
- protected boolean mRequestGatekeeperPassword;
- protected boolean mWasSecureBefore;
- protected int mUserId;
- protected int mUnificationProfileId = UserHandle.USER_NULL;
- protected LockscreenCredential mUnificationProfileCredential;
-
- private boolean mBlocking;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setRetainInstance(true);
- }
-
- public void setListener(Listener listener) {
- if (mListener == listener) {
- return;
- }
-
- mListener = listener;
- if (mFinished && mListener != null) {
- mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
- }
- }
-
- protected void prepare(LockPatternUtils utils, boolean requestGatekeeperPassword, int userId) {
- mUtils = utils;
- mUserId = userId;
- mRequestGatekeeperPassword = requestGatekeeperPassword;
- // This will be a no-op for non managed profiles.
- mWasSecureBefore = mUtils.isSecure(mUserId);
- mFinished = false;
- mResultData = null;
- }
-
- protected void start() {
- if (mBlocking) {
- finish(saveAndVerifyInBackground().second);
- } else {
- new Task().execute();
- }
- }
-
- /**
- * Executes the save and verify work in background.
- * @return pair where the first is a boolean confirming whether the change was successful or not
- * and second is the Intent which has the challenge token or is null.
- */
- protected abstract Pair<Boolean, Intent> saveAndVerifyInBackground();
-
- protected void finish(Intent resultData) {
- mFinished = true;
- mResultData = resultData;
- if (mListener != null) {
- mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
- }
- if (mUnificationProfileCredential != null) {
- mUnificationProfileCredential.zeroize();
- }
- LockScreenSafetySource.onLockScreenChange(getContext());
- }
-
- public void setBlocking(boolean blocking) {
- mBlocking = blocking;
- }
-
- public void setProfileToUnify(int profileId, LockscreenCredential credential) {
- mUnificationProfileId = profileId;
- mUnificationProfileCredential = credential.duplicate();
- }
-
- protected void unifyProfileCredentialIfRequested() {
- if (mUnificationProfileId != UserHandle.USER_NULL) {
- mUtils.setSeparateProfileChallengeEnabled(mUnificationProfileId, false,
- mUnificationProfileCredential);
- }
- }
-
- private class Task extends AsyncTask<Void, Void, Pair<Boolean, Intent>> {
-
- @Override
- protected Pair<Boolean, Intent> doInBackground(Void... params){
- return saveAndVerifyInBackground();
- }
-
- @Override
- protected void onPostExecute(Pair<Boolean, Intent> resultData) {
- if (!resultData.first) {
- Toast.makeText(getContext(), R.string.lockpassword_credential_changed,
- Toast.LENGTH_LONG).show();
- }
- finish(resultData.second);
- }
- }
-
- interface Listener {
- void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData);
- }
-}
diff --git a/src/com/android/settings/password/SetupChooseLockPattern.java b/src/com/android/settings/password/SetupChooseLockPattern.java
index 2f48fa1..560906d 100644
--- a/src/com/android/settings/password/SetupChooseLockPattern.java
+++ b/src/com/android/settings/password/SetupChooseLockPattern.java
@@ -94,12 +94,6 @@
}
// Show the skip button during SUW but not during Settings > Biometric Enrollment
mSkipOrClearButton.setOnClickListener(this::onSkipOrClearButtonClick);
-
- final View headerView = view.findViewById(R.id.sud_layout_header);
- final ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) headerView.getLayoutParams();
- lp.bottomMargin = 0;
- view.setLayoutParams(lp);
return view;
}
diff --git a/src/com/android/settings/spa/notification/AppNotificationsListModel.kt b/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
index 028b2f4..0b9b676 100644
--- a/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
+++ b/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
@@ -35,7 +35,7 @@
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
-import com.android.settingslib.spaprivileged.template.app.AppListSwitchItem
+import com.android.settingslib.spaprivileged.template.app.AppListTwoTargetSwitchItem
import com.android.settingslib.utils.StringUtil
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -117,7 +117,7 @@
@Composable
override fun AppListItemModel<AppNotificationsRecord>.AppItem() {
- AppListSwitchItem(
+ AppListTwoTargetSwitchItem(
onClick = { navigateToAppNotificationSettings(app = record.app) },
checked = record.controller.isEnabled.observeAsState(),
changeable = produceState(initialValue = false) {
diff --git a/tests/robotests/assets/exempt_not_implementing_instrumentable b/tests/robotests/assets/exempt_not_implementing_instrumentable
index 04ef0ef..28e1e73 100644
--- a/tests/robotests/assets/exempt_not_implementing_instrumentable
+++ b/tests/robotests/assets/exempt_not_implementing_instrumentable
@@ -1,8 +1,7 @@
com.android.settings.deletionhelper.ActivationWarningFragment
com.android.settings.applications.appops.AppOpsCategory
com.android.settings.CustomListPreference$CustomListPreferenceDialogFragment
-com.android.settings.password.ChooseLockPassword$SaveAndFinishWorker
-com.android.settings.password.ChooseLockPattern$SaveAndFinishWorker
+com.android.settings.password.SaveAndFinishWorker
com.android.settings.RestrictedListPreference$RestrictedListPreferenceDialogFragment
com.android.settings.password.ConfirmDeviceCredentialBaseFragment$LastTryDialog
com.android.settings.password.CredentialCheckResultTracker
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusUsbFirmwareControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusUsbFirmwareControllerTest.java
new file mode 100644
index 0000000..5922016
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusUsbFirmwareControllerTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.stylus;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.Collections;
+import java.util.HashMap;
+
+@RunWith(RobolectricTestRunner.class)
+public class StylusUsbFirmwareControllerTest {
+
+ private Context mContext;
+ private FakeFeatureFactory mFeatureFactory;
+ private Lifecycle mLifecycle;
+ private PreferenceScreen mScreen;
+
+ private StylusUsbFirmwareController mController;
+ @Mock
+ private StylusUsiDetailsFragment mFragment;
+ @Mock
+ private UsbManager mUsbManager;
+ private PreferenceCategory mPreferenceCategory;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ mLifecycle = new Lifecycle(() -> mLifecycle);
+
+ when(mFragment.getContext()).thenReturn(mContext);
+
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
+ mController = new StylusUsbFirmwareController(mContext, "stylus_usb_firmware");
+
+ PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ mScreen = preferenceManager.createPreferenceScreen(mContext);
+
+ mPreferenceCategory = new PreferenceCategory(mContext);
+ mPreferenceCategory.setKey(mController.getPreferenceKey());
+ }
+
+ @Test
+ public void displayPreference_featurePresentUsbStylusAttached_preferenceAdded() {
+ attachUsbDevice();
+ enableFullStylusFeature();
+
+ mController.displayPreference(mScreen);
+
+ assertNotNull(mScreen.findPreference("stylus_usb_firmware"));
+ }
+
+ @Test
+ public void displayPreference_featureAbsentUsbStylusAttached_preferenceNotAdded() {
+ attachUsbDevice();
+ mController.mUsbConnectionListener.onUsbStylusConnectionChanged(
+ mock(UsbDevice.class), true);
+
+ mController.displayPreference(mScreen);
+
+ assertNull(mScreen.findPreference(mController.getPreferenceKey()));
+ }
+
+ @Test
+ public void onUsbStylusConnectionChanged_featurePresentUsbStylusAttached_preferenceAdded() {
+ mController.displayPreference(mScreen);
+
+ attachUsbDevice();
+ enableFullStylusFeature();
+ mController.mUsbConnectionListener.onUsbStylusConnectionChanged(
+ mock(UsbDevice.class), true);
+
+ assertNotNull(mScreen.findPreference(mController.getPreferenceKey()));
+ }
+
+ @Test
+ public void onUsbStylusConnectionChanged_featureAbsentUsbStylusAttached_preferenceRemoved() {
+ mController.displayPreference(mScreen);
+
+ attachUsbDevice();
+ mController.mUsbConnectionListener.onUsbStylusConnectionChanged(
+ mock(UsbDevice.class), true);
+
+ assertNull(mScreen.findPreference(mController.getPreferenceKey()));
+ }
+
+ @Test
+ public void hasUsbStylusFirmwareUpdateFeature_featurePresent_true() {
+ when(mFeatureFactory.getStylusFeatureProvider()
+ .isUsbFirmwareUpdateEnabled(any())).thenReturn(true);
+ attachUsbDevice();
+
+ assertTrue(StylusUsbFirmwareController
+ .hasUsbStylusFirmwareUpdateFeature(mock(UsbDevice.class)));
+ }
+
+ @Test
+ public void hasUsbStylusFirmwareUpdateFeature_featureNotPresent_false() {
+ when(mFeatureFactory.getStylusFeatureProvider()
+ .isUsbFirmwareUpdateEnabled(any())).thenReturn(false);
+ attachUsbDevice();
+
+ assertFalse(StylusUsbFirmwareController
+ .hasUsbStylusFirmwareUpdateFeature(mock(UsbDevice.class)));
+ }
+
+ private void attachUsbDevice() {
+ when(mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager);
+ HashMap<String, UsbDevice> deviceList = new HashMap<>();
+ deviceList.put("0", mock(UsbDevice.class));
+ when(mUsbManager.getDeviceList()).thenReturn(deviceList);
+ }
+
+ private void enableFullStylusFeature() {
+ when(mFeatureFactory.getStylusFeatureProvider()
+ .isUsbFirmwareUpdateEnabled(any())).thenReturn(true);
+ when(mFeatureFactory.getStylusFeatureProvider()
+ .getUsbFirmwareUpdatePreferences(any()))
+ .thenReturn(Collections.singletonList(mock(Preference.class)));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/stylus/UsbStylusBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/connecteddevice/stylus/UsbStylusBroadcastReceiverTest.java
new file mode 100644
index 0000000..ccaefb2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/UsbStylusBroadcastReceiverTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.stylus;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class UsbStylusBroadcastReceiverTest {
+ private Context mContext;
+ private UsbStylusBroadcastReceiver mReceiver;
+ private FakeFeatureFactory mFeatureFactory;
+ @Mock
+ private UsbStylusBroadcastReceiver.UsbStylusConnectionListener mListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ mReceiver = new UsbStylusBroadcastReceiver(mContext, mListener);
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
+ }
+
+ @Test
+ public void onReceive_usbDeviceAttachedStylus_invokeCallback() {
+ when(mFeatureFactory.mStylusFeatureProvider.isUsbFirmwareUpdateEnabled(any()))
+ .thenReturn(true);
+ final UsbDevice usbDevice = mock(UsbDevice.class);
+ final Intent intent = new Intent();
+ intent.setAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ intent.putExtra(UsbManager.EXTRA_DEVICE, usbDevice);
+
+ mReceiver.onReceive(mContext, intent);
+
+ verify(mListener).onUsbStylusConnectionChanged(usbDevice, true);
+ }
+
+ @Test
+ public void onReceive_usbDeviceDetachedStylus_invokeCallback() {
+ when(mFeatureFactory.mStylusFeatureProvider.isUsbFirmwareUpdateEnabled(any()))
+ .thenReturn(true);
+ final UsbDevice usbDevice = mock(UsbDevice.class);
+ final Intent intent = new Intent();
+ intent.setAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ intent.putExtra(UsbManager.EXTRA_DEVICE, usbDevice);
+
+ mReceiver.onReceive(mContext, intent);
+
+ verify(mListener).onUsbStylusConnectionChanged(usbDevice, false);
+ }
+
+ @Test
+ public void onReceive_usbDeviceAttachedNotStylus_doesNotInvokeCallback() {
+ when(mFeatureFactory.mStylusFeatureProvider.isUsbFirmwareUpdateEnabled(any()))
+ .thenReturn(false);
+ final UsbDevice usbDevice = mock(UsbDevice.class);
+ final Intent intent = new Intent();
+ intent.setAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ intent.putExtra(UsbManager.EXTRA_DEVICE, usbDevice);
+
+ mReceiver.onReceive(mContext, intent);
+
+ verifyNoMoreInteractions(mListener);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/display/StayAwakeOnFoldPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/StayAwakeOnFoldPreferenceControllerTest.java
new file mode 100644
index 0000000..c994818
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/StayAwakeOnFoldPreferenceControllerTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.display;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class StayAwakeOnFoldPreferenceControllerTest {
+
+ @Mock
+ private Resources mResources;
+ private Context mContext;
+ private StayAwakeOnFoldPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mResources = Mockito.mock(Resources.class);
+ mController = new StayAwakeOnFoldPreferenceController(mContext, "key", mResources);
+ }
+
+ @Test
+ public void getAvailabilityStatus_withConfigNoShow_returnUnsupported() {
+ when(mResources.getBoolean(R.bool.config_stay_awake_on_fold)).thenReturn(false);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_withConfigNoShow_returnAvailable() {
+ when(mResources.getBoolean(R.bool.config_stay_awake_on_fold)).thenReturn(true);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void setChecked_enableStayAwakeOnFold_setChecked() {
+ mController.setChecked(true);
+
+ assertThat(isStayAwakeOnFoldEnabled())
+ .isTrue();
+ }
+
+ @Test
+ public void setChecked_disableStayAwakeOnFold_setUnchecked() {
+ mController.setChecked(false);
+
+ assertThat(isStayAwakeOnFoldEnabled())
+ .isFalse();
+ }
+
+ @Test
+ public void isChecked_enableStayAwakeOnFold_returnTrue() {
+ enableStayAwakeOnFoldPreference();
+
+ assertThat(mController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void isChecked_disableStayAwakeOnFold_returnFalse() {
+ disableStayAwakeOnFoldPreference();
+
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ private void enableStayAwakeOnFoldPreference() {
+ Settings.System.putInt(
+ mContext.getContentResolver(),
+ Settings.System.STAY_AWAKE_ON_FOLD,
+ 1);
+ }
+
+ private void disableStayAwakeOnFoldPreference() {
+ Settings.System.putInt(
+ mContext.getContentResolver(),
+ Settings.System.STAY_AWAKE_ON_FOLD,
+ 0);
+ }
+
+ private boolean isStayAwakeOnFoldEnabled() {
+ return (Settings.System.getInt(
+ mContext.getContentResolver(),
+ Settings.System.STAY_AWAKE_ON_FOLD,
+ 0) == 1);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java b/tests/robotests/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java
new file mode 100644
index 0000000..57f2b01
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.localepicker;
+
+import static com.android.settings.localepicker.LocaleDialogFragment.ARG_DIALOG_TYPE;
+import static com.android.settings.localepicker.LocaleDialogFragment.ARG_TARGET_LOCALE;
+import static com.android.settings.localepicker.LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.os.Bundle;
+import android.window.OnBackInvokedDispatcher;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
+import com.android.internal.app.LocaleStore;
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+import com.android.settings.utils.ActivityControllerWrapper;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.Locale;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowAlertDialogCompat.class})
+public class LocaleDialogFragmentTest {
+
+ @Mock
+ private OnBackInvokedDispatcher mOnBackInvokedDispatcher;
+
+ private FragmentActivity mActivity;
+ private LocaleDialogFragment mDialogFragment;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mActivity = (FragmentActivity) ActivityControllerWrapper.setup(
+ Robolectric.buildActivity(FragmentActivity.class)).get();
+ mDialogFragment = LocaleDialogFragment.newInstance();
+ LocaleStore.LocaleInfo localeInfo = LocaleStore.getLocaleInfo(Locale.ENGLISH);
+ Bundle args = new Bundle();
+ args.putInt(ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
+ args.putSerializable(ARG_TARGET_LOCALE, localeInfo);
+ mDialogFragment.setArguments(args);
+ FragmentManager fragmentManager = mActivity.getSupportFragmentManager();
+ FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+ fragmentTransaction.add(mDialogFragment, null);
+ fragmentTransaction.commit();
+ }
+
+ @Test
+ public void onCreateDialog_onBackInvokedCallbackIsRegistered() {
+ mDialogFragment.setBackDispatcher(mOnBackInvokedDispatcher);
+ mDialogFragment.onCreateDialog(null);
+
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), any());
+ }
+
+ @Test
+ public void onBackInvoked_dialogIsStillDisplaying() {
+ mDialogFragment.setBackDispatcher(mOnBackInvokedDispatcher);
+ AlertDialog alertDialog = (AlertDialog) mDialogFragment.onCreateDialog(null);
+ alertDialog.show();
+ assertThat(alertDialog).isNotNull();
+ assertThat(alertDialog.isShowing()).isTrue();
+
+ mOnBackInvokedDispatcher.registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), any());
+
+ mDialogFragment.getBackInvokedCallback().onBackInvoked();
+
+ assertThat(alertDialog.isShowing()).isTrue();
+
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java b/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
index 147b44b..d349865 100644
--- a/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
+++ b/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
@@ -67,6 +67,7 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import java.util.ArrayList;
import java.util.List;
@@ -105,6 +106,8 @@
private View mView;
@Mock
private IActivityManager mActivityService;
+ @Mock
+ private MetricsFeatureProvider mMetricsFeatureProvider;
@Before
public void setUp() throws Exception {
@@ -123,6 +126,8 @@
ReflectionHelpers.setField(mLocaleListEditor, "mAdapter", mAdapter);
ReflectionHelpers.setField(mLocaleListEditor, "mAddLanguage", mAddLanguage);
ReflectionHelpers.setField(mLocaleListEditor, "mFragmentManager", mFragmentManager);
+ ReflectionHelpers.setField(mLocaleListEditor, "mMetricsFeatureProvider",
+ mMetricsFeatureProvider);
when(mFragmentManager.beginTransaction()).thenReturn(mFragmentTransaction);
FakeFeatureFactory.setupForTest();
}
@@ -217,6 +222,38 @@
}
@Test
+ public void showConfirmDialog_systemLocaleSelected_shouldShowLocaleChangeDialog()
+ throws Exception {
+ //pre-condition
+ setUpLocaleConditions();
+ final Configuration config = new Configuration();
+ config.setLocales((LocaleList.forLanguageTags("zh-TW,en-US")));
+ when(mActivityService.getConfiguration()).thenReturn(config);
+ when(mAdapter.getFeedItemList()).thenReturn(mLocaleList);
+ when(mAdapter.getCheckedCount()).thenReturn(1);
+ when(mAdapter.getItemCount()).thenReturn(2);
+ when(mAdapter.isFirstLocaleChecked()).thenReturn(true);
+ ReflectionHelpers.setField(mLocaleListEditor, "mRemoveMode", true);
+ ReflectionHelpers.setField(mLocaleListEditor, "mShowingRemoveDialog", true);
+
+ //launch the first dialog
+ mLocaleListEditor.showRemoveLocaleWarningDialog();
+
+ final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+
+ assertThat(dialog).isNotNull();
+
+ // click the remove button
+ dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
+
+ assertThat(dialog.isShowing()).isFalse();
+
+ // check the second dialog is showing
+ verify(mFragmentTransaction).add(any(LocaleDialogFragment.class),
+ eq(TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT));
+ }
+
+ @Test
public void mayAppendUnicodeTags_appendUnicodeTags_success() {
LocaleStore.LocaleInfo localeInfo = LocaleStore.fromLocale(Locale.forLanguageTag("en-US"));
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
index 7cfd6b1..12e4b8d 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
@@ -65,7 +65,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
@@ -413,26 +412,8 @@
"Must be at least 6 characters");
}
- @Ignore
@Test
- public void processAndValidatePasswordRequirements_autoPinDisabled_defaultPinMinimumLength() {
- DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
- /* value= */ "false", /* makeDefault= */ false);
- PasswordPolicy policy = new PasswordPolicy();
- policy.quality = PASSWORD_QUALITY_UNSPECIFIED;
-
- assertPasswordValidationResult(
- /* minMetrics */ policy.getMinMetrics(),
- /* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
- /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
- /* userEnteredPassword= */ LockscreenCredential.createPassword("11"),
- "PIN must be at least 4 digits");
- }
-
- @Test
- public void processAndValidatePasswordRequirements_autoPinEnabled_defaultPinMinimumLength() {
- DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
- /* value= */ "true", /* makeDefault= */ false);
+ public void processAndValidatePasswordRequirements_defaultPinMinimumLength() {
PasswordPolicy policy = new PasswordPolicy();
policy.quality = PASSWORD_QUALITY_UNSPECIFIED;
@@ -543,39 +524,6 @@
assertThat(pinAutoConfirmOption.isChecked()).isFalse();
}
- @Ignore
- @Test
- public void autoPinConfirmOption_featureDisabled_shouldRemainInvisibleAndUnchecked() {
- DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
- /* value= */ "false", /* makeDefault= */ false);
- ChooseLockPassword passwordActivity = setupActivityWithPinTypeAndDefaultPolicy();
-
- ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
- ScrollToParentEditText passwordEntry = passwordActivity.findViewById(R.id.password_entry);
- CheckBox pinAutoConfirmOption = passwordActivity
- .findViewById(R.id.auto_pin_confirm_enabler);
- TextView securityMessage =
- passwordActivity.findViewById(R.id.auto_pin_confirm_security_message);
-
- passwordEntry.setText("1234");
- fragment.updateUi();
- assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.GONE);
- assertThat(securityMessage.getVisibility()).isEqualTo(View.GONE);
- assertThat(pinAutoConfirmOption.isChecked()).isFalse();
-
- passwordEntry.setText("123456");
- fragment.updateUi();
- assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.GONE);
- assertThat(securityMessage.getVisibility()).isEqualTo(View.GONE);
- assertThat(pinAutoConfirmOption.isChecked()).isFalse();
-
- passwordEntry.setText("12345678");
- fragment.updateUi();
- assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.GONE);
- assertThat(securityMessage.getVisibility()).isEqualTo(View.GONE);
- assertThat(pinAutoConfirmOption.isChecked()).isFalse();
- }
-
private ChooseLockPassword setupActivityWithPinTypeAndDefaultPolicy() {
PasswordPolicy policy = new PasswordPolicy();
policy.quality = PASSWORD_QUALITY_UNSPECIFIED;
diff --git a/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java b/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java
index 2f46986..c5e0813 100644
--- a/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java
+++ b/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java
@@ -28,7 +28,6 @@
import android.os.UserHandle;
import android.util.TypedValue;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
@@ -115,14 +114,6 @@
assertThat(button.getVisibility()).isEqualTo(View.VISIBLE);
}
- @Test
- public void headerView_noBottomMargin() {
- final View header = mActivity.findViewById(R.id.sud_layout_header);
- final ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) header.getLayoutParams();
- assertThat(lp.bottomMargin).isEqualTo(0);
- }
-
private void verifyScreenLockOptionsShown() {
final Button button = mActivity.findViewById(R.id.screen_lock_options);
assertThat(button).isNotNull();
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index 0f4c255..90985dd 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -26,6 +26,7 @@
import com.android.settings.biometrics.face.FaceFeatureProvider;
import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider;
import com.android.settings.bluetooth.BluetoothFeatureProvider;
+import com.android.settings.connecteddevice.stylus.StylusFeatureProvider;
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider;
@@ -91,6 +92,7 @@
public AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider;
public WifiFeatureProvider mWifiFeatureProvider;
public KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider;
+ public StylusFeatureProvider mStylusFeatureProvider;
/**
* Call this in {@code @Before} method of the test class to use fake factory.
@@ -134,6 +136,7 @@
mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class);
mWifiFeatureProvider = mock(WifiFeatureProvider.class);
mKeyboardSettingsFeatureProvider = mock(KeyboardSettingsFeatureProvider.class);
+ mStylusFeatureProvider = mock(StylusFeatureProvider.class);
}
@Override
@@ -288,4 +291,9 @@
public KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider() {
return mKeyboardSettingsFeatureProvider;
}
+
+ @Override
+ public StylusFeatureProvider getStylusFeatureProvider() {
+ return mStylusFeatureProvider;
+ }
}
diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
index f732bce..54934a7 100644
--- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
+++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
@@ -24,6 +24,7 @@
import com.android.settings.biometrics.face.FaceFeatureProvider
import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider
import com.android.settings.bluetooth.BluetoothFeatureProvider
+import com.android.settings.connecteddevice.stylus.StylusFeatureProvider
import com.android.settings.dashboard.DashboardFeatureProvider
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider
import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider
@@ -139,4 +140,6 @@
get() = TODO("Not yet implemented")
override val keyboardSettingsFeatureProvider: KeyboardSettingsFeatureProvider
get() = TODO("Not yet implemented")
+ override val stylusFeatureProvider: StylusFeatureProvider
+ get() = TODO("Not yet implemented")
}
diff --git a/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java b/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java
index 66514ac..9190d0a 100644
--- a/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java
+++ b/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java
@@ -39,7 +39,6 @@
import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -100,7 +99,9 @@
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
callbackCaptor.capture(),
- same(DISABLED_FEATURES));
+ same(DISABLED_FEATURES),
+ same(null),
+ eq(false));
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
@@ -126,12 +127,14 @@
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
callbackCaptor.capture(),
- same(DISABLED_FEATURES));
+ same(DISABLED_FEATURES),
+ same(null),
+ eq(false));
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 0);
- verify(mSafetyCenterManagerWrapper).isEnabled(mContext);
+ verify(mSafetyCenterManagerWrapper, atLeast(1)).isEnabled(mContext);
}
@Test
@@ -145,7 +148,9 @@
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
callbackCaptor.capture(),
- same(DISABLED_FEATURES));
+ same(DISABLED_FEATURES),
+ same(null),
+ eq(false));
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 1);
@@ -153,7 +158,6 @@
verify(mSafetyCenterManagerWrapper, never()).isEnabled(any());
}
- @Ignore("b/282413778")
@Test
public void enroll_secondVersion_onEnrollmentCallbacks_triggerGivenCallback() {
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
@@ -182,7 +186,6 @@
.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE);
}
- @Ignore("b/282413778")
@Test
public void enroll_secondVersion_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() {
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
@@ -204,7 +207,6 @@
verify(mSafetyCenterManagerWrapper).isEnabled(mContext);
}
- @Ignore("b/282413778")
@Test
public void enroll_secondVersion_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() {
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
diff --git a/tests/unit/src/com/android/settings/password/SaveAndFinishWorkerTest.java b/tests/unit/src/com/android/settings/password/SaveAndFinishWorkerTest.java
new file mode 100644
index 0000000..88e3150
--- /dev/null
+++ b/tests/unit/src/com/android/settings/password/SaveAndFinishWorkerTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.password;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.VerifyCredentialResponse;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SaveAndFinishWorkerTest {
+ @Test
+ public void testSetRequestWriteRepairModePassword_setLockCredentialFail() {
+ int userId = 0;
+ int flags = LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
+ var chosenCredential = LockscreenCredential.createPassword("1234");
+ var currentCredential = LockscreenCredential.createNone();
+ var worker = new SaveAndFinishWorker();
+ var lpu = mock(LockPatternUtils.class);
+
+ when(lpu.setLockCredential(chosenCredential, currentCredential, userId)).thenReturn(false);
+
+ worker.setRequestWriteRepairModePassword(true);
+ worker.prepare(lpu, chosenCredential, currentCredential, userId);
+ var result = worker.saveAndVerifyInBackground();
+
+ verify(lpu).setLockCredential(chosenCredential, currentCredential, userId);
+ verify(lpu, never()).verifyCredential(chosenCredential, userId, flags);
+ assertThat(result.first).isFalse();
+ }
+
+ @Test
+ public void testSetRequestWriteRepairModePassword_verifyCredentialFail() {
+ int userId = 0;
+ int flags = LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
+ var chosenCredential = LockscreenCredential.createPassword("1234");
+ var currentCredential = LockscreenCredential.createNone();
+ var worker = new SaveAndFinishWorker();
+ var lpu = mock(LockPatternUtils.class);
+ var response = VerifyCredentialResponse.fromError();
+
+ when(lpu.setLockCredential(chosenCredential, currentCredential, userId)).thenReturn(true);
+ when(lpu.verifyCredential(chosenCredential, userId, flags)).thenReturn(response);
+
+ worker.setRequestWriteRepairModePassword(true);
+ worker.prepare(lpu, chosenCredential, currentCredential, userId);
+ var result = worker.saveAndVerifyInBackground();
+
+ verify(lpu).setLockCredential(chosenCredential, currentCredential, userId);
+ verify(lpu).verifyCredential(chosenCredential, userId, flags);
+ assertThat(result.first).isTrue();
+ assertThat(result.second.getBooleanExtra(
+ ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL, true))
+ .isFalse();
+ }
+
+ @Test
+ public void testSetRequestWriteRepairModePassword_verifyCredentialSucceed() {
+ int userId = 0;
+ int flags = LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
+ var chosenCredential = LockscreenCredential.createPassword("1234");
+ var currentCredential = LockscreenCredential.createNone();
+ var worker = new SaveAndFinishWorker();
+ var lpu = mock(LockPatternUtils.class);
+ var response = new VerifyCredentialResponse.Builder().build();
+
+ when(lpu.setLockCredential(chosenCredential, currentCredential, userId)).thenReturn(true);
+ when(lpu.verifyCredential(chosenCredential, userId, flags)).thenReturn(response);
+
+ worker.setRequestWriteRepairModePassword(true);
+ worker.prepare(lpu, chosenCredential, currentCredential, userId);
+ var result = worker.saveAndVerifyInBackground();
+
+ verify(lpu).setLockCredential(chosenCredential, currentCredential, userId);
+ verify(lpu).verifyCredential(chosenCredential, userId, flags);
+ assertThat(result.first).isTrue();
+ assertThat(result.second.getBooleanExtra(
+ ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL, false))
+ .isTrue();
+ }
+
+ @Test
+ public void testSetRequestWriteRepairModePassword_verifyCredentialSucceed_noGkPwHandle() {
+ int userId = 0;
+ int flags = LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW
+ | LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
+ var chosenCredential = LockscreenCredential.createPassword("1234");
+ var currentCredential = LockscreenCredential.createNone();
+ var worker = new SaveAndFinishWorker();
+ var lpu = mock(LockPatternUtils.class);
+ var response = new VerifyCredentialResponse.Builder().build();
+
+ when(lpu.setLockCredential(chosenCredential, currentCredential, userId)).thenReturn(true);
+ when(lpu.verifyCredential(chosenCredential, userId, flags)).thenReturn(response);
+
+ worker.setRequestWriteRepairModePassword(true);
+ worker.setRequestGatekeeperPasswordHandle(true);
+ worker.prepare(lpu, chosenCredential, currentCredential, userId);
+ var result = worker.saveAndVerifyInBackground();
+
+ verify(lpu).setLockCredential(chosenCredential, currentCredential, userId);
+ verify(lpu).verifyCredential(chosenCredential, userId, flags);
+ assertThat(result.first).isTrue();
+ assertThat(result.second.getBooleanExtra(
+ ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL, false))
+ .isTrue();
+ assertThat(result.second.getLongExtra(
+ ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, -1))
+ .isEqualTo(-1);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index e0d3eca..79804e3 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -26,6 +26,7 @@
import com.android.settings.biometrics.face.FaceFeatureProvider;
import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider;
import com.android.settings.bluetooth.BluetoothFeatureProvider;
+import com.android.settings.connecteddevice.stylus.StylusFeatureProvider;
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider;
@@ -90,6 +91,7 @@
public AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider;
public WifiFeatureProvider mWifiFeatureProvider;
public KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider;
+ public StylusFeatureProvider mStylusFeatureProvider;
/**
* Call this in {@code @Before} method of the test class to use fake factory.
@@ -133,6 +135,7 @@
mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class);
mWifiFeatureProvider = mock(WifiFeatureProvider.class);
mKeyboardSettingsFeatureProvider = mock(KeyboardSettingsFeatureProvider.class);
+ mStylusFeatureProvider = mock(StylusFeatureProvider.class);
}
@Override
@@ -287,4 +290,9 @@
public KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider() {
return mKeyboardSettingsFeatureProvider;
}
+
+ @Override
+ public StylusFeatureProvider getStylusFeatureProvider() {
+ return mStylusFeatureProvider;
+ }
}