UDFPS Enrollment Refactor (4/N)
Accessibility + text/dpi change + rotation should be properly handled. Debug repos were added to make UI developemnt for UDFPS much easier(not requiring calls to fingerprint manager). Change-Id: I89900cea0d9e953124781cdf308fb38858de5d16
This commit is contained in:
@@ -2788,6 +2788,7 @@
|
|||||||
<activity android:name=".biometrics.fingerprint2.ui.enrollment.activity.FingerprintEnrollmentV2Activity"
|
<activity android:name=".biometrics.fingerprint2.ui.enrollment.activity.FingerprintEnrollmentV2Activity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="android.permission.MANAGE_FINGERPRINT"
|
android:permission="android.permission.MANAGE_FINGERPRINT"
|
||||||
|
android:configChanges="density"
|
||||||
android:theme="@style/GlifTheme.Light">
|
android:theme="@style/GlifTheme.Light">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.settings.FINGERPRINT_SETUP" />
|
<action android:name="android.settings.FINGERPRINT_SETUP" />
|
||||||
|
100
res/layout-land/fingerprint_v2_udfps_enroll_enrolling.xml
Normal file
100
res/layout-land/fingerprint_v2_udfps_enroll_enrolling.xml
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/udfps_layout"
|
||||||
|
style="?attr/fingerprint_layout_theme"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<!-- This is used to grab style attributes and apply them
|
||||||
|
to this layout -->
|
||||||
|
<com.google.android.setupdesign.GlifLayout
|
||||||
|
android:id="@+id/dummy_glif_layout"
|
||||||
|
style="?attr/fingerprint_layout_theme"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="300dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/sud_layout_icon"
|
||||||
|
style="@style/SudGlifIcon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scaleType="fitStart"
|
||||||
|
android:src="@drawable/ic_lock" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
style="@style/SudGlifHeaderTitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:lines="2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/description"
|
||||||
|
style="@style/SudDescription.Glif"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:lines="3"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:paddingRight="10dp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<com.airbnb.lottie.LottieAnimationView
|
||||||
|
android:id="@+id/illustration_lottie"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:paddingRight="10dp"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:lottie_autoPlay="true"
|
||||||
|
app:lottie_loop="true"
|
||||||
|
app:lottie_speed=".85"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/layout_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center_horizontal|bottom"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
>
|
||||||
|
|
||||||
|
<include layout="@layout/fingerprint_v2_udfps_enroll_view" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
@@ -18,7 +18,7 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/setup_wizard_layout"
|
android:id="@+id/udfps_layout"
|
||||||
style="?attr/fingerprint_layout_theme"
|
style="?attr/fingerprint_layout_theme"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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.data.repository
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
|
||||||
|
/** Indicates if the developer has debugging features enabled. */
|
||||||
|
interface DebuggingRepository {
|
||||||
|
|
||||||
|
/** A function that will return if a build is debuggable */
|
||||||
|
fun isDebuggingEnabled(): Boolean
|
||||||
|
/** A function that will return if udfps enrollment should be swapped with debug repos */
|
||||||
|
fun isUdfpsEnrollmentDebuggingEnabled(): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
class DebuggingRepositoryImpl : DebuggingRepository {
|
||||||
|
/**
|
||||||
|
* This flag can be flipped by the engineer which should allow for certain debugging features to
|
||||||
|
* be enabled.
|
||||||
|
*/
|
||||||
|
private val isBuildDebuggable = Build.IS_DEBUGGABLE
|
||||||
|
/** This flag indicates if udfps should use debug repos to supply data to its various views. */
|
||||||
|
private val udfpsEnrollmentDebugEnabled = true
|
||||||
|
|
||||||
|
override fun isDebuggingEnabled(): Boolean {
|
||||||
|
return isBuildDebuggable
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isUdfpsEnrollmentDebuggingEnabled(): Boolean {
|
||||||
|
return isDebuggingEnabled() && udfpsEnrollmentDebugEnabled
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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.data.repository
|
||||||
|
|
||||||
|
import android.graphics.Point
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This repository simulates touch events. This is mainly used to debug accessibility and ensure
|
||||||
|
* that talkback is correct.
|
||||||
|
*/
|
||||||
|
interface SimulatedTouchEventsRepository {
|
||||||
|
/**
|
||||||
|
* A flow simulating user touches.
|
||||||
|
*/
|
||||||
|
val touchExplorationDebug: Flow<Point>
|
||||||
|
}
|
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* 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.data.repository
|
||||||
|
|
||||||
|
import android.graphics.Point
|
||||||
|
import android.graphics.Rect
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
|
import com.android.systemui.biometrics.shared.model.FingerprintSensor
|
||||||
|
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
||||||
|
import com.android.systemui.biometrics.shared.model.SensorStrength
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to simulate enroll data. This has two major use cases. 1). Ease of Development
|
||||||
|
* 2). Bug Fixes
|
||||||
|
*/
|
||||||
|
class UdfpsEnrollDebugRepositoryImpl :
|
||||||
|
FingerprintEnrollInteractor, FingerprintSensorRepository, SimulatedTouchEventsRepository {
|
||||||
|
|
||||||
|
override suspend fun enroll(hardwareAuthToken: ByteArray?, enrollReason: EnrollReason) = flow {
|
||||||
|
emit(FingerEnrollState.OverlayShown)
|
||||||
|
delay(200)
|
||||||
|
emit(FingerEnrollState.EnrollHelp(helpMsgId, "Hello world"))
|
||||||
|
delay(200)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(15, 16))
|
||||||
|
delay(300)
|
||||||
|
emit(FingerEnrollState.EnrollHelp(helpMsgId, "Hello world"))
|
||||||
|
delay(1000)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(14, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(13, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(12, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(11, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(10, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(9, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(8, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(7, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(6, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(5, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(4, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(3, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(2, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(1, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(0, 16))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Provides touch events to the UdfpsEnrollFragment */
|
||||||
|
override val touchExplorationDebug: Flow<Point> = flow {
|
||||||
|
delay(2000)
|
||||||
|
emit(pointToLeftOfSensor(sensorRect))
|
||||||
|
delay(2000)
|
||||||
|
emit(pointBelowSensor(sensorRect))
|
||||||
|
delay(2000)
|
||||||
|
emit(pointToRightOfSensor(sensorRect))
|
||||||
|
delay(2000)
|
||||||
|
emit(pointAboveSensor(sensorRect))
|
||||||
|
}
|
||||||
|
|
||||||
|
override val fingerprintSensor: Flow<FingerprintSensor> = flowOf(sensorProps)
|
||||||
|
|
||||||
|
private fun pointToLeftOfSensor(sensorLocation: Rect) =
|
||||||
|
Point(sensorLocation.right + 5, sensorLocation.centerY())
|
||||||
|
|
||||||
|
private fun pointToRightOfSensor(sensorLocation: Rect) =
|
||||||
|
Point(sensorLocation.left - 5, sensorLocation.centerY())
|
||||||
|
|
||||||
|
private fun pointBelowSensor(sensorLocation: Rect) =
|
||||||
|
Point(sensorLocation.centerX(), sensorLocation.bottom + 5)
|
||||||
|
|
||||||
|
private fun pointAboveSensor(sensorLocation: Rect) =
|
||||||
|
Point(sensorLocation.centerX(), sensorLocation.top - 5)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val helpMsgId: Int = 1
|
||||||
|
private val sensorLocationInternal = Pair(540, 1713)
|
||||||
|
private val sensorRadius = 100
|
||||||
|
private val sensorRect =
|
||||||
|
Rect(
|
||||||
|
this.sensorLocationInternal.first - sensorRadius,
|
||||||
|
this.sensorLocationInternal.second - sensorRadius,
|
||||||
|
this.sensorLocationInternal.first + sensorRadius,
|
||||||
|
this.sensorLocationInternal.second + sensorRadius,
|
||||||
|
)
|
||||||
|
val sensorProps =
|
||||||
|
FingerprintSensor(
|
||||||
|
1,
|
||||||
|
SensorStrength.STRONG,
|
||||||
|
5,
|
||||||
|
FingerprintSensorType.UDFPS_OPTICAL,
|
||||||
|
sensorRect,
|
||||||
|
sensorRadius,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.domain.interactor
|
||||||
|
|
||||||
|
import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepository
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
|
||||||
|
/** Interactor indicating if certain debug flows are enabled. */
|
||||||
|
interface DebuggingInteractor {
|
||||||
|
/** This indicates that certain debug flows are enabled. */
|
||||||
|
val debuggingEnabled: Flow<Boolean>
|
||||||
|
/** This indicates if udfps should instead use debug repos to supply data to its various views. */
|
||||||
|
val udfpsEnrollmentDebuggingEnabled: Flow<Boolean>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interactor essentially forwards the [DebuggingRepository]
|
||||||
|
*/
|
||||||
|
class DebuggingInteractorImpl(val debuggingRepository: DebuggingRepository) : DebuggingInteractor {
|
||||||
|
override val debuggingEnabled: Flow<Boolean> = flow {
|
||||||
|
emit(debuggingRepository.isDebuggingEnabled())
|
||||||
|
}
|
||||||
|
override val udfpsEnrollmentDebuggingEnabled: Flow<Boolean> = flow {
|
||||||
|
emit(debuggingRepository.isUdfpsEnrollmentDebuggingEnabled())
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* 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.domain.interactor
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.flow.shareIn
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible for handling updates to fontScale and displayDensity and forwarding
|
||||||
|
* these events to classes that need them
|
||||||
|
*/
|
||||||
|
interface DisplayDensityInteractor {
|
||||||
|
/** Indicates the display density has been updated. */
|
||||||
|
fun updateDisplayDensity(density: Int)
|
||||||
|
|
||||||
|
/** Indicates the font scale has been updates. */
|
||||||
|
fun updateFontScale(fontScale: Float)
|
||||||
|
|
||||||
|
/** A flow that propagates fontscale. */
|
||||||
|
val fontScale: Flow<Float>
|
||||||
|
|
||||||
|
/** A flow that propagates displayDensity. */
|
||||||
|
val displayDensity: Flow<Int>
|
||||||
|
|
||||||
|
/** A flow that propagates the default display density. */
|
||||||
|
val defaultDisplayDensity: Flow<Int>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the [DisplayDensityInteractor]. This interactor is used to forward activity
|
||||||
|
* information to the rest of the application.
|
||||||
|
*/
|
||||||
|
class DisplayDensityInteractorImpl(
|
||||||
|
currentFontScale: Float,
|
||||||
|
currentDisplayDensity: Int,
|
||||||
|
defaultDisplayDensity: Int,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
) : DisplayDensityInteractor {
|
||||||
|
override fun updateDisplayDensity(density: Int) {
|
||||||
|
_displayDensity.update { density }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateFontScale(fontScale: Float) {
|
||||||
|
_fontScale.update { fontScale }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _fontScale = MutableStateFlow(currentFontScale)
|
||||||
|
private val _displayDensity = MutableStateFlow(currentDisplayDensity)
|
||||||
|
|
||||||
|
override val fontScale: Flow<Float> = _fontScale.asStateFlow()
|
||||||
|
|
||||||
|
override val displayDensity: Flow<Int> = _displayDensity.asStateFlow()
|
||||||
|
|
||||||
|
override val defaultDisplayDensity: Flow<Int> =
|
||||||
|
flowOf(defaultDisplayDensity).shareIn(scope, SharingStarted.Eagerly, 1)
|
||||||
|
}
|
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.domain.interactor
|
||||||
|
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
||||||
|
typealias EnrollStageThresholds = Map<Float, StageViewModel>
|
||||||
|
|
||||||
|
/** Interactor that provides enroll stages for enrollment. */
|
||||||
|
interface EnrollStageInteractor {
|
||||||
|
|
||||||
|
/** Provides enroll stages for enrollment. */
|
||||||
|
val enrollStageThresholds: Flow<EnrollStageThresholds>
|
||||||
|
}
|
||||||
|
|
||||||
|
class EnrollStageInteractorImpl() : EnrollStageInteractor {
|
||||||
|
override val enrollStageThresholds: Flow<EnrollStageThresholds> =
|
||||||
|
flowOf(
|
||||||
|
mapOf(
|
||||||
|
0.0f to StageViewModel.Center,
|
||||||
|
0.25f to StageViewModel.Guided,
|
||||||
|
0.5f to StageViewModel.Fingertip,
|
||||||
|
0.75f to StageViewModel.LeftEdge,
|
||||||
|
0.875f to StageViewModel.RightEdge,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* 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.domain.interactor
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.hardware.fingerprint.FingerprintEnrollOptions
|
||||||
|
import android.hardware.fingerprint.FingerprintManager
|
||||||
|
import android.os.CancellationSignal
|
||||||
|
import android.util.Log
|
||||||
|
import com.android.settings.biometrics.fingerprint2.conversion.Util.toEnrollError
|
||||||
|
import com.android.settings.biometrics.fingerprint2.conversion.Util.toOriginalReason
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.channels.onFailure
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
|
/** This repository is responsible for collecting all state related to the enroll API. */
|
||||||
|
interface FingerprintEnrollInteractor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By calling this function, [fingerEnrollState] will begin to be populated with data on success.
|
||||||
|
*/
|
||||||
|
suspend fun enroll(
|
||||||
|
hardwareAuthToken: ByteArray?,
|
||||||
|
enrollReason: EnrollReason,
|
||||||
|
): Flow<FingerEnrollState>
|
||||||
|
}
|
||||||
|
|
||||||
|
class FingerprintEnrollInteractorImpl(
|
||||||
|
private val applicationContext: Context,
|
||||||
|
private val fingerprintEnrollOptions: FingerprintEnrollOptions,
|
||||||
|
private val fingerprintManager: FingerprintManager,
|
||||||
|
private val fingerprintFlow: FingerprintFlow,
|
||||||
|
) : FingerprintEnrollInteractor {
|
||||||
|
private val enrollRequestOutstanding = MutableStateFlow(false)
|
||||||
|
|
||||||
|
override suspend fun enroll(
|
||||||
|
hardwareAuthToken: ByteArray?,
|
||||||
|
enrollReason: EnrollReason,
|
||||||
|
): Flow<FingerEnrollState> = callbackFlow {
|
||||||
|
// TODO (b/308456120) Improve this logic
|
||||||
|
if (enrollRequestOutstanding.value) {
|
||||||
|
Log.d(TAG, "Outstanding enroll request, waiting 150ms")
|
||||||
|
delay(150)
|
||||||
|
if (enrollRequestOutstanding.value) {
|
||||||
|
Log.e(TAG, "Request still present, continuing")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enrollRequestOutstanding.update { true }
|
||||||
|
|
||||||
|
var streamEnded = false
|
||||||
|
var totalSteps: Int? = null
|
||||||
|
val enrollmentCallback =
|
||||||
|
object : FingerprintManager.EnrollmentCallback() {
|
||||||
|
override fun onEnrollmentProgress(remaining: Int) {
|
||||||
|
// This is sort of an implementation detail, but unfortunately the API isn't
|
||||||
|
// very expressive. If anything we should look at changing the FingerprintManager API.
|
||||||
|
if (totalSteps == null) {
|
||||||
|
totalSteps = remaining + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
trySend(FingerEnrollState.EnrollProgress(remaining, totalSteps!!)).onFailure { error ->
|
||||||
|
Log.d(TAG, "onEnrollmentProgress($remaining) failed to send, due to $error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining == 0) {
|
||||||
|
streamEnded = true
|
||||||
|
enrollRequestOutstanding.update { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onEnrollmentHelp(helpMsgId: Int, helpString: CharSequence?) {
|
||||||
|
trySend(FingerEnrollState.EnrollHelp(helpMsgId, helpString.toString())).onFailure { error
|
||||||
|
->
|
||||||
|
Log.d(TAG, "onEnrollmentHelp failed to send, due to $error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onEnrollmentError(errMsgId: Int, errString: CharSequence?) {
|
||||||
|
trySend(errMsgId.toEnrollError(fingerprintFlow == SetupWizard)).onFailure { error ->
|
||||||
|
Log.d(TAG, "onEnrollmentError failed to send, due to $error")
|
||||||
|
}
|
||||||
|
Log.d(TAG, "onEnrollmentError($errMsgId)")
|
||||||
|
streamEnded = true
|
||||||
|
enrollRequestOutstanding.update { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val cancellationSignal = CancellationSignal()
|
||||||
|
|
||||||
|
fingerprintManager.enroll(
|
||||||
|
hardwareAuthToken,
|
||||||
|
cancellationSignal,
|
||||||
|
applicationContext.userId,
|
||||||
|
enrollmentCallback,
|
||||||
|
enrollReason.toOriginalReason(),
|
||||||
|
fingerprintEnrollOptions,
|
||||||
|
)
|
||||||
|
awaitClose {
|
||||||
|
// If the stream has not been ended, and the user has stopped collecting the flow
|
||||||
|
// before it was over, send cancel.
|
||||||
|
if (!streamEnded) {
|
||||||
|
Log.e(TAG, "Cancel is sent from settings for enroll()")
|
||||||
|
cancellationSignal.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "FingerprintEnrollStateRepository"
|
||||||
|
}
|
||||||
|
}
|
@@ -18,43 +18,25 @@ package com.android.settings.biometrics.fingerprint2.domain.interactor
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.hardware.biometrics.BiometricConstants;
|
|
||||||
import android.hardware.biometrics.BiometricFingerprintConstants
|
|
||||||
import android.hardware.biometrics.SensorLocationInternal
|
|
||||||
import android.hardware.fingerprint.FingerprintEnrollOptions;
|
|
||||||
import android.hardware.fingerprint.FingerprintManager
|
import android.hardware.fingerprint.FingerprintManager
|
||||||
import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback
|
import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback
|
||||||
import android.hardware.fingerprint.FingerprintManager.RemovalCallback
|
import android.hardware.fingerprint.FingerprintManager.RemovalCallback
|
||||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
|
|
||||||
import android.os.CancellationSignal
|
import android.os.CancellationSignal
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
||||||
import com.android.settings.biometrics.BiometricUtils
|
|
||||||
import com.android.settings.biometrics.fingerprint2.conversion.Util.toEnrollError
|
|
||||||
import com.android.settings.biometrics.fingerprint2.conversion.Util.toOriginalReason
|
|
||||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
|
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
|
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
|
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper
|
import com.android.settings.password.ChooseLockSettingsHelper
|
||||||
import com.android.systemui.biometrics.shared.model.toFingerprintSensor
|
|
||||||
import com.google.android.setupcompat.util.WizardManagerHelper
|
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
import kotlinx.coroutines.CancellableContinuation
|
import kotlinx.coroutines.CancellableContinuation
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
|
||||||
import kotlinx.coroutines.channels.onFailure
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.update
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@@ -66,9 +48,7 @@ class FingerprintManagerInteractorImpl(
|
|||||||
private val fingerprintManager: FingerprintManager,
|
private val fingerprintManager: FingerprintManager,
|
||||||
fingerprintSensorRepository: FingerprintSensorRepository,
|
fingerprintSensorRepository: FingerprintSensorRepository,
|
||||||
private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
|
private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
|
||||||
private val pressToAuthInteractor: PressToAuthInteractor,
|
private val fingerprintEnrollStateRepository: FingerprintEnrollInteractor,
|
||||||
private val fingerprintFlow: FingerprintFlow,
|
|
||||||
private val intent: Intent,
|
|
||||||
) : FingerprintManagerInteractor {
|
) : FingerprintManagerInteractor {
|
||||||
|
|
||||||
private val maxFingerprints =
|
private val maxFingerprints =
|
||||||
@@ -77,7 +57,6 @@ class FingerprintManagerInteractorImpl(
|
|||||||
)
|
)
|
||||||
private val applicationContext = applicationContext.applicationContext
|
private val applicationContext = applicationContext.applicationContext
|
||||||
|
|
||||||
private val enrollRequestOutstanding = MutableStateFlow(false)
|
|
||||||
|
|
||||||
override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray> =
|
override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray> =
|
||||||
suspendCoroutine {
|
suspendCoroutine {
|
||||||
@@ -113,85 +92,8 @@ class FingerprintManagerInteractorImpl(
|
|||||||
|
|
||||||
override val maxEnrollableFingerprints = flow { emit(maxFingerprints) }
|
override val maxEnrollableFingerprints = flow { emit(maxFingerprints) }
|
||||||
|
|
||||||
override suspend fun enroll(
|
override suspend fun enroll(hardwareAuthToken: ByteArray?, enrollReason: EnrollReason): Flow<FingerEnrollState> =
|
||||||
hardwareAuthToken: ByteArray?,
|
fingerprintEnrollStateRepository.enroll(hardwareAuthToken, enrollReason)
|
||||||
enrollReason: EnrollReason,
|
|
||||||
): Flow<FingerEnrollState> = callbackFlow {
|
|
||||||
// TODO (b/308456120) Improve this logic
|
|
||||||
if (enrollRequestOutstanding.value) {
|
|
||||||
Log.d(TAG, "Outstanding enroll request, waiting 150ms")
|
|
||||||
delay(150)
|
|
||||||
if (enrollRequestOutstanding.value) {
|
|
||||||
Log.e(TAG, "Request still present, continuing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enrollRequestOutstanding.update { true }
|
|
||||||
|
|
||||||
var streamEnded = false
|
|
||||||
var totalSteps: Int? = null
|
|
||||||
val enrollmentCallback =
|
|
||||||
object : FingerprintManager.EnrollmentCallback() {
|
|
||||||
override fun onEnrollmentProgress(remaining: Int) {
|
|
||||||
// This is sort of an implementation detail, but unfortunately the API isn't
|
|
||||||
// very expressive. If anything we should look at changing the FingerprintManager API.
|
|
||||||
if (totalSteps == null) {
|
|
||||||
totalSteps = remaining + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
trySend(FingerEnrollState.EnrollProgress(remaining, totalSteps!!)).onFailure { error ->
|
|
||||||
Log.d(TAG, "onEnrollmentProgress($remaining) failed to send, due to $error")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remaining == 0) {
|
|
||||||
streamEnded = true
|
|
||||||
enrollRequestOutstanding.update { false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onEnrollmentHelp(helpMsgId: Int, helpString: CharSequence?) {
|
|
||||||
trySend(FingerEnrollState.EnrollHelp(helpMsgId, helpString.toString())).onFailure { error
|
|
||||||
->
|
|
||||||
Log.d(TAG, "onEnrollmentHelp failed to send, due to $error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onEnrollmentError(errMsgId: Int, errString: CharSequence?) {
|
|
||||||
trySend(errMsgId.toEnrollError(fingerprintFlow == SetupWizard)).onFailure { error ->
|
|
||||||
Log.d(TAG, "onEnrollmentError failed to send, due to $error")
|
|
||||||
}
|
|
||||||
Log.d(TAG, "onEnrollmentError($errMsgId)")
|
|
||||||
streamEnded = true
|
|
||||||
enrollRequestOutstanding.update { false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val cancellationSignal = CancellationSignal()
|
|
||||||
|
|
||||||
if (intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) === -1) {
|
|
||||||
val isSuw: Boolean = WizardManagerHelper.isAnySetupWizard(intent)
|
|
||||||
intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
|
|
||||||
if (isSuw) FingerprintEnrollOptions.ENROLL_REASON_SUW else
|
|
||||||
FingerprintEnrollOptions.ENROLL_REASON_SETTINGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
fingerprintManager.enroll(
|
|
||||||
hardwareAuthToken,
|
|
||||||
cancellationSignal,
|
|
||||||
applicationContext.userId,
|
|
||||||
enrollmentCallback,
|
|
||||||
enrollReason.toOriginalReason(),
|
|
||||||
toFingerprintEnrollOptions(intent)
|
|
||||||
)
|
|
||||||
awaitClose {
|
|
||||||
// If the stream has not been ended, and the user has stopped collecting the flow
|
|
||||||
// before it was over, send cancel.
|
|
||||||
if (!streamEnded) {
|
|
||||||
Log.e(TAG, "Cancel is sent from settings for enroll()")
|
|
||||||
cancellationSignal.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun removeFingerprint(fp: FingerprintData): Boolean = suspendCoroutine {
|
override suspend fun removeFingerprint(fp: FingerprintData): Boolean = suspendCoroutine {
|
||||||
val callback =
|
val callback =
|
||||||
@@ -263,14 +165,4 @@ class FingerprintManagerInteractorImpl(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toFingerprintEnrollOptions(intent: Intent): FingerprintEnrollOptions {
|
|
||||||
val reason: Int =
|
|
||||||
intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)
|
|
||||||
val builder: FingerprintEnrollOptions.Builder = FingerprintEnrollOptions.Builder()
|
|
||||||
builder.setEnrollReason(FingerprintEnrollOptions.ENROLL_REASON_UNKNOWN)
|
|
||||||
if (reason != -1) {
|
|
||||||
builder.setEnrollReason(reason)
|
|
||||||
}
|
|
||||||
return builder.build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
import android.view.OrientationEventListener
|
import android.view.OrientationEventListener
|
||||||
import com.android.internal.R
|
import com.android.internal.R
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -24,16 +25,23 @@ import kotlinx.coroutines.channels.awaitClose
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.shareIn
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.flow.transform
|
||||||
|
|
||||||
/**
|
/** Interactor which provides information about orientation */
|
||||||
* Interactor which provides information about orientation
|
|
||||||
*/
|
|
||||||
interface OrientationInteractor {
|
interface OrientationInteractor {
|
||||||
/** A flow that contains the information about the orientation changing */
|
/** A flow that contains the information about the orientation changing */
|
||||||
val orientation: Flow<Int>
|
val orientation: Flow<Int>
|
||||||
/** A flow that contains the rotation info */
|
/**
|
||||||
|
* A flow that contains the rotation info
|
||||||
|
*/
|
||||||
val rotation: Flow<Int>
|
val rotation: Flow<Int>
|
||||||
|
/**
|
||||||
|
* A flow that contains the rotation info matched against the def [config_reverseDefaultRotation]
|
||||||
|
*/
|
||||||
|
val rotationFromDefault: Flow<Int>
|
||||||
/**
|
/**
|
||||||
* A Helper function that computes rotation if device is in
|
* A Helper function that computes rotation if device is in
|
||||||
* [R.bool.config_reverseDefaultConfigRotation]
|
* [R.bool.config_reverseDefaultConfigRotation]
|
||||||
@@ -53,24 +61,11 @@ class OrientationInteractorImpl(private val context: Context, activityScope: Cor
|
|||||||
}
|
}
|
||||||
orientationEventListener.enable()
|
orientationEventListener.enable()
|
||||||
awaitClose { orientationEventListener.disable() }
|
awaitClose { orientationEventListener.disable() }
|
||||||
}
|
}.shareIn(activityScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
override val rotation: Flow<Int> =
|
override val rotation: Flow<Int> = orientation.transform { emit(context.display!!.rotation) }
|
||||||
callbackFlow {
|
|
||||||
val orientationEventListener =
|
override val rotationFromDefault: Flow<Int> = rotation.map { getRotationFromDefault(it) }
|
||||||
object : OrientationEventListener(context) {
|
|
||||||
override fun onOrientationChanged(orientation: Int) {
|
|
||||||
trySend(getRotationFromDefault(context.display!!.rotation))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
orientationEventListener.enable()
|
|
||||||
awaitClose { orientationEventListener.disable() }
|
|
||||||
}
|
|
||||||
.stateIn(
|
|
||||||
activityScope, // This is tied to the activity scope
|
|
||||||
SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener
|
|
||||||
context.display!!.rotation,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun getRotationFromDefault(rotation: Int): Int {
|
override fun getRotationFromDefault(rotation: Int): Int {
|
||||||
val isReverseDefaultRotation =
|
val isReverseDefaultRotation =
|
||||||
|
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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.domain.interactor
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Process
|
||||||
|
import android.os.VibrationAttributes
|
||||||
|
import android.os.VibrationEffect
|
||||||
|
import android.os.Vibrator
|
||||||
|
|
||||||
|
/** Indicates the possible vibration effects for fingerprint enrollment */
|
||||||
|
sealed class FingerprintVibrationEffects {
|
||||||
|
/** A vibration indicating an error */
|
||||||
|
data object UdfpsError : FingerprintVibrationEffects()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A vibration indicating success, this usually occurs when progress on the UDFPS enrollment has
|
||||||
|
* been made
|
||||||
|
*/
|
||||||
|
data object UdfpsSuccess : FingerprintVibrationEffects()
|
||||||
|
|
||||||
|
/** This vibration typically occurs when a help message is shown during UDFPS enrollment */
|
||||||
|
data object UdfpsHelp : FingerprintVibrationEffects()
|
||||||
|
}
|
||||||
|
/** Interface for sending haptic feedback */
|
||||||
|
interface VibrationInteractor {
|
||||||
|
/** This will send a haptic vibration */
|
||||||
|
fun vibrate(effect: FingerprintVibrationEffects, caller: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implementation of the VibrationInteractor interface */
|
||||||
|
class VibrationInteractorImpl(val vibrator: Vibrator, val applicationContext: Context) :
|
||||||
|
VibrationInteractor {
|
||||||
|
override fun vibrate(effect: FingerprintVibrationEffects, caller: String) {
|
||||||
|
val callerString = "$caller::$effect"
|
||||||
|
val res =
|
||||||
|
when (effect) {
|
||||||
|
FingerprintVibrationEffects.UdfpsHelp,
|
||||||
|
FingerprintVibrationEffects.UdfpsError ->
|
||||||
|
Pair(VIBRATE_EFFECT_ERROR, FINGERPRINT_ENROLLING_SONIFICATION_ATTRIBUTES)
|
||||||
|
FingerprintVibrationEffects.UdfpsSuccess ->
|
||||||
|
Pair(VIBRATE_EFFECT_SUCCESS, HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES)
|
||||||
|
}
|
||||||
|
vibrator.vibrate(
|
||||||
|
Process.myUid(),
|
||||||
|
applicationContext.opPackageName,
|
||||||
|
res.first,
|
||||||
|
callerString,
|
||||||
|
res.second,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val VIBRATE_EFFECT_ERROR = VibrationEffect.createWaveform(longArrayOf(0, 5, 55, 60), -1)
|
||||||
|
private val FINGERPRINT_ENROLLING_SONIFICATION_ATTRIBUTES =
|
||||||
|
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ACCESSIBILITY)
|
||||||
|
private val HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
|
||||||
|
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK)
|
||||||
|
private val VIBRATE_EFFECT_SUCCESS = VibrationEffect.get(VibrationEffect.EFFECT_CLICK)
|
||||||
|
}
|
||||||
|
}
|
@@ -57,8 +57,7 @@ interface FingerprintManagerInteractor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs [FingerprintManager.enroll] with the [hardwareAuthToken] and [EnrollReason] for this
|
* Runs [FingerprintManager.enroll] with the [hardwareAuthToken] and [EnrollReason] for this
|
||||||
* enrollment. Returning the [FingerEnrollState] that represents this fingerprint enrollment
|
* enrollment. If successful data in the [fingerprintEnrollState] should be populated.
|
||||||
* state.
|
|
||||||
*/
|
*/
|
||||||
suspend fun enroll(
|
suspend fun enroll(
|
||||||
hardwareAuthToken: ByteArray?,
|
hardwareAuthToken: ByteArray?,
|
||||||
|
@@ -42,4 +42,16 @@ sealed class FingerEnrollState {
|
|||||||
val shouldRetryEnrollment: Boolean,
|
val shouldRetryEnrollment: Boolean,
|
||||||
val isCancelled: Boolean,
|
val isCancelled: Boolean,
|
||||||
) : FingerEnrollState()
|
) : FingerEnrollState()
|
||||||
|
|
||||||
|
/** Indicates an acquired event has occurred */
|
||||||
|
data class Acquired(val acquiredGood: Boolean) : FingerEnrollState()
|
||||||
|
|
||||||
|
/** Indicates a pointer down event has occurred */
|
||||||
|
data object PointerDown : FingerEnrollState()
|
||||||
|
|
||||||
|
/** Indicates a pointer up event has occurred */
|
||||||
|
data object PointerUp : FingerEnrollState()
|
||||||
|
|
||||||
|
/** Indicates the overlay has shown */
|
||||||
|
data object OverlayShown : FingerEnrollState()
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
package com.android.settings.biometrics.fingerprint2.lib.model
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A view model that describes the various stages of UDFPS Enrollment. This stages typically update
|
* A view model that describes the various stages of UDFPS Enrollment. This stages typically update
|
@@ -19,8 +19,10 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.activity
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.hardware.fingerprint.FingerprintEnrollOptions
|
||||||
import android.hardware.fingerprint.FingerprintManager
|
import android.hardware.fingerprint.FingerprintManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Vibrator
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.accessibility.AccessibilityManager
|
import android.view.accessibility.AccessibilityManager
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
@@ -35,21 +37,35 @@ import com.android.settings.Utils.SETTINGS_PACKAGE_NAME
|
|||||||
import com.android.settings.biometrics.BiometricEnrollBase
|
import com.android.settings.biometrics.BiometricEnrollBase
|
||||||
import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
|
import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
|
||||||
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
|
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
|
||||||
|
import com.android.settings.biometrics.BiometricUtils
|
||||||
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
||||||
|
import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepositoryImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
|
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
|
||||||
|
import com.android.settings.biometrics.fingerprint2.data.repository.UdfpsEnrollDebugRepositoryImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractorImpl
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractorImpl
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractorImpl
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.Default
|
import com.android.settings.biometrics.fingerprint2.lib.model.Default
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.Settings
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
|
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollEnrollingV2Fragment
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollEnrollingV2Fragment
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.RfpsEnrollFindSensorFragment
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.SfpsEnrollFindSensorFragment
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.UdfpsEnrollFindSensorFragment
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions
|
||||||
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.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.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.fragment.UdfpsEnrollFragment
|
||||||
@@ -77,6 +93,7 @@ import com.android.settings.flags.Flags
|
|||||||
import com.android.settings.password.ChooseLockGeneric
|
import com.android.settings.password.ChooseLockGeneric
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper
|
import com.android.settings.password.ChooseLockSettingsHelper
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE
|
import com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE
|
||||||
|
import com.android.settingslib.display.DisplayDensityUtils
|
||||||
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
||||||
import com.google.android.setupcompat.util.WizardManagerHelper
|
import com.google.android.setupcompat.util.WizardManagerHelper
|
||||||
import com.google.android.setupdesign.util.ThemeHelper
|
import com.google.android.setupdesign.util.ThemeHelper
|
||||||
@@ -95,14 +112,17 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
private lateinit var navigationViewModel: FingerprintNavigationViewModel
|
private lateinit var navigationViewModel: FingerprintNavigationViewModel
|
||||||
private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
|
private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
|
||||||
private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
|
private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
|
||||||
|
private lateinit var vibrationInteractor: VibrationInteractor
|
||||||
private lateinit var foldStateInteractor: FoldStateInteractor
|
private lateinit var foldStateInteractor: FoldStateInteractor
|
||||||
private lateinit var orientationInteractor: OrientationInteractor
|
private lateinit var orientationInteractor: OrientationInteractor
|
||||||
|
private lateinit var displayDensityInteractor: DisplayDensityInteractor
|
||||||
private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
|
private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
|
||||||
private lateinit var backgroundViewModel: BackgroundViewModel
|
private lateinit var backgroundViewModel: BackgroundViewModel
|
||||||
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
|
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
|
||||||
private lateinit var fingerprintEnrollConfirmationViewModel:
|
private lateinit var fingerprintEnrollConfirmationViewModel:
|
||||||
FingerprintEnrollConfirmationViewModel
|
FingerprintEnrollConfirmationViewModel
|
||||||
private lateinit var udfpsViewModel: UdfpsViewModel
|
private lateinit var udfpsViewModel: UdfpsViewModel
|
||||||
|
private lateinit var enrollStageInteractor: EnrollStageInteractor
|
||||||
private val coroutineDispatcher = Dispatchers.Default
|
private val coroutineDispatcher = Dispatchers.Default
|
||||||
|
|
||||||
/** Result listener for ChooseLock activity flow. */
|
/** Result listener for ChooseLock activity flow. */
|
||||||
@@ -135,6 +155,12 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
super.onConfigurationChanged(newConfig)
|
super.onConfigurationChanged(newConfig)
|
||||||
foldStateInteractor.onConfigurationChange(newConfig)
|
foldStateInteractor.onConfigurationChange(newConfig)
|
||||||
|
val displayDensityUtils = DisplayDensityUtils(applicationContext)
|
||||||
|
val currIndex = displayDensityUtils.currentIndexForDefaultDisplay
|
||||||
|
displayDensityInteractor.updateFontScale(resources.configuration.fontScale)
|
||||||
|
displayDensityInteractor.updateDisplayDensity(
|
||||||
|
displayDensityUtils.defaultDisplayDensityValues[currIndex]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onConfirmDevice(resultCode: Int, data: Intent?) {
|
private fun onConfirmDevice(resultCode: Int, data: Intent?) {
|
||||||
@@ -193,10 +219,43 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
fingerprintFlowViewModel =
|
fingerprintFlowViewModel =
|
||||||
ViewModelProvider(this, FingerprintFlowViewModel.FingerprintFlowViewModelFactory(enrollType))[
|
ViewModelProvider(this, FingerprintFlowViewModel.FingerprintFlowViewModelFactory(enrollType))[
|
||||||
FingerprintFlowViewModel::class.java]
|
FingerprintFlowViewModel::class.java]
|
||||||
|
val displayDensityUtils = DisplayDensityUtils(context)
|
||||||
|
val currIndex = displayDensityUtils.currentIndexForDefaultDisplay
|
||||||
|
val defaultDisplayDensity = displayDensityUtils.defaultDensityForDefaultDisplay
|
||||||
|
displayDensityInteractor =
|
||||||
|
DisplayDensityInteractorImpl(
|
||||||
|
resources.configuration.fontScale,
|
||||||
|
displayDensityUtils.defaultDisplayDensityValues[currIndex],
|
||||||
|
defaultDisplayDensity,
|
||||||
|
lifecycleScope,
|
||||||
|
)
|
||||||
|
|
||||||
|
val debuggingRepo = DebuggingRepositoryImpl()
|
||||||
|
val debuggingInteractor = DebuggingInteractorImpl(debuggingRepo)
|
||||||
|
val udfpsEnrollDebugRepositoryImpl = UdfpsEnrollDebugRepositoryImpl()
|
||||||
|
|
||||||
val fingerprintSensorRepo =
|
val fingerprintSensorRepo =
|
||||||
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
|
if (debuggingRepo.isUdfpsEnrollmentDebuggingEnabled()) udfpsEnrollDebugRepositoryImpl
|
||||||
val pressToAuthInteractor = PressToAuthInteractorImpl(context, backgroundDispatcher)
|
else FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
|
||||||
|
|
||||||
|
if (intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) === -1) {
|
||||||
|
val isSuw: Boolean = WizardManagerHelper.isAnySetupWizard(intent)
|
||||||
|
intent.putExtra(
|
||||||
|
BiometricUtils.EXTRA_ENROLL_REASON,
|
||||||
|
if (isSuw) FingerprintEnrollOptions.ENROLL_REASON_SUW
|
||||||
|
else FingerprintEnrollOptions.ENROLL_REASON_SETTINGS,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val fingerprintEnrollStateRepository =
|
||||||
|
if (debuggingRepo.isUdfpsEnrollmentDebuggingEnabled()) udfpsEnrollDebugRepositoryImpl
|
||||||
|
else
|
||||||
|
FingerprintEnrollInteractorImpl(
|
||||||
|
context.applicationContext,
|
||||||
|
intent.toFingerprintEnrollOptions(),
|
||||||
|
fingerprintManager,
|
||||||
|
Settings,
|
||||||
|
)
|
||||||
|
|
||||||
val fingerprintManagerInteractor =
|
val fingerprintManagerInteractor =
|
||||||
FingerprintManagerInteractorImpl(
|
FingerprintManagerInteractorImpl(
|
||||||
@@ -205,12 +264,10 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
fingerprintManager,
|
fingerprintManager,
|
||||||
fingerprintSensorRepo,
|
fingerprintSensorRepo,
|
||||||
GatekeeperPasswordProvider(LockPatternUtils(context)),
|
GatekeeperPasswordProvider(LockPatternUtils(context)),
|
||||||
pressToAuthInteractor,
|
fingerprintEnrollStateRepository,
|
||||||
enrollType,
|
|
||||||
getIntent(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var challenge: Long? = intent.getExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE) as Long?
|
var challenge = intent.getExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE) as Long?
|
||||||
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
|
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
|
||||||
val gatekeeperInfo = FingerprintGatekeeperViewModel.toGateKeeperInfo(challenge, token)
|
val gatekeeperInfo = FingerprintGatekeeperViewModel.toGateKeeperInfo(challenge, token)
|
||||||
|
|
||||||
@@ -256,6 +313,8 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
foldStateInteractor.onConfigurationChange(resources.configuration)
|
foldStateInteractor.onConfigurationChange(resources.configuration)
|
||||||
|
|
||||||
orientationInteractor = OrientationInteractorImpl(context, lifecycleScope)
|
orientationInteractor = OrientationInteractorImpl(context, lifecycleScope)
|
||||||
|
vibrationInteractor =
|
||||||
|
VibrationInteractorImpl(context.getSystemService(Vibrator::class.java)!!, context)
|
||||||
|
|
||||||
// Initialize FingerprintViewModel
|
// Initialize FingerprintViewModel
|
||||||
fingerprintEnrollViewModel =
|
fingerprintEnrollViewModel =
|
||||||
@@ -309,10 +368,23 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
),
|
),
|
||||||
)[RFPSViewModel::class.java]
|
)[RFPSViewModel::class.java]
|
||||||
|
|
||||||
|
enrollStageInteractor = EnrollStageInteractorImpl()
|
||||||
|
|
||||||
udfpsViewModel =
|
udfpsViewModel =
|
||||||
ViewModelProvider(
|
ViewModelProvider(
|
||||||
this,
|
this,
|
||||||
UdfpsViewModel.UdfpsEnrollmentFactory(),
|
UdfpsViewModel.UdfpsEnrollmentFactory(
|
||||||
|
vibrationInteractor,
|
||||||
|
displayDensityInteractor,
|
||||||
|
navigationViewModel,
|
||||||
|
debuggingInteractor,
|
||||||
|
fingerprintEnrollEnrollingViewModel,
|
||||||
|
udfpsEnrollDebugRepositoryImpl,
|
||||||
|
enrollStageInteractor,
|
||||||
|
orientationInteractor,
|
||||||
|
backgroundViewModel,
|
||||||
|
fingerprintSensorRepo,
|
||||||
|
),
|
||||||
)[UdfpsViewModel::class.java]
|
)[UdfpsViewModel::class.java]
|
||||||
|
|
||||||
fingerprintEnrollConfirmationViewModel =
|
fingerprintEnrollConfirmationViewModel =
|
||||||
@@ -348,7 +420,12 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
when (step) {
|
when (step) {
|
||||||
Confirmation -> FingerprintEnrollConfirmationV2Fragment()
|
Confirmation -> FingerprintEnrollConfirmationV2Fragment()
|
||||||
is Education -> {
|
is Education -> {
|
||||||
FingerprintEnrollFindSensorV2Fragment(step.sensor.sensorType)
|
when (step.sensor.sensorType) {
|
||||||
|
FingerprintSensorType.REAR -> RfpsEnrollFindSensorFragment()
|
||||||
|
FingerprintSensorType.UDFPS_OPTICAL,
|
||||||
|
FingerprintSensorType.UDFPS_ULTRASONIC -> UdfpsEnrollFindSensorFragment()
|
||||||
|
else -> SfpsEnrollFindSensorFragment()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is Enrollment -> {
|
is Enrollment -> {
|
||||||
when (step.sensor.sensorType) {
|
when (step.sensor.sensorType) {
|
||||||
@@ -370,7 +447,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
supportFragmentManager
|
supportFragmentManager
|
||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
.setReorderingAllowed(true)
|
.setReorderingAllowed(true)
|
||||||
.add(R.id.fragment_container_view, theClass, null)
|
.add(R.id.fragment_container_view, theClass::class.java, null)
|
||||||
.commit()
|
.commit()
|
||||||
navigationViewModel.update(
|
navigationViewModel.update(
|
||||||
FingerprintAction.TRANSITION_FINISHED,
|
FingerprintAction.TRANSITION_FINISHED,
|
||||||
@@ -386,7 +463,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
navigationViewModel.shouldFinish.filterNotNull().collect {
|
navigationViewModel.shouldFinish.filterNotNull().collect {
|
||||||
Log.d(TAG, "FingerprintSettingsNav.finishing($it)")
|
Log.d(TAG, "FingerprintSettingsNav.finishing($it)")
|
||||||
if (it.result != null) {
|
if (it.result != null) {
|
||||||
finishActivity(it.result as Int)
|
finishActivity(it.result)
|
||||||
} else {
|
} else {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* 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.fragment.education
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
|
||||||
|
import com.android.settings.biometrics.fingerprint.FingerprintFindSensorAnimation
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
|
||||||
|
import com.google.android.setupcompat.template.FooterBarMixin
|
||||||
|
import com.google.android.setupcompat.template.FooterButton
|
||||||
|
import com.google.android.setupdesign.GlifLayout
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fragment that is used to educate the user about the rear fingerprint sensor on this device.
|
||||||
|
*
|
||||||
|
* The main goals of this page are
|
||||||
|
* 1. Inform the user where the fingerprint sensor is on their device
|
||||||
|
* 2. Explain to the user how the enrollment process shown by [FingerprintEnrollEnrollingV2Fragment]
|
||||||
|
* will work.
|
||||||
|
*/
|
||||||
|
class RfpsEnrollFindSensorFragment() : Fragment() {
|
||||||
|
/** Used for testing purposes */
|
||||||
|
private var factory: ViewModelProvider.Factory? = null
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
constructor(theFactory: ViewModelProvider.Factory) : this() {
|
||||||
|
factory = theFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
private val viewModelProvider: ViewModelProvider by lazy {
|
||||||
|
if (factory != null) {
|
||||||
|
ViewModelProvider(requireActivity(), factory!!)
|
||||||
|
} else {
|
||||||
|
ViewModelProvider(requireActivity())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var animation: FingerprintFindSensorAnimation? = null
|
||||||
|
|
||||||
|
private val viewModel: FingerprintEnrollFindSensorViewModel by lazy {
|
||||||
|
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?,
|
||||||
|
): View? {
|
||||||
|
val view =
|
||||||
|
inflater.inflate(R.layout.fingerprint_v2_enroll_find_sensor, container, false)!! as GlifLayout
|
||||||
|
view.setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title)
|
||||||
|
view.setDescriptionText(R.string.security_settings_fingerprint_enroll_find_sensor_message)
|
||||||
|
|
||||||
|
// Set up footer bar
|
||||||
|
val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
|
||||||
|
setupSecondaryButton(footerBarMixin)
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) }
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.showRfpsAnimation.collect {
|
||||||
|
animation = view.findViewById(R.id.fingerprint_sensor_location_animation)
|
||||||
|
animation!!.startAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.showErrorDialog.collect { (errMsgId, isSetup) ->
|
||||||
|
// TODO: Covert error dialog kotlin as well
|
||||||
|
FingerprintErrorDialog.showErrorDialog(requireActivity(), errMsgId, isSetup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
animation?.stopAnimation()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupSecondaryButton(footerBarMixin: FooterBarMixin) {
|
||||||
|
footerBarMixin.secondaryButton =
|
||||||
|
FooterButton.Builder(requireActivity())
|
||||||
|
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
|
||||||
|
.setListener { viewModel.secondaryButtonClicked() }
|
||||||
|
.setButtonType(FooterButton.ButtonType.SKIP)
|
||||||
|
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupPrimaryButton(footerBarMixin: FooterBarMixin) {
|
||||||
|
footerBarMixin.primaryButton =
|
||||||
|
FooterButton.Builder(requireActivity())
|
||||||
|
.setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
|
||||||
|
.setListener {
|
||||||
|
Log.d(TAG, "onStartButtonClick")
|
||||||
|
viewModel.proceedToEnrolling()
|
||||||
|
}
|
||||||
|
.setButtonType(FooterButton.ButtonType.NEXT)
|
||||||
|
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "RfpsEnrollFindSensor"
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* 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.fragment.education
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Surface
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.airbnb.lottie.LottieAnimationView
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
|
||||||
|
import com.google.android.setupcompat.template.FooterBarMixin
|
||||||
|
import com.google.android.setupcompat.template.FooterButton
|
||||||
|
import com.google.android.setupdesign.GlifLayout
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fragment that is used to educate the user about the side fingerprint sensor on this device.
|
||||||
|
*
|
||||||
|
* The main goals of this page are
|
||||||
|
* 1. Inform the user where the fingerprint sensor is on their device
|
||||||
|
* 2. Explain to the user how the enrollment process shown by [FingerprintEnrollEnrollingV2Fragment]
|
||||||
|
* will work.
|
||||||
|
*/
|
||||||
|
class SfpsEnrollFindSensorFragment() : Fragment() {
|
||||||
|
/** Used for testing purposes */
|
||||||
|
private var factory: ViewModelProvider.Factory? = null
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
constructor(theFactory: ViewModelProvider.Factory) : this() {
|
||||||
|
factory = theFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
private val viewModelProvider: ViewModelProvider by lazy {
|
||||||
|
if (factory != null) {
|
||||||
|
ViewModelProvider(requireActivity(), factory!!)
|
||||||
|
} else {
|
||||||
|
ViewModelProvider(requireActivity())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val viewModel: FingerprintEnrollFindSensorViewModel by lazy {
|
||||||
|
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?,
|
||||||
|
): View? {
|
||||||
|
val view =
|
||||||
|
inflater.inflate(R.layout.sfps_enroll_find_sensor_layout, container, false)!! as GlifLayout
|
||||||
|
view.setHeaderText(R.string.security_settings_sfps_enroll_find_sensor_title)
|
||||||
|
view.setDescriptionText(R.string.security_settings_sfps_enroll_find_sensor_message)
|
||||||
|
|
||||||
|
// Set up footer bar
|
||||||
|
val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
|
||||||
|
setupSecondaryButton(footerBarMixin)
|
||||||
|
|
||||||
|
// Set up lottie
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.sfpsLottieInfo.collect { (isFolded, rotation) ->
|
||||||
|
setupLottie(view, getSfpsIllustrationLottieAnimation(isFolded, rotation))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) }
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.showErrorDialog.collect { (errMsgId, isSetup) ->
|
||||||
|
// TODO: Covert error dialog kotlin as well
|
||||||
|
FingerprintErrorDialog.showErrorDialog(requireActivity(), errMsgId, isSetup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupSecondaryButton(footerBarMixin: FooterBarMixin) {
|
||||||
|
footerBarMixin.secondaryButton =
|
||||||
|
FooterButton.Builder(requireActivity())
|
||||||
|
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
|
||||||
|
.setListener { viewModel.secondaryButtonClicked() }
|
||||||
|
.setButtonType(FooterButton.ButtonType.SKIP)
|
||||||
|
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupPrimaryButton(footerBarMixin: FooterBarMixin) {
|
||||||
|
footerBarMixin.primaryButton =
|
||||||
|
FooterButton.Builder(requireActivity())
|
||||||
|
.setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
|
||||||
|
.setListener {
|
||||||
|
Log.d(TAG, "onStartButtonClick")
|
||||||
|
viewModel.proceedToEnrolling()
|
||||||
|
}
|
||||||
|
.setButtonType(FooterButton.ButtonType.NEXT)
|
||||||
|
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSfpsIllustrationLottieAnimation(isFolded: Boolean, rotation: Int): Int {
|
||||||
|
val animation: Int
|
||||||
|
when (rotation) {
|
||||||
|
Surface.ROTATION_90 ->
|
||||||
|
animation =
|
||||||
|
(if (isFolded) R.raw.fingerprint_edu_lottie_folded_top_left
|
||||||
|
else R.raw.fingerprint_edu_lottie_portrait_top_left)
|
||||||
|
Surface.ROTATION_180 ->
|
||||||
|
animation =
|
||||||
|
(if (isFolded) R.raw.fingerprint_edu_lottie_folded_bottom_left
|
||||||
|
else R.raw.fingerprint_edu_lottie_landscape_bottom_left)
|
||||||
|
Surface.ROTATION_270 ->
|
||||||
|
animation =
|
||||||
|
(if (isFolded) R.raw.fingerprint_edu_lottie_folded_bottom_right
|
||||||
|
else R.raw.fingerprint_edu_lottie_portrait_bottom_right)
|
||||||
|
else ->
|
||||||
|
animation =
|
||||||
|
(if (isFolded) R.raw.fingerprint_edu_lottie_folded_top_right
|
||||||
|
else R.raw.fingerprint_edu_lottie_landscape_top_right)
|
||||||
|
}
|
||||||
|
return animation
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupLottie(
|
||||||
|
view: View,
|
||||||
|
lottieAnimation: Int,
|
||||||
|
lottieClickListener: View.OnClickListener? = null,
|
||||||
|
) {
|
||||||
|
val illustrationLottie: LottieAnimationView? = view.findViewById(R.id.illustration_lottie)
|
||||||
|
illustrationLottie?.setAnimation(lottieAnimation)
|
||||||
|
illustrationLottie?.playAnimation()
|
||||||
|
illustrationLottie?.setOnClickListener(lottieClickListener)
|
||||||
|
illustrationLottie?.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "SfpsEnrollFindSensor"
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2023 The Android Open Source Project
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -29,36 +29,27 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import com.airbnb.lottie.LottieAnimationView
|
import com.airbnb.lottie.LottieAnimationView
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
|
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintFindSensorAnimation
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
|
||||||
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
|
||||||
import com.google.android.setupcompat.template.FooterBarMixin
|
import com.google.android.setupcompat.template.FooterBarMixin
|
||||||
import com.google.android.setupcompat.template.FooterButton
|
import com.google.android.setupcompat.template.FooterButton
|
||||||
import com.google.android.setupdesign.GlifLayout
|
import com.google.android.setupdesign.GlifLayout
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
private const val TAG = "FingerprintEnrollFindSensorV2Fragment"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A fragment that is used to educate the user about the fingerprint sensor on this device.
|
* A fragment that is used to educate the user about the under display fingerprint sensor on this
|
||||||
*
|
* device.
|
||||||
* If the sensor is not a udfps sensor, this fragment listens to fingerprint enrollment for
|
|
||||||
* proceeding to the enroll enrolling.
|
|
||||||
*
|
*
|
||||||
* The main goals of this page are
|
* The main goals of this page are
|
||||||
* 1. Inform the user where the fingerprint sensor is on their device
|
* 1. Inform the user where the fingerprint sensor is on their device
|
||||||
* 2. Explain to the user how the enrollment process shown by [FingerprintEnrollEnrollingV2Fragment]
|
* 2. Explain to the user how the enrollment process shown by [FingerprintEnrollEnrollingV2Fragment]
|
||||||
* will work.
|
* will work.
|
||||||
*/
|
*/
|
||||||
class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorType) : Fragment() {
|
class UdfpsEnrollFindSensorFragment() : Fragment() {
|
||||||
/** Used for testing purposes */
|
/** Used for testing purposes */
|
||||||
private var factory: ViewModelProvider.Factory? = null
|
private var factory: ViewModelProvider.Factory? = null
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
constructor(
|
constructor(theFactory: ViewModelProvider.Factory) : this() {
|
||||||
sensorType: FingerprintSensorType,
|
|
||||||
theFactory: ViewModelProvider.Factory,
|
|
||||||
) : this(sensorType) {
|
|
||||||
factory = theFactory
|
factory = theFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,10 +61,6 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is only for non-udfps or non-sfps sensor. For udfps and sfps, we show lottie.
|
|
||||||
private var animation: FingerprintFindSensorAnimation? = null
|
|
||||||
|
|
||||||
private var contentLayoutId: Int = -1
|
|
||||||
private val viewModel: FingerprintEnrollFindSensorViewModel by lazy {
|
private val viewModel: FingerprintEnrollFindSensorViewModel by lazy {
|
||||||
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
|
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
|
||||||
}
|
}
|
||||||
@@ -83,31 +70,18 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
|
|||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?,
|
savedInstanceState: Bundle?,
|
||||||
): View? {
|
): View? {
|
||||||
|
val view =
|
||||||
contentLayoutId =
|
inflater.inflate(R.layout.udfps_enroll_find_sensor_layout, container, false)!! as GlifLayout
|
||||||
when (sensorType) {
|
view.setHeaderText(R.string.security_settings_udfps_enroll_find_sensor_title)
|
||||||
FingerprintSensorType.UDFPS_OPTICAL,
|
view.setDescriptionText(R.string.security_settings_udfps_enroll_find_sensor_message)
|
||||||
FingerprintSensorType.UDFPS_ULTRASONIC -> R.layout.udfps_enroll_find_sensor_layout
|
|
||||||
FingerprintSensorType.POWER_BUTTON -> R.layout.sfps_enroll_find_sensor_layout
|
|
||||||
else -> R.layout.fingerprint_v2_enroll_find_sensor
|
|
||||||
}
|
|
||||||
|
|
||||||
val view = inflater.inflate(contentLayoutId, container, false)!! as GlifLayout
|
|
||||||
setTexts(sensorType, view)
|
|
||||||
|
|
||||||
// Set up footer bar
|
// Set up footer bar
|
||||||
val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
|
val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
|
||||||
setupSecondaryButton(footerBarMixin)
|
setupSecondaryButton(footerBarMixin)
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) }
|
viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up lottie or animation
|
|
||||||
lifecycleScope.launch {
|
|
||||||
viewModel.sfpsLottieInfo.collect { (isFolded, rotation) ->
|
|
||||||
setupLottie(view, getSfpsIllustrationLottieAnimation(isFolded, rotation))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewModel.udfpsLottieInfo.collect { isAccessibilityEnabled ->
|
viewModel.udfpsLottieInfo.collect { isAccessibilityEnabled ->
|
||||||
val lottieAnimation =
|
val lottieAnimation =
|
||||||
@@ -115,12 +89,6 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
|
|||||||
setupLottie(view, lottieAnimation) { viewModel.proceedToEnrolling() }
|
setupLottie(view, lottieAnimation) { viewModel.proceedToEnrolling() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
|
||||||
viewModel.showRfpsAnimation.collect {
|
|
||||||
animation = view.findViewById(R.id.fingerprint_sensor_location_animation)
|
|
||||||
animation!!.startAnimation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewModel.showErrorDialog.collect { (errMsgId, isSetup) ->
|
viewModel.showErrorDialog.collect { (errMsgId, isSetup) ->
|
||||||
@@ -131,11 +99,6 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
|
|||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
animation?.stopAnimation()
|
|
||||||
super.onDestroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupSecondaryButton(footerBarMixin: FooterBarMixin) {
|
private fun setupSecondaryButton(footerBarMixin: FooterBarMixin) {
|
||||||
footerBarMixin.secondaryButton =
|
footerBarMixin.secondaryButton =
|
||||||
FooterButton.Builder(requireActivity())
|
FooterButton.Builder(requireActivity())
|
||||||
@@ -159,36 +122,6 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupLottie(
|
|
||||||
view: View,
|
|
||||||
lottieAnimation: Int,
|
|
||||||
lottieClickListener: View.OnClickListener? = null,
|
|
||||||
) {
|
|
||||||
val illustrationLottie: LottieAnimationView? = view.findViewById(R.id.illustration_lottie)
|
|
||||||
illustrationLottie?.setAnimation(lottieAnimation)
|
|
||||||
illustrationLottie?.playAnimation()
|
|
||||||
illustrationLottie?.setOnClickListener(lottieClickListener)
|
|
||||||
illustrationLottie?.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setTexts(sensorType: FingerprintSensorType?, view: GlifLayout) {
|
|
||||||
when (sensorType) {
|
|
||||||
FingerprintSensorType.UDFPS_OPTICAL,
|
|
||||||
FingerprintSensorType.UDFPS_ULTRASONIC -> {
|
|
||||||
view.setHeaderText(R.string.security_settings_udfps_enroll_find_sensor_title)
|
|
||||||
view.setDescriptionText(R.string.security_settings_udfps_enroll_find_sensor_message)
|
|
||||||
}
|
|
||||||
FingerprintSensorType.POWER_BUTTON -> {
|
|
||||||
view.setHeaderText(R.string.security_settings_sfps_enroll_find_sensor_title)
|
|
||||||
view.setDescriptionText(R.string.security_settings_sfps_enroll_find_sensor_message)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
view.setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title)
|
|
||||||
view.setDescriptionText(R.string.security_settings_fingerprint_enroll_find_sensor_message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getSfpsIllustrationLottieAnimation(isFolded: Boolean, rotation: Int): Int {
|
private fun getSfpsIllustrationLottieAnimation(isFolded: Boolean, rotation: Int): Int {
|
||||||
val animation: Int
|
val animation: Int
|
||||||
when (rotation) {
|
when (rotation) {
|
||||||
@@ -211,4 +144,20 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
|
|||||||
}
|
}
|
||||||
return animation
|
return animation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setupLottie(
|
||||||
|
view: View,
|
||||||
|
lottieAnimation: Int,
|
||||||
|
lottieClickListener: View.OnClickListener? = null,
|
||||||
|
) {
|
||||||
|
val illustrationLottie: LottieAnimationView? = view.findViewById(R.id.illustration_lottie)
|
||||||
|
illustrationLottie?.setAnimation(lottieAnimation)
|
||||||
|
illustrationLottie?.playAnimation()
|
||||||
|
illustrationLottie?.setOnClickListener(lottieClickListener)
|
||||||
|
illustrationLottie?.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "UdfpsEnrollFindSensor"
|
||||||
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.util
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.hardware.fingerprint.FingerprintEnrollOptions
|
||||||
|
import com.android.settings.biometrics.BiometricUtils
|
||||||
|
|
||||||
|
fun Intent.toFingerprintEnrollOptions(): FingerprintEnrollOptions {
|
||||||
|
val reason: Int = this.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)
|
||||||
|
val builder: FingerprintEnrollOptions.Builder = FingerprintEnrollOptions.Builder()
|
||||||
|
builder.setEnrollReason(FingerprintEnrollOptions.ENROLL_REASON_UNKNOWN)
|
||||||
|
if (reason != -1) {
|
||||||
|
builder.setEnrollReason(reason)
|
||||||
|
}
|
||||||
|
return builder.build()
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2023 The Android Open Source Project
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.widget
|
||||||
|
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
@@ -29,8 +29,6 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment
|
|||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
||||||
private const val TAG = "FingerprintErrorDialog"
|
|
||||||
|
|
||||||
/** A Dialog used for fingerprint enrollment when an error occurs. */
|
/** A Dialog used for fingerprint enrollment when an error occurs. */
|
||||||
class FingerprintErrorDialog : InstrumentedDialogFragment() {
|
class FingerprintErrorDialog : InstrumentedDialogFragment() {
|
||||||
private lateinit var onContinue: DialogInterface.OnClickListener
|
private lateinit var onContinue: DialogInterface.OnClickListener
|
||||||
@@ -82,6 +80,7 @@ class FingerprintErrorDialog : InstrumentedDialogFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val TAG = "FingerprintErrorDialog"
|
||||||
private const val KEY_MESSAGE = "fingerprint_message"
|
private const val KEY_MESSAGE = "fingerprint_message"
|
||||||
private const val KEY_TITLE = "fingerprint_title"
|
private const val KEY_TITLE = "fingerprint_title"
|
||||||
private const val KEY_SHOULD_TRY_AGAIN = "should_try_again"
|
private const val KEY_SHOULD_TRY_AGAIN = "should_try_again"
|
@@ -34,9 +34,9 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.widget.FingerprintErrorDialog
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSIconTouchViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSIconTouchViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.FingerprintErrorDialog
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.IconTouchDialog
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.IconTouchDialog
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.RFPSProgressBar
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.RFPSProgressBar
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
|
||||||
|
@@ -43,7 +43,7 @@ class RFPSViewModel(
|
|||||||
orientationInteractor: OrientationInteractor,
|
orientationInteractor: OrientationInteractor,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _textViewIsVisible = MutableStateFlow<Boolean>(false)
|
private val _textViewIsVisible = MutableStateFlow(false)
|
||||||
/** Value to indicate if the text view is visible or not */
|
/** Value to indicate if the text view is visible or not */
|
||||||
val textViewIsVisible: Flow<Boolean> = _textViewIsVisible.asStateFlow()
|
val textViewIsVisible: Flow<Boolean> = _textViewIsVisible.asStateFlow()
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ class RFPSViewModel(
|
|||||||
/** Indicates if the icon should be animating or not */
|
/** Indicates if the icon should be animating or not */
|
||||||
val shouldAnimateIcon = _shouldAnimateIcon
|
val shouldAnimateIcon = _shouldAnimateIcon
|
||||||
|
|
||||||
private var enrollFlow: Flow<FingerEnrollState?> = fingerprintEnrollViewModel.enrollFLow
|
private var enrollFlow: Flow<FingerEnrollState?> = fingerprintEnrollViewModel.enrollFlow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enroll progress message with a replay of size 1 allowing for new subscribers to get the most
|
* Enroll progress message with a replay of size 1 allowing for new subscribers to get the most
|
||||||
@@ -142,7 +142,7 @@ class RFPSViewModel(
|
|||||||
_textViewIsVisible.update { false }
|
_textViewIsVisible.update { false }
|
||||||
_shouldAnimateIcon = fingerprintEnrollViewModel.enrollFlowShouldBeRunning
|
_shouldAnimateIcon = fingerprintEnrollViewModel.enrollFlowShouldBeRunning
|
||||||
/** Indicates if the icon should be animating or not */
|
/** Indicates if the icon should be animating or not */
|
||||||
enrollFlow = fingerprintEnrollViewModel.enrollFLow
|
enrollFlow = fingerprintEnrollViewModel.enrollFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
class RFPSViewModelFactory(
|
class RFPSViewModelFactory(
|
||||||
|
@@ -18,6 +18,8 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrol
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.MotionEvent.ACTION_HOVER_MOVE
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
@@ -30,10 +32,12 @@ import androidx.lifecycle.repeatOnLifecycle
|
|||||||
import com.airbnb.lottie.LottieAnimationView
|
import com.airbnb.lottie.LottieAnimationView
|
||||||
import com.airbnb.lottie.LottieCompositionFactory
|
import com.airbnb.lottie.LottieCompositionFactory
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.widget.FingerprintErrorDialog
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.DescriptionText
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.DescriptionText
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.HeaderText
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.EducationAnimationModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.EducationAnimationModel
|
||||||
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.HeaderText
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget.UdfpsEnrollViewV2
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget.UdfpsEnrollViewV2
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
||||||
@@ -47,6 +51,7 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
private var factory: ViewModelProvider.Factory? = null
|
private var factory: ViewModelProvider.Factory? = null
|
||||||
private val viewModel: UdfpsViewModel by lazy { viewModelProvider[UdfpsViewModel::class.java] }
|
private val viewModel: UdfpsViewModel by lazy { viewModelProvider[UdfpsViewModel::class.java] }
|
||||||
private lateinit var udfpsEnrollView: UdfpsEnrollViewV2
|
private lateinit var udfpsEnrollView: UdfpsEnrollViewV2
|
||||||
|
private lateinit var lottie: LottieAnimationView
|
||||||
|
|
||||||
private val viewModelProvider: ViewModelProvider by lazy {
|
private val viewModelProvider: ViewModelProvider by lazy {
|
||||||
if (factory != null) {
|
if (factory != null) {
|
||||||
@@ -63,7 +68,8 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val illustrationLottie: LottieAnimationView = view.findViewById(R.id.illustration_lottie)!!
|
val fragment = this
|
||||||
|
lottie = view.findViewById(R.id.illustration_lottie)!!
|
||||||
udfpsEnrollView = view.findViewById(R.id.udfps_animation_view)!!
|
udfpsEnrollView = view.findViewById(R.id.udfps_animation_view)!!
|
||||||
val titleTextView = view.findViewById<TextView>(R.id.title)!!
|
val titleTextView = view.findViewById<TextView>(R.id.title)!!
|
||||||
val descriptionTextView = view.findViewById<TextView>(R.id.description)!!
|
val descriptionTextView = view.findViewById<TextView>(R.id.description)!!
|
||||||
@@ -79,6 +85,11 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
launch {
|
||||||
|
viewModel.sensorLocation.collect { sensor ->
|
||||||
|
udfpsEnrollView.setSensorRect(sensor.sensorBounds, sensor.sensorType)
|
||||||
|
}
|
||||||
|
}
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewModel.headerText.collect { titleTextView.setText(it.toResource()) }
|
viewModel.headerText.collect { titleTextView.setText(it.toResource()) }
|
||||||
}
|
}
|
||||||
@@ -92,35 +103,59 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewModel.sensorLocation.collect { rect -> udfpsEnrollView.setSensorRect(rect) }
|
viewModel.shouldShowLottie.collect {
|
||||||
|
lottie.visibility = if (it) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
viewModel.accessibilityEnabled.collect { isEnabled -> udfpsEnrollView.setAccessibilityEnabled(isEnabled) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewModel.lottie.collect { lottieModel ->
|
viewModel.lottie.collect { lottieModel ->
|
||||||
|
if (lottie.visibility == View.GONE) {
|
||||||
|
return@collect
|
||||||
|
}
|
||||||
val resource = lottieModel.toResource()
|
val resource = lottieModel.toResource()
|
||||||
if (resource != null) {
|
if (resource != null) {
|
||||||
LottieCompositionFactory.fromRawRes(requireContext(), resource).addListener { comp ->
|
LottieCompositionFactory.fromRawRes(requireContext(), resource).addListener { comp ->
|
||||||
comp?.let { composition ->
|
comp?.let { composition ->
|
||||||
illustrationLottie.setComposition(composition)
|
lottie.setComposition(composition)
|
||||||
illustrationLottie.visibility = View.VISIBLE
|
lottie.visibility = View.VISIBLE
|
||||||
illustrationLottie.playAnimation()
|
lottie.playAnimation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
illustrationLottie.visibility = View.INVISIBLE
|
lottie.visibility = View.INVISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewModel.udfpsEvent.collect {
|
repeatOnLifecycle(Lifecycle.State.DESTROYED) { viewModel.stopEnrollment() }
|
||||||
|
}
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewModel.accessibilityEnabled.collect { enabled ->
|
||||||
|
udfpsEnrollView.setAccessibilityEnabled(enabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewModel.enrollState.collect {
|
||||||
Log.d(TAG, "EnrollEvent $it")
|
Log.d(TAG, "EnrollEvent $it")
|
||||||
udfpsEnrollView.onUdfpsEvent(it) }
|
if (it is FingerEnrollState.EnrollError) {
|
||||||
|
try {
|
||||||
|
FingerprintErrorDialog.showInstance(it, fragment)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Log.e(TAG, "Exception occurred $exception")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
udfpsEnrollView.onUdfpsEvent(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewModel.progressSaved.collect { udfpsEnrollView.onEnrollProgressSaved(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
@@ -128,6 +163,15 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewModel.touchExplorationDebug.collect {
|
||||||
|
udfpsEnrollView.sendDebugTouchExplorationEvent(
|
||||||
|
MotionEvent.obtain(100, 100, ACTION_HOVER_MOVE, it.x.toFloat(), it.y.toFloat(), 0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
viewModel.readyForEnrollment()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun HeaderText.toResource(): Int {
|
private fun HeaderText.toResource(): Int {
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
||||||
|
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
|
||||||
/** Represents the description text for UDFPS enrollment */
|
/** Represents the description text for UDFPS enrollment */
|
||||||
data class DescriptionText(
|
data class DescriptionText(
|
||||||
val isSuw: Boolean,
|
val isSuw: Boolean,
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
||||||
|
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
|
||||||
/** Represents the lottie for UDFPS enrollment */
|
/** Represents the lottie for UDFPS enrollment */
|
||||||
data class EducationAnimationModel(
|
data class EducationAnimationModel(
|
||||||
val isSuw: Boolean,
|
val isSuw: Boolean,
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
||||||
|
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
|
||||||
/** Represents the header text for UDFPS enrollment */
|
/** Represents the header text for UDFPS enrollment */
|
||||||
data class HeaderText(
|
data class HeaderText(
|
||||||
val isSuw: Boolean,
|
val isSuw: Boolean,
|
||||||
|
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 class indicating a udfps enroll event occurred. */
|
|
||||||
sealed class UdfpsEnrollEvent
|
|
||||||
|
|
||||||
/** Describes how many [remainingSteps] and how many [totalSteps] are left in udfps enrollment. */
|
|
||||||
data class UdfpsProgress(val remainingSteps: Int, val totalSteps: Int) : UdfpsEnrollEvent()
|
|
||||||
|
|
||||||
/** Indicates a help event has been sent by enrollment */
|
|
||||||
data class UdfpsHelp(val helpMsgId: Int, val helpString: String) : UdfpsEnrollEvent()
|
|
||||||
|
|
||||||
/** Indicates a error event has been sent by enrollment */
|
|
||||||
data class UdfpsError(val errMsgId: Int, val errString: String) : UdfpsEnrollEvent()
|
|
||||||
|
|
||||||
/** Indicates an acquired event has occurred */
|
|
||||||
data class Acquired(val acquiredGood: Boolean) : UdfpsEnrollEvent()
|
|
||||||
|
|
||||||
/** Indicates a pointer down event has occurred */
|
|
||||||
data object PointerDown : UdfpsEnrollEvent()
|
|
||||||
|
|
||||||
/** Indicates a pointer up event has occurred */
|
|
||||||
data object PointerUp : UdfpsEnrollEvent()
|
|
||||||
|
|
||||||
/** Indicates the overlay has shown */
|
|
||||||
data object OverlayShown : UdfpsEnrollEvent()
|
|
@@ -16,71 +16,172 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
||||||
|
|
||||||
import android.graphics.Rect
|
import android.graphics.Point
|
||||||
|
import android.view.Surface
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
||||||
|
import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintVibrationEffects
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
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.FingerprintEnrollEnrollingViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
||||||
import kotlinx.coroutines.Dispatchers
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.combineTransform
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.filterIsInstance
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.shareIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */
|
/** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */
|
||||||
class UdfpsViewModel() : ViewModel() {
|
class UdfpsViewModel(
|
||||||
|
val vibrationInteractor: VibrationInteractor,
|
||||||
|
displayDensityInteractor: DisplayDensityInteractor,
|
||||||
|
val navigationViewModel: FingerprintNavigationViewModel,
|
||||||
|
debuggingInteractor: DebuggingInteractor,
|
||||||
|
val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||||
|
simulatedTouchEventsDebugRepository: SimulatedTouchEventsRepository,
|
||||||
|
enrollStageInteractor: EnrollStageInteractor,
|
||||||
|
orientationInteractor: OrientationInteractor,
|
||||||
|
backgroundViewModel: BackgroundViewModel,
|
||||||
|
sensorRepository: FingerprintSensorRepository,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
private val isSetupWizard = flowOf(false)
|
private val isSetupWizard = flowOf(false)
|
||||||
|
|
||||||
/** Indicates which Enrollment stage we are currently in. */
|
private var _enrollState: Flow<FingerEnrollState?> =
|
||||||
private val sensorLocationInternal = Pair(540, 1713)
|
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||||
private val sensorRadius = 100
|
/** The current state of the enrollment. */
|
||||||
private val sensorRect =
|
var enrollState: Flow<FingerEnrollState> =
|
||||||
Rect(
|
combine(fingerprintEnrollEnrollingViewModel.enrollFlowShouldBeRunning, _enrollState) {
|
||||||
this.sensorLocationInternal.first - sensorRadius,
|
shouldBeRunning,
|
||||||
this.sensorLocationInternal.second - sensorRadius,
|
state ->
|
||||||
this.sensorLocationInternal.first + sensorRadius,
|
if (shouldBeRunning) {
|
||||||
this.sensorLocationInternal.second + sensorRadius,
|
state
|
||||||
)
|
|
||||||
|
|
||||||
private val stageThresholds = flowOf(listOf(.25, .5, .75, .875))
|
|
||||||
|
|
||||||
/** Indicates if accessibility is enabled */
|
|
||||||
val accessibilityEnabled = flowOf(false)
|
|
||||||
|
|
||||||
/** Indicates the locates of the fingerprint sensor. */
|
|
||||||
val sensorLocation: Flow<Rect> = flowOf(sensorRect)
|
|
||||||
|
|
||||||
/** This is currently not hooked up to fingerprint manager, and is being fed mock events. */
|
|
||||||
val udfpsEvent: Flow<UdfpsEnrollEvent> =
|
|
||||||
flow {
|
|
||||||
enrollEvents.forEach { events ->
|
|
||||||
events.forEach { event -> emit(event) }
|
|
||||||
delay(1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
/** Determines the current [StageViewModel] enrollment is in */
|
|
||||||
val enrollStage: Flow<StageViewModel> =
|
|
||||||
combine(stageThresholds, udfpsEvent) { thresholds, event ->
|
|
||||||
if (event is UdfpsProgress) {
|
|
||||||
thresholdToStageMap(thresholds, event.totalSteps - event.remainingSteps, event.totalSteps)
|
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forwards the property sensor information. This is typically used to recreate views that must be
|
||||||
|
* aligned with the sensor.
|
||||||
|
*/
|
||||||
|
val sensorLocation = sensorRepository.fingerprintSensor
|
||||||
|
|
||||||
|
/** Indicates if accessibility is enabled */
|
||||||
|
val accessibilityEnabled = flowOf(true).shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
enrollState
|
||||||
|
.combine(accessibilityEnabled) { event, isEnabled -> Pair(event, isEnabled) }
|
||||||
|
.collect {
|
||||||
|
if (
|
||||||
|
when (it.first) {
|
||||||
|
is FingerEnrollState.EnrollError -> true
|
||||||
|
is FingerEnrollState.EnrollHelp -> it.second
|
||||||
|
is FingerEnrollState.EnrollProgress -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
vibrate(it.first)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
backgroundViewModel.background.filter { it }.collect { didGoToBackground() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the saved progress, this is for when views are recreated and need saved state for the
|
||||||
|
* first time.
|
||||||
|
*/
|
||||||
|
var progressSaved: Flow<FingerEnrollState.EnrollProgress> =
|
||||||
|
enrollState
|
||||||
|
.filterIsInstance<FingerEnrollState.EnrollProgress>()
|
||||||
|
.filterNotNull()
|
||||||
|
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
|
/** This sends touch exploration events only used for debugging purposes. */
|
||||||
|
val touchExplorationDebug: Flow<Point> =
|
||||||
|
debuggingInteractor.debuggingEnabled.combineTransform(
|
||||||
|
simulatedTouchEventsDebugRepository.touchExplorationDebug
|
||||||
|
) { enabled, point ->
|
||||||
|
if (enabled) {
|
||||||
|
emit(point)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Determines the current [StageViewModel] enrollment is in */
|
||||||
|
val enrollStage: Flow<StageViewModel> =
|
||||||
|
combine(enrollStageInteractor.enrollStageThresholds, enrollState) { thresholds, event ->
|
||||||
|
if (event is FingerEnrollState.EnrollProgress) {
|
||||||
|
val progress =
|
||||||
|
(event.totalStepsRequired - event.remainingSteps).toFloat() / event.totalStepsRequired
|
||||||
|
var stageToReturn: StageViewModel = StageViewModel.Center
|
||||||
|
thresholds.forEach { (threshold, stage) ->
|
||||||
|
if (progress < threshold) {
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
stageToReturn = stage
|
||||||
|
}
|
||||||
|
stageToReturn
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filterNotNull()
|
||||||
|
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
|
/** Indicates if we should show the lottie. */
|
||||||
|
val shouldShowLottie: Flow<Boolean> =
|
||||||
|
combine(
|
||||||
|
displayDensityInteractor.displayDensity,
|
||||||
|
displayDensityInteractor.defaultDisplayDensity,
|
||||||
|
displayDensityInteractor.fontScale,
|
||||||
|
orientationInteractor.rotation,
|
||||||
|
) { currDisplayDensity, defaultDisplayDensity, fontScale, rotation ->
|
||||||
|
val canShowLottieForRotation =
|
||||||
|
when (rotation) {
|
||||||
|
Surface.ROTATION_0 -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
canShowLottieForRotation &&
|
||||||
|
if (fontScale > 1.0f) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
defaultDisplayDensity == currDisplayDensity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||||
|
|
||||||
/** The header text for UDFPS enrollment */
|
/** The header text for UDFPS enrollment */
|
||||||
val headerText: Flow<HeaderText> =
|
val headerText: Flow<HeaderText> =
|
||||||
combine(isSetupWizard, accessibilityEnabled, enrollStage) { isSuw, isAccessibility, stage ->
|
combine(isSetupWizard, accessibilityEnabled, enrollStage) { isSuw, isAccessibility, stage ->
|
||||||
return@combine HeaderText(isSuw, isAccessibility, stage)
|
return@combine HeaderText(isSuw, isAccessibility, stage)
|
||||||
}
|
}
|
||||||
|
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
private val shouldClearDescriptionText = enrollStage.map { it is StageViewModel.Unknown }
|
private val shouldClearDescriptionText = enrollStage.map { it is StageViewModel.Unknown }
|
||||||
|
|
||||||
@@ -97,86 +198,102 @@ class UdfpsViewModel() : ViewModel() {
|
|||||||
return@combine DescriptionText(isSuw, isAccessibility, stage)
|
return@combine DescriptionText(isSuw, isAccessibility, stage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
|
/** Indicates if the consumer is ready for enrollment */
|
||||||
|
fun readyForEnrollment() {
|
||||||
|
fingerprintEnrollEnrollingViewModel.canEnroll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates if enrollment should stop */
|
||||||
|
fun stopEnrollment() {
|
||||||
|
fingerprintEnrollEnrollingViewModel.stopEnroll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates the negative button has been clicked */
|
||||||
|
fun negativeButtonClicked() {
|
||||||
|
doReset()
|
||||||
|
navigationViewModel.update(
|
||||||
|
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
|
||||||
|
navStep,
|
||||||
|
"$TAG#negativeButtonClicked",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates that an enrollment was completed */
|
||||||
|
fun finishedSuccessfully() {
|
||||||
|
doReset()
|
||||||
|
navigationViewModel.update(FingerprintAction.NEXT, navStep, "${TAG}#progressFinished")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates that the application went to the background. */
|
||||||
|
private fun didGoToBackground() {
|
||||||
|
navigationViewModel.update(
|
||||||
|
FingerprintAction.DID_GO_TO_BACKGROUND,
|
||||||
|
navStep,
|
||||||
|
"$TAG#didGoToBackground",
|
||||||
|
)
|
||||||
|
stopEnrollment()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doReset() {
|
||||||
|
/** Indicates if the icon should be animating or not */
|
||||||
|
_enrollState = fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||||
|
}
|
||||||
|
|
||||||
/** The lottie that should be shown for UDFPS Enrollment */
|
/** The lottie that should be shown for UDFPS Enrollment */
|
||||||
val lottie: Flow<EducationAnimationModel> =
|
val lottie: Flow<EducationAnimationModel> =
|
||||||
combine(isSetupWizard, accessibilityEnabled, enrollStage) { isSuw, isAccessibility, stage ->
|
combine(isSetupWizard, accessibilityEnabled, enrollStage) { isSuw, isAccessibility, stage ->
|
||||||
return@combine EducationAnimationModel(isSuw, isAccessibility, stage)
|
return@combine EducationAnimationModel(isSuw, isAccessibility, stage)
|
||||||
}.distinctUntilChanged()
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
class UdfpsEnrollmentFactory() : ViewModelProvider.Factory {
|
/** Indicates we should send a vibration event */
|
||||||
|
private fun vibrate(event: FingerEnrollState) {
|
||||||
|
val vibrationEvent =
|
||||||
|
when (event) {
|
||||||
|
is FingerEnrollState.EnrollError -> FingerprintVibrationEffects.UdfpsError
|
||||||
|
is FingerEnrollState.EnrollHelp -> FingerprintVibrationEffects.UdfpsHelp
|
||||||
|
is FingerEnrollState.EnrollProgress -> FingerprintVibrationEffects.UdfpsSuccess
|
||||||
|
else -> FingerprintVibrationEffects.UdfpsError
|
||||||
|
}
|
||||||
|
vibrationInteractor.vibrate(vibrationEvent, "UdfpsEnrollFragment")
|
||||||
|
}
|
||||||
|
|
||||||
|
class UdfpsEnrollmentFactory(
|
||||||
|
private val vibrationInteractor: VibrationInteractor,
|
||||||
|
private val displayDensityInteractor: DisplayDensityInteractor,
|
||||||
|
private val navigationViewModel: FingerprintNavigationViewModel,
|
||||||
|
private val debuggingInteractor: DebuggingInteractor,
|
||||||
|
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||||
|
private val simulatedTouchEventsRepository: SimulatedTouchEventsRepository,
|
||||||
|
private val enrollStageInteractor: EnrollStageInteractor,
|
||||||
|
private val orientationInteractor: OrientationInteractor,
|
||||||
|
private val backgroundViewModel: BackgroundViewModel,
|
||||||
|
private val sensorRepository: FingerprintSensorRepository,
|
||||||
|
) : ViewModelProvider.Factory {
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
return UdfpsViewModel() as T
|
return UdfpsViewModel(
|
||||||
|
vibrationInteractor,
|
||||||
|
displayDensityInteractor,
|
||||||
|
navigationViewModel,
|
||||||
|
debuggingInteractor,
|
||||||
|
fingerprintEnrollEnrollingViewModel,
|
||||||
|
simulatedTouchEventsRepository,
|
||||||
|
enrollStageInteractor,
|
||||||
|
orientationInteractor,
|
||||||
|
backgroundViewModel,
|
||||||
|
sensorRepository,
|
||||||
|
)
|
||||||
|
as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val navStep = FingerprintNavigationStep.Enrollment::class
|
private val navStep = FingerprintNavigationStep.Enrollment::class
|
||||||
private const val TAG = "UDFPSViewModel"
|
private const val TAG = "UDFPSViewModel"
|
||||||
private val ENROLLMENT_STAGES_ORDERED =
|
|
||||||
listOf(
|
|
||||||
StageViewModel.Center,
|
|
||||||
StageViewModel.Guided,
|
|
||||||
StageViewModel.Fingertip,
|
|
||||||
StageViewModel.LeftEdge,
|
|
||||||
StageViewModel.RightEdge,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [thresholds] is a list of 4 numbers from [0,1] that separate enrollment into 5 stages. The
|
|
||||||
* stage is determined by mapping [thresholds] * [maxSteps] and finding where the [currentStep]
|
|
||||||
* is.
|
|
||||||
*
|
|
||||||
* Each number in the array should be strictly increasing such as [0.2, 0.5, 0.6, 0.8]
|
|
||||||
*/
|
|
||||||
private fun thresholdToStageMap(
|
|
||||||
thresholds: List<Double>,
|
|
||||||
currentStep: Int,
|
|
||||||
maxSteps: Int,
|
|
||||||
): StageViewModel {
|
|
||||||
val stageIterator = ENROLLMENT_STAGES_ORDERED.iterator()
|
|
||||||
thresholds.forEach {
|
|
||||||
val thresholdLimit = it * maxSteps
|
|
||||||
val curr = stageIterator.next()
|
|
||||||
if (currentStep < thresholdLimit) {
|
|
||||||
return curr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stageIterator.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This will be removed */
|
|
||||||
private val enrollEvents: List<List<UdfpsEnrollEvent>> =
|
|
||||||
listOf(
|
|
||||||
listOf(OverlayShown),
|
|
||||||
listOf(UdfpsHelp(1,"hi")),
|
|
||||||
listOf(UdfpsHelp(1,"hi")),
|
|
||||||
CreateProgress(15, 16),
|
|
||||||
listOf(UdfpsHelp(1,"hi")),
|
|
||||||
CreateProgress(14, 16),
|
|
||||||
listOf(PointerDown, UdfpsHelp(1,"hi"), PointerUp),
|
|
||||||
listOf(PointerDown, UdfpsHelp(1,"hi"), PointerUp),
|
|
||||||
CreateProgress(13, 16),
|
|
||||||
CreateProgress(12, 16),
|
|
||||||
CreateProgress(11, 16),
|
|
||||||
CreateProgress(10, 16),
|
|
||||||
CreateProgress(9, 16),
|
|
||||||
CreateProgress(8, 16),
|
|
||||||
CreateProgress(7, 16),
|
|
||||||
CreateProgress(6, 16),
|
|
||||||
CreateProgress(5, 16),
|
|
||||||
CreateProgress(4, 16),
|
|
||||||
CreateProgress(3, 16),
|
|
||||||
CreateProgress(2, 16),
|
|
||||||
CreateProgress(1, 16),
|
|
||||||
CreateProgress(0, 16),
|
|
||||||
)
|
|
||||||
|
|
||||||
/** This will be removed */
|
|
||||||
private fun CreateProgress(remaining: Int, total: Int): List<UdfpsEnrollEvent> {
|
|
||||||
return listOf(PointerDown, Acquired(true), UdfpsProgress(remaining, total), PointerUp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@ import android.content.Context
|
|||||||
import android.graphics.PointF
|
import android.graphics.PointF
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.accessibility.AccessibilityManager
|
import android.view.accessibility.AccessibilityManager
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
|
||||||
/** Keeps track of which guided enrollment point we should be using */
|
/** Keeps track of which guided enrollment point we should be using */
|
||||||
class UdfpsEnrollHelperV2(private val mContext: Context) {
|
class UdfpsEnrollHelperV2(private val mContext: Context) {
|
||||||
@@ -28,6 +28,7 @@ class UdfpsEnrollHelperV2(private val mContext: Context) {
|
|||||||
private var isGuidedEnrollment: Boolean = false
|
private var isGuidedEnrollment: Boolean = false
|
||||||
private val accessibilityEnabled: Boolean
|
private val accessibilityEnabled: Boolean
|
||||||
private val guidedEnrollmentPoints: MutableList<PointF>
|
private val guidedEnrollmentPoints: MutableList<PointF>
|
||||||
|
/** The current index of [guidedEnrollmentPoints] for the guided enrollment. */
|
||||||
private var index = 0
|
private var index = 0
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -76,7 +77,7 @@ class UdfpsEnrollHelperV2(private val mContext: Context) {
|
|||||||
if (accessibilityEnabled || !isGuidedEnrollment) {
|
if (accessibilityEnabled || !isGuidedEnrollment) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
var scale = SCALE
|
val scale = SCALE
|
||||||
val originalPoint = guidedEnrollmentPoints[index % guidedEnrollmentPoints.size]
|
val originalPoint = guidedEnrollmentPoints[index % guidedEnrollmentPoints.size]
|
||||||
return PointF(originalPoint.x * scale, originalPoint.y * scale)
|
return PointF(originalPoint.x * scale, originalPoint.y * scale)
|
||||||
}
|
}
|
||||||
|
@@ -37,7 +37,7 @@ import androidx.core.animation.addListener
|
|||||||
import androidx.core.graphics.toRect
|
import androidx.core.graphics.toRect
|
||||||
import androidx.core.graphics.toRectF
|
import androidx.core.graphics.toRectF
|
||||||
import com.android.settings.R
|
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.lib.model.StageViewModel
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,6 +45,7 @@ import kotlin.math.sin
|
|||||||
* various stages of enrollment
|
* various stages of enrollment
|
||||||
*/
|
*/
|
||||||
class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeSet?) : Drawable() {
|
class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeSet?) : Drawable() {
|
||||||
|
private var targetAnimationDuration: Long = TARGET_ANIM_DURATION_LONG
|
||||||
private var targetAnimatorSet: AnimatorSet? = null
|
private var targetAnimatorSet: AnimatorSet? = null
|
||||||
private val movingTargetFpIcon: Drawable
|
private val movingTargetFpIcon: Drawable
|
||||||
private val fingerprintDrawable: ShapeDrawable
|
private val fingerprintDrawable: ShapeDrawable
|
||||||
@@ -88,19 +89,22 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
it.recycle()
|
it.recycle()
|
||||||
}
|
}
|
||||||
|
|
||||||
sensorOutlinePaint = Paint(0 /* flags */).apply {
|
sensorOutlinePaint =
|
||||||
|
Paint(0 /* flags */).apply {
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
setColor(movingTargetFill)
|
setColor(movingTargetFill)
|
||||||
style = Paint.Style.FILL
|
style = Paint.Style.FILL
|
||||||
}
|
}
|
||||||
|
|
||||||
blueFill = Paint(0 /* flags */).apply {
|
blueFill =
|
||||||
|
Paint(0 /* flags */).apply {
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
setColor(movingTargetFill)
|
setColor(movingTargetFill)
|
||||||
style = Paint.Style.FILL
|
style = Paint.Style.FILL
|
||||||
}
|
}
|
||||||
|
|
||||||
movingTargetFpIcon = context.resources.getDrawable(R.drawable.ic_enrollment_fingerprint, null).apply {
|
movingTargetFpIcon =
|
||||||
|
context.resources.getDrawable(R.drawable.ic_enrollment_fingerprint, null).apply {
|
||||||
setTint(enrollIconColor)
|
setTint(enrollIconColor)
|
||||||
mutate()
|
mutate()
|
||||||
}
|
}
|
||||||
@@ -140,7 +144,16 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Update the progress of the icon */
|
/** Update the progress of the icon */
|
||||||
fun onEnrollmentProgress(remaining: Int, totalSteps: Int) {
|
fun onEnrollmentProgress(remaining: Int, totalSteps: Int, isRecreating: Boolean = false) {
|
||||||
|
restoreAnimationTime()
|
||||||
|
// If we are restoring this view from a saved state, set animation duration to 0 to avoid
|
||||||
|
// animating progress that has already occurred.
|
||||||
|
if (isRecreating) {
|
||||||
|
setAnimationTimeToZero()
|
||||||
|
} else {
|
||||||
|
restoreAnimationTime()
|
||||||
|
}
|
||||||
|
|
||||||
helper.onEnrollmentProgress(remaining, totalSteps)
|
helper.onEnrollmentProgress(remaining, totalSteps)
|
||||||
val offset = helper.guidedEnrollmentLocation
|
val offset = helper.guidedEnrollmentLocation
|
||||||
val currentBounds = getCurrLocation().toRect()
|
val currentBounds = getCurrLocation().toRect()
|
||||||
@@ -149,10 +162,10 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
// offsets the initial sensor rect by a bit to get the user to move their finger a bit more.
|
// offsets the initial sensor rect by a bit to get the user to move their finger a bit more.
|
||||||
val targetRect = Rect(sensorRectBounds).toRectF()
|
val targetRect = Rect(sensorRectBounds).toRectF()
|
||||||
targetRect.offset(offset.x, offset.y)
|
targetRect.offset(offset.x, offset.y)
|
||||||
var shouldAnimateMovement =
|
val shouldAnimateMovement =
|
||||||
!currentBounds.equals(targetRect) && offset.x != 0f && offset.y != 0f
|
!currentBounds.equals(targetRect) && offset.x != 0f && offset.y != 0f
|
||||||
if (shouldAnimateMovement) {
|
if (shouldAnimateMovement) {
|
||||||
targetAnimatorSet?.let { it.cancel() }
|
targetAnimatorSet?.cancel()
|
||||||
animateMovement(currentBounds, targetRect, true)
|
animateMovement(currentBounds, targetRect, true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -186,7 +199,7 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
val currLocation = getCurrLocation()
|
val currLocation = getCurrLocation()
|
||||||
canvas.scale(currentScale, currentScale, currLocation.centerX(), currLocation.centerY())
|
canvas.scale(currentScale, currentScale, currLocation.centerX(), currLocation.centerY())
|
||||||
|
|
||||||
sensorRectBounds?.let { canvas.drawOval(currLocation, sensorOutlinePaint) }
|
canvas.drawOval(currLocation, sensorOutlinePaint)
|
||||||
fingerprintDrawable.bounds = currLocation.toRect()
|
fingerprintDrawable.bounds = currLocation.toRect()
|
||||||
fingerprintDrawable.draw(canvas)
|
fingerprintDrawable.draw(canvas)
|
||||||
}
|
}
|
||||||
@@ -234,6 +247,19 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sets animation time to 0. This typically happens after an activity recreation, we don't
|
||||||
|
* want to re-animate the progress/success animation with the default timer
|
||||||
|
*/
|
||||||
|
private fun setAnimationTimeToZero() {
|
||||||
|
targetAnimationDuration = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This sets animation timers back to normal, this happens after we have */
|
||||||
|
private fun restoreAnimationTime() {
|
||||||
|
targetAnimationDuration = TARGET_ANIM_DURATION_LONG
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "UdfpsEnrollDrawableV2"
|
private const val TAG = "UdfpsEnrollDrawableV2"
|
||||||
private const val DEFAULT_STROKE_WIDTH = 3f
|
private const val DEFAULT_STROKE_WIDTH = 3f
|
||||||
@@ -242,7 +268,8 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
|
|
||||||
private fun createUdfpsIcon(context: Context): ShapeDrawable {
|
private fun createUdfpsIcon(context: Context): ShapeDrawable {
|
||||||
val fpPath = context.resources.getString(R.string.config_udfpsIcon)
|
val fpPath = context.resources.getString(R.string.config_udfpsIcon)
|
||||||
val drawable = ShapeDrawable(PathShape(PathParser.createPathFromPathData(fpPath), 72f, 72f)).apply {
|
val drawable =
|
||||||
|
ShapeDrawable(PathShape(PathParser.createPathFromPathData(fpPath), 72f, 72f)).apply {
|
||||||
mutate()
|
mutate()
|
||||||
paint.style = Paint.Style.STROKE
|
paint.style = Paint.Style.STROKE
|
||||||
paint.strokeCap = Paint.Cap.ROUND
|
paint.strokeCap = Paint.Cap.ROUND
|
||||||
|
@@ -25,28 +25,28 @@ import android.graphics.Paint
|
|||||||
import android.graphics.PixelFormat
|
import android.graphics.PixelFormat
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Process
|
|
||||||
import android.os.VibrationAttributes
|
|
||||||
import android.os.VibrationEffect
|
|
||||||
import android.os.Vibrator
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.view.animation.DecelerateInterpolator
|
import android.view.animation.DecelerateInterpolator
|
||||||
import android.view.animation.Interpolator
|
import android.view.animation.Interpolator
|
||||||
|
import android.view.animation.OvershootInterpolator
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.core.animation.doOnEnd
|
import androidx.core.animation.doOnEnd
|
||||||
import androidx.core.graphics.toRectF
|
import androidx.core.graphics.toRectF
|
||||||
import com.android.internal.annotations.VisibleForTesting
|
import com.android.internal.annotations.VisibleForTesting
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import kotlin.math.cos
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
import kotlin.math.sin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UDFPS enrollment progress bar. This view is responsible for drawing the progress ring and its
|
* UDFPS enrollment progress bar. This view is responsible for drawing the progress ring and its
|
||||||
* fill around the center of the UDFPS sensor.
|
* fill around the center of the UDFPS sensor.
|
||||||
*/
|
*/
|
||||||
class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: AttributeSet?) :
|
class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: AttributeSet?) :
|
||||||
Drawable() {
|
Drawable() {
|
||||||
private val sensorRect: Rect = Rect()
|
private val sensorRect: Rect = Rect()
|
||||||
|
private var rotation: Int = 0
|
||||||
private val strokeWidthPx: Float
|
private val strokeWidthPx: Float
|
||||||
|
|
||||||
@ColorInt private val progressColor: Int
|
@ColorInt private val progressColor: Int
|
||||||
@@ -56,7 +56,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
private val backgroundPaint: Paint
|
private val backgroundPaint: Paint
|
||||||
|
|
||||||
@VisibleForTesting val fillPaint: Paint
|
@VisibleForTesting val fillPaint: Paint
|
||||||
private val vibrator: Vibrator
|
|
||||||
private var isAccessibilityEnabled: Boolean = false
|
private var isAccessibilityEnabled: Boolean = false
|
||||||
private var afterFirstTouch = false
|
private var afterFirstTouch = false
|
||||||
private var remainingSteps = 0
|
private var remainingSteps = 0
|
||||||
@@ -64,22 +63,27 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
private var progress = 0f
|
private var progress = 0f
|
||||||
private var progressAnimator: ValueAnimator? = null
|
private var progressAnimator: ValueAnimator? = null
|
||||||
private val progressUpdateListener: AnimatorUpdateListener
|
private val progressUpdateListener: AnimatorUpdateListener
|
||||||
private var showingHelp = false
|
|
||||||
private var fillColorAnimator: ValueAnimator? = null
|
private var fillColorAnimator: ValueAnimator? = null
|
||||||
private val fillColorUpdateListener: AnimatorUpdateListener
|
private val fillColorUpdateListener: AnimatorUpdateListener
|
||||||
private var backgroundColorAnimator: ValueAnimator? = null
|
private var backgroundColorAnimator: ValueAnimator? = null
|
||||||
private val backgroundColorUpdateListener: AnimatorUpdateListener
|
private val backgroundColorUpdateListener: AnimatorUpdateListener
|
||||||
private var complete = false
|
|
||||||
private var movingTargetFill = 0
|
private var movingTargetFill = 0
|
||||||
private var movingTargetFillError = 0
|
private var movingTargetFillError = 0
|
||||||
private var enrollProgressColor = 0
|
private var enrollProgressColor = 0
|
||||||
private var enrollProgressHelp = 0
|
private var enrollProgressHelp = 0
|
||||||
private var enrollProgressHelpWithTalkback = 0
|
private var enrollProgressHelpWithTalkback = 0
|
||||||
private val progressBarRadius: Int
|
private val progressBarRadius: Int
|
||||||
|
private var checkMarkDrawable: Drawable
|
||||||
|
private var checkMarkAnimator: ValueAnimator? = null
|
||||||
|
|
||||||
|
private var fillColorAnimationDuration = FILL_COLOR_ANIMATION_DURATION_MS
|
||||||
|
private var animateArcDuration = PROGRESS_ANIMATION_DURATION_MS
|
||||||
|
private var checkmarkAnimationDelayDuration = CHECKMARK_ANIMATION_DELAY_MS
|
||||||
|
private var checkmarkAnimationDuration = CHECKMARK_ANIMATION_DURATION_MS
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val ta =
|
val ta =
|
||||||
mContext.obtainStyledAttributes(
|
context.obtainStyledAttributes(
|
||||||
attrs,
|
attrs,
|
||||||
R.styleable.BiometricsEnrollView,
|
R.styleable.BiometricsEnrollView,
|
||||||
R.attr.biometricsEnrollStyle,
|
R.attr.biometricsEnrollStyle,
|
||||||
@@ -94,12 +98,13 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
enrollProgressHelpWithTalkback =
|
enrollProgressHelpWithTalkback =
|
||||||
ta.getColor(R.styleable.BiometricsEnrollView_biometricsEnrollProgressHelpWithTalkback, 0)
|
ta.getColor(R.styleable.BiometricsEnrollView_biometricsEnrollProgressHelpWithTalkback, 0)
|
||||||
ta.recycle()
|
ta.recycle()
|
||||||
val density = mContext.resources.displayMetrics.densityDpi.toFloat()
|
val density = context.resources.displayMetrics.densityDpi.toFloat()
|
||||||
strokeWidthPx = STROKE_WIDTH_DP * (density / DisplayMetrics.DENSITY_DEFAULT)
|
strokeWidthPx = STROKE_WIDTH_DP * (density / DisplayMetrics.DENSITY_DEFAULT)
|
||||||
progressColor = enrollProgressColor
|
progressColor = enrollProgressColor
|
||||||
onFirstBucketFailedColor = movingTargetFillError
|
onFirstBucketFailedColor = movingTargetFillError
|
||||||
updateHelpColor()
|
updateHelpColor()
|
||||||
backgroundPaint = Paint().apply {
|
backgroundPaint =
|
||||||
|
Paint().apply {
|
||||||
strokeWidth = strokeWidthPx
|
strokeWidth = strokeWidthPx
|
||||||
setColor(movingTargetFill)
|
setColor(movingTargetFill)
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
@@ -107,17 +112,19 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
strokeCap = Paint.Cap.ROUND
|
strokeCap = Paint.Cap.ROUND
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkMarkDrawable = context.getDrawable(R.drawable.udfps_enroll_checkmark)!!
|
||||||
|
|
||||||
// Progress fill should *not* use the extracted system color.
|
// Progress fill should *not* use the extracted system color.
|
||||||
fillPaint = Paint().apply {
|
fillPaint =
|
||||||
|
Paint().apply {
|
||||||
strokeWidth = strokeWidthPx
|
strokeWidth = strokeWidthPx
|
||||||
setColor(progressColor)
|
setColor(progressColor)
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
style = Paint.Style.STROKE
|
style = Paint.Style.STROKE
|
||||||
strokeCap = Paint.Cap.ROUND
|
strokeCap = Paint.Cap.ROUND
|
||||||
}
|
}
|
||||||
vibrator = mContext.getSystemService(Vibrator::class.java)!!
|
|
||||||
|
|
||||||
progressBarRadius = mContext.resources.getInteger(R.integer.config_udfpsEnrollProgressBar)
|
progressBarRadius = context.resources.getInteger(R.integer.config_udfpsEnrollProgressBar)
|
||||||
|
|
||||||
progressUpdateListener = AnimatorUpdateListener { animation: ValueAnimator ->
|
progressUpdateListener = AnimatorUpdateListener { animation: ValueAnimator ->
|
||||||
progress = animation.getAnimatedValue() as Float
|
progress = animation.getAnimatedValue() as Float
|
||||||
@@ -134,9 +141,10 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Indicates enrollment progress has occurred. */
|
/** Indicates enrollment progress has occurred. */
|
||||||
fun onEnrollmentProgress(remaining: Int, totalSteps: Int) {
|
fun onEnrollmentProgress(remaining: Int, totalSteps: Int, isRecreating: Boolean = false) {
|
||||||
|
|
||||||
afterFirstTouch = true
|
afterFirstTouch = true
|
||||||
updateProgress(remaining, totalSteps)
|
updateProgress(remaining, totalSteps, isRecreating)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Indicates enrollment help has occurred. */
|
/** Indicates enrollment help has occurred. */
|
||||||
@@ -157,18 +165,12 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
|
|
||||||
canvas.save()
|
canvas.save()
|
||||||
// This takes the sensors bounding box and expands it by [progressBarRadius] in all directions
|
// This takes the sensors bounding box and expands it by [progressBarRadius] in all directions
|
||||||
val sensorProgressRect = Rect(sensorRect)
|
val sensorProgressRect = getSensorProgressRect()
|
||||||
sensorProgressRect.inset(
|
|
||||||
-progressBarRadius,
|
|
||||||
-progressBarRadius,
|
|
||||||
-progressBarRadius,
|
|
||||||
-progressBarRadius,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Rotate -90 degrees to make the progress start from the top right and not the bottom
|
// Rotate -90 degrees to make the progress start from the top right and not the bottom
|
||||||
// right
|
// right
|
||||||
canvas.rotate(
|
canvas.rotate(
|
||||||
-90f,
|
rotation - 90f,
|
||||||
sensorProgressRect.centerX().toFloat(),
|
sensorProgressRect.centerX().toFloat(),
|
||||||
sensorProgressRect.centerY().toFloat(),
|
sensorProgressRect.centerY().toFloat(),
|
||||||
)
|
)
|
||||||
@@ -176,9 +178,9 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
// Draw the background color of the progress circle.
|
// Draw the background color of the progress circle.
|
||||||
canvas.drawArc(
|
canvas.drawArc(
|
||||||
sensorProgressRect.toRectF(),
|
sensorProgressRect.toRectF(),
|
||||||
0f /* startAngle */,
|
0f, /* startAngle */
|
||||||
360f /* sweepAngle */,
|
360f, /* sweepAngle */
|
||||||
false /* useCenter */,
|
false, /* useCenter */
|
||||||
backgroundPaint,
|
backgroundPaint,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -186,13 +188,15 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
// Draw the filled portion of the progress circle.
|
// Draw the filled portion of the progress circle.
|
||||||
canvas.drawArc(
|
canvas.drawArc(
|
||||||
sensorProgressRect.toRectF(),
|
sensorProgressRect.toRectF(),
|
||||||
0f /* startAngle */,
|
0f, /* startAngle */
|
||||||
360f * progress /* sweepAngle */,
|
360f * progress, /* sweepAngle */
|
||||||
false /* useCenter */,
|
false, /* useCenter */
|
||||||
fillPaint,
|
fillPaint,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.restore()
|
canvas.restore()
|
||||||
|
checkMarkDrawable.draw(canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Do nothing here, we will control the alpha internally. */
|
/** Do nothing here, we will control the alpha internally. */
|
||||||
@@ -211,6 +215,7 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
*/
|
*/
|
||||||
fun drawProgressAt(sensorRect: Rect) {
|
fun drawProgressAt(sensorRect: Rect) {
|
||||||
this.sensorRect.set(sensorRect)
|
this.sensorRect.set(sensorRect)
|
||||||
|
invalidateSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Indicates if accessibility is enabled or not. */
|
/** Indicates if accessibility is enabled or not. */
|
||||||
@@ -228,47 +233,21 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateProgress(remainingSteps: Int, totalSteps: Int) {
|
private fun updateProgress(remainingSteps: Int, totalSteps: Int, isRecreating: Boolean) {
|
||||||
if (this.remainingSteps == remainingSteps && this.totalSteps == totalSteps) {
|
if (this.remainingSteps == remainingSteps && this.totalSteps == totalSteps) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we are restoring this view from a saved state, set animation duration to 0 to avoid
|
||||||
|
// animating progress that has already occurred.
|
||||||
|
if (isRecreating) {
|
||||||
|
setAnimationTimeToZero()
|
||||||
|
} else {
|
||||||
|
restoreAnimationTime()
|
||||||
|
}
|
||||||
|
|
||||||
this.remainingSteps = remainingSteps
|
this.remainingSteps = remainingSteps
|
||||||
this.totalSteps = totalSteps
|
this.totalSteps = totalSteps
|
||||||
if (this.showingHelp) {
|
|
||||||
if (vibrator != null && isAccessibilityEnabled) {
|
|
||||||
vibrator.vibrate(
|
|
||||||
Process.myUid(),
|
|
||||||
mContext.opPackageName,
|
|
||||||
VIBRATE_EFFECT_ERROR,
|
|
||||||
javaClass.getSimpleName() + "::onEnrollmentHelp",
|
|
||||||
FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If the first touch is an error, remainingSteps will be -1 and the callback
|
|
||||||
// doesn't come from onEnrollmentHelp. If we are in the accessibility flow,
|
|
||||||
// we still would like to vibrate.
|
|
||||||
if (vibrator != null) {
|
|
||||||
if (remainingSteps == -1 && isAccessibilityEnabled) {
|
|
||||||
vibrator.vibrate(
|
|
||||||
Process.myUid(),
|
|
||||||
mContext.opPackageName,
|
|
||||||
VIBRATE_EFFECT_ERROR,
|
|
||||||
javaClass.getSimpleName() + "::onFirstTouchError",
|
|
||||||
FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES,
|
|
||||||
)
|
|
||||||
} else if (remainingSteps != -1 && !isAccessibilityEnabled) {
|
|
||||||
vibrator.vibrate(
|
|
||||||
Process.myUid(),
|
|
||||||
mContext.opPackageName,
|
|
||||||
SUCCESS_VIBRATION_EFFECT,
|
|
||||||
javaClass.getSimpleName() + "::OnEnrollmentProgress",
|
|
||||||
HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.showingHelp = showingHelp
|
|
||||||
this.remainingSteps = remainingSteps
|
this.remainingSteps = remainingSteps
|
||||||
this.totalSteps = totalSteps
|
this.totalSteps = totalSteps
|
||||||
val targetProgress = (totalSteps - remainingSteps).toFloat().div(max(1, totalSteps))
|
val targetProgress = (totalSteps - remainingSteps).toFloat().div(max(1, totalSteps))
|
||||||
@@ -276,12 +255,69 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
if (progressAnimator != null && progressAnimator!!.isRunning) {
|
if (progressAnimator != null && progressAnimator!!.isRunning) {
|
||||||
progressAnimator!!.cancel()
|
progressAnimator!!.cancel()
|
||||||
}
|
}
|
||||||
|
/** The [progressUpdateListener] will force re-[draw]s to occur depending on the progress. */
|
||||||
progressAnimator =
|
progressAnimator =
|
||||||
ValueAnimator.ofFloat(progress, targetProgress).also {
|
ValueAnimator.ofFloat(progress, targetProgress).also {
|
||||||
it.setDuration(PROGRESS_ANIMATION_DURATION_MS)
|
it.setDuration(animateArcDuration)
|
||||||
it.addUpdateListener(progressUpdateListener)
|
it.addUpdateListener(progressUpdateListener)
|
||||||
it.start()
|
it.start()
|
||||||
}
|
}
|
||||||
|
if (remainingSteps == 0) {
|
||||||
|
runCompletionAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runCompletionAnimation() {
|
||||||
|
checkMarkAnimator?.cancel()
|
||||||
|
|
||||||
|
checkMarkAnimator = ValueAnimator.ofFloat(0f, 1f)
|
||||||
|
checkMarkAnimator?.apply {
|
||||||
|
startDelay = checkmarkAnimationDelayDuration
|
||||||
|
setDuration(checkmarkAnimationDuration)
|
||||||
|
interpolator = OvershootInterpolator()
|
||||||
|
addUpdateListener {
|
||||||
|
val newBounds = getCheckMarkStartBounds()
|
||||||
|
val scale = it.animatedFraction
|
||||||
|
newBounds.set(
|
||||||
|
newBounds.left,
|
||||||
|
newBounds.top,
|
||||||
|
(newBounds.left + (newBounds.width() * scale)).toInt(),
|
||||||
|
(newBounds.top + (newBounds.height() * scale)).toInt(),
|
||||||
|
)
|
||||||
|
checkMarkDrawable.bounds = newBounds
|
||||||
|
checkMarkDrawable.setVisible(true, false)
|
||||||
|
}
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This returns the bounds for which the checkmark drawable should be drawn at. It should be drawn
|
||||||
|
* on the arc of the progress bar at the 315 degree mark.
|
||||||
|
*/
|
||||||
|
private fun getCheckMarkStartBounds(): Rect {
|
||||||
|
val progressBounds = getSensorProgressRect()
|
||||||
|
val radius = progressBounds.width() / 2.0
|
||||||
|
|
||||||
|
var x = (cos(Math.toRadians(315.0)) * radius).toInt() + progressBounds.centerX()
|
||||||
|
// Remember to negate this value as sin(>180) will return negative value
|
||||||
|
var y = (-sin(Math.toRadians(315.0)) * radius).toInt() + progressBounds.centerY()
|
||||||
|
// Subtract height|width /2 to make sure we draw in the middle of the arc.
|
||||||
|
x -= (checkMarkDrawable.intrinsicWidth / 2.0).toInt()
|
||||||
|
y -= (checkMarkDrawable.intrinsicHeight / 2.0).toInt()
|
||||||
|
|
||||||
|
return Rect(x, y, x + checkMarkDrawable.intrinsicWidth, y + checkMarkDrawable.intrinsicHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSensorProgressRect(): Rect {
|
||||||
|
val sensorProgressRect = Rect(sensorRect)
|
||||||
|
sensorProgressRect.inset(
|
||||||
|
-progressBarRadius,
|
||||||
|
-progressBarRadius,
|
||||||
|
-progressBarRadius,
|
||||||
|
-progressBarRadius,
|
||||||
|
)
|
||||||
|
return sensorProgressRect
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -294,7 +330,7 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
}
|
}
|
||||||
backgroundColorAnimator =
|
backgroundColorAnimator =
|
||||||
ValueAnimator.ofArgb(backgroundPaint.color, onFirstBucketFailedColor).also {
|
ValueAnimator.ofArgb(backgroundPaint.color, onFirstBucketFailedColor).also {
|
||||||
it.setDuration(FILL_COLOR_ANIMATION_DURATION_MS)
|
it.setDuration(fillColorAnimationDuration)
|
||||||
it.repeatCount = 1
|
it.repeatCount = 1
|
||||||
it.repeatMode = ValueAnimator.REVERSE
|
it.repeatMode = ValueAnimator.REVERSE
|
||||||
it.interpolator = DEACCEL
|
it.interpolator = DEACCEL
|
||||||
@@ -315,7 +351,7 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
@ColorInt val targetColor = helpColor
|
@ColorInt val targetColor = helpColor
|
||||||
fillColorAnimator =
|
fillColorAnimator =
|
||||||
ValueAnimator.ofArgb(fillPaint.color, targetColor).also {
|
ValueAnimator.ofArgb(fillPaint.color, targetColor).also {
|
||||||
it.setDuration(FILL_COLOR_ANIMATION_DURATION_MS)
|
it.setDuration(fillColorAnimationDuration)
|
||||||
it.repeatCount = 1
|
it.repeatCount = 1
|
||||||
it.repeatMode = ValueAnimator.REVERSE
|
it.repeatMode = ValueAnimator.REVERSE
|
||||||
it.interpolator = DEACCEL
|
it.interpolator = DEACCEL
|
||||||
@@ -325,33 +361,32 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startCompletionAnimation() {
|
/**
|
||||||
if (complete) {
|
* This sets animation time to 0. This typically happens after an activity recreation, we don't
|
||||||
return
|
* want to re-animate the progress/success animation with the default timer
|
||||||
}
|
*/
|
||||||
complete = true
|
private fun setAnimationTimeToZero() {
|
||||||
|
fillColorAnimationDuration = 0
|
||||||
|
animateArcDuration = 0
|
||||||
|
checkmarkAnimationDelayDuration = 0
|
||||||
|
checkmarkAnimationDuration = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rollBackCompletionAnimation() {
|
/** This sets animation timers back to normal, this happens after we have */
|
||||||
if (!complete) {
|
private fun restoreAnimationTime() {
|
||||||
return
|
fillColorAnimationDuration = FILL_COLOR_ANIMATION_DURATION_MS
|
||||||
|
animateArcDuration = PROGRESS_ANIMATION_DURATION_MS
|
||||||
|
checkmarkAnimationDelayDuration = CHECKMARK_ANIMATION_DELAY_MS
|
||||||
|
checkmarkAnimationDuration = CHECKMARK_ANIMATION_DURATION_MS
|
||||||
}
|
}
|
||||||
complete = false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadResources(context: Context, attrs: AttributeSet?) {}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "UdfpsProgressBar"
|
private const val TAG = "UdfpsProgressBar"
|
||||||
private const val FILL_COLOR_ANIMATION_DURATION_MS = 350L
|
private const val FILL_COLOR_ANIMATION_DURATION_MS = 350L
|
||||||
private const val PROGRESS_ANIMATION_DURATION_MS = 400L
|
private const val PROGRESS_ANIMATION_DURATION_MS = 400L
|
||||||
|
private const val CHECKMARK_ANIMATION_DELAY_MS = 200L
|
||||||
|
private const val CHECKMARK_ANIMATION_DURATION_MS = 300L
|
||||||
private const val STROKE_WIDTH_DP = 12f
|
private const val STROKE_WIDTH_DP = 12f
|
||||||
private val DEACCEL: Interpolator = DecelerateInterpolator()
|
private val DEACCEL: Interpolator = DecelerateInterpolator()
|
||||||
private val VIBRATE_EFFECT_ERROR = VibrationEffect.createWaveform(longArrayOf(0, 5, 55, 60), -1)
|
|
||||||
private val FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES =
|
|
||||||
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ACCESSIBILITY)
|
|
||||||
private val HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
|
|
||||||
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK)
|
|
||||||
private val SUCCESS_VIBRATION_EFFECT = VibrationEffect.get(VibrationEffect.EFFECT_CLICK)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,59 +17,99 @@
|
|||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Point
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.DisplayInfo
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.Surface
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.OnHoverListener
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.Acquired
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.OverlayShown
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.PointerDown
|
import com.android.systemui.biometrics.UdfpsUtils
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.PointerUp
|
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel
|
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsEnrollEvent
|
import com.android.systemui.biometrics.shared.model.toInt
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsError
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsHelp
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsProgress
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View corresponding with fingerprint_v2_udfps_enroll_view.xml. This view is responsible for
|
* View corresponding with fingerprint_v2_udfps_enroll_view.xml. This view is responsible for
|
||||||
* drawing the [UdfpsEnrollIconV2] and the [UdfpsEnrollProgressBarDrawableV2].
|
* drawing the [UdfpsEnrollIconV2] and the [UdfpsEnrollProgressBarDrawableV2].
|
||||||
*/
|
*/
|
||||||
class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
|
class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
|
||||||
|
private lateinit var fingerprintSensorType: FingerprintSensorType
|
||||||
|
private var onHoverListener: OnHoverListener = OnHoverListener { _, _ -> false }
|
||||||
private var isAccessibilityEnabled: Boolean = false
|
private var isAccessibilityEnabled: Boolean = false
|
||||||
private lateinit var sensorRect: Rect
|
private lateinit var sensorRect: Rect
|
||||||
private val fingerprintIcon: UdfpsEnrollIconV2 = UdfpsEnrollIconV2(mContext, attrs)
|
private val fingerprintIcon: UdfpsEnrollIconV2 = UdfpsEnrollIconV2(mContext, attrs)
|
||||||
private val fingerprintProgressDrawable: UdfpsEnrollProgressBarDrawableV2 =
|
private val fingerprintProgressDrawable: UdfpsEnrollProgressBarDrawableV2 =
|
||||||
UdfpsEnrollProgressBarDrawableV2(mContext, attrs)
|
UdfpsEnrollProgressBarDrawableV2(mContext, attrs)
|
||||||
private var mTotalSteps = -1
|
private var remainingSteps = -1
|
||||||
private var mRemainingSteps = -1
|
private val udfpsUtils: UdfpsUtils = UdfpsUtils()
|
||||||
|
private lateinit var touchExplorationAnnouncer: TouchExplorationAnnouncer
|
||||||
|
private var isRecreating = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function computes the center (x,y) location with respect to the parent [FrameLayout] for
|
* This function computes the center (x,y) location with respect to the parent [FrameLayout] for
|
||||||
* the [UdfpsEnrollProgressBarDrawableV2]. It also computes the [Rect] with respect to the parent
|
* the [UdfpsEnrollProgressBarDrawableV2]. It also computes the [Rect] with respect to the parent
|
||||||
* [FrameLayout] for the [UdfpsEnrollIconV2].
|
* [FrameLayout] for the [UdfpsEnrollIconV2]. This function will also setup the
|
||||||
|
* [touchExplorationAnnouncer]
|
||||||
*/
|
*/
|
||||||
fun setSensorRect(rect: Rect) {
|
fun setSensorRect(rect: Rect, sensorType: FingerprintSensorType) {
|
||||||
this.sensorRect = rect
|
this.sensorRect = rect
|
||||||
|
this.fingerprintSensorType = sensorType
|
||||||
findViewById<ImageView?>(R.id.udfps_enroll_animation_fp_progress_view)?.also {
|
findViewById<ImageView?>(R.id.udfps_enroll_animation_fp_progress_view)?.also {
|
||||||
it.setImageDrawable(fingerprintProgressDrawable)
|
it.setImageDrawable(fingerprintProgressDrawable)
|
||||||
}
|
}
|
||||||
findViewById<ImageView>(R.id.udfps_enroll_animation_fp_view)?.also {
|
findViewById<ImageView>(R.id.udfps_enroll_animation_fp_view)?.also {
|
||||||
it.setImageDrawable(fingerprintIcon)
|
it.setImageDrawable(fingerprintIcon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val rotation = display.rotation
|
||||||
|
var displayInfo = DisplayInfo()
|
||||||
|
context.display.getDisplayInfo(displayInfo)
|
||||||
|
val scaleFactor = udfpsUtils.getScaleFactor(displayInfo)
|
||||||
|
val overlayParams =
|
||||||
|
UdfpsOverlayParams(
|
||||||
|
sensorRect,
|
||||||
|
fingerprintProgressDrawable.bounds,
|
||||||
|
displayInfo.naturalWidth,
|
||||||
|
displayInfo.naturalHeight,
|
||||||
|
scaleFactor,
|
||||||
|
rotation,
|
||||||
|
sensorType.toInt(),
|
||||||
|
)
|
||||||
val parentView = parent as ViewGroup
|
val parentView = parent as ViewGroup
|
||||||
val coords = parentView.getLocationOnScreen()
|
val coords = parentView.getLocationOnScreen()
|
||||||
val parentLeft = coords[0]
|
val parentLeft = coords[0]
|
||||||
val parentTop = coords[1]
|
val parentTop = coords[1]
|
||||||
val sensorRectOffset = Rect(sensorRect)
|
val sensorRectOffset = Rect(sensorRect)
|
||||||
|
// If the view has been rotated, we need to translate the sensor coordinates
|
||||||
|
// to the new rotated view.
|
||||||
|
when (rotation) {
|
||||||
|
Surface.ROTATION_90,
|
||||||
|
Surface.ROTATION_270 -> {
|
||||||
|
sensorRectOffset.set(
|
||||||
|
sensorRectOffset.top,
|
||||||
|
sensorRectOffset.left,
|
||||||
|
sensorRectOffset.bottom,
|
||||||
|
sensorRectOffset.right,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
// Translate the sensor position into UdfpsEnrollView's view space.
|
||||||
sensorRectOffset.offset(-parentLeft, -parentTop)
|
sensorRectOffset.offset(-parentLeft, -parentTop)
|
||||||
|
|
||||||
fingerprintIcon.drawSensorRectAt(sensorRectOffset)
|
fingerprintIcon.drawSensorRectAt(sensorRectOffset)
|
||||||
fingerprintProgressDrawable.drawProgressAt(sensorRectOffset)
|
fingerprintProgressDrawable.drawProgressAt(sensorRectOffset)
|
||||||
|
|
||||||
|
touchExplorationAnnouncer = TouchExplorationAnnouncer(context, this, overlayParams, udfpsUtils)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Updates the current enrollment stage. */
|
/** Updates the current enrollment stage. */
|
||||||
@@ -78,15 +118,17 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Receive enroll progress event */
|
/** Receive enroll progress event */
|
||||||
fun onUdfpsEvent(event: UdfpsEnrollEvent) {
|
fun onUdfpsEvent(event: FingerEnrollState) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is UdfpsProgress -> onEnrollmentProgress(event.remainingSteps, event.totalSteps)
|
is FingerEnrollState.EnrollProgress ->
|
||||||
is Acquired -> onAcquired(event.acquiredGood)
|
onEnrollmentProgress(event.remainingSteps, event.totalStepsRequired)
|
||||||
is UdfpsHelp -> onEnrollmentHelp()
|
is FingerEnrollState.Acquired -> onAcquired(event.acquiredGood)
|
||||||
is PointerDown -> onPointerDown()
|
is FingerEnrollState.EnrollHelp -> onEnrollmentHelp()
|
||||||
is PointerUp -> onPointerUp()
|
is FingerEnrollState.PointerDown -> onPointerDown()
|
||||||
OverlayShown -> overlayShown()
|
is FingerEnrollState.PointerUp -> onPointerUp()
|
||||||
is UdfpsError -> udfpsError(event.errMsgId, event.errString)
|
is FingerEnrollState.OverlayShown -> overlayShown()
|
||||||
|
is FingerEnrollState.EnrollError ->
|
||||||
|
throw IllegalArgumentException("$TAG should not handle udfps error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,9 +136,37 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
fun setAccessibilityEnabled(enabled: Boolean) {
|
fun setAccessibilityEnabled(enabled: Boolean) {
|
||||||
this.isAccessibilityEnabled = enabled
|
this.isAccessibilityEnabled = enabled
|
||||||
fingerprintProgressDrawable.setAccessibilityEnabled(enabled)
|
fingerprintProgressDrawable.setAccessibilityEnabled(enabled)
|
||||||
|
if (enabled) {
|
||||||
|
addHoverListener()
|
||||||
|
} else {
|
||||||
|
clearHoverListener()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun udfpsError(errMsgId: Int, errString: String) {}
|
private fun udfpsError(errMsgId: Int, errString: String) {}
|
||||||
|
/**
|
||||||
|
* Sends a touch exploration event to the [onHoverListener] this should only be used for
|
||||||
|
* debugging.
|
||||||
|
*/
|
||||||
|
fun sendDebugTouchExplorationEvent(motionEvent: MotionEvent) {
|
||||||
|
touchExplorationAnnouncer.onTouch(motionEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the addHoverListener, this should happen when talkback is enabled. */
|
||||||
|
private fun addHoverListener() {
|
||||||
|
onHoverListener = OnHoverListener { _: View, event: MotionEvent ->
|
||||||
|
sendDebugTouchExplorationEvent(event)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
this.setOnHoverListener(onHoverListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clears the hover listener if one was set. */
|
||||||
|
private fun clearHoverListener() {
|
||||||
|
val listener = OnHoverListener { _, _ -> false }
|
||||||
|
this.setOnHoverListener(listener)
|
||||||
|
onHoverListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
private fun overlayShown() {
|
private fun overlayShown() {
|
||||||
Log.e(TAG, "Implement overlayShown")
|
Log.e(TAG, "Implement overlayShown")
|
||||||
@@ -115,7 +185,7 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
|
|
||||||
/** Receive onAcquired event */
|
/** Receive onAcquired event */
|
||||||
private fun onAcquired(isAcquiredGood: Boolean) {
|
private fun onAcquired(isAcquiredGood: Boolean) {
|
||||||
val animateIfLastStepGood = isAcquiredGood && mRemainingSteps <= 2 && mRemainingSteps >= 0
|
val animateIfLastStepGood = isAcquiredGood && remainingSteps <= 2 && remainingSteps >= 0
|
||||||
if (animateIfLastStepGood) fingerprintProgressDrawable.onLastStepAcquired()
|
if (animateIfLastStepGood) fingerprintProgressDrawable.onLastStepAcquired()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +199,52 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
fingerprintIcon.startDrawing()
|
fingerprintIcon.startDrawing()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||||
|
super.onLayout(changed, left, top, right, bottom)
|
||||||
|
// Because the layout has changed, we need to recompute all locations.
|
||||||
|
if (this::sensorRect.isInitialized && this::fingerprintSensorType.isInitialized) {
|
||||||
|
setSensorRect(sensorRect, fingerprintSensorType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible for announcing touch events that are outside of the sensort rect
|
||||||
|
* area. Generally, if a touch is to the left of the sensor, the accessibility announcement will
|
||||||
|
* be something like "move right"
|
||||||
|
*/
|
||||||
|
private class TouchExplorationAnnouncer(
|
||||||
|
val context: Context,
|
||||||
|
val view: View,
|
||||||
|
val overlayParams: UdfpsOverlayParams,
|
||||||
|
val udfpsUtils: UdfpsUtils,
|
||||||
|
) {
|
||||||
|
/** Will announce accessibility event for touches outside of the sensor rect. */
|
||||||
|
fun onTouch(event: MotionEvent) {
|
||||||
|
val scaledTouch: Point =
|
||||||
|
udfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0), event, overlayParams)
|
||||||
|
if (udfpsUtils.isWithinSensorArea(event.getPointerId(0), event, overlayParams)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val theStr: String =
|
||||||
|
udfpsUtils.onTouchOutsideOfSensorArea(
|
||||||
|
true /*touchExplorationEnabled*/,
|
||||||
|
context,
|
||||||
|
scaledTouch.x,
|
||||||
|
scaledTouch.y,
|
||||||
|
overlayParams,
|
||||||
|
)
|
||||||
|
if (theStr != null) {
|
||||||
|
view.announceForAccessibility(theStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates we should should restore the views saved state. */
|
||||||
|
fun onEnrollProgressSaved(it: FingerEnrollState.EnrollProgress) {
|
||||||
|
fingerprintIcon.onEnrollmentProgress(it.remainingSteps, it.totalStepsRequired, true)
|
||||||
|
fingerprintProgressDrawable.onEnrollmentProgress(it.remainingSteps, it.totalStepsRequired, true)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "UdfpsEnrollView"
|
private const val TAG = "UdfpsEnrollView"
|
||||||
}
|
}
|
||||||
|
@@ -18,9 +18,11 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.android.systemui.biometrics.shared.model.FingerprintSensor
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.transformLatest
|
import kotlinx.coroutines.flow.transformLatest
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
@@ -63,7 +65,7 @@ class FingerprintEnrollEnrollingViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Collects the enrollment flow based on [enrollFlowShouldBeRunning] */
|
/** Collects the enrollment flow based on [enrollFlowShouldBeRunning] */
|
||||||
val enrollFLow =
|
val enrollFlow =
|
||||||
enrollFlowShouldBeRunning.transformLatest {
|
enrollFlowShouldBeRunning.transformLatest {
|
||||||
if (it) {
|
if (it) {
|
||||||
fingerprintEnrollViewModel.enrollFlow.collect { event -> emit(event) }
|
fingerprintEnrollViewModel.enrollFlow.collect { event -> emit(event) }
|
||||||
|
@@ -25,7 +25,6 @@ import com.android.settings.biometrics.fingerprint2.domain.interactor.Orientatio
|
|||||||
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
|
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
|
||||||
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@@ -38,7 +37,7 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/** Models the UI state for [FingerprintEnrollFindSensorV2Fragment]. */
|
/** Models the UI state for fingerprint enroll education */
|
||||||
class FingerprintEnrollFindSensorViewModel(
|
class FingerprintEnrollFindSensorViewModel(
|
||||||
private val navigationViewModel: FingerprintNavigationViewModel,
|
private val navigationViewModel: FingerprintNavigationViewModel,
|
||||||
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
|
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
|
||||||
@@ -70,7 +69,7 @@ class FingerprintEnrollFindSensorViewModel(
|
|||||||
combineTransform(
|
combineTransform(
|
||||||
_showSfpsLottie,
|
_showSfpsLottie,
|
||||||
foldStateInteractor.isFolded,
|
foldStateInteractor.isFolded,
|
||||||
orientationInteractor.rotation,
|
orientationInteractor.rotationFromDefault,
|
||||||
) { _, isFolded, rotation ->
|
) { _, isFolded, rotation ->
|
||||||
emit(Pair(isFolded, rotation))
|
emit(Pair(isFolded, rotation))
|
||||||
}
|
}
|
||||||
@@ -147,6 +146,7 @@ class FingerprintEnrollFindSensorViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is FingerEnrollState.EnrollHelp -> {}
|
is FingerEnrollState.EnrollHelp -> {}
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -45,12 +45,14 @@ import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
|
|||||||
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
|
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
|
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.Settings
|
import com.android.settings.biometrics.fingerprint2.lib.model.Settings
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.settings.binder.FingerprintSettingsViewBinder
|
import com.android.settings.biometrics.fingerprint2.ui.settings.binder.FingerprintSettingsViewBinder
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsViewModel
|
||||||
@@ -222,6 +224,13 @@ class FingerprintSettingsV2Fragment :
|
|||||||
val fingerprintSensorProvider =
|
val fingerprintSensorProvider =
|
||||||
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
|
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
|
||||||
val pressToAuthInteractor = PressToAuthInteractorImpl(context, backgroundDispatcher)
|
val pressToAuthInteractor = PressToAuthInteractorImpl(context, backgroundDispatcher)
|
||||||
|
val fingerprintEnrollStateRepository =
|
||||||
|
FingerprintEnrollInteractorImpl(
|
||||||
|
requireContext().applicationContext,
|
||||||
|
intent.toFingerprintEnrollOptions(),
|
||||||
|
fingerprintManager,
|
||||||
|
Settings,
|
||||||
|
)
|
||||||
|
|
||||||
val interactor =
|
val interactor =
|
||||||
FingerprintManagerInteractorImpl(
|
FingerprintManagerInteractorImpl(
|
||||||
@@ -230,9 +239,7 @@ class FingerprintSettingsV2Fragment :
|
|||||||
fingerprintManager,
|
fingerprintManager,
|
||||||
fingerprintSensorProvider,
|
fingerprintSensorProvider,
|
||||||
GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)),
|
GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)),
|
||||||
pressToAuthInteractor,
|
fingerprintEnrollStateRepository,
|
||||||
Settings,
|
|
||||||
getIntent()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
|
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
|
||||||
|
@@ -91,6 +91,7 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
|
|||||||
object : OrientationInteractor {
|
object : OrientationInteractor {
|
||||||
override val orientation: Flow<Int> = flowOf(Configuration.ORIENTATION_LANDSCAPE)
|
override val orientation: Flow<Int> = flowOf(Configuration.ORIENTATION_LANDSCAPE)
|
||||||
override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
|
override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
|
||||||
|
override val rotationFromDefault: Flow<Int> = rotation
|
||||||
|
|
||||||
override fun getRotationFromDefault(rotation: Int): Int = rotation
|
override fun getRotationFromDefault(rotation: Int): Int = rotation
|
||||||
}
|
}
|
||||||
|
@@ -17,9 +17,7 @@ package com.android.settings.tests.screenshot.biometrics.fingerprint.fragment
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.RfpsEnrollFindSensorFragment
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
|
||||||
import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector
|
|
||||||
import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector.Companion.BiometricFragmentScreenShotRule
|
import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector.Companion.BiometricFragmentScreenShotRule
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -28,10 +26,7 @@ import platform.test.screenshot.FragmentScreenshotTestRule
|
|||||||
import platform.test.screenshot.ViewScreenshotTestRule.Mode
|
import platform.test.screenshot.ViewScreenshotTestRule.Mode
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class FingerprintEnrollFindSensorScreenshotTest {
|
class RfpsEnrollFindSensorScreenshotTest {
|
||||||
private val injector: Injector =
|
|
||||||
Injector(FingerprintNavigationStep.Education(Injector.interactor.sensorProp))
|
|
||||||
|
|
||||||
@Rule @JvmField var rule: FragmentScreenshotTestRule = BiometricFragmentScreenShotRule()
|
@Rule @JvmField var rule: FragmentScreenshotTestRule = BiometricFragmentScreenShotRule()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -39,7 +34,7 @@ class FingerprintEnrollFindSensorScreenshotTest {
|
|||||||
rule.screenshotTest(
|
rule.screenshotTest(
|
||||||
"fp_enroll_find_sensor",
|
"fp_enroll_find_sensor",
|
||||||
Mode.MatchSize,
|
Mode.MatchSize,
|
||||||
FingerprintEnrollFindSensorV2Fragment(injector.fingerprintSensor.sensorType, injector.factory),
|
RfpsEnrollFindSensorFragment(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -33,8 +33,8 @@ import android.os.Handler
|
|||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
||||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractor
|
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.Default
|
import com.android.settings.biometrics.fingerprint2.lib.model.Default
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
|
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
|
||||||
@@ -82,10 +82,6 @@ class FingerprintManagerInteractorTest {
|
|||||||
@Mock private lateinit var gateKeeperPasswordProvider: GatekeeperPasswordProvider
|
@Mock private lateinit var gateKeeperPasswordProvider: GatekeeperPasswordProvider
|
||||||
|
|
||||||
private var testScope = TestScope(backgroundDispatcher)
|
private var testScope = TestScope(backgroundDispatcher)
|
||||||
private var pressToAuthInteractor =
|
|
||||||
object : PressToAuthInteractor {
|
|
||||||
override val isEnabled = flowOf(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
@@ -113,9 +109,12 @@ class FingerprintManagerInteractorTest {
|
|||||||
fingerprintManager,
|
fingerprintManager,
|
||||||
fingerprintSensorRepository,
|
fingerprintSensorRepository,
|
||||||
gateKeeperPasswordProvider,
|
gateKeeperPasswordProvider,
|
||||||
pressToAuthInteractor,
|
FingerprintEnrollInteractorImpl(
|
||||||
|
context,
|
||||||
|
FingerprintEnrollOptions.Builder().build(),
|
||||||
|
fingerprintManager,
|
||||||
Default,
|
Default,
|
||||||
Intent(),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -146,6 +146,7 @@ class FingerprintEnrollFindSensorViewModelV2Test {
|
|||||||
object : OrientationInteractor {
|
object : OrientationInteractor {
|
||||||
override val orientation: Flow<Int> = flowOf(Configuration.ORIENTATION_LANDSCAPE)
|
override val orientation: Flow<Int> = flowOf(Configuration.ORIENTATION_LANDSCAPE)
|
||||||
override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
|
override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
|
||||||
|
override val rotationFromDefault: Flow<Int> = flowOf(Surface.ROTATION_0)
|
||||||
override fun getRotationFromDefault(rotation: Int): Int = rotation
|
override fun getRotationFromDefault(rotation: Int): Int = rotation
|
||||||
}
|
}
|
||||||
underTest =
|
underTest =
|
||||||
|
Reference in New Issue
Block a user