Merge "Refactor preference controllers to receive and mutate ZenMode/ZenPolicy" into main
diff --git a/res/layout-land/fingerprint_v2_udfps_enroll_enrolling.xml b/res/layout-land/fingerprint_v2_udfps_enroll_enrolling.xml
index 86768d6..669c282 100644
--- a/res/layout-land/fingerprint_v2_udfps_enroll_enrolling.xml
+++ b/res/layout-land/fingerprint_v2_udfps_enroll_enrolling.xml
@@ -14,87 +14,65 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<LinearLayout
+<!-- This is used to grab style attributes and apply them
+to this layout -->
+<com.google.android.setupdesign.GlifLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/udfps_layout"
+ android:id="@+id/glif_layout"
style="?attr/fingerprint_layout_theme"
android:layout_width="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_height="match_parent"
- android:orientation="horizontal">
-
- <!-- This is used to grab style attributes and apply them
- to this layout -->
- <com.google.android.setupdesign.GlifLayout
- android:id="@+id/dummy_glif_layout"
- style="?attr/fingerprint_layout_theme"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:visibility="gone"
- />
+ >
<LinearLayout
- android:layout_width="300dp"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <ImageView
- android:id="@+id/sud_layout_icon"
- style="@style/SudGlifIcon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scaleType="fitStart"
- android:src="@drawable/ic_lock" />
-
- <TextView
- android:id="@+id/title"
- style="@style/SudGlifHeaderTitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:lines="2"
- />
-
- <TextView
- android:id="@+id/description"
- style="@style/SudDescription.Glif"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:lines="3"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
- />
-
-
- <com.airbnb.lottie.LottieAnimationView
- android:id="@+id/illustration_lottie"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
- android:scaleType="centerInside"
- android:visibility="gone"
- app:lottie_autoPlay="true"
- app:lottie_loop="true"
- app:lottie_speed=".85"
- />
- </LinearLayout>
-
- <FrameLayout
- android:id="@+id/layout_container"
+ style="@style/SudContentFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center_horizontal|bottom"
+ android:clipChildren="false"
android:clipToPadding="false"
+ android:orientation="vertical"
>
- <include layout="@layout/fingerprint_v2_udfps_enroll_view" />
- </FrameLayout>
+ <LinearLayout
+ 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"
+ >
-</LinearLayout>
+ <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>
+
diff --git a/res/layout/fingerprint_v2_udfps_enroll_enrolling.xml b/res/layout/fingerprint_v2_udfps_enroll_enrolling.xml
index ab8fb2c..497e164 100644
--- a/res/layout/fingerprint_v2_udfps_enroll_enrolling.xml
+++ b/res/layout/fingerprint_v2_udfps_enroll_enrolling.xml
@@ -19,7 +19,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/udfps_layout"
- style="?attr/fingerprint_layout_theme"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -27,65 +26,57 @@
<!-- This is used to grab style attributes and apply them
to this layout -->
<com.google.android.setupdesign.GlifLayout
- android:id="@+id/dummy_glif_layout"
+ android:id="@+id/glif_layout"
style="?attr/fingerprint_layout_theme"
- android:layout_width="0dp"
+ android:layout_width="match_parent"
android:layout_height="0dp"
- android:visibility="gone"
- />
+ android:layout_weight="5"
+ >
- <ImageView
- android:id="@+id/sud_layout_icon"
- style="@style/SudGlifIcon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scaleType="fitStart"
- android:src="@drawable/ic_lock" />
+ <LinearLayout
+ style="@style/SudContentFrame"
+ android:id="@+id/sud_content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
- <TextView
- android:id="@+id/title"
- style="@style/SudGlifHeaderTitle"
- android:layout_width="match_parent"
- android:layout_height="80dp"
- 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="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" />
+ <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" />
+ </LinearLayout>
+ </com.google.android.setupdesign.GlifLayout>
<FrameLayout
android:id="@+id/layout_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="0dp"
android:layout_gravity="center_horizontal|bottom"
+ android:layout_weight="4"
android:clipToPadding="false"
+ android:clipChildren="false"
>
<include layout="@layout/fingerprint_v2_udfps_enroll_view" />
- </FrameLayout>
+ <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>
</LinearLayout>
diff --git a/res/layout/fingerprint_v2_udfps_enroll_view.xml b/res/layout/fingerprint_v2_udfps_enroll_view.xml
index 20df6e1..9002eca 100644
--- a/res/layout/fingerprint_v2_udfps_enroll_view.xml
+++ b/res/layout/fingerprint_v2_udfps_enroll_view.xml
@@ -21,6 +21,8 @@
android:id="@+id/udfps_animation_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:orientation="vertical">
<ImageView
diff --git a/res/raw/udfps_left_edge_hint_lottie.json b/res/raw/udfps_left_edge_hint_lottie.json
index 9e26dfe..e3c4ce1 100644
--- a/res/raw/udfps_left_edge_hint_lottie.json
+++ b/res/raw/udfps_left_edge_hint_lottie.json
@@ -1 +1 @@
-{}
\ No newline at end of file
+{"v":"5.8.1","fr":60,"ip":0,"op":211,"w":412,"h":200,"nm":"FPS Left Side","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":55,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":85,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":115,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":140,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":165,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":170,"s":[100]},{"t":195,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[169.175,161.377,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[7.894,8.852],[0,0]],"v":[[-7.497,-6.019],[7.497,6.019]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-13.066,-25.141],[-1.414,-2.132]],"o":[[0,0],[1.326,2.55],[0,0]],"v":[[-25.707,-58.269],[-17.526,-20.998],[-13.399,-13.981]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[-10.063,-10.595],[0,0]],"o":[[0.312,1.13],[11.84,12.467],[0,0]],"v":[[-8.095,-38.352],[4.748,-14.866],[28.345,-2.398]],"c":false},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.68235296011,0.796078443527,0.980392158031,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Fingerprint Side","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[169.175,161.377,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[7.894,8.852],[0,0]],"v":[[-7.497,-6.019],[7.497,6.019]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-13.066,-25.141],[-1.414,-2.132]],"o":[[0,0],[1.326,2.55],[0,0]],"v":[[-25.707,-58.269],[-17.526,-20.998],[-13.399,-13.981]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[-10.063,-10.595],[0,0]],"o":[[0.312,1.13],[11.84,12.467],[0,0]],"v":[[-8.095,-38.352],[4.748,-14.866],[28.345,-2.398]],"c":false},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[3.585,11.306],[0,0]],"v":[[90.35,-61.769],[93.054,-42.731]],"c":false},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0,0],[-14.956,-24.065],[-1.112,-2.304]],"o":[[0,0],[1.517,2.441],[0,0]],"v":[[54.72,-104.215],[82.601,-78.164],[86.53,-71.035]],"c":false},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ind":5,"ty":"sh","ix":6,"ks":{"a":0,"k":{"i":[[0,0],[-3.978,-14.061],[0,0]],"o":[[0.813,0.844],[4.681,16.544],[0,0]],"v":[[63.033,-78.092],[76.68,-55.064],[75.364,-28.408]],"c":false},"ix":2},"nm":"Path 6","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.596069335938,0.321563720703,0.239196777344,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":7,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Fingerprint","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[176.139,66.987,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.408,-0.232],[1.017,0.68],[-0.959,1.434],[-6.841,3.896],[-7.562,0.41],[-0.093,-1.722],[1.724,-0.094],[6.003,-3.419],[3.674,-5.493]],"o":[[-0.995,0.567],[-1.433,-0.959],[4.21,-6.294],[6.841,-3.896],[1.722,-0.093],[0.094,1.722],[-6.599,0.357],[-6.003,3.418],[-0.279,0.417]],"v":[[-16.693,13.1],[-19.976,12.983],[-20.836,8.652],[-3.944,-6.925],[18.072,-13.506],[21.359,-10.556],[18.409,-7.27],[-0.853,-1.498],[-15.645,12.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[7.044,-4.012],[6.96,0.908],[-0.223,1.71],[-1.711,-0.224],[-5.298,3.017],[-2.065,5.824],[-1.624,-0.577],[0.576,-1.626]],"o":[[-6.489,3.695],[-1.71,-0.223],[0.223,-1.71],[5.601,0.73],[5.742,-3.27],[0.577,-1.625],[1.625,0.576],[-2.566,7.236]],"v":[[44.597,78.169],[24.039,82.43],[21.346,78.93],[24.846,76.237],[41.507,72.742],[53.614,58.64],[57.601,56.74],[59.5,60.727]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.323,-0.184],[1.029,0.844],[0.13,0.229],[-15.469,8.81],[-8.28,-14.539],[1.498,-0.853],[0.853,1.498],[12.426,-7.076],[-6.547,-11.496],[-4.085,-3.35],[1.094,-1.333]],"o":[[-1.091,0.621],[-5.004,-4.103],[-8.28,-14.539],[15.469,-8.81],[0.853,1.498],[-1.498,0.853],[-6.547,-11.496],[-12.426,7.077],[0.024,0.042],[1.333,1.093],[-0.25,0.305]],"v":[[10.147,71.146],[6.621,70.847],[-1.636,61.086],[11.185,19.448],[53.537,29.665],[52.369,33.924],[48.111,32.756],[14.276,24.874],[3.791,57.996],[10.581,66.018],[11.016,70.413]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[1.183,-0.674],[0.287,-0.071],[3.505,8.779],[-6.102,3.475],[-3.887,-4.766],[0,0],[-3.067,1.747],[1.97,3.459],[0,0],[-1.498,0.853],[-0.853,-1.498],[0,0],[6.451,-3.674],[4.175,5.108],[0,0],[2.675,-1.523],[-1.489,-3.73],[-5.283,1.305],[-0.414,-1.674]],"o":[[-0.244,0.139],[-9.188,2.268],[-2.649,-6.633],[5.335,-3.038],[0,0],[2.235,2.735],[3.459,-1.97],[0,0],[-0.853,-1.498],[1.498,-0.853],[0,0],[3.674,6.452],[-5.725,3.26],[0,0],[-1.949,-2.391],[-3.377,1.923],[2.674,6.697],[1.674,-0.413],[0.342,1.387]],"v":[[36.276,64.961],[35.477,65.279],[11.151,49.991],[16.667,33.848],[32.582,36.831],[36.686,41.876],[45.842,43.586],[48.542,33.739],[47.994,32.777],[49.163,28.518],[53.421,29.686],[53.969,30.648],[48.932,49.012],[31.846,45.823],[27.739,40.774],[19.758,39.275],[16.951,47.675],[33.98,59.216],[37.761,61.499]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0.501,-0.285],[0.978,0.515],[11.066,-6.302],[0.514,-9.958],[1.721,0.088],[-0.089,1.722],[-13.068,7.442],[-10.828,-5.692],[0.802,-1.526]],"o":[[-0.895,0.51],[-8.827,-4.639],[-11.066,6.302],[-0.089,1.722],[-1.722,-0.089],[0.63,-12.217],[13.068,-7.442],[1.527,0.802],[-0.288,0.548]],"v":[[42.238,8.912],[39.237,8.964],[6.49,11.701],[-12.571,38.468],[-15.851,41.426],[-18.808,38.146],[3.4,6.274],[42.142,3.436],[43.453,7.653]],"c":true},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.596078455448,0.321568638086,0.239215686917,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Thumb","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[225.438,108.028,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[42.783,10.613],[3.8,0.302],[0,0],[0.105,0.008],[0.484,0.019],[6.105,-2.438],[0,0],[0.032,-0.013],[0.85,-0.396],[0,0],[0,0],[0,0],[0,0],[-26.827,-45.427],[0,0],[0,0]],"o":[[-28.424,-53.851],[-3.608,-1.195],[0,0],[-0.106,-0.009],[-0.482,-0.035],[-6.685,-0.314],[0,0],[-0.032,0.013],[-0.853,0.343],[-20.172,9.126],[0,0],[-6.217,10.813],[0,0],[-5.276,37.379],[0,0],[0,0],[0,0]],"v":[[76.157,43.103],[-34.58,-89.69],[-45.717,-91.957],[-45.708,-91.966],[-46.022,-91.983],[-47.471,-92.063],[-66.474,-88.859],[-66.467,-88.866],[-66.56,-88.826],[-69.115,-87.715],[-94.124,-66.879],[-94.112,-66.885],[-101.514,-44.23],[-101.507,-44.234],[-53.003,92.116],[30.37,92.116],[102.173,92.116]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431391716,0.403921574354,0.305882364511,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".grey900","cl":"grey900","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,100,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[412,200],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":38,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.1254902035,0.129411771894,0.141176477075,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/res/raw/udfps_right_edge_hint_lottie.json b/res/raw/udfps_right_edge_hint_lottie.json
index 9e26dfe..93a75b3 100644
--- a/res/raw/udfps_right_edge_hint_lottie.json
+++ b/res/raw/udfps_right_edge_hint_lottie.json
@@ -1 +1 @@
-{}
\ No newline at end of file
+{"v":"5.8.1","fr":60,"ip":0,"op":211,"w":412,"h":200,"nm":"FPS Right Side","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":55,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":85,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":115,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":140,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":165,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":170,"s":[100]},{"t":195,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[169.175,161.377,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[3.585,11.306],[0,0]],"v":[[90.35,-61.769],[93.054,-42.731]],"c":false},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-14.956,-24.065],[-1.112,-2.304]],"o":[[0,0],[1.517,2.441],[0,0]],"v":[[54.72,-104.215],[82.601,-78.164],[86.53,-71.035]],"c":false},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[-3.978,-14.061],[0,0]],"o":[[0.813,0.844],[4.681,16.544],[0,0]],"v":[[63.033,-78.092],[76.68,-55.064],[75.364,-28.408]],"c":false},"ix":2},"nm":"Path 6","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.68235296011,0.796078443527,0.980392158031,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Fingerprint Side","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[169.175,161.377,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[7.894,8.852],[0,0]],"v":[[-7.497,-6.019],[7.497,6.019]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-13.066,-25.141],[-1.414,-2.132]],"o":[[0,0],[1.326,2.55],[0,0]],"v":[[-25.707,-58.269],[-17.526,-20.998],[-13.399,-13.981]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[-10.063,-10.595],[0,0]],"o":[[0.312,1.13],[11.84,12.467],[0,0]],"v":[[-8.095,-38.352],[4.748,-14.866],[28.345,-2.398]],"c":false},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[3.585,11.306],[0,0]],"v":[[90.35,-61.769],[93.054,-42.731]],"c":false},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0,0],[-14.956,-24.065],[-1.112,-2.304]],"o":[[0,0],[1.517,2.441],[0,0]],"v":[[54.72,-104.215],[82.601,-78.164],[86.53,-71.035]],"c":false},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ind":5,"ty":"sh","ix":6,"ks":{"a":0,"k":{"i":[[0,0],[-3.978,-14.061],[0,0]],"o":[[0.813,0.844],[4.681,16.544],[0,0]],"v":[[63.033,-78.092],[76.68,-55.064],[75.364,-28.408]],"c":false},"ix":2},"nm":"Path 6","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.596069335938,0.321563720703,0.239196777344,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":7,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Fingerprint","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[176.139,66.987,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.408,-0.232],[1.017,0.68],[-0.959,1.434],[-6.841,3.896],[-7.562,0.41],[-0.093,-1.722],[1.724,-0.094],[6.003,-3.419],[3.674,-5.493]],"o":[[-0.995,0.567],[-1.433,-0.959],[4.21,-6.294],[6.841,-3.896],[1.722,-0.093],[0.094,1.722],[-6.599,0.357],[-6.003,3.418],[-0.279,0.417]],"v":[[-16.693,13.1],[-19.976,12.983],[-20.836,8.652],[-3.944,-6.925],[18.072,-13.506],[21.359,-10.556],[18.409,-7.27],[-0.853,-1.498],[-15.645,12.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[7.044,-4.012],[6.96,0.908],[-0.223,1.71],[-1.711,-0.224],[-5.298,3.017],[-2.065,5.824],[-1.624,-0.577],[0.576,-1.626]],"o":[[-6.489,3.695],[-1.71,-0.223],[0.223,-1.71],[5.601,0.73],[5.742,-3.27],[0.577,-1.625],[1.625,0.576],[-2.566,7.236]],"v":[[44.597,78.169],[24.039,82.43],[21.346,78.93],[24.846,76.237],[41.507,72.742],[53.614,58.64],[57.601,56.74],[59.5,60.727]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.323,-0.184],[1.029,0.844],[0.13,0.229],[-15.469,8.81],[-8.28,-14.539],[1.498,-0.853],[0.853,1.498],[12.426,-7.076],[-6.547,-11.496],[-4.085,-3.35],[1.094,-1.333]],"o":[[-1.091,0.621],[-5.004,-4.103],[-8.28,-14.539],[15.469,-8.81],[0.853,1.498],[-1.498,0.853],[-6.547,-11.496],[-12.426,7.077],[0.024,0.042],[1.333,1.093],[-0.25,0.305]],"v":[[10.147,71.146],[6.621,70.847],[-1.636,61.086],[11.185,19.448],[53.537,29.665],[52.369,33.924],[48.111,32.756],[14.276,24.874],[3.791,57.996],[10.581,66.018],[11.016,70.413]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[1.183,-0.674],[0.287,-0.071],[3.505,8.779],[-6.102,3.475],[-3.887,-4.766],[0,0],[-3.067,1.747],[1.97,3.459],[0,0],[-1.498,0.853],[-0.853,-1.498],[0,0],[6.451,-3.674],[4.175,5.108],[0,0],[2.675,-1.523],[-1.489,-3.73],[-5.283,1.305],[-0.414,-1.674]],"o":[[-0.244,0.139],[-9.188,2.268],[-2.649,-6.633],[5.335,-3.038],[0,0],[2.235,2.735],[3.459,-1.97],[0,0],[-0.853,-1.498],[1.498,-0.853],[0,0],[3.674,6.452],[-5.725,3.26],[0,0],[-1.949,-2.391],[-3.377,1.923],[2.674,6.697],[1.674,-0.413],[0.342,1.387]],"v":[[36.276,64.961],[35.477,65.279],[11.151,49.991],[16.667,33.848],[32.582,36.831],[36.686,41.876],[45.842,43.586],[48.542,33.739],[47.994,32.777],[49.163,28.518],[53.421,29.686],[53.969,30.648],[48.932,49.012],[31.846,45.823],[27.739,40.774],[19.758,39.275],[16.951,47.675],[33.98,59.216],[37.761,61.499]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0.501,-0.285],[0.978,0.515],[11.066,-6.302],[0.514,-9.958],[1.721,0.088],[-0.089,1.722],[-13.068,7.442],[-10.828,-5.692],[0.802,-1.526]],"o":[[-0.895,0.51],[-8.827,-4.639],[-11.066,6.302],[-0.089,1.722],[-1.722,-0.089],[0.63,-12.217],[13.068,-7.442],[1.527,0.802],[-0.288,0.548]],"v":[[42.238,8.912],[39.237,8.964],[6.49,11.701],[-12.571,38.468],[-15.851,41.426],[-18.808,38.146],[3.4,6.274],[42.142,3.436],[43.453,7.653]],"c":true},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.596078455448,0.321568638086,0.239215686917,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Thumb","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[225.438,108.028,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[42.783,10.613],[3.8,0.302],[0,0],[0.105,0.008],[0.484,0.019],[6.105,-2.438],[0,0],[0.032,-0.013],[0.85,-0.396],[0,0],[0,0],[0,0],[0,0],[-26.827,-45.427],[0,0],[0,0]],"o":[[-28.424,-53.851],[-3.608,-1.195],[0,0],[-0.106,-0.009],[-0.482,-0.035],[-6.685,-0.314],[0,0],[-0.032,0.013],[-0.853,0.343],[-20.172,9.126],[0,0],[-6.217,10.813],[0,0],[-5.276,37.379],[0,0],[0,0],[0,0]],"v":[[76.157,43.103],[-34.58,-89.69],[-45.717,-91.957],[-45.708,-91.966],[-46.022,-91.983],[-47.471,-92.063],[-66.474,-88.859],[-66.467,-88.866],[-66.56,-88.826],[-69.115,-87.715],[-94.124,-66.879],[-94.112,-66.885],[-101.514,-44.23],[-101.507,-44.234],[-53.003,92.116],[30.37,92.116],[102.173,92.116]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431391716,0.403921574354,0.305882364511,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".grey900","cl":"grey900","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,100,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[412,200],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":38,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.1254902035,0.129411771894,0.141176477075,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/res/raw/udfps_tip_hint_lottie.json b/res/raw/udfps_tip_hint_lottie.json
index 9e26dfe..1e45382 100644
--- a/res/raw/udfps_tip_hint_lottie.json
+++ b/res/raw/udfps_tip_hint_lottie.json
@@ -1 +1 @@
-{}
\ No newline at end of file
+{"v":"5.7.6","fr":60,"ip":0,"op":211,"w":412,"h":200,"nm":"FPS Tip","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":55,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":85,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":115,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":140,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":165,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":170,"s":[100]},{"t":195,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.598,42.31,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[14.087,-6.682],[0,0]],"v":[[-13.068,2.034],[13.068,-1.085]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-22.142,12.611]],"o":[[0,0],[0,0]],"v":[[-49.515,34.761],[-23.485,7.239]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[-17.076,8.616],[-6.271,0.508]],"o":[[0,0],[7.731,-3.901],[0,0]],"v":[[-49.303,7.104],[-26.763,-9.607],[-5.197,-15.604]],"c":false},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.68235296011,0.796078443527,0.980392158031,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Fingerprint Tip","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.598,42.31,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[14.087,-6.682],[0,0]],"v":[[-13.068,2.034],[13.068,-1.085]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-22.142,12.611]],"o":[[0,0],[0,0]],"v":[[-49.515,34.761],[-23.485,7.239]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[-17.076,8.616],[-6.271,0.508]],"o":[[0,0],[7.731,-3.901],[0,0]],"v":[[-49.303,7.104],[-26.763,-9.607],[-5.197,-15.604]],"c":false},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.596069335938,0.321563720703,0.239196777344,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Fingerprint","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[176.139,66.987,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.408,-0.232],[1.017,0.68],[-0.959,1.434],[-6.841,3.896],[-7.562,0.41],[-0.093,-1.722],[1.724,-0.094],[6.003,-3.419],[3.674,-5.493]],"o":[[-0.995,0.567],[-1.433,-0.959],[4.21,-6.294],[6.841,-3.896],[1.722,-0.093],[0.094,1.722],[-6.599,0.357],[-6.003,3.418],[-0.279,0.417]],"v":[[-16.693,13.1],[-19.976,12.983],[-20.836,8.652],[-3.944,-6.925],[18.072,-13.506],[21.359,-10.556],[18.409,-7.27],[-0.853,-1.498],[-15.645,12.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[7.044,-4.012],[6.96,0.908],[-0.223,1.71],[-1.711,-0.224],[-5.298,3.017],[-2.065,5.824],[-1.624,-0.577],[0.576,-1.626]],"o":[[-6.489,3.695],[-1.71,-0.223],[0.223,-1.71],[5.601,0.73],[5.742,-3.27],[0.577,-1.625],[1.625,0.576],[-2.566,7.236]],"v":[[44.597,78.169],[24.039,82.43],[21.346,78.93],[24.846,76.237],[41.507,72.742],[53.614,58.64],[57.601,56.74],[59.5,60.727]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.323,-0.184],[1.029,0.844],[0.13,0.229],[-15.469,8.81],[-8.28,-14.539],[1.498,-0.853],[0.853,1.498],[12.426,-7.076],[-6.547,-11.496],[-4.085,-3.35],[1.094,-1.333]],"o":[[-1.091,0.621],[-5.004,-4.103],[-8.28,-14.539],[15.469,-8.81],[0.853,1.498],[-1.498,0.853],[-6.547,-11.496],[-12.426,7.077],[0.024,0.042],[1.333,1.093],[-0.25,0.305]],"v":[[10.147,71.146],[6.621,70.847],[-1.636,61.086],[11.185,19.448],[53.537,29.665],[52.369,33.924],[48.111,32.756],[14.276,24.874],[3.791,57.996],[10.581,66.018],[11.016,70.413]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[1.183,-0.674],[0.287,-0.071],[3.505,8.779],[-6.102,3.475],[-3.887,-4.766],[0,0],[-3.067,1.747],[1.97,3.459],[0,0],[-1.498,0.853],[-0.853,-1.498],[0,0],[6.451,-3.674],[4.175,5.108],[0,0],[2.675,-1.523],[-1.489,-3.73],[-5.283,1.305],[-0.414,-1.674]],"o":[[-0.244,0.139],[-9.188,2.268],[-2.649,-6.633],[5.335,-3.038],[0,0],[2.235,2.735],[3.459,-1.97],[0,0],[-0.853,-1.498],[1.498,-0.853],[0,0],[3.674,6.452],[-5.725,3.26],[0,0],[-1.949,-2.391],[-3.377,1.923],[2.674,6.697],[1.674,-0.413],[0.342,1.387]],"v":[[36.276,64.961],[35.477,65.279],[11.151,49.991],[16.667,33.848],[32.582,36.831],[36.686,41.876],[45.842,43.586],[48.542,33.739],[47.994,32.777],[49.163,28.518],[53.421,29.686],[53.969,30.648],[48.932,49.012],[31.846,45.823],[27.739,40.774],[19.758,39.275],[16.951,47.675],[33.98,59.216],[37.761,61.499]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0.501,-0.285],[0.978,0.515],[11.066,-6.302],[0.514,-9.958],[1.721,0.088],[-0.089,1.722],[-13.068,7.442],[-10.828,-5.692],[0.802,-1.526]],"o":[[-0.895,0.51],[-8.827,-4.639],[-11.066,6.302],[-0.089,1.722],[-1.722,-0.089],[0.63,-12.217],[13.068,-7.442],[1.527,0.802],[-0.288,0.548]],"v":[[42.238,8.912],[39.237,8.964],[6.49,11.701],[-12.571,38.468],[-15.851,41.426],[-18.808,38.146],[3.4,6.274],[42.142,3.436],[43.453,7.653]],"c":true},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.596078455448,0.321568638086,0.239215686917,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Thumb","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[225.438,108.028,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[42.783,10.613],[3.8,0.302],[0,0],[0.105,0.008],[0.484,0.019],[6.105,-2.438],[0,0],[0.032,-0.013],[0.85,-0.396],[0,0],[0,0],[0,0],[0,0],[-26.827,-45.427],[0,0],[0,0]],"o":[[-28.424,-53.851],[-3.608,-1.195],[0,0],[-0.106,-0.009],[-0.482,-0.035],[-6.685,-0.314],[0,0],[-0.032,0.013],[-0.853,0.343],[-20.172,9.126],[0,0],[-6.217,10.813],[0,0],[-5.276,37.379],[0,0],[0,0],[0,0]],"v":[[76.157,43.103],[-34.58,-89.69],[-45.717,-91.957],[-45.708,-91.966],[-46.022,-91.983],[-47.471,-92.063],[-66.474,-88.859],[-66.467,-88.866],[-66.56,-88.826],[-69.115,-87.715],[-94.124,-66.879],[-94.112,-66.885],[-101.514,-44.23],[-101.507,-44.234],[-53.003,92.116],[30.37,92.116],[102.173,92.116]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431391716,0.403921574354,0.305882364511,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".grey900","cl":"grey900","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,100,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[412,200],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":38,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.1254902035,0.129411771894,0.141176477075,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":960,"st":0,"bm":0}],"markers":[]}
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e84356b..e62bf14 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -11896,6 +11896,10 @@
<string name="sim_onboarding_progressbar_turning_sim_on">Turning on <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>…</string>
<!-- Title of service provider name(SPN) at mobile network settings page. [CHAR LIMIT=30] -->
<string name="mobile_network_spn_title">Mobile network</string>
+ <!-- At the mobile network page, title for primary IMEI for multi-sim devices -->
+ <string name="imei_primary">IMEI (primary)</string>
+ <!-- At the mobile network page, title for primary MEID for multi-sim devices -->
+ <string name="meid_primary">MEID (primary)</string>
<!-- Title of phone number at mobile network settings page. [CHAR LIMIT=30] -->
<string name="mobile_network_phone_number_title">Phone number</string>
<!-- Title of SIM label and color editor dialog at mobile network settings page. [CHAR LIMIT=30] -->
diff --git a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
index 53de7c5..1d80099 100644
--- a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
@@ -559,7 +559,8 @@
if (android.app.Flags.appRestrictionsApi()) {
am.noteAppRestrictionEnabled(pkgName, mAppEntry.info.uid,
ActivityManager.RESTRICTION_LEVEL_FORCE_STOPPED, true,
- ActivityManager.RESTRICTION_REASON_USER, "settings", 0L);
+ ActivityManager.RESTRICTION_REASON_USER,
+ "settings", ActivityManager.RESTRICTION_SOURCE_USER, 0L);
}
am.forceStopPackage(pkgName);
int userId = UserHandle.getUserId(mAppEntry.info.uid);
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollHelper.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollHelper.java
index 8d1113e..f292aae 100644
--- a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollHelper.java
+++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollHelper.java
@@ -184,6 +184,7 @@
*/
public void onAcquired(boolean isAcquiredGood) {
if (mListener != null) {
+ Log.e("JRM", "OnaCquired " + isAcquiredGood + " lastStepIsGood" + animateIfLastStep());
mListener.onAcquired(isAcquiredGood && animateIfLastStep());
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/DebuggingRepository.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/DebuggingRepository.kt
index e99f85b..d007e1a 100644
--- a/src/com/android/settings/biometrics/fingerprint2/data/repository/DebuggingRepository.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/DebuggingRepository.kt
@@ -34,7 +34,7 @@
*/
private val isBuildDebuggable = Build.IS_DEBUGGABLE
/** This flag indicates if udfps should use debug repos to supply data to its various views. */
- private val udfpsEnrollmentDebugEnabled = true
+ private val udfpsEnrollmentDebugEnabled = false
override fun isDebuggingEnabled(): Boolean {
return isBuildDebuggable
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrollStageInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrollStageInteractor.kt
index e683cb8..32a1763 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrollStageInteractor.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrollStageInteractor.kt
@@ -34,10 +34,10 @@
flowOf(
mapOf(
0.0f to EnrollStageModel.Center,
- 0.25f to EnrollStageModel.Guided,
- 0.5f to EnrollStageModel.Fingertip,
- 0.75f to EnrollStageModel.LeftEdge,
- 0.875f to EnrollStageModel.RightEdge,
+ 0.065f to EnrollStageModel.Guided,
+ 0.48f to EnrollStageModel.Fingertip,
+ 0.584f to EnrollStageModel.LeftEdge,
+ 0.792f to EnrollStageModel.RightEdge,
)
)
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollInteractor.kt
index e4180d3..f967e04 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollInteractor.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollInteractor.kt
@@ -33,6 +33,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.flow.update
/** This repository is responsible for collecting all state related to the enroll API. */
@@ -106,6 +107,30 @@
streamEnded = true
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()
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt
index f9276e6..3ecf312 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt
@@ -17,17 +17,12 @@
package com.android.settings.biometrics.fingerprint2.domain.interactor
import android.content.Context
-import android.util.Log
import android.view.OrientationEventListener
import com.android.internal.R
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
-import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transform
/** Interactor which provides information about orientation */
@@ -35,7 +30,9 @@
/** A flow that contains the information about the orientation changing */
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>
/**
@@ -50,8 +47,7 @@
fun getRotationFromDefault(rotation: Int): Int
}
-class OrientationInteractorImpl(private val context: Context, activityScope: CoroutineScope) :
- OrientationInteractor {
+class OrientationInteractorImpl(private val context: Context) : OrientationInteractor {
override val orientation: Flow<Int> = callbackFlow {
val orientationEventListener =
@@ -62,9 +58,12 @@
}
orientationEventListener.enable()
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) }
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UdfpsEnrollInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UdfpsEnrollInteractor.kt
index ec09ffd..107d1f6 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UdfpsEnrollInteractor.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UdfpsEnrollInteractor.kt
@@ -17,6 +17,7 @@
package com.android.settings.biometrics.fingerprint2.domain.interactor
import android.graphics.PointF
+import android.util.Log
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
@@ -69,6 +70,7 @@
override fun onEnrollmentStep(stepsRemaining: Int, totalStep: Int) {
val index = (totalStep - stepsRemaining) % guidedEnrollmentPoints.size
+ Log.e("JRM", "guided enroll step $index")
_guidedEnrollment.update { guidedEnrollmentPoints[index] }
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/model/FingerEnrollState.kt b/src/com/android/settings/biometrics/fingerprint2/lib/model/FingerEnrollState.kt
index 24a9a86..e087304 100644
--- a/src/com/android/settings/biometrics/fingerprint2/lib/model/FingerEnrollState.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/model/FingerEnrollState.kt
@@ -47,10 +47,10 @@
data class Acquired(val acquiredGood: Boolean) : FingerEnrollState()
/** 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 */
- data object PointerUp : FingerEnrollState()
+ data class PointerUp(val fingerId: Int) : FingerEnrollState()
/** Indicates the overlay has shown */
data object OverlayShown : FingerEnrollState()
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
index 6d353a4..d25fcd0 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
@@ -72,6 +72,7 @@
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.fragment.RFPSEnrollFragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment.UdfpsEnrollFragment
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsLastStepViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
@@ -126,6 +127,7 @@
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
private lateinit var fingerprintEnrollConfirmationViewModel:
FingerprintEnrollConfirmationViewModel
+ private lateinit var udfpsLastStepViewModel: UdfpsLastStepViewModel
private lateinit var udfpsViewModel: UdfpsViewModel
private lateinit var enrollStageInteractor: EnrollStageInteractor
private val coroutineDispatcher = Dispatchers.Default
@@ -320,7 +322,7 @@
foldStateInteractor = FoldStateInteractorImpl(context)
foldStateInteractor.onConfigurationChange(resources.configuration)
- orientationInteractor = OrientationInteractorImpl(context, lifecycleScope)
+ orientationInteractor = OrientationInteractorImpl(context)
vibrationInteractor =
VibrationInteractorImpl(context.getSystemService(Vibrator::class.java)!!, context)
@@ -373,11 +375,15 @@
fingerprintEnrollEnrollingViewModel,
navigationViewModel,
orientationInteractor,
+ fingerprintManagerInteractor,
),
)[RFPSViewModel::class.java]
enrollStageInteractor = EnrollStageInteractorImpl()
+ udfpsLastStepViewModel =
+ UdfpsLastStepViewModel(fingerprintEnrollEnrollingViewModel, vibrationInteractor)
+
udfpsViewModel =
ViewModelProvider(
this,
@@ -393,6 +399,9 @@
backgroundViewModel,
fingerprintSensorRepo,
udfpsEnrollInteractor,
+ fingerprintManagerInteractor,
+ udfpsLastStepViewModel,
+ accessibilityInteractor,
),
)[UdfpsViewModel::class.java]
@@ -456,7 +465,7 @@
step.exitTransition.toAnimation(),
)
.setReorderingAllowed(true)
- .add(R.id.fragment_container_view, theClass::class.java, null)
+ .replace(R.id.fragment_container_view, theClass::class.java, null)
.commit()
navigationViewModel.update(
FingerprintAction.TRANSITION_FINISHED,
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt
index 0645081..8abcf1a 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt
@@ -20,15 +20,18 @@
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
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.ui.enrollment.viewmodel.FingerprintAction
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.FingerprintNavigationViewModel
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
@@ -41,6 +44,7 @@
private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel,
private val navigationViewModel: FingerprintNavigationViewModel,
orientationInteractor: OrientationInteractor,
+ private val fingerprintManager: FingerprintManagerInteractor,
) : ViewModel() {
private val _textViewIsVisible = MutableStateFlow(false)
@@ -52,7 +56,16 @@
/** Indicates if the icon should be animating or not */
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
@@ -149,6 +162,7 @@
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
private val navigationViewModel: FingerprintNavigationViewModel,
private val orientationInteractor: OrientationInteractor,
+ private val fingerprintManager: FingerprintManagerInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@@ -157,6 +171,7 @@
fingerprintEnrollEnrollingViewModel,
navigationViewModel,
orientationInteractor,
+ fingerprintManager,
)
as T
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt
index a2e5232..4588a07 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt
@@ -21,8 +21,10 @@
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_HOVER_MOVE
import android.view.View
+import android.view.ViewGroup
import android.view.WindowManager
-import android.widget.TextView
+import android.widget.Button
+import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
@@ -40,7 +42,6 @@
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.widget.UdfpsEnrollViewV2
-import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.google.android.setupdesign.GlifLayout
import kotlinx.coroutines.launch
@@ -71,10 +72,8 @@
val fragment = this
lottie = view.findViewById(R.id.illustration_lottie)!!
udfpsEnrollView = view.findViewById(R.id.udfps_animation_view)!!
- val titleTextView = view.findViewById<TextView>(R.id.title)!!
- val descriptionTextView = view.findViewById<TextView>(R.id.description)!!
+ val glifLayout: GlifLayout = view.findViewById(R.id.glif_layout)!!
- val glifLayout = view.findViewById<GlifLayout>(R.id.dummy_glif_layout)!!
val backgroundColor = glifLayout.backgroundBaseColor
val window = requireActivity().window
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
@@ -83,6 +82,10 @@
window.statusBarColor = color
view.setBackgroundColor(color)
+ view.findViewById<Button>(R.id.skip)?.apply {
+ setOnClickListener { viewModel.negativeButtonClicked() }
+ }
+
udfpsEnrollView.setFinishAnimationCompleted { viewModel.finishedSuccessfully() }
viewLifecycleOwner.lifecycleScope.launch {
@@ -92,32 +95,41 @@
udfpsEnrollView.setSensorRect(sensor.sensorBounds, sensor.sensorType)
}
}
- viewLifecycleOwner.lifecycleScope.launch {
- viewModel.headerText.collect { titleTextView.setText(it.toResource()) }
- }
- viewLifecycleOwner.lifecycleScope.launch {
- viewModel.descriptionText.collect {
- if (it != null) {
- it.toResource()?.let { text -> descriptionTextView.setText(text) }
- } else {
- descriptionTextView.text = ""
+ launch { viewModel.overlayShown.collect { udfpsEnrollView.overlayShown() } }
+ launch { viewModel.headerText.collect { glifLayout.setHeaderText(it.toResource()) } }
+ launch {
+ viewModel.userInteractedWithSensor.collect {
+ if (!it) {
+ 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 {
lottie.visibility = if (it) View.VISIBLE else View.GONE
}
}
- viewLifecycleOwner.lifecycleScope.launch {
+ launch {
viewModel.lottie.collect { lottieModel ->
if (lottie.visibility == View.GONE) {
return@collect
}
val resource = lottieModel.toResource()
if (resource != null) {
+ glifLayout.descriptionTextView.visibility = View.GONE
LottieCompositionFactory.fromRawRes(requireContext(), resource).addListener { comp ->
comp?.let { composition ->
lottie.setComposition(composition)
@@ -126,27 +138,24 @@
}
}
} else {
+ glifLayout.descriptionTextView.visibility = View.VISIBLE
lottie.visibility = View.INVISIBLE
}
}
}
-
- viewLifecycleOwner.lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.DESTROYED) { viewModel.stopEnrollment() }
- }
-
- viewLifecycleOwner.lifecycleScope.launch {
+ launch {
viewModel.accessibilityEnabled.collect { enabled ->
udfpsEnrollView.setAccessibilityEnabled(enabled)
}
}
- viewLifecycleOwner.lifecycleScope.launch {
+ launch {
viewModel.enrollState.collect {
Log.d(TAG, "EnrollEvent $it")
if (it is FingerEnrollState.EnrollError) {
try {
FingerprintErrorDialog.showInstance(it, fragment)
+ viewModel.errorDialogShown(it)
} catch (exception: Exception) {
Log.e(TAG, "Exception occurred $exception")
}
@@ -156,19 +165,40 @@
}
}
- viewLifecycleOwner.lifecycleScope.launch {
- viewModel.progressSaved.collect { udfpsEnrollView.onEnrollProgressSaved(it) }
+ launch { viewModel.progressSaved.collect { udfpsEnrollView.onEnrollProgressSaved(it) } }
+
+ launch { viewModel.guidedEnrollment.collect { udfpsEnrollView.updateGuidedEnrollment(it) } }
+ launch {
+ viewModel.guidedEnrollmentSaved.collect { udfpsEnrollView.onGuidedPointSaved(it) }
}
- viewLifecycleOwner.lifecycleScope.launch {
- viewModel.guidedEnrollment.collect {
- glifLayout.post { udfpsEnrollView.updateGuidedEnrollment(it) }
+ launch { viewModel.shouldDrawIcon.collect { udfpsEnrollView.shouldDrawIcon(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 @@
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 {
return when (this.enrollStageModel) {
EnrollStageModel.Center,
- EnrollStageModel.Guided,
- EnrollStageModel.Fingertip,
- EnrollStageModel.Unknown -> R.string.security_settings_udfps_enroll_fingertip_title
+ EnrollStageModel.Guided -> R.string.security_settings_fingerprint_enroll_repeat_title
+ EnrollStageModel.Fingertip -> 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.RightEdge -> R.string.security_settings_udfps_enroll_right_edge_title
}
@@ -218,6 +306,5 @@
companion object {
private const val TAG = "UDFPSEnrollFragment"
- private val navStep = FingerprintNavigationStep.Enrollment::class
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/model/UdfpsSensorLocation.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/model/UdfpsSensorLocation.kt
new file mode 100644
index 0000000..6eb29f9
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/model/UdfpsSensorLocation.kt
@@ -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?)
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsLastStepViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsLastStepViewModel.kt
new file mode 100644
index 0000000..6a45ec4
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsLastStepViewModel.kt
@@ -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
+ }
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt
index a22f680..508084e 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt
@@ -22,9 +22,10 @@
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
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.repository.FingerprintSensorRepository
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.DisplayDensityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
@@ -32,6 +33,7 @@
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.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.ui.enrollment.modules.enrolling.udfps.ui.model.DescriptionText
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.FingerprintNavigationStep
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combineTransform
@@ -51,6 +54,8 @@
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.transform
+import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
/** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */
@@ -66,13 +71,25 @@
backgroundViewModel: BackgroundViewModel,
sensorRepository: FingerprintSensorRepository,
udfpsEnrollInteractor: UdfpsEnrollInteractor,
+ fingerprintManager: FingerprintManagerInteractor,
+ val udfpsLastStepViewModel: UdfpsLastStepViewModel,
+ accessibilityInteractor: AccessibilityInteractor,
) : ViewModel() {
private val isSetupWizard = flowOf(false)
private var shouldResetErollment = false
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. */
var enrollState: Flow<FingerEnrollState> =
combine(fingerprintEnrollEnrollingViewModel.enrollFlowShouldBeRunning, _enrollState) {
@@ -86,48 +103,59 @@
}
.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
* aligned with the sensor.
*/
val sensorLocation = sensorRepository.fingerprintSensor
- /** Indicates if accessibility is enabled */
- val accessibilityEnabled = flowOf(true).shareIn(viewModelScope, SharingStarted.Eagerly, 1)
-
- init {
- viewModelScope.launch {
- enrollState
- .combine(accessibilityEnabled) { event, isEnabled -> Pair(event, isEnabled) }
- .collect {
- if (
- when (it.first) {
- is FingerEnrollState.EnrollError -> true
- is FingerEnrollState.EnrollHelp -> it.second
- is FingerEnrollState.EnrollProgress -> true
- else -> false
- }
- ) {
- vibrate(it.first)
- }
- }
- }
-
- viewModelScope.launch {
- backgroundViewModel.background.filter { it }.collect { didGoToBackground() }
- }
- }
-
- /**
- * This indicates at which point the UI should offset the fingerprint sensor icon for guided
- * enrollment.
- */
+ /** Indicates a step of guided enrollment, the ui should animate the icon to the new location. */
val guidedEnrollment: Flow<PointF> =
- udfpsEnrollInteractor.guidedEnrollmentOffset.distinctUntilChanged()
+ udfpsEnrollInteractor.guidedEnrollmentOffset
+ .distinctUntilChanged()
+ .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
- /** The saved version of [guidedEnrollment] */
- val guidedEnrollmentSaved: Flow<PointF> =
- guidedEnrollment.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
+ private var _lastOrientation: Int? = null
+
+ /** 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
@@ -136,8 +164,32 @@
var progressSaved: Flow<FingerEnrollState.EnrollProgress> =
enrollState
.filterIsInstance<FingerEnrollState.EnrollProgress>()
- .filterNotNull()
- .shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
+ .combineTransform(shouldReplayLastEvent) { enroll, shouldReplay ->
+ 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. */
val touchExplorationDebug: Flow<Point> =
@@ -170,6 +222,18 @@
.filterNotNull()
.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 {
viewModelScope.launch {
enrollState
@@ -200,7 +264,7 @@
}
viewModelScope.launch {
- backgroundViewModel.background.filter { true }.collect { didGoToBackground() }
+ backgroundViewModel.background.filter { it }.collect { didGoToBackground() }
}
}
@@ -210,20 +274,12 @@
displayDensityInteractor.displayDensity,
displayDensityInteractor.defaultDisplayDensity,
displayDensityInteractor.fontScale,
- orientationInteractor.rotation,
- ) { currDisplayDensity, defaultDisplayDensity, fontScale, rotation ->
- val canShowLottieForRotation =
- when (rotation) {
- Surface.ROTATION_0 -> true
- else -> false
- }
-
- canShowLottieForRotation &&
- if (fontScale > 1.0f) {
- false
- } else {
- defaultDisplayDensity == currDisplayDensity
- }
+ ) { currDisplayDensity, defaultDisplayDensity, fontScale ->
+ if (fontScale > 1.0f) {
+ false
+ } else {
+ defaultDisplayDensity == currDisplayDensity
+ }
}
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
@@ -234,6 +290,18 @@
}
.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 }
/** The description text for UDFPS enrollment */
@@ -267,12 +335,12 @@
/** Indicates the negative button has been clicked */
fun negativeButtonClicked() {
- doReset()
navigationViewModel.update(
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
navStep,
"$TAG#negativeButtonClicked",
)
+ doReset()
}
/** Indicates that an enrollment was completed */
@@ -282,7 +350,7 @@
}
/** Indicates that the application went to the background. */
- private fun didGoToBackground() {
+ fun didGoToBackground() {
navigationViewModel.update(
FingerprintAction.DID_GO_TO_BACKGROUND,
navStep,
@@ -293,11 +361,7 @@
private fun doReset() {
_enrollState = fingerprintEnrollEnrollingViewModel.enrollFlow
- progressSaved =
- enrollState
- .filterIsInstance<FingerEnrollState.EnrollProgress>()
- .filterNotNull()
- .shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
+ _userInteractedWithSensor.update { false }
}
/** The lottie that should be shown for UDFPS Enrollment */
@@ -320,6 +384,15 @@
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(
private val vibrationInteractor: VibrationInteractor,
private val displayDensityInteractor: DisplayDensityInteractor,
@@ -332,6 +405,9 @@
private val backgroundViewModel: BackgroundViewModel,
private val sensorRepository: FingerprintSensorRepository,
private val udfpsEnrollInteractor: UdfpsEnrollInteractor,
+ private val fingerprintManager: FingerprintManagerInteractor,
+ private val udfpsLastStepViewModel: UdfpsLastStepViewModel,
+ private val accessibilityInteractor: AccessibilityInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@@ -348,6 +424,9 @@
backgroundViewModel,
sensorRepository,
udfpsEnrollInteractor,
+ fingerprintManager,
+ udfpsLastStepViewModel,
+ accessibilityInteractor,
)
as T
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollIconV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollIconV2.kt
index c209c55..1eec046 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollIconV2.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollIconV2.kt
@@ -36,8 +36,8 @@
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.core.animation.addListener
import androidx.core.graphics.toRect
-import androidx.core.graphics.toRectF
import com.android.settings.R
+import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import kotlin.math.sin
/**
@@ -45,7 +45,6 @@
* various stages of enrollment
*/
class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeSet?) : Drawable() {
- private var targetAnimationDuration: Long = TARGET_ANIM_DURATION_LONG
private var targetAnimatorSet: AnimatorSet? = null
private val movingTargetFpIcon: Drawable
private val fingerprintDrawable: ShapeDrawable
@@ -55,7 +54,7 @@
@ColorInt private var movingTargetFill = 0
private var currentScale = 1.0f
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]
@@ -63,15 +62,12 @@
private val sensorRectBounds: Rect = Rect()
/**
- * The following values are used to describe where the icon should be drawn. [currX] and [currY]
- * are changed based on the current guided enrollment step which is given by the
- * [UdfpsEnrollHelperV2]
+ * The following values are used to describe where the icon should be drawn. [sensorLeftOffset]
+ * and [sensorTopOffset] are changed based on the current guided enrollment step which is given by
+ * the [UdfpsEnrollHelperV2]
*/
- private var currX = 0f
- private var currY = 0f
-
- private var sensorWidth = 0f
- private var sensorHeight = 0f
+ private var sensorLeftOffset = 0f
+ private var sensorTopOffset = 0f
init {
fingerprintDrawable = createUdfpsIcon(context)
@@ -131,29 +127,35 @@
* The [sensorRect] coordinates for the sensor area. The [sensorRect] should be the coordinates
* with respect to its root frameview
*/
- fun drawSensorRectAt(sensorRect: Rect) {
- Log.e(TAG, "UdfpsEnrollIcon#drawSensorRect($sensorRect)")
+ fun drawSensorRectAt(overlayParams: UdfpsOverlayParams) {
+ Log.e(TAG, "UdfpsEnrollIcon#drawSensorRect(${overlayParams.sensorBounds})")
+ val sensorRect = overlayParams.sensorBounds
sensorRectBounds.set(sensorRect)
fingerprintDrawable.bounds = sensorRect
movingTargetFpIcon.bounds = sensorRect
- currX = sensorRect.left.toFloat()
- currY = sensorRect.top.toFloat()
- sensorWidth = (sensorRect.right - sensorRect.left).toFloat()
- sensorHeight = (sensorRect.bottom - sensorRect.top).toFloat()
+
+ // End existing animation if we get an update of the sensor rect.
+ targetAnimatorSet?.end()
+
invalidateSelf()
}
/** Stop drawing the fingerprint icon. */
fun stopDrawing() {
- alpha = 0
+ stopDrawing = true
+ invalidateSelf()
}
/** Resume drawing the fingerprint icon */
fun startDrawing() {
- alpha = 255
+ stopDrawing = false
+ invalidateSelf()
}
override fun draw(canvas: Canvas) {
+ if (stopDrawing) {
+ return
+ }
movingTargetFpIcon.alpha = alpha
fingerprintDrawable.setAlpha(alpha)
sensorOutlinePaint.setAlpha(alpha)
@@ -165,23 +167,28 @@
fingerprintDrawable.draw(canvas)
}
- private fun getCurrLocation(): RectF =
- RectF(currX, currY, currX + sensorWidth, currY + sensorHeight)
+ private fun getCurrLocation(): RectF {
+ 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) {
- if (currentBounds.equals(offsetRect)) {
+ private fun animateMovement(leftOffset: Float, topOffset: Float, scaleMovement: Boolean) {
+ if (leftOffset == sensorLeftOffset && topOffset == sensorTopOffset) {
return
}
- val xAnimator = ValueAnimator.ofFloat(currentBounds.left.toFloat(), offsetRect.left)
+ val currLocation = getCurrLocation()
+
+ val xAnimator = ValueAnimator.ofFloat(currLocation.left - sensorRectBounds.left, leftOffset)
xAnimator.addUpdateListener {
- currX = it.animatedValue as Float
+ sensorLeftOffset = it.animatedValue as Float
invalidateSelf()
}
- val yAnimator = ValueAnimator.ofFloat(currentBounds.top.toFloat(), offsetRect.top)
+ val yAnimator = ValueAnimator.ofFloat(currLocation.top - sensorRectBounds.top, topOffset)
yAnimator.addUpdateListener {
- currY = it.animatedValue as Float
+ sensorTopOffset = it.animatedValue as Float
invalidateSelf()
}
val animators = mutableListOf(xAnimator, yAnimator)
@@ -199,6 +206,7 @@
animators.add(scaleAnimator)
}
+ targetAnimatorSet?.cancel()
targetAnimatorSet = AnimatorSet()
targetAnimatorSet?.let {
@@ -210,50 +218,13 @@
}
/**
- * 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
* view, in which case their is no need to animate the icon to whatever position it was in.
*/
- fun updateGuidedEnrollment(point: PointF, isRecreating: Boolean) {
- guidedEnrollmentOffset = point
- if (isRecreating) {
- setAnimationTimeToZero()
- } 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()
+ fun updateGuidedEnrollment(point: PointF, isRecreating: Boolean) {
+ val pointIsZero = point.x == 0f && point.y == 0f
+ val shouldAnimateMovement = pointIsZero || !isRecreating
+ animateMovement(point?.x ?: 0f, point?.y ?: 0f, shouldAnimateMovement)
}
companion object {
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt
index bf2f026..c3adc87 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt
@@ -27,16 +27,15 @@
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.util.DisplayMetrics
-import android.util.Log
import android.view.animation.DecelerateInterpolator
import android.view.animation.Interpolator
import android.view.animation.OvershootInterpolator
import androidx.annotation.ColorInt
-import androidx.core.animation.addListener
import androidx.core.animation.doOnEnd
import androidx.core.graphics.toRectF
import com.android.internal.annotations.VisibleForTesting
import com.android.settings.R
+import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import kotlin.math.cos
import kotlin.math.max
import kotlin.math.sin
@@ -145,7 +144,6 @@
/** Indicates enrollment progress has occurred. */
fun onEnrollmentProgress(remaining: Int, totalSteps: Int, isRecreating: Boolean = false) {
-
afterFirstTouch = true
updateProgress(remaining, totalSteps, isRecreating)
}
@@ -216,8 +214,8 @@
* Draws the progress with locations [sensorLocationX] [sensorLocationY], note these must be with
* respect to the parent framelayout.
*/
- fun drawProgressAt(sensorRect: Rect) {
- this.sensorRect.set(sensorRect)
+ fun drawProgressAt(overlayParams: UdfpsOverlayParams) {
+ this.sensorRect.set(overlayParams.sensorBounds)
invalidateSelf()
}
@@ -251,8 +249,6 @@
this.remainingSteps = remainingSteps
this.totalSteps = totalSteps
- this.remainingSteps = remainingSteps
- this.totalSteps = totalSteps
val targetProgress = (totalSteps - remainingSteps).toFloat().div(max(1, totalSteps))
if (progressAnimator != null && progressAnimator!!.isRunning) {
@@ -290,12 +286,8 @@
checkMarkDrawable.bounds = newBounds
checkMarkDrawable.setVisible(true, false)
}
- doOnEnd {
- onFinishedCompletionAnimation?.let{
- it()
- }
+ doOnEnd { onFinishedCompletionAnimation?.let { it() } }
- }
start()
}
}
@@ -356,6 +348,7 @@
private fun flashHelpFillColor() {
if (fillColorAnimator != null && fillColorAnimator!!.isRunning) {
fillColorAnimator!!.end()
+ fillColorAnimator = null
}
@ColorInt val targetColor = helpColor
fillColorAnimator =
@@ -375,7 +368,6 @@
* want to re-animate the progress/success animation with the default timer
*/
private fun setAnimationTimeToZero() {
- fillColorAnimationDuration = 0
animateArcDuration = 0
checkmarkAnimationDelayDuration = 0
checkmarkAnimationDuration = 0
@@ -383,7 +375,6 @@
/** This sets animation timers back to normal, this happens after we have */
private fun restoreAnimationTime() {
- fillColorAnimationDuration = FILL_COLOR_ANIMATION_DURATION_MS
animateArcDuration = PROGRESS_ANIMATION_DURATION_MS
checkmarkAnimationDelayDuration = CHECKMARK_ANIMATION_DELAY_MS
checkmarkAnimationDuration = CHECKMARK_ANIMATION_DURATION_MS
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt
index 586408f..d9593c1 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt
@@ -70,27 +70,11 @@
fun setSensorRect(rect: Rect, sensorType: FingerprintSensorType) {
this.sensorRect = rect
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()
context.display.getDisplayInfo(displayInfo)
+ val rotation = displayInfo.rotation
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 coords = parentView.getLocationOnScreen()
val parentLeft = coords[0]
@@ -99,22 +83,44 @@
// If the view has been rotated, we need to translate the sensor coordinates
// to the new rotated view.
when (rotation) {
- Surface.ROTATION_90,
- Surface.ROTATION_270 -> {
+ Surface.ROTATION_90 -> {
sensorRectOffset.set(
sensorRectOffset.top,
sensorRectOffset.left,
sensorRectOffset.bottom,
sensorRectOffset.right,
)
+ sensorRectOffset.offset(-parentLeft, -parentTop)
}
- else -> {}
- }
- // Translate the sensor position into UdfpsEnrollView's view space.
- sensorRectOffset.offset(-parentLeft, -parentTop)
+ // When the view is rotated 270 degrees, 0,0 is the top corner left
+ Surface.ROTATION_270 -> {
+ sensorRectOffset.set(
+ (displayInfo.naturalHeight - sensorRectOffset.bottom) - parentLeft,
+ sensorRectOffset.left - parentTop,
+ (displayInfo.naturalHeight - sensorRectOffset.top) - parentLeft,
+ sensorRectOffset.right - parentTop,
+ )
+ }
+ else -> {
- fingerprintIcon.drawSensorRectAt(sensorRectOffset)
- fingerprintProgressDrawable.drawProgressAt(sensorRectOffset)
+ sensorRectOffset.offset(-parentLeft, -parentTop)
+ }
+ }
+
+ // 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)
}
@@ -126,11 +132,8 @@
onEnrollmentProgress(event.remainingSteps, event.totalStepsRequired)
is FingerEnrollState.Acquired -> onAcquired(event.acquiredGood)
is FingerEnrollState.EnrollHelp -> onEnrollmentHelp()
- is FingerEnrollState.PointerDown -> onPointerDown()
- is FingerEnrollState.PointerUp -> onPointerUp()
- is FingerEnrollState.OverlayShown -> overlayShown()
- is FingerEnrollState.EnrollError ->
- throw IllegalArgumentException("$TAG should not handle udfps error")
+ // Else ignore
+ else -> {}
}
}
@@ -145,7 +148,6 @@
}
}
- private fun udfpsError(errMsgId: Int, errString: String) {}
/**
* Sends a touch exploration event to the [onHoverListener] this should only be used for
* debugging.
@@ -170,8 +172,15 @@
onHoverListener = listener
}
- private fun overlayShown() {
- Log.e(TAG, "Implement overlayShown")
+ /** Indicates the overlay has been shown */
+ 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 */
@@ -190,16 +199,6 @@
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) {
super.onLayout(changed, left, top, right, bottom)
// Because the layout has changed, we need to recompute all locations.
@@ -261,6 +260,17 @@
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 {
private const val TAG = "UdfpsEnrollView"
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModel.kt
index 7ddb142..bbe3cfd 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModel.kt
@@ -16,6 +16,7 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
+import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.android.systemui.biometrics.shared.model.FingerprintSensor
@@ -68,7 +69,8 @@
val enrollFlow =
enrollFlowShouldBeRunning.transformLatest {
if (it) {
- fingerprintEnrollViewModel.enrollFlow.collect { event -> emit(event) }
+ fingerprintEnrollViewModel.enrollFlow.collect { event ->
+ emit(event) }
}
}
@@ -82,4 +84,8 @@
as T
}
}
+
+ companion object {
+ private val TAG = "FingerprintEnrollEnrollingViewModel"
+ }
}
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index b53bf47..9e08664 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -393,7 +393,7 @@
packageName, uid, ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED,
mode == AppOpsManager.MODE_IGNORED,
ActivityManager.RESTRICTION_REASON_USER,
- "settings", 0);
+ "settings", ActivityManager.RESTRICTION_SOURCE_USER, 0L);
}
// Control whether app could run jobs in the background
mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode);
diff --git a/src/com/android/settings/network/UiccSlotUtil.java b/src/com/android/settings/network/UiccSlotUtil.java
index df23f12..f15808f 100644
--- a/src/com/android/settings/network/UiccSlotUtil.java
+++ b/src/com/android/settings/network/UiccSlotUtil.java
@@ -329,38 +329,6 @@
return INVALID_PHYSICAL_SLOT_ID;
}
- // Device | |Slot |
- // Working| |Mapping|
- // State |Type |Mode |Friendly name
- //--------------------------------------------------------------------------
- // Single |SIM pSIM [RIL 0] |1 |pSIM active
- // Single |SIM MEP Port #0 [RIL0] |2 |eSIM Port0 active
- // Single |SIM MEP Port #1 [RIL0] |2.1 |eSIM Port1 active
- // DSDS |pSIM [RIL 0] + MEP Port #0 [RIL 1] |3 |pSIM+Port0
- // DSDS |pSIM [RIL 0] + MEP Port #1 [RIL 1] |3.1 |pSIM+Port1
- // DSDS |MEP Port #0 [RIL 0] + MEP Port #1 [RIL1]|3.2 |Dual-Ports-A
- // DSDS |MEP Port #1 [RIL 0] + MEP Port #0 [RIL1]|4 |Dual-Ports-B
- //
- // The rules are:
- // 1. pSIM's logical slots always is [RIL 0].
- // 2. assign the new active port to the same stack that will be de-activated
- // For example: mode#3->mode#4
- // 3. Add an eSIM carrier or enable eSIM carrier. The cases are at the below.
- // 1) 1 => 2 / 2.1 / 3 / 3.1
- // 2) 2 => 1 / 3 / 3.2
- // 3) 2.1 => 3.1 / 4
- // 4) 3 => 4
- // 5) 3.1 => 3.2
- // Note:
- // 1) 2 <=> 2.1 blocked by LPA (reason: existing active port in SS so just re-use)
- // 2) 3 <=> 3.1 blocked by LPA (reason: if pSIM+an active port, re-use the active port)
- // 4. pSIM insertion or enabling
- // 1) 2 => 1 / 3
- // 2) 2.1 => 1 / 3.1
- // 3) 3.2 => 3 / 3.1
- // 4) 4 => 3 / 3.1
-
-
@VisibleForTesting
static Collection<UiccSlotMapping> prepareUiccSlotMappings(
Collection<UiccSlotMapping> uiccSlotMappings, boolean isPsim, int physicalSlotId,
diff --git a/src/com/android/settings/network/telephony/MobileNetworkImeiPreferenceController.kt b/src/com/android/settings/network/telephony/MobileNetworkImeiPreferenceController.kt
index e134681..a6fb7ba 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkImeiPreferenceController.kt
+++ b/src/com/android/settings/network/telephony/MobileNetworkImeiPreferenceController.kt
@@ -17,7 +17,6 @@
package com.android.settings.network.telephony
import android.content.Context
-import android.os.UserManager
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
@@ -41,6 +40,7 @@
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+
/**
* Preference controller for "IMEI"
*/
@@ -123,17 +123,21 @@
ImeiInfoDialogFragment.show(fragment, simSlot, preference.title.toString())
return true
}
+
private fun getImei(): String {
val phoneType = getPhoneType()
return if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) mTelephonyManager.meid?: String()
else mTelephonyManager.imei?: String()
}
+
private fun getTitleForGsmPhone(): String {
- return mContext.getString(R.string.status_imei)
+ return mContext.getString(
+ if (isPrimaryImei()) R.string.imei_primary else R.string.status_imei)
}
private fun getTitleForCdmaPhone(): String {
- return mContext.getString(R.string.status_meid_number)
+ return mContext.getString(
+ if (isPrimaryImei()) R.string.meid_primary else R.string.status_meid_number)
}
private fun getTitle(): String {
@@ -142,6 +146,28 @@
else getTitleForGsmPhone()
}
+ /**
+ * As per GSMA specification TS37, below Primary IMEI requirements are mandatory to support
+ *
+ * TS37_2.2_REQ_5
+ * TS37_2.2_REQ_8 (Attached the document has description about this test cases)
+ */
+ protected fun isPrimaryImei(): Boolean {
+ val imei = getImei()
+ var primaryImei = String()
+
+ try {
+ primaryImei = mTelephonyManager.primaryImei
+ } catch (exception: Exception) {
+ Log.e(TAG, "PrimaryImei not available. $exception")
+ }
+ return primaryImei == imei && isMultiSim()
+ }
+
+ private fun isMultiSim(): Boolean {
+ return mTelephonyManager.activeModemCount > 1
+ }
+
fun getPhoneType(): Int {
return mTelephonyManager.currentPhoneType
}
diff --git a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
index d5ce3af..8dbcb14 100644
--- a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
+++ b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
@@ -160,7 +160,8 @@
context.activityManager.noteAppRestrictionEnabled(
packageName, uid,
ActivityManager.RESTRICTION_LEVEL_FORCE_STOPPED, true,
- ActivityManager.RESTRICTION_REASON_USER, "settings", 0)
+ ActivityManager.RESTRICTION_REASON_USER, "settings",
+ ActivityManager.RESTRICTION_SOURCE_USER, 0)
}
context.activityManager.forceStopPackageAsUser(packageName, userId)
}
diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt
index ebdc504..652afa0 100644
--- a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt
+++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt
@@ -118,7 +118,12 @@
var rfpsIconTouchViewModel = RFPSIconTouchViewModel()
var rfpsViewModel =
- RFPSViewModel(fingerprintEnrollEnrollingViewModel, navigationViewModel, orientationInteractor)
+ RFPSViewModel(
+ fingerprintEnrollEnrollingViewModel,
+ navigationViewModel,
+ orientationInteractor,
+ interactor,
+ )
val fingerprintEnrollConfirmationViewModel =
FingerprintEnrollConfirmationViewModel(navigationViewModel, interactor)
@@ -151,7 +156,8 @@
BackgroundViewModel::class.java -> backgroundViewModel
RFPSIconTouchViewModel::class.java -> rfpsIconTouchViewModel
FingerprintEnrollEnrollingViewModel::class.java -> fingerprintEnrollEnrollingViewModel
- FingerprintEnrollConfirmationViewModel::class.java -> fingerprintEnrollConfirmationViewModel
+ FingerprintEnrollConfirmationViewModel::class.java ->
+ fingerprintEnrollConfirmationViewModel
else -> null
}
as T
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkImeiPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkImeiPreferenceControllerTest.kt
index 2f67846..1da8fd9 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkImeiPreferenceControllerTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkImeiPreferenceControllerTest.kt
@@ -19,18 +19,16 @@
import android.content.Context
import android.telephony.SubscriptionInfo
import android.telephony.TelephonyManager
-import android.telephony.euicc.EuiccManager
import androidx.fragment.app.Fragment
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
-import com.android.internal.telephony.PhoneConstants
+import com.android.settings.R
import com.android.settings.core.BasePreferenceController
import com.android.settings.network.SubscriptionInfoListViewModel
import com.android.settings.network.SubscriptionUtil
-import com.android.settingslib.CustomDialogPreferenceCompat
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.After
@@ -62,6 +60,8 @@
on { currentPhoneType } doReturn TelephonyManager.PHONE_TYPE_GSM
on { imei } doReturn mockImei
on { meid } doReturn mockImei
+ on { primaryImei } doReturn mockImei
+ on { activeModemCount } doReturn 2
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
@@ -90,7 +90,7 @@
}
@Test
- fun refreshData_getPhoneNumber_preferenceSummaryIsExpected() = runBlocking {
+ fun refreshData_getImei_preferenceSummaryIsExpected() = runBlocking {
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
listOf(
@@ -111,6 +111,50 @@
}
@Test
+ fun refreshData_getImeiTitle_showImei() = runBlocking {
+ whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
+ whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
+ listOf(
+ SUB_INFO_1,
+ SUB_INFO_2
+ )
+ )
+ var mockSubId = 2
+ controller.init(mockFragment, mockSubId)
+ mockImei = "test imei"
+ mockTelephonyManager.stub {
+ on { imei } doReturn mockImei
+ on { primaryImei } doReturn ""
+ }
+
+ controller.refreshData(SUB_INFO_2)
+
+ assertThat(preference.title).isEqualTo(context.getString(R.string.status_imei))
+ }
+
+ @Test
+ fun refreshData_getPrimaryImeiTitle_showPrimaryImei() = runBlocking {
+ whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
+ whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
+ listOf(
+ SUB_INFO_1,
+ SUB_INFO_2
+ )
+ )
+ var mockSubId = 2
+ controller.init(mockFragment, mockSubId)
+ mockImei = "test imei"
+ mockTelephonyManager.stub {
+ on { imei } doReturn mockImei
+ on { primaryImei } doReturn mockImei
+ }
+
+ controller.refreshData(SUB_INFO_2)
+
+ assertThat(preference.title).isEqualTo(context.getString(R.string.imei_primary))
+ }
+
+ @Test
fun getAvailabilityStatus_notSimHardwareVisible() {
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(false)