Loading res/layout/fingerprint_v2_udfps_enroll_enrolling.xml 0 → 100644 +70 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ 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. --> <com.google.android.setupdesign.GlifLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/setup_wizard_layout" style="?attr/fingerprint_layout_theme" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout style="@style/SudContentFrame" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:gravity="center|bottom" android:orientation="vertical"> <FrameLayout android:id="@+id/layout_container" android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center_horizontal|bottom" android:clipChildren="false" android:clipToPadding="false" tools:ignore="Suspicious0dp"> <!-- Animation res MUST be set in code --> <com.airbnb.lottie.LottieAnimationView android:id="@+id/illustration_lottie" android:layout_width="200dp" android:layout_height="200dp" android:clipChildren="false" android:clipToPadding="false" android:scaleType="centerInside" app:lottie_autoPlay="true" app:lottie_loop="true" app:lottie_speed=".85" /> </FrameLayout> </LinearLayout> </LinearLayout> </com.google.android.setupdesign.GlifLayout> src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt +11 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,8 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.Finge import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.fragment.RFPSEnrollFragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment.UdfpsEnrollFragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollConfirmationViewModel Loading Loading @@ -100,6 +102,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel private lateinit var fingerprintEnrollConfirmationViewModel: FingerprintEnrollConfirmationViewModel private lateinit var udfpsViewModel: UdfpsViewModel private val coroutineDispatcher = Dispatchers.Default /** Result listener for ChooseLock activity flow. */ Loading Loading @@ -306,6 +309,12 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { ), )[RFPSViewModel::class.java] udfpsViewModel = ViewModelProvider( this, UdfpsViewModel.UdfpsEnrollmentFactory(), )[UdfpsViewModel::class.java] fingerprintEnrollConfirmationViewModel = ViewModelProvider( this, Loading Loading @@ -344,6 +353,8 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { is Enrollment -> { when (step.sensor.sensorType) { FingerprintSensorType.REAR -> RFPSEnrollFragment() FingerprintSensorType.UDFPS_OPTICAL, FingerprintSensorType.UDFPS_ULTRASONIC -> UdfpsEnrollFragment() else -> FingerprintEnrollEnrollingV2Fragment() } } Loading src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment import android.os.Bundle import android.util.Log import android.view.View import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieCompositionFactory import com.android.settings.R import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep import com.google.android.setupdesign.GlifLayout import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enrolling) { /** Used for testing purposes */ private var factory: ViewModelProvider.Factory? = null private val viewModel: UdfpsViewModel by lazy { viewModelProvider[UdfpsViewModel::class.java] } @VisibleForTesting constructor(theFactory: ViewModelProvider.Factory) : this() { factory = theFactory } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val layout = view as GlifLayout val illustrationLottie: LottieAnimationView = layout.findViewById(R.id.illustration_lottie)!! viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.RESUMED) { viewLifecycleOwner.lifecycleScope.launch { viewModel.stageFlow.collect { layout.setHeaderText(getHeaderText(it)) getDescriptionText(it)?.let { descriptionText -> layout.setDescriptionText(descriptionText) } getLottie(it)?.let { lottie -> layout.descriptionText = "" LottieCompositionFactory.fromRawRes(requireContext().applicationContext, lottie) .addListener { comp -> comp?.let { composition -> viewLifecycleOwner.lifecycleScope.launch { illustrationLottie.setComposition(composition) illustrationLottie.visibility = View.VISIBLE illustrationLottie.playAnimation() } } } } } } } } } private fun getHeaderText(stageViewModel: StageViewModel): Int { return when (stageViewModel) { StageViewModel.Center, StageViewModel.Guided, StageViewModel.Fingertip, StageViewModel.Unknown -> R.string.security_settings_udfps_enroll_fingertip_title StageViewModel.LeftEdge -> R.string.security_settings_udfps_enroll_left_edge_title StageViewModel.RightEdge -> R.string.security_settings_udfps_enroll_right_edge_title } } private fun getDescriptionText(stageViewModel: StageViewModel): Int? { return when (stageViewModel) { StageViewModel.Center, StageViewModel.Guided, StageViewModel.Fingertip, StageViewModel.LeftEdge, StageViewModel.RightEdge -> null StageViewModel.Unknown -> R.string.security_settings_udfps_enroll_start_message } } private fun getLottie(stageViewModel: StageViewModel): Int? { return when (stageViewModel) { StageViewModel.Center, StageViewModel.Guided -> R.raw.udfps_center_hint_lottie StageViewModel.Fingertip -> R.raw.udfps_tip_hint_lottie StageViewModel.LeftEdge -> R.raw.udfps_left_edge_hint_lottie StageViewModel.RightEdge -> R.raw.udfps_right_edge_hint_lottie StageViewModel.Unknown -> null } } private val viewModelProvider: ViewModelProvider by lazy { if (factory != null) { ViewModelProvider(requireActivity(), factory!!) } else { ViewModelProvider(requireActivity()) } } companion object { private const val TAG = "UDFPSEnrollFragment" private val navStep = FingerprintNavigationStep.Enrollment::class } } src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/StageViewModel.kt 0 → 100644 +36 −0 Original line number Diff line number Diff line /* * 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel /** * A view model that describes the various stages of UDFPS Enrollment. This stages typically update * the enrollment UI in a major way, such as changing the lottie animation or changing the location * of the where the user should press their fingerprint */ sealed class StageViewModel { data object Unknown : StageViewModel() data object Guided : StageViewModel() data object Center : StageViewModel() data object Fingertip : StageViewModel() data object LeftEdge : StageViewModel() data object RightEdge : StageViewModel() } src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt 0 → 100644 +42 −0 Original line number Diff line number Diff line /* * 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep import kotlinx.coroutines.flow.flowOf /** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */ class UdfpsViewModel() : ViewModel() { /** Indicates what stage UDFPS enrollment is in. */ val stageFlow = flowOf(StageViewModel.Center) class UdfpsEnrollmentFactory() : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { return UdfpsViewModel() as T } } companion object { private val navStep = FingerprintNavigationStep.Enrollment::class private const val TAG = "UDFPSViewModel" } } Loading
res/layout/fingerprint_v2_udfps_enroll_enrolling.xml 0 → 100644 +70 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ 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. --> <com.google.android.setupdesign.GlifLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/setup_wizard_layout" style="?attr/fingerprint_layout_theme" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout style="@style/SudContentFrame" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:gravity="center|bottom" android:orientation="vertical"> <FrameLayout android:id="@+id/layout_container" android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center_horizontal|bottom" android:clipChildren="false" android:clipToPadding="false" tools:ignore="Suspicious0dp"> <!-- Animation res MUST be set in code --> <com.airbnb.lottie.LottieAnimationView android:id="@+id/illustration_lottie" android:layout_width="200dp" android:layout_height="200dp" android:clipChildren="false" android:clipToPadding="false" android:scaleType="centerInside" app:lottie_autoPlay="true" app:lottie_loop="true" app:lottie_speed=".85" /> </FrameLayout> </LinearLayout> </LinearLayout> </com.google.android.setupdesign.GlifLayout>
src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt +11 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,8 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.Finge import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.fragment.RFPSEnrollFragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment.UdfpsEnrollFragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollConfirmationViewModel Loading Loading @@ -100,6 +102,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel private lateinit var fingerprintEnrollConfirmationViewModel: FingerprintEnrollConfirmationViewModel private lateinit var udfpsViewModel: UdfpsViewModel private val coroutineDispatcher = Dispatchers.Default /** Result listener for ChooseLock activity flow. */ Loading Loading @@ -306,6 +309,12 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { ), )[RFPSViewModel::class.java] udfpsViewModel = ViewModelProvider( this, UdfpsViewModel.UdfpsEnrollmentFactory(), )[UdfpsViewModel::class.java] fingerprintEnrollConfirmationViewModel = ViewModelProvider( this, Loading Loading @@ -344,6 +353,8 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { is Enrollment -> { when (step.sensor.sensorType) { FingerprintSensorType.REAR -> RFPSEnrollFragment() FingerprintSensorType.UDFPS_OPTICAL, FingerprintSensorType.UDFPS_ULTRASONIC -> UdfpsEnrollFragment() else -> FingerprintEnrollEnrollingV2Fragment() } } Loading
src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment import android.os.Bundle import android.util.Log import android.view.View import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieCompositionFactory import com.android.settings.R import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep import com.google.android.setupdesign.GlifLayout import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enrolling) { /** Used for testing purposes */ private var factory: ViewModelProvider.Factory? = null private val viewModel: UdfpsViewModel by lazy { viewModelProvider[UdfpsViewModel::class.java] } @VisibleForTesting constructor(theFactory: ViewModelProvider.Factory) : this() { factory = theFactory } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val layout = view as GlifLayout val illustrationLottie: LottieAnimationView = layout.findViewById(R.id.illustration_lottie)!! viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.RESUMED) { viewLifecycleOwner.lifecycleScope.launch { viewModel.stageFlow.collect { layout.setHeaderText(getHeaderText(it)) getDescriptionText(it)?.let { descriptionText -> layout.setDescriptionText(descriptionText) } getLottie(it)?.let { lottie -> layout.descriptionText = "" LottieCompositionFactory.fromRawRes(requireContext().applicationContext, lottie) .addListener { comp -> comp?.let { composition -> viewLifecycleOwner.lifecycleScope.launch { illustrationLottie.setComposition(composition) illustrationLottie.visibility = View.VISIBLE illustrationLottie.playAnimation() } } } } } } } } } private fun getHeaderText(stageViewModel: StageViewModel): Int { return when (stageViewModel) { StageViewModel.Center, StageViewModel.Guided, StageViewModel.Fingertip, StageViewModel.Unknown -> R.string.security_settings_udfps_enroll_fingertip_title StageViewModel.LeftEdge -> R.string.security_settings_udfps_enroll_left_edge_title StageViewModel.RightEdge -> R.string.security_settings_udfps_enroll_right_edge_title } } private fun getDescriptionText(stageViewModel: StageViewModel): Int? { return when (stageViewModel) { StageViewModel.Center, StageViewModel.Guided, StageViewModel.Fingertip, StageViewModel.LeftEdge, StageViewModel.RightEdge -> null StageViewModel.Unknown -> R.string.security_settings_udfps_enroll_start_message } } private fun getLottie(stageViewModel: StageViewModel): Int? { return when (stageViewModel) { StageViewModel.Center, StageViewModel.Guided -> R.raw.udfps_center_hint_lottie StageViewModel.Fingertip -> R.raw.udfps_tip_hint_lottie StageViewModel.LeftEdge -> R.raw.udfps_left_edge_hint_lottie StageViewModel.RightEdge -> R.raw.udfps_right_edge_hint_lottie StageViewModel.Unknown -> null } } private val viewModelProvider: ViewModelProvider by lazy { if (factory != null) { ViewModelProvider(requireActivity(), factory!!) } else { ViewModelProvider(requireActivity()) } } companion object { private const val TAG = "UDFPSEnrollFragment" private val navStep = FingerprintNavigationStep.Enrollment::class } }
src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/StageViewModel.kt 0 → 100644 +36 −0 Original line number Diff line number Diff line /* * 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel /** * A view model that describes the various stages of UDFPS Enrollment. This stages typically update * the enrollment UI in a major way, such as changing the lottie animation or changing the location * of the where the user should press their fingerprint */ sealed class StageViewModel { data object Unknown : StageViewModel() data object Guided : StageViewModel() data object Center : StageViewModel() data object Fingertip : StageViewModel() data object LeftEdge : StageViewModel() data object RightEdge : StageViewModel() }
src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt 0 → 100644 +42 −0 Original line number Diff line number Diff line /* * 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep import kotlinx.coroutines.flow.flowOf /** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */ class UdfpsViewModel() : ViewModel() { /** Indicates what stage UDFPS enrollment is in. */ val stageFlow = flowOf(StageViewModel.Center) class UdfpsEnrollmentFactory() : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { return UdfpsViewModel() as T } } companion object { private val navStep = FingerprintNavigationStep.Enrollment::class private const val TAG = "UDFPSViewModel" } }