Snap for 11865526 from 1a5eb3ed59 to 24Q3-release
Change-Id: Id0d226a30060d55daa9185fe113478cac53b1461
This commit is contained in:
@@ -2777,6 +2777,7 @@
|
|||||||
<activity android:name=".biometrics.fingerprint2.ui.enrollment.activity.FingerprintEnrollmentV2Activity"
|
<activity android:name=".biometrics.fingerprint2.ui.enrollment.activity.FingerprintEnrollmentV2Activity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="android.permission.MANAGE_FINGERPRINT"
|
android:permission="android.permission.MANAGE_FINGERPRINT"
|
||||||
|
android:configChanges="density"
|
||||||
android:theme="@style/GlifTheme.Light">
|
android:theme="@style/GlifTheme.Light">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.settings.FINGERPRINT_SETUP" />
|
<action android:name="android.settings.FINGERPRINT_SETUP" />
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright (C) 2024 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:color="@*android:color/ripple_material_light">
|
|
||||||
<item
|
|
||||||
android:left="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:right="?android:attr/listPreferredItemPaddingEnd">
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<solid
|
|
||||||
android:color="@color/settingslib_materialColorPrimaryContainer" />
|
|
||||||
<corners
|
|
||||||
android:radius="?android:attr/dialogCornerRadius" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
</ripple>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright (C) 2024 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:color="?android:attr/colorControlHighlight">
|
|
||||||
<item android:id="@android:id/mask"
|
|
||||||
android:left="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:right="?android:attr/listPreferredItemPaddingEnd">
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<solid
|
|
||||||
android:color="@android:color/white" />
|
|
||||||
<corners
|
|
||||||
android:radius="?android:attr/dialogCornerRadius" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
</ripple>
|
|
||||||
25
res/drawable/ic_modes_event.xml
Normal file
25
res/drawable/ic_modes_event.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24.0dp"
|
||||||
|
android:height="24.0dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:tint="?android:attr/colorControlNormal">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M17.0,12.0l-5.0,0.0l0.0,5.0l5.0,0.0l0.0,-5.0zM16.0,1.0l0.0,2.0L8.0,3.0L8.0,1.0L6.0,1.0l0.0,2.0L5.0,3.0c-1.11,0.0 -1.9,0.9 -1.99,2.0L3.0,19.0c0.0,1.0 0.89,2.0 2.0,2.0l14.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L21.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-1.0,0.0L18.0,1.0l-2.0,0.0zm3.0,18.0L5.0,19.0L5.0,8.0l14.0,0.0l0.0,11.0z"/>
|
||||||
|
</vector>
|
||||||
26
res/drawable/ic_modes_time.xml
Normal file
26
res/drawable/ic_modes_time.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24.0dp"
|
||||||
|
android:height="24.0dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:tint="?android:attr/colorControlNormal">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M612,668L668,612L520,464L520,280L440,280L440,496L612,668ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM480,800Q613,800 706.5,706.5Q800,613 800,480Q800,347 706.5,253.5Q613,160 480,160Q347,160 253.5,253.5Q160,347 160,480Q160,613 253.5,706.5Q347,800 480,800Z"/>
|
||||||
|
</vector>
|
||||||
100
res/layout-land/fingerprint_v2_udfps_enroll_enrolling.xml
Normal file
100
res/layout-land/fingerprint_v2_udfps_enroll_enrolling.xml
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2024 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/udfps_layout"
|
||||||
|
style="?attr/fingerprint_layout_theme"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<!-- This is used to grab style attributes and apply them
|
||||||
|
to this layout -->
|
||||||
|
<com.google.android.setupdesign.GlifLayout
|
||||||
|
android:id="@+id/dummy_glif_layout"
|
||||||
|
style="?attr/fingerprint_layout_theme"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="300dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/sud_layout_icon"
|
||||||
|
style="@style/SudGlifIcon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scaleType="fitStart"
|
||||||
|
android:src="@drawable/ic_lock" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
style="@style/SudGlifHeaderTitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:lines="2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/description"
|
||||||
|
style="@style/SudDescription.Glif"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:lines="3"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:paddingRight="10dp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<com.airbnb.lottie.LottieAnimationView
|
||||||
|
android:id="@+id/illustration_lottie"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:paddingRight="10dp"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:lottie_autoPlay="true"
|
||||||
|
app:lottie_loop="true"
|
||||||
|
app:lottie_speed=".85"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/layout_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center_horizontal|bottom"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
>
|
||||||
|
|
||||||
|
<include layout="@layout/fingerprint_v2_udfps_enroll_view" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/setup_wizard_layout"
|
android:id="@+id/udfps_layout"
|
||||||
style="?attr/fingerprint_layout_theme"
|
style="?attr/fingerprint_layout_theme"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|||||||
@@ -17,28 +17,34 @@
|
|||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
style="@style/SearchBarStyle"
|
style="@style/SearchBarStyle_v2"
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1">
|
|
||||||
<Toolbar
|
|
||||||
android:id="@+id/search_action_bar_unified"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/search_bar_height"
|
android:layout_height="wrap_content">
|
||||||
android:paddingStart="8dp"
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/search_action_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="72dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="24dp"
|
||||||
android:paddingEnd="24dp"
|
android:paddingEnd="24dp"
|
||||||
android:background="@drawable/search_bar_selected_background"
|
android:background="@drawable/search_bar_selected_background">
|
||||||
android:touchscreenBlocksFocus="false"
|
|
||||||
android:nextFocusForward="@+id/homepage_container"
|
<ImageView
|
||||||
android:contentInsetStartWithNavigation="@dimen/search_bar_content_inset"
|
android:id="@+id/imageView"
|
||||||
android:navigationIcon="@drawable/ic_homepage_search">
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/ic_homepage_search" />
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/search_bar_title"
|
android:id="@+id/search_bar_title"
|
||||||
style="@style/TextAppearance.SearchBar"
|
style="@style/TextAppearance.SearchBar"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="8dp"
|
android:paddingStart="8dp"
|
||||||
android:layout_gravity="start"
|
android:paddingEnd="8dp"
|
||||||
android:text="@string/search_settings"/>
|
android:text="@string/search_settings"/>
|
||||||
</Toolbar>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright (C) 2024 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/app_bar_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="16dp"
|
|
||||||
android:padding="6dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<include layout="@layout/search_bar_unified_version"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -38,7 +38,6 @@
|
|||||||
android:id="@+id/suggestion_content"
|
android:id="@+id/suggestion_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
|
android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd" />
|
android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd" />
|
||||||
|
|
||||||
@@ -62,19 +61,21 @@
|
|||||||
android:id="@+id/app_bar"
|
android:id="@+id/app_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
app:elevation="0dp"
|
||||||
android:touchscreenBlocksFocus="false"
|
android:touchscreenBlocksFocus="false"
|
||||||
android:keyboardNavigationCluster="false">
|
android:keyboardNavigationCluster="false">
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/app_bar_container"
|
android:id="@+id/app_bar_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="horizontal"
|
||||||
android:minHeight="76dp"
|
android:paddingTop="8dp"
|
||||||
app:layout_scrollFlags="scroll|exitUntilCollapsed">
|
android:paddingBottom="24dp"
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
|
||||||
|
|
||||||
|
<include layout="@layout/search_bar_unified_version"/>
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/homepage_app_bar_unified_view"
|
|
||||||
layout="@layout/settings_homepage_app_bar_unified_layout"/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|||||||
@@ -459,6 +459,13 @@
|
|||||||
<item name="cardElevation">0dp</item>
|
<item name="cardElevation">0dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="SearchBarStyle_v2">
|
||||||
|
<item name="cardCornerRadius">40dp</item>
|
||||||
|
<item name="cardElevation">0dp</item>
|
||||||
|
<item name="strokeWidth">1dp</item>
|
||||||
|
<item name="strokeColor">@color/settingslib_materialColorOutlineVariant</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="ConditionCardBorderlessButton"
|
<style name="ConditionCardBorderlessButton"
|
||||||
parent="android:Widget.DeviceDefault.Button.Borderless">
|
parent="android:Widget.DeviceDefault.Button.Borderless">
|
||||||
<item name="android:textColor">?android:attr/colorAccent</item>
|
<item name="android:textColor">?android:attr/colorAccent</item>
|
||||||
|
|||||||
@@ -185,6 +185,7 @@
|
|||||||
<style name="Theme.Settings.HomeBase" parent="Theme.Settings.NoActionBar">
|
<style name="Theme.Settings.HomeBase" parent="Theme.Settings.NoActionBar">
|
||||||
<item name="colorPrimary">@*android:color/primary_device_default_settings_light</item>
|
<item name="colorPrimary">@*android:color/primary_device_default_settings_light</item>
|
||||||
<item name="colorAccent">@*android:color/accent_device_default_light</item>
|
<item name="colorAccent">@*android:color/accent_device_default_light</item>
|
||||||
|
<item name="android:listPreferredItemPaddingStart">16dp</item>
|
||||||
<item name="preferenceTheme">@style/SettingsPreferenceTheme</item>
|
<item name="preferenceTheme">@style/SettingsPreferenceTheme</item>
|
||||||
|
|
||||||
<!-- action bar, needed for search bar icon tinting -->
|
<!-- action bar, needed for search bar icon tinting -->
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:title="@string/zen_modes_list_title" >
|
android:title="@string/zen_modes_list_title" >
|
||||||
|
|
||||||
<!-- TODO: b/308819292 - implement page, delete this test preference -->
|
<com.android.settingslib.widget.LayoutPreference
|
||||||
<Preference
|
android:key="header"
|
||||||
android:key="zen_mode_test" />
|
android:layout="@layout/settings_entity_header" />
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
@@ -22,12 +22,14 @@
|
|||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:order="-140"
|
android:order="-140"
|
||||||
android:key="top_level_account_category">
|
android:key="top_level_account_category"
|
||||||
|
android:layout="@layout/settingslib_preference_category_no_title">
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:order="-130"
|
android:order="-130"
|
||||||
android:key="top_level_connectivity_category">
|
android:key="top_level_connectivity_category"
|
||||||
|
android:layout="@layout/settingslib_preference_category_no_title">
|
||||||
<com.android.settings.widget.HomepagePreference
|
<com.android.settings.widget.HomepagePreference
|
||||||
android:fragment="com.android.settings.network.NetworkDashboardFragment"
|
android:fragment="com.android.settings.network.NetworkDashboardFragment"
|
||||||
android:icon="@drawable/ic_settings_wireless_filled"
|
android:icon="@drawable/ic_settings_wireless_filled"
|
||||||
@@ -51,7 +53,8 @@
|
|||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:order="-120"
|
android:order="-120"
|
||||||
android:key="top_level_personalize_category">
|
android:key="top_level_personalize_category"
|
||||||
|
android:layout="@layout/settingslib_preference_category_no_title">
|
||||||
<com.android.settings.widget.HomepagePreference
|
<com.android.settings.widget.HomepagePreference
|
||||||
android:fragment="com.android.settings.applications.AppDashboardFragment"
|
android:fragment="com.android.settings.applications.AppDashboardFragment"
|
||||||
android:icon="@drawable/ic_apps_filled"
|
android:icon="@drawable/ic_apps_filled"
|
||||||
@@ -111,7 +114,8 @@
|
|||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:order="-110"
|
android:order="-110"
|
||||||
android:key="top_level_system_info_category">
|
android:key="top_level_system_info_category"
|
||||||
|
android:layout="@layout/settingslib_preference_category_no_title">
|
||||||
<com.android.settings.widget.HomepagePreference
|
<com.android.settings.widget.HomepagePreference
|
||||||
android:fragment="com.android.settings.deviceinfo.StorageDashboardFragment"
|
android:fragment="com.android.settings.deviceinfo.StorageDashboardFragment"
|
||||||
android:icon="@drawable/ic_storage_filled"
|
android:icon="@drawable/ic_storage_filled"
|
||||||
@@ -154,7 +158,8 @@
|
|||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:order="-100"
|
android:order="-100"
|
||||||
android:key="top_level_security_privacy_category">
|
android:key="top_level_security_privacy_category"
|
||||||
|
android:layout="@layout/settingslib_preference_category_no_title">
|
||||||
<com.android.settings.widget.HomepagePreference
|
<com.android.settings.widget.HomepagePreference
|
||||||
android:icon="@drawable/ic_settings_safety_center_filled"
|
android:icon="@drawable/ic_settings_safety_center_filled"
|
||||||
android:key="top_level_safety_center"
|
android:key="top_level_safety_center"
|
||||||
@@ -203,20 +208,22 @@
|
|||||||
android:summary="@string/summary_placeholder"
|
android:summary="@string/summary_placeholder"
|
||||||
settings:highlightableMenuKey="@string/menu_key_accounts"
|
settings:highlightableMenuKey="@string/menu_key_accounts"
|
||||||
settings:controller="com.android.settings.accounts.TopLevelAccountEntryPreferenceController"/>
|
settings:controller="com.android.settings.accounts.TopLevelAccountEntryPreferenceController"/>
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory
|
|
||||||
android:order="100"
|
|
||||||
android:key="top_level_support_category">
|
|
||||||
<com.android.settings.widget.HomepagePreference
|
<com.android.settings.widget.HomepagePreference
|
||||||
android:key="top_level_emergency"
|
android:key="top_level_emergency"
|
||||||
android:title="@string/emergency_settings_preference_title"
|
android:title="@string/emergency_settings_preference_title"
|
||||||
android:summary="@string/emergency_dashboard_summary"
|
android:summary="@string/emergency_dashboard_summary"
|
||||||
android:icon="@drawable/ic_settings_emergency_filled"
|
android:icon="@drawable/ic_settings_emergency_filled"
|
||||||
android:order="-30"
|
android:order="10"
|
||||||
android:fragment="com.android.settings.emergency.EmergencyDashboardFragment"
|
android:fragment="com.android.settings.emergency.EmergencyDashboardFragment"
|
||||||
settings:isPreferenceVisible="@bool/config_show_emergency_settings"
|
settings:isPreferenceVisible="@bool/config_show_emergency_settings"
|
||||||
settings:highlightableMenuKey="@string/menu_key_emergency"/>
|
settings:highlightableMenuKey="@string/menu_key_emergency"/>
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:order="100"
|
||||||
|
android:key="top_level_support_category"
|
||||||
|
android:layout="@layout/settingslib_preference_category_no_title">
|
||||||
|
|
||||||
<com.android.settings.widget.HomepagePreference
|
<com.android.settings.widget.HomepagePreference
|
||||||
android:fragment="com.android.settings.accessibility.AccessibilitySettings"
|
android:fragment="com.android.settings.accessibility.AccessibilitySettings"
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint2.data.repository
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
|
||||||
|
/** Indicates if the developer has debugging features enabled. */
|
||||||
|
interface DebuggingRepository {
|
||||||
|
|
||||||
|
/** A function that will return if a build is debuggable */
|
||||||
|
fun isDebuggingEnabled(): Boolean
|
||||||
|
/** A function that will return if udfps enrollment should be swapped with debug repos */
|
||||||
|
fun isUdfpsEnrollmentDebuggingEnabled(): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
class DebuggingRepositoryImpl : DebuggingRepository {
|
||||||
|
/**
|
||||||
|
* This flag can be flipped by the engineer which should allow for certain debugging features to
|
||||||
|
* be enabled.
|
||||||
|
*/
|
||||||
|
private val isBuildDebuggable = Build.IS_DEBUGGABLE
|
||||||
|
/** This flag indicates if udfps should use debug repos to supply data to its various views. */
|
||||||
|
private val udfpsEnrollmentDebugEnabled = true
|
||||||
|
|
||||||
|
override fun isDebuggingEnabled(): Boolean {
|
||||||
|
return isBuildDebuggable
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isUdfpsEnrollmentDebuggingEnabled(): Boolean {
|
||||||
|
return isDebuggingEnabled() && udfpsEnrollmentDebugEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint2.data.repository
|
||||||
|
|
||||||
|
import android.graphics.Point
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This repository simulates touch events. This is mainly used to debug accessibility and ensure
|
||||||
|
* that talkback is correct.
|
||||||
|
*/
|
||||||
|
interface SimulatedTouchEventsRepository {
|
||||||
|
/**
|
||||||
|
* A flow simulating user touches.
|
||||||
|
*/
|
||||||
|
val touchExplorationDebug: Flow<Point>
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint2.data.repository
|
||||||
|
|
||||||
|
import android.graphics.Point
|
||||||
|
import android.graphics.Rect
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
|
import com.android.systemui.biometrics.shared.model.FingerprintSensor
|
||||||
|
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
||||||
|
import com.android.systemui.biometrics.shared.model.SensorStrength
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to simulate enroll data. This has two major use cases. 1). Ease of Development
|
||||||
|
* 2). Bug Fixes
|
||||||
|
*/
|
||||||
|
class UdfpsEnrollDebugRepositoryImpl :
|
||||||
|
FingerprintEnrollInteractor, FingerprintSensorRepository, SimulatedTouchEventsRepository {
|
||||||
|
|
||||||
|
override suspend fun enroll(hardwareAuthToken: ByteArray?, enrollReason: EnrollReason) = flow {
|
||||||
|
emit(FingerEnrollState.OverlayShown)
|
||||||
|
delay(200)
|
||||||
|
emit(FingerEnrollState.EnrollHelp(helpMsgId, "Hello world"))
|
||||||
|
delay(200)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(15, 16))
|
||||||
|
delay(300)
|
||||||
|
emit(FingerEnrollState.EnrollHelp(helpMsgId, "Hello world"))
|
||||||
|
delay(1000)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(14, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(13, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(12, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(11, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(10, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(9, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(8, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(7, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(6, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(5, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(4, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(3, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(2, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(1, 16))
|
||||||
|
delay(500)
|
||||||
|
emit(FingerEnrollState.EnrollProgress(0, 16))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Provides touch events to the UdfpsEnrollFragment */
|
||||||
|
override val touchExplorationDebug: Flow<Point> = flow {
|
||||||
|
delay(2000)
|
||||||
|
emit(pointToLeftOfSensor(sensorRect))
|
||||||
|
delay(2000)
|
||||||
|
emit(pointBelowSensor(sensorRect))
|
||||||
|
delay(2000)
|
||||||
|
emit(pointToRightOfSensor(sensorRect))
|
||||||
|
delay(2000)
|
||||||
|
emit(pointAboveSensor(sensorRect))
|
||||||
|
}
|
||||||
|
|
||||||
|
override val fingerprintSensor: Flow<FingerprintSensor> = flowOf(sensorProps)
|
||||||
|
|
||||||
|
private fun pointToLeftOfSensor(sensorLocation: Rect) =
|
||||||
|
Point(sensorLocation.right + 5, sensorLocation.centerY())
|
||||||
|
|
||||||
|
private fun pointToRightOfSensor(sensorLocation: Rect) =
|
||||||
|
Point(sensorLocation.left - 5, sensorLocation.centerY())
|
||||||
|
|
||||||
|
private fun pointBelowSensor(sensorLocation: Rect) =
|
||||||
|
Point(sensorLocation.centerX(), sensorLocation.bottom + 5)
|
||||||
|
|
||||||
|
private fun pointAboveSensor(sensorLocation: Rect) =
|
||||||
|
Point(sensorLocation.centerX(), sensorLocation.top - 5)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val helpMsgId: Int = 1
|
||||||
|
private val sensorLocationInternal = Pair(540, 1713)
|
||||||
|
private val sensorRadius = 100
|
||||||
|
private val sensorRect =
|
||||||
|
Rect(
|
||||||
|
this.sensorLocationInternal.first - sensorRadius,
|
||||||
|
this.sensorLocationInternal.second - sensorRadius,
|
||||||
|
this.sensorLocationInternal.first + sensorRadius,
|
||||||
|
this.sensorLocationInternal.second + sensorRadius,
|
||||||
|
)
|
||||||
|
val sensorProps =
|
||||||
|
FingerprintSensor(
|
||||||
|
1,
|
||||||
|
SensorStrength.STRONG,
|
||||||
|
5,
|
||||||
|
FingerprintSensorType.UDFPS_OPTICAL,
|
||||||
|
sensorRect,
|
||||||
|
sensorRadius,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
||||||
|
|
||||||
|
import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepository
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
|
||||||
|
/** Interactor indicating if certain debug flows are enabled. */
|
||||||
|
interface DebuggingInteractor {
|
||||||
|
/** This indicates that certain debug flows are enabled. */
|
||||||
|
val debuggingEnabled: Flow<Boolean>
|
||||||
|
/** This indicates if udfps should instead use debug repos to supply data to its various views. */
|
||||||
|
val udfpsEnrollmentDebuggingEnabled: Flow<Boolean>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interactor essentially forwards the [DebuggingRepository]
|
||||||
|
*/
|
||||||
|
class DebuggingInteractorImpl(val debuggingRepository: DebuggingRepository) : DebuggingInteractor {
|
||||||
|
override val debuggingEnabled: Flow<Boolean> = flow {
|
||||||
|
emit(debuggingRepository.isDebuggingEnabled())
|
||||||
|
}
|
||||||
|
override val udfpsEnrollmentDebuggingEnabled: Flow<Boolean> = flow {
|
||||||
|
emit(debuggingRepository.isUdfpsEnrollmentDebuggingEnabled())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.flow.shareIn
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible for handling updates to fontScale and displayDensity and forwarding
|
||||||
|
* these events to classes that need them
|
||||||
|
*/
|
||||||
|
interface DisplayDensityInteractor {
|
||||||
|
/** Indicates the display density has been updated. */
|
||||||
|
fun updateDisplayDensity(density: Int)
|
||||||
|
|
||||||
|
/** Indicates the font scale has been updates. */
|
||||||
|
fun updateFontScale(fontScale: Float)
|
||||||
|
|
||||||
|
/** A flow that propagates fontscale. */
|
||||||
|
val fontScale: Flow<Float>
|
||||||
|
|
||||||
|
/** A flow that propagates displayDensity. */
|
||||||
|
val displayDensity: Flow<Int>
|
||||||
|
|
||||||
|
/** A flow that propagates the default display density. */
|
||||||
|
val defaultDisplayDensity: Flow<Int>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the [DisplayDensityInteractor]. This interactor is used to forward activity
|
||||||
|
* information to the rest of the application.
|
||||||
|
*/
|
||||||
|
class DisplayDensityInteractorImpl(
|
||||||
|
currentFontScale: Float,
|
||||||
|
currentDisplayDensity: Int,
|
||||||
|
defaultDisplayDensity: Int,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
) : DisplayDensityInteractor {
|
||||||
|
override fun updateDisplayDensity(density: Int) {
|
||||||
|
_displayDensity.update { density }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateFontScale(fontScale: Float) {
|
||||||
|
_fontScale.update { fontScale }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _fontScale = MutableStateFlow(currentFontScale)
|
||||||
|
private val _displayDensity = MutableStateFlow(currentDisplayDensity)
|
||||||
|
|
||||||
|
override val fontScale: Flow<Float> = _fontScale.asStateFlow()
|
||||||
|
|
||||||
|
override val displayDensity: Flow<Int> = _displayDensity.asStateFlow()
|
||||||
|
|
||||||
|
override val defaultDisplayDensity: Flow<Int> =
|
||||||
|
flowOf(defaultDisplayDensity).shareIn(scope, SharingStarted.Eagerly, 1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
||||||
|
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
||||||
|
typealias EnrollStageThresholds = Map<Float, StageViewModel>
|
||||||
|
|
||||||
|
/** Interactor that provides enroll stages for enrollment. */
|
||||||
|
interface EnrollStageInteractor {
|
||||||
|
|
||||||
|
/** Provides enroll stages for enrollment. */
|
||||||
|
val enrollStageThresholds: Flow<EnrollStageThresholds>
|
||||||
|
}
|
||||||
|
|
||||||
|
class EnrollStageInteractorImpl() : EnrollStageInteractor {
|
||||||
|
override val enrollStageThresholds: Flow<EnrollStageThresholds> =
|
||||||
|
flowOf(
|
||||||
|
mapOf(
|
||||||
|
0.0f to StageViewModel.Center,
|
||||||
|
0.25f to StageViewModel.Guided,
|
||||||
|
0.5f to StageViewModel.Fingertip,
|
||||||
|
0.75f to StageViewModel.LeftEdge,
|
||||||
|
0.875f to StageViewModel.RightEdge,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.hardware.fingerprint.FingerprintEnrollOptions
|
||||||
|
import android.hardware.fingerprint.FingerprintManager
|
||||||
|
import android.os.CancellationSignal
|
||||||
|
import android.util.Log
|
||||||
|
import com.android.settings.biometrics.fingerprint2.conversion.Util.toEnrollError
|
||||||
|
import com.android.settings.biometrics.fingerprint2.conversion.Util.toOriginalReason
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.channels.onFailure
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
|
/** This repository is responsible for collecting all state related to the enroll API. */
|
||||||
|
interface FingerprintEnrollInteractor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By calling this function, [fingerEnrollState] will begin to be populated with data on success.
|
||||||
|
*/
|
||||||
|
suspend fun enroll(
|
||||||
|
hardwareAuthToken: ByteArray?,
|
||||||
|
enrollReason: EnrollReason,
|
||||||
|
): Flow<FingerEnrollState>
|
||||||
|
}
|
||||||
|
|
||||||
|
class FingerprintEnrollInteractorImpl(
|
||||||
|
private val applicationContext: Context,
|
||||||
|
private val fingerprintEnrollOptions: FingerprintEnrollOptions,
|
||||||
|
private val fingerprintManager: FingerprintManager,
|
||||||
|
private val fingerprintFlow: FingerprintFlow,
|
||||||
|
) : FingerprintEnrollInteractor {
|
||||||
|
private val enrollRequestOutstanding = MutableStateFlow(false)
|
||||||
|
|
||||||
|
override suspend fun enroll(
|
||||||
|
hardwareAuthToken: ByteArray?,
|
||||||
|
enrollReason: EnrollReason,
|
||||||
|
): Flow<FingerEnrollState> = callbackFlow {
|
||||||
|
// TODO (b/308456120) Improve this logic
|
||||||
|
if (enrollRequestOutstanding.value) {
|
||||||
|
Log.d(TAG, "Outstanding enroll request, waiting 150ms")
|
||||||
|
delay(150)
|
||||||
|
if (enrollRequestOutstanding.value) {
|
||||||
|
Log.e(TAG, "Request still present, continuing")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enrollRequestOutstanding.update { true }
|
||||||
|
|
||||||
|
var streamEnded = false
|
||||||
|
var totalSteps: Int? = null
|
||||||
|
val enrollmentCallback =
|
||||||
|
object : FingerprintManager.EnrollmentCallback() {
|
||||||
|
override fun onEnrollmentProgress(remaining: Int) {
|
||||||
|
// This is sort of an implementation detail, but unfortunately the API isn't
|
||||||
|
// very expressive. If anything we should look at changing the FingerprintManager API.
|
||||||
|
if (totalSteps == null) {
|
||||||
|
totalSteps = remaining + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
trySend(FingerEnrollState.EnrollProgress(remaining, totalSteps!!)).onFailure { error ->
|
||||||
|
Log.d(TAG, "onEnrollmentProgress($remaining) failed to send, due to $error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining == 0) {
|
||||||
|
streamEnded = true
|
||||||
|
enrollRequestOutstanding.update { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onEnrollmentHelp(helpMsgId: Int, helpString: CharSequence?) {
|
||||||
|
trySend(FingerEnrollState.EnrollHelp(helpMsgId, helpString.toString())).onFailure { error
|
||||||
|
->
|
||||||
|
Log.d(TAG, "onEnrollmentHelp failed to send, due to $error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onEnrollmentError(errMsgId: Int, errString: CharSequence?) {
|
||||||
|
trySend(errMsgId.toEnrollError(fingerprintFlow == SetupWizard)).onFailure { error ->
|
||||||
|
Log.d(TAG, "onEnrollmentError failed to send, due to $error")
|
||||||
|
}
|
||||||
|
Log.d(TAG, "onEnrollmentError($errMsgId)")
|
||||||
|
streamEnded = true
|
||||||
|
enrollRequestOutstanding.update { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val cancellationSignal = CancellationSignal()
|
||||||
|
|
||||||
|
fingerprintManager.enroll(
|
||||||
|
hardwareAuthToken,
|
||||||
|
cancellationSignal,
|
||||||
|
applicationContext.userId,
|
||||||
|
enrollmentCallback,
|
||||||
|
enrollReason.toOriginalReason(),
|
||||||
|
fingerprintEnrollOptions,
|
||||||
|
)
|
||||||
|
awaitClose {
|
||||||
|
// If the stream has not been ended, and the user has stopped collecting the flow
|
||||||
|
// before it was over, send cancel.
|
||||||
|
if (!streamEnded) {
|
||||||
|
Log.e(TAG, "Cancel is sent from settings for enroll()")
|
||||||
|
cancellationSignal.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "FingerprintEnrollStateRepository"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,43 +18,25 @@ package com.android.settings.biometrics.fingerprint2.domain.interactor
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.hardware.biometrics.BiometricConstants;
|
|
||||||
import android.hardware.biometrics.BiometricFingerprintConstants
|
|
||||||
import android.hardware.biometrics.SensorLocationInternal
|
|
||||||
import android.hardware.fingerprint.FingerprintEnrollOptions;
|
|
||||||
import android.hardware.fingerprint.FingerprintManager
|
import android.hardware.fingerprint.FingerprintManager
|
||||||
import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback
|
import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback
|
||||||
import android.hardware.fingerprint.FingerprintManager.RemovalCallback
|
import android.hardware.fingerprint.FingerprintManager.RemovalCallback
|
||||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
|
|
||||||
import android.os.CancellationSignal
|
import android.os.CancellationSignal
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
||||||
import com.android.settings.biometrics.BiometricUtils
|
|
||||||
import com.android.settings.biometrics.fingerprint2.conversion.Util.toEnrollError
|
|
||||||
import com.android.settings.biometrics.fingerprint2.conversion.Util.toOriginalReason
|
|
||||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
|
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
|
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
|
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper
|
import com.android.settings.password.ChooseLockSettingsHelper
|
||||||
import com.android.systemui.biometrics.shared.model.toFingerprintSensor
|
|
||||||
import com.google.android.setupcompat.util.WizardManagerHelper
|
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
import kotlinx.coroutines.CancellableContinuation
|
import kotlinx.coroutines.CancellableContinuation
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
|
||||||
import kotlinx.coroutines.channels.onFailure
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.update
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@@ -66,9 +48,7 @@ class FingerprintManagerInteractorImpl(
|
|||||||
private val fingerprintManager: FingerprintManager,
|
private val fingerprintManager: FingerprintManager,
|
||||||
fingerprintSensorRepository: FingerprintSensorRepository,
|
fingerprintSensorRepository: FingerprintSensorRepository,
|
||||||
private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
|
private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
|
||||||
private val pressToAuthInteractor: PressToAuthInteractor,
|
private val fingerprintEnrollStateRepository: FingerprintEnrollInteractor,
|
||||||
private val fingerprintFlow: FingerprintFlow,
|
|
||||||
private val intent: Intent,
|
|
||||||
) : FingerprintManagerInteractor {
|
) : FingerprintManagerInteractor {
|
||||||
|
|
||||||
private val maxFingerprints =
|
private val maxFingerprints =
|
||||||
@@ -77,7 +57,6 @@ class FingerprintManagerInteractorImpl(
|
|||||||
)
|
)
|
||||||
private val applicationContext = applicationContext.applicationContext
|
private val applicationContext = applicationContext.applicationContext
|
||||||
|
|
||||||
private val enrollRequestOutstanding = MutableStateFlow(false)
|
|
||||||
|
|
||||||
override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray> =
|
override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray> =
|
||||||
suspendCoroutine {
|
suspendCoroutine {
|
||||||
@@ -113,85 +92,8 @@ class FingerprintManagerInteractorImpl(
|
|||||||
|
|
||||||
override val maxEnrollableFingerprints = flow { emit(maxFingerprints) }
|
override val maxEnrollableFingerprints = flow { emit(maxFingerprints) }
|
||||||
|
|
||||||
override suspend fun enroll(
|
override suspend fun enroll(hardwareAuthToken: ByteArray?, enrollReason: EnrollReason): Flow<FingerEnrollState> =
|
||||||
hardwareAuthToken: ByteArray?,
|
fingerprintEnrollStateRepository.enroll(hardwareAuthToken, enrollReason)
|
||||||
enrollReason: EnrollReason,
|
|
||||||
): Flow<FingerEnrollState> = callbackFlow {
|
|
||||||
// TODO (b/308456120) Improve this logic
|
|
||||||
if (enrollRequestOutstanding.value) {
|
|
||||||
Log.d(TAG, "Outstanding enroll request, waiting 150ms")
|
|
||||||
delay(150)
|
|
||||||
if (enrollRequestOutstanding.value) {
|
|
||||||
Log.e(TAG, "Request still present, continuing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enrollRequestOutstanding.update { true }
|
|
||||||
|
|
||||||
var streamEnded = false
|
|
||||||
var totalSteps: Int? = null
|
|
||||||
val enrollmentCallback =
|
|
||||||
object : FingerprintManager.EnrollmentCallback() {
|
|
||||||
override fun onEnrollmentProgress(remaining: Int) {
|
|
||||||
// This is sort of an implementation detail, but unfortunately the API isn't
|
|
||||||
// very expressive. If anything we should look at changing the FingerprintManager API.
|
|
||||||
if (totalSteps == null) {
|
|
||||||
totalSteps = remaining + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
trySend(FingerEnrollState.EnrollProgress(remaining, totalSteps!!)).onFailure { error ->
|
|
||||||
Log.d(TAG, "onEnrollmentProgress($remaining) failed to send, due to $error")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remaining == 0) {
|
|
||||||
streamEnded = true
|
|
||||||
enrollRequestOutstanding.update { false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onEnrollmentHelp(helpMsgId: Int, helpString: CharSequence?) {
|
|
||||||
trySend(FingerEnrollState.EnrollHelp(helpMsgId, helpString.toString())).onFailure { error
|
|
||||||
->
|
|
||||||
Log.d(TAG, "onEnrollmentHelp failed to send, due to $error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onEnrollmentError(errMsgId: Int, errString: CharSequence?) {
|
|
||||||
trySend(errMsgId.toEnrollError(fingerprintFlow == SetupWizard)).onFailure { error ->
|
|
||||||
Log.d(TAG, "onEnrollmentError failed to send, due to $error")
|
|
||||||
}
|
|
||||||
Log.d(TAG, "onEnrollmentError($errMsgId)")
|
|
||||||
streamEnded = true
|
|
||||||
enrollRequestOutstanding.update { false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val cancellationSignal = CancellationSignal()
|
|
||||||
|
|
||||||
if (intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) === -1) {
|
|
||||||
val isSuw: Boolean = WizardManagerHelper.isAnySetupWizard(intent)
|
|
||||||
intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
|
|
||||||
if (isSuw) FingerprintEnrollOptions.ENROLL_REASON_SUW else
|
|
||||||
FingerprintEnrollOptions.ENROLL_REASON_SETTINGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
fingerprintManager.enroll(
|
|
||||||
hardwareAuthToken,
|
|
||||||
cancellationSignal,
|
|
||||||
applicationContext.userId,
|
|
||||||
enrollmentCallback,
|
|
||||||
enrollReason.toOriginalReason(),
|
|
||||||
toFingerprintEnrollOptions(intent)
|
|
||||||
)
|
|
||||||
awaitClose {
|
|
||||||
// If the stream has not been ended, and the user has stopped collecting the flow
|
|
||||||
// before it was over, send cancel.
|
|
||||||
if (!streamEnded) {
|
|
||||||
Log.e(TAG, "Cancel is sent from settings for enroll()")
|
|
||||||
cancellationSignal.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun removeFingerprint(fp: FingerprintData): Boolean = suspendCoroutine {
|
override suspend fun removeFingerprint(fp: FingerprintData): Boolean = suspendCoroutine {
|
||||||
val callback =
|
val callback =
|
||||||
@@ -263,14 +165,4 @@ class FingerprintManagerInteractorImpl(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toFingerprintEnrollOptions(intent: Intent): FingerprintEnrollOptions {
|
|
||||||
val reason: Int =
|
|
||||||
intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)
|
|
||||||
val builder: FingerprintEnrollOptions.Builder = FingerprintEnrollOptions.Builder()
|
|
||||||
builder.setEnrollReason(FingerprintEnrollOptions.ENROLL_REASON_UNKNOWN)
|
|
||||||
if (reason != -1) {
|
|
||||||
builder.setEnrollReason(reason)
|
|
||||||
}
|
|
||||||
return builder.build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
import android.view.OrientationEventListener
|
import android.view.OrientationEventListener
|
||||||
import com.android.internal.R
|
import com.android.internal.R
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -24,16 +25,23 @@ import kotlinx.coroutines.channels.awaitClose
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.shareIn
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.flow.transform
|
||||||
|
|
||||||
/**
|
/** Interactor which provides information about orientation */
|
||||||
* Interactor which provides information about orientation
|
|
||||||
*/
|
|
||||||
interface OrientationInteractor {
|
interface OrientationInteractor {
|
||||||
/** A flow that contains the information about the orientation changing */
|
/** A flow that contains the information about the orientation changing */
|
||||||
val orientation: Flow<Int>
|
val orientation: Flow<Int>
|
||||||
/** A flow that contains the rotation info */
|
/**
|
||||||
|
* A flow that contains the rotation info
|
||||||
|
*/
|
||||||
val rotation: Flow<Int>
|
val rotation: Flow<Int>
|
||||||
|
/**
|
||||||
|
* A flow that contains the rotation info matched against the def [config_reverseDefaultRotation]
|
||||||
|
*/
|
||||||
|
val rotationFromDefault: Flow<Int>
|
||||||
/**
|
/**
|
||||||
* A Helper function that computes rotation if device is in
|
* A Helper function that computes rotation if device is in
|
||||||
* [R.bool.config_reverseDefaultConfigRotation]
|
* [R.bool.config_reverseDefaultConfigRotation]
|
||||||
@@ -53,24 +61,11 @@ class OrientationInteractorImpl(private val context: Context, activityScope: Cor
|
|||||||
}
|
}
|
||||||
orientationEventListener.enable()
|
orientationEventListener.enable()
|
||||||
awaitClose { orientationEventListener.disable() }
|
awaitClose { orientationEventListener.disable() }
|
||||||
}
|
}.shareIn(activityScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
override val rotation: Flow<Int> =
|
override val rotation: Flow<Int> = orientation.transform { emit(context.display!!.rotation) }
|
||||||
callbackFlow {
|
|
||||||
val orientationEventListener =
|
override val rotationFromDefault: Flow<Int> = rotation.map { getRotationFromDefault(it) }
|
||||||
object : OrientationEventListener(context) {
|
|
||||||
override fun onOrientationChanged(orientation: Int) {
|
|
||||||
trySend(getRotationFromDefault(context.display!!.rotation))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
orientationEventListener.enable()
|
|
||||||
awaitClose { orientationEventListener.disable() }
|
|
||||||
}
|
|
||||||
.stateIn(
|
|
||||||
activityScope, // This is tied to the activity scope
|
|
||||||
SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener
|
|
||||||
context.display!!.rotation,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun getRotationFromDefault(rotation: Int): Int {
|
override fun getRotationFromDefault(rotation: Int): Int {
|
||||||
val isReverseDefaultRotation =
|
val isReverseDefaultRotation =
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Process
|
||||||
|
import android.os.VibrationAttributes
|
||||||
|
import android.os.VibrationEffect
|
||||||
|
import android.os.Vibrator
|
||||||
|
|
||||||
|
/** Indicates the possible vibration effects for fingerprint enrollment */
|
||||||
|
sealed class FingerprintVibrationEffects {
|
||||||
|
/** A vibration indicating an error */
|
||||||
|
data object UdfpsError : FingerprintVibrationEffects()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A vibration indicating success, this usually occurs when progress on the UDFPS enrollment has
|
||||||
|
* been made
|
||||||
|
*/
|
||||||
|
data object UdfpsSuccess : FingerprintVibrationEffects()
|
||||||
|
|
||||||
|
/** This vibration typically occurs when a help message is shown during UDFPS enrollment */
|
||||||
|
data object UdfpsHelp : FingerprintVibrationEffects()
|
||||||
|
}
|
||||||
|
/** Interface for sending haptic feedback */
|
||||||
|
interface VibrationInteractor {
|
||||||
|
/** This will send a haptic vibration */
|
||||||
|
fun vibrate(effect: FingerprintVibrationEffects, caller: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implementation of the VibrationInteractor interface */
|
||||||
|
class VibrationInteractorImpl(val vibrator: Vibrator, val applicationContext: Context) :
|
||||||
|
VibrationInteractor {
|
||||||
|
override fun vibrate(effect: FingerprintVibrationEffects, caller: String) {
|
||||||
|
val callerString = "$caller::$effect"
|
||||||
|
val res =
|
||||||
|
when (effect) {
|
||||||
|
FingerprintVibrationEffects.UdfpsHelp,
|
||||||
|
FingerprintVibrationEffects.UdfpsError ->
|
||||||
|
Pair(VIBRATE_EFFECT_ERROR, FINGERPRINT_ENROLLING_SONIFICATION_ATTRIBUTES)
|
||||||
|
FingerprintVibrationEffects.UdfpsSuccess ->
|
||||||
|
Pair(VIBRATE_EFFECT_SUCCESS, HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES)
|
||||||
|
}
|
||||||
|
vibrator.vibrate(
|
||||||
|
Process.myUid(),
|
||||||
|
applicationContext.opPackageName,
|
||||||
|
res.first,
|
||||||
|
callerString,
|
||||||
|
res.second,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val VIBRATE_EFFECT_ERROR = VibrationEffect.createWaveform(longArrayOf(0, 5, 55, 60), -1)
|
||||||
|
private val FINGERPRINT_ENROLLING_SONIFICATION_ATTRIBUTES =
|
||||||
|
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ACCESSIBILITY)
|
||||||
|
private val HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
|
||||||
|
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK)
|
||||||
|
private val VIBRATE_EFFECT_SUCCESS = VibrationEffect.get(VibrationEffect.EFFECT_CLICK)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,8 +57,7 @@ interface FingerprintManagerInteractor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs [FingerprintManager.enroll] with the [hardwareAuthToken] and [EnrollReason] for this
|
* Runs [FingerprintManager.enroll] with the [hardwareAuthToken] and [EnrollReason] for this
|
||||||
* enrollment. Returning the [FingerEnrollState] that represents this fingerprint enrollment
|
* enrollment. If successful data in the [fingerprintEnrollState] should be populated.
|
||||||
* state.
|
|
||||||
*/
|
*/
|
||||||
suspend fun enroll(
|
suspend fun enroll(
|
||||||
hardwareAuthToken: ByteArray?,
|
hardwareAuthToken: ByteArray?,
|
||||||
|
|||||||
@@ -42,4 +42,16 @@ sealed class FingerEnrollState {
|
|||||||
val shouldRetryEnrollment: Boolean,
|
val shouldRetryEnrollment: Boolean,
|
||||||
val isCancelled: Boolean,
|
val isCancelled: Boolean,
|
||||||
) : FingerEnrollState()
|
) : FingerEnrollState()
|
||||||
|
|
||||||
|
/** Indicates an acquired event has occurred */
|
||||||
|
data class Acquired(val acquiredGood: Boolean) : FingerEnrollState()
|
||||||
|
|
||||||
|
/** Indicates a pointer down event has occurred */
|
||||||
|
data object PointerDown : FingerEnrollState()
|
||||||
|
|
||||||
|
/** Indicates a pointer up event has occurred */
|
||||||
|
data object PointerUp : FingerEnrollState()
|
||||||
|
|
||||||
|
/** Indicates the overlay has shown */
|
||||||
|
data object OverlayShown : FingerEnrollState()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
package com.android.settings.biometrics.fingerprint2.lib.model
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A view model that describes the various stages of UDFPS Enrollment. This stages typically update
|
* A view model that describes the various stages of UDFPS Enrollment. This stages typically update
|
||||||
@@ -19,8 +19,10 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.activity
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.hardware.fingerprint.FingerprintEnrollOptions
|
||||||
import android.hardware.fingerprint.FingerprintManager
|
import android.hardware.fingerprint.FingerprintManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Vibrator
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.accessibility.AccessibilityManager
|
import android.view.accessibility.AccessibilityManager
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
@@ -35,21 +37,35 @@ import com.android.settings.Utils.SETTINGS_PACKAGE_NAME
|
|||||||
import com.android.settings.biometrics.BiometricEnrollBase
|
import com.android.settings.biometrics.BiometricEnrollBase
|
||||||
import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
|
import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
|
||||||
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
|
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
|
||||||
|
import com.android.settings.biometrics.BiometricUtils
|
||||||
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
||||||
|
import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepositoryImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
|
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
|
||||||
|
import com.android.settings.biometrics.fingerprint2.data.repository.UdfpsEnrollDebugRepositoryImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractorImpl
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractorImpl
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractorImpl
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.Default
|
import com.android.settings.biometrics.fingerprint2.lib.model.Default
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.Settings
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
|
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollEnrollingV2Fragment
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollEnrollingV2Fragment
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.RfpsEnrollFindSensorFragment
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.SfpsEnrollFindSensorFragment
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.UdfpsEnrollFindSensorFragment
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.fragment.RFPSEnrollFragment
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.fragment.RFPSEnrollFragment
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment.UdfpsEnrollFragment
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment.UdfpsEnrollFragment
|
||||||
@@ -77,6 +93,7 @@ import com.android.settings.flags.Flags
|
|||||||
import com.android.settings.password.ChooseLockGeneric
|
import com.android.settings.password.ChooseLockGeneric
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper
|
import com.android.settings.password.ChooseLockSettingsHelper
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE
|
import com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE
|
||||||
|
import com.android.settingslib.display.DisplayDensityUtils
|
||||||
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
||||||
import com.google.android.setupcompat.util.WizardManagerHelper
|
import com.google.android.setupcompat.util.WizardManagerHelper
|
||||||
import com.google.android.setupdesign.util.ThemeHelper
|
import com.google.android.setupdesign.util.ThemeHelper
|
||||||
@@ -95,14 +112,17 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
private lateinit var navigationViewModel: FingerprintNavigationViewModel
|
private lateinit var navigationViewModel: FingerprintNavigationViewModel
|
||||||
private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
|
private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
|
||||||
private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
|
private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
|
||||||
|
private lateinit var vibrationInteractor: VibrationInteractor
|
||||||
private lateinit var foldStateInteractor: FoldStateInteractor
|
private lateinit var foldStateInteractor: FoldStateInteractor
|
||||||
private lateinit var orientationInteractor: OrientationInteractor
|
private lateinit var orientationInteractor: OrientationInteractor
|
||||||
|
private lateinit var displayDensityInteractor: DisplayDensityInteractor
|
||||||
private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
|
private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
|
||||||
private lateinit var backgroundViewModel: BackgroundViewModel
|
private lateinit var backgroundViewModel: BackgroundViewModel
|
||||||
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
|
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
|
||||||
private lateinit var fingerprintEnrollConfirmationViewModel:
|
private lateinit var fingerprintEnrollConfirmationViewModel:
|
||||||
FingerprintEnrollConfirmationViewModel
|
FingerprintEnrollConfirmationViewModel
|
||||||
private lateinit var udfpsViewModel: UdfpsViewModel
|
private lateinit var udfpsViewModel: UdfpsViewModel
|
||||||
|
private lateinit var enrollStageInteractor: EnrollStageInteractor
|
||||||
private val coroutineDispatcher = Dispatchers.Default
|
private val coroutineDispatcher = Dispatchers.Default
|
||||||
|
|
||||||
/** Result listener for ChooseLock activity flow. */
|
/** Result listener for ChooseLock activity flow. */
|
||||||
@@ -135,6 +155,12 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
super.onConfigurationChanged(newConfig)
|
super.onConfigurationChanged(newConfig)
|
||||||
foldStateInteractor.onConfigurationChange(newConfig)
|
foldStateInteractor.onConfigurationChange(newConfig)
|
||||||
|
val displayDensityUtils = DisplayDensityUtils(applicationContext)
|
||||||
|
val currIndex = displayDensityUtils.currentIndexForDefaultDisplay
|
||||||
|
displayDensityInteractor.updateFontScale(resources.configuration.fontScale)
|
||||||
|
displayDensityInteractor.updateDisplayDensity(
|
||||||
|
displayDensityUtils.defaultDisplayDensityValues[currIndex]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onConfirmDevice(resultCode: Int, data: Intent?) {
|
private fun onConfirmDevice(resultCode: Int, data: Intent?) {
|
||||||
@@ -193,10 +219,43 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
fingerprintFlowViewModel =
|
fingerprintFlowViewModel =
|
||||||
ViewModelProvider(this, FingerprintFlowViewModel.FingerprintFlowViewModelFactory(enrollType))[
|
ViewModelProvider(this, FingerprintFlowViewModel.FingerprintFlowViewModelFactory(enrollType))[
|
||||||
FingerprintFlowViewModel::class.java]
|
FingerprintFlowViewModel::class.java]
|
||||||
|
val displayDensityUtils = DisplayDensityUtils(context)
|
||||||
|
val currIndex = displayDensityUtils.currentIndexForDefaultDisplay
|
||||||
|
val defaultDisplayDensity = displayDensityUtils.defaultDensityForDefaultDisplay
|
||||||
|
displayDensityInteractor =
|
||||||
|
DisplayDensityInteractorImpl(
|
||||||
|
resources.configuration.fontScale,
|
||||||
|
displayDensityUtils.defaultDisplayDensityValues[currIndex],
|
||||||
|
defaultDisplayDensity,
|
||||||
|
lifecycleScope,
|
||||||
|
)
|
||||||
|
|
||||||
|
val debuggingRepo = DebuggingRepositoryImpl()
|
||||||
|
val debuggingInteractor = DebuggingInteractorImpl(debuggingRepo)
|
||||||
|
val udfpsEnrollDebugRepositoryImpl = UdfpsEnrollDebugRepositoryImpl()
|
||||||
|
|
||||||
val fingerprintSensorRepo =
|
val fingerprintSensorRepo =
|
||||||
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
|
if (debuggingRepo.isUdfpsEnrollmentDebuggingEnabled()) udfpsEnrollDebugRepositoryImpl
|
||||||
val pressToAuthInteractor = PressToAuthInteractorImpl(context, backgroundDispatcher)
|
else FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
|
||||||
|
|
||||||
|
if (intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) === -1) {
|
||||||
|
val isSuw: Boolean = WizardManagerHelper.isAnySetupWizard(intent)
|
||||||
|
intent.putExtra(
|
||||||
|
BiometricUtils.EXTRA_ENROLL_REASON,
|
||||||
|
if (isSuw) FingerprintEnrollOptions.ENROLL_REASON_SUW
|
||||||
|
else FingerprintEnrollOptions.ENROLL_REASON_SETTINGS,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val fingerprintEnrollStateRepository =
|
||||||
|
if (debuggingRepo.isUdfpsEnrollmentDebuggingEnabled()) udfpsEnrollDebugRepositoryImpl
|
||||||
|
else
|
||||||
|
FingerprintEnrollInteractorImpl(
|
||||||
|
context.applicationContext,
|
||||||
|
intent.toFingerprintEnrollOptions(),
|
||||||
|
fingerprintManager,
|
||||||
|
Settings,
|
||||||
|
)
|
||||||
|
|
||||||
val fingerprintManagerInteractor =
|
val fingerprintManagerInteractor =
|
||||||
FingerprintManagerInteractorImpl(
|
FingerprintManagerInteractorImpl(
|
||||||
@@ -205,12 +264,10 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
fingerprintManager,
|
fingerprintManager,
|
||||||
fingerprintSensorRepo,
|
fingerprintSensorRepo,
|
||||||
GatekeeperPasswordProvider(LockPatternUtils(context)),
|
GatekeeperPasswordProvider(LockPatternUtils(context)),
|
||||||
pressToAuthInteractor,
|
fingerprintEnrollStateRepository,
|
||||||
enrollType,
|
|
||||||
getIntent(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var challenge: Long? = intent.getExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE) as Long?
|
var challenge = intent.getExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE) as Long?
|
||||||
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
|
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
|
||||||
val gatekeeperInfo = FingerprintGatekeeperViewModel.toGateKeeperInfo(challenge, token)
|
val gatekeeperInfo = FingerprintGatekeeperViewModel.toGateKeeperInfo(challenge, token)
|
||||||
|
|
||||||
@@ -256,6 +313,8 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
foldStateInteractor.onConfigurationChange(resources.configuration)
|
foldStateInteractor.onConfigurationChange(resources.configuration)
|
||||||
|
|
||||||
orientationInteractor = OrientationInteractorImpl(context, lifecycleScope)
|
orientationInteractor = OrientationInteractorImpl(context, lifecycleScope)
|
||||||
|
vibrationInteractor =
|
||||||
|
VibrationInteractorImpl(context.getSystemService(Vibrator::class.java)!!, context)
|
||||||
|
|
||||||
// Initialize FingerprintViewModel
|
// Initialize FingerprintViewModel
|
||||||
fingerprintEnrollViewModel =
|
fingerprintEnrollViewModel =
|
||||||
@@ -309,10 +368,23 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
),
|
),
|
||||||
)[RFPSViewModel::class.java]
|
)[RFPSViewModel::class.java]
|
||||||
|
|
||||||
|
enrollStageInteractor = EnrollStageInteractorImpl()
|
||||||
|
|
||||||
udfpsViewModel =
|
udfpsViewModel =
|
||||||
ViewModelProvider(
|
ViewModelProvider(
|
||||||
this,
|
this,
|
||||||
UdfpsViewModel.UdfpsEnrollmentFactory(),
|
UdfpsViewModel.UdfpsEnrollmentFactory(
|
||||||
|
vibrationInteractor,
|
||||||
|
displayDensityInteractor,
|
||||||
|
navigationViewModel,
|
||||||
|
debuggingInteractor,
|
||||||
|
fingerprintEnrollEnrollingViewModel,
|
||||||
|
udfpsEnrollDebugRepositoryImpl,
|
||||||
|
enrollStageInteractor,
|
||||||
|
orientationInteractor,
|
||||||
|
backgroundViewModel,
|
||||||
|
fingerprintSensorRepo,
|
||||||
|
),
|
||||||
)[UdfpsViewModel::class.java]
|
)[UdfpsViewModel::class.java]
|
||||||
|
|
||||||
fingerprintEnrollConfirmationViewModel =
|
fingerprintEnrollConfirmationViewModel =
|
||||||
@@ -348,7 +420,12 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
when (step) {
|
when (step) {
|
||||||
Confirmation -> FingerprintEnrollConfirmationV2Fragment()
|
Confirmation -> FingerprintEnrollConfirmationV2Fragment()
|
||||||
is Education -> {
|
is Education -> {
|
||||||
FingerprintEnrollFindSensorV2Fragment(step.sensor.sensorType)
|
when (step.sensor.sensorType) {
|
||||||
|
FingerprintSensorType.REAR -> RfpsEnrollFindSensorFragment()
|
||||||
|
FingerprintSensorType.UDFPS_OPTICAL,
|
||||||
|
FingerprintSensorType.UDFPS_ULTRASONIC -> UdfpsEnrollFindSensorFragment()
|
||||||
|
else -> SfpsEnrollFindSensorFragment()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is Enrollment -> {
|
is Enrollment -> {
|
||||||
when (step.sensor.sensorType) {
|
when (step.sensor.sensorType) {
|
||||||
@@ -370,7 +447,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
supportFragmentManager
|
supportFragmentManager
|
||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
.setReorderingAllowed(true)
|
.setReorderingAllowed(true)
|
||||||
.add(R.id.fragment_container_view, theClass, null)
|
.add(R.id.fragment_container_view, theClass::class.java, null)
|
||||||
.commit()
|
.commit()
|
||||||
navigationViewModel.update(
|
navigationViewModel.update(
|
||||||
FingerprintAction.TRANSITION_FINISHED,
|
FingerprintAction.TRANSITION_FINISHED,
|
||||||
@@ -386,7 +463,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
|||||||
navigationViewModel.shouldFinish.filterNotNull().collect {
|
navigationViewModel.shouldFinish.filterNotNull().collect {
|
||||||
Log.d(TAG, "FingerprintSettingsNav.finishing($it)")
|
Log.d(TAG, "FingerprintSettingsNav.finishing($it)")
|
||||||
if (it.result != null) {
|
if (it.result != null) {
|
||||||
finishActivity(it.result as Int)
|
finishActivity(it.result)
|
||||||
} else {
|
} else {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
|
||||||
|
import com.android.settings.biometrics.fingerprint.FingerprintFindSensorAnimation
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
|
||||||
|
import com.google.android.setupcompat.template.FooterBarMixin
|
||||||
|
import com.google.android.setupcompat.template.FooterButton
|
||||||
|
import com.google.android.setupdesign.GlifLayout
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fragment that is used to educate the user about the rear fingerprint sensor on this device.
|
||||||
|
*
|
||||||
|
* The main goals of this page are
|
||||||
|
* 1. Inform the user where the fingerprint sensor is on their device
|
||||||
|
* 2. Explain to the user how the enrollment process shown by [FingerprintEnrollEnrollingV2Fragment]
|
||||||
|
* will work.
|
||||||
|
*/
|
||||||
|
class RfpsEnrollFindSensorFragment() : Fragment() {
|
||||||
|
/** Used for testing purposes */
|
||||||
|
private var factory: ViewModelProvider.Factory? = null
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
constructor(theFactory: ViewModelProvider.Factory) : this() {
|
||||||
|
factory = theFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
private val viewModelProvider: ViewModelProvider by lazy {
|
||||||
|
if (factory != null) {
|
||||||
|
ViewModelProvider(requireActivity(), factory!!)
|
||||||
|
} else {
|
||||||
|
ViewModelProvider(requireActivity())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var animation: FingerprintFindSensorAnimation? = null
|
||||||
|
|
||||||
|
private val viewModel: FingerprintEnrollFindSensorViewModel by lazy {
|
||||||
|
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?,
|
||||||
|
): View? {
|
||||||
|
val view =
|
||||||
|
inflater.inflate(R.layout.fingerprint_v2_enroll_find_sensor, container, false)!! as GlifLayout
|
||||||
|
view.setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title)
|
||||||
|
view.setDescriptionText(R.string.security_settings_fingerprint_enroll_find_sensor_message)
|
||||||
|
|
||||||
|
// Set up footer bar
|
||||||
|
val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
|
||||||
|
setupSecondaryButton(footerBarMixin)
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) }
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.showRfpsAnimation.collect {
|
||||||
|
animation = view.findViewById(R.id.fingerprint_sensor_location_animation)
|
||||||
|
animation!!.startAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.showErrorDialog.collect { (errMsgId, isSetup) ->
|
||||||
|
// TODO: Covert error dialog kotlin as well
|
||||||
|
FingerprintErrorDialog.showErrorDialog(requireActivity(), errMsgId, isSetup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
animation?.stopAnimation()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupSecondaryButton(footerBarMixin: FooterBarMixin) {
|
||||||
|
footerBarMixin.secondaryButton =
|
||||||
|
FooterButton.Builder(requireActivity())
|
||||||
|
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
|
||||||
|
.setListener { viewModel.secondaryButtonClicked() }
|
||||||
|
.setButtonType(FooterButton.ButtonType.SKIP)
|
||||||
|
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupPrimaryButton(footerBarMixin: FooterBarMixin) {
|
||||||
|
footerBarMixin.primaryButton =
|
||||||
|
FooterButton.Builder(requireActivity())
|
||||||
|
.setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
|
||||||
|
.setListener {
|
||||||
|
Log.d(TAG, "onStartButtonClick")
|
||||||
|
viewModel.proceedToEnrolling()
|
||||||
|
}
|
||||||
|
.setButtonType(FooterButton.ButtonType.NEXT)
|
||||||
|
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "RfpsEnrollFindSensor"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Surface
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.airbnb.lottie.LottieAnimationView
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
|
||||||
|
import com.google.android.setupcompat.template.FooterBarMixin
|
||||||
|
import com.google.android.setupcompat.template.FooterButton
|
||||||
|
import com.google.android.setupdesign.GlifLayout
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fragment that is used to educate the user about the side fingerprint sensor on this device.
|
||||||
|
*
|
||||||
|
* The main goals of this page are
|
||||||
|
* 1. Inform the user where the fingerprint sensor is on their device
|
||||||
|
* 2. Explain to the user how the enrollment process shown by [FingerprintEnrollEnrollingV2Fragment]
|
||||||
|
* will work.
|
||||||
|
*/
|
||||||
|
class SfpsEnrollFindSensorFragment() : Fragment() {
|
||||||
|
/** Used for testing purposes */
|
||||||
|
private var factory: ViewModelProvider.Factory? = null
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
constructor(theFactory: ViewModelProvider.Factory) : this() {
|
||||||
|
factory = theFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
private val viewModelProvider: ViewModelProvider by lazy {
|
||||||
|
if (factory != null) {
|
||||||
|
ViewModelProvider(requireActivity(), factory!!)
|
||||||
|
} else {
|
||||||
|
ViewModelProvider(requireActivity())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val viewModel: FingerprintEnrollFindSensorViewModel by lazy {
|
||||||
|
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?,
|
||||||
|
): View? {
|
||||||
|
val view =
|
||||||
|
inflater.inflate(R.layout.sfps_enroll_find_sensor_layout, container, false)!! as GlifLayout
|
||||||
|
view.setHeaderText(R.string.security_settings_sfps_enroll_find_sensor_title)
|
||||||
|
view.setDescriptionText(R.string.security_settings_sfps_enroll_find_sensor_message)
|
||||||
|
|
||||||
|
// Set up footer bar
|
||||||
|
val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
|
||||||
|
setupSecondaryButton(footerBarMixin)
|
||||||
|
|
||||||
|
// Set up lottie
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.sfpsLottieInfo.collect { (isFolded, rotation) ->
|
||||||
|
setupLottie(view, getSfpsIllustrationLottieAnimation(isFolded, rotation))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) }
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.showErrorDialog.collect { (errMsgId, isSetup) ->
|
||||||
|
// TODO: Covert error dialog kotlin as well
|
||||||
|
FingerprintErrorDialog.showErrorDialog(requireActivity(), errMsgId, isSetup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupSecondaryButton(footerBarMixin: FooterBarMixin) {
|
||||||
|
footerBarMixin.secondaryButton =
|
||||||
|
FooterButton.Builder(requireActivity())
|
||||||
|
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
|
||||||
|
.setListener { viewModel.secondaryButtonClicked() }
|
||||||
|
.setButtonType(FooterButton.ButtonType.SKIP)
|
||||||
|
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupPrimaryButton(footerBarMixin: FooterBarMixin) {
|
||||||
|
footerBarMixin.primaryButton =
|
||||||
|
FooterButton.Builder(requireActivity())
|
||||||
|
.setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
|
||||||
|
.setListener {
|
||||||
|
Log.d(TAG, "onStartButtonClick")
|
||||||
|
viewModel.proceedToEnrolling()
|
||||||
|
}
|
||||||
|
.setButtonType(FooterButton.ButtonType.NEXT)
|
||||||
|
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSfpsIllustrationLottieAnimation(isFolded: Boolean, rotation: Int): Int {
|
||||||
|
val animation: Int
|
||||||
|
when (rotation) {
|
||||||
|
Surface.ROTATION_90 ->
|
||||||
|
animation =
|
||||||
|
(if (isFolded) R.raw.fingerprint_edu_lottie_folded_top_left
|
||||||
|
else R.raw.fingerprint_edu_lottie_portrait_top_left)
|
||||||
|
Surface.ROTATION_180 ->
|
||||||
|
animation =
|
||||||
|
(if (isFolded) R.raw.fingerprint_edu_lottie_folded_bottom_left
|
||||||
|
else R.raw.fingerprint_edu_lottie_landscape_bottom_left)
|
||||||
|
Surface.ROTATION_270 ->
|
||||||
|
animation =
|
||||||
|
(if (isFolded) R.raw.fingerprint_edu_lottie_folded_bottom_right
|
||||||
|
else R.raw.fingerprint_edu_lottie_portrait_bottom_right)
|
||||||
|
else ->
|
||||||
|
animation =
|
||||||
|
(if (isFolded) R.raw.fingerprint_edu_lottie_folded_top_right
|
||||||
|
else R.raw.fingerprint_edu_lottie_landscape_top_right)
|
||||||
|
}
|
||||||
|
return animation
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupLottie(
|
||||||
|
view: View,
|
||||||
|
lottieAnimation: Int,
|
||||||
|
lottieClickListener: View.OnClickListener? = null,
|
||||||
|
) {
|
||||||
|
val illustrationLottie: LottieAnimationView? = view.findViewById(R.id.illustration_lottie)
|
||||||
|
illustrationLottie?.setAnimation(lottieAnimation)
|
||||||
|
illustrationLottie?.playAnimation()
|
||||||
|
illustrationLottie?.setOnClickListener(lottieClickListener)
|
||||||
|
illustrationLottie?.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "SfpsEnrollFindSensor"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2023 The Android Open Source Project
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -29,36 +29,27 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import com.airbnb.lottie.LottieAnimationView
|
import com.airbnb.lottie.LottieAnimationView
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
|
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintFindSensorAnimation
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
|
||||||
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
|
||||||
import com.google.android.setupcompat.template.FooterBarMixin
|
import com.google.android.setupcompat.template.FooterBarMixin
|
||||||
import com.google.android.setupcompat.template.FooterButton
|
import com.google.android.setupcompat.template.FooterButton
|
||||||
import com.google.android.setupdesign.GlifLayout
|
import com.google.android.setupdesign.GlifLayout
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
private const val TAG = "FingerprintEnrollFindSensorV2Fragment"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A fragment that is used to educate the user about the fingerprint sensor on this device.
|
* A fragment that is used to educate the user about the under display fingerprint sensor on this
|
||||||
*
|
* device.
|
||||||
* If the sensor is not a udfps sensor, this fragment listens to fingerprint enrollment for
|
|
||||||
* proceeding to the enroll enrolling.
|
|
||||||
*
|
*
|
||||||
* The main goals of this page are
|
* The main goals of this page are
|
||||||
* 1. Inform the user where the fingerprint sensor is on their device
|
* 1. Inform the user where the fingerprint sensor is on their device
|
||||||
* 2. Explain to the user how the enrollment process shown by [FingerprintEnrollEnrollingV2Fragment]
|
* 2. Explain to the user how the enrollment process shown by [FingerprintEnrollEnrollingV2Fragment]
|
||||||
* will work.
|
* will work.
|
||||||
*/
|
*/
|
||||||
class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorType) : Fragment() {
|
class UdfpsEnrollFindSensorFragment() : Fragment() {
|
||||||
/** Used for testing purposes */
|
/** Used for testing purposes */
|
||||||
private var factory: ViewModelProvider.Factory? = null
|
private var factory: ViewModelProvider.Factory? = null
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
constructor(
|
constructor(theFactory: ViewModelProvider.Factory) : this() {
|
||||||
sensorType: FingerprintSensorType,
|
|
||||||
theFactory: ViewModelProvider.Factory,
|
|
||||||
) : this(sensorType) {
|
|
||||||
factory = theFactory
|
factory = theFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,10 +61,6 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is only for non-udfps or non-sfps sensor. For udfps and sfps, we show lottie.
|
|
||||||
private var animation: FingerprintFindSensorAnimation? = null
|
|
||||||
|
|
||||||
private var contentLayoutId: Int = -1
|
|
||||||
private val viewModel: FingerprintEnrollFindSensorViewModel by lazy {
|
private val viewModel: FingerprintEnrollFindSensorViewModel by lazy {
|
||||||
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
|
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
|
||||||
}
|
}
|
||||||
@@ -83,31 +70,18 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
|
|||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?,
|
savedInstanceState: Bundle?,
|
||||||
): View? {
|
): View? {
|
||||||
|
val view =
|
||||||
contentLayoutId =
|
inflater.inflate(R.layout.udfps_enroll_find_sensor_layout, container, false)!! as GlifLayout
|
||||||
when (sensorType) {
|
view.setHeaderText(R.string.security_settings_udfps_enroll_find_sensor_title)
|
||||||
FingerprintSensorType.UDFPS_OPTICAL,
|
view.setDescriptionText(R.string.security_settings_udfps_enroll_find_sensor_message)
|
||||||
FingerprintSensorType.UDFPS_ULTRASONIC -> R.layout.udfps_enroll_find_sensor_layout
|
|
||||||
FingerprintSensorType.POWER_BUTTON -> R.layout.sfps_enroll_find_sensor_layout
|
|
||||||
else -> R.layout.fingerprint_v2_enroll_find_sensor
|
|
||||||
}
|
|
||||||
|
|
||||||
val view = inflater.inflate(contentLayoutId, container, false)!! as GlifLayout
|
|
||||||
setTexts(sensorType, view)
|
|
||||||
|
|
||||||
// Set up footer bar
|
// Set up footer bar
|
||||||
val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
|
val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
|
||||||
setupSecondaryButton(footerBarMixin)
|
setupSecondaryButton(footerBarMixin)
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) }
|
viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up lottie or animation
|
|
||||||
lifecycleScope.launch {
|
|
||||||
viewModel.sfpsLottieInfo.collect { (isFolded, rotation) ->
|
|
||||||
setupLottie(view, getSfpsIllustrationLottieAnimation(isFolded, rotation))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewModel.udfpsLottieInfo.collect { isAccessibilityEnabled ->
|
viewModel.udfpsLottieInfo.collect { isAccessibilityEnabled ->
|
||||||
val lottieAnimation =
|
val lottieAnimation =
|
||||||
@@ -115,12 +89,6 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
|
|||||||
setupLottie(view, lottieAnimation) { viewModel.proceedToEnrolling() }
|
setupLottie(view, lottieAnimation) { viewModel.proceedToEnrolling() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
|
||||||
viewModel.showRfpsAnimation.collect {
|
|
||||||
animation = view.findViewById(R.id.fingerprint_sensor_location_animation)
|
|
||||||
animation!!.startAnimation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewModel.showErrorDialog.collect { (errMsgId, isSetup) ->
|
viewModel.showErrorDialog.collect { (errMsgId, isSetup) ->
|
||||||
@@ -131,11 +99,6 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
|
|||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
animation?.stopAnimation()
|
|
||||||
super.onDestroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupSecondaryButton(footerBarMixin: FooterBarMixin) {
|
private fun setupSecondaryButton(footerBarMixin: FooterBarMixin) {
|
||||||
footerBarMixin.secondaryButton =
|
footerBarMixin.secondaryButton =
|
||||||
FooterButton.Builder(requireActivity())
|
FooterButton.Builder(requireActivity())
|
||||||
@@ -159,36 +122,6 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupLottie(
|
|
||||||
view: View,
|
|
||||||
lottieAnimation: Int,
|
|
||||||
lottieClickListener: View.OnClickListener? = null,
|
|
||||||
) {
|
|
||||||
val illustrationLottie: LottieAnimationView? = view.findViewById(R.id.illustration_lottie)
|
|
||||||
illustrationLottie?.setAnimation(lottieAnimation)
|
|
||||||
illustrationLottie?.playAnimation()
|
|
||||||
illustrationLottie?.setOnClickListener(lottieClickListener)
|
|
||||||
illustrationLottie?.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setTexts(sensorType: FingerprintSensorType?, view: GlifLayout) {
|
|
||||||
when (sensorType) {
|
|
||||||
FingerprintSensorType.UDFPS_OPTICAL,
|
|
||||||
FingerprintSensorType.UDFPS_ULTRASONIC -> {
|
|
||||||
view.setHeaderText(R.string.security_settings_udfps_enroll_find_sensor_title)
|
|
||||||
view.setDescriptionText(R.string.security_settings_udfps_enroll_find_sensor_message)
|
|
||||||
}
|
|
||||||
FingerprintSensorType.POWER_BUTTON -> {
|
|
||||||
view.setHeaderText(R.string.security_settings_sfps_enroll_find_sensor_title)
|
|
||||||
view.setDescriptionText(R.string.security_settings_sfps_enroll_find_sensor_message)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
view.setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title)
|
|
||||||
view.setDescriptionText(R.string.security_settings_fingerprint_enroll_find_sensor_message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getSfpsIllustrationLottieAnimation(isFolded: Boolean, rotation: Int): Int {
|
private fun getSfpsIllustrationLottieAnimation(isFolded: Boolean, rotation: Int): Int {
|
||||||
val animation: Int
|
val animation: Int
|
||||||
when (rotation) {
|
when (rotation) {
|
||||||
@@ -211,4 +144,20 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
|
|||||||
}
|
}
|
||||||
return animation
|
return animation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setupLottie(
|
||||||
|
view: View,
|
||||||
|
lottieAnimation: Int,
|
||||||
|
lottieClickListener: View.OnClickListener? = null,
|
||||||
|
) {
|
||||||
|
val illustrationLottie: LottieAnimationView? = view.findViewById(R.id.illustration_lottie)
|
||||||
|
illustrationLottie?.setAnimation(lottieAnimation)
|
||||||
|
illustrationLottie?.playAnimation()
|
||||||
|
illustrationLottie?.setOnClickListener(lottieClickListener)
|
||||||
|
illustrationLottie?.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "UdfpsEnrollFindSensor"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.hardware.fingerprint.FingerprintEnrollOptions
|
||||||
|
import com.android.settings.biometrics.BiometricUtils
|
||||||
|
|
||||||
|
fun Intent.toFingerprintEnrollOptions(): FingerprintEnrollOptions {
|
||||||
|
val reason: Int = this.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)
|
||||||
|
val builder: FingerprintEnrollOptions.Builder = FingerprintEnrollOptions.Builder()
|
||||||
|
builder.setEnrollReason(FingerprintEnrollOptions.ENROLL_REASON_UNKNOWN)
|
||||||
|
if (reason != -1) {
|
||||||
|
builder.setEnrollReason(reason)
|
||||||
|
}
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2023 The Android Open Source Project
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.widget
|
||||||
|
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
@@ -29,8 +29,6 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment
|
|||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
||||||
private const val TAG = "FingerprintErrorDialog"
|
|
||||||
|
|
||||||
/** A Dialog used for fingerprint enrollment when an error occurs. */
|
/** A Dialog used for fingerprint enrollment when an error occurs. */
|
||||||
class FingerprintErrorDialog : InstrumentedDialogFragment() {
|
class FingerprintErrorDialog : InstrumentedDialogFragment() {
|
||||||
private lateinit var onContinue: DialogInterface.OnClickListener
|
private lateinit var onContinue: DialogInterface.OnClickListener
|
||||||
@@ -82,6 +80,7 @@ class FingerprintErrorDialog : InstrumentedDialogFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val TAG = "FingerprintErrorDialog"
|
||||||
private const val KEY_MESSAGE = "fingerprint_message"
|
private const val KEY_MESSAGE = "fingerprint_message"
|
||||||
private const val KEY_TITLE = "fingerprint_title"
|
private const val KEY_TITLE = "fingerprint_title"
|
||||||
private const val KEY_SHOULD_TRY_AGAIN = "should_try_again"
|
private const val KEY_SHOULD_TRY_AGAIN = "should_try_again"
|
||||||
@@ -34,9 +34,9 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.widget.FingerprintErrorDialog
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSIconTouchViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSIconTouchViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.FingerprintErrorDialog
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.IconTouchDialog
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.IconTouchDialog
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.RFPSProgressBar
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.RFPSProgressBar
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class RFPSViewModel(
|
|||||||
orientationInteractor: OrientationInteractor,
|
orientationInteractor: OrientationInteractor,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _textViewIsVisible = MutableStateFlow<Boolean>(false)
|
private val _textViewIsVisible = MutableStateFlow(false)
|
||||||
/** Value to indicate if the text view is visible or not */
|
/** Value to indicate if the text view is visible or not */
|
||||||
val textViewIsVisible: Flow<Boolean> = _textViewIsVisible.asStateFlow()
|
val textViewIsVisible: Flow<Boolean> = _textViewIsVisible.asStateFlow()
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ class RFPSViewModel(
|
|||||||
/** Indicates if the icon should be animating or not */
|
/** Indicates if the icon should be animating or not */
|
||||||
val shouldAnimateIcon = _shouldAnimateIcon
|
val shouldAnimateIcon = _shouldAnimateIcon
|
||||||
|
|
||||||
private var enrollFlow: Flow<FingerEnrollState?> = fingerprintEnrollViewModel.enrollFLow
|
private var enrollFlow: Flow<FingerEnrollState?> = fingerprintEnrollViewModel.enrollFlow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enroll progress message with a replay of size 1 allowing for new subscribers to get the most
|
* Enroll progress message with a replay of size 1 allowing for new subscribers to get the most
|
||||||
@@ -142,7 +142,7 @@ class RFPSViewModel(
|
|||||||
_textViewIsVisible.update { false }
|
_textViewIsVisible.update { false }
|
||||||
_shouldAnimateIcon = fingerprintEnrollViewModel.enrollFlowShouldBeRunning
|
_shouldAnimateIcon = fingerprintEnrollViewModel.enrollFlowShouldBeRunning
|
||||||
/** Indicates if the icon should be animating or not */
|
/** Indicates if the icon should be animating or not */
|
||||||
enrollFlow = fingerprintEnrollViewModel.enrollFLow
|
enrollFlow = fingerprintEnrollViewModel.enrollFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
class RFPSViewModelFactory(
|
class RFPSViewModelFactory(
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrol
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.MotionEvent.ACTION_HOVER_MOVE
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
@@ -30,10 +32,12 @@ import androidx.lifecycle.repeatOnLifecycle
|
|||||||
import com.airbnb.lottie.LottieAnimationView
|
import com.airbnb.lottie.LottieAnimationView
|
||||||
import com.airbnb.lottie.LottieCompositionFactory
|
import com.airbnb.lottie.LottieCompositionFactory
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.widget.FingerprintErrorDialog
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.DescriptionText
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.DescriptionText
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.HeaderText
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.EducationAnimationModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.EducationAnimationModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.HeaderText
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget.UdfpsEnrollViewV2
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget.UdfpsEnrollViewV2
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
||||||
@@ -47,6 +51,7 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
private var factory: ViewModelProvider.Factory? = null
|
private var factory: ViewModelProvider.Factory? = null
|
||||||
private val viewModel: UdfpsViewModel by lazy { viewModelProvider[UdfpsViewModel::class.java] }
|
private val viewModel: UdfpsViewModel by lazy { viewModelProvider[UdfpsViewModel::class.java] }
|
||||||
private lateinit var udfpsEnrollView: UdfpsEnrollViewV2
|
private lateinit var udfpsEnrollView: UdfpsEnrollViewV2
|
||||||
|
private lateinit var lottie: LottieAnimationView
|
||||||
|
|
||||||
private val viewModelProvider: ViewModelProvider by lazy {
|
private val viewModelProvider: ViewModelProvider by lazy {
|
||||||
if (factory != null) {
|
if (factory != null) {
|
||||||
@@ -63,7 +68,8 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val illustrationLottie: LottieAnimationView = view.findViewById(R.id.illustration_lottie)!!
|
val fragment = this
|
||||||
|
lottie = view.findViewById(R.id.illustration_lottie)!!
|
||||||
udfpsEnrollView = view.findViewById(R.id.udfps_animation_view)!!
|
udfpsEnrollView = view.findViewById(R.id.udfps_animation_view)!!
|
||||||
val titleTextView = view.findViewById<TextView>(R.id.title)!!
|
val titleTextView = view.findViewById<TextView>(R.id.title)!!
|
||||||
val descriptionTextView = view.findViewById<TextView>(R.id.description)!!
|
val descriptionTextView = view.findViewById<TextView>(R.id.description)!!
|
||||||
@@ -79,6 +85,11 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
launch {
|
||||||
|
viewModel.sensorLocation.collect { sensor ->
|
||||||
|
udfpsEnrollView.setSensorRect(sensor.sensorBounds, sensor.sensorType)
|
||||||
|
}
|
||||||
|
}
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewModel.headerText.collect { titleTextView.setText(it.toResource()) }
|
viewModel.headerText.collect { titleTextView.setText(it.toResource()) }
|
||||||
}
|
}
|
||||||
@@ -92,35 +103,59 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewModel.sensorLocation.collect { rect -> udfpsEnrollView.setSensorRect(rect) }
|
viewModel.shouldShowLottie.collect {
|
||||||
|
lottie.visibility = if (it) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
viewModel.accessibilityEnabled.collect { isEnabled -> udfpsEnrollView.setAccessibilityEnabled(isEnabled) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewModel.lottie.collect { lottieModel ->
|
viewModel.lottie.collect { lottieModel ->
|
||||||
|
if (lottie.visibility == View.GONE) {
|
||||||
|
return@collect
|
||||||
|
}
|
||||||
val resource = lottieModel.toResource()
|
val resource = lottieModel.toResource()
|
||||||
if (resource != null) {
|
if (resource != null) {
|
||||||
LottieCompositionFactory.fromRawRes(requireContext(), resource).addListener { comp ->
|
LottieCompositionFactory.fromRawRes(requireContext(), resource).addListener { comp ->
|
||||||
comp?.let { composition ->
|
comp?.let { composition ->
|
||||||
illustrationLottie.setComposition(composition)
|
lottie.setComposition(composition)
|
||||||
illustrationLottie.visibility = View.VISIBLE
|
lottie.visibility = View.VISIBLE
|
||||||
illustrationLottie.playAnimation()
|
lottie.playAnimation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
illustrationLottie.visibility = View.INVISIBLE
|
lottie.visibility = View.INVISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewModel.udfpsEvent.collect {
|
repeatOnLifecycle(Lifecycle.State.DESTROYED) { viewModel.stopEnrollment() }
|
||||||
|
}
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewModel.accessibilityEnabled.collect { enabled ->
|
||||||
|
udfpsEnrollView.setAccessibilityEnabled(enabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewModel.enrollState.collect {
|
||||||
Log.d(TAG, "EnrollEvent $it")
|
Log.d(TAG, "EnrollEvent $it")
|
||||||
udfpsEnrollView.onUdfpsEvent(it) }
|
if (it is FingerEnrollState.EnrollError) {
|
||||||
|
try {
|
||||||
|
FingerprintErrorDialog.showInstance(it, fragment)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Log.e(TAG, "Exception occurred $exception")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
udfpsEnrollView.onUdfpsEvent(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewModel.progressSaved.collect { udfpsEnrollView.onEnrollProgressSaved(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
@@ -128,6 +163,15 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewModel.touchExplorationDebug.collect {
|
||||||
|
udfpsEnrollView.sendDebugTouchExplorationEvent(
|
||||||
|
MotionEvent.obtain(100, 100, ACTION_HOVER_MOVE, it.x.toFloat(), it.y.toFloat(), 0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
viewModel.readyForEnrollment()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun HeaderText.toResource(): Int {
|
private fun HeaderText.toResource(): Int {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
||||||
|
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
|
||||||
/** Represents the description text for UDFPS enrollment */
|
/** Represents the description text for UDFPS enrollment */
|
||||||
data class DescriptionText(
|
data class DescriptionText(
|
||||||
val isSuw: Boolean,
|
val isSuw: Boolean,
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
||||||
|
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
|
||||||
/** Represents the lottie for UDFPS enrollment */
|
/** Represents the lottie for UDFPS enrollment */
|
||||||
data class EducationAnimationModel(
|
data class EducationAnimationModel(
|
||||||
val isSuw: Boolean,
|
val isSuw: Boolean,
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
||||||
|
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
|
||||||
/** Represents the header text for UDFPS enrollment */
|
/** Represents the header text for UDFPS enrollment */
|
||||||
data class HeaderText(
|
data class HeaderText(
|
||||||
val isSuw: Boolean,
|
val isSuw: Boolean,
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2024 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
|
||||||
|
|
||||||
/** A class indicating a udfps enroll event occurred. */
|
|
||||||
sealed class UdfpsEnrollEvent
|
|
||||||
|
|
||||||
/** Describes how many [remainingSteps] and how many [totalSteps] are left in udfps enrollment. */
|
|
||||||
data class UdfpsProgress(val remainingSteps: Int, val totalSteps: Int) : UdfpsEnrollEvent()
|
|
||||||
|
|
||||||
/** Indicates a help event has been sent by enrollment */
|
|
||||||
data class UdfpsHelp(val helpMsgId: Int, val helpString: String) : UdfpsEnrollEvent()
|
|
||||||
|
|
||||||
/** Indicates a error event has been sent by enrollment */
|
|
||||||
data class UdfpsError(val errMsgId: Int, val errString: String) : UdfpsEnrollEvent()
|
|
||||||
|
|
||||||
/** Indicates an acquired event has occurred */
|
|
||||||
data class Acquired(val acquiredGood: Boolean) : UdfpsEnrollEvent()
|
|
||||||
|
|
||||||
/** Indicates a pointer down event has occurred */
|
|
||||||
data object PointerDown : UdfpsEnrollEvent()
|
|
||||||
|
|
||||||
/** Indicates a pointer up event has occurred */
|
|
||||||
data object PointerUp : UdfpsEnrollEvent()
|
|
||||||
|
|
||||||
/** Indicates the overlay has shown */
|
|
||||||
data object OverlayShown : UdfpsEnrollEvent()
|
|
||||||
@@ -16,71 +16,172 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
||||||
|
|
||||||
import android.graphics.Rect
|
import android.graphics.Point
|
||||||
|
import android.view.Surface
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
||||||
|
import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintVibrationEffects
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
||||||
import kotlinx.coroutines.Dispatchers
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.combineTransform
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.filterIsInstance
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.shareIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */
|
/** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */
|
||||||
class UdfpsViewModel() : ViewModel() {
|
class UdfpsViewModel(
|
||||||
|
val vibrationInteractor: VibrationInteractor,
|
||||||
|
displayDensityInteractor: DisplayDensityInteractor,
|
||||||
|
val navigationViewModel: FingerprintNavigationViewModel,
|
||||||
|
debuggingInteractor: DebuggingInteractor,
|
||||||
|
val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||||
|
simulatedTouchEventsDebugRepository: SimulatedTouchEventsRepository,
|
||||||
|
enrollStageInteractor: EnrollStageInteractor,
|
||||||
|
orientationInteractor: OrientationInteractor,
|
||||||
|
backgroundViewModel: BackgroundViewModel,
|
||||||
|
sensorRepository: FingerprintSensorRepository,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
private val isSetupWizard = flowOf(false)
|
private val isSetupWizard = flowOf(false)
|
||||||
|
|
||||||
/** Indicates which Enrollment stage we are currently in. */
|
private var _enrollState: Flow<FingerEnrollState?> =
|
||||||
private val sensorLocationInternal = Pair(540, 1713)
|
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||||
private val sensorRadius = 100
|
/** The current state of the enrollment. */
|
||||||
private val sensorRect =
|
var enrollState: Flow<FingerEnrollState> =
|
||||||
Rect(
|
combine(fingerprintEnrollEnrollingViewModel.enrollFlowShouldBeRunning, _enrollState) {
|
||||||
this.sensorLocationInternal.first - sensorRadius,
|
shouldBeRunning,
|
||||||
this.sensorLocationInternal.second - sensorRadius,
|
state ->
|
||||||
this.sensorLocationInternal.first + sensorRadius,
|
if (shouldBeRunning) {
|
||||||
this.sensorLocationInternal.second + sensorRadius,
|
state
|
||||||
)
|
|
||||||
|
|
||||||
private val stageThresholds = flowOf(listOf(.25, .5, .75, .875))
|
|
||||||
|
|
||||||
/** Indicates if accessibility is enabled */
|
|
||||||
val accessibilityEnabled = flowOf(false)
|
|
||||||
|
|
||||||
/** Indicates the locates of the fingerprint sensor. */
|
|
||||||
val sensorLocation: Flow<Rect> = flowOf(sensorRect)
|
|
||||||
|
|
||||||
/** This is currently not hooked up to fingerprint manager, and is being fed mock events. */
|
|
||||||
val udfpsEvent: Flow<UdfpsEnrollEvent> =
|
|
||||||
flow {
|
|
||||||
enrollEvents.forEach { events ->
|
|
||||||
events.forEach { event -> emit(event) }
|
|
||||||
delay(1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
/** Determines the current [StageViewModel] enrollment is in */
|
|
||||||
val enrollStage: Flow<StageViewModel> =
|
|
||||||
combine(stageThresholds, udfpsEvent) { thresholds, event ->
|
|
||||||
if (event is UdfpsProgress) {
|
|
||||||
thresholdToStageMap(thresholds, event.totalSteps - event.remainingSteps, event.totalSteps)
|
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forwards the property sensor information. This is typically used to recreate views that must be
|
||||||
|
* aligned with the sensor.
|
||||||
|
*/
|
||||||
|
val sensorLocation = sensorRepository.fingerprintSensor
|
||||||
|
|
||||||
|
/** Indicates if accessibility is enabled */
|
||||||
|
val accessibilityEnabled = flowOf(true).shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
enrollState
|
||||||
|
.combine(accessibilityEnabled) { event, isEnabled -> Pair(event, isEnabled) }
|
||||||
|
.collect {
|
||||||
|
if (
|
||||||
|
when (it.first) {
|
||||||
|
is FingerEnrollState.EnrollError -> true
|
||||||
|
is FingerEnrollState.EnrollHelp -> it.second
|
||||||
|
is FingerEnrollState.EnrollProgress -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
vibrate(it.first)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
backgroundViewModel.background.filter { it }.collect { didGoToBackground() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the saved progress, this is for when views are recreated and need saved state for the
|
||||||
|
* first time.
|
||||||
|
*/
|
||||||
|
var progressSaved: Flow<FingerEnrollState.EnrollProgress> =
|
||||||
|
enrollState
|
||||||
|
.filterIsInstance<FingerEnrollState.EnrollProgress>()
|
||||||
|
.filterNotNull()
|
||||||
|
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
|
/** This sends touch exploration events only used for debugging purposes. */
|
||||||
|
val touchExplorationDebug: Flow<Point> =
|
||||||
|
debuggingInteractor.debuggingEnabled.combineTransform(
|
||||||
|
simulatedTouchEventsDebugRepository.touchExplorationDebug
|
||||||
|
) { enabled, point ->
|
||||||
|
if (enabled) {
|
||||||
|
emit(point)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Determines the current [StageViewModel] enrollment is in */
|
||||||
|
val enrollStage: Flow<StageViewModel> =
|
||||||
|
combine(enrollStageInteractor.enrollStageThresholds, enrollState) { thresholds, event ->
|
||||||
|
if (event is FingerEnrollState.EnrollProgress) {
|
||||||
|
val progress =
|
||||||
|
(event.totalStepsRequired - event.remainingSteps).toFloat() / event.totalStepsRequired
|
||||||
|
var stageToReturn: StageViewModel = StageViewModel.Center
|
||||||
|
thresholds.forEach { (threshold, stage) ->
|
||||||
|
if (progress < threshold) {
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
stageToReturn = stage
|
||||||
|
}
|
||||||
|
stageToReturn
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filterNotNull()
|
||||||
|
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
|
/** Indicates if we should show the lottie. */
|
||||||
|
val shouldShowLottie: Flow<Boolean> =
|
||||||
|
combine(
|
||||||
|
displayDensityInteractor.displayDensity,
|
||||||
|
displayDensityInteractor.defaultDisplayDensity,
|
||||||
|
displayDensityInteractor.fontScale,
|
||||||
|
orientationInteractor.rotation,
|
||||||
|
) { currDisplayDensity, defaultDisplayDensity, fontScale, rotation ->
|
||||||
|
val canShowLottieForRotation =
|
||||||
|
when (rotation) {
|
||||||
|
Surface.ROTATION_0 -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
canShowLottieForRotation &&
|
||||||
|
if (fontScale > 1.0f) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
defaultDisplayDensity == currDisplayDensity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||||
|
|
||||||
/** The header text for UDFPS enrollment */
|
/** The header text for UDFPS enrollment */
|
||||||
val headerText: Flow<HeaderText> =
|
val headerText: Flow<HeaderText> =
|
||||||
combine(isSetupWizard, accessibilityEnabled, enrollStage) { isSuw, isAccessibility, stage ->
|
combine(isSetupWizard, accessibilityEnabled, enrollStage) { isSuw, isAccessibility, stage ->
|
||||||
return@combine HeaderText(isSuw, isAccessibility, stage)
|
return@combine HeaderText(isSuw, isAccessibility, stage)
|
||||||
}
|
}
|
||||||
|
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
private val shouldClearDescriptionText = enrollStage.map { it is StageViewModel.Unknown }
|
private val shouldClearDescriptionText = enrollStage.map { it is StageViewModel.Unknown }
|
||||||
|
|
||||||
@@ -97,86 +198,102 @@ class UdfpsViewModel() : ViewModel() {
|
|||||||
return@combine DescriptionText(isSuw, isAccessibility, stage)
|
return@combine DescriptionText(isSuw, isAccessibility, stage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
|
/** Indicates if the consumer is ready for enrollment */
|
||||||
|
fun readyForEnrollment() {
|
||||||
|
fingerprintEnrollEnrollingViewModel.canEnroll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates if enrollment should stop */
|
||||||
|
fun stopEnrollment() {
|
||||||
|
fingerprintEnrollEnrollingViewModel.stopEnroll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates the negative button has been clicked */
|
||||||
|
fun negativeButtonClicked() {
|
||||||
|
doReset()
|
||||||
|
navigationViewModel.update(
|
||||||
|
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
|
||||||
|
navStep,
|
||||||
|
"$TAG#negativeButtonClicked",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates that an enrollment was completed */
|
||||||
|
fun finishedSuccessfully() {
|
||||||
|
doReset()
|
||||||
|
navigationViewModel.update(FingerprintAction.NEXT, navStep, "${TAG}#progressFinished")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates that the application went to the background. */
|
||||||
|
private fun didGoToBackground() {
|
||||||
|
navigationViewModel.update(
|
||||||
|
FingerprintAction.DID_GO_TO_BACKGROUND,
|
||||||
|
navStep,
|
||||||
|
"$TAG#didGoToBackground",
|
||||||
|
)
|
||||||
|
stopEnrollment()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doReset() {
|
||||||
|
/** Indicates if the icon should be animating or not */
|
||||||
|
_enrollState = fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||||
|
}
|
||||||
|
|
||||||
/** The lottie that should be shown for UDFPS Enrollment */
|
/** The lottie that should be shown for UDFPS Enrollment */
|
||||||
val lottie: Flow<EducationAnimationModel> =
|
val lottie: Flow<EducationAnimationModel> =
|
||||||
combine(isSetupWizard, accessibilityEnabled, enrollStage) { isSuw, isAccessibility, stage ->
|
combine(isSetupWizard, accessibilityEnabled, enrollStage) { isSuw, isAccessibility, stage ->
|
||||||
return@combine EducationAnimationModel(isSuw, isAccessibility, stage)
|
return@combine EducationAnimationModel(isSuw, isAccessibility, stage)
|
||||||
}.distinctUntilChanged()
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
class UdfpsEnrollmentFactory() : ViewModelProvider.Factory {
|
/** Indicates we should send a vibration event */
|
||||||
|
private fun vibrate(event: FingerEnrollState) {
|
||||||
|
val vibrationEvent =
|
||||||
|
when (event) {
|
||||||
|
is FingerEnrollState.EnrollError -> FingerprintVibrationEffects.UdfpsError
|
||||||
|
is FingerEnrollState.EnrollHelp -> FingerprintVibrationEffects.UdfpsHelp
|
||||||
|
is FingerEnrollState.EnrollProgress -> FingerprintVibrationEffects.UdfpsSuccess
|
||||||
|
else -> FingerprintVibrationEffects.UdfpsError
|
||||||
|
}
|
||||||
|
vibrationInteractor.vibrate(vibrationEvent, "UdfpsEnrollFragment")
|
||||||
|
}
|
||||||
|
|
||||||
|
class UdfpsEnrollmentFactory(
|
||||||
|
private val vibrationInteractor: VibrationInteractor,
|
||||||
|
private val displayDensityInteractor: DisplayDensityInteractor,
|
||||||
|
private val navigationViewModel: FingerprintNavigationViewModel,
|
||||||
|
private val debuggingInteractor: DebuggingInteractor,
|
||||||
|
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||||
|
private val simulatedTouchEventsRepository: SimulatedTouchEventsRepository,
|
||||||
|
private val enrollStageInteractor: EnrollStageInteractor,
|
||||||
|
private val orientationInteractor: OrientationInteractor,
|
||||||
|
private val backgroundViewModel: BackgroundViewModel,
|
||||||
|
private val sensorRepository: FingerprintSensorRepository,
|
||||||
|
) : ViewModelProvider.Factory {
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
return UdfpsViewModel() as T
|
return UdfpsViewModel(
|
||||||
|
vibrationInteractor,
|
||||||
|
displayDensityInteractor,
|
||||||
|
navigationViewModel,
|
||||||
|
debuggingInteractor,
|
||||||
|
fingerprintEnrollEnrollingViewModel,
|
||||||
|
simulatedTouchEventsRepository,
|
||||||
|
enrollStageInteractor,
|
||||||
|
orientationInteractor,
|
||||||
|
backgroundViewModel,
|
||||||
|
sensorRepository,
|
||||||
|
)
|
||||||
|
as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val navStep = FingerprintNavigationStep.Enrollment::class
|
private val navStep = FingerprintNavigationStep.Enrollment::class
|
||||||
private const val TAG = "UDFPSViewModel"
|
private const val TAG = "UDFPSViewModel"
|
||||||
private val ENROLLMENT_STAGES_ORDERED =
|
|
||||||
listOf(
|
|
||||||
StageViewModel.Center,
|
|
||||||
StageViewModel.Guided,
|
|
||||||
StageViewModel.Fingertip,
|
|
||||||
StageViewModel.LeftEdge,
|
|
||||||
StageViewModel.RightEdge,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [thresholds] is a list of 4 numbers from [0,1] that separate enrollment into 5 stages. The
|
|
||||||
* stage is determined by mapping [thresholds] * [maxSteps] and finding where the [currentStep]
|
|
||||||
* is.
|
|
||||||
*
|
|
||||||
* Each number in the array should be strictly increasing such as [0.2, 0.5, 0.6, 0.8]
|
|
||||||
*/
|
|
||||||
private fun thresholdToStageMap(
|
|
||||||
thresholds: List<Double>,
|
|
||||||
currentStep: Int,
|
|
||||||
maxSteps: Int,
|
|
||||||
): StageViewModel {
|
|
||||||
val stageIterator = ENROLLMENT_STAGES_ORDERED.iterator()
|
|
||||||
thresholds.forEach {
|
|
||||||
val thresholdLimit = it * maxSteps
|
|
||||||
val curr = stageIterator.next()
|
|
||||||
if (currentStep < thresholdLimit) {
|
|
||||||
return curr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stageIterator.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This will be removed */
|
|
||||||
private val enrollEvents: List<List<UdfpsEnrollEvent>> =
|
|
||||||
listOf(
|
|
||||||
listOf(OverlayShown),
|
|
||||||
listOf(UdfpsHelp(1,"hi")),
|
|
||||||
listOf(UdfpsHelp(1,"hi")),
|
|
||||||
CreateProgress(15, 16),
|
|
||||||
listOf(UdfpsHelp(1,"hi")),
|
|
||||||
CreateProgress(14, 16),
|
|
||||||
listOf(PointerDown, UdfpsHelp(1,"hi"), PointerUp),
|
|
||||||
listOf(PointerDown, UdfpsHelp(1,"hi"), PointerUp),
|
|
||||||
CreateProgress(13, 16),
|
|
||||||
CreateProgress(12, 16),
|
|
||||||
CreateProgress(11, 16),
|
|
||||||
CreateProgress(10, 16),
|
|
||||||
CreateProgress(9, 16),
|
|
||||||
CreateProgress(8, 16),
|
|
||||||
CreateProgress(7, 16),
|
|
||||||
CreateProgress(6, 16),
|
|
||||||
CreateProgress(5, 16),
|
|
||||||
CreateProgress(4, 16),
|
|
||||||
CreateProgress(3, 16),
|
|
||||||
CreateProgress(2, 16),
|
|
||||||
CreateProgress(1, 16),
|
|
||||||
CreateProgress(0, 16),
|
|
||||||
)
|
|
||||||
|
|
||||||
/** This will be removed */
|
|
||||||
private fun CreateProgress(remaining: Int, total: Int): List<UdfpsEnrollEvent> {
|
|
||||||
return listOf(PointerDown, Acquired(true), UdfpsProgress(remaining, total), PointerUp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import android.content.Context
|
|||||||
import android.graphics.PointF
|
import android.graphics.PointF
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.accessibility.AccessibilityManager
|
import android.view.accessibility.AccessibilityManager
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
|
|
||||||
/** Keeps track of which guided enrollment point we should be using */
|
/** Keeps track of which guided enrollment point we should be using */
|
||||||
class UdfpsEnrollHelperV2(private val mContext: Context) {
|
class UdfpsEnrollHelperV2(private val mContext: Context) {
|
||||||
@@ -28,6 +28,7 @@ class UdfpsEnrollHelperV2(private val mContext: Context) {
|
|||||||
private var isGuidedEnrollment: Boolean = false
|
private var isGuidedEnrollment: Boolean = false
|
||||||
private val accessibilityEnabled: Boolean
|
private val accessibilityEnabled: Boolean
|
||||||
private val guidedEnrollmentPoints: MutableList<PointF>
|
private val guidedEnrollmentPoints: MutableList<PointF>
|
||||||
|
/** The current index of [guidedEnrollmentPoints] for the guided enrollment. */
|
||||||
private var index = 0
|
private var index = 0
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -76,7 +77,7 @@ class UdfpsEnrollHelperV2(private val mContext: Context) {
|
|||||||
if (accessibilityEnabled || !isGuidedEnrollment) {
|
if (accessibilityEnabled || !isGuidedEnrollment) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
var scale = SCALE
|
val scale = SCALE
|
||||||
val originalPoint = guidedEnrollmentPoints[index % guidedEnrollmentPoints.size]
|
val originalPoint = guidedEnrollmentPoints[index % guidedEnrollmentPoints.size]
|
||||||
return PointF(originalPoint.x * scale, originalPoint.y * scale)
|
return PointF(originalPoint.x * scale, originalPoint.y * scale)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import androidx.core.animation.addListener
|
|||||||
import androidx.core.graphics.toRect
|
import androidx.core.graphics.toRect
|
||||||
import androidx.core.graphics.toRectF
|
import androidx.core.graphics.toRectF
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,6 +45,7 @@ import kotlin.math.sin
|
|||||||
* various stages of enrollment
|
* various stages of enrollment
|
||||||
*/
|
*/
|
||||||
class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeSet?) : Drawable() {
|
class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeSet?) : Drawable() {
|
||||||
|
private var targetAnimationDuration: Long = TARGET_ANIM_DURATION_LONG
|
||||||
private var targetAnimatorSet: AnimatorSet? = null
|
private var targetAnimatorSet: AnimatorSet? = null
|
||||||
private val movingTargetFpIcon: Drawable
|
private val movingTargetFpIcon: Drawable
|
||||||
private val fingerprintDrawable: ShapeDrawable
|
private val fingerprintDrawable: ShapeDrawable
|
||||||
@@ -88,19 +89,22 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
it.recycle()
|
it.recycle()
|
||||||
}
|
}
|
||||||
|
|
||||||
sensorOutlinePaint = Paint(0 /* flags */).apply {
|
sensorOutlinePaint =
|
||||||
|
Paint(0 /* flags */).apply {
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
setColor(movingTargetFill)
|
setColor(movingTargetFill)
|
||||||
style = Paint.Style.FILL
|
style = Paint.Style.FILL
|
||||||
}
|
}
|
||||||
|
|
||||||
blueFill = Paint(0 /* flags */).apply {
|
blueFill =
|
||||||
|
Paint(0 /* flags */).apply {
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
setColor(movingTargetFill)
|
setColor(movingTargetFill)
|
||||||
style = Paint.Style.FILL
|
style = Paint.Style.FILL
|
||||||
}
|
}
|
||||||
|
|
||||||
movingTargetFpIcon = context.resources.getDrawable(R.drawable.ic_enrollment_fingerprint, null).apply {
|
movingTargetFpIcon =
|
||||||
|
context.resources.getDrawable(R.drawable.ic_enrollment_fingerprint, null).apply {
|
||||||
setTint(enrollIconColor)
|
setTint(enrollIconColor)
|
||||||
mutate()
|
mutate()
|
||||||
}
|
}
|
||||||
@@ -140,7 +144,16 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Update the progress of the icon */
|
/** Update the progress of the icon */
|
||||||
fun onEnrollmentProgress(remaining: Int, totalSteps: Int) {
|
fun onEnrollmentProgress(remaining: Int, totalSteps: Int, isRecreating: Boolean = false) {
|
||||||
|
restoreAnimationTime()
|
||||||
|
// If we are restoring this view from a saved state, set animation duration to 0 to avoid
|
||||||
|
// animating progress that has already occurred.
|
||||||
|
if (isRecreating) {
|
||||||
|
setAnimationTimeToZero()
|
||||||
|
} else {
|
||||||
|
restoreAnimationTime()
|
||||||
|
}
|
||||||
|
|
||||||
helper.onEnrollmentProgress(remaining, totalSteps)
|
helper.onEnrollmentProgress(remaining, totalSteps)
|
||||||
val offset = helper.guidedEnrollmentLocation
|
val offset = helper.guidedEnrollmentLocation
|
||||||
val currentBounds = getCurrLocation().toRect()
|
val currentBounds = getCurrLocation().toRect()
|
||||||
@@ -149,10 +162,10 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
// offsets the initial sensor rect by a bit to get the user to move their finger a bit more.
|
// offsets the initial sensor rect by a bit to get the user to move their finger a bit more.
|
||||||
val targetRect = Rect(sensorRectBounds).toRectF()
|
val targetRect = Rect(sensorRectBounds).toRectF()
|
||||||
targetRect.offset(offset.x, offset.y)
|
targetRect.offset(offset.x, offset.y)
|
||||||
var shouldAnimateMovement =
|
val shouldAnimateMovement =
|
||||||
!currentBounds.equals(targetRect) && offset.x != 0f && offset.y != 0f
|
!currentBounds.equals(targetRect) && offset.x != 0f && offset.y != 0f
|
||||||
if (shouldAnimateMovement) {
|
if (shouldAnimateMovement) {
|
||||||
targetAnimatorSet?.let { it.cancel() }
|
targetAnimatorSet?.cancel()
|
||||||
animateMovement(currentBounds, targetRect, true)
|
animateMovement(currentBounds, targetRect, true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -186,7 +199,7 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
val currLocation = getCurrLocation()
|
val currLocation = getCurrLocation()
|
||||||
canvas.scale(currentScale, currentScale, currLocation.centerX(), currLocation.centerY())
|
canvas.scale(currentScale, currentScale, currLocation.centerX(), currLocation.centerY())
|
||||||
|
|
||||||
sensorRectBounds?.let { canvas.drawOval(currLocation, sensorOutlinePaint) }
|
canvas.drawOval(currLocation, sensorOutlinePaint)
|
||||||
fingerprintDrawable.bounds = currLocation.toRect()
|
fingerprintDrawable.bounds = currLocation.toRect()
|
||||||
fingerprintDrawable.draw(canvas)
|
fingerprintDrawable.draw(canvas)
|
||||||
}
|
}
|
||||||
@@ -234,6 +247,19 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sets animation time to 0. This typically happens after an activity recreation, we don't
|
||||||
|
* want to re-animate the progress/success animation with the default timer
|
||||||
|
*/
|
||||||
|
private fun setAnimationTimeToZero() {
|
||||||
|
targetAnimationDuration = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This sets animation timers back to normal, this happens after we have */
|
||||||
|
private fun restoreAnimationTime() {
|
||||||
|
targetAnimationDuration = TARGET_ANIM_DURATION_LONG
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "UdfpsEnrollDrawableV2"
|
private const val TAG = "UdfpsEnrollDrawableV2"
|
||||||
private const val DEFAULT_STROKE_WIDTH = 3f
|
private const val DEFAULT_STROKE_WIDTH = 3f
|
||||||
@@ -242,7 +268,8 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
|||||||
|
|
||||||
private fun createUdfpsIcon(context: Context): ShapeDrawable {
|
private fun createUdfpsIcon(context: Context): ShapeDrawable {
|
||||||
val fpPath = context.resources.getString(R.string.config_udfpsIcon)
|
val fpPath = context.resources.getString(R.string.config_udfpsIcon)
|
||||||
val drawable = ShapeDrawable(PathShape(PathParser.createPathFromPathData(fpPath), 72f, 72f)).apply {
|
val drawable =
|
||||||
|
ShapeDrawable(PathShape(PathParser.createPathFromPathData(fpPath), 72f, 72f)).apply {
|
||||||
mutate()
|
mutate()
|
||||||
paint.style = Paint.Style.STROKE
|
paint.style = Paint.Style.STROKE
|
||||||
paint.strokeCap = Paint.Cap.ROUND
|
paint.strokeCap = Paint.Cap.ROUND
|
||||||
|
|||||||
@@ -25,28 +25,28 @@ import android.graphics.Paint
|
|||||||
import android.graphics.PixelFormat
|
import android.graphics.PixelFormat
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Process
|
|
||||||
import android.os.VibrationAttributes
|
|
||||||
import android.os.VibrationEffect
|
|
||||||
import android.os.Vibrator
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.view.animation.DecelerateInterpolator
|
import android.view.animation.DecelerateInterpolator
|
||||||
import android.view.animation.Interpolator
|
import android.view.animation.Interpolator
|
||||||
|
import android.view.animation.OvershootInterpolator
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.core.animation.doOnEnd
|
import androidx.core.animation.doOnEnd
|
||||||
import androidx.core.graphics.toRectF
|
import androidx.core.graphics.toRectF
|
||||||
import com.android.internal.annotations.VisibleForTesting
|
import com.android.internal.annotations.VisibleForTesting
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import kotlin.math.cos
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
import kotlin.math.sin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UDFPS enrollment progress bar. This view is responsible for drawing the progress ring and its
|
* UDFPS enrollment progress bar. This view is responsible for drawing the progress ring and its
|
||||||
* fill around the center of the UDFPS sensor.
|
* fill around the center of the UDFPS sensor.
|
||||||
*/
|
*/
|
||||||
class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: AttributeSet?) :
|
class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: AttributeSet?) :
|
||||||
Drawable() {
|
Drawable() {
|
||||||
private val sensorRect: Rect = Rect()
|
private val sensorRect: Rect = Rect()
|
||||||
|
private var rotation: Int = 0
|
||||||
private val strokeWidthPx: Float
|
private val strokeWidthPx: Float
|
||||||
|
|
||||||
@ColorInt private val progressColor: Int
|
@ColorInt private val progressColor: Int
|
||||||
@@ -56,7 +56,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
private val backgroundPaint: Paint
|
private val backgroundPaint: Paint
|
||||||
|
|
||||||
@VisibleForTesting val fillPaint: Paint
|
@VisibleForTesting val fillPaint: Paint
|
||||||
private val vibrator: Vibrator
|
|
||||||
private var isAccessibilityEnabled: Boolean = false
|
private var isAccessibilityEnabled: Boolean = false
|
||||||
private var afterFirstTouch = false
|
private var afterFirstTouch = false
|
||||||
private var remainingSteps = 0
|
private var remainingSteps = 0
|
||||||
@@ -64,22 +63,27 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
private var progress = 0f
|
private var progress = 0f
|
||||||
private var progressAnimator: ValueAnimator? = null
|
private var progressAnimator: ValueAnimator? = null
|
||||||
private val progressUpdateListener: AnimatorUpdateListener
|
private val progressUpdateListener: AnimatorUpdateListener
|
||||||
private var showingHelp = false
|
|
||||||
private var fillColorAnimator: ValueAnimator? = null
|
private var fillColorAnimator: ValueAnimator? = null
|
||||||
private val fillColorUpdateListener: AnimatorUpdateListener
|
private val fillColorUpdateListener: AnimatorUpdateListener
|
||||||
private var backgroundColorAnimator: ValueAnimator? = null
|
private var backgroundColorAnimator: ValueAnimator? = null
|
||||||
private val backgroundColorUpdateListener: AnimatorUpdateListener
|
private val backgroundColorUpdateListener: AnimatorUpdateListener
|
||||||
private var complete = false
|
|
||||||
private var movingTargetFill = 0
|
private var movingTargetFill = 0
|
||||||
private var movingTargetFillError = 0
|
private var movingTargetFillError = 0
|
||||||
private var enrollProgressColor = 0
|
private var enrollProgressColor = 0
|
||||||
private var enrollProgressHelp = 0
|
private var enrollProgressHelp = 0
|
||||||
private var enrollProgressHelpWithTalkback = 0
|
private var enrollProgressHelpWithTalkback = 0
|
||||||
private val progressBarRadius: Int
|
private val progressBarRadius: Int
|
||||||
|
private var checkMarkDrawable: Drawable
|
||||||
|
private var checkMarkAnimator: ValueAnimator? = null
|
||||||
|
|
||||||
|
private var fillColorAnimationDuration = FILL_COLOR_ANIMATION_DURATION_MS
|
||||||
|
private var animateArcDuration = PROGRESS_ANIMATION_DURATION_MS
|
||||||
|
private var checkmarkAnimationDelayDuration = CHECKMARK_ANIMATION_DELAY_MS
|
||||||
|
private var checkmarkAnimationDuration = CHECKMARK_ANIMATION_DURATION_MS
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val ta =
|
val ta =
|
||||||
mContext.obtainStyledAttributes(
|
context.obtainStyledAttributes(
|
||||||
attrs,
|
attrs,
|
||||||
R.styleable.BiometricsEnrollView,
|
R.styleable.BiometricsEnrollView,
|
||||||
R.attr.biometricsEnrollStyle,
|
R.attr.biometricsEnrollStyle,
|
||||||
@@ -94,12 +98,13 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
enrollProgressHelpWithTalkback =
|
enrollProgressHelpWithTalkback =
|
||||||
ta.getColor(R.styleable.BiometricsEnrollView_biometricsEnrollProgressHelpWithTalkback, 0)
|
ta.getColor(R.styleable.BiometricsEnrollView_biometricsEnrollProgressHelpWithTalkback, 0)
|
||||||
ta.recycle()
|
ta.recycle()
|
||||||
val density = mContext.resources.displayMetrics.densityDpi.toFloat()
|
val density = context.resources.displayMetrics.densityDpi.toFloat()
|
||||||
strokeWidthPx = STROKE_WIDTH_DP * (density / DisplayMetrics.DENSITY_DEFAULT)
|
strokeWidthPx = STROKE_WIDTH_DP * (density / DisplayMetrics.DENSITY_DEFAULT)
|
||||||
progressColor = enrollProgressColor
|
progressColor = enrollProgressColor
|
||||||
onFirstBucketFailedColor = movingTargetFillError
|
onFirstBucketFailedColor = movingTargetFillError
|
||||||
updateHelpColor()
|
updateHelpColor()
|
||||||
backgroundPaint = Paint().apply {
|
backgroundPaint =
|
||||||
|
Paint().apply {
|
||||||
strokeWidth = strokeWidthPx
|
strokeWidth = strokeWidthPx
|
||||||
setColor(movingTargetFill)
|
setColor(movingTargetFill)
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
@@ -107,17 +112,19 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
strokeCap = Paint.Cap.ROUND
|
strokeCap = Paint.Cap.ROUND
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkMarkDrawable = context.getDrawable(R.drawable.udfps_enroll_checkmark)!!
|
||||||
|
|
||||||
// Progress fill should *not* use the extracted system color.
|
// Progress fill should *not* use the extracted system color.
|
||||||
fillPaint = Paint().apply {
|
fillPaint =
|
||||||
|
Paint().apply {
|
||||||
strokeWidth = strokeWidthPx
|
strokeWidth = strokeWidthPx
|
||||||
setColor(progressColor)
|
setColor(progressColor)
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
style = Paint.Style.STROKE
|
style = Paint.Style.STROKE
|
||||||
strokeCap = Paint.Cap.ROUND
|
strokeCap = Paint.Cap.ROUND
|
||||||
}
|
}
|
||||||
vibrator = mContext.getSystemService(Vibrator::class.java)!!
|
|
||||||
|
|
||||||
progressBarRadius = mContext.resources.getInteger(R.integer.config_udfpsEnrollProgressBar)
|
progressBarRadius = context.resources.getInteger(R.integer.config_udfpsEnrollProgressBar)
|
||||||
|
|
||||||
progressUpdateListener = AnimatorUpdateListener { animation: ValueAnimator ->
|
progressUpdateListener = AnimatorUpdateListener { animation: ValueAnimator ->
|
||||||
progress = animation.getAnimatedValue() as Float
|
progress = animation.getAnimatedValue() as Float
|
||||||
@@ -134,9 +141,10 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Indicates enrollment progress has occurred. */
|
/** Indicates enrollment progress has occurred. */
|
||||||
fun onEnrollmentProgress(remaining: Int, totalSteps: Int) {
|
fun onEnrollmentProgress(remaining: Int, totalSteps: Int, isRecreating: Boolean = false) {
|
||||||
|
|
||||||
afterFirstTouch = true
|
afterFirstTouch = true
|
||||||
updateProgress(remaining, totalSteps)
|
updateProgress(remaining, totalSteps, isRecreating)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Indicates enrollment help has occurred. */
|
/** Indicates enrollment help has occurred. */
|
||||||
@@ -157,18 +165,12 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
|
|
||||||
canvas.save()
|
canvas.save()
|
||||||
// This takes the sensors bounding box and expands it by [progressBarRadius] in all directions
|
// This takes the sensors bounding box and expands it by [progressBarRadius] in all directions
|
||||||
val sensorProgressRect = Rect(sensorRect)
|
val sensorProgressRect = getSensorProgressRect()
|
||||||
sensorProgressRect.inset(
|
|
||||||
-progressBarRadius,
|
|
||||||
-progressBarRadius,
|
|
||||||
-progressBarRadius,
|
|
||||||
-progressBarRadius,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Rotate -90 degrees to make the progress start from the top right and not the bottom
|
// Rotate -90 degrees to make the progress start from the top right and not the bottom
|
||||||
// right
|
// right
|
||||||
canvas.rotate(
|
canvas.rotate(
|
||||||
-90f,
|
rotation - 90f,
|
||||||
sensorProgressRect.centerX().toFloat(),
|
sensorProgressRect.centerX().toFloat(),
|
||||||
sensorProgressRect.centerY().toFloat(),
|
sensorProgressRect.centerY().toFloat(),
|
||||||
)
|
)
|
||||||
@@ -176,9 +178,9 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
// Draw the background color of the progress circle.
|
// Draw the background color of the progress circle.
|
||||||
canvas.drawArc(
|
canvas.drawArc(
|
||||||
sensorProgressRect.toRectF(),
|
sensorProgressRect.toRectF(),
|
||||||
0f /* startAngle */,
|
0f, /* startAngle */
|
||||||
360f /* sweepAngle */,
|
360f, /* sweepAngle */
|
||||||
false /* useCenter */,
|
false, /* useCenter */
|
||||||
backgroundPaint,
|
backgroundPaint,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -186,13 +188,15 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
// Draw the filled portion of the progress circle.
|
// Draw the filled portion of the progress circle.
|
||||||
canvas.drawArc(
|
canvas.drawArc(
|
||||||
sensorProgressRect.toRectF(),
|
sensorProgressRect.toRectF(),
|
||||||
0f /* startAngle */,
|
0f, /* startAngle */
|
||||||
360f * progress /* sweepAngle */,
|
360f * progress, /* sweepAngle */
|
||||||
false /* useCenter */,
|
false, /* useCenter */
|
||||||
fillPaint,
|
fillPaint,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.restore()
|
canvas.restore()
|
||||||
|
checkMarkDrawable.draw(canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Do nothing here, we will control the alpha internally. */
|
/** Do nothing here, we will control the alpha internally. */
|
||||||
@@ -211,6 +215,7 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
*/
|
*/
|
||||||
fun drawProgressAt(sensorRect: Rect) {
|
fun drawProgressAt(sensorRect: Rect) {
|
||||||
this.sensorRect.set(sensorRect)
|
this.sensorRect.set(sensorRect)
|
||||||
|
invalidateSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Indicates if accessibility is enabled or not. */
|
/** Indicates if accessibility is enabled or not. */
|
||||||
@@ -228,47 +233,21 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateProgress(remainingSteps: Int, totalSteps: Int) {
|
private fun updateProgress(remainingSteps: Int, totalSteps: Int, isRecreating: Boolean) {
|
||||||
if (this.remainingSteps == remainingSteps && this.totalSteps == totalSteps) {
|
if (this.remainingSteps == remainingSteps && this.totalSteps == totalSteps) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we are restoring this view from a saved state, set animation duration to 0 to avoid
|
||||||
|
// animating progress that has already occurred.
|
||||||
|
if (isRecreating) {
|
||||||
|
setAnimationTimeToZero()
|
||||||
|
} else {
|
||||||
|
restoreAnimationTime()
|
||||||
|
}
|
||||||
|
|
||||||
this.remainingSteps = remainingSteps
|
this.remainingSteps = remainingSteps
|
||||||
this.totalSteps = totalSteps
|
this.totalSteps = totalSteps
|
||||||
if (this.showingHelp) {
|
|
||||||
if (vibrator != null && isAccessibilityEnabled) {
|
|
||||||
vibrator.vibrate(
|
|
||||||
Process.myUid(),
|
|
||||||
mContext.opPackageName,
|
|
||||||
VIBRATE_EFFECT_ERROR,
|
|
||||||
javaClass.getSimpleName() + "::onEnrollmentHelp",
|
|
||||||
FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If the first touch is an error, remainingSteps will be -1 and the callback
|
|
||||||
// doesn't come from onEnrollmentHelp. If we are in the accessibility flow,
|
|
||||||
// we still would like to vibrate.
|
|
||||||
if (vibrator != null) {
|
|
||||||
if (remainingSteps == -1 && isAccessibilityEnabled) {
|
|
||||||
vibrator.vibrate(
|
|
||||||
Process.myUid(),
|
|
||||||
mContext.opPackageName,
|
|
||||||
VIBRATE_EFFECT_ERROR,
|
|
||||||
javaClass.getSimpleName() + "::onFirstTouchError",
|
|
||||||
FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES,
|
|
||||||
)
|
|
||||||
} else if (remainingSteps != -1 && !isAccessibilityEnabled) {
|
|
||||||
vibrator.vibrate(
|
|
||||||
Process.myUid(),
|
|
||||||
mContext.opPackageName,
|
|
||||||
SUCCESS_VIBRATION_EFFECT,
|
|
||||||
javaClass.getSimpleName() + "::OnEnrollmentProgress",
|
|
||||||
HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.showingHelp = showingHelp
|
|
||||||
this.remainingSteps = remainingSteps
|
this.remainingSteps = remainingSteps
|
||||||
this.totalSteps = totalSteps
|
this.totalSteps = totalSteps
|
||||||
val targetProgress = (totalSteps - remainingSteps).toFloat().div(max(1, totalSteps))
|
val targetProgress = (totalSteps - remainingSteps).toFloat().div(max(1, totalSteps))
|
||||||
@@ -276,12 +255,69 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
if (progressAnimator != null && progressAnimator!!.isRunning) {
|
if (progressAnimator != null && progressAnimator!!.isRunning) {
|
||||||
progressAnimator!!.cancel()
|
progressAnimator!!.cancel()
|
||||||
}
|
}
|
||||||
|
/** The [progressUpdateListener] will force re-[draw]s to occur depending on the progress. */
|
||||||
progressAnimator =
|
progressAnimator =
|
||||||
ValueAnimator.ofFloat(progress, targetProgress).also {
|
ValueAnimator.ofFloat(progress, targetProgress).also {
|
||||||
it.setDuration(PROGRESS_ANIMATION_DURATION_MS)
|
it.setDuration(animateArcDuration)
|
||||||
it.addUpdateListener(progressUpdateListener)
|
it.addUpdateListener(progressUpdateListener)
|
||||||
it.start()
|
it.start()
|
||||||
}
|
}
|
||||||
|
if (remainingSteps == 0) {
|
||||||
|
runCompletionAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runCompletionAnimation() {
|
||||||
|
checkMarkAnimator?.cancel()
|
||||||
|
|
||||||
|
checkMarkAnimator = ValueAnimator.ofFloat(0f, 1f)
|
||||||
|
checkMarkAnimator?.apply {
|
||||||
|
startDelay = checkmarkAnimationDelayDuration
|
||||||
|
setDuration(checkmarkAnimationDuration)
|
||||||
|
interpolator = OvershootInterpolator()
|
||||||
|
addUpdateListener {
|
||||||
|
val newBounds = getCheckMarkStartBounds()
|
||||||
|
val scale = it.animatedFraction
|
||||||
|
newBounds.set(
|
||||||
|
newBounds.left,
|
||||||
|
newBounds.top,
|
||||||
|
(newBounds.left + (newBounds.width() * scale)).toInt(),
|
||||||
|
(newBounds.top + (newBounds.height() * scale)).toInt(),
|
||||||
|
)
|
||||||
|
checkMarkDrawable.bounds = newBounds
|
||||||
|
checkMarkDrawable.setVisible(true, false)
|
||||||
|
}
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This returns the bounds for which the checkmark drawable should be drawn at. It should be drawn
|
||||||
|
* on the arc of the progress bar at the 315 degree mark.
|
||||||
|
*/
|
||||||
|
private fun getCheckMarkStartBounds(): Rect {
|
||||||
|
val progressBounds = getSensorProgressRect()
|
||||||
|
val radius = progressBounds.width() / 2.0
|
||||||
|
|
||||||
|
var x = (cos(Math.toRadians(315.0)) * radius).toInt() + progressBounds.centerX()
|
||||||
|
// Remember to negate this value as sin(>180) will return negative value
|
||||||
|
var y = (-sin(Math.toRadians(315.0)) * radius).toInt() + progressBounds.centerY()
|
||||||
|
// Subtract height|width /2 to make sure we draw in the middle of the arc.
|
||||||
|
x -= (checkMarkDrawable.intrinsicWidth / 2.0).toInt()
|
||||||
|
y -= (checkMarkDrawable.intrinsicHeight / 2.0).toInt()
|
||||||
|
|
||||||
|
return Rect(x, y, x + checkMarkDrawable.intrinsicWidth, y + checkMarkDrawable.intrinsicHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSensorProgressRect(): Rect {
|
||||||
|
val sensorProgressRect = Rect(sensorRect)
|
||||||
|
sensorProgressRect.inset(
|
||||||
|
-progressBarRadius,
|
||||||
|
-progressBarRadius,
|
||||||
|
-progressBarRadius,
|
||||||
|
-progressBarRadius,
|
||||||
|
)
|
||||||
|
return sensorProgressRect
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -294,7 +330,7 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
}
|
}
|
||||||
backgroundColorAnimator =
|
backgroundColorAnimator =
|
||||||
ValueAnimator.ofArgb(backgroundPaint.color, onFirstBucketFailedColor).also {
|
ValueAnimator.ofArgb(backgroundPaint.color, onFirstBucketFailedColor).also {
|
||||||
it.setDuration(FILL_COLOR_ANIMATION_DURATION_MS)
|
it.setDuration(fillColorAnimationDuration)
|
||||||
it.repeatCount = 1
|
it.repeatCount = 1
|
||||||
it.repeatMode = ValueAnimator.REVERSE
|
it.repeatMode = ValueAnimator.REVERSE
|
||||||
it.interpolator = DEACCEL
|
it.interpolator = DEACCEL
|
||||||
@@ -315,7 +351,7 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
@ColorInt val targetColor = helpColor
|
@ColorInt val targetColor = helpColor
|
||||||
fillColorAnimator =
|
fillColorAnimator =
|
||||||
ValueAnimator.ofArgb(fillPaint.color, targetColor).also {
|
ValueAnimator.ofArgb(fillPaint.color, targetColor).also {
|
||||||
it.setDuration(FILL_COLOR_ANIMATION_DURATION_MS)
|
it.setDuration(fillColorAnimationDuration)
|
||||||
it.repeatCount = 1
|
it.repeatCount = 1
|
||||||
it.repeatMode = ValueAnimator.REVERSE
|
it.repeatMode = ValueAnimator.REVERSE
|
||||||
it.interpolator = DEACCEL
|
it.interpolator = DEACCEL
|
||||||
@@ -325,33 +361,32 @@ class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: Att
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startCompletionAnimation() {
|
/**
|
||||||
if (complete) {
|
* This sets animation time to 0. This typically happens after an activity recreation, we don't
|
||||||
return
|
* want to re-animate the progress/success animation with the default timer
|
||||||
}
|
*/
|
||||||
complete = true
|
private fun setAnimationTimeToZero() {
|
||||||
|
fillColorAnimationDuration = 0
|
||||||
|
animateArcDuration = 0
|
||||||
|
checkmarkAnimationDelayDuration = 0
|
||||||
|
checkmarkAnimationDuration = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rollBackCompletionAnimation() {
|
/** This sets animation timers back to normal, this happens after we have */
|
||||||
if (!complete) {
|
private fun restoreAnimationTime() {
|
||||||
return
|
fillColorAnimationDuration = FILL_COLOR_ANIMATION_DURATION_MS
|
||||||
|
animateArcDuration = PROGRESS_ANIMATION_DURATION_MS
|
||||||
|
checkmarkAnimationDelayDuration = CHECKMARK_ANIMATION_DELAY_MS
|
||||||
|
checkmarkAnimationDuration = CHECKMARK_ANIMATION_DURATION_MS
|
||||||
}
|
}
|
||||||
complete = false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadResources(context: Context, attrs: AttributeSet?) {}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "UdfpsProgressBar"
|
private const val TAG = "UdfpsProgressBar"
|
||||||
private const val FILL_COLOR_ANIMATION_DURATION_MS = 350L
|
private const val FILL_COLOR_ANIMATION_DURATION_MS = 350L
|
||||||
private const val PROGRESS_ANIMATION_DURATION_MS = 400L
|
private const val PROGRESS_ANIMATION_DURATION_MS = 400L
|
||||||
|
private const val CHECKMARK_ANIMATION_DELAY_MS = 200L
|
||||||
|
private const val CHECKMARK_ANIMATION_DURATION_MS = 300L
|
||||||
private const val STROKE_WIDTH_DP = 12f
|
private const val STROKE_WIDTH_DP = 12f
|
||||||
private val DEACCEL: Interpolator = DecelerateInterpolator()
|
private val DEACCEL: Interpolator = DecelerateInterpolator()
|
||||||
private val VIBRATE_EFFECT_ERROR = VibrationEffect.createWaveform(longArrayOf(0, 5, 55, 60), -1)
|
|
||||||
private val FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES =
|
|
||||||
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ACCESSIBILITY)
|
|
||||||
private val HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
|
|
||||||
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK)
|
|
||||||
private val SUCCESS_VIBRATION_EFFECT = VibrationEffect.get(VibrationEffect.EFFECT_CLICK)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,59 +17,99 @@
|
|||||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget
|
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Point
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.DisplayInfo
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.Surface
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.OnHoverListener
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.Acquired
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.OverlayShown
|
import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.PointerDown
|
import com.android.systemui.biometrics.UdfpsUtils
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.PointerUp
|
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel
|
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsEnrollEvent
|
import com.android.systemui.biometrics.shared.model.toInt
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsError
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsHelp
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsProgress
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View corresponding with fingerprint_v2_udfps_enroll_view.xml. This view is responsible for
|
* View corresponding with fingerprint_v2_udfps_enroll_view.xml. This view is responsible for
|
||||||
* drawing the [UdfpsEnrollIconV2] and the [UdfpsEnrollProgressBarDrawableV2].
|
* drawing the [UdfpsEnrollIconV2] and the [UdfpsEnrollProgressBarDrawableV2].
|
||||||
*/
|
*/
|
||||||
class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
|
class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
|
||||||
|
private lateinit var fingerprintSensorType: FingerprintSensorType
|
||||||
|
private var onHoverListener: OnHoverListener = OnHoverListener { _, _ -> false }
|
||||||
private var isAccessibilityEnabled: Boolean = false
|
private var isAccessibilityEnabled: Boolean = false
|
||||||
private lateinit var sensorRect: Rect
|
private lateinit var sensorRect: Rect
|
||||||
private val fingerprintIcon: UdfpsEnrollIconV2 = UdfpsEnrollIconV2(mContext, attrs)
|
private val fingerprintIcon: UdfpsEnrollIconV2 = UdfpsEnrollIconV2(mContext, attrs)
|
||||||
private val fingerprintProgressDrawable: UdfpsEnrollProgressBarDrawableV2 =
|
private val fingerprintProgressDrawable: UdfpsEnrollProgressBarDrawableV2 =
|
||||||
UdfpsEnrollProgressBarDrawableV2(mContext, attrs)
|
UdfpsEnrollProgressBarDrawableV2(mContext, attrs)
|
||||||
private var mTotalSteps = -1
|
private var remainingSteps = -1
|
||||||
private var mRemainingSteps = -1
|
private val udfpsUtils: UdfpsUtils = UdfpsUtils()
|
||||||
|
private lateinit var touchExplorationAnnouncer: TouchExplorationAnnouncer
|
||||||
|
private var isRecreating = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function computes the center (x,y) location with respect to the parent [FrameLayout] for
|
* This function computes the center (x,y) location with respect to the parent [FrameLayout] for
|
||||||
* the [UdfpsEnrollProgressBarDrawableV2]. It also computes the [Rect] with respect to the parent
|
* the [UdfpsEnrollProgressBarDrawableV2]. It also computes the [Rect] with respect to the parent
|
||||||
* [FrameLayout] for the [UdfpsEnrollIconV2].
|
* [FrameLayout] for the [UdfpsEnrollIconV2]. This function will also setup the
|
||||||
|
* [touchExplorationAnnouncer]
|
||||||
*/
|
*/
|
||||||
fun setSensorRect(rect: Rect) {
|
fun setSensorRect(rect: Rect, sensorType: FingerprintSensorType) {
|
||||||
this.sensorRect = rect
|
this.sensorRect = rect
|
||||||
|
this.fingerprintSensorType = sensorType
|
||||||
findViewById<ImageView?>(R.id.udfps_enroll_animation_fp_progress_view)?.also {
|
findViewById<ImageView?>(R.id.udfps_enroll_animation_fp_progress_view)?.also {
|
||||||
it.setImageDrawable(fingerprintProgressDrawable)
|
it.setImageDrawable(fingerprintProgressDrawable)
|
||||||
}
|
}
|
||||||
findViewById<ImageView>(R.id.udfps_enroll_animation_fp_view)?.also {
|
findViewById<ImageView>(R.id.udfps_enroll_animation_fp_view)?.also {
|
||||||
it.setImageDrawable(fingerprintIcon)
|
it.setImageDrawable(fingerprintIcon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val rotation = display.rotation
|
||||||
|
var displayInfo = DisplayInfo()
|
||||||
|
context.display.getDisplayInfo(displayInfo)
|
||||||
|
val scaleFactor = udfpsUtils.getScaleFactor(displayInfo)
|
||||||
|
val overlayParams =
|
||||||
|
UdfpsOverlayParams(
|
||||||
|
sensorRect,
|
||||||
|
fingerprintProgressDrawable.bounds,
|
||||||
|
displayInfo.naturalWidth,
|
||||||
|
displayInfo.naturalHeight,
|
||||||
|
scaleFactor,
|
||||||
|
rotation,
|
||||||
|
sensorType.toInt(),
|
||||||
|
)
|
||||||
val parentView = parent as ViewGroup
|
val parentView = parent as ViewGroup
|
||||||
val coords = parentView.getLocationOnScreen()
|
val coords = parentView.getLocationOnScreen()
|
||||||
val parentLeft = coords[0]
|
val parentLeft = coords[0]
|
||||||
val parentTop = coords[1]
|
val parentTop = coords[1]
|
||||||
val sensorRectOffset = Rect(sensorRect)
|
val sensorRectOffset = Rect(sensorRect)
|
||||||
|
// If the view has been rotated, we need to translate the sensor coordinates
|
||||||
|
// to the new rotated view.
|
||||||
|
when (rotation) {
|
||||||
|
Surface.ROTATION_90,
|
||||||
|
Surface.ROTATION_270 -> {
|
||||||
|
sensorRectOffset.set(
|
||||||
|
sensorRectOffset.top,
|
||||||
|
sensorRectOffset.left,
|
||||||
|
sensorRectOffset.bottom,
|
||||||
|
sensorRectOffset.right,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
// Translate the sensor position into UdfpsEnrollView's view space.
|
||||||
sensorRectOffset.offset(-parentLeft, -parentTop)
|
sensorRectOffset.offset(-parentLeft, -parentTop)
|
||||||
|
|
||||||
fingerprintIcon.drawSensorRectAt(sensorRectOffset)
|
fingerprintIcon.drawSensorRectAt(sensorRectOffset)
|
||||||
fingerprintProgressDrawable.drawProgressAt(sensorRectOffset)
|
fingerprintProgressDrawable.drawProgressAt(sensorRectOffset)
|
||||||
|
|
||||||
|
touchExplorationAnnouncer = TouchExplorationAnnouncer(context, this, overlayParams, udfpsUtils)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Updates the current enrollment stage. */
|
/** Updates the current enrollment stage. */
|
||||||
@@ -78,15 +118,17 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Receive enroll progress event */
|
/** Receive enroll progress event */
|
||||||
fun onUdfpsEvent(event: UdfpsEnrollEvent) {
|
fun onUdfpsEvent(event: FingerEnrollState) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is UdfpsProgress -> onEnrollmentProgress(event.remainingSteps, event.totalSteps)
|
is FingerEnrollState.EnrollProgress ->
|
||||||
is Acquired -> onAcquired(event.acquiredGood)
|
onEnrollmentProgress(event.remainingSteps, event.totalStepsRequired)
|
||||||
is UdfpsHelp -> onEnrollmentHelp()
|
is FingerEnrollState.Acquired -> onAcquired(event.acquiredGood)
|
||||||
is PointerDown -> onPointerDown()
|
is FingerEnrollState.EnrollHelp -> onEnrollmentHelp()
|
||||||
is PointerUp -> onPointerUp()
|
is FingerEnrollState.PointerDown -> onPointerDown()
|
||||||
OverlayShown -> overlayShown()
|
is FingerEnrollState.PointerUp -> onPointerUp()
|
||||||
is UdfpsError -> udfpsError(event.errMsgId, event.errString)
|
is FingerEnrollState.OverlayShown -> overlayShown()
|
||||||
|
is FingerEnrollState.EnrollError ->
|
||||||
|
throw IllegalArgumentException("$TAG should not handle udfps error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,9 +136,37 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
fun setAccessibilityEnabled(enabled: Boolean) {
|
fun setAccessibilityEnabled(enabled: Boolean) {
|
||||||
this.isAccessibilityEnabled = enabled
|
this.isAccessibilityEnabled = enabled
|
||||||
fingerprintProgressDrawable.setAccessibilityEnabled(enabled)
|
fingerprintProgressDrawable.setAccessibilityEnabled(enabled)
|
||||||
|
if (enabled) {
|
||||||
|
addHoverListener()
|
||||||
|
} else {
|
||||||
|
clearHoverListener()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun udfpsError(errMsgId: Int, errString: String) {}
|
private fun udfpsError(errMsgId: Int, errString: String) {}
|
||||||
|
/**
|
||||||
|
* Sends a touch exploration event to the [onHoverListener] this should only be used for
|
||||||
|
* debugging.
|
||||||
|
*/
|
||||||
|
fun sendDebugTouchExplorationEvent(motionEvent: MotionEvent) {
|
||||||
|
touchExplorationAnnouncer.onTouch(motionEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the addHoverListener, this should happen when talkback is enabled. */
|
||||||
|
private fun addHoverListener() {
|
||||||
|
onHoverListener = OnHoverListener { _: View, event: MotionEvent ->
|
||||||
|
sendDebugTouchExplorationEvent(event)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
this.setOnHoverListener(onHoverListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clears the hover listener if one was set. */
|
||||||
|
private fun clearHoverListener() {
|
||||||
|
val listener = OnHoverListener { _, _ -> false }
|
||||||
|
this.setOnHoverListener(listener)
|
||||||
|
onHoverListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
private fun overlayShown() {
|
private fun overlayShown() {
|
||||||
Log.e(TAG, "Implement overlayShown")
|
Log.e(TAG, "Implement overlayShown")
|
||||||
@@ -115,7 +185,7 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
|
|
||||||
/** Receive onAcquired event */
|
/** Receive onAcquired event */
|
||||||
private fun onAcquired(isAcquiredGood: Boolean) {
|
private fun onAcquired(isAcquiredGood: Boolean) {
|
||||||
val animateIfLastStepGood = isAcquiredGood && mRemainingSteps <= 2 && mRemainingSteps >= 0
|
val animateIfLastStepGood = isAcquiredGood && remainingSteps <= 2 && remainingSteps >= 0
|
||||||
if (animateIfLastStepGood) fingerprintProgressDrawable.onLastStepAcquired()
|
if (animateIfLastStepGood) fingerprintProgressDrawable.onLastStepAcquired()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +199,52 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
|||||||
fingerprintIcon.startDrawing()
|
fingerprintIcon.startDrawing()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||||
|
super.onLayout(changed, left, top, right, bottom)
|
||||||
|
// Because the layout has changed, we need to recompute all locations.
|
||||||
|
if (this::sensorRect.isInitialized && this::fingerprintSensorType.isInitialized) {
|
||||||
|
setSensorRect(sensorRect, fingerprintSensorType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible for announcing touch events that are outside of the sensort rect
|
||||||
|
* area. Generally, if a touch is to the left of the sensor, the accessibility announcement will
|
||||||
|
* be something like "move right"
|
||||||
|
*/
|
||||||
|
private class TouchExplorationAnnouncer(
|
||||||
|
val context: Context,
|
||||||
|
val view: View,
|
||||||
|
val overlayParams: UdfpsOverlayParams,
|
||||||
|
val udfpsUtils: UdfpsUtils,
|
||||||
|
) {
|
||||||
|
/** Will announce accessibility event for touches outside of the sensor rect. */
|
||||||
|
fun onTouch(event: MotionEvent) {
|
||||||
|
val scaledTouch: Point =
|
||||||
|
udfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0), event, overlayParams)
|
||||||
|
if (udfpsUtils.isWithinSensorArea(event.getPointerId(0), event, overlayParams)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val theStr: String =
|
||||||
|
udfpsUtils.onTouchOutsideOfSensorArea(
|
||||||
|
true /*touchExplorationEnabled*/,
|
||||||
|
context,
|
||||||
|
scaledTouch.x,
|
||||||
|
scaledTouch.y,
|
||||||
|
overlayParams,
|
||||||
|
)
|
||||||
|
if (theStr != null) {
|
||||||
|
view.announceForAccessibility(theStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates we should should restore the views saved state. */
|
||||||
|
fun onEnrollProgressSaved(it: FingerEnrollState.EnrollProgress) {
|
||||||
|
fingerprintIcon.onEnrollmentProgress(it.remainingSteps, it.totalStepsRequired, true)
|
||||||
|
fingerprintProgressDrawable.onEnrollmentProgress(it.remainingSteps, it.totalStepsRequired, true)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "UdfpsEnrollView"
|
private const val TAG = "UdfpsEnrollView"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.android.systemui.biometrics.shared.model.FingerprintSensor
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.transformLatest
|
import kotlinx.coroutines.flow.transformLatest
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
@@ -63,7 +65,7 @@ class FingerprintEnrollEnrollingViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Collects the enrollment flow based on [enrollFlowShouldBeRunning] */
|
/** Collects the enrollment flow based on [enrollFlowShouldBeRunning] */
|
||||||
val enrollFLow =
|
val enrollFlow =
|
||||||
enrollFlowShouldBeRunning.transformLatest {
|
enrollFlowShouldBeRunning.transformLatest {
|
||||||
if (it) {
|
if (it) {
|
||||||
fingerprintEnrollViewModel.enrollFlow.collect { event -> emit(event) }
|
fingerprintEnrollViewModel.enrollFlow.collect { event -> emit(event) }
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import com.android.settings.biometrics.fingerprint2.domain.interactor.Orientatio
|
|||||||
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
|
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
|
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
|
||||||
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@@ -38,7 +37,7 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/** Models the UI state for [FingerprintEnrollFindSensorV2Fragment]. */
|
/** Models the UI state for fingerprint enroll education */
|
||||||
class FingerprintEnrollFindSensorViewModel(
|
class FingerprintEnrollFindSensorViewModel(
|
||||||
private val navigationViewModel: FingerprintNavigationViewModel,
|
private val navigationViewModel: FingerprintNavigationViewModel,
|
||||||
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
|
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
|
||||||
@@ -70,7 +69,7 @@ class FingerprintEnrollFindSensorViewModel(
|
|||||||
combineTransform(
|
combineTransform(
|
||||||
_showSfpsLottie,
|
_showSfpsLottie,
|
||||||
foldStateInteractor.isFolded,
|
foldStateInteractor.isFolded,
|
||||||
orientationInteractor.rotation,
|
orientationInteractor.rotationFromDefault,
|
||||||
) { _, isFolded, rotation ->
|
) { _, isFolded, rotation ->
|
||||||
emit(Pair(isFolded, rotation))
|
emit(Pair(isFolded, rotation))
|
||||||
}
|
}
|
||||||
@@ -147,6 +146,7 @@ class FingerprintEnrollFindSensorViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is FingerEnrollState.EnrollHelp -> {}
|
is FingerEnrollState.EnrollHelp -> {}
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,12 +45,14 @@ import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
|
|||||||
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
|
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
|
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.Settings
|
import com.android.settings.biometrics.fingerprint2.lib.model.Settings
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.settings.binder.FingerprintSettingsViewBinder
|
import com.android.settings.biometrics.fingerprint2.ui.settings.binder.FingerprintSettingsViewBinder
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsViewModel
|
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsViewModel
|
||||||
@@ -222,6 +224,13 @@ class FingerprintSettingsV2Fragment :
|
|||||||
val fingerprintSensorProvider =
|
val fingerprintSensorProvider =
|
||||||
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
|
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
|
||||||
val pressToAuthInteractor = PressToAuthInteractorImpl(context, backgroundDispatcher)
|
val pressToAuthInteractor = PressToAuthInteractorImpl(context, backgroundDispatcher)
|
||||||
|
val fingerprintEnrollStateRepository =
|
||||||
|
FingerprintEnrollInteractorImpl(
|
||||||
|
requireContext().applicationContext,
|
||||||
|
intent.toFingerprintEnrollOptions(),
|
||||||
|
fingerprintManager,
|
||||||
|
Settings,
|
||||||
|
)
|
||||||
|
|
||||||
val interactor =
|
val interactor =
|
||||||
FingerprintManagerInteractorImpl(
|
FingerprintManagerInteractorImpl(
|
||||||
@@ -230,9 +239,7 @@ class FingerprintSettingsV2Fragment :
|
|||||||
fingerprintManager,
|
fingerprintManager,
|
||||||
fingerprintSensorProvider,
|
fingerprintSensorProvider,
|
||||||
GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)),
|
GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)),
|
||||||
pressToAuthInteractor,
|
fingerprintEnrollStateRepository,
|
||||||
Settings,
|
|
||||||
getIntent()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
|
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.os.Handler;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceCategory;
|
import androidx.preference.PreferenceCategory;
|
||||||
@@ -27,6 +28,7 @@ import androidx.preference.PreferenceGroup;
|
|||||||
import androidx.preference.PreferenceGroupAdapter;
|
import androidx.preference.PreferenceGroupAdapter;
|
||||||
import androidx.preference.PreferenceViewHolder;
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
|
||||||
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settingslib.widget.theme.R;
|
import com.android.settingslib.widget.theme.R;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -68,14 +70,47 @@ public class RoundCornerPreferenceAdapter extends PreferenceGroupAdapter {
|
|||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull PreferenceViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull PreferenceViewHolder holder, int position) {
|
||||||
super.onBindViewHolder(holder, position);
|
super.onBindViewHolder(holder, position);
|
||||||
|
if (Flags.homepageRevamp()) {
|
||||||
updateBackground(holder, position);
|
updateBackground(holder, position);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected @DrawableRes int getRoundCornerDrawableRes(int position, boolean isSelected) {
|
||||||
|
int CornerType = mRoundCornerMappingList.get(position);
|
||||||
|
|
||||||
|
if ((CornerType & ROUND_CORNER_CENTER) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((CornerType & ROUND_CORNER_TOP) != 0) && ((CornerType & ROUND_CORNER_BOTTOM) == 0)) {
|
||||||
|
// the first
|
||||||
|
return isSelected ? R.drawable.settingslib_round_background_top_selected
|
||||||
|
: R.drawable.settingslib_round_background_top;
|
||||||
|
} else if (((CornerType & ROUND_CORNER_BOTTOM) != 0)
|
||||||
|
&& ((CornerType & ROUND_CORNER_TOP) == 0)) {
|
||||||
|
// the last
|
||||||
|
return isSelected ? R.drawable.settingslib_round_background_bottom_selected
|
||||||
|
: R.drawable.settingslib_round_background_bottom;
|
||||||
|
} else if (((CornerType & ROUND_CORNER_TOP) != 0)
|
||||||
|
&& ((CornerType & ROUND_CORNER_BOTTOM) != 0)) {
|
||||||
|
// the only one preference
|
||||||
|
return isSelected ? R.drawable.settingslib_round_background_selected
|
||||||
|
: R.drawable.settingslib_round_background;
|
||||||
|
} else {
|
||||||
|
// in the center
|
||||||
|
return isSelected ? R.drawable.settingslib_round_background_center_selected
|
||||||
|
: R.drawable.settingslib_round_background_center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
private void updatePreferences() {
|
private void updatePreferences() {
|
||||||
|
if (Flags.homepageRevamp()) {
|
||||||
mRoundCornerMappingList = new ArrayList<>();
|
mRoundCornerMappingList = new ArrayList<>();
|
||||||
mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup);
|
mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void mappingPreferenceGroup(List<Integer> visibleList, PreferenceGroup group) {
|
private void mappingPreferenceGroup(List<Integer> visibleList, PreferenceGroup group) {
|
||||||
int groupSize = group.getPreferenceCount();
|
int groupSize = group.getPreferenceCount();
|
||||||
int firstVisible = 0;
|
int firstVisible = 0;
|
||||||
@@ -125,27 +160,9 @@ public class RoundCornerPreferenceAdapter extends PreferenceGroupAdapter {
|
|||||||
|
|
||||||
/** handle roundCorner background */
|
/** handle roundCorner background */
|
||||||
private void updateBackground(PreferenceViewHolder holder, int position) {
|
private void updateBackground(PreferenceViewHolder holder, int position) {
|
||||||
int CornerType = mRoundCornerMappingList.get(position);
|
@DrawableRes int backgroundRes = getRoundCornerDrawableRes(position, false /* isSelected*/);
|
||||||
|
|
||||||
if ((CornerType & ROUND_CORNER_CENTER) == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
View v = holder.itemView;
|
View v = holder.itemView;
|
||||||
if (((CornerType & ROUND_CORNER_TOP) != 0) && ((CornerType & ROUND_CORNER_BOTTOM) == 0)) {
|
v.setBackgroundResource(backgroundRes);
|
||||||
// the first
|
|
||||||
v.setBackgroundResource(R.drawable.settingslib_round_background_top);
|
|
||||||
} else if (((CornerType & ROUND_CORNER_BOTTOM) != 0)
|
|
||||||
&& ((CornerType & ROUND_CORNER_TOP) == 0)) {
|
|
||||||
// the last
|
|
||||||
v.setBackgroundResource(R.drawable.settingslib_round_background_bottom);
|
|
||||||
} else if (((CornerType & ROUND_CORNER_TOP) != 0)
|
|
||||||
&& ((CornerType & ROUND_CORNER_BOTTOM) != 0)) {
|
|
||||||
// the only one preference
|
|
||||||
v.setBackgroundResource(R.drawable.settingslib_round_background);
|
|
||||||
} else {
|
|
||||||
// in the center
|
|
||||||
v.setBackgroundResource(R.drawable.settingslib_round_background_center);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,17 +96,17 @@ public class BluetoothLeAudioModePreferenceController
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String currentValue;
|
||||||
if (mBluetoothAdapter.isLeAudioBroadcastSourceSupported()
|
if (mBluetoothAdapter.isLeAudioBroadcastSourceSupported()
|
||||||
== BluetoothStatusCodes.FEATURE_SUPPORTED) {
|
== BluetoothStatusCodes.FEATURE_SUPPORTED) {
|
||||||
SystemProperties.set(LE_AUDIO_DYNAMIC_SWITCHER_MODE_PROPERTY, "broadcast");
|
currentValue = "broadcast";
|
||||||
} else if (mBluetoothAdapter.isLeAudioSupported()
|
} else if (mBluetoothAdapter.isLeAudioSupported()
|
||||||
== BluetoothStatusCodes.FEATURE_SUPPORTED) {
|
== BluetoothStatusCodes.FEATURE_SUPPORTED) {
|
||||||
SystemProperties.set(LE_AUDIO_DYNAMIC_SWITCHER_MODE_PROPERTY, "unicast");
|
currentValue = "unicast";
|
||||||
} else {
|
} else {
|
||||||
SystemProperties.set(LE_AUDIO_DYNAMIC_SWITCHER_MODE_PROPERTY, "disabled");
|
currentValue = "disabled";
|
||||||
}
|
}
|
||||||
|
|
||||||
final String currentValue = SystemProperties.get(LE_AUDIO_DYNAMIC_SWITCHER_MODE_PROPERTY);
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (int i = 0; i < mListValues.length; i++) {
|
for (int i = 0; i < mListValues.length; i++) {
|
||||||
if (TextUtils.equals(currentValue, mListValues[i])) {
|
if (TextUtils.equals(currentValue, mListValues[i])) {
|
||||||
@@ -118,6 +118,11 @@ public class BluetoothLeAudioModePreferenceController
|
|||||||
final ListPreference listPreference = (ListPreference) preference;
|
final ListPreference listPreference = (ListPreference) preference;
|
||||||
listPreference.setValue(mListValues[index]);
|
listPreference.setValue(mListValues[index]);
|
||||||
listPreference.setSummary(mListSummaries[index]);
|
listPreference.setSummary(mListSummaries[index]);
|
||||||
|
|
||||||
|
if (!mBluetoothAdapter.isEnabled()) {
|
||||||
|
listPreference.setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -406,7 +406,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
|||||||
|
|
||||||
private void initSearchBarView() {
|
private void initSearchBarView() {
|
||||||
if (Flags.homepageRevamp()) {
|
if (Flags.homepageRevamp()) {
|
||||||
Toolbar toolbar = findViewById(R.id.search_action_bar_unified);
|
View toolbar = findViewById(R.id.search_action_bar);
|
||||||
FeatureFactory.getFeatureFactory().getSearchFeatureProvider()
|
FeatureFactory.getFeatureFactory().getSearchFeatureProvider()
|
||||||
.initSearchToolbar(this /* activity */, toolbar,
|
.initSearchToolbar(this /* activity */, toolbar,
|
||||||
SettingsEnums.SETTINGS_HOMEPAGE);
|
SettingsEnums.SETTINGS_HOMEPAGE);
|
||||||
@@ -469,6 +469,10 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
|||||||
window.setStatusBarColor(color);
|
window.setStatusBarColor(color);
|
||||||
// Update content background.
|
// Update content background.
|
||||||
findViewById(android.R.id.content).setBackgroundColor(color);
|
findViewById(android.R.id.content).setBackgroundColor(color);
|
||||||
|
if (Flags.homepageRevamp()) {
|
||||||
|
//Update search bar background
|
||||||
|
findViewById(R.id.app_bar_container).setBackgroundColor(color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSuggestionFragment(boolean scrollNeeded) {
|
private void showSuggestionFragment(boolean scrollNeeded) {
|
||||||
|
|||||||
@@ -109,21 +109,21 @@ fun Context.getSelectableSubscriptionInfoList(): List<SubscriptionInfo> {
|
|||||||
// to users so they should never be returned.
|
// to users so they should never be returned.
|
||||||
SubscriptionUtil.isSubscriptionVisible(subscriptionManager, this, subInfo)
|
SubscriptionUtil.isSubscriptionVisible(subscriptionManager, this, subInfo)
|
||||||
}
|
}
|
||||||
// Multiple subscriptions in a group should only have one representative.
|
|
||||||
// It should be the current active primary subscription if any, or any primary subscription.
|
|
||||||
val groupUuidToSelectedIdMap = visibleList
|
|
||||||
.groupBy { it.groupUuid }
|
|
||||||
.mapValues { (_, subInfos) ->
|
|
||||||
subInfos.filter { it.simSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX }
|
|
||||||
.ifEmpty { subInfos }
|
|
||||||
.minOf { it.subscriptionId }
|
|
||||||
}
|
|
||||||
|
|
||||||
return visibleList
|
return visibleList
|
||||||
.filter { subInfo ->
|
.groupBy { it.groupUuid }
|
||||||
val groupUuid = subInfo.groupUuid ?: return@filter true
|
.flatMap { (groupUuid, subInfos) ->
|
||||||
groupUuidToSelectedIdMap[groupUuid] == subInfo.subscriptionId
|
if (groupUuid == null) {
|
||||||
|
subInfos
|
||||||
|
} else {
|
||||||
|
// Multiple subscriptions in a group should only have one representative.
|
||||||
|
// It should be the current active primary subscription if any, or the primary
|
||||||
|
// subscription with minimum subscription id.
|
||||||
|
subInfos.filter { it.simSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX }
|
||||||
|
.ifEmpty { subInfos.sortedBy { it.subscriptionId } }
|
||||||
|
.take(1)
|
||||||
}
|
}
|
||||||
.sortedBy { it.subscriptionId }
|
}
|
||||||
|
// Matching the sorting order in SubscriptionManagerService.getAvailableSubscriptionInfoList
|
||||||
|
.sortedWith(compareBy({ it.simSlotIndex }, { it.subscriptionId }))
|
||||||
.also { Log.d(TAG, "getSelectableSubscriptionInfoList: $it") }
|
.also { Log.d(TAG, "getSelectableSubscriptionInfoList: $it") }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.settings.notification.modes;
|
package com.android.settings.notification.modes;
|
||||||
|
|
||||||
import android.app.AutomaticZenRule;
|
import android.app.AutomaticZenRule;
|
||||||
|
import android.app.Flags;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -55,6 +56,11 @@ abstract class AbstractZenModePreferenceController extends AbstractPreferenceCon
|
|||||||
return mKey;
|
return mKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return Flags.modesUi();
|
||||||
|
}
|
||||||
|
|
||||||
// Called by the parent Fragment onStart, which means it will happen before resume.
|
// Called by the parent Fragment onStart, which means it will happen before resume.
|
||||||
public void updateZenMode(@NonNull Preference preference, @NonNull ZenMode zenMode) {
|
public void updateZenMode(@NonNull Preference preference, @NonNull ZenMode zenMode) {
|
||||||
mZenMode = zenMode;
|
mZenMode = zenMode;
|
||||||
|
|||||||
@@ -155,8 +155,8 @@ class ZenMode {
|
|||||||
int iconResIdFromType = switch (mRule.getType()) {
|
int iconResIdFromType = switch (mRule.getType()) {
|
||||||
case AutomaticZenRule.TYPE_UNKNOWN -> R.drawable.ic_do_not_disturb_on_24dp;
|
case AutomaticZenRule.TYPE_UNKNOWN -> R.drawable.ic_do_not_disturb_on_24dp;
|
||||||
case AutomaticZenRule.TYPE_OTHER -> R.drawable.ic_do_not_disturb_on_24dp;
|
case AutomaticZenRule.TYPE_OTHER -> R.drawable.ic_do_not_disturb_on_24dp;
|
||||||
case AutomaticZenRule.TYPE_SCHEDULE_TIME -> R.drawable.ic_do_not_disturb_on_24dp;
|
case AutomaticZenRule.TYPE_SCHEDULE_TIME -> R.drawable.ic_modes_time;
|
||||||
case AutomaticZenRule.TYPE_SCHEDULE_CALENDAR -> R.drawable.ic_do_not_disturb_on_24dp;
|
case AutomaticZenRule.TYPE_SCHEDULE_CALENDAR -> R.drawable.ic_modes_event;
|
||||||
case AutomaticZenRule.TYPE_BEDTIME -> R.drawable.ic_do_not_disturb_on_24dp;
|
case AutomaticZenRule.TYPE_BEDTIME -> R.drawable.ic_do_not_disturb_on_24dp;
|
||||||
case AutomaticZenRule.TYPE_DRIVING -> R.drawable.ic_do_not_disturb_on_24dp;
|
case AutomaticZenRule.TYPE_DRIVING -> R.drawable.ic_do_not_disturb_on_24dp;
|
||||||
case AutomaticZenRule.TYPE_IMMERSIVE -> R.drawable.ic_do_not_disturb_on_24dp;
|
case AutomaticZenRule.TYPE_IMMERSIVE -> R.drawable.ic_do_not_disturb_on_24dp;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public class ZenModeFragment extends ZenModeFragmentBase {
|
|||||||
// TODO: fill in with all the elements of this page. Each should be an instance of
|
// TODO: fill in with all the elements of this page. Each should be an instance of
|
||||||
// {@link AbstractZenModePreferenceController}.
|
// {@link AbstractZenModePreferenceController}.
|
||||||
List<AbstractPreferenceController> prefControllers = new ArrayList<>();
|
List<AbstractPreferenceController> prefControllers = new ArrayList<>();
|
||||||
|
prefControllers.add(new ZenModeHeaderController(context, "header", this, mBackend));
|
||||||
return prefControllers;
|
return prefControllers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,19 +56,6 @@ public class ZenModeFragment extends ZenModeFragmentBase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getActivity().setTitle(azr.getName());
|
getActivity().setTitle(azr.getName());
|
||||||
|
|
||||||
// TODO: b/308819292 - implement the real screen!
|
|
||||||
final PreferenceScreen screen = getPreferenceScreen();
|
|
||||||
if (screen == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Preference tmpPref = screen.findPreference("zen_mode_test");
|
|
||||||
if (tmpPref == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tmpPref.setTitle(azr.getTriggerDescription());
|
|
||||||
tmpPref.setSummary("active?: " + mode.isActive());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.android.settings.notification.modes;
|
||||||
|
|
||||||
|
import android.app.Flags;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
|
import com.android.settings.widget.EntityHeaderController;
|
||||||
|
import com.android.settingslib.widget.LayoutPreference;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class ZenModeHeaderController extends AbstractZenModePreferenceController {
|
||||||
|
|
||||||
|
private final DashboardFragment mFragment;
|
||||||
|
private EntityHeaderController mHeaderController;
|
||||||
|
|
||||||
|
ZenModeHeaderController(
|
||||||
|
@NonNull Context context,
|
||||||
|
@NonNull String key,
|
||||||
|
@NonNull DashboardFragment fragment,
|
||||||
|
@Nullable ZenModesBackend backend) {
|
||||||
|
super(context, key, backend);
|
||||||
|
mFragment = fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return Flags.modesApi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateState(Preference preference) {
|
||||||
|
if (getAZR() == null || mFragment == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mHeaderController == null) {
|
||||||
|
final LayoutPreference pref = (LayoutPreference) preference;
|
||||||
|
mHeaderController = EntityHeaderController.newInstance(
|
||||||
|
mFragment.getActivity(),
|
||||||
|
mFragment,
|
||||||
|
pref.findViewById(R.id.entity_header));
|
||||||
|
}
|
||||||
|
Drawable icon = null;
|
||||||
|
try {
|
||||||
|
icon = getMode().getIcon(mContext).get(200, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// no icon
|
||||||
|
}
|
||||||
|
mHeaderController.setIcon(icon)
|
||||||
|
.setLabel(getAZR().getName())
|
||||||
|
.done(false /* rebindActions */);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,10 @@ import com.android.settings.core.SubSettingLauncher;
|
|||||||
import com.android.settings.notification.zen.ZenModeSettings;
|
import com.android.settings.notification.zen.ZenModeSettings;
|
||||||
import com.android.settingslib.RestrictedPreference;
|
import com.android.settingslib.RestrictedPreference;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preference representing a single mode item on the modes aggregator page. Clicking on this
|
* Preference representing a single mode item on the modes aggregator page. Clicking on this
|
||||||
* preference leads to an individual mode's configuration page.
|
* preference leads to an individual mode's configuration page.
|
||||||
@@ -36,10 +40,8 @@ public class ZenModeListPreference extends RestrictedPreference {
|
|||||||
ZenModeListPreference(Context context, ZenMode zenMode) {
|
ZenModeListPreference(Context context, ZenMode zenMode) {
|
||||||
super(context);
|
super(context);
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mZenMode = zenMode;
|
setZenMode(zenMode);
|
||||||
setTitle(mZenMode.getRule().getName());
|
setKey(zenMode.getId());
|
||||||
setSummary((mZenMode.isActive() ? "ACTIVE" : "inactive") + ": "
|
|
||||||
+ mZenMode.getRule().getTriggerDescription());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -60,6 +62,16 @@ public class ZenModeListPreference extends RestrictedPreference {
|
|||||||
.setSourceMetricsCategory(SettingsEnums.NOTIFICATION_ZEN_MODE_AUTOMATION)
|
.setSourceMetricsCategory(SettingsEnums.NOTIFICATION_ZEN_MODE_AUTOMATION)
|
||||||
.launch();
|
.launch();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZenMode(ZenMode zenMode) {
|
||||||
|
mZenMode = zenMode;
|
||||||
|
setTitle(mZenMode.getRule().getName());
|
||||||
|
setSummary(mZenMode.getRule().getTriggerDescription());
|
||||||
|
try {
|
||||||
|
setIcon(mZenMode.getIcon(mContext).get(200, TimeUnit.MILLISECONDS));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// no icon
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import com.android.settings.R;
|
|||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -77,7 +78,15 @@ class ZenModesBackend {
|
|||||||
isRuleActive(ruleId, currentConfig)));
|
isRuleActive(ruleId, currentConfig)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: b/331429435 - Sort modes.
|
modes.sort((l, r) -> {
|
||||||
|
if (l.isManualDnd()) {
|
||||||
|
return -1;
|
||||||
|
} else if (r.isManualDnd()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return l.getRule().getName().compareTo(r.getRule().getName());
|
||||||
|
});
|
||||||
|
|
||||||
return modes;
|
return modes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +114,6 @@ class ZenModesBackend {
|
|||||||
.setZenPolicy(ZenAdapters.notificationPolicyToZenPolicy(
|
.setZenPolicy(ZenAdapters.notificationPolicyToZenPolicy(
|
||||||
mNotificationManager.getNotificationPolicy()))
|
mNotificationManager.getNotificationPolicy()))
|
||||||
.setDeviceEffects(null)
|
.setDeviceEffects(null)
|
||||||
.setTriggerDescription(mContext.getString(R.string.zen_mode_settings_summary))
|
|
||||||
.setManualInvocationAllowed(true)
|
.setManualInvocationAllowed(true)
|
||||||
.setConfigurationActivity(null) // No further settings
|
.setConfigurationActivity(null) // No further settings
|
||||||
.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
|
.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
|
||||||
|
|||||||
@@ -15,8 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.settings.notification.modes;
|
package com.android.settings.notification.modes;
|
||||||
|
|
||||||
|
import android.app.AutomaticZenRule;
|
||||||
import android.app.Flags;
|
import android.app.Flags;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -24,14 +26,20 @@ import androidx.fragment.app.Fragment;
|
|||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceCategory;
|
import androidx.preference.PreferenceCategory;
|
||||||
|
|
||||||
import com.android.settingslib.core.AbstractPreferenceController;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.core.BasePreferenceController;
|
||||||
|
import com.android.settingslib.search.SearchIndexableRaw;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for the PreferenceCategory on the modes aggregator page ({@link ZenModesListFragment})
|
* Controller for the PreferenceCategory on the modes aggregator page ({@link ZenModesListFragment})
|
||||||
* containing links to each individual mode. This is a central controller that populates and updates
|
* containing links to each individual mode. This is a central controller that populates and updates
|
||||||
* all the preferences that then lead to a mode configuration page.
|
* all the preferences that then lead to a mode configuration page.
|
||||||
*/
|
*/
|
||||||
public class ZenModesListPreferenceController extends AbstractPreferenceController {
|
public class ZenModesListPreferenceController extends BasePreferenceController {
|
||||||
protected static final String KEY = "zen_modes_list";
|
protected static final String KEY = "zen_modes_list";
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -40,7 +48,7 @@ public class ZenModesListPreferenceController extends AbstractPreferenceControll
|
|||||||
|
|
||||||
public ZenModesListPreferenceController(Context context, @Nullable Fragment parent,
|
public ZenModesListPreferenceController(Context context, @Nullable Fragment parent,
|
||||||
@NonNull ZenModesBackend backend) {
|
@NonNull ZenModesBackend backend) {
|
||||||
super(context);
|
super(context, KEY);
|
||||||
mParent = parent;
|
mParent = parent;
|
||||||
mBackend = backend;
|
mBackend = backend;
|
||||||
}
|
}
|
||||||
@@ -51,8 +59,9 @@ public class ZenModesListPreferenceController extends AbstractPreferenceControll
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAvailable() {
|
@AvailabilityStatus
|
||||||
return Flags.modesUi();
|
public int getAvailabilityStatus() {
|
||||||
|
return Flags.modesUi() ? AVAILABLE_UNSEARCHABLE : UNSUPPORTED_ON_DEVICE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -65,16 +74,53 @@ public class ZenModesListPreferenceController extends AbstractPreferenceControll
|
|||||||
// category for each rule that exists.
|
// category for each rule that exists.
|
||||||
PreferenceCategory category = (PreferenceCategory) preference;
|
PreferenceCategory category = (PreferenceCategory) preference;
|
||||||
|
|
||||||
// TODO: b/322373473 - This is not the right way to replace these preferences; we should
|
Map<String, ZenModeListPreference> originalPreferences = new HashMap<>();
|
||||||
// follow something similar to what
|
for (int i = 0; i < category.getPreferenceCount(); i++) {
|
||||||
// ZenModeAutomaticRulesPreferenceController does to change rules
|
ZenModeListPreference pref = (ZenModeListPreference) category.getPreference(i);
|
||||||
// only as necessary and update them.
|
originalPreferences.put(pref.getKey(), pref);
|
||||||
category.removeAll();
|
}
|
||||||
|
|
||||||
|
// Loop through each rule, either updating the existing rule or creating the rule's
|
||||||
|
// preference
|
||||||
for (ZenMode mode : mBackend.getModes()) {
|
for (ZenMode mode : mBackend.getModes()) {
|
||||||
|
if (originalPreferences.containsKey(mode.getId())) {
|
||||||
|
// existing rule; update its info if it's changed since the last display
|
||||||
|
AutomaticZenRule rule = mode.getRule();
|
||||||
|
originalPreferences.get(mode.getId()).setZenMode(mode);
|
||||||
|
} else {
|
||||||
|
// new rule; create a new ZenRulePreference & add it to the preference category
|
||||||
Preference pref = new ZenModeListPreference(mContext, mode);
|
Preference pref = new ZenModeListPreference(mContext, mode);
|
||||||
category.addPreference(pref);
|
category.addPreference(pref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
originalPreferences.remove(mode.getId());
|
||||||
|
}
|
||||||
|
// Remove preferences that no longer have a rule
|
||||||
|
for (String key : originalPreferences.keySet()) {
|
||||||
|
category.removePreferenceRecursively(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Provide search data for the modes, which will allow users to reach the modes page if they
|
||||||
|
// search for a mode name.
|
||||||
|
@Override
|
||||||
|
public void updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData) {
|
||||||
|
// Don't add anything if flag is off. In theory this preference controller itself shouldn't
|
||||||
|
// be available in that case, but we check anyway to be sure.
|
||||||
|
if (!Flags.modesUi()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mBackend == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Resources res = mContext.getResources();
|
||||||
|
for (ZenMode mode : mBackend.getModes()) {
|
||||||
|
SearchIndexableRaw data = new SearchIndexableRaw(mContext);
|
||||||
|
data.key = mode.getId();
|
||||||
|
data.title = mode.getRule().getName();
|
||||||
|
data.screenTitle = res.getString(R.string.zen_modes_list_title);
|
||||||
|
rawData.add(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -448,7 +448,7 @@ public class PrivateSpaceMaintainer {
|
|||||||
private void removeSettingsAllTasks() {
|
private void removeSettingsAllTasks() {
|
||||||
List<ActivityManager.AppTask> appTasks = mActivityManager.getAppTasks();
|
List<ActivityManager.AppTask> appTasks = mActivityManager.getAppTasks();
|
||||||
for (var appTask : appTasks) {
|
for (var appTask : appTasks) {
|
||||||
if (!appTask.getTaskInfo().isVisible()) {
|
if (!(appTask.getTaskInfo().isVisible() || appTask.getTaskInfo().isFocused)) {
|
||||||
appTask.finishAndRemoveTask();
|
appTask.finishAndRemoveTask();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.Toolbar;
|
import android.widget.Toolbar;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
@@ -80,8 +81,9 @@ public interface SearchFeatureProvider {
|
|||||||
/**
|
/**
|
||||||
* Initializes the search toolbar.
|
* Initializes the search toolbar.
|
||||||
*/
|
*/
|
||||||
default void initSearchToolbar(FragmentActivity activity, Toolbar toolbar, int pageId) {
|
default void initSearchToolbar(@NonNull FragmentActivity activity, @Nullable View toolbar,
|
||||||
if (activity == null || toolbar == null) {
|
int pageId) {
|
||||||
|
if (toolbar == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,11 +100,13 @@ public interface SearchFeatureProvider {
|
|||||||
//
|
//
|
||||||
// Need to make the navigation icon non-clickable so that the entire card is clickable
|
// Need to make the navigation icon non-clickable so that the entire card is clickable
|
||||||
// and goes to the search UI. Also set the background to null so there's no ripple.
|
// and goes to the search UI. Also set the background to null so there's no ripple.
|
||||||
final View navView = toolbar.getNavigationView();
|
if (toolbar instanceof Toolbar) {
|
||||||
|
final View navView = ((Toolbar) toolbar).getNavigationView();
|
||||||
navView.setClickable(false);
|
navView.setClickable(false);
|
||||||
navView.setFocusable(false);
|
navView.setFocusable(false);
|
||||||
navView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
|
navView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||||
navView.setBackground(null);
|
navView.setBackground(null);
|
||||||
|
}
|
||||||
|
|
||||||
final Context context = activity.getApplicationContext();
|
final Context context = activity.getApplicationContext();
|
||||||
final Intent intent = buildSearchIntent(context, pageId)
|
final Intent intent = buildSearchIntent(context, pageId)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||||
import com.android.settingslib.R
|
import com.android.settingslib.R
|
||||||
import com.android.settingslib.spa.lifecycle.collectAsCallbackWithLifecycle
|
import com.android.settingslib.spa.lifecycle.collectAsCallbackWithLifecycle
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.model.app.AppOpsController
|
import com.android.settingslib.spaprivileged.model.app.AppOpsController
|
||||||
import com.android.settingslib.spaprivileged.model.app.AppRecord
|
import com.android.settingslib.spaprivileged.model.app.AppRecord
|
||||||
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
|
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
|
||||||
@@ -116,8 +117,7 @@ class AlarmsAndRemindersAppListModel(
|
|||||||
controller = AppOpsController(
|
controller = AppOpsController(
|
||||||
context = context,
|
context = context,
|
||||||
app = app,
|
app = app,
|
||||||
op = AppOpsManager.OP_SCHEDULE_EXACT_ALARM,
|
appOps = APP_OPS,
|
||||||
setModeByUid = true,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -136,6 +136,11 @@ class AlarmsAndRemindersAppListModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val APP_OPS = AppOps(
|
||||||
|
op = AppOpsManager.OP_SCHEDULE_EXACT_ALARM,
|
||||||
|
setModeByUid = true,
|
||||||
|
)
|
||||||
|
|
||||||
private const val PERMISSION: String = Manifest.permission.SCHEDULE_EXACT_ALARM
|
private const val PERMISSION: String = Manifest.permission.SCHEDULE_EXACT_ALARM
|
||||||
|
|
||||||
/** Checks whether [Manifest.permission.SCHEDULE_EXACT_ALARM] is enabled. */
|
/** Checks whether [Manifest.permission.SCHEDULE_EXACT_ALARM] is enabled. */
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import android.app.settings.SettingsEnums
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
||||||
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
||||||
@@ -35,9 +36,11 @@ class AllFilesAccessListModel(context: Context) : AppOpPermissionListModel(conte
|
|||||||
override val pageTitleResId = R.string.manage_external_storage_title
|
override val pageTitleResId = R.string.manage_external_storage_title
|
||||||
override val switchTitleResId = R.string.permit_manage_external_storage
|
override val switchTitleResId = R.string.permit_manage_external_storage
|
||||||
override val footerResId = R.string.allow_manage_external_storage_description
|
override val footerResId = R.string.allow_manage_external_storage_description
|
||||||
override val appOp = AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE
|
override val appOps = AppOps(
|
||||||
|
op = AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE,
|
||||||
|
setModeByUid = true,
|
||||||
|
)
|
||||||
override val permission = Manifest.permission.MANAGE_EXTERNAL_STORAGE
|
override val permission = Manifest.permission.MANAGE_EXTERNAL_STORAGE
|
||||||
override val setModeByUid = true
|
|
||||||
|
|
||||||
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
||||||
super.setAllowed(record, newAllowed)
|
super.setAllowed(record, newAllowed)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import android.app.settings.SettingsEnums
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
||||||
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
||||||
@@ -35,7 +36,7 @@ class DisplayOverOtherAppsListModel(context: Context) : AppOpPermissionListModel
|
|||||||
override val pageTitleResId = R.string.system_alert_window_settings
|
override val pageTitleResId = R.string.system_alert_window_settings
|
||||||
override val switchTitleResId = R.string.permit_draw_overlay
|
override val switchTitleResId = R.string.permit_draw_overlay
|
||||||
override val footerResId = R.string.allow_overlay_description
|
override val footerResId = R.string.allow_overlay_description
|
||||||
override val appOp = AppOpsManager.OP_SYSTEM_ALERT_WINDOW
|
override val appOps = AppOps(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
|
||||||
override val permission = Manifest.permission.SYSTEM_ALERT_WINDOW
|
override val permission = Manifest.permission.SYSTEM_ALERT_WINDOW
|
||||||
|
|
||||||
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import android.Manifest
|
|||||||
import android.app.AppGlobals
|
import android.app.AppGlobals
|
||||||
import android.app.AppOpsManager
|
import android.app.AppOpsManager
|
||||||
import android.app.AppOpsManager.MODE_DEFAULT
|
import android.app.AppOpsManager.MODE_DEFAULT
|
||||||
import android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
@@ -28,6 +27,7 @@ import android.os.UserManager
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settingslib.spa.lifecycle.collectAsCallbackWithLifecycle
|
import com.android.settingslib.spa.lifecycle.collectAsCallbackWithLifecycle
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.model.app.AppOpsController
|
import com.android.settingslib.spaprivileged.model.app.AppOpsController
|
||||||
import com.android.settingslib.spaprivileged.model.app.AppRecord
|
import com.android.settingslib.spaprivileged.model.app.AppRecord
|
||||||
import com.android.settingslib.spaprivileged.model.app.userId
|
import com.android.settingslib.spaprivileged.model.app.userId
|
||||||
@@ -62,12 +62,7 @@ class InstallUnknownAppsListModel(private val context: Context) :
|
|||||||
override fun transformItem(app: ApplicationInfo) =
|
override fun transformItem(app: ApplicationInfo) =
|
||||||
InstallUnknownAppsRecord(
|
InstallUnknownAppsRecord(
|
||||||
app = app,
|
app = app,
|
||||||
appOpsController =
|
appOpsController = AppOpsController(context = context, app = app, appOps = APP_OPS),
|
||||||
AppOpsController(
|
|
||||||
context = context,
|
|
||||||
app = app,
|
|
||||||
op = OP_REQUEST_INSTALL_PACKAGES,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun filter(
|
override fun filter(
|
||||||
@@ -92,6 +87,8 @@ class InstallUnknownAppsListModel(private val context: Context) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val APP_OPS = AppOps(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES)
|
||||||
|
|
||||||
private fun isChangeable(
|
private fun isChangeable(
|
||||||
record: InstallUnknownAppsRecord,
|
record: InstallUnknownAppsRecord,
|
||||||
potentialPackageNames: Set<String>,
|
potentialPackageNames: Set<String>,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import android.app.settings.SettingsEnums
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
||||||
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
||||||
@@ -35,9 +36,11 @@ class LongBackgroundTasksAppsListModel(context: Context) : AppOpPermissionListMo
|
|||||||
override val pageTitleResId = R.string.long_background_tasks_title
|
override val pageTitleResId = R.string.long_background_tasks_title
|
||||||
override val switchTitleResId = R.string.long_background_tasks_switch_title
|
override val switchTitleResId = R.string.long_background_tasks_switch_title
|
||||||
override val footerResId = R.string.long_background_tasks_footer_title
|
override val footerResId = R.string.long_background_tasks_footer_title
|
||||||
override val appOp = AppOpsManager.OP_RUN_USER_INITIATED_JOBS
|
override val appOps = AppOps(
|
||||||
|
op = AppOpsManager.OP_RUN_USER_INITIATED_JOBS,
|
||||||
|
setModeByUid = true,
|
||||||
|
)
|
||||||
override val permission = Manifest.permission.RUN_USER_INITIATED_JOBS
|
override val permission = Manifest.permission.RUN_USER_INITIATED_JOBS
|
||||||
override val setModeByUid = true
|
|
||||||
|
|
||||||
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
||||||
super.setAllowed(record, newAllowed)
|
super.setAllowed(record, newAllowed)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import android.app.settings.SettingsEnums
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
||||||
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
||||||
@@ -35,9 +36,11 @@ class MediaManagementAppsListModel(context: Context) : AppOpPermissionListModel(
|
|||||||
override val pageTitleResId = R.string.media_management_apps_title
|
override val pageTitleResId = R.string.media_management_apps_title
|
||||||
override val switchTitleResId = R.string.media_management_apps_toggle_label
|
override val switchTitleResId = R.string.media_management_apps_toggle_label
|
||||||
override val footerResId = R.string.media_management_apps_description
|
override val footerResId = R.string.media_management_apps_description
|
||||||
override val appOp = AppOpsManager.OP_MANAGE_MEDIA
|
override val appOps = AppOps(
|
||||||
|
op = AppOpsManager.OP_MANAGE_MEDIA,
|
||||||
|
setModeByUid = true,
|
||||||
|
)
|
||||||
override val permission = Manifest.permission.MANAGE_MEDIA
|
override val permission = Manifest.permission.MANAGE_MEDIA
|
||||||
override val setModeByUid = true
|
|
||||||
|
|
||||||
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
||||||
super.setAllowed(record, newAllowed)
|
super.setAllowed(record, newAllowed)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.content.Context
|
|||||||
import com.android.media.flags.Flags;
|
import com.android.media.flags.Flags;
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
||||||
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
||||||
@@ -38,9 +39,11 @@ class MediaRoutingControlAppsListModel(context: Context) : AppOpPermissionListMo
|
|||||||
override val pageTitleResId = R.string.media_routing_control_title
|
override val pageTitleResId = R.string.media_routing_control_title
|
||||||
override val switchTitleResId = R.string.allow_media_routing_control
|
override val switchTitleResId = R.string.allow_media_routing_control
|
||||||
override val footerResId = R.string.allow_media_routing_description
|
override val footerResId = R.string.allow_media_routing_description
|
||||||
override val appOp = AppOpsManager.OP_MEDIA_ROUTING_CONTROL
|
override val appOps = AppOps(
|
||||||
|
op = AppOpsManager.OP_MEDIA_ROUTING_CONTROL,
|
||||||
|
setModeByUid = true,
|
||||||
|
)
|
||||||
override val permission = Manifest.permission.MEDIA_ROUTING_CONTROL
|
override val permission = Manifest.permission.MEDIA_ROUTING_CONTROL
|
||||||
override val setModeByUid = true
|
|
||||||
private val roleManager = context.getSystemService(RoleManager::class.java)
|
private val roleManager = context.getSystemService(RoleManager::class.java)
|
||||||
|
|
||||||
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import android.app.settings.SettingsEnums
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
||||||
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
||||||
@@ -35,7 +36,7 @@ class ModifySystemSettingsListModel(context: Context) : AppOpPermissionListModel
|
|||||||
override val pageTitleResId = R.string.write_system_settings
|
override val pageTitleResId = R.string.write_system_settings
|
||||||
override val switchTitleResId = R.string.permit_write_settings
|
override val switchTitleResId = R.string.permit_write_settings
|
||||||
override val footerResId = R.string.write_settings_description
|
override val footerResId = R.string.write_settings_description
|
||||||
override val appOp = AppOpsManager.OP_WRITE_SETTINGS
|
override val appOps = AppOps(AppOpsManager.OP_WRITE_SETTINGS)
|
||||||
override val permission = Manifest.permission.WRITE_SETTINGS
|
override val permission = Manifest.permission.WRITE_SETTINGS
|
||||||
|
|
||||||
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package com.android.settings.spa.app.specialaccess
|
package com.android.settings.spa.app.specialaccess
|
||||||
|
|
||||||
import android.app.AppOpsManager
|
import android.app.AppOpsManager
|
||||||
import android.app.AppOpsManager.OP_PICTURE_IN_PICTURE
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
@@ -28,6 +27,7 @@ import android.util.Log
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settingslib.spa.lifecycle.collectAsCallbackWithLifecycle
|
import com.android.settingslib.spa.lifecycle.collectAsCallbackWithLifecycle
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.model.app.AppOpsController
|
import com.android.settingslib.spaprivileged.model.app.AppOpsController
|
||||||
import com.android.settingslib.spaprivileged.model.app.AppRecord
|
import com.android.settingslib.spaprivileged.model.app.AppRecord
|
||||||
import com.android.settingslib.spaprivileged.model.app.installed
|
import com.android.settingslib.spaprivileged.model.app.installed
|
||||||
@@ -79,12 +79,7 @@ class PictureInPictureListModel(private val context: Context) :
|
|||||||
PictureInPictureRecord(
|
PictureInPictureRecord(
|
||||||
app = app,
|
app = app,
|
||||||
isSupport = isSupport,
|
isSupport = isSupport,
|
||||||
appOpsController =
|
appOpsController = AppOpsController(context = context, app = app, appOps = APP_OPS),
|
||||||
AppOpsController(
|
|
||||||
context = context,
|
|
||||||
app = app,
|
|
||||||
op = OP_PICTURE_IN_PICTURE,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<PictureInPictureRecord>>) =
|
override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<PictureInPictureRecord>>) =
|
||||||
@@ -131,6 +126,8 @@ class PictureInPictureListModel(private val context: Context) :
|
|||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "PictureInPictureListModel"
|
private const val TAG = "PictureInPictureListModel"
|
||||||
|
|
||||||
|
private val APP_OPS = AppOps(AppOpsManager.OP_PICTURE_IN_PICTURE)
|
||||||
|
|
||||||
private fun PackageInfo.supportsPictureInPicture() =
|
private fun PackageInfo.supportsPictureInPicture() =
|
||||||
activities?.any(ActivityInfo::supportsPictureInPicture) ?: false
|
activities?.any(ActivityInfo::supportsPictureInPicture) ?: false
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import android.Manifest
|
|||||||
import android.app.AppOpsManager
|
import android.app.AppOpsManager
|
||||||
import android.app.settings.SettingsEnums
|
import android.app.settings.SettingsEnums
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.settings.R
|
|
||||||
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
||||||
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
||||||
@@ -35,9 +35,11 @@ class TurnScreenOnAppsListModel(context: Context) : AppOpPermissionListModel(con
|
|||||||
override val pageTitleResId = com.android.settingslib.R.string.turn_screen_on_title
|
override val pageTitleResId = com.android.settingslib.R.string.turn_screen_on_title
|
||||||
override val switchTitleResId = com.android.settingslib.R.string.allow_turn_screen_on
|
override val switchTitleResId = com.android.settingslib.R.string.allow_turn_screen_on
|
||||||
override val footerResId = com.android.settingslib.R.string.allow_turn_screen_on_description
|
override val footerResId = com.android.settingslib.R.string.allow_turn_screen_on_description
|
||||||
override val appOp = AppOpsManager.OP_TURN_SCREEN_ON
|
override val appOps = AppOps(
|
||||||
|
op = AppOpsManager.OP_TURN_SCREEN_ON,
|
||||||
|
setModeByUid = true,
|
||||||
|
)
|
||||||
override val permission = Manifest.permission.TURN_SCREEN_ON
|
override val permission = Manifest.permission.TURN_SCREEN_ON
|
||||||
override val setModeByUid = true
|
|
||||||
|
|
||||||
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
||||||
super.setAllowed(record, newAllowed)
|
super.setAllowed(record, newAllowed)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.Manifest
|
|||||||
import android.app.AppOpsManager
|
import android.app.AppOpsManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
||||||
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
||||||
|
|
||||||
@@ -32,7 +33,9 @@ class UseFullScreenIntentListModel(context: Context) : AppOpPermissionListModel(
|
|||||||
override val pageTitleResId = R.string.full_screen_intent_title
|
override val pageTitleResId = R.string.full_screen_intent_title
|
||||||
override val switchTitleResId = R.string.permit_full_screen_intent
|
override val switchTitleResId = R.string.permit_full_screen_intent
|
||||||
override val footerResId = R.string.footer_description_full_screen_intent
|
override val footerResId = R.string.footer_description_full_screen_intent
|
||||||
override val appOp = AppOpsManager.OP_USE_FULL_SCREEN_INTENT
|
override val appOps = AppOps(
|
||||||
|
op = AppOpsManager.OP_USE_FULL_SCREEN_INTENT,
|
||||||
|
setModeByUid = true,
|
||||||
|
)
|
||||||
override val permission = Manifest.permission.USE_FULL_SCREEN_INTENT
|
override val permission = Manifest.permission.USE_FULL_SCREEN_INTENT
|
||||||
override val setModeByUid = true
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.app.AppOpsManager
|
|||||||
import android.app.AppOpsManager.MODE_IGNORED
|
import android.app.AppOpsManager.MODE_IGNORED
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
|
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
|
||||||
import com.android.settingslib.spaprivileged.model.app.PackageManagers
|
import com.android.settingslib.spaprivileged.model.app.PackageManagers
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
||||||
@@ -39,11 +40,13 @@ class WifiControlAppListModel(
|
|||||||
override val switchTitleResId = R.string.change_wifi_state_app_detail_switch
|
override val switchTitleResId = R.string.change_wifi_state_app_detail_switch
|
||||||
override val footerResId = R.string.change_wifi_state_app_detail_summary
|
override val footerResId = R.string.change_wifi_state_app_detail_summary
|
||||||
|
|
||||||
override val appOp = AppOpsManager.OP_CHANGE_WIFI_STATE
|
override val appOps = AppOps(
|
||||||
|
op = AppOpsManager.OP_CHANGE_WIFI_STATE,
|
||||||
|
modeForNotAllowed = MODE_IGNORED,
|
||||||
|
)
|
||||||
override val permission = Manifest.permission.CHANGE_WIFI_STATE
|
override val permission = Manifest.permission.CHANGE_WIFI_STATE
|
||||||
|
|
||||||
/** NETWORK_SETTINGS permission trumps CHANGE_WIFI_CONFIG. */
|
/** NETWORK_SETTINGS permission trumps CHANGE_WIFI_CONFIG. */
|
||||||
override val broaderPermission = Manifest.permission.NETWORK_SETTINGS
|
override val broaderPermission = Manifest.permission.NETWORK_SETTINGS
|
||||||
override val permissionHasAppOpFlag = false
|
override val permissionHasAppOpFlag = false
|
||||||
override val modeForNotAllowed = MODE_IGNORED
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,35 +25,32 @@ import android.view.View;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.preference.PreferenceGroup;
|
import androidx.preference.PreferenceGroup;
|
||||||
import androidx.preference.PreferenceGroupAdapter;
|
|
||||||
import androidx.preference.PreferenceViewHolder;
|
import androidx.preference.PreferenceViewHolder;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.window.embedding.ActivityEmbeddingController;
|
import androidx.window.embedding.ActivityEmbeddingController;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
|
import com.android.settings.core.RoundCornerPreferenceAdapter;
|
||||||
import com.android.settings.flags.Flags;
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settings.homepage.SettingsHomepageActivity;
|
import com.android.settings.homepage.SettingsHomepageActivity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter for highlighting top level preferences
|
* Adapter for highlighting top level preferences
|
||||||
*/
|
*/
|
||||||
public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapter implements
|
public class HighlightableTopLevelPreferenceAdapter extends RoundCornerPreferenceAdapter implements
|
||||||
SettingsHomepageActivity.HomepageLoadedListener {
|
SettingsHomepageActivity.HomepageLoadedListener {
|
||||||
|
|
||||||
private static final String TAG = "HighlightableTopLevelAdapter";
|
private static final String TAG = "HighlightableTopLevelAdapter";
|
||||||
|
|
||||||
static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 100L;
|
static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 100L;
|
||||||
private static final int RES_NORMAL_BACKGROUND =
|
private static final int RES_NORMAL_BACKGROUND =
|
||||||
Flags.homepageRevamp()
|
R.drawable.homepage_selectable_item_background;
|
||||||
? R.drawable.homepage_selectable_item_background_v2
|
|
||||||
: R.drawable.homepage_selectable_item_background;
|
|
||||||
private static final int RES_HIGHLIGHTED_BACKGROUND =
|
private static final int RES_HIGHLIGHTED_BACKGROUND =
|
||||||
Flags.homepageRevamp()
|
R.drawable.homepage_highlighted_item_background;
|
||||||
? R.drawable.homepage_highlighted_item_background_v2
|
|
||||||
: R.drawable.homepage_highlighted_item_background;
|
|
||||||
|
|
||||||
private final int mTitleColorNormal;
|
private final int mTitleColorNormal;
|
||||||
private final int mTitleColorHighlight;
|
private final int mTitleColorHighlight;
|
||||||
@@ -101,7 +98,7 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void updateBackground(PreferenceViewHolder holder, int position) {
|
void updateBackground(PreferenceViewHolder holder, int position) {
|
||||||
if (!isHighlightNeeded()) {
|
if (!isHighlightNeeded()) {
|
||||||
removeHighlightBackground(holder);
|
removeHighlightBackground(holder, position);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,9 +106,9 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt
|
|||||||
&& mHighlightKey != null
|
&& mHighlightKey != null
|
||||||
&& TextUtils.equals(mHighlightKey, getItem(position).getKey())) {
|
&& TextUtils.equals(mHighlightKey, getItem(position).getKey())) {
|
||||||
// This position should be highlighted.
|
// This position should be highlighted.
|
||||||
addHighlightBackground(holder);
|
addHighlightBackground(holder, position);
|
||||||
} else {
|
} else {
|
||||||
removeHighlightBackground(holder);
|
removeHighlightBackground(holder, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,14 +223,18 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt
|
|||||||
// De-highlight the existing preference view holder at an early stage
|
// De-highlight the existing preference view holder at an early stage
|
||||||
final PreferenceViewHolder holder = mViewHolders.get(position);
|
final PreferenceViewHolder holder = mViewHolders.get(position);
|
||||||
if (holder != null) {
|
if (holder != null) {
|
||||||
removeHighlightBackground(holder);
|
removeHighlightBackground(holder, position);
|
||||||
}
|
}
|
||||||
notifyItemChanged(position);
|
notifyItemChanged(position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addHighlightBackground(PreferenceViewHolder holder) {
|
private void addHighlightBackground(PreferenceViewHolder holder, int position) {
|
||||||
final View v = holder.itemView;
|
final View v = holder.itemView;
|
||||||
|
if (Flags.homepageRevamp()) {
|
||||||
|
@DrawableRes int bgRes = getRoundCornerDrawableRes(position, true /*isSelected*/);
|
||||||
|
v.setBackgroundResource(bgRes);
|
||||||
|
} else {
|
||||||
v.setBackgroundResource(RES_HIGHLIGHTED_BACKGROUND);
|
v.setBackgroundResource(RES_HIGHLIGHTED_BACKGROUND);
|
||||||
((TextView) v.findViewById(android.R.id.title)).setTextColor(mTitleColorHighlight);
|
((TextView) v.findViewById(android.R.id.title)).setTextColor(mTitleColorHighlight);
|
||||||
((TextView) v.findViewById(android.R.id.summary)).setTextColor(mSummaryColorHighlight);
|
((TextView) v.findViewById(android.R.id.summary)).setTextColor(mSummaryColorHighlight);
|
||||||
@@ -242,9 +243,14 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt
|
|||||||
drawable.setTint(mIconColorHighlight);
|
drawable.setTint(mIconColorHighlight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void removeHighlightBackground(PreferenceViewHolder holder) {
|
private void removeHighlightBackground(PreferenceViewHolder holder, int position) {
|
||||||
final View v = holder.itemView;
|
final View v = holder.itemView;
|
||||||
|
if (Flags.homepageRevamp()) {
|
||||||
|
@DrawableRes int bgRes = getRoundCornerDrawableRes(position, false /*isSelected*/);
|
||||||
|
v.setBackgroundResource(bgRes);
|
||||||
|
} else {
|
||||||
v.setBackgroundResource(RES_NORMAL_BACKGROUND);
|
v.setBackgroundResource(RES_NORMAL_BACKGROUND);
|
||||||
((TextView) v.findViewById(android.R.id.title)).setTextColor(mTitleColorNormal);
|
((TextView) v.findViewById(android.R.id.title)).setTextColor(mTitleColorNormal);
|
||||||
((TextView) v.findViewById(android.R.id.summary)).setTextColor(mSummaryColorNormal);
|
((TextView) v.findViewById(android.R.id.summary)).setTextColor(mSummaryColorNormal);
|
||||||
@@ -253,6 +259,7 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt
|
|||||||
drawable.setTint(mIconColorNormal);
|
drawable.setTint(mIconColorNormal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isHighlightNeeded() {
|
private boolean isHighlightNeeded() {
|
||||||
return ActivityEmbeddingController.getInstance(mHomepageActivity)
|
return ActivityEmbeddingController.getInstance(mHomepageActivity)
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ import static java.util.Collections.singletonList;
|
|||||||
|
|
||||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||||
import android.accessibilityservice.AccessibilityShortcutInfo;
|
import android.accessibilityservice.AccessibilityShortcutInfo;
|
||||||
import android.app.Application;
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -49,7 +47,6 @@ import androidx.fragment.app.Fragment;
|
|||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import com.android.internal.accessibility.util.AccessibilityUtils;
|
import com.android.internal.accessibility.util.AccessibilityUtils;
|
||||||
import com.android.internal.content.PackageMonitor;
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.SettingsActivity;
|
import com.android.settings.SettingsActivity;
|
||||||
import com.android.settings.testutils.XmlTestUtils;
|
import com.android.settings.testutils.XmlTestUtils;
|
||||||
@@ -333,12 +330,6 @@ public class AccessibilitySettingsTest {
|
|||||||
AccessibilitySettingsContentObserver.class).isTrue();
|
AccessibilitySettingsContentObserver.class).isTrue();
|
||||||
assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
|
assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
|
||||||
AccessibilitySettingsContentObserver.class).isFalse();
|
AccessibilitySettingsContentObserver.class).isFalse();
|
||||||
List<BroadcastReceiver> broadcastReceivers =
|
|
||||||
shadowOf((Application) ApplicationProvider.getApplicationContext())
|
|
||||||
.getRegisteredReceivers()
|
|
||||||
.stream().map(wrapper -> wrapper.broadcastReceiver).toList();
|
|
||||||
assertThat(broadcastReceivers.stream().anyMatch(
|
|
||||||
broadcastReceiver -> broadcastReceiver instanceof PackageMonitor)).isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -352,12 +343,6 @@ public class AccessibilitySettingsTest {
|
|||||||
AccessibilitySettingsContentObserver.class).isTrue();
|
AccessibilitySettingsContentObserver.class).isTrue();
|
||||||
assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
|
assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
|
||||||
AccessibilitySettingsContentObserver.class).isTrue();
|
AccessibilitySettingsContentObserver.class).isTrue();
|
||||||
List<BroadcastReceiver> broadcastReceivers =
|
|
||||||
shadowOf((Application) ApplicationProvider.getApplicationContext())
|
|
||||||
.getRegisteredReceivers()
|
|
||||||
.stream().map(wrapper -> wrapper.broadcastReceiver).toList();
|
|
||||||
assertThat(broadcastReceivers.stream().anyMatch(
|
|
||||||
broadcastReceiver -> broadcastReceiver instanceof PackageMonitor)).isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -372,12 +357,6 @@ public class AccessibilitySettingsTest {
|
|||||||
AccessibilitySettingsContentObserver.class).isFalse();
|
AccessibilitySettingsContentObserver.class).isFalse();
|
||||||
assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
|
assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
|
||||||
AccessibilitySettingsContentObserver.class).isFalse();
|
AccessibilitySettingsContentObserver.class).isFalse();
|
||||||
List<BroadcastReceiver> broadcastReceivers =
|
|
||||||
shadowOf((Application) ApplicationProvider.getApplicationContext())
|
|
||||||
.getRegisteredReceivers()
|
|
||||||
.stream().map(wrapper -> wrapper.broadcastReceiver).toList();
|
|
||||||
assertThat(broadcastReceivers.stream().anyMatch(
|
|
||||||
broadcastReceiver -> broadcastReceiver instanceof PackageMonitor)).isFalse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -105,4 +105,16 @@ public class BluetoothLeAudioModePreferenceControllerTest {
|
|||||||
assertThat(SystemProperties.get(LE_AUDIO_DYNAMIC_SWITCHER_MODE_PROPERTY, mListValues[0])
|
assertThat(SystemProperties.get(LE_AUDIO_DYNAMIC_SWITCHER_MODE_PROPERTY, mListValues[0])
|
||||||
.equals(mController.mNewMode)).isFalse();
|
.equals(mController.mNewMode)).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBluetoothTurnOff_shouldNotChangeLeAudioMode() {
|
||||||
|
SystemProperties.set(LE_AUDIO_DYNAMIC_SWITCHER_MODE_PROPERTY, mListValues[1]);
|
||||||
|
when(mBluetoothAdapter.isEnabled())
|
||||||
|
.thenReturn(false);
|
||||||
|
|
||||||
|
mController.updateState(mPreference);
|
||||||
|
final String mode = SystemProperties
|
||||||
|
.get(LE_AUDIO_DYNAMIC_SWITCHER_MODE_PROPERTY, mListValues[0]);
|
||||||
|
assertThat(mode.equals(mListValues[1])).isTrue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,13 +142,11 @@ public class ZenModesBackendTest {
|
|||||||
.setType(AutomaticZenRule.TYPE_OTHER)
|
.setType(AutomaticZenRule.TYPE_OTHER)
|
||||||
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
|
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
|
||||||
.setZenPolicy(ZenAdapters.notificationPolicyToZenPolicy(dndPolicy))
|
.setZenPolicy(ZenAdapters.notificationPolicyToZenPolicy(dndPolicy))
|
||||||
.setTriggerDescription(
|
|
||||||
mContext.getString(R.string.zen_mode_settings_summary))
|
|
||||||
.setManualInvocationAllowed(true)
|
.setManualInvocationAllowed(true)
|
||||||
.build(),
|
.build(),
|
||||||
false),
|
false),
|
||||||
new ZenMode("rule1", ZEN_RULE, false),
|
new ZenMode("rule2", rule2, false),
|
||||||
new ZenMode("rule2", rule2, false))
|
new ZenMode("rule1", ZEN_RULE, false))
|
||||||
.inOrder();
|
.inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,8 +165,6 @@ public class ZenModesBackendTest {
|
|||||||
.setType(AutomaticZenRule.TYPE_OTHER)
|
.setType(AutomaticZenRule.TYPE_OTHER)
|
||||||
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
|
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
|
||||||
.setZenPolicy(ZenAdapters.notificationPolicyToZenPolicy(dndPolicy))
|
.setZenPolicy(ZenAdapters.notificationPolicyToZenPolicy(dndPolicy))
|
||||||
.setTriggerDescription(
|
|
||||||
mContext.getString(R.string.zen_mode_settings_summary))
|
|
||||||
.setManualInvocationAllowed(true)
|
.setManualInvocationAllowed(true)
|
||||||
.build(), false));
|
.build(), false));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* 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.notification.modes;
|
||||||
|
|
||||||
|
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.AutomaticZenRule;
|
||||||
|
import android.app.Flags;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.platform.test.annotations.DisableFlags;
|
||||||
|
import android.platform.test.annotations.EnableFlags;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
|
import android.service.notification.ZenPolicy;
|
||||||
|
|
||||||
|
import com.android.settingslib.search.SearchIndexableRaw;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class ZenModesListPreferenceControllerTest {
|
||||||
|
private static final String TEST_MODE_ID = "test_mode";
|
||||||
|
private static final String TEST_MODE_NAME = "Test Mode";
|
||||||
|
private static final ZenMode TEST_MODE = new ZenMode(
|
||||||
|
TEST_MODE_ID,
|
||||||
|
new AutomaticZenRule.Builder(TEST_MODE_NAME, Uri.parse("test_uri"))
|
||||||
|
.setType(AutomaticZenRule.TYPE_BEDTIME)
|
||||||
|
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
|
||||||
|
.setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
|
||||||
|
.build(),
|
||||||
|
false);
|
||||||
|
|
||||||
|
private static final ZenMode TEST_MANUAL_MODE = ZenMode.manualDndMode(
|
||||||
|
new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY)
|
||||||
|
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
|
||||||
|
.setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
|
||||||
|
.build(),
|
||||||
|
false);
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
|
||||||
|
SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ZenModesBackend mBackend;
|
||||||
|
|
||||||
|
private ZenModesListPreferenceController mPrefController;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mContext = RuntimeEnvironment.application;
|
||||||
|
|
||||||
|
mPrefController = new ZenModesListPreferenceController(mContext, null, mBackend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisableFlags(Flags.FLAG_MODES_UI)
|
||||||
|
public void testModesUiOff_notAvailableAndNoSearchData() {
|
||||||
|
// There exist modes
|
||||||
|
when(mBackend.getModes()).thenReturn(List.of(TEST_MANUAL_MODE, TEST_MODE));
|
||||||
|
|
||||||
|
assertThat(mPrefController.isAvailable()).isFalse();
|
||||||
|
List<SearchIndexableRaw> data = new ArrayList<>();
|
||||||
|
mPrefController.updateDynamicRawDataToIndex(data);
|
||||||
|
assertThat(data).isEmpty(); // despite existence of modes
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@EnableFlags(Flags.FLAG_MODES_UI)
|
||||||
|
public void testUpdateDynamicRawDataToIndex_empty() {
|
||||||
|
// Case of no modes.
|
||||||
|
when(mBackend.getModes()).thenReturn(new ArrayList<>());
|
||||||
|
|
||||||
|
List<SearchIndexableRaw> data = new ArrayList<>();
|
||||||
|
mPrefController.updateDynamicRawDataToIndex(data);
|
||||||
|
assertThat(data).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@EnableFlags(Flags.FLAG_MODES_UI)
|
||||||
|
public void testUpdateDynamicRawDataToIndex_oneMode() {
|
||||||
|
// One mode present, confirm it's the correct one
|
||||||
|
when(mBackend.getModes()).thenReturn(List.of(TEST_MODE));
|
||||||
|
|
||||||
|
List<SearchIndexableRaw> data = new ArrayList<>();
|
||||||
|
mPrefController.updateDynamicRawDataToIndex(data);
|
||||||
|
assertThat(data).hasSize(1);
|
||||||
|
|
||||||
|
SearchIndexableRaw item = data.get(0);
|
||||||
|
assertThat(item.key).isEqualTo(TEST_MODE_ID);
|
||||||
|
assertThat(item.title).isEqualTo(TEST_MODE_NAME);
|
||||||
|
|
||||||
|
// Changing mode data so there's a different one mode doesn't keep any previous data
|
||||||
|
// (and setting that state up in the caller)
|
||||||
|
when(mBackend.getModes()).thenReturn(List.of(TEST_MANUAL_MODE));
|
||||||
|
List<SearchIndexableRaw> newData = new ArrayList<>();
|
||||||
|
mPrefController.updateDynamicRawDataToIndex(newData);
|
||||||
|
assertThat(newData).hasSize(1);
|
||||||
|
|
||||||
|
SearchIndexableRaw newItem = newData.get(0);
|
||||||
|
assertThat(newItem.key).isEqualTo(ZenMode.MANUAL_DND_MODE_ID);
|
||||||
|
assertThat(newItem.title).isEqualTo("Do Not Disturb"); // set above
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@EnableFlags(Flags.FLAG_MODES_UI)
|
||||||
|
public void testUpdateDynamicRawDataToIndex_multipleModes() {
|
||||||
|
when(mBackend.getModes()).thenReturn(List.of(TEST_MANUAL_MODE, TEST_MODE));
|
||||||
|
|
||||||
|
List<SearchIndexableRaw> data = new ArrayList<>();
|
||||||
|
mPrefController.updateDynamicRawDataToIndex(data);
|
||||||
|
assertThat(data).hasSize(2);
|
||||||
|
|
||||||
|
// Should keep the order presented by getModes()
|
||||||
|
SearchIndexableRaw item0 = data.get(0);
|
||||||
|
assertThat(item0.key).isEqualTo(ZenMode.MANUAL_DND_MODE_ID);
|
||||||
|
assertThat(item0.title).isEqualTo("Do Not Disturb"); // set above
|
||||||
|
|
||||||
|
SearchIndexableRaw item1 = data.get(1);
|
||||||
|
assertThat(item1.key).isEqualTo(TEST_MODE_ID);
|
||||||
|
assertThat(item1.title).isEqualTo(TEST_MODE_NAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -91,6 +91,7 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
|
|||||||
object : OrientationInteractor {
|
object : OrientationInteractor {
|
||||||
override val orientation: Flow<Int> = flowOf(Configuration.ORIENTATION_LANDSCAPE)
|
override val orientation: Flow<Int> = flowOf(Configuration.ORIENTATION_LANDSCAPE)
|
||||||
override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
|
override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
|
||||||
|
override val rotationFromDefault: Flow<Int> = rotation
|
||||||
|
|
||||||
override fun getRotationFromDefault(rotation: Int): Int = rotation
|
override fun getRotationFromDefault(rotation: Int): Int = rotation
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ package com.android.settings.tests.screenshot.biometrics.fingerprint.fragment
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.RfpsEnrollFindSensorFragment
|
||||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
|
||||||
import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector
|
|
||||||
import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector.Companion.BiometricFragmentScreenShotRule
|
import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector.Companion.BiometricFragmentScreenShotRule
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -28,10 +26,7 @@ import platform.test.screenshot.FragmentScreenshotTestRule
|
|||||||
import platform.test.screenshot.ViewScreenshotTestRule.Mode
|
import platform.test.screenshot.ViewScreenshotTestRule.Mode
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class FingerprintEnrollFindSensorScreenshotTest {
|
class RfpsEnrollFindSensorScreenshotTest {
|
||||||
private val injector: Injector =
|
|
||||||
Injector(FingerprintNavigationStep.Education(Injector.interactor.sensorProp))
|
|
||||||
|
|
||||||
@Rule @JvmField var rule: FragmentScreenshotTestRule = BiometricFragmentScreenShotRule()
|
@Rule @JvmField var rule: FragmentScreenshotTestRule = BiometricFragmentScreenShotRule()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -39,7 +34,7 @@ class FingerprintEnrollFindSensorScreenshotTest {
|
|||||||
rule.screenshotTest(
|
rule.screenshotTest(
|
||||||
"fp_enroll_find_sensor",
|
"fp_enroll_find_sensor",
|
||||||
Mode.MatchSize,
|
Mode.MatchSize,
|
||||||
FingerprintEnrollFindSensorV2Fragment(injector.fingerprintSensor.sensorType, injector.factory),
|
RfpsEnrollFindSensorFragment(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,10 +66,11 @@ class SubscriptionRepositoryTest {
|
|||||||
@Test
|
@Test
|
||||||
fun isSubscriptionEnabledFlow_enabled() = runBlocking {
|
fun isSubscriptionEnabledFlow_enabled() = runBlocking {
|
||||||
mockSubscriptionManager.stub {
|
mockSubscriptionManager.stub {
|
||||||
on { isSubscriptionEnabled(SUB_ID_1) } doReturn true
|
on { isSubscriptionEnabled(SUB_ID_IN_SLOT_0) } doReturn true
|
||||||
}
|
}
|
||||||
|
|
||||||
val isEnabled = repository.isSubscriptionEnabledFlow(SUB_ID_1).firstWithTimeoutOrNull()
|
val isEnabled =
|
||||||
|
repository.isSubscriptionEnabledFlow(SUB_ID_IN_SLOT_0).firstWithTimeoutOrNull()
|
||||||
|
|
||||||
assertThat(isEnabled).isTrue()
|
assertThat(isEnabled).isTrue()
|
||||||
}
|
}
|
||||||
@@ -94,21 +95,24 @@ class SubscriptionRepositoryTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getSelectableSubscriptionInfoList_sortedBySubId() {
|
fun getSelectableSubscriptionInfoList_sortedBySimSlotIndex() {
|
||||||
mockSubscriptionManager.stub {
|
mockSubscriptionManager.stub {
|
||||||
on { getAvailableSubscriptionInfoList() } doReturn listOf(
|
on { getAvailableSubscriptionInfoList() } doReturn listOf(
|
||||||
SubscriptionInfo.Builder().apply {
|
SubscriptionInfo.Builder().apply {
|
||||||
setId(SUB_ID_2)
|
setSimSlotIndex(SIM_SLOT_INDEX_0)
|
||||||
|
setId(SUB_ID_IN_SLOT_0)
|
||||||
}.build(),
|
}.build(),
|
||||||
SubscriptionInfo.Builder().apply {
|
SubscriptionInfo.Builder().apply {
|
||||||
setId(SUB_ID_1)
|
setSimSlotIndex(SIM_SLOT_INDEX_1)
|
||||||
|
setId(SUB_ID_IN_SLOT_1)
|
||||||
}.build(),
|
}.build(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val subInfos = context.getSelectableSubscriptionInfoList()
|
val subInfos = context.getSelectableSubscriptionInfoList()
|
||||||
|
|
||||||
assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_1, SUB_ID_2).inOrder()
|
assertThat(subInfos.map { it.simSlotIndex })
|
||||||
|
.containsExactly(SIM_SLOT_INDEX_0, SIM_SLOT_INDEX_1).inOrder()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -116,20 +120,21 @@ class SubscriptionRepositoryTest {
|
|||||||
mockSubscriptionManager.stub {
|
mockSubscriptionManager.stub {
|
||||||
on { getAvailableSubscriptionInfoList() } doReturn listOf(
|
on { getAvailableSubscriptionInfoList() } doReturn listOf(
|
||||||
SubscriptionInfo.Builder().apply {
|
SubscriptionInfo.Builder().apply {
|
||||||
setId(SUB_ID_1)
|
setSimSlotIndex(SubscriptionManager.INVALID_SIM_SLOT_INDEX)
|
||||||
|
setId(SUB_ID_3_NOT_IN_SLOT)
|
||||||
setGroupUuid(GROUP_UUID)
|
setGroupUuid(GROUP_UUID)
|
||||||
}.build(),
|
}.build(),
|
||||||
SubscriptionInfo.Builder().apply {
|
SubscriptionInfo.Builder().apply {
|
||||||
setId(SUB_ID_2)
|
setSimSlotIndex(SIM_SLOT_INDEX_0)
|
||||||
|
setId(SUB_ID_IN_SLOT_0)
|
||||||
setGroupUuid(GROUP_UUID)
|
setGroupUuid(GROUP_UUID)
|
||||||
setSimSlotIndex(SIM_SLOT_INDEX)
|
|
||||||
}.build(),
|
}.build(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val subInfos = context.getSelectableSubscriptionInfoList()
|
val subInfos = context.getSelectableSubscriptionInfoList()
|
||||||
|
|
||||||
assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_2)
|
assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_IN_SLOT_0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -137,11 +142,11 @@ class SubscriptionRepositoryTest {
|
|||||||
mockSubscriptionManager.stub {
|
mockSubscriptionManager.stub {
|
||||||
on { getAvailableSubscriptionInfoList() } doReturn listOf(
|
on { getAvailableSubscriptionInfoList() } doReturn listOf(
|
||||||
SubscriptionInfo.Builder().apply {
|
SubscriptionInfo.Builder().apply {
|
||||||
setId(SUB_ID_2)
|
setId(SUB_ID_4_NOT_IN_SLOT)
|
||||||
setGroupUuid(GROUP_UUID)
|
setGroupUuid(GROUP_UUID)
|
||||||
}.build(),
|
}.build(),
|
||||||
SubscriptionInfo.Builder().apply {
|
SubscriptionInfo.Builder().apply {
|
||||||
setId(SUB_ID_1)
|
setId(SUB_ID_3_NOT_IN_SLOT)
|
||||||
setGroupUuid(GROUP_UUID)
|
setGroupUuid(GROUP_UUID)
|
||||||
}.build(),
|
}.build(),
|
||||||
)
|
)
|
||||||
@@ -149,16 +154,16 @@ class SubscriptionRepositoryTest {
|
|||||||
|
|
||||||
val subInfos = context.getSelectableSubscriptionInfoList()
|
val subInfos = context.getSelectableSubscriptionInfoList()
|
||||||
|
|
||||||
assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_1)
|
assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_3_NOT_IN_SLOT)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun phoneNumberFlow() = runBlocking {
|
fun phoneNumberFlow() = runBlocking {
|
||||||
mockSubscriptionManager.stub {
|
mockSubscriptionManager.stub {
|
||||||
on { getPhoneNumber(SUB_ID_1) } doReturn NUMBER_1
|
on { getPhoneNumber(SUB_ID_IN_SLOT_1) } doReturn NUMBER_1
|
||||||
}
|
}
|
||||||
val subInfo = SubscriptionInfo.Builder().apply {
|
val subInfo = SubscriptionInfo.Builder().apply {
|
||||||
setId(SUB_ID_1)
|
setId(SUB_ID_IN_SLOT_1)
|
||||||
setMcc(MCC)
|
setMcc(MCC)
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
@@ -168,10 +173,13 @@ class SubscriptionRepositoryTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val SUB_ID_1 = 1
|
const val SIM_SLOT_INDEX_0 = 0
|
||||||
const val SUB_ID_2 = 2
|
const val SUB_ID_IN_SLOT_0 = 2
|
||||||
|
const val SIM_SLOT_INDEX_1 = 1
|
||||||
|
const val SUB_ID_IN_SLOT_1 = 1
|
||||||
|
const val SUB_ID_3_NOT_IN_SLOT = 3
|
||||||
|
const val SUB_ID_4_NOT_IN_SLOT = 4
|
||||||
val GROUP_UUID = UUID.randomUUID().toString()
|
val GROUP_UUID = UUID.randomUUID().toString()
|
||||||
const val SIM_SLOT_INDEX = 1
|
|
||||||
const val NUMBER_1 = "000000001"
|
const val NUMBER_1 = "000000001"
|
||||||
const val MCC = "310"
|
const val MCC = "310"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ class WifiControlAppListModelTest {
|
|||||||
private class FakeAppOpsController(private val fakeMode: Int) : IAppOpsController {
|
private class FakeAppOpsController(private val fakeMode: Int) : IAppOpsController {
|
||||||
var setAllowedCalledWith: Boolean? = null
|
var setAllowedCalledWith: Boolean? = null
|
||||||
|
|
||||||
override val mode = flowOf(fakeMode)
|
override val modeFlow = flowOf(fakeMode)
|
||||||
|
|
||||||
override fun setAllowed(allowed: Boolean) {
|
override fun setAllowed(allowed: Boolean) {
|
||||||
setAllowedCalledWith = allowed
|
setAllowedCalledWith = allowed
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import android.content.Context
|
|||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -37,8 +38,12 @@ class AllFilesAccessTest {
|
|||||||
assertThat(listModel.pageTitleResId).isEqualTo(R.string.manage_external_storage_title)
|
assertThat(listModel.pageTitleResId).isEqualTo(R.string.manage_external_storage_title)
|
||||||
assertThat(listModel.switchTitleResId).isEqualTo(R.string.permit_manage_external_storage)
|
assertThat(listModel.switchTitleResId).isEqualTo(R.string.permit_manage_external_storage)
|
||||||
assertThat(listModel.footerResId).isEqualTo(R.string.allow_manage_external_storage_description)
|
assertThat(listModel.footerResId).isEqualTo(R.string.allow_manage_external_storage_description)
|
||||||
assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE)
|
assertThat(listModel.appOps).isEqualTo(
|
||||||
|
AppOps(
|
||||||
|
op = AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE,
|
||||||
|
setModeByUid = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
assertThat(listModel.permission).isEqualTo(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
|
assertThat(listModel.permission).isEqualTo(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
|
||||||
assertThat(listModel.setModeByUid).isTrue()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,7 @@ import androidx.test.core.app.ApplicationProvider
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
@@ -37,8 +38,12 @@ class LongBackgroundTasksAppsTest {
|
|||||||
assertThat(listModel.pageTitleResId).isEqualTo(R.string.long_background_tasks_title)
|
assertThat(listModel.pageTitleResId).isEqualTo(R.string.long_background_tasks_title)
|
||||||
assertThat(listModel.switchTitleResId).isEqualTo(R.string.long_background_tasks_switch_title)
|
assertThat(listModel.switchTitleResId).isEqualTo(R.string.long_background_tasks_switch_title)
|
||||||
assertThat(listModel.footerResId).isEqualTo(R.string.long_background_tasks_footer_title)
|
assertThat(listModel.footerResId).isEqualTo(R.string.long_background_tasks_footer_title)
|
||||||
assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_RUN_USER_INITIATED_JOBS)
|
assertThat(listModel.appOps).isEqualTo(
|
||||||
|
AppOps(
|
||||||
|
op = AppOpsManager.OP_RUN_USER_INITIATED_JOBS,
|
||||||
|
setModeByUid = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
assertThat(listModel.permission).isEqualTo(Manifest.permission.RUN_USER_INITIATED_JOBS)
|
assertThat(listModel.permission).isEqualTo(Manifest.permission.RUN_USER_INITIATED_JOBS)
|
||||||
assertThat(listModel.setModeByUid).isTrue()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@ import android.content.Context
|
|||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -37,8 +38,12 @@ class MediaManagementAppsTest {
|
|||||||
assertThat(listModel.pageTitleResId).isEqualTo(R.string.media_management_apps_title)
|
assertThat(listModel.pageTitleResId).isEqualTo(R.string.media_management_apps_title)
|
||||||
assertThat(listModel.switchTitleResId).isEqualTo(R.string.media_management_apps_toggle_label)
|
assertThat(listModel.switchTitleResId).isEqualTo(R.string.media_management_apps_toggle_label)
|
||||||
assertThat(listModel.footerResId).isEqualTo(R.string.media_management_apps_description)
|
assertThat(listModel.footerResId).isEqualTo(R.string.media_management_apps_description)
|
||||||
assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_MANAGE_MEDIA)
|
assertThat(listModel.appOps).isEqualTo(
|
||||||
|
AppOps(
|
||||||
|
op = AppOpsManager.OP_MANAGE_MEDIA,
|
||||||
|
setModeByUid = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
assertThat(listModel.permission).isEqualTo(Manifest.permission.MANAGE_MEDIA)
|
assertThat(listModel.permission).isEqualTo(Manifest.permission.MANAGE_MEDIA)
|
||||||
assertThat(listModel.setModeByUid).isTrue()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||||||
import com.android.media.flags.Flags
|
import com.android.media.flags.Flags
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.testutils.FakeFeatureFactory
|
import com.android.settings.testutils.FakeFeatureFactory
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.model.app.IAppOpsController
|
import com.android.settingslib.spaprivileged.model.app.IAppOpsController
|
||||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
@@ -74,9 +75,13 @@ class MediaRoutingControlTest {
|
|||||||
assertThat(listModel.pageTitleResId).isEqualTo(R.string.media_routing_control_title)
|
assertThat(listModel.pageTitleResId).isEqualTo(R.string.media_routing_control_title)
|
||||||
assertThat(listModel.switchTitleResId).isEqualTo(R.string.allow_media_routing_control)
|
assertThat(listModel.switchTitleResId).isEqualTo(R.string.allow_media_routing_control)
|
||||||
assertThat(listModel.footerResId).isEqualTo(R.string.allow_media_routing_description)
|
assertThat(listModel.footerResId).isEqualTo(R.string.allow_media_routing_description)
|
||||||
assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_MEDIA_ROUTING_CONTROL)
|
assertThat(listModel.appOps).isEqualTo(
|
||||||
|
AppOps(
|
||||||
|
op = AppOpsManager.OP_MEDIA_ROUTING_CONTROL,
|
||||||
|
setModeByUid = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
assertThat(listModel.permission).isEqualTo(Manifest.permission.MEDIA_ROUTING_CONTROL)
|
assertThat(listModel.permission).isEqualTo(Manifest.permission.MEDIA_ROUTING_CONTROL)
|
||||||
assertThat(listModel.setModeByUid).isTrue()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -223,13 +228,13 @@ class MediaRoutingControlTest {
|
|||||||
|
|
||||||
private class FakeAppOpsController(fakeMode: Int) : IAppOpsController {
|
private class FakeAppOpsController(fakeMode: Int) : IAppOpsController {
|
||||||
|
|
||||||
override val mode = MutableStateFlow(fakeMode)
|
override val modeFlow = MutableStateFlow(fakeMode)
|
||||||
|
|
||||||
override fun setAllowed(allowed: Boolean) {
|
override fun setAllowed(allowed: Boolean) {
|
||||||
mode.value = if (allowed) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
|
modeFlow.value = if (allowed) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMode(): Int = mode.value
|
override fun getMode(): Int = modeFlow.value
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import android.os.DeadSystemRuntimeException
|
|||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.android.settingslib.spaprivileged.model.app.AppOpsController
|
import com.android.settingslib.spaprivileged.model.app.AppOpsController
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
@@ -179,7 +180,7 @@ class PictureInPictureTest {
|
|||||||
appOpsController = AppOpsController(
|
appOpsController = AppOpsController(
|
||||||
context = context,
|
context = context,
|
||||||
app = PICTURE_IN_PICTURE_APP,
|
app = PICTURE_IN_PICTURE_APP,
|
||||||
op = AppOpsManager.OP_PICTURE_IN_PICTURE,
|
appOps = AppOps(AppOpsManager.OP_PICTURE_IN_PICTURE),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.app.AppOpsManager
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppOps
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -35,8 +36,12 @@ class TurnScreenOnAppsTest {
|
|||||||
assertThat(listModel.pageTitleResId).isEqualTo(com.android.settingslib.R.string.turn_screen_on_title)
|
assertThat(listModel.pageTitleResId).isEqualTo(com.android.settingslib.R.string.turn_screen_on_title)
|
||||||
assertThat(listModel.switchTitleResId).isEqualTo(com.android.settingslib.R.string.allow_turn_screen_on)
|
assertThat(listModel.switchTitleResId).isEqualTo(com.android.settingslib.R.string.allow_turn_screen_on)
|
||||||
assertThat(listModel.footerResId).isEqualTo(com.android.settingslib.R.string.allow_turn_screen_on_description)
|
assertThat(listModel.footerResId).isEqualTo(com.android.settingslib.R.string.allow_turn_screen_on_description)
|
||||||
assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_TURN_SCREEN_ON)
|
assertThat(listModel.appOps).isEqualTo(
|
||||||
|
AppOps(
|
||||||
|
op = AppOpsManager.OP_TURN_SCREEN_ON,
|
||||||
|
setModeByUid = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
assertThat(listModel.permission).isEqualTo(Manifest.permission.TURN_SCREEN_ON)
|
assertThat(listModel.permission).isEqualTo(Manifest.permission.TURN_SCREEN_ON)
|
||||||
assertThat(listModel.setModeByUid).isTrue()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,18 +21,16 @@ android_test {
|
|||||||
static_libs: [
|
static_libs: [
|
||||||
"aconfig_settings_flags_lib",
|
"aconfig_settings_flags_lib",
|
||||||
"androidx.arch.core_core-testing",
|
"androidx.arch.core_core-testing",
|
||||||
"androidx.test.core",
|
"androidx.lifecycle_lifecycle-runtime-testing",
|
||||||
"androidx.test.espresso.core",
|
"androidx.test.espresso.core",
|
||||||
"androidx.test.rules",
|
|
||||||
"androidx.test.ext.junit",
|
"androidx.test.ext.junit",
|
||||||
"androidx.preference_preference",
|
"androidx.test.rules",
|
||||||
"flag-junit",
|
"flag-junit",
|
||||||
"mockito-target-minus-junit4",
|
"mockito-target-minus-junit4",
|
||||||
"platform-test-annotations",
|
"platform-test-annotations",
|
||||||
"platform-test-rules",
|
"platform-test-rules",
|
||||||
"truth",
|
"truth",
|
||||||
"kotlinx_coroutines_test",
|
"kotlinx_coroutines_test",
|
||||||
"flag-junit",
|
|
||||||
"Settings-testutils2",
|
"Settings-testutils2",
|
||||||
"MediaDrmSettingsFlagsLib",
|
"MediaDrmSettingsFlagsLib",
|
||||||
// Don't add SettingsLib libraries here - you can use them directly as they are in the
|
// Don't add SettingsLib libraries here - you can use them directly as they are in the
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ import android.os.Handler
|
|||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
import com.android.settings.biometrics.GatekeeperPasswordProvider
|
||||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
||||||
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractor
|
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.Default
|
import com.android.settings.biometrics.fingerprint2.lib.model.Default
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
|
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
|
||||||
@@ -82,10 +82,6 @@ class FingerprintManagerInteractorTest {
|
|||||||
@Mock private lateinit var gateKeeperPasswordProvider: GatekeeperPasswordProvider
|
@Mock private lateinit var gateKeeperPasswordProvider: GatekeeperPasswordProvider
|
||||||
|
|
||||||
private var testScope = TestScope(backgroundDispatcher)
|
private var testScope = TestScope(backgroundDispatcher)
|
||||||
private var pressToAuthInteractor =
|
|
||||||
object : PressToAuthInteractor {
|
|
||||||
override val isEnabled = flowOf(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
@@ -113,9 +109,12 @@ class FingerprintManagerInteractorTest {
|
|||||||
fingerprintManager,
|
fingerprintManager,
|
||||||
fingerprintSensorRepository,
|
fingerprintSensorRepository,
|
||||||
gateKeeperPasswordProvider,
|
gateKeeperPasswordProvider,
|
||||||
pressToAuthInteractor,
|
FingerprintEnrollInteractorImpl(
|
||||||
|
context,
|
||||||
|
FingerprintEnrollOptions.Builder().build(),
|
||||||
|
fingerprintManager,
|
||||||
Default,
|
Default,
|
||||||
Intent(),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ class FingerprintEnrollFindSensorViewModelV2Test {
|
|||||||
object : OrientationInteractor {
|
object : OrientationInteractor {
|
||||||
override val orientation: Flow<Int> = flowOf(Configuration.ORIENTATION_LANDSCAPE)
|
override val orientation: Flow<Int> = flowOf(Configuration.ORIENTATION_LANDSCAPE)
|
||||||
override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
|
override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
|
||||||
|
override val rotationFromDefault: Flow<Int> = flowOf(Surface.ROTATION_0)
|
||||||
override fun getRotationFromDefault(rotation: Int): Int = rotation
|
override fun getRotationFromDefault(rotation: Int): Int = rotation
|
||||||
}
|
}
|
||||||
underTest =
|
underTest =
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import android.telephony.TelephonyManager;
|
|||||||
|
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.lifecycle.testing.TestLifecycleOwner;
|
||||||
import androidx.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
@@ -60,7 +61,6 @@ import com.android.settings.network.telephony.TelephonyConstants.TelephonyManage
|
|||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
@@ -113,6 +113,7 @@ public class EnabledNetworkModePreferenceControllerTest {
|
|||||||
doReturn(mPersistableBundle).when(mCarrierConfigCache).getConfig();
|
doReturn(mPersistableBundle).when(mCarrierConfigCache).getConfig();
|
||||||
doReturn(mPersistableBundle).when(mCarrierConfigCache).getConfigForSubId(SUB_ID);
|
doReturn(mPersistableBundle).when(mCarrierConfigCache).getConfigForSubId(SUB_ID);
|
||||||
mPersistableBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
|
mPersistableBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
|
||||||
|
mPersistableBundle.putBoolean(CarrierConfigManager.KEY_PREFER_3G_VISIBILITY_BOOL, true);
|
||||||
mPreference = new ListPreference(mContext);
|
mPreference = new ListPreference(mContext);
|
||||||
mController = new EnabledNetworkModePreferenceController(mContext, KEY);
|
mController = new EnabledNetworkModePreferenceController(mContext, KEY);
|
||||||
mockAllowedNetworkTypes(ALLOWED_ALL_NETWORK_TYPE);
|
mockAllowedNetworkTypes(ALLOWED_ALL_NETWORK_TYPE);
|
||||||
@@ -347,7 +348,6 @@ public class EnabledNetworkModePreferenceControllerTest {
|
|||||||
|
|
||||||
@UiThreadTest
|
@UiThreadTest
|
||||||
@Test
|
@Test
|
||||||
@Ignore("b/337418033")
|
|
||||||
public void updateState_updateByNetworkMode() {
|
public void updateState_updateByNetworkMode() {
|
||||||
mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA);
|
mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA);
|
||||||
|
|
||||||
@@ -379,7 +379,6 @@ public class EnabledNetworkModePreferenceControllerTest {
|
|||||||
|
|
||||||
@UiThreadTest
|
@UiThreadTest
|
||||||
@Test
|
@Test
|
||||||
@Ignore("b/337418033")
|
|
||||||
public void onPreferenceChange_updateSuccess() {
|
public void onPreferenceChange_updateSuccess() {
|
||||||
mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA);
|
mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA);
|
||||||
doReturn(true).when(mTelephonyManager).setPreferredNetworkTypeBitmask(
|
doReturn(true).when(mTelephonyManager).setPreferredNetworkTypeBitmask(
|
||||||
@@ -387,6 +386,7 @@ public class EnabledNetworkModePreferenceControllerTest {
|
|||||||
TelephonyManagerConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA));
|
TelephonyManagerConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA));
|
||||||
|
|
||||||
mController.updateState(mPreference);
|
mController.updateState(mPreference);
|
||||||
|
mController.onViewCreated(new TestLifecycleOwner());
|
||||||
mController.onPreferenceChange(mPreference,
|
mController.onPreferenceChange(mPreference,
|
||||||
String.valueOf(TelephonyManagerConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA));
|
String.valueOf(TelephonyManagerConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA));
|
||||||
|
|
||||||
@@ -396,13 +396,13 @@ public class EnabledNetworkModePreferenceControllerTest {
|
|||||||
|
|
||||||
@UiThreadTest
|
@UiThreadTest
|
||||||
@Test
|
@Test
|
||||||
@Ignore("b/337418033")
|
|
||||||
public void onPreferenceChange_updateFail() {
|
public void onPreferenceChange_updateFail() {
|
||||||
mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA);
|
mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA);
|
||||||
doReturn(false).when(mTelephonyManager).setPreferredNetworkTypeBitmask(
|
doReturn(false).when(mTelephonyManager).setPreferredNetworkTypeBitmask(
|
||||||
getRafFromNetworkType(TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA));
|
getRafFromNetworkType(TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA));
|
||||||
|
|
||||||
mController.updateState(mPreference);
|
mController.updateState(mPreference);
|
||||||
|
mController.onViewCreated(new TestLifecycleOwner());
|
||||||
mController.onPreferenceChange(mPreference,
|
mController.onPreferenceChange(mPreference,
|
||||||
String.valueOf(TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA));
|
String.valueOf(TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA));
|
||||||
|
|
||||||
@@ -412,7 +412,6 @@ public class EnabledNetworkModePreferenceControllerTest {
|
|||||||
|
|
||||||
@UiThreadTest
|
@UiThreadTest
|
||||||
@Test
|
@Test
|
||||||
@Ignore("b/337418033")
|
|
||||||
public void preferredNetworkModeNotification_preferenceUpdates() {
|
public void preferredNetworkModeNotification_preferenceUpdates() {
|
||||||
|
|
||||||
final PreferenceManager preferenceManager = new PreferenceManager(mContext);
|
final PreferenceManager preferenceManager = new PreferenceManager(mContext);
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
@@ -66,6 +65,7 @@ public class CdmaSystemSelectPreferenceControllerTest {
|
|||||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
|
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
|
||||||
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
|
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
|
||||||
|
when(mTelephonyManager.getPhoneType()).thenReturn(TelephonyManager.PHONE_TYPE_CDMA);
|
||||||
|
|
||||||
mPreference = new ListPreference(mContext);
|
mPreference = new ListPreference(mContext);
|
||||||
mController = new CdmaSystemSelectPreferenceController(mContext, "mobile_data");
|
mController = new CdmaSystemSelectPreferenceController(mContext, "mobile_data");
|
||||||
@@ -100,7 +100,6 @@ public class CdmaSystemSelectPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("b/337417544")
|
|
||||||
public void updateState_stateHome_displayHome() {
|
public void updateState_stateHome_displayHome() {
|
||||||
doReturn(TelephonyManager.CDMA_ROAMING_MODE_HOME).when(
|
doReturn(TelephonyManager.CDMA_ROAMING_MODE_HOME).when(
|
||||||
mTelephonyManager).getCdmaRoamingMode();
|
mTelephonyManager).getCdmaRoamingMode();
|
||||||
@@ -112,7 +111,6 @@ public class CdmaSystemSelectPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("b/337417897")
|
|
||||||
public void updateState_LteGSMWcdma_disabled() {
|
public void updateState_LteGSMWcdma_disabled() {
|
||||||
doReturn(TelephonyManager.CDMA_ROAMING_MODE_HOME).when(
|
doReturn(TelephonyManager.CDMA_ROAMING_MODE_HOME).when(
|
||||||
mTelephonyManager).getCdmaRoamingMode();
|
mTelephonyManager).getCdmaRoamingMode();
|
||||||
@@ -126,7 +124,6 @@ public class CdmaSystemSelectPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("b/337417917")
|
|
||||||
public void updateState_stateOther_resetToDefault() {
|
public void updateState_stateOther_resetToDefault() {
|
||||||
Settings.Global.putInt(mContext.getContentResolver(),
|
Settings.Global.putInt(mContext.getContentResolver(),
|
||||||
Settings.Global.CDMA_ROAMING_MODE,
|
Settings.Global.CDMA_ROAMING_MODE,
|
||||||
|
|||||||
Reference in New Issue
Block a user