Merge "[Biometric Onboarding & Edu] Update Set up Face Unlock page" into main

This commit is contained in:
Shawn Lin
2025-01-08 19:29:47 -08:00
committed by Android (Google) Code Review
11 changed files with 195 additions and 12 deletions

View File

@@ -2830,6 +2830,12 @@
android:screenOrientation="portrait"/>
<activity android:name=".biometrics.face.FaceEnrollIntroduction"
android:exported="false"
android:theme="@style/GlifV4Theme.DayNight"
android:screenOrientation="nosensor">
</activity>
<activity android:name=".biometrics.face.FaceEnroll"
android:exported="true"
android:theme="@style/GlifV4Theme.DayNight"
android:screenOrientation="nosensor">

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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()
}
}

View File

@@ -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<out Activity>
get() = FaceEnrollIntroduction::class.java
companion object {
@JvmStatic
val instance = FaceEnrollActivityClassProvider()
}
}

View File

@@ -625,7 +625,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
updateDescriptionText();
}
private boolean isPrivateProfile() {
protected boolean isPrivateProfile() {
return Utils.isPrivateProfile(mUserId, getApplicationContext());
}
}

View File

@@ -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();
}

View File

@@ -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();
}
}

View File

@@ -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) {

View File

@@ -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<out FaceEnroll>): 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<out Activity>
) {
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)
}
}