UDFPS Enrollment Refactor (6/N)
Bug fixes Bug: 297082837 Change-Id: I86013007f089e9c57e1f7406f327c001bc4099b4
This commit is contained in:
@@ -14,87 +14,65 @@
|
|||||||
~ See the License for the specific language governing permissions and
|
~ See the License for the specific language governing permissions and
|
||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
<!-- This is used to grab style attributes and apply them
|
||||||
<LinearLayout
|
to this layout -->
|
||||||
|
<com.google.android.setupdesign.GlifLayout
|
||||||
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/udfps_layout"
|
android:id="@+id/glif_layout"
|
||||||
style="?attr/fingerprint_layout_theme"
|
style="?attr/fingerprint_layout_theme"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
android:layout_height="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
|
<LinearLayout
|
||||||
android:layout_width="300dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<ImageView
|
style="@style/SudContentFrame"
|
||||||
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_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center_horizontal|bottom"
|
android:clipChildren="false"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical"
|
||||||
>
|
>
|
||||||
|
|
||||||
<include layout="@layout/fingerprint_v2_udfps_enroll_view" />
|
<LinearLayout
|
||||||
</FrameLayout>
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center|bottom"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/layout_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center_horizontal|bottom"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
>
|
||||||
|
|
||||||
|
<com.airbnb.lottie.LottieAnimationView
|
||||||
|
android:id="@+id/illustration_lottie"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:lottie_autoPlay="true"
|
||||||
|
app:lottie_loop="true"
|
||||||
|
app:lottie_speed=".85"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<include layout="@layout/fingerprint_v2_udfps_enroll_view" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.setupdesign.GlifLayout>
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
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/udfps_layout"
|
android:id="@+id/udfps_layout"
|
||||||
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"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
@@ -27,65 +26,57 @@
|
|||||||
<!-- This is used to grab style attributes and apply them
|
<!-- This is used to grab style attributes and apply them
|
||||||
to this layout -->
|
to this layout -->
|
||||||
<com.google.android.setupdesign.GlifLayout
|
<com.google.android.setupdesign.GlifLayout
|
||||||
android:id="@+id/dummy_glif_layout"
|
android:id="@+id/glif_layout"
|
||||||
style="?attr/fingerprint_layout_theme"
|
style="?attr/fingerprint_layout_theme"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:visibility="gone"
|
android:layout_weight="5"
|
||||||
/>
|
>
|
||||||
|
|
||||||
<ImageView
|
<LinearLayout
|
||||||
android:id="@+id/sud_layout_icon"
|
style="@style/SudContentFrame"
|
||||||
style="@style/SudGlifIcon"
|
android:id="@+id/sud_content_frame"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:scaleType="fitStart"
|
>
|
||||||
android:src="@drawable/ic_lock" />
|
|
||||||
|
|
||||||
<TextView
|
<com.airbnb.lottie.LottieAnimationView
|
||||||
android:id="@+id/title"
|
android:id="@+id/illustration_lottie"
|
||||||
style="@style/SudGlifHeaderTitle"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="200dp"
|
||||||
android:layout_height="80dp"
|
android:clipChildren="false"
|
||||||
android:ellipsize="end"
|
android:clipToPadding="false"
|
||||||
android:lines="2"
|
android:paddingLeft="10dp"
|
||||||
/>
|
android:paddingRight="10dp"
|
||||||
|
android:scaleType="centerInside"
|
||||||
<TextView
|
app:lottie_autoPlay="true"
|
||||||
android:id="@+id/description"
|
app:lottie_loop="true"
|
||||||
style="@style/SudDescription.Glif"
|
app:lottie_speed=".85" />
|
||||||
android:layout_width="match_parent"
|
</LinearLayout>
|
||||||
android:layout_height="wrap_content"
|
</com.google.android.setupdesign.GlifLayout>
|
||||||
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="200dp"
|
|
||||||
android:clipChildren="false"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingLeft="10dp"
|
|
||||||
android:paddingRight="10dp"
|
|
||||||
android:scaleType="centerInside"
|
|
||||||
app:lottie_autoPlay="true"
|
|
||||||
app:lottie_loop="true"
|
|
||||||
app:lottie_speed=".85" />
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/layout_container"
|
android:id="@+id/layout_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="0dp"
|
||||||
android:layout_gravity="center_horizontal|bottom"
|
android:layout_gravity="center_horizontal|bottom"
|
||||||
|
android:layout_weight="4"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:clipChildren="false"
|
||||||
>
|
>
|
||||||
|
|
||||||
<include layout="@layout/fingerprint_v2_udfps_enroll_view" />
|
<include layout="@layout/fingerprint_v2_udfps_enroll_view" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/skip"
|
||||||
|
style="@style/SudGlifButton.Secondary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|left"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
android:text="@string/security_settings_fingerprint_enroll_enrolling_skip" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@@ -21,6 +21,8 @@
|
|||||||
android:id="@+id/udfps_animation_view"
|
android:id="@+id/udfps_animation_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -184,6 +184,7 @@ public class UdfpsEnrollHelper extends InstrumentedFragment {
|
|||||||
*/
|
*/
|
||||||
public void onAcquired(boolean isAcquiredGood) {
|
public void onAcquired(boolean isAcquiredGood) {
|
||||||
if (mListener != null) {
|
if (mListener != null) {
|
||||||
|
Log.e("JRM", "OnaCquired " + isAcquiredGood + " lastStepIsGood" + animateIfLastStep());
|
||||||
mListener.onAcquired(isAcquiredGood && animateIfLastStep());
|
mListener.onAcquired(isAcquiredGood && animateIfLastStep());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,7 @@ class DebuggingRepositoryImpl : DebuggingRepository {
|
|||||||
*/
|
*/
|
||||||
private val isBuildDebuggable = Build.IS_DEBUGGABLE
|
private val isBuildDebuggable = Build.IS_DEBUGGABLE
|
||||||
/** This flag indicates if udfps should use debug repos to supply data to its various views. */
|
/** This flag indicates if udfps should use debug repos to supply data to its various views. */
|
||||||
private val udfpsEnrollmentDebugEnabled = true
|
private val udfpsEnrollmentDebugEnabled = false
|
||||||
|
|
||||||
override fun isDebuggingEnabled(): Boolean {
|
override fun isDebuggingEnabled(): Boolean {
|
||||||
return isBuildDebuggable
|
return isBuildDebuggable
|
||||||
|
@@ -34,10 +34,10 @@ class EnrollStageInteractorImpl() : EnrollStageInteractor {
|
|||||||
flowOf(
|
flowOf(
|
||||||
mapOf(
|
mapOf(
|
||||||
0.0f to EnrollStageModel.Center,
|
0.0f to EnrollStageModel.Center,
|
||||||
0.25f to EnrollStageModel.Guided,
|
0.065f to EnrollStageModel.Guided,
|
||||||
0.5f to EnrollStageModel.Fingertip,
|
0.48f to EnrollStageModel.Fingertip,
|
||||||
0.75f to EnrollStageModel.LeftEdge,
|
0.584f to EnrollStageModel.LeftEdge,
|
||||||
0.875f to EnrollStageModel.RightEdge,
|
0.792f to EnrollStageModel.RightEdge,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -33,6 +33,7 @@ import kotlinx.coroutines.delay
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.transform
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
/** This repository is responsible for collecting all state related to the enroll API. */
|
/** This repository is responsible for collecting all state related to the enroll API. */
|
||||||
@@ -106,6 +107,30 @@ class FingerprintEnrollInteractorImpl(
|
|||||||
streamEnded = true
|
streamEnded = true
|
||||||
enrollRequestOutstanding.update { false }
|
enrollRequestOutstanding.update { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onUdfpsPointerDown(sensorId: Int) {
|
||||||
|
trySend(FingerEnrollState.PointerDown(sensorId)).onFailure { error ->
|
||||||
|
Log.d(TAG, "onUdfpsPointerDown failed to send, due to $error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUdfpsPointerUp(sensorId: Int) {
|
||||||
|
trySend(FingerEnrollState.PointerUp(sensorId)).onFailure { error ->
|
||||||
|
Log.d(TAG, "onUdfpsPointerUp failed to send, due to $error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUdfpsOverlayShown() {
|
||||||
|
trySend(FingerEnrollState.OverlayShown).onFailure { error ->
|
||||||
|
Log.d(TAG, "OverlayShown failed to send, due to $error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAcquired(isAcquiredGood: Boolean) {
|
||||||
|
trySend(FingerEnrollState.Acquired(isAcquiredGood)).onFailure { error ->
|
||||||
|
Log.d(TAG, "Acquired failed to send, due to $error")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val cancellationSignal = CancellationSignal()
|
val cancellationSignal = CancellationSignal()
|
||||||
|
@@ -17,17 +17,12 @@
|
|||||||
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.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.shareIn
|
|
||||||
import kotlinx.coroutines.flow.stateIn
|
|
||||||
import kotlinx.coroutines.flow.transform
|
import kotlinx.coroutines.flow.transform
|
||||||
|
|
||||||
/** Interactor which provides information about orientation */
|
/** Interactor which provides information about orientation */
|
||||||
@@ -35,7 +30,9 @@ 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
|
* This indicates the surface rotation that hte view is currently in. For instance its possible to
|
||||||
|
* rotate a view to 90 degrees but for it to still be portrait mode. In this case, this flow
|
||||||
|
* should emit that we are in rotation 0 (SurfaceView.Rotation_0)
|
||||||
*/
|
*/
|
||||||
val rotation: Flow<Int>
|
val rotation: Flow<Int>
|
||||||
/**
|
/**
|
||||||
@@ -50,8 +47,7 @@ interface OrientationInteractor {
|
|||||||
fun getRotationFromDefault(rotation: Int): Int
|
fun getRotationFromDefault(rotation: Int): Int
|
||||||
}
|
}
|
||||||
|
|
||||||
class OrientationInteractorImpl(private val context: Context, activityScope: CoroutineScope) :
|
class OrientationInteractorImpl(private val context: Context) : OrientationInteractor {
|
||||||
OrientationInteractor {
|
|
||||||
|
|
||||||
override val orientation: Flow<Int> = callbackFlow {
|
override val orientation: Flow<Int> = callbackFlow {
|
||||||
val orientationEventListener =
|
val orientationEventListener =
|
||||||
@@ -62,9 +58,12 @@ 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> = orientation.transform { emit(context.display!!.rotation) }
|
override val rotation: Flow<Int> =
|
||||||
|
orientation.transform {
|
||||||
|
emit(context.display!!.rotation)
|
||||||
|
}
|
||||||
|
|
||||||
override val rotationFromDefault: Flow<Int> = rotation.map { getRotationFromDefault(it) }
|
override val rotationFromDefault: Flow<Int> = rotation.map { getRotationFromDefault(it) }
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
||||||
|
|
||||||
import android.graphics.PointF
|
import android.graphics.PointF
|
||||||
|
import android.util.Log
|
||||||
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
|
||||||
@@ -69,6 +70,7 @@ class UdfpsEnrollInteractorImpl(
|
|||||||
|
|
||||||
override fun onEnrollmentStep(stepsRemaining: Int, totalStep: Int) {
|
override fun onEnrollmentStep(stepsRemaining: Int, totalStep: Int) {
|
||||||
val index = (totalStep - stepsRemaining) % guidedEnrollmentPoints.size
|
val index = (totalStep - stepsRemaining) % guidedEnrollmentPoints.size
|
||||||
|
Log.e("JRM", "guided enroll step $index")
|
||||||
_guidedEnrollment.update { guidedEnrollmentPoints[index] }
|
_guidedEnrollment.update { guidedEnrollmentPoints[index] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -47,10 +47,10 @@ sealed class FingerEnrollState {
|
|||||||
data class Acquired(val acquiredGood: Boolean) : FingerEnrollState()
|
data class Acquired(val acquiredGood: Boolean) : FingerEnrollState()
|
||||||
|
|
||||||
/** Indicates a pointer down event has occurred */
|
/** Indicates a pointer down event has occurred */
|
||||||
data object PointerDown : FingerEnrollState()
|
data class PointerDown(val fingerId: Int) : FingerEnrollState()
|
||||||
|
|
||||||
/** Indicates a pointer up event has occurred */
|
/** Indicates a pointer up event has occurred */
|
||||||
data object PointerUp : FingerEnrollState()
|
data class PointerUp(val fingerId: Int) : FingerEnrollState()
|
||||||
|
|
||||||
/** Indicates the overlay has shown */
|
/** Indicates the overlay has shown */
|
||||||
data object OverlayShown : FingerEnrollState()
|
data object OverlayShown : FingerEnrollState()
|
||||||
|
@@ -72,6 +72,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enroll
|
|||||||
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
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsLastStepViewModel
|
||||||
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.viewmodel.BackgroundViewModel
|
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.FingerprintAction
|
||||||
@@ -126,6 +127,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
|
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
|
||||||
private lateinit var fingerprintEnrollConfirmationViewModel:
|
private lateinit var fingerprintEnrollConfirmationViewModel:
|
||||||
FingerprintEnrollConfirmationViewModel
|
FingerprintEnrollConfirmationViewModel
|
||||||
|
private lateinit var udfpsLastStepViewModel: UdfpsLastStepViewModel
|
||||||
private lateinit var udfpsViewModel: UdfpsViewModel
|
private lateinit var udfpsViewModel: UdfpsViewModel
|
||||||
private lateinit var enrollStageInteractor: EnrollStageInteractor
|
private lateinit var enrollStageInteractor: EnrollStageInteractor
|
||||||
private val coroutineDispatcher = Dispatchers.Default
|
private val coroutineDispatcher = Dispatchers.Default
|
||||||
@@ -320,7 +322,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
foldStateInteractor = FoldStateInteractorImpl(context)
|
foldStateInteractor = FoldStateInteractorImpl(context)
|
||||||
foldStateInteractor.onConfigurationChange(resources.configuration)
|
foldStateInteractor.onConfigurationChange(resources.configuration)
|
||||||
|
|
||||||
orientationInteractor = OrientationInteractorImpl(context, lifecycleScope)
|
orientationInteractor = OrientationInteractorImpl(context)
|
||||||
vibrationInteractor =
|
vibrationInteractor =
|
||||||
VibrationInteractorImpl(context.getSystemService(Vibrator::class.java)!!, context)
|
VibrationInteractorImpl(context.getSystemService(Vibrator::class.java)!!, context)
|
||||||
|
|
||||||
@@ -373,11 +375,15 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
fingerprintEnrollEnrollingViewModel,
|
fingerprintEnrollEnrollingViewModel,
|
||||||
navigationViewModel,
|
navigationViewModel,
|
||||||
orientationInteractor,
|
orientationInteractor,
|
||||||
|
fingerprintManagerInteractor,
|
||||||
),
|
),
|
||||||
)[RFPSViewModel::class.java]
|
)[RFPSViewModel::class.java]
|
||||||
|
|
||||||
enrollStageInteractor = EnrollStageInteractorImpl()
|
enrollStageInteractor = EnrollStageInteractorImpl()
|
||||||
|
|
||||||
|
udfpsLastStepViewModel =
|
||||||
|
UdfpsLastStepViewModel(fingerprintEnrollEnrollingViewModel, vibrationInteractor)
|
||||||
|
|
||||||
udfpsViewModel =
|
udfpsViewModel =
|
||||||
ViewModelProvider(
|
ViewModelProvider(
|
||||||
this,
|
this,
|
||||||
@@ -393,6 +399,9 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
backgroundViewModel,
|
backgroundViewModel,
|
||||||
fingerprintSensorRepo,
|
fingerprintSensorRepo,
|
||||||
udfpsEnrollInteractor,
|
udfpsEnrollInteractor,
|
||||||
|
fingerprintManagerInteractor,
|
||||||
|
udfpsLastStepViewModel,
|
||||||
|
accessibilityInteractor,
|
||||||
),
|
),
|
||||||
)[UdfpsViewModel::class.java]
|
)[UdfpsViewModel::class.java]
|
||||||
|
|
||||||
@@ -456,7 +465,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
step.exitTransition.toAnimation(),
|
step.exitTransition.toAnimation(),
|
||||||
)
|
)
|
||||||
.setReorderingAllowed(true)
|
.setReorderingAllowed(true)
|
||||||
.add(R.id.fragment_container_view, theClass::class.java, null)
|
.replace(R.id.fragment_container_view, theClass::class.java, null)
|
||||||
.commit()
|
.commit()
|
||||||
navigationViewModel.update(
|
navigationViewModel.update(
|
||||||
FingerprintAction.TRANSITION_FINISHED,
|
FingerprintAction.TRANSITION_FINISHED,
|
||||||
|
@@ -20,15 +20,18 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
|
||||||
|
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.ui.enrollment.viewmodel.FingerprintAction
|
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.FingerprintEnrollEnrollingViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
|
||||||
|
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.filterIsInstance
|
import kotlinx.coroutines.flow.filterIsInstance
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
@@ -41,6 +44,7 @@ class RFPSViewModel(
|
|||||||
private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel,
|
private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel,
|
||||||
private val navigationViewModel: FingerprintNavigationViewModel,
|
private val navigationViewModel: FingerprintNavigationViewModel,
|
||||||
orientationInteractor: OrientationInteractor,
|
orientationInteractor: OrientationInteractor,
|
||||||
|
private val fingerprintManager: FingerprintManagerInteractor,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _textViewIsVisible = MutableStateFlow(false)
|
private val _textViewIsVisible = MutableStateFlow(false)
|
||||||
@@ -52,7 +56,16 @@ 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?> =
|
||||||
|
fingerprintManager.sensorPropertiesInternal.filterNotNull().combine(
|
||||||
|
fingerprintEnrollViewModel.enrollFlow
|
||||||
|
) { props, enroll ->
|
||||||
|
if (props.sensorType == FingerprintSensorType.REAR) {
|
||||||
|
enroll
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@@ -149,6 +162,7 @@ class RFPSViewModel(
|
|||||||
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||||
private val navigationViewModel: FingerprintNavigationViewModel,
|
private val navigationViewModel: FingerprintNavigationViewModel,
|
||||||
private val orientationInteractor: OrientationInteractor,
|
private val orientationInteractor: OrientationInteractor,
|
||||||
|
private val fingerprintManager: FingerprintManagerInteractor,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -157,6 +171,7 @@ class RFPSViewModel(
|
|||||||
fingerprintEnrollEnrollingViewModel,
|
fingerprintEnrollEnrollingViewModel,
|
||||||
navigationViewModel,
|
navigationViewModel,
|
||||||
orientationInteractor,
|
orientationInteractor,
|
||||||
|
fingerprintManager,
|
||||||
)
|
)
|
||||||
as T
|
as T
|
||||||
}
|
}
|
||||||
|
@@ -21,8 +21,10 @@ import android.util.Log
|
|||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.MotionEvent.ACTION_HOVER_MOVE
|
import android.view.MotionEvent.ACTION_HOVER_MOVE
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.TextView
|
import android.widget.Button
|
||||||
|
import android.widget.FrameLayout
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
@@ -40,7 +42,6 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enroll
|
|||||||
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.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.google.android.setupdesign.GlifLayout
|
import com.google.android.setupdesign.GlifLayout
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -71,10 +72,8 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
val fragment = this
|
val fragment = this
|
||||||
lottie = view.findViewById(R.id.illustration_lottie)!!
|
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 glifLayout: GlifLayout = view.findViewById(R.id.glif_layout)!!
|
||||||
val descriptionTextView = view.findViewById<TextView>(R.id.description)!!
|
|
||||||
|
|
||||||
val glifLayout = view.findViewById<GlifLayout>(R.id.dummy_glif_layout)!!
|
|
||||||
val backgroundColor = glifLayout.backgroundBaseColor
|
val backgroundColor = glifLayout.backgroundBaseColor
|
||||||
val window = requireActivity().window
|
val window = requireActivity().window
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||||
@@ -83,6 +82,10 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
window.statusBarColor = color
|
window.statusBarColor = color
|
||||||
view.setBackgroundColor(color)
|
view.setBackgroundColor(color)
|
||||||
|
|
||||||
|
view.findViewById<Button>(R.id.skip)?.apply {
|
||||||
|
setOnClickListener { viewModel.negativeButtonClicked() }
|
||||||
|
}
|
||||||
|
|
||||||
udfpsEnrollView.setFinishAnimationCompleted { viewModel.finishedSuccessfully() }
|
udfpsEnrollView.setFinishAnimationCompleted { viewModel.finishedSuccessfully() }
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
@@ -92,32 +95,41 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
udfpsEnrollView.setSensorRect(sensor.sensorBounds, sensor.sensorType)
|
udfpsEnrollView.setSensorRect(sensor.sensorBounds, sensor.sensorType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
viewModel.headerText.collect { titleTextView.setText(it.toResource()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
launch { viewModel.overlayShown.collect { udfpsEnrollView.overlayShown() } }
|
||||||
viewModel.descriptionText.collect {
|
launch { viewModel.headerText.collect { glifLayout.setHeaderText(it.toResource()) } }
|
||||||
if (it != null) {
|
launch {
|
||||||
it.toResource()?.let { text -> descriptionTextView.setText(text) }
|
viewModel.userInteractedWithSensor.collect {
|
||||||
} else {
|
if (!it) {
|
||||||
descriptionTextView.text = ""
|
glifLayout.setHeaderText(R.string.security_settings_fingerprint_enroll_udfps_title)
|
||||||
|
glifLayout.setDescriptionText(R.string.security_settings_udfps_enroll_start_message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
|
launch {
|
||||||
|
viewModel.descriptionText.collect {
|
||||||
|
if (it != null) {
|
||||||
|
it.toResource()?.let { text -> glifLayout.setDescriptionText(text) }
|
||||||
|
} else {
|
||||||
|
glifLayout.descriptionText = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launch {
|
||||||
viewModel.shouldShowLottie.collect {
|
viewModel.shouldShowLottie.collect {
|
||||||
lottie.visibility = if (it) View.VISIBLE else View.GONE
|
lottie.visibility = if (it) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
launch {
|
||||||
viewModel.lottie.collect { lottieModel ->
|
viewModel.lottie.collect { lottieModel ->
|
||||||
if (lottie.visibility == View.GONE) {
|
if (lottie.visibility == View.GONE) {
|
||||||
return@collect
|
return@collect
|
||||||
}
|
}
|
||||||
val resource = lottieModel.toResource()
|
val resource = lottieModel.toResource()
|
||||||
if (resource != null) {
|
if (resource != null) {
|
||||||
|
glifLayout.descriptionTextView.visibility = View.GONE
|
||||||
LottieCompositionFactory.fromRawRes(requireContext(), resource).addListener { comp ->
|
LottieCompositionFactory.fromRawRes(requireContext(), resource).addListener { comp ->
|
||||||
comp?.let { composition ->
|
comp?.let { composition ->
|
||||||
lottie.setComposition(composition)
|
lottie.setComposition(composition)
|
||||||
@@ -126,27 +138,24 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
glifLayout.descriptionTextView.visibility = View.VISIBLE
|
||||||
lottie.visibility = View.INVISIBLE
|
lottie.visibility = View.INVISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
launch {
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.DESTROYED) { viewModel.stopEnrollment() }
|
|
||||||
}
|
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
viewModel.accessibilityEnabled.collect { enabled ->
|
viewModel.accessibilityEnabled.collect { enabled ->
|
||||||
udfpsEnrollView.setAccessibilityEnabled(enabled)
|
udfpsEnrollView.setAccessibilityEnabled(enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
launch {
|
||||||
viewModel.enrollState.collect {
|
viewModel.enrollState.collect {
|
||||||
Log.d(TAG, "EnrollEvent $it")
|
Log.d(TAG, "EnrollEvent $it")
|
||||||
if (it is FingerEnrollState.EnrollError) {
|
if (it is FingerEnrollState.EnrollError) {
|
||||||
try {
|
try {
|
||||||
FingerprintErrorDialog.showInstance(it, fragment)
|
FingerprintErrorDialog.showInstance(it, fragment)
|
||||||
|
viewModel.errorDialogShown(it)
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(TAG, "Exception occurred $exception")
|
Log.e(TAG, "Exception occurred $exception")
|
||||||
}
|
}
|
||||||
@@ -156,19 +165,40 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
launch { viewModel.progressSaved.collect { udfpsEnrollView.onEnrollProgressSaved(it) } }
|
||||||
viewModel.progressSaved.collect { udfpsEnrollView.onEnrollProgressSaved(it) }
|
|
||||||
|
launch { viewModel.guidedEnrollment.collect { udfpsEnrollView.updateGuidedEnrollment(it) } }
|
||||||
|
launch {
|
||||||
|
viewModel.guidedEnrollmentSaved.collect { udfpsEnrollView.onGuidedPointSaved(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
launch { viewModel.shouldDrawIcon.collect { udfpsEnrollView.shouldDrawIcon(it) } }
|
||||||
viewModel.guidedEnrollment.collect {
|
|
||||||
glifLayout.post { udfpsEnrollView.updateGuidedEnrollment(it) }
|
// Hack to get the final step of enroll progress to animate.
|
||||||
|
launch {
|
||||||
|
viewModel.udfpsLastStepViewModel.shouldAnimateCompletion.collect {
|
||||||
|
Log.d(TAG, "Sending fake last enroll event $it")
|
||||||
|
udfpsEnrollView.onUdfpsEvent(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
}
|
||||||
viewModel.guidedEnrollmentSaved.collect {
|
}
|
||||||
glifLayout.post { udfpsEnrollView.onGuidedPointSaved(it) }
|
|
||||||
}
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.DESTROYED) { viewModel.stopEnrollment() }
|
||||||
|
}
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewModel.isLandscape.collect {
|
||||||
|
if (it) {
|
||||||
|
changeViewToLandscape()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewModel.isReverseLandscape.collect {
|
||||||
|
if (it) {
|
||||||
|
changeViewToReverseLandscape()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,12 +213,70 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
viewModel.readyForEnrollment()
|
viewModel.readyForEnrollment()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun changeViewToReverseLandscape() {
|
||||||
|
Log.d(TAG, "changeViewToReverseLandscape")
|
||||||
|
val glifContainer = requireView().findViewById<GlifLayout>(R.id.glif_layout)!!
|
||||||
|
val headerView =
|
||||||
|
glifContainer.findViewById<View>(
|
||||||
|
com.google.android.setupdesign.R.id.sud_landscape_header_area
|
||||||
|
)
|
||||||
|
// The landscape_header_area nad landscape_content_area should have the same parent
|
||||||
|
val parent = headerView!!.parent as ViewGroup
|
||||||
|
val sudContentFrame =
|
||||||
|
glifContainer.findViewById<View>(
|
||||||
|
com.google.android.setupdesign.R.id.sud_landscape_content_area
|
||||||
|
)!!
|
||||||
|
val udfpsContainer = requireView().findViewById<FrameLayout>(R.id.layout_container)!!
|
||||||
|
|
||||||
|
parent.removeView(headerView)
|
||||||
|
parent.removeView(sudContentFrame)
|
||||||
|
parent.addView(sudContentFrame)
|
||||||
|
parent.addView(headerView)
|
||||||
|
|
||||||
|
unclipSubviewsFromParent(udfpsContainer)
|
||||||
|
udfpsEnrollView.requestLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun changeViewToLandscape() {
|
||||||
|
Log.d(TAG, "changeViewToLandscape")
|
||||||
|
|
||||||
|
val glifContainer = requireView().findViewById<GlifLayout>(R.id.glif_layout)!!
|
||||||
|
val headerView =
|
||||||
|
glifContainer.findViewById<View>(
|
||||||
|
com.google.android.setupdesign.R.id.sud_landscape_header_area
|
||||||
|
)
|
||||||
|
// The landscape_header_area nad landscape_content_area should have the same parent
|
||||||
|
val parent = headerView!!.parent as ViewGroup
|
||||||
|
val sudContentFrame =
|
||||||
|
glifContainer.findViewById<View>(
|
||||||
|
com.google.android.setupdesign.R.id.sud_landscape_content_area
|
||||||
|
)!!
|
||||||
|
|
||||||
|
parent.removeView(headerView)
|
||||||
|
parent.removeView(sudContentFrame)
|
||||||
|
parent.addView(headerView)
|
||||||
|
parent.addView(sudContentFrame)
|
||||||
|
|
||||||
|
val udfpsContainer = requireView().findViewById<FrameLayout>(R.id.layout_container)!!
|
||||||
|
unclipSubviewsFromParent(udfpsContainer)
|
||||||
|
udfpsEnrollView.requestLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unclipSubviewsFromParent(view: View) {
|
||||||
|
var currParent = view.parent
|
||||||
|
while (currParent is ViewGroup) {
|
||||||
|
currParent.clipChildren = false
|
||||||
|
currParent.clipToPadding = false
|
||||||
|
currParent = currParent.parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun HeaderText.toResource(): Int {
|
private fun HeaderText.toResource(): Int {
|
||||||
return when (this.enrollStageModel) {
|
return when (this.enrollStageModel) {
|
||||||
EnrollStageModel.Center,
|
EnrollStageModel.Center,
|
||||||
EnrollStageModel.Guided,
|
EnrollStageModel.Guided -> R.string.security_settings_fingerprint_enroll_repeat_title
|
||||||
EnrollStageModel.Fingertip,
|
EnrollStageModel.Fingertip -> R.string.security_settings_udfps_enroll_fingertip_title
|
||||||
EnrollStageModel.Unknown -> R.string.security_settings_udfps_enroll_fingertip_title
|
EnrollStageModel.Unknown -> R.string.security_settings_fingerprint_enroll_udfps_title
|
||||||
EnrollStageModel.LeftEdge -> R.string.security_settings_udfps_enroll_left_edge_title
|
EnrollStageModel.LeftEdge -> R.string.security_settings_udfps_enroll_left_edge_title
|
||||||
EnrollStageModel.RightEdge -> R.string.security_settings_udfps_enroll_right_edge_title
|
EnrollStageModel.RightEdge -> R.string.security_settings_udfps_enroll_right_edge_title
|
||||||
}
|
}
|
||||||
@@ -218,6 +306,5 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "UDFPSEnrollFragment"
|
private const val TAG = "UDFPSEnrollFragment"
|
||||||
private val navStep = FingerprintNavigationStep.Enrollment::class
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* 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.model
|
||||||
|
|
||||||
|
import android.graphics.PointF
|
||||||
|
import com.android.systemui.biometrics.shared.model.FingerprintSensor
|
||||||
|
|
||||||
|
data class UdfpsSensorLocation(val sensorLocation: FingerprintSensor, val offset: PointF?)
|
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintVibrationEffects
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.filterIsInstance
|
||||||
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
|
import kotlinx.coroutines.flow.take
|
||||||
|
import kotlinx.coroutines.flow.toList
|
||||||
|
import kotlinx.coroutines.flow.transform
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible for determining if we should animate the last step of an enrollment. It
|
||||||
|
* seems to be due to poor hardware implementation that the last step of a UDFPS enrollment takes
|
||||||
|
* much longer then normal (likely due to to saving the whole enrollment/or some kind of
|
||||||
|
* verification)
|
||||||
|
*
|
||||||
|
* Because of this, we will use the information of if a fingerprint was acquired(good) + enrollment
|
||||||
|
* has reached the last step
|
||||||
|
*/
|
||||||
|
class UdfpsLastStepViewModel(
|
||||||
|
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||||
|
private val vibrationInteractor: VibrationInteractor,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val lastStep: MutableStateFlow<FingerEnrollState.EnrollProgress?> = MutableStateFlow(null)
|
||||||
|
private val stepSize: MutableStateFlow<Int?> = MutableStateFlow(null)
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val steps =
|
||||||
|
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||||
|
.filterIsInstance<FingerEnrollState.EnrollProgress>()
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.take(2)
|
||||||
|
.toList()
|
||||||
|
stepSize.update { steps[0].remainingSteps - steps[1].remainingSteps }
|
||||||
|
lastStep.update { FingerEnrollState.EnrollProgress(0, steps[0].totalStepsRequired) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val enrollProgress =
|
||||||
|
fingerprintEnrollEnrollingViewModel.enrollFlow.filterIsInstance<
|
||||||
|
FingerEnrollState.EnrollProgress
|
||||||
|
>()
|
||||||
|
|
||||||
|
/** Indicates if we should animate the completion of udfps enrollment. */
|
||||||
|
val shouldAnimateCompletion =
|
||||||
|
enrollProgress
|
||||||
|
.transform { it ->
|
||||||
|
if (it.remainingSteps == stepSize.value) {
|
||||||
|
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||||
|
.filterIsInstance<FingerEnrollState.Acquired>()
|
||||||
|
.filter { acquired -> acquired.acquiredGood }
|
||||||
|
.collect {
|
||||||
|
vibrationInteractor.vibrate(
|
||||||
|
FingerprintVibrationEffects.UdfpsSuccess,
|
||||||
|
"UdfpsLastStepViewModel",
|
||||||
|
)
|
||||||
|
|
||||||
|
emit(lastStep.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filterNotNull()
|
||||||
|
|
||||||
|
class UdfpsLastStepViewModelFactory(
|
||||||
|
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||||
|
private val vibrationInteractor: VibrationInteractor,
|
||||||
|
) : ViewModelProvider.Factory {
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
|
return UdfpsLastStepViewModel(fingerprintEnrollEnrollingViewModel, vibrationInteractor) as T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -22,9 +22,10 @@ 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 androidx.lifecycle.viewModelScope
|
||||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
|
||||||
import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel
|
import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel
|
||||||
|
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
||||||
import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository
|
import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor
|
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.DisplayDensityInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
|
||||||
@@ -32,6 +33,7 @@ import com.android.settings.biometrics.fingerprint2.domain.interactor.Fingerprin
|
|||||||
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.UdfpsEnrollInteractor
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
|
||||||
|
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.ui.enrollment.modules.enrolling.udfps.ui.model.DescriptionText
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.DescriptionText
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.HeaderText
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.HeaderText
|
||||||
@@ -41,6 +43,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing
|
|||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.combineTransform
|
import kotlinx.coroutines.flow.combineTransform
|
||||||
@@ -51,6 +54,8 @@ import kotlinx.coroutines.flow.filterNotNull
|
|||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.shareIn
|
import kotlinx.coroutines.flow.shareIn
|
||||||
|
import kotlinx.coroutines.flow.transform
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */
|
/** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */
|
||||||
@@ -66,13 +71,25 @@ class UdfpsViewModel(
|
|||||||
backgroundViewModel: BackgroundViewModel,
|
backgroundViewModel: BackgroundViewModel,
|
||||||
sensorRepository: FingerprintSensorRepository,
|
sensorRepository: FingerprintSensorRepository,
|
||||||
udfpsEnrollInteractor: UdfpsEnrollInteractor,
|
udfpsEnrollInteractor: UdfpsEnrollInteractor,
|
||||||
|
fingerprintManager: FingerprintManagerInteractor,
|
||||||
|
val udfpsLastStepViewModel: UdfpsLastStepViewModel,
|
||||||
|
accessibilityInteractor: AccessibilityInteractor,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val isSetupWizard = flowOf(false)
|
private val isSetupWizard = flowOf(false)
|
||||||
private var shouldResetErollment = false
|
private var shouldResetErollment = false
|
||||||
|
|
||||||
private var _enrollState: Flow<FingerEnrollState?> =
|
private var _enrollState: Flow<FingerEnrollState?> =
|
||||||
fingerprintEnrollEnrollingViewModel.enrollFlow
|
fingerprintManager.sensorPropertiesInternal.filterNotNull().combine(
|
||||||
|
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||||
|
) { props, enroll ->
|
||||||
|
if (props.sensorType.isUdfps()) {
|
||||||
|
enroll
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** The current state of the enrollment. */
|
/** The current state of the enrollment. */
|
||||||
var enrollState: Flow<FingerEnrollState> =
|
var enrollState: Flow<FingerEnrollState> =
|
||||||
combine(fingerprintEnrollEnrollingViewModel.enrollFlowShouldBeRunning, _enrollState) {
|
combine(fingerprintEnrollEnrollingViewModel.enrollFlowShouldBeRunning, _enrollState) {
|
||||||
@@ -86,48 +103,59 @@ class UdfpsViewModel(
|
|||||||
}
|
}
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
|
|
||||||
|
/** Indicates that overlay has been shown */
|
||||||
|
val overlayShown =
|
||||||
|
enrollState
|
||||||
|
.filterIsInstance<FingerEnrollState.OverlayShown>()
|
||||||
|
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||||
|
|
||||||
|
private var _userInteractedWithSensor = MutableStateFlow(false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This indicates whether the user has interacted with the sensor or not. This indicates if we are
|
||||||
|
* in the initial state of the UI.
|
||||||
|
*/
|
||||||
|
val userInteractedWithSensor: Flow<Boolean> =
|
||||||
|
enrollState.transform {
|
||||||
|
val interactiveMessage =
|
||||||
|
when (it) {
|
||||||
|
is FingerEnrollState.Acquired,
|
||||||
|
is FingerEnrollState.EnrollError,
|
||||||
|
is FingerEnrollState.EnrollHelp,
|
||||||
|
is FingerEnrollState.EnrollProgress,
|
||||||
|
is FingerEnrollState.PointerDown,
|
||||||
|
is FingerEnrollState.PointerUp -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
val hasPreviouslyInteracted = _userInteractedWithSensor.value or interactiveMessage
|
||||||
|
_userInteractedWithSensor.update { hasPreviouslyInteracted }
|
||||||
|
emit(hasPreviouslyInteracted)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forwards the property sensor information. This is typically used to recreate views that must be
|
* Forwards the property sensor information. This is typically used to recreate views that must be
|
||||||
* aligned with the sensor.
|
* aligned with the sensor.
|
||||||
*/
|
*/
|
||||||
val sensorLocation = sensorRepository.fingerprintSensor
|
val sensorLocation = sensorRepository.fingerprintSensor
|
||||||
|
|
||||||
/** Indicates if accessibility is enabled */
|
/** Indicates a step of guided enrollment, the ui should animate the icon to the new location. */
|
||||||
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 indicates at which point the UI should offset the fingerprint sensor icon for guided
|
|
||||||
* enrollment.
|
|
||||||
*/
|
|
||||||
val guidedEnrollment: Flow<PointF> =
|
val guidedEnrollment: Flow<PointF> =
|
||||||
udfpsEnrollInteractor.guidedEnrollmentOffset.distinctUntilChanged()
|
udfpsEnrollInteractor.guidedEnrollmentOffset
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
|
||||||
|
|
||||||
/** The saved version of [guidedEnrollment] */
|
private var _lastOrientation: Int? = null
|
||||||
val guidedEnrollmentSaved: Flow<PointF> =
|
|
||||||
guidedEnrollment.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
/** In case of rotations we should ensure the UI does not re-animate the last state. */
|
||||||
|
private val shouldReplayLastEvent =
|
||||||
|
orientationInteractor.rotation.transform {
|
||||||
|
if (_lastOrientation != null && _lastOrientation!! != it) {
|
||||||
|
emit(true)
|
||||||
|
} else {
|
||||||
|
emit(false)
|
||||||
|
}
|
||||||
|
_lastOrientation = it
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the saved progress, this is for when views are recreated and need saved state for the
|
* This is the saved progress, this is for when views are recreated and need saved state for the
|
||||||
@@ -136,8 +164,32 @@ class UdfpsViewModel(
|
|||||||
var progressSaved: Flow<FingerEnrollState.EnrollProgress> =
|
var progressSaved: Flow<FingerEnrollState.EnrollProgress> =
|
||||||
enrollState
|
enrollState
|
||||||
.filterIsInstance<FingerEnrollState.EnrollProgress>()
|
.filterIsInstance<FingerEnrollState.EnrollProgress>()
|
||||||
.filterNotNull()
|
.combineTransform(shouldReplayLastEvent) { enroll, shouldReplay ->
|
||||||
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
if (shouldReplay) {
|
||||||
|
emit(enroll)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||||
|
|
||||||
|
/** Indicates if accessibility is enabled */
|
||||||
|
val accessibilityEnabled =
|
||||||
|
accessibilityInteractor.isAccessibilityEnabled.shareIn(
|
||||||
|
this.viewModelScope,
|
||||||
|
SharingStarted.Eagerly,
|
||||||
|
replay = 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Indicates if we are in reverse landscape orientation. */
|
||||||
|
val isReverseLandscape =
|
||||||
|
orientationInteractor.rotation
|
||||||
|
.transform { emit(it == Surface.ROTATION_270) }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
|
/** Indicates if we are in the landscape orientation */
|
||||||
|
val isLandscape =
|
||||||
|
orientationInteractor.rotation
|
||||||
|
.transform { emit(it == Surface.ROTATION_90) }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
/** This sends touch exploration events only used for debugging purposes. */
|
/** This sends touch exploration events only used for debugging purposes. */
|
||||||
val touchExplorationDebug: Flow<Point> =
|
val touchExplorationDebug: Flow<Point> =
|
||||||
@@ -170,6 +222,18 @@ class UdfpsViewModel(
|
|||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
|
/** The saved version of [guidedEnrollment] */
|
||||||
|
val guidedEnrollmentSaved: Flow<PointF> =
|
||||||
|
combineTransform(guidedEnrollment, shouldReplayLastEvent, enrollStage) {
|
||||||
|
point,
|
||||||
|
shouldReplay,
|
||||||
|
stage ->
|
||||||
|
if (shouldReplay && stage is EnrollStageModel.Guided) {
|
||||||
|
emit(point)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
enrollState
|
enrollState
|
||||||
@@ -200,7 +264,7 @@ class UdfpsViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
backgroundViewModel.background.filter { true }.collect { didGoToBackground() }
|
backgroundViewModel.background.filter { it }.collect { didGoToBackground() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,20 +274,12 @@ class UdfpsViewModel(
|
|||||||
displayDensityInteractor.displayDensity,
|
displayDensityInteractor.displayDensity,
|
||||||
displayDensityInteractor.defaultDisplayDensity,
|
displayDensityInteractor.defaultDisplayDensity,
|
||||||
displayDensityInteractor.fontScale,
|
displayDensityInteractor.fontScale,
|
||||||
orientationInteractor.rotation,
|
) { currDisplayDensity, defaultDisplayDensity, fontScale ->
|
||||||
) { currDisplayDensity, defaultDisplayDensity, fontScale, rotation ->
|
if (fontScale > 1.0f) {
|
||||||
val canShowLottieForRotation =
|
false
|
||||||
when (rotation) {
|
} else {
|
||||||
Surface.ROTATION_0 -> true
|
defaultDisplayDensity == currDisplayDensity
|
||||||
else -> false
|
}
|
||||||
}
|
|
||||||
|
|
||||||
canShowLottieForRotation &&
|
|
||||||
if (fontScale > 1.0f) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
defaultDisplayDensity == currDisplayDensity
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||||
|
|
||||||
@@ -234,6 +290,18 @@ class UdfpsViewModel(
|
|||||||
}
|
}
|
||||||
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
|
/** Indicates if we should or shold not draw the fingerprint icon */
|
||||||
|
val shouldDrawIcon: Flow<Boolean> =
|
||||||
|
enrollState.transform { state ->
|
||||||
|
when (state) {
|
||||||
|
is FingerEnrollState.EnrollProgress,
|
||||||
|
is FingerEnrollState.EnrollError,
|
||||||
|
is FingerEnrollState.PointerUp -> emit(true)
|
||||||
|
is FingerEnrollState.PointerDown -> emit(false)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val shouldClearDescriptionText = enrollStage.map { it is EnrollStageModel.Unknown }
|
private val shouldClearDescriptionText = enrollStage.map { it is EnrollStageModel.Unknown }
|
||||||
|
|
||||||
/** The description text for UDFPS enrollment */
|
/** The description text for UDFPS enrollment */
|
||||||
@@ -267,12 +335,12 @@ class UdfpsViewModel(
|
|||||||
|
|
||||||
/** Indicates the negative button has been clicked */
|
/** Indicates the negative button has been clicked */
|
||||||
fun negativeButtonClicked() {
|
fun negativeButtonClicked() {
|
||||||
doReset()
|
|
||||||
navigationViewModel.update(
|
navigationViewModel.update(
|
||||||
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
|
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
|
||||||
navStep,
|
navStep,
|
||||||
"$TAG#negativeButtonClicked",
|
"$TAG#negativeButtonClicked",
|
||||||
)
|
)
|
||||||
|
doReset()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Indicates that an enrollment was completed */
|
/** Indicates that an enrollment was completed */
|
||||||
@@ -282,7 +350,7 @@ class UdfpsViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Indicates that the application went to the background. */
|
/** Indicates that the application went to the background. */
|
||||||
private fun didGoToBackground() {
|
fun didGoToBackground() {
|
||||||
navigationViewModel.update(
|
navigationViewModel.update(
|
||||||
FingerprintAction.DID_GO_TO_BACKGROUND,
|
FingerprintAction.DID_GO_TO_BACKGROUND,
|
||||||
navStep,
|
navStep,
|
||||||
@@ -293,11 +361,7 @@ class UdfpsViewModel(
|
|||||||
|
|
||||||
private fun doReset() {
|
private fun doReset() {
|
||||||
_enrollState = fingerprintEnrollEnrollingViewModel.enrollFlow
|
_enrollState = fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||||
progressSaved =
|
_userInteractedWithSensor.update { false }
|
||||||
enrollState
|
|
||||||
.filterIsInstance<FingerEnrollState.EnrollProgress>()
|
|
||||||
.filterNotNull()
|
|
||||||
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The lottie that should be shown for UDFPS Enrollment */
|
/** The lottie that should be shown for UDFPS Enrollment */
|
||||||
@@ -320,6 +384,15 @@ class UdfpsViewModel(
|
|||||||
vibrationInteractor.vibrate(vibrationEvent, "UdfpsEnrollFragment")
|
vibrationInteractor.vibrate(vibrationEvent, "UdfpsEnrollFragment")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Indicates an error sent by the HAL has been acknowledged by the user */
|
||||||
|
fun errorDialogShown(it: FingerEnrollState.EnrollError) {
|
||||||
|
navigationViewModel.update(
|
||||||
|
FingerprintAction.USER_CLICKED_FINISH,
|
||||||
|
navStep,
|
||||||
|
"${TAG}#userClickedStopEnrollingDialog",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
class UdfpsEnrollmentFactory(
|
class UdfpsEnrollmentFactory(
|
||||||
private val vibrationInteractor: VibrationInteractor,
|
private val vibrationInteractor: VibrationInteractor,
|
||||||
private val displayDensityInteractor: DisplayDensityInteractor,
|
private val displayDensityInteractor: DisplayDensityInteractor,
|
||||||
@@ -332,6 +405,9 @@ class UdfpsViewModel(
|
|||||||
private val backgroundViewModel: BackgroundViewModel,
|
private val backgroundViewModel: BackgroundViewModel,
|
||||||
private val sensorRepository: FingerprintSensorRepository,
|
private val sensorRepository: FingerprintSensorRepository,
|
||||||
private val udfpsEnrollInteractor: UdfpsEnrollInteractor,
|
private val udfpsEnrollInteractor: UdfpsEnrollInteractor,
|
||||||
|
private val fingerprintManager: FingerprintManagerInteractor,
|
||||||
|
private val udfpsLastStepViewModel: UdfpsLastStepViewModel,
|
||||||
|
private val accessibilityInteractor: AccessibilityInteractor,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -348,6 +424,9 @@ class UdfpsViewModel(
|
|||||||
backgroundViewModel,
|
backgroundViewModel,
|
||||||
sensorRepository,
|
sensorRepository,
|
||||||
udfpsEnrollInteractor,
|
udfpsEnrollInteractor,
|
||||||
|
fingerprintManager,
|
||||||
|
udfpsLastStepViewModel,
|
||||||
|
accessibilityInteractor,
|
||||||
)
|
)
|
||||||
as T
|
as T
|
||||||
}
|
}
|
||||||
|
@@ -36,8 +36,8 @@ import android.util.PathParser
|
|||||||
import android.view.animation.AccelerateDecelerateInterpolator
|
import android.view.animation.AccelerateDecelerateInterpolator
|
||||||
import androidx.core.animation.addListener
|
import androidx.core.animation.addListener
|
||||||
import androidx.core.graphics.toRect
|
import androidx.core.graphics.toRect
|
||||||
import androidx.core.graphics.toRectF
|
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,7 +45,6 @@ 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
|
||||||
@@ -55,7 +54,7 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
@ColorInt private var movingTargetFill = 0
|
@ColorInt private var movingTargetFill = 0
|
||||||
private var currentScale = 1.0f
|
private var currentScale = 1.0f
|
||||||
private var alpha = 0
|
private var alpha = 0
|
||||||
private var guidedEnrollmentOffset: PointF? = null
|
private var stopDrawing = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the physical location of the sensor. This rect will be updated by [drawSensorRectAt]
|
* This is the physical location of the sensor. This rect will be updated by [drawSensorRectAt]
|
||||||
@@ -63,15 +62,12 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
private val sensorRectBounds: Rect = Rect()
|
private val sensorRectBounds: Rect = Rect()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The following values are used to describe where the icon should be drawn. [currX] and [currY]
|
* The following values are used to describe where the icon should be drawn. [sensorLeftOffset]
|
||||||
* are changed based on the current guided enrollment step which is given by the
|
* and [sensorTopOffset] are changed based on the current guided enrollment step which is given by
|
||||||
* [UdfpsEnrollHelperV2]
|
* the [UdfpsEnrollHelperV2]
|
||||||
*/
|
*/
|
||||||
private var currX = 0f
|
private var sensorLeftOffset = 0f
|
||||||
private var currY = 0f
|
private var sensorTopOffset = 0f
|
||||||
|
|
||||||
private var sensorWidth = 0f
|
|
||||||
private var sensorHeight = 0f
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
fingerprintDrawable = createUdfpsIcon(context)
|
fingerprintDrawable = createUdfpsIcon(context)
|
||||||
@@ -131,29 +127,35 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
* The [sensorRect] coordinates for the sensor area. The [sensorRect] should be the coordinates
|
* The [sensorRect] coordinates for the sensor area. The [sensorRect] should be the coordinates
|
||||||
* with respect to its root frameview
|
* with respect to its root frameview
|
||||||
*/
|
*/
|
||||||
fun drawSensorRectAt(sensorRect: Rect) {
|
fun drawSensorRectAt(overlayParams: UdfpsOverlayParams) {
|
||||||
Log.e(TAG, "UdfpsEnrollIcon#drawSensorRect($sensorRect)")
|
Log.e(TAG, "UdfpsEnrollIcon#drawSensorRect(${overlayParams.sensorBounds})")
|
||||||
|
val sensorRect = overlayParams.sensorBounds
|
||||||
sensorRectBounds.set(sensorRect)
|
sensorRectBounds.set(sensorRect)
|
||||||
fingerprintDrawable.bounds = sensorRect
|
fingerprintDrawable.bounds = sensorRect
|
||||||
movingTargetFpIcon.bounds = sensorRect
|
movingTargetFpIcon.bounds = sensorRect
|
||||||
currX = sensorRect.left.toFloat()
|
|
||||||
currY = sensorRect.top.toFloat()
|
// End existing animation if we get an update of the sensor rect.
|
||||||
sensorWidth = (sensorRect.right - sensorRect.left).toFloat()
|
targetAnimatorSet?.end()
|
||||||
sensorHeight = (sensorRect.bottom - sensorRect.top).toFloat()
|
|
||||||
invalidateSelf()
|
invalidateSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stop drawing the fingerprint icon. */
|
/** Stop drawing the fingerprint icon. */
|
||||||
fun stopDrawing() {
|
fun stopDrawing() {
|
||||||
alpha = 0
|
stopDrawing = true
|
||||||
|
invalidateSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resume drawing the fingerprint icon */
|
/** Resume drawing the fingerprint icon */
|
||||||
fun startDrawing() {
|
fun startDrawing() {
|
||||||
alpha = 255
|
stopDrawing = false
|
||||||
|
invalidateSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun draw(canvas: Canvas) {
|
override fun draw(canvas: Canvas) {
|
||||||
|
if (stopDrawing) {
|
||||||
|
return
|
||||||
|
}
|
||||||
movingTargetFpIcon.alpha = alpha
|
movingTargetFpIcon.alpha = alpha
|
||||||
fingerprintDrawable.setAlpha(alpha)
|
fingerprintDrawable.setAlpha(alpha)
|
||||||
sensorOutlinePaint.setAlpha(alpha)
|
sensorOutlinePaint.setAlpha(alpha)
|
||||||
@@ -165,23 +167,28 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
fingerprintDrawable.draw(canvas)
|
fingerprintDrawable.draw(canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCurrLocation(): RectF =
|
private fun getCurrLocation(): RectF {
|
||||||
RectF(currX, currY, currX + sensorWidth, currY + sensorHeight)
|
val x = sensorRectBounds.left + sensorLeftOffset
|
||||||
|
val y = sensorRectBounds.top + sensorTopOffset
|
||||||
|
return RectF(x, y, x + sensorRectBounds.width(), y + sensorRectBounds.height())
|
||||||
|
}
|
||||||
|
|
||||||
private fun animateMovement(currentBounds: Rect, offsetRect: RectF, scaleMovement: Boolean) {
|
private fun animateMovement(leftOffset: Float, topOffset: Float, scaleMovement: Boolean) {
|
||||||
if (currentBounds.equals(offsetRect)) {
|
if (leftOffset == sensorLeftOffset && topOffset == sensorTopOffset) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val xAnimator = ValueAnimator.ofFloat(currentBounds.left.toFloat(), offsetRect.left)
|
val currLocation = getCurrLocation()
|
||||||
|
|
||||||
|
val xAnimator = ValueAnimator.ofFloat(currLocation.left - sensorRectBounds.left, leftOffset)
|
||||||
xAnimator.addUpdateListener {
|
xAnimator.addUpdateListener {
|
||||||
currX = it.animatedValue as Float
|
sensorLeftOffset = it.animatedValue as Float
|
||||||
invalidateSelf()
|
invalidateSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
val yAnimator = ValueAnimator.ofFloat(currentBounds.top.toFloat(), offsetRect.top)
|
val yAnimator = ValueAnimator.ofFloat(currLocation.top - sensorRectBounds.top, topOffset)
|
||||||
yAnimator.addUpdateListener {
|
yAnimator.addUpdateListener {
|
||||||
currY = it.animatedValue as Float
|
sensorTopOffset = it.animatedValue as Float
|
||||||
invalidateSelf()
|
invalidateSelf()
|
||||||
}
|
}
|
||||||
val animators = mutableListOf(xAnimator, yAnimator)
|
val animators = mutableListOf(xAnimator, yAnimator)
|
||||||
@@ -199,6 +206,7 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
animators.add(scaleAnimator)
|
animators.add(scaleAnimator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targetAnimatorSet?.cancel()
|
||||||
targetAnimatorSet = AnimatorSet()
|
targetAnimatorSet = AnimatorSet()
|
||||||
|
|
||||||
targetAnimatorSet?.let {
|
targetAnimatorSet?.let {
|
||||||
@@ -209,51 +217,14 @@ 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
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates a change to guided enrollment has occurred. Also indicates if we are recreating the
|
* Indicates a change to guided enrollment has occurred. Also indicates if we are recreating the
|
||||||
* view, in which case their is no need to animate the icon to whatever position it was in.
|
* view, in which case their is no need to animate the icon to whatever position it was in.
|
||||||
*/
|
*/
|
||||||
fun updateGuidedEnrollment(point: PointF, isRecreating: Boolean) {
|
fun updateGuidedEnrollment(point: PointF, isRecreating: Boolean) {
|
||||||
guidedEnrollmentOffset = point
|
val pointIsZero = point.x == 0f && point.y == 0f
|
||||||
if (isRecreating) {
|
val shouldAnimateMovement = pointIsZero || !isRecreating
|
||||||
setAnimationTimeToZero()
|
animateMovement(point?.x ?: 0f, point?.y ?: 0f, shouldAnimateMovement)
|
||||||
} else {
|
|
||||||
restoreAnimationTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentBounds = getCurrLocation().toRect()
|
|
||||||
val offset = guidedEnrollmentOffset
|
|
||||||
if (offset?.x != 0f && offset?.y != 0f) {
|
|
||||||
val targetRect = Rect(sensorRectBounds).toRectF()
|
|
||||||
// This is the desired location of the sensor rect, the [EnrollHelper]
|
|
||||||
// offsets the initial sensor rect by a bit to get the user to move their finger a bit more.
|
|
||||||
targetRect.offset(offset!!.x, offset!!.y)
|
|
||||||
val shouldAnimateMovement = !currentBounds.equals(targetRect)
|
|
||||||
if (shouldAnimateMovement) {
|
|
||||||
targetAnimatorSet?.cancel()
|
|
||||||
animateMovement(currentBounds, targetRect, true)
|
|
||||||
} else {
|
|
||||||
// If we are not offsetting the sensor, move it back to its original place
|
|
||||||
animateMovement(currentBounds, sensorRectBounds.toRectF(), false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If we are not offsetting the sensor, move it back to its original place
|
|
||||||
animateMovement(currentBounds, sensorRectBounds.toRectF(), false)
|
|
||||||
}
|
|
||||||
invalidateSelf()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@@ -27,16 +27,15 @@ import android.graphics.Rect
|
|||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.util.Log
|
|
||||||
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 android.view.animation.OvershootInterpolator
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.core.animation.addListener
|
|
||||||
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 com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
@@ -145,7 +144,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
|||||||
|
|
||||||
/** Indicates enrollment progress has occurred. */
|
/** Indicates enrollment progress has occurred. */
|
||||||
fun onEnrollmentProgress(remaining: Int, totalSteps: Int, isRecreating: Boolean = false) {
|
fun onEnrollmentProgress(remaining: Int, totalSteps: Int, isRecreating: Boolean = false) {
|
||||||
|
|
||||||
afterFirstTouch = true
|
afterFirstTouch = true
|
||||||
updateProgress(remaining, totalSteps, isRecreating)
|
updateProgress(remaining, totalSteps, isRecreating)
|
||||||
}
|
}
|
||||||
@@ -216,8 +214,8 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
|||||||
* Draws the progress with locations [sensorLocationX] [sensorLocationY], note these must be with
|
* Draws the progress with locations [sensorLocationX] [sensorLocationY], note these must be with
|
||||||
* respect to the parent framelayout.
|
* respect to the parent framelayout.
|
||||||
*/
|
*/
|
||||||
fun drawProgressAt(sensorRect: Rect) {
|
fun drawProgressAt(overlayParams: UdfpsOverlayParams) {
|
||||||
this.sensorRect.set(sensorRect)
|
this.sensorRect.set(overlayParams.sensorBounds)
|
||||||
invalidateSelf()
|
invalidateSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,8 +247,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
|||||||
restoreAnimationTime()
|
restoreAnimationTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.remainingSteps = remainingSteps
|
|
||||||
this.totalSteps = totalSteps
|
|
||||||
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))
|
||||||
@@ -290,12 +286,8 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
|||||||
checkMarkDrawable.bounds = newBounds
|
checkMarkDrawable.bounds = newBounds
|
||||||
checkMarkDrawable.setVisible(true, false)
|
checkMarkDrawable.setVisible(true, false)
|
||||||
}
|
}
|
||||||
doOnEnd {
|
doOnEnd { onFinishedCompletionAnimation?.let { it() } }
|
||||||
onFinishedCompletionAnimation?.let{
|
|
||||||
it()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
start()
|
start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,6 +348,7 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
|||||||
private fun flashHelpFillColor() {
|
private fun flashHelpFillColor() {
|
||||||
if (fillColorAnimator != null && fillColorAnimator!!.isRunning) {
|
if (fillColorAnimator != null && fillColorAnimator!!.isRunning) {
|
||||||
fillColorAnimator!!.end()
|
fillColorAnimator!!.end()
|
||||||
|
fillColorAnimator = null
|
||||||
}
|
}
|
||||||
@ColorInt val targetColor = helpColor
|
@ColorInt val targetColor = helpColor
|
||||||
fillColorAnimator =
|
fillColorAnimator =
|
||||||
@@ -375,7 +368,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
|||||||
* want to re-animate the progress/success animation with the default timer
|
* want to re-animate the progress/success animation with the default timer
|
||||||
*/
|
*/
|
||||||
private fun setAnimationTimeToZero() {
|
private fun setAnimationTimeToZero() {
|
||||||
fillColorAnimationDuration = 0
|
|
||||||
animateArcDuration = 0
|
animateArcDuration = 0
|
||||||
checkmarkAnimationDelayDuration = 0
|
checkmarkAnimationDelayDuration = 0
|
||||||
checkmarkAnimationDuration = 0
|
checkmarkAnimationDuration = 0
|
||||||
@@ -383,7 +375,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
|||||||
|
|
||||||
/** This sets animation timers back to normal, this happens after we have */
|
/** This sets animation timers back to normal, this happens after we have */
|
||||||
private fun restoreAnimationTime() {
|
private fun restoreAnimationTime() {
|
||||||
fillColorAnimationDuration = FILL_COLOR_ANIMATION_DURATION_MS
|
|
||||||
animateArcDuration = PROGRESS_ANIMATION_DURATION_MS
|
animateArcDuration = PROGRESS_ANIMATION_DURATION_MS
|
||||||
checkmarkAnimationDelayDuration = CHECKMARK_ANIMATION_DELAY_MS
|
checkmarkAnimationDelayDuration = CHECKMARK_ANIMATION_DELAY_MS
|
||||||
checkmarkAnimationDuration = CHECKMARK_ANIMATION_DURATION_MS
|
checkmarkAnimationDuration = CHECKMARK_ANIMATION_DURATION_MS
|
||||||
|
@@ -70,27 +70,11 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
fun setSensorRect(rect: Rect, sensorType: FingerprintSensorType) {
|
fun setSensorRect(rect: Rect, sensorType: FingerprintSensorType) {
|
||||||
this.sensorRect = rect
|
this.sensorRect = rect
|
||||||
this.fingerprintSensorType = sensorType
|
this.fingerprintSensorType = sensorType
|
||||||
findViewById<ImageView?>(R.id.udfps_enroll_animation_fp_progress_view)?.also {
|
|
||||||
it.setImageDrawable(fingerprintProgressDrawable)
|
|
||||||
}
|
|
||||||
findViewById<ImageView>(R.id.udfps_enroll_animation_fp_view)?.also {
|
|
||||||
it.setImageDrawable(fingerprintIcon)
|
|
||||||
}
|
|
||||||
|
|
||||||
val rotation = display.rotation
|
|
||||||
var displayInfo = DisplayInfo()
|
var displayInfo = DisplayInfo()
|
||||||
context.display.getDisplayInfo(displayInfo)
|
context.display.getDisplayInfo(displayInfo)
|
||||||
|
val rotation = displayInfo.rotation
|
||||||
val scaleFactor = udfpsUtils.getScaleFactor(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]
|
||||||
@@ -99,22 +83,44 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
// If the view has been rotated, we need to translate the sensor coordinates
|
// If the view has been rotated, we need to translate the sensor coordinates
|
||||||
// to the new rotated view.
|
// to the new rotated view.
|
||||||
when (rotation) {
|
when (rotation) {
|
||||||
Surface.ROTATION_90,
|
Surface.ROTATION_90 -> {
|
||||||
Surface.ROTATION_270 -> {
|
|
||||||
sensorRectOffset.set(
|
sensorRectOffset.set(
|
||||||
sensorRectOffset.top,
|
sensorRectOffset.top,
|
||||||
sensorRectOffset.left,
|
sensorRectOffset.left,
|
||||||
sensorRectOffset.bottom,
|
sensorRectOffset.bottom,
|
||||||
sensorRectOffset.right,
|
sensorRectOffset.right,
|
||||||
)
|
)
|
||||||
|
sensorRectOffset.offset(-parentLeft, -parentTop)
|
||||||
}
|
}
|
||||||
else -> {}
|
// When the view is rotated 270 degrees, 0,0 is the top corner left
|
||||||
}
|
Surface.ROTATION_270 -> {
|
||||||
// Translate the sensor position into UdfpsEnrollView's view space.
|
sensorRectOffset.set(
|
||||||
sensorRectOffset.offset(-parentLeft, -parentTop)
|
(displayInfo.naturalHeight - sensorRectOffset.bottom) - parentLeft,
|
||||||
|
sensorRectOffset.left - parentTop,
|
||||||
|
(displayInfo.naturalHeight - sensorRectOffset.top) - parentLeft,
|
||||||
|
sensorRectOffset.right - parentTop,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
|
||||||
fingerprintIcon.drawSensorRectAt(sensorRectOffset)
|
sensorRectOffset.offset(-parentLeft, -parentTop)
|
||||||
fingerprintProgressDrawable.drawProgressAt(sensorRectOffset)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate the sensor position into UdfpsEnrollView's view space.
|
||||||
|
val overlayParams =
|
||||||
|
UdfpsOverlayParams(
|
||||||
|
sensorRectOffset,
|
||||||
|
fingerprintProgressDrawable.bounds,
|
||||||
|
displayInfo.naturalWidth,
|
||||||
|
displayInfo.naturalHeight,
|
||||||
|
scaleFactor,
|
||||||
|
rotation,
|
||||||
|
sensorType.toInt(),
|
||||||
|
)
|
||||||
|
|
||||||
|
fingerprintIcon.drawSensorRectAt(overlayParams)
|
||||||
|
fingerprintProgressDrawable.drawProgressAt(overlayParams)
|
||||||
|
|
||||||
touchExplorationAnnouncer = TouchExplorationAnnouncer(context, this, overlayParams, udfpsUtils)
|
touchExplorationAnnouncer = TouchExplorationAnnouncer(context, this, overlayParams, udfpsUtils)
|
||||||
}
|
}
|
||||||
@@ -126,11 +132,8 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
onEnrollmentProgress(event.remainingSteps, event.totalStepsRequired)
|
onEnrollmentProgress(event.remainingSteps, event.totalStepsRequired)
|
||||||
is FingerEnrollState.Acquired -> onAcquired(event.acquiredGood)
|
is FingerEnrollState.Acquired -> onAcquired(event.acquiredGood)
|
||||||
is FingerEnrollState.EnrollHelp -> onEnrollmentHelp()
|
is FingerEnrollState.EnrollHelp -> onEnrollmentHelp()
|
||||||
is FingerEnrollState.PointerDown -> onPointerDown()
|
// Else ignore
|
||||||
is FingerEnrollState.PointerUp -> onPointerUp()
|
else -> {}
|
||||||
is FingerEnrollState.OverlayShown -> overlayShown()
|
|
||||||
is FingerEnrollState.EnrollError ->
|
|
||||||
throw IllegalArgumentException("$TAG should not handle udfps error")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +148,6 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun udfpsError(errMsgId: Int, errString: String) {}
|
|
||||||
/**
|
/**
|
||||||
* Sends a touch exploration event to the [onHoverListener] this should only be used for
|
* Sends a touch exploration event to the [onHoverListener] this should only be used for
|
||||||
* debugging.
|
* debugging.
|
||||||
@@ -170,8 +172,15 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
onHoverListener = listener
|
onHoverListener = listener
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun overlayShown() {
|
/** Indicates the overlay has been shown */
|
||||||
Log.e(TAG, "Implement overlayShown")
|
fun overlayShown() {
|
||||||
|
Log.d(TAG, "Showing udfps overlay")
|
||||||
|
findViewById<ImageView?>(R.id.udfps_enroll_animation_fp_progress_view)?.also {
|
||||||
|
it.setImageDrawable(fingerprintProgressDrawable)
|
||||||
|
}
|
||||||
|
findViewById<ImageView>(R.id.udfps_enroll_animation_fp_view)?.also {
|
||||||
|
it.setImageDrawable(fingerprintIcon)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Receive enroll progress event */
|
/** Receive enroll progress event */
|
||||||
@@ -190,16 +199,6 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
if (animateIfLastStepGood) fingerprintProgressDrawable.onLastStepAcquired()
|
if (animateIfLastStepGood) fingerprintProgressDrawable.onLastStepAcquired()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Receive onPointerDown event */
|
|
||||||
private fun onPointerDown() {
|
|
||||||
fingerprintIcon.stopDrawing()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Receive onPointerUp event */
|
|
||||||
private fun onPointerUp() {
|
|
||||||
fingerprintIcon.startDrawing()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||||
super.onLayout(changed, left, top, right, bottom)
|
super.onLayout(changed, left, top, right, bottom)
|
||||||
// Because the layout has changed, we need to recompute all locations.
|
// Because the layout has changed, we need to recompute all locations.
|
||||||
@@ -261,6 +260,17 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
fingerprintIcon.updateGuidedEnrollment(point, false)
|
fingerprintIcon.updateGuidedEnrollment(point, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Indicates if the enroll icon should be drawn. */
|
||||||
|
fun shouldDrawIcon(it: Boolean) {
|
||||||
|
post {
|
||||||
|
if (it) {
|
||||||
|
fingerprintIcon.startDrawing()
|
||||||
|
} else {
|
||||||
|
fingerprintIcon.stopDrawing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "UdfpsEnrollView"
|
private const val TAG = "UdfpsEnrollView"
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
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 com.android.systemui.biometrics.shared.model.FingerprintSensor
|
||||||
@@ -68,7 +69,8 @@ class FingerprintEnrollEnrollingViewModel(
|
|||||||
val enrollFlow =
|
val enrollFlow =
|
||||||
enrollFlowShouldBeRunning.transformLatest {
|
enrollFlowShouldBeRunning.transformLatest {
|
||||||
if (it) {
|
if (it) {
|
||||||
fingerprintEnrollViewModel.enrollFlow.collect { event -> emit(event) }
|
fingerprintEnrollViewModel.enrollFlow.collect { event ->
|
||||||
|
emit(event) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,4 +84,8 @@ class FingerprintEnrollEnrollingViewModel(
|
|||||||
as T
|
as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = "FingerprintEnrollEnrollingViewModel"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -118,7 +118,12 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
|
|||||||
|
|
||||||
var rfpsIconTouchViewModel = RFPSIconTouchViewModel()
|
var rfpsIconTouchViewModel = RFPSIconTouchViewModel()
|
||||||
var rfpsViewModel =
|
var rfpsViewModel =
|
||||||
RFPSViewModel(fingerprintEnrollEnrollingViewModel, navigationViewModel, orientationInteractor)
|
RFPSViewModel(
|
||||||
|
fingerprintEnrollEnrollingViewModel,
|
||||||
|
navigationViewModel,
|
||||||
|
orientationInteractor,
|
||||||
|
interactor,
|
||||||
|
)
|
||||||
|
|
||||||
val fingerprintEnrollConfirmationViewModel =
|
val fingerprintEnrollConfirmationViewModel =
|
||||||
FingerprintEnrollConfirmationViewModel(navigationViewModel, interactor)
|
FingerprintEnrollConfirmationViewModel(navigationViewModel, interactor)
|
||||||
@@ -151,7 +156,8 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
|
|||||||
BackgroundViewModel::class.java -> backgroundViewModel
|
BackgroundViewModel::class.java -> backgroundViewModel
|
||||||
RFPSIconTouchViewModel::class.java -> rfpsIconTouchViewModel
|
RFPSIconTouchViewModel::class.java -> rfpsIconTouchViewModel
|
||||||
FingerprintEnrollEnrollingViewModel::class.java -> fingerprintEnrollEnrollingViewModel
|
FingerprintEnrollEnrollingViewModel::class.java -> fingerprintEnrollEnrollingViewModel
|
||||||
FingerprintEnrollConfirmationViewModel::class.java -> fingerprintEnrollConfirmationViewModel
|
FingerprintEnrollConfirmationViewModel::class.java ->
|
||||||
|
fingerprintEnrollConfirmationViewModel
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
as T
|
as T
|
||||||
|
Reference in New Issue
Block a user