diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 185d21ec7b2..9c5d6c6a404 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2398,6 +2398,7 @@ + diff --git a/res/layout/udfps_enroll_view.xml b/res/layout/udfps_enroll_view.xml index 6bf339b6b07..bd626093ce4 100644 --- a/res/layout/udfps_enroll_view.xml +++ b/res/layout/udfps_enroll_view.xml @@ -18,7 +18,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/udfps_animation_view" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:visibility="gone"> Also use password to unlock this device + + Verify pattern + + Verify PIN + + Verify password + + Enter your device pattern enrolled in normal mode to continue + + Enter your device PIN enrolled in normal mode to continue + + Enter your device password enrolled in normal mode to continue diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 68b1a48a380..a2195df1b6b 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -708,9 +708,13 @@ public final class Utils extends com.android.settingslib.Utils { final int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId()); if (userId == LockPatternUtils.USER_FRP) { return allowAnyUser ? userId : checkUserOwnsFrpCredential(context, userId); - } else { - return allowAnyUser ? userId : enforceSameOwner(context, userId); } + if (userId == LockPatternUtils.USER_REPAIR_MODE) { + enforceRepairModeActive(context); + // any users can exit repair mode + return userId; + } + return allowAnyUser ? userId : enforceSameOwner(context, userId); } /** @@ -729,6 +733,16 @@ public final class Utils extends com.android.settingslib.Utils { + " does not own frp credential."); } + /** + * Throws {@link SecurityException} if repair mode is not active on the device. + */ + private static void enforceRepairModeActive(Context context) { + if (LockPatternUtils.isRepairModeActive(context)) { + return; + } + throw new SecurityException("Repair mode is not active on the device."); + } + /** * Returns the given user id if it belongs to the current user. * diff --git a/src/com/android/settings/applications/specialaccess/DataSaverController.kt b/src/com/android/settings/applications/specialaccess/DataSaverController.kt index 3a2fdb002b1..baed0aa5ec4 100644 --- a/src/com/android/settings/applications/specialaccess/DataSaverController.kt +++ b/src/com/android/settings/applications/specialaccess/DataSaverController.kt @@ -51,7 +51,7 @@ class DataSaverController(context: Context, key: String) : BasePreferenceControl preference = screen.findPreference(preferenceKey)!! } - fun init(viewLifecycleOwner: LifecycleOwner) { + override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { preference.summary = getUnrestrictedSummary(mContext) diff --git a/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java b/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java index 9f4c8958cf4..2cbc30422fc 100644 --- a/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java +++ b/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java @@ -21,10 +21,6 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGE_DE import android.app.settings.SettingsEnums; import android.os.Bundle; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; @@ -50,12 +46,6 @@ public class SpecialAccessSettings extends DashboardFragment { MANAGE_DEVICE_ADMIN_APPS, R.string.manage_device_admin); } - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - use(DataSaverController.class).init(getViewLifecycleOwner()); - } - @Override protected int getPreferenceScreenResId() { return R.xml.special_access; diff --git a/src/com/android/settings/biometrics/BiometricEnrollSidecar.java b/src/com/android/settings/biometrics/BiometricEnrollSidecar.java index 97d46a420e3..369fa4b4311 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollSidecar.java +++ b/src/com/android/settings/biometrics/BiometricEnrollSidecar.java @@ -48,11 +48,16 @@ public abstract class BiometricEnrollSidecar extends InstrumentedFragment { /** * Called when a pointer down event has occurred. */ - default void onPointerDown(int sensorId) { } + default void onUdfpsPointerDown(int sensorId) { } /** * Called when a pointer up event has occurred. */ - default void onPointerUp(int sensorId) { } + default void onUdfpsPointerUp(int sensorId) { } + + /** + * Called when udfps overlay is shown. + */ + default void onUdfpsOverlayShown() { } } private int mEnrollmentSteps = -1; @@ -126,29 +131,36 @@ public abstract class BiometricEnrollSidecar extends InstrumentedFragment { } } - private class QueuedPointerDown extends QueuedEvent { + private class QueuedUdfpsPointerDown extends QueuedEvent { private final int sensorId; - public QueuedPointerDown(int sensorId) { + QueuedUdfpsPointerDown(int sensorId) { this.sensorId = sensorId; } @Override public void send(Listener listener) { - listener.onPointerDown(sensorId); + listener.onUdfpsPointerDown(sensorId); } } - private class QueuedPointerUp extends QueuedEvent { + private class QueuedUdfpsPointerUp extends QueuedEvent { private final int sensorId; - public QueuedPointerUp(int sensorId) { + QueuedUdfpsPointerUp(int sensorId) { this.sensorId = sensorId; } @Override public void send(Listener listener) { - listener.onPointerUp(sensorId); + listener.onUdfpsPointerUp(sensorId); + } + } + + private class QueuedUdfpsOverlayShown extends QueuedEvent { + @Override + public void send(Listener listener) { + listener.onUdfpsOverlayShown(); } } @@ -249,19 +261,27 @@ public abstract class BiometricEnrollSidecar extends InstrumentedFragment { } } - protected void onPointerDown(int sensorId) { + protected void onUdfpsPointerDown(int sensorId) { if (mListener != null) { - mListener.onPointerDown(sensorId); + mListener.onUdfpsPointerDown(sensorId); } else { - mQueuedEvents.add(new QueuedPointerDown(sensorId)); + mQueuedEvents.add(new QueuedUdfpsPointerDown(sensorId)); } } - protected void onPointerUp(int sensorId) { + protected void onUdfpsPointerUp(int sensorId) { if (mListener != null) { - mListener.onPointerUp(sensorId); + mListener.onUdfpsPointerUp(sensorId); } else { - mQueuedEvents.add(new QueuedPointerUp(sensorId)); + mQueuedEvents.add(new QueuedUdfpsPointerUp(sensorId)); + } + } + + protected void onUdfpsOverlayShown() { + if (mListener != null) { + mListener.onUdfpsOverlayShown(); + } else { + mQueuedEvents.add(new QueuedUdfpsOverlayShown()); } } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java index 400a92ee7a7..dbdb024973a 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java @@ -828,19 +828,26 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { } @Override - public void onPointerDown(int sensorId) { + public void onUdfpsPointerDown(int sensorId) { if (mUdfpsEnrollHelper != null) { mUdfpsEnrollHelper.onPointerDown(sensorId); } } @Override - public void onPointerUp(int sensorId) { + public void onUdfpsPointerUp(int sensorId) { if (mUdfpsEnrollHelper != null) { mUdfpsEnrollHelper.onPointerUp(sensorId); } } + @Override + public void onUdfpsOverlayShown() { + if (mCanAssumeUdfps) { + findViewById(R.id.udfps_animation_view).setVisibility(View.VISIBLE); + } + } + private void updateProgress(boolean animate) { if (mSidecar == null || !mSidecar.isEnrolling()) { Log.d(TAG, "Enrollment not started yet"); diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java index 5d04cd6c5ed..493302bd13c 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java @@ -124,13 +124,18 @@ public class FingerprintEnrollSidecar extends BiometricEnrollSidecar { } @Override - public void onPointerDown(int sensorId) { - FingerprintEnrollSidecar.super.onPointerDown(sensorId); + public void onUdfpsPointerDown(int sensorId) { + FingerprintEnrollSidecar.super.onUdfpsPointerDown(sensorId); } @Override - public void onPointerUp(int sensorId) { - FingerprintEnrollSidecar.super.onPointerUp(sensorId); + public void onUdfpsPointerUp(int sensorId) { + FingerprintEnrollSidecar.super.onUdfpsPointerUp(sensorId); + } + + @Override + public void onUdfpsOverlayShown() { + FingerprintEnrollSidecar.super.onUdfpsOverlayShown(); } }; diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java index 36325a7b975..306b1a3e136 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java @@ -98,13 +98,18 @@ public class FingerprintUpdater { } @Override - public void onPointerDown(int sensorId) { - mCallback.onPointerDown(sensorId); + public void onUdfpsPointerDown(int sensorId) { + mCallback.onUdfpsPointerDown(sensorId); } @Override - public void onPointerUp(int sensorId) { - mCallback.onPointerUp(sensorId); + public void onUdfpsPointerUp(int sensorId) { + mCallback.onUdfpsPointerUp(sensorId); + } + + @Override + public void onUdfpsOverlayShown() { + mCallback.onUdfpsOverlayShown(); } } diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java index 9225c64498f..df2f2f77d3b 100644 --- a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java +++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java @@ -86,7 +86,7 @@ public class UdfpsEnrollEnrollingView extends GlifLayout { UdfpsEnrollHelper udfpsEnrollHelper, AccessibilityManager accessibilityManager) { mAccessibilityManager = accessibilityManager; - initUdfpsEnrollView(mUdfpsEnrollView, udfpsProps, udfpsEnrollHelper); + initUdfpsEnrollView(udfpsProps, udfpsEnrollHelper); if (!mIsLandscape) { adjustPortraitPaddings(); @@ -117,8 +117,7 @@ public class UdfpsEnrollEnrollingView extends GlifLayout { }); } - private void initUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView, - FingerprintSensorPropertiesInternal udfpsProps, + private void initUdfpsEnrollView(FingerprintSensorPropertiesInternal udfpsProps, UdfpsEnrollHelper udfpsEnrollHelper) { DisplayInfo displayInfo = new DisplayInfo(); mContext.getDisplay().getDisplayInfo(displayInfo); @@ -141,8 +140,8 @@ public class UdfpsEnrollEnrollingView extends GlifLayout { scaleFactor, displayInfo.rotation); - udfpsEnrollView.setOverlayParams(params); - udfpsEnrollView.setEnrollHelper(udfpsEnrollHelper); + mUdfpsEnrollView.setOverlayParams(params); + mUdfpsEnrollView.setEnrollHelper(udfpsEnrollHelper); } private void adjustPortraitPaddings() { diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java index d77d9d3f7e8..7074288716e 100644 --- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java +++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java @@ -103,12 +103,12 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel { } @Override - public void onPointerDown(int sensorId) { + public void onUdfpsPointerDown(int sensorId) { mPointerDownLiveData.postValue(sensorId); } @Override - public void onPointerUp(int sensorId) { + public void onUdfpsPointerUp(int sensorId) { mPointerUpLiveData.postValue(sensorId); } }; diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java index 7ee61ee249d..f2bc6fcfde6 100644 --- a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java +++ b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java @@ -128,7 +128,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere if (device != null && mSelectedList.contains(device)) { setResult(RESULT_OK); finish(); - } else if (mDevicePreferenceMap.containsKey(cachedDevice)) { + } else { onDeviceDeleted(cachedDevice); } } @@ -175,8 +175,6 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere public void updateContent(int bluetoothState) { switch (bluetoothState) { case BluetoothAdapter.STATE_ON: - mDevicePreferenceMap.clear(); - clearPreferenceGroupCache(); mBluetoothAdapter.enable(); enableScanning(); break; @@ -187,14 +185,6 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere } } - /** - * Clears all cached preferences in {@code preferenceGroup}. - */ - private void clearPreferenceGroupCache() { - cacheRemoveAllPrefs(mAvailableDevicesCategory); - removeCachedPrefs(mAvailableDevicesCategory); - } - @VisibleForTesting void showBluetoothTurnedOnToast() { Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast, diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java index 5256f3d6596..039080b26ba 100644 --- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java +++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * 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. @@ -35,6 +35,8 @@ import android.view.View; import android.widget.ImageView; import androidx.annotation.IntDef; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; @@ -52,6 +54,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.HashSet; import java.util.Set; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicInteger; /** * BluetoothDevicePreference is the preference type used to display each remote @@ -79,7 +82,9 @@ public final class BluetoothDevicePreference extends GearPreference { @VisibleForTesting BluetoothAdapter mBluetoothAdapter; private final boolean mShowDevicesWithoutNames; - private final long mCurrentTime; + @NonNull + private static final AtomicInteger sNextId = new AtomicInteger(); + private final int mId; private final int mType; private AlertDialog mDisconnectDialog; @@ -127,8 +132,9 @@ public final class BluetoothDevicePreference extends GearPreference { mCachedDevice = cachedDevice; mCallback = new BluetoothDevicePreferenceCallback(); - mCurrentTime = System.currentTimeMillis(); + mId = sNextId.getAndIncrement(); mType = type; + setVisible(false); onPreferenceAttributesChanged(); } @@ -229,35 +235,41 @@ public final class BluetoothDevicePreference extends GearPreference { @SuppressWarnings("FutureReturnValueIgnored") void onPreferenceAttributesChanged() { - Pair pair = mCachedDevice.getDrawableWithDescription(); - setIcon(pair.first); - contentDescription = pair.second; - - /* - * The preference framework takes care of making sure the value has - * changed before proceeding. It will also call notifyChanged() if - * any preference info has changed from the previous value. - */ - setTitle(mCachedDevice.getName()); try { ThreadUtils.postOnBackgroundThread(() -> { + @Nullable String name = mCachedDevice.getName(); // Null check is done at the framework - ThreadUtils.postOnMainThread(() -> setSummary(getConnectionSummary())); + @Nullable String connectionSummary = getConnectionSummary(); + @NonNull Pair pair = mCachedDevice.getDrawableWithDescription(); + boolean isBusy = mCachedDevice.isBusy(); + // Device is only visible in the UI if it has a valid name besides MAC address or + // when user allows showing devices without user-friendly name in developer settings + boolean isVisible = + mShowDevicesWithoutNames || mCachedDevice.hasHumanReadableName(); + + ThreadUtils.postOnMainThread(() -> { + /* + * The preference framework takes care of making sure the value has + * changed before proceeding. It will also call notifyChanged() if + * any preference info has changed from the previous value. + */ + setTitle(name); + setSummary(connectionSummary); + setIcon(pair.first); + contentDescription = pair.second; + // Used to gray out the item + setEnabled(!isBusy); + setVisible(isVisible); + + // This could affect ordering, so notify that + if (mNeedNotifyHierarchyChanged) { + notifyHierarchyChanged(); + } + }); }); } catch (RejectedExecutionException e) { Log.w(TAG, "Handler thread unavailable, skipping getConnectionSummary!"); } - // Used to gray out the item - setEnabled(!mCachedDevice.isBusy()); - - // Device is only visible in the UI if it has a valid name besides MAC address or when user - // allows showing devices without user-friendly name in developer settings - setVisible(mShowDevicesWithoutNames || mCachedDevice.hasHumanReadableName()); - - // This could affect ordering, so notify that - if (mNeedNotifyHierarchyChanged) { - notifyHierarchyChanged(); - } } @Override @@ -311,7 +323,7 @@ public final class BluetoothDevicePreference extends GearPreference { return mCachedDevice .compareTo(((BluetoothDevicePreference) another).mCachedDevice); case SortType.TYPE_FIFO: - return mCurrentTime > ((BluetoothDevicePreference) another).mCurrentTime ? 1 : -1; + return mId > ((BluetoothDevicePreference) another).mId ? 1 : -1; default: return super.compareTo(another); } diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java deleted file mode 100644 index a4a98917974..00000000000 --- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.bluetooth; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.le.BluetoothLeScanner; -import android.bluetooth.le.ScanCallback; -import android.bluetooth.le.ScanFilter; -import android.bluetooth.le.ScanResult; -import android.bluetooth.le.ScanSettings; -import android.os.Bundle; -import android.os.SystemProperties; -import android.text.BidiFormatter; -import android.util.Log; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; -import androidx.preference.PreferenceCategory; -import androidx.preference.PreferenceGroup; - -import com.android.settings.R; -import com.android.settings.dashboard.RestrictedDashboardFragment; -import com.android.settingslib.bluetooth.BluetoothCallback; -import com.android.settingslib.bluetooth.BluetoothDeviceFilter; -import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; -import com.android.settingslib.bluetooth.LocalBluetoothManager; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; - -/** - * Parent class for settings fragments that contain a list of Bluetooth - * devices. - * - * @see DevicePickerFragment - */ -// TODO: Refactor this fragment -public abstract class DeviceListPreferenceFragment extends - RestrictedDashboardFragment implements BluetoothCallback { - - private static final String TAG = "DeviceListPreferenceFragment"; - - private static final String KEY_BT_SCAN = "bt_scan"; - - // Copied from BluetoothDeviceNoNamePreferenceController.java - private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY = - "persist.bluetooth.showdeviceswithoutnames"; - - private BluetoothDeviceFilter.Filter mFilter; - private List mLeScanFilters; - private ScanCallback mScanCallback; - - @VisibleForTesting - protected boolean mScanEnabled; - - protected BluetoothDevice mSelectedDevice; - - protected BluetoothAdapter mBluetoothAdapter; - protected LocalBluetoothManager mLocalManager; - protected CachedBluetoothDeviceManager mCachedDeviceManager; - - @VisibleForTesting - protected PreferenceGroup mDeviceListGroup; - - protected final HashMap mDevicePreferenceMap = - new HashMap<>(); - protected final List mSelectedList = new ArrayList<>(); - - protected boolean mShowDevicesWithoutNames; - - public DeviceListPreferenceFragment(String restrictedKey) { - super(restrictedKey); - mFilter = BluetoothDeviceFilter.ALL_FILTER; - } - - protected final void setFilter(BluetoothDeviceFilter.Filter filter) { - mFilter = filter; - } - - protected final void setFilter(int filterType) { - mFilter = BluetoothDeviceFilter.getFilter(filterType); - } - - /** - * Sets the bluetooth device scanning filter with {@link ScanFilter}s. It will change to start - * {@link BluetoothLeScanner} which will scan BLE device only. - * - * @param leScanFilters list of settings to filter scan result - */ - protected void setFilter(List leScanFilters) { - mFilter = null; - mLeScanFilters = leScanFilters; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mLocalManager = Utils.getLocalBtManager(getActivity()); - if (mLocalManager == null) { - Log.e(TAG, "Bluetooth is not supported on this device"); - return; - } - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - mCachedDeviceManager = mLocalManager.getCachedDeviceManager(); - mShowDevicesWithoutNames = SystemProperties.getBoolean( - BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false); - - initPreferencesFromPreferenceScreen(); - - mDeviceListGroup = (PreferenceCategory) findPreference(getDeviceListKey()); - } - - /** find and update preference that already existed in preference screen */ - protected abstract void initPreferencesFromPreferenceScreen(); - - @Override - public void onStart() { - super.onStart(); - if (mLocalManager == null || isUiRestricted()) return; - - mLocalManager.setForegroundActivity(getActivity()); - mLocalManager.getEventManager().registerCallback(this); - } - - @Override - public void onStop() { - super.onStop(); - if (mLocalManager == null || isUiRestricted()) { - return; - } - - removeAllDevices(); - mLocalManager.setForegroundActivity(null); - mLocalManager.getEventManager().unregisterCallback(this); - } - - void removeAllDevices() { - mDevicePreferenceMap.clear(); - mDeviceListGroup.removeAll(); - } - - void addCachedDevices() { - Collection cachedDevices = - mCachedDeviceManager.getCachedDevicesCopy(); - for (CachedBluetoothDevice cachedDevice : cachedDevices) { - onDeviceAdded(cachedDevice); - } - } - - @Override - public boolean onPreferenceTreeClick(Preference preference) { - if (KEY_BT_SCAN.equals(preference.getKey())) { - startScanning(); - return true; - } - - if (preference instanceof BluetoothDevicePreference) { - BluetoothDevicePreference btPreference = (BluetoothDevicePreference) preference; - CachedBluetoothDevice device = btPreference.getCachedDevice(); - mSelectedDevice = device.getDevice(); - mSelectedList.add(mSelectedDevice); - onDevicePreferenceClick(btPreference); - return true; - } - - return super.onPreferenceTreeClick(preference); - } - - protected void onDevicePreferenceClick(BluetoothDevicePreference btPreference) { - btPreference.onClicked(); - } - - @Override - public void onDeviceAdded(CachedBluetoothDevice cachedDevice) { - if (mDevicePreferenceMap.get(cachedDevice) != null) { - return; - } - - // Prevent updates while the list shows one of the state messages - if (mBluetoothAdapter.getState() != BluetoothAdapter.STATE_ON) { - return; - } - - if (mFilter != null && mFilter.matches(cachedDevice.getDevice())) { - createDevicePreference(cachedDevice); - } - } - - void createDevicePreference(CachedBluetoothDevice cachedDevice) { - if (mDeviceListGroup == null) { - Log.w(TAG, "Trying to create a device preference before the list group/category " - + "exists!"); - return; - } - - String key = cachedDevice.getDevice().getAddress(); - BluetoothDevicePreference preference = (BluetoothDevicePreference) getCachedPreference(key); - - if (preference == null) { - preference = new BluetoothDevicePreference(getPrefContext(), cachedDevice, - mShowDevicesWithoutNames, BluetoothDevicePreference.SortType.TYPE_FIFO); - preference.setKey(key); - //Set hideSecondTarget is true if it's bonded device. - preference.hideSecondTarget(true); - mDeviceListGroup.addPreference(preference); - } - - initDevicePreference(preference); - mDevicePreferenceMap.put(cachedDevice, preference); - } - - protected void initDevicePreference(BluetoothDevicePreference preference) { - // Does nothing by default - } - - @VisibleForTesting - void updateFooterPreference(Preference myDevicePreference) { - final BidiFormatter bidiFormatter = BidiFormatter.getInstance(); - - myDevicePreference.setTitle(getString( - R.string.bluetooth_footer_mac_message, - bidiFormatter.unicodeWrap(mBluetoothAdapter.getAddress()))); - } - - @Override - public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) { - BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice); - if (preference != null) { - mDeviceListGroup.removePreference(preference); - } - } - - @VisibleForTesting - protected void enableScanning() { - // BluetoothAdapter already handles repeated scan requests - if (!mScanEnabled) { - startScanning(); - mScanEnabled = true; - } - } - - @VisibleForTesting - protected void disableScanning() { - if (mScanEnabled) { - stopScanning(); - mScanEnabled = false; - } - } - - @Override - public void onScanningStateChanged(boolean started) { - if (!started && mScanEnabled) { - startScanning(); - } - } - - /** - * Return the key of the {@link PreferenceGroup} that contains the bluetooth devices - */ - public abstract String getDeviceListKey(); - - public boolean shouldShowDevicesWithoutNames() { - return mShowDevicesWithoutNames; - } - - @VisibleForTesting - void startScanning() { - if (mFilter != null) { - startClassicScanning(); - } else if (mLeScanFilters != null) { - startLeScanning(); - } - - } - - @VisibleForTesting - void stopScanning() { - if (mFilter != null) { - stopClassicScanning(); - } else if (mLeScanFilters != null) { - stopLeScanning(); - } - } - - private void startClassicScanning() { - if (!mBluetoothAdapter.isDiscovering()) { - mBluetoothAdapter.startDiscovery(); - } - } - - private void stopClassicScanning() { - if (mBluetoothAdapter.isDiscovering()) { - mBluetoothAdapter.cancelDiscovery(); - } - } - - private void startLeScanning() { - final BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner(); - final ScanSettings settings = new ScanSettings.Builder() - .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) - .build(); - mScanCallback = new ScanCallback() { - @Override - public void onScanResult(int callbackType, ScanResult result) { - final BluetoothDevice device = result.getDevice(); - CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(device); - if (cachedDevice == null) { - cachedDevice = mCachedDeviceManager.addDevice(device); - } - // Only add device preference when it's not found in the map and there's no other - // state message showing in the list - if (mDevicePreferenceMap.get(cachedDevice) == null - && mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) { - createDevicePreference(cachedDevice); - } - } - - @Override - public void onScanFailed(int errorCode) { - Log.w(TAG, "BLE Scan failed with error code " + errorCode); - } - }; - scanner.startScan(mLeScanFilters, settings, mScanCallback); - } - - private void stopLeScanning() { - final BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner(); - if (scanner != null) { - scanner.stopScan(mScanCallback); - } - } -} diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt new file mode 100644 index 00000000000..9c86e4398f6 --- /dev/null +++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt @@ -0,0 +1,348 @@ +/* + * 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.bluetooth + +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.bluetooth.le.BluetoothLeScanner +import android.bluetooth.le.ScanCallback +import android.bluetooth.le.ScanFilter +import android.bluetooth.le.ScanResult +import android.bluetooth.le.ScanSettings +import android.os.Bundle +import android.os.SystemProperties +import android.text.BidiFormatter +import android.util.Log +import android.view.View +import androidx.annotation.VisibleForTesting +import androidx.lifecycle.LifecycleCoroutineScope +import androidx.lifecycle.lifecycleScope +import androidx.preference.Preference +import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceGroup +import com.android.settings.R +import com.android.settings.dashboard.RestrictedDashboardFragment +import com.android.settingslib.bluetooth.BluetoothCallback +import com.android.settingslib.bluetooth.BluetoothDeviceFilter +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager +import com.android.settingslib.bluetooth.LocalBluetoothManager +import java.util.concurrent.ConcurrentHashMap +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +/** + * Parent class for settings fragments that contain a list of Bluetooth devices. + * + * @see DevicePickerFragment + * + * TODO: Refactor this fragment + */ +abstract class DeviceListPreferenceFragment(restrictedKey: String?) : + RestrictedDashboardFragment(restrictedKey), BluetoothCallback { + + private var filter: BluetoothDeviceFilter.Filter? = BluetoothDeviceFilter.ALL_FILTER + private var leScanFilters: List? = null + + @JvmField + @VisibleForTesting + var mScanEnabled = false + + @JvmField + var mSelectedDevice: BluetoothDevice? = null + + @JvmField + var mBluetoothAdapter: BluetoothAdapter? = null + + @JvmField + var mLocalManager: LocalBluetoothManager? = null + + @JvmField + var mCachedDeviceManager: CachedBluetoothDeviceManager? = null + + @JvmField + @VisibleForTesting + var mDeviceListGroup: PreferenceGroup? = null + + @VisibleForTesting + val devicePreferenceMap = + ConcurrentHashMap() + + @JvmField + val mSelectedList: MutableList = ArrayList() + + private var showDevicesWithoutNames = false + + protected fun setFilter(filter: BluetoothDeviceFilter.Filter?) { + this.filter = filter + } + + protected fun setFilter(filterType: Int) { + filter = BluetoothDeviceFilter.getFilter(filterType) + } + + /** + * Sets the bluetooth device scanning filter with [ScanFilter]s. It will change to start + * [BluetoothLeScanner] which will scan BLE device only. + * + * @param leScanFilters list of settings to filter scan result + */ + fun setFilter(leScanFilters: List?) { + filter = null + this.leScanFilters = leScanFilters + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mLocalManager = Utils.getLocalBtManager(activity) + if (mLocalManager == null) { + Log.e(TAG, "Bluetooth is not supported on this device") + return + } + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter() + mCachedDeviceManager = mLocalManager!!.cachedDeviceManager + showDevicesWithoutNames = SystemProperties.getBoolean( + BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false + ) + initPreferencesFromPreferenceScreen() + mDeviceListGroup = findPreference(deviceListKey) as PreferenceCategory + } + + /** find and update preference that already existed in preference screen */ + protected abstract fun initPreferencesFromPreferenceScreen() + + private var lifecycleScope: LifecycleCoroutineScope? = null + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + lifecycleScope = viewLifecycleOwner.lifecycleScope + } + + override fun onStart() { + super.onStart() + if (mLocalManager == null || isUiRestricted) return + mLocalManager!!.foregroundActivity = activity + mLocalManager!!.eventManager.registerCallback(this) + } + + override fun onStop() { + super.onStop() + if (mLocalManager == null || isUiRestricted) { + return + } + removeAllDevices() + mLocalManager!!.foregroundActivity = null + mLocalManager!!.eventManager.unregisterCallback(this) + } + + fun removeAllDevices() { + devicePreferenceMap.clear() + mDeviceListGroup!!.removeAll() + } + + fun addCachedDevices() { + lifecycleScope?.launch { + withContext(Dispatchers.Default) { + val cachedDevices = mCachedDeviceManager!!.cachedDevicesCopy + for (cachedDevice in cachedDevices) { + onDeviceAdded(cachedDevice) + } + } + } + } + + override fun onPreferenceTreeClick(preference: Preference): Boolean { + if (KEY_BT_SCAN == preference.key) { + startScanning() + return true + } + if (preference is BluetoothDevicePreference) { + val device = preference.cachedDevice.device + mSelectedDevice = device + mSelectedList.add(device) + onDevicePreferenceClick(preference) + return true + } + return super.onPreferenceTreeClick(preference) + } + + protected open fun onDevicePreferenceClick(btPreference: BluetoothDevicePreference) { + btPreference.onClicked() + } + + override fun onDeviceAdded(cachedDevice: CachedBluetoothDevice) { + lifecycleScope?.launch { + addDevice(cachedDevice) + } + } + + private suspend fun addDevice(cachedDevice: CachedBluetoothDevice) = + withContext(Dispatchers.Default) { + // Prevent updates while the list shows one of the state messages + if (mBluetoothAdapter!!.state == BluetoothAdapter.STATE_ON && + filter?.matches(cachedDevice.device) == true + ) { + createDevicePreference(cachedDevice) + } + } + + private suspend fun createDevicePreference(cachedDevice: CachedBluetoothDevice) { + if (mDeviceListGroup == null) { + Log.w( + TAG, + "Trying to create a device preference before the list group/category exists!", + ) + return + } + // Only add device preference when it's not found in the map and there's no other state + // message showing in the list + val preference = devicePreferenceMap.computeIfAbsent(cachedDevice) { + BluetoothDevicePreference( + prefContext, + cachedDevice, + showDevicesWithoutNames, + BluetoothDevicePreference.SortType.TYPE_FIFO, + ).apply { + key = cachedDevice.device.address + //Set hideSecondTarget is true if it's bonded device. + hideSecondTarget(true) + } + } + withContext(Dispatchers.Main) { + mDeviceListGroup!!.addPreference(preference) + initDevicePreference(preference) + } + } + + protected open fun initDevicePreference(preference: BluetoothDevicePreference?) { + // Does nothing by default + } + + @VisibleForTesting + fun updateFooterPreference(myDevicePreference: Preference) { + val bidiFormatter = BidiFormatter.getInstance() + myDevicePreference.title = getString( + R.string.bluetooth_footer_mac_message, + bidiFormatter.unicodeWrap(mBluetoothAdapter!!.address) + ) + } + + override fun onDeviceDeleted(cachedDevice: CachedBluetoothDevice) { + devicePreferenceMap.remove(cachedDevice)?.let { + mDeviceListGroup!!.removePreference(it) + } + } + + @VisibleForTesting + open fun enableScanning() { + // BluetoothAdapter already handles repeated scan requests + if (!mScanEnabled) { + startScanning() + mScanEnabled = true + } + } + + @VisibleForTesting + fun disableScanning() { + if (mScanEnabled) { + stopScanning() + mScanEnabled = false + } + } + + override fun onScanningStateChanged(started: Boolean) { + if (!started && mScanEnabled) { + startScanning() + } + } + + /** + * Return the key of the [PreferenceGroup] that contains the bluetooth devices + */ + abstract val deviceListKey: String + + @VisibleForTesting + open fun startScanning() { + if (filter != null) { + startClassicScanning() + } else if (leScanFilters != null) { + startLeScanning() + } + } + + @VisibleForTesting + open fun stopScanning() { + if (filter != null) { + stopClassicScanning() + } else if (leScanFilters != null) { + stopLeScanning() + } + } + + private fun startClassicScanning() { + if (!mBluetoothAdapter!!.isDiscovering) { + mBluetoothAdapter!!.startDiscovery() + } + } + + private fun stopClassicScanning() { + if (mBluetoothAdapter!!.isDiscovering) { + mBluetoothAdapter!!.cancelDiscovery() + } + } + + private val scanCallback = object : ScanCallback() { + override fun onScanResult(callbackType: Int, result: ScanResult) { + lifecycleScope?.launch { + withContext(Dispatchers.Default) { + if (mBluetoothAdapter!!.state == BluetoothAdapter.STATE_ON) { + val device = result.device + val cachedDevice = mCachedDeviceManager!!.findDevice(device) + ?: mCachedDeviceManager!!.addDevice(device) + createDevicePreference(cachedDevice) + } + } + } + } + + override fun onScanFailed(errorCode: Int) { + Log.w(TAG, "BLE Scan failed with error code $errorCode") + } + } + + private fun startLeScanning() { + val scanner = mBluetoothAdapter!!.bluetoothLeScanner + val settings = ScanSettings.Builder() + .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) + .build() + scanner.startScan(leScanFilters, settings, scanCallback) + } + + private fun stopLeScanning() { + val scanner = mBluetoothAdapter!!.bluetoothLeScanner + scanner?.stopScan(scanCallback) + } + + companion object { + private const val TAG = "DeviceListPreferenceFragment" + private const val KEY_BT_SCAN = "bt_scan" + + // Copied from BluetoothDeviceNoNamePreferenceController.java + private const val BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY = + "persist.bluetooth.showdeviceswithoutnames" + } +} diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java index f8a5d76b6fa..d4acfa11c57 100644 --- a/src/com/android/settings/dashboard/DashboardFragment.java +++ b/src/com/android/settings/dashboard/DashboardFragment.java @@ -25,11 +25,14 @@ import android.preference.PreferenceManager.OnActivityResultListener; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; +import android.view.View; import androidx.annotation.CallSuper; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceGroup; @@ -169,6 +172,15 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment } } + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + LifecycleOwner viewLifecycleOwner = getViewLifecycleOwner(); + for (AbstractPreferenceController controller : mControllers) { + controller.onViewCreated(viewLifecycleOwner); + } + } + @Override public void onCategoriesChanged(Set categories) { final String categoryKey = getCategoryKey(); diff --git a/src/com/android/settings/datausage/DataSaverSummary.kt b/src/com/android/settings/datausage/DataSaverSummary.kt index 13fbbfa3069..0828d362410 100644 --- a/src/com/android/settings/datausage/DataSaverSummary.kt +++ b/src/com/android/settings/datausage/DataSaverSummary.kt @@ -19,11 +19,9 @@ import android.app.settings.SettingsEnums import android.content.Context import android.os.Bundle import android.telephony.SubscriptionManager -import android.view.View import android.widget.Switch import com.android.settings.R import com.android.settings.SettingsActivity -import com.android.settings.applications.specialaccess.DataSaverController import com.android.settings.dashboard.DashboardFragment import com.android.settings.search.BaseSearchIndexProvider import com.android.settings.widget.SettingsMainSwitchBar @@ -59,11 +57,6 @@ class DataSaverSummary : DashboardFragment() { } } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - use(DataSaverController::class.java).init(viewLifecycleOwner) - } - override fun onResume() { super.onResume() dataSaverBackend.addListener(dataSaverBackendListener) diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java index 943a937a32a..9533314c8a2 100644 --- a/src/com/android/settings/password/ChooseLockSettingsHelper.java +++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java @@ -362,7 +362,8 @@ public final class ChooseLockSettingsHelper { } @NonNull public ChooseLockSettingsHelper build() { - if (!mAllowAnyUserId && mUserId != LockPatternUtils.USER_FRP) { + if (!mAllowAnyUserId && mUserId != LockPatternUtils.USER_FRP + && mUserId != LockPatternUtils.USER_REPAIR_MODE) { Utils.enforceSameOwner(mActivity, mUserId); } diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java index d409c0f9dfb..314ce053127 100644 --- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java +++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java @@ -166,8 +166,12 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { mDetails = intent.getCharSequenceExtra(KeyguardManager.EXTRA_DESCRIPTION); String alternateButton = intent.getStringExtra( KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL); - boolean frp = KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction()); - boolean remoteValidation = + final boolean frp = + KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction()); + final boolean repairMode = + KeyguardManager.ACTION_CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL + .equals(intent.getAction()); + final boolean remoteValidation = KeyguardManager.ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL.equals(intent.getAction()); mTaskOverlay = isInternalActivity() && intent.getBooleanExtra(KeyguardManager.EXTRA_FORCE_TASK_OVERLAY, false); @@ -222,6 +226,14 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { .setExternal(true) .setUserId(LockPatternUtils.USER_FRP) .show(); + } else if (repairMode) { + final ChooseLockSettingsHelper.Builder builder = + new ChooseLockSettingsHelper.Builder(this); + launchedCDC = builder.setHeader(mTitle) + .setDescription(mDetails) + .setExternal(true) + .setUserId(LockPatternUtils.USER_REPAIR_MODE) + .show(); } else if (remoteValidation) { RemoteLockscreenValidationSession remoteLockscreenValidationSession = intent.getParcelableExtra( diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java index 5a123b89315..43d8440512b 100644 --- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java +++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java @@ -106,6 +106,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr protected boolean mFrp; protected boolean mRemoteValidation; protected boolean mRequestWriteRepairModePassword; + protected boolean mRepairMode; protected CharSequence mAlternateButtonText; protected BiometricManager mBiometricManager; @Nullable protected RemoteLockscreenValidationSession mRemoteLockscreenValidationSession; @@ -181,6 +182,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras(), isInternalActivity()); mFrp = (mUserId == LockPatternUtils.USER_FRP); + mRepairMode = (mUserId == LockPatternUtils.USER_REPAIR_MODE); mUserManager = UserManager.get(getActivity()); mEffectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId); mLockPatternUtils = new LockPatternUtils(getActivity()); @@ -269,7 +271,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr // verifyTiedProfileChallenge. In such case, we also wanna show the user message that // fingerprint is disabled due to device restart. protected boolean isStrongAuthRequired() { - return mFrp + return mFrp || mRepairMode || !mLockPatternUtils.isBiometricAllowedForUser(mEffectiveUserId) || !mUserManager.isUserUnlocked(mUserId); } diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java index 1b535069f3c..c6022b5d3ce 100644 --- a/src/com/android/settings/password/ConfirmLockPassword.java +++ b/src/com/android/settings/password/ConfirmLockPassword.java @@ -284,6 +284,11 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_header_frp) : getString(R.string.lockpassword_confirm_your_pin_header_frp); } + if (mRepairMode) { + return mIsAlpha + ? getString(R.string.lockpassword_confirm_repair_mode_password_header) + : getString(R.string.lockpassword_confirm_repair_mode_pin_header); + } if (mRemoteValidation) { return getString(R.string.lockpassword_remote_validation_header); } @@ -307,6 +312,11 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_details_frp) : getString(R.string.lockpassword_confirm_your_pin_details_frp); } + if (mRepairMode) { + return mIsAlpha + ? getString(R.string.lockpassword_confirm_repair_mode_password_details) + : getString(R.string.lockpassword_confirm_repair_mode_pin_details); + } if (mRemoteValidation) { return getContext().getString(mIsAlpha ? R.string.lockpassword_remote_validation_password_details diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java index 3951bde9bac..a2bcb5af510 100644 --- a/src/com/android/settings/password/ConfirmLockPattern.java +++ b/src/com/android/settings/password/ConfirmLockPattern.java @@ -179,7 +179,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { // ability to disable the pattern in L. Remove this block after // ensuring it's safe to do so. (Note that ConfirmLockPassword // doesn't have this). - if (!mFrp && !mRemoteValidation + if (!mFrp && !mRemoteValidation && !mRepairMode && !mLockPatternUtils.isLockPatternEnabled(mEffectiveUserId)) { getActivity().setResult(Activity.RESULT_OK); getActivity().finish(); @@ -308,6 +308,9 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { if (mFrp) { return getString(R.string.lockpassword_confirm_your_pattern_details_frp); } + if (mRepairMode) { + return getString(R.string.lockpassword_confirm_repair_mode_pattern_details); + } if (mRemoteValidation) { return getString( R.string.lockpassword_remote_validation_pattern_details); @@ -402,7 +405,12 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { } private String getDefaultHeader() { - if (mFrp) return getString(R.string.lockpassword_confirm_your_pattern_header_frp); + if (mFrp) { + return getString(R.string.lockpassword_confirm_your_pattern_header_frp); + } + if (mRepairMode) { + return getString(R.string.lockpassword_confirm_repair_mode_pattern_header); + } if (mRemoteValidation) { return getString(R.string.lockpassword_remote_validation_header); } diff --git a/src/com/android/settings/spa/development/UsageStats.kt b/src/com/android/settings/spa/development/UsageStats.kt index b681d7579de..4d9c4551f10 100644 --- a/src/com/android/settings/spa/development/UsageStats.kt +++ b/src/com/android/settings/spa/development/UsageStats.kt @@ -32,7 +32,6 @@ object UsageStatsPageProvider : SettingsPageProvider { AppListPage( title = stringResource(R.string.testing_usage_stats), listModel = rememberContext(::UsageStatsListModel), - primaryUserOnly = true, ) } } diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java index e14e27109e0..ea2852fd75e 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java @@ -38,6 +38,7 @@ import android.content.pm.ServiceInfo; import android.view.accessibility.AccessibilityManager; import androidx.fragment.app.FragmentActivity; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; @@ -93,6 +94,7 @@ public class AccessibilitySettingsForSetupWizardTest { when(mAccessibilityManager.getInstalledAccessibilityServiceList()).thenReturn( mAccessibilityServices); doReturn(mActivity).when(mFragment).getActivity(); + doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner(); doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class); } diff --git a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java index 1cd301f98b9..4ee2a2dedbd 100644 --- a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java @@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -29,6 +30,7 @@ import android.app.settings.SettingsEnums; import android.content.Context; import androidx.fragment.app.FragmentActivity; +import androidx.lifecycle.LifecycleOwner; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; @@ -73,6 +75,7 @@ public class TextReadingPreferenceFragmentForSetupWizardTest { final LayoutPreference resetPreference = new LayoutPreference(mContext, R.layout.accessibility_text_reading_reset_button); doReturn(mContext).when(mFragment).getContext(); + doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner(); doReturn(resetPreference).when(mFragment).findPreference(RESET_KEY); doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class); } diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java index 84783b21a3a..aa622f58afd 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -27,6 +28,7 @@ import static org.mockito.Mockito.when; import android.app.settings.SettingsEnums; import android.content.Context; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; @@ -75,6 +77,7 @@ public class ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest { mFragment = spy(new TestToggleScreenMagnificationPreferenceFragmentForSetupWizard(mContext)); doReturn(mActivity).when(mFragment).getActivity(); + doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner(); when(mActivity.getSwitchBar()).thenReturn(mSwitchBar); doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class); } diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java index c604652fd50..77e5b1f9b9d 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -28,6 +29,7 @@ import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Bundle; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; @@ -72,6 +74,7 @@ public class ToggleScreenReaderPreferenceFragmentForSetupWizardTest { public void setUp() { mFragment = spy(new TestToggleScreenReaderPreferenceFragmentForSetupWizard(mContext)); doReturn(mActivity).when(mFragment).getActivity(); + doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner(); when(mActivity.getSwitchBar()).thenReturn(mSwitchBar); doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class); } diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java index 78938310d9a..8878064afa0 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -28,6 +29,7 @@ import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Bundle; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; @@ -72,6 +74,7 @@ public class ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest { public void setUp() { mFragment = spy(new TestToggleSelectToSpeakPreferenceFragmentForSetupWizard(mContext)); doReturn(mActivity).when(mFragment).getActivity(); + doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner(); when(mActivity.getSwitchBar()).thenReturn(mSwitchBar); doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class); } diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java index 8c84128913f..0f12d1e5023 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java @@ -352,6 +352,19 @@ public class FingerprintEnrollEnrollingTest { assertThat(descriptionTextView.getVisibility()).isEqualTo(View.VISIBLE); } + @Test + public void fingerprintUdfpsOverlayEnrollment_udfpsAnimationViewVisibility() { + initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL); + when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0); + createActivity(); + + final UdfpsEnrollView enrollView = mActivity.findViewById(R.id.udfps_animation_view); + assertThat(enrollView.getVisibility()).isEqualTo(View.GONE); + + mActivity.onUdfpsOverlayShown(); + assertThat(enrollView.getVisibility()).isEqualTo(View.VISIBLE); + } + @Test public void forwardEnrollProgressEvents() { initializeActivityFor(TYPE_UDFPS_OPTICAL); @@ -393,11 +406,11 @@ public class FingerprintEnrollEnrollingTest { } @Test - public void forwardEnrollPointerDownEvents() { + public void forwardUdfpsEnrollPointerDownEvents() { initializeActivityFor(TYPE_UDFPS_OPTICAL); EnrollListener listener = new EnrollListener(mActivity); - mActivity.onPointerDown(0); + mActivity.onUdfpsPointerDown(0); assertThat(listener.mProgress).isFalse(); assertThat(listener.mHelp).isFalse(); assertThat(listener.mAcquired).isFalse(); @@ -406,11 +419,11 @@ public class FingerprintEnrollEnrollingTest { } @Test - public void forwardEnrollPointerUpEvents() { + public void forwardUdfpsEnrollPointerUpEvents() { initializeActivityFor(TYPE_UDFPS_OPTICAL); EnrollListener listener = new EnrollListener(mActivity); - mActivity.onPointerUp(0); + mActivity.onUdfpsPointerUp(0); assertThat(listener.mProgress).isFalse(); assertThat(listener.mHelp).isFalse(); assertThat(listener.mAcquired).isFalse(); diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java index 184f5212e77..7c598e00e42 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java @@ -202,7 +202,7 @@ public class BluetoothDevicePairingDetailBaseTest { new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, true, BluetoothDevicePreference.SortType.TYPE_FIFO); final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS); - mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference); + mFragment.getDevicePreferenceMap().put(mCachedBluetoothDevice, preference); when(mCachedBluetoothDevice.isConnected()).thenReturn(true); when(mCachedBluetoothDevice.getDevice()).thenReturn(device); @@ -210,7 +210,7 @@ public class BluetoothDevicePairingDetailBaseTest { mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice, BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED); - assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0); + assertThat(mFragment.getDevicePreferenceMap().size()).isEqualTo(0); } @Test @@ -221,7 +221,7 @@ public class BluetoothDevicePairingDetailBaseTest { true, BluetoothDevicePreference.SortType.TYPE_FIFO); final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS); final BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B); - mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference); + mFragment.getDevicePreferenceMap().put(mCachedBluetoothDevice, preference); when(mCachedBluetoothDevice.isConnected()).thenReturn(true); when(mCachedBluetoothDevice.getDevice()).thenReturn(device); diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java index 5fbfee8b50d..ce67051a7c7 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java @@ -27,7 +27,12 @@ import static org.mockito.Mockito.verify; import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.os.Bundle; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.LifecycleOwner; import androidx.test.core.app.ApplicationProvider; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; @@ -53,6 +58,20 @@ public class BluetoothPairingDetailTest { private final Context mContext = ApplicationProvider.getApplicationContext(); + private final Lifecycle mFakeLifecycle = new Lifecycle() { + @Override + public void addObserver(@NonNull LifecycleObserver observer) {} + + @Override + public void removeObserver(@NonNull LifecycleObserver observer) {} + + @NonNull + @Override + public State getCurrentState() { + return State.CREATED; + } + }; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private LocalBluetoothManager mLocalManager; @Mock(answer = Answers.RETURNS_DEEP_STUBS) @@ -74,6 +93,8 @@ public class BluetoothPairingDetailTest { .findPreference(BluetoothPairingDetail.KEY_AVAIL_DEVICES); doReturn(mFooterPreference).when(mFragment) .findPreference(BluetoothPairingDetail.KEY_FOOTER_PREF); + doReturn(new View(mContext)).when(mFragment).getView(); + doReturn((LifecycleOwner) () -> mFakeLifecycle).when(mFragment).getViewLifecycleOwner(); doReturn(Collections.emptyList()).when(mDeviceManager).getCachedDevicesCopy(); mFragment.mBluetoothAdapter = mBluetoothAdapter; @@ -82,7 +103,7 @@ public class BluetoothPairingDetailTest { mFragment.mDeviceListGroup = mAvailableDevicesCategory; mFragment.onViewCreated(mFragment.getView(), Bundle.EMPTY); } -// + @Test public void initPreferencesFromPreferenceScreen_findPreferences() { mFragment.initPreferencesFromPreferenceScreen(); diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java index bdb45b0300f..2c830ad314c 100644 --- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java +++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java @@ -379,7 +379,7 @@ public class FingerprintEnrollProgressViewModelTest { // Notify acquire message final int value = 33; - mCallbackWrapper.mValue.onPointerDown(value); + mCallbackWrapper.mValue.onUdfpsPointerDown(value); assertThat(liveData.getValue()).isEqualTo(value); } @@ -397,7 +397,7 @@ public class FingerprintEnrollProgressViewModelTest { // Notify acquire message final int value = 44; - mCallbackWrapper.mValue.onPointerUp(value); + mCallbackWrapper.mValue.onUdfpsPointerUp(value); assertThat(liveData.getValue()).isEqualTo(value); }