diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index bce30dc7068..b178f0a7d82 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2830,6 +2830,12 @@
android:screenOrientation="portrait"/>
+
+
+
diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
index 809b32d8731..1ab86f9f708 100644
--- a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
+++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
@@ -38,7 +38,7 @@ import com.android.settings.R;
import com.android.settings.Settings;
import com.android.settings.SettingsActivity;
import com.android.settings.SubSettings;
-import com.android.settings.biometrics.face.FaceEnrollIntroduction;
+import com.android.settings.biometrics.face.FaceEnrollActivityClassProvider;
import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollActivityClassProvider;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
@@ -254,6 +254,11 @@ public class ActivityEmbeddingRulesController {
.buildSearchIntent(mContext, SettingsEnums.SETTINGS_HOMEPAGE);
addActivityFilter(activityFilters, searchIntent);
}
+ final FaceEnrollActivityClassProvider faceClassProvider = FeatureFactory
+ .getFeatureFactory()
+ .getFaceFeatureProvider()
+ .getEnrollActivityClassProvider();
+ addActivityFilter(activityFilters, faceClassProvider.getNext());
final FingerprintEnrollActivityClassProvider fpClassProvider = FeatureFactory
.getFeatureFactory()
.getFingerprintFeatureProvider()
@@ -263,7 +268,6 @@ public class ActivityEmbeddingRulesController {
addActivityFilter(activityFilters, fpClassProvider.getAddAnother());
addActivityFilter(activityFilters, FingerprintEnrollEnrolling.class);
addActivityFilter(activityFilters, FaceEnrollIntroductionInternal.class);
- addActivityFilter(activityFilters, FaceEnrollIntroduction.class);
addActivityFilter(activityFilters, RemoteAuthActivity.class);
addActivityFilter(activityFilters, RemoteAuthActivityInternal.class);
addActivityFilter(activityFilters, ChooseLockPattern.class);
diff --git a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
index 1f7b3e512b2..5d1c4784b98 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
@@ -71,7 +71,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
private boolean mParentalConsentRequired;
private boolean mHasScrolledToBottom = false;
- @Nullable private PorterDuffColorFilter mIconColorFilter;
+ @Nullable protected PorterDuffColorFilter mIconColorFilter;
/**
* @return true if the biometric is disabled by a device administrator
diff --git a/src/com/android/settings/biometrics/BiometricUtils.java b/src/com/android/settings/biometrics/BiometricUtils.java
index 2a457f50e9b..e080ef48905 100644
--- a/src/com/android/settings/biometrics/BiometricUtils.java
+++ b/src/com/android/settings/biometrics/BiometricUtils.java
@@ -43,7 +43,6 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
-import com.android.settings.biometrics.face.FaceEnrollIntroduction;
import com.android.settings.biometrics.fingerprint.FingerprintEnroll;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor;
@@ -281,7 +280,9 @@ public class BiometricUtils {
*/
public static Intent getFaceIntroIntent(@NonNull Context context,
@NonNull Intent activityIntent) {
- final Intent intent = new Intent(context, FaceEnrollIntroduction.class);
+ final Intent intent = new Intent(context,
+ FeatureFactory.getFeatureFactory().getFaceFeatureProvider()
+ .getEnrollActivityClassProvider().getNext());
WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
return intent;
}
diff --git a/src/com/android/settings/biometrics/face/FaceEnroll.kt b/src/com/android/settings/biometrics/face/FaceEnroll.kt
new file mode 100644
index 00000000000..0a3dae5d0a2
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceEnroll.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.face
+
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+
+import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
+
+class FaceEnroll: AppCompatActivity() {
+
+ /**
+ * The class of the next activity to launch. This is open to allow subclasses to provide their
+ * own behavior. Defaults to the default activity class provided by the
+ * enrollActivityClassProvider.
+ */
+ private val nextActivityClass: Class<*>
+ get() = enrollActivityProvider.next
+
+ private val enrollActivityProvider: FaceEnrollActivityClassProvider
+ get() = featureFactory.faceFeatureProvider.enrollActivityClassProvider
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ /**
+ * Logs the next activity to be launched, creates an intent for that activity,
+ * adds flags to forward the result, includes any existing extras from the current intent,
+ * starts the new activity and then finishes the current one
+ */
+ Log.d("FaceEnroll", "forward to $nextActivityClass")
+ val nextIntent = Intent(this, nextActivityClass)
+ nextIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
+ nextIntent.putExtras(intent)
+ startActivity(nextIntent)
+ finish()
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollActivityClassProvider.kt b/src/com/android/settings/biometrics/face/FaceEnrollActivityClassProvider.kt
new file mode 100644
index 00000000000..938b7253d6f
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceEnrollActivityClassProvider.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.face
+
+import android.app.Activity
+
+open class FaceEnrollActivityClassProvider {
+ open val next: Class
+ get() = FaceEnrollIntroduction::class.java
+
+ companion object {
+ @JvmStatic
+ val instance = FaceEnrollActivityClassProvider()
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index d3f75195305..946d9b3eb25 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -625,7 +625,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
updateDescriptionText();
}
- private boolean isPrivateProfile() {
+ protected boolean isPrivateProfile() {
return Utils.isPrivateProfile(mUserId, getApplicationContext());
}
}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroductionInternal.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroductionInternal.java
index 51d3a3a806d..062d5df0634 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroductionInternal.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroductionInternal.java
@@ -16,13 +16,13 @@
package com.android.settings.biometrics.face;
-import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
-
import android.content.Intent;
import android.os.Bundle;
import androidx.fragment.app.FragmentActivity;
+import com.android.settings.overlay.FeatureFactory;
+
/**
* Wrapper of {@link FaceEnrollIntroduction} to use with a pre-defined task affinity.
*
@@ -42,7 +42,9 @@ public class FaceEnrollIntroductionInternal extends FragmentActivity {
trampoline.setFlags(0);
// Trampoline to the intended activity, and finish
- trampoline.setClassName(SETTINGS_PACKAGE_NAME, FaceEnrollIntroduction.class.getName());
+ trampoline.setClass(getApplicationContext(),
+ FeatureFactory.getFeatureFactory().getFaceFeatureProvider()
+ .getEnrollActivityClassProvider().getNext());
startActivity(trampoline);
finish();
}
diff --git a/src/com/android/settings/biometrics/face/FaceFeatureProvider.java b/src/com/android/settings/biometrics/face/FaceFeatureProvider.java
index 1a4fd90668e..d38ce6d5067 100644
--- a/src/com/android/settings/biometrics/face/FaceFeatureProvider.java
+++ b/src/com/android/settings/biometrics/face/FaceFeatureProvider.java
@@ -19,6 +19,7 @@ package com.android.settings.biometrics.face;
import android.content.Context;
import android.content.Intent;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/** Feature provider for face unlock */
@@ -32,4 +33,13 @@ public interface FaceFeatureProvider {
/** Returns true if setup wizard supported face enrollment. */
boolean isSetupWizardSupported(Context context);
+
+ /**
+ * Gets the provider for current face enrollment activity classes
+ * @return the provider
+ */
+ @NonNull
+ default FaceEnrollActivityClassProvider getEnrollActivityClassProvider() {
+ return FaceEnrollActivityClassProvider.getInstance();
+ }
}
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsEnrollButtonPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsEnrollButtonPreferenceController.java
index 74133547242..e7badded405 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsEnrollButtonPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsEnrollButtonPreferenceController.java
@@ -16,8 +16,6 @@
package com.android.settings.biometrics.face;
-import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
-
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
@@ -28,6 +26,7 @@ import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.widget.LayoutPreference;
@@ -83,7 +82,8 @@ public class FaceSettingsEnrollButtonPreferenceController extends BasePreference
public void onClick(View v) {
mIsClicked = true;
final Intent intent = new Intent();
- intent.setClassName(SETTINGS_PACKAGE_NAME, FaceEnrollIntroduction.class.getName());
+ intent.setClass(mContext, FeatureFactory.getFeatureFactory().getFaceFeatureProvider()
+ .getEnrollActivityClassProvider().getNext());
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
if (mListener != null) {
diff --git a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollTest.kt b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollTest.kt
new file mode 100644
index 00000000000..e600061b316
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollTest.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.face
+
+import android.app.Activity
+import android.content.Intent
+import com.android.settings.overlay.FeatureFactory
+import com.android.settings.testutils.FakeFeatureFactory
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.robolectric.Robolectric
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.Shadows
+
+@RunWith(RobolectricTestRunner::class)
+class FaceEnrollTest {
+ private lateinit var featureFactory: FeatureFactory
+
+ private companion object {
+ const val INTENT_KEY = "testKey"
+ const val INTENT_VALUE = "testValue"
+ val INTENT = Intent().apply {
+ putExtra(INTENT_KEY, INTENT_VALUE)
+ }
+ }
+
+ private val activityProvider = FaceEnrollActivityClassProvider()
+
+ @Before
+ fun setUp() {
+ featureFactory = FakeFeatureFactory.setupForTest()
+ Mockito.`when`(featureFactory.faceFeatureProvider.enrollActivityClassProvider)
+ .thenReturn(activityProvider)
+ }
+
+ private fun setupActivity(activityClass: Class): FaceEnroll {
+ return Robolectric.buildActivity(activityClass, INTENT).create().get()
+ }
+
+ @Test
+ fun testFinishAndLaunchDefaultActivity() {
+ // Run
+ val activity = setupActivity(FaceEnroll::class.java)
+
+ // Verify
+ verifyLaunchNextActivity(activity, activityProvider.next)
+ }
+
+ private fun verifyLaunchNextActivity(
+ currentActivityInstance : FaceEnroll,
+ nextActivityClass: Class
+ ) {
+ Truth.assertThat(currentActivityInstance.isFinishing).isTrue()
+ val nextActivityIntent = Shadows.shadowOf(currentActivityInstance).nextStartedActivity
+ assertThat(nextActivityIntent.component!!.className).isEqualTo(nextActivityClass.name)
+ assertThat(nextActivityIntent.extras!!.size()).isEqualTo(1)
+ assertThat(nextActivityIntent.getStringExtra(INTENT_KEY)).isEqualTo(INTENT_VALUE)
+ }
+}
\ No newline at end of file