diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java index 06f8d74b802..783da72aa18 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java @@ -23,6 +23,7 @@ import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.settings.biometrics.fingerprint.feature.FingerprintExtPreferencesProvider; import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature; import com.android.settings.biometrics.fingerprint.feature.SfpsRestToUnlockFeature; @@ -60,4 +61,14 @@ public interface FingerprintFeatureProvider { default FingerprintEnrollActivityClassProvider getEnrollActivityClassProvider(@NonNull Context context) { return FingerprintEnrollActivityClassProvider.getInstance(); } + + /** + * Gets new Preferences in Fingerprint Settings + */ + @NonNull + default FingerprintExtPreferencesProvider getExtPreferenceProvider( + @NonNull Context context + ) { + return new FingerprintExtPreferencesProvider(); + } } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index e618ae358b3..9bd7b319e7f 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -29,6 +29,7 @@ import static com.android.settings.biometrics.BiometricEnrollBase.BIOMETRIC_AUTH import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY; import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_CHALLENGE; +import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; import android.app.admin.DevicePolicyManager; @@ -82,6 +83,8 @@ import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.BiometricUtils; import com.android.settings.biometrics.GatekeeperPasswordProvider; import com.android.settings.biometrics.IdentityCheckBiometricErrorDialog; +import com.android.settings.biometrics.fingerprint.feature.FingerprintExtPreferencesProvider; +import com.android.settings.biometrics.fingerprint.feature.PrimarySwitchIntentPreference; import com.android.settings.core.SettingsBaseActivity; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.dashboard.DashboardFragment; @@ -95,6 +98,7 @@ import com.android.settingslib.HelpUtils; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtilsInternal; +import com.android.settingslib.RestrictedPreference; import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.search.SearchIndexable; @@ -259,6 +263,7 @@ public class FingerprintSettings extends SubSettings { private static final String KEY_LAUNCHED_CONFIRM = "launched_confirm"; private static final String KEY_HAS_FIRST_ENROLLED = "has_first_enrolled"; private static final String KEY_IS_ENROLLING = "is_enrolled"; + private static final String KEY_IS_LAUNCHING_EXT_PREF = "is_launching_ext_pref"; @VisibleForTesting static final String KEY_REQUIRE_SCREEN_ON_TO_AUTH = "security_settings_require_screen_on_to_auth"; @@ -289,6 +294,8 @@ public class FingerprintSettings extends SubSettings { @VisibleForTesting static final int ADD_FINGERPRINT_REQUEST = 10; private static final int AUTO_ADD_FIRST_FINGERPRINT_REQUEST = 11; + /** For launching extern preferences coming from FingerprintExtPreferencesProvider */ + private static final int LAUNCH_EXT_PREF_REQUEST = 12; protected static final boolean DEBUG = false; @@ -318,6 +325,8 @@ public class FingerprintSettings extends SubSettings { private int mUserId; private final List mFooterColumns = new ArrayList<>(); private boolean mIsEnrolling; + /** SaveInstance key if we are waiting activity result from a extension preference */ + @NonNull private String mLaunchedExtPrefKey = ""; private long mChallenge; @@ -417,6 +426,53 @@ public class FingerprintSettings extends SubSettings { } }; + /** Lambda function for creating a FingerprintExtPreferencesProvider::PreferenceInflater */ + @SuppressLint("RestrictedApi") + private PreferenceScreen inflateFromResource(int resId) { + return getPreferenceManager().inflateFromResource( + requireContext(), + resId, + /* rootPreferences */ null + ); + } + + /** + * Lambda function for handling click event in preferences from + * FingerprintExtPreferencesProvider + */ + private boolean onExtIntentPreferenceClick(@NonNull Preference preference) { + if (!(preference instanceof PrimarySwitchIntentPreference)) { + return false; + } + + mLaunchedExtPrefKey = preference.getKey(); + startActivityForResult( + ((PrimarySwitchIntentPreference) preference).getLaunchedIntent(mToken), + LAUNCH_EXT_PREF_REQUEST + ); + return true; + } + + /** + * Add new preferences from FingerprintExtPreferencesProvider + */ + public void setupExtFingerprintPreferences() { + final FingerprintExtPreferencesProvider preferencesProvider = + FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider() + .getExtPreferenceProvider(requireContext()); + for (int index = 0; index < preferencesProvider.getSize(); ++index) { + final RestrictedPreference preference = preferencesProvider.newPreference( + index, this::inflateFromResource, requireContext()); + if (preference == null || findPreference(preference.getKey()) != null) { + continue; + } + if (preference instanceof PrimarySwitchIntentPreference) { + preference.setOnPreferenceClickListener(this::onExtIntentPreferenceClick); + } + mFingerprintUnlockCategory.addPreference(preference); + } + } + /** * */ @@ -529,6 +585,8 @@ public class FingerprintSettings extends SubSettings { mLaunchedConfirm = savedInstanceState.getBoolean( KEY_LAUNCHED_CONFIRM, false); mIsEnrolling = savedInstanceState.getBoolean(KEY_IS_ENROLLING, mIsEnrolling); + mLaunchedExtPrefKey = savedInstanceState.getString( + KEY_IS_LAUNCHING_EXT_PREF, mLaunchedExtPrefKey); mHasFirstEnrolled = savedInstanceState.getBoolean(KEY_HAS_FIRST_ENROLLED, mHasFirstEnrolled); mBiometricsAuthenticationRequested = savedInstanceState.getBoolean( @@ -781,6 +839,7 @@ public class FingerprintSettings extends SubSettings { } else if (screenOffUnlockUdfps() && isUltrasnoicUdfps()) { setupFingerprintUnlockCategoryPreferencesForScreenOffUnlock(); } + setupExtFingerprintPreferences(); updateFingerprintUnlockCategoryVisibility(); } @@ -965,7 +1024,9 @@ public class FingerprintSettings extends SubSettings { @Override public void onStop() { super.onStop(); - if (!getActivity().isChangingConfigurations() && !mLaunchedConfirm && !mIsEnrolling) { + if (!getActivity().isChangingConfigurations() && !mLaunchedConfirm && !mIsEnrolling + && mLaunchedExtPrefKey.isEmpty()) { + Log.d(TAG, "Finish activity for unknown stop"); setResult(RESULT_TIMEOUT); getActivity().finish(); } @@ -988,6 +1049,7 @@ public class FingerprintSettings extends SubSettings { outState.putBoolean(KEY_LAUNCHED_CONFIRM, mLaunchedConfirm); outState.putSerializable("mFingerprintsRenaming", mFingerprintsRenaming); outState.putBoolean(KEY_IS_ENROLLING, mIsEnrolling); + outState.putString(KEY_IS_LAUNCHING_EXT_PREF, mLaunchedExtPrefKey); outState.putBoolean(KEY_HAS_FIRST_ENROLLED, mHasFirstEnrolled); outState.putBoolean(KEY_BIOMETRICS_AUTHENTICATION_REQUESTED, mBiometricsAuthenticationRequested); @@ -1255,6 +1317,14 @@ public class FingerprintSettings extends SubSettings { finish(); } } + } else if (requestCode == LAUNCH_EXT_PREF_REQUEST) { + if (!mLaunchedExtPrefKey.isEmpty()) { + final Preference preference = findPreference(mLaunchedExtPrefKey); + if (preference instanceof PrimarySwitchIntentPreference) { + ((PrimarySwitchIntentPreference) preference).forceUpdate(); + } + } + mLaunchedExtPrefKey = ""; } } diff --git a/src/com/android/settings/biometrics/fingerprint/feature/FingerprintExtPreferencesProvider.kt b/src/com/android/settings/biometrics/fingerprint/feature/FingerprintExtPreferencesProvider.kt new file mode 100644 index 00000000000..1e3b38c3f5e --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint/feature/FingerprintExtPreferencesProvider.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2025 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.biometrics.fingerprint.feature + +import android.content.Context +import androidx.annotation.XmlRes +import androidx.preference.PreferenceScreen +import com.android.settingslib.RestrictedPreference + +/** + * Provides a list of preference resource IDs (from res/xml) for Fingerprint Settings page under + * category FingerprintSettings::mFingerprintUnlockCategory + * + * @see com.android.settings.biometrics.fingerprint.FingerprintSettings + */ +open class FingerprintExtPreferencesProvider { + + open val size: Int = 0 + + open fun newPreference( + index: Int, + inflater: PreferenceInflater, + context: Context + ): RestrictedPreference? = null + + interface PreferenceInflater { + fun inflateFromResource(@XmlRes resId: Int): PreferenceScreen + } +} diff --git a/src/com/android/settings/biometrics/fingerprint/feature/PrimarySwitchIntentPreference.kt b/src/com/android/settings/biometrics/fingerprint/feature/PrimarySwitchIntentPreference.kt new file mode 100644 index 00000000000..b4da356c78f --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint/feature/PrimarySwitchIntentPreference.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2025 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.biometrics.fingerprint.feature + +import android.content.Context +import android.content.Intent +import android.util.AttributeSet +import android.view.View +import androidx.preference.PreferenceViewHolder +import com.android.settingslib.PrimarySwitchPreference +import com.android.settingslib.core.instrumentation.SettingsJankMonitor.detectToggleJank + +/** + * Supports launching a new activity through Intent for PrimarySwitchPreference + */ +abstract class PrimarySwitchIntentPreference : PrimarySwitchPreference { + + constructor( + context: Context?, attrs: AttributeSet?, + defStyleAttr: Int, defStyleRes: Int + ) : super(context, attrs, defStyleAttr, defStyleRes) + + constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super( + context, + attrs, + defStyleAttr + ) + + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + + constructor(context: Context?) : super(context) + + open fun forceUpdate() {} + abstract fun getLaunchedIntent(token: ByteArray): Intent +}