Snap for 11889377 from 5d062852c9 to 24Q3-release
Change-Id: Ia34425ceeff4bcf0603aa5c8b60f82bf40fa5b54
This commit is contained in:
@@ -14,87 +14,65 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
<!-- This is used to grab style attributes and apply them
|
||||
to this layout -->
|
||||
<com.google.android.setupdesign.GlifLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/udfps_layout"
|
||||
android:id="@+id/glif_layout"
|
||||
style="?attr/fingerprint_layout_theme"
|
||||
android:layout_width="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- This is used to grab style attributes and apply them
|
||||
to this layout -->
|
||||
<com.google.android.setupdesign.GlifLayout
|
||||
android:id="@+id/dummy_glif_layout"
|
||||
style="?attr/fingerprint_layout_theme"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sud_layout_icon"
|
||||
style="@style/SudGlifIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="fitStart"
|
||||
android:src="@drawable/ic_lock" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/SudGlifHeaderTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="2"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
style="@style/SudDescription.Glif"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="3"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
/>
|
||||
|
||||
|
||||
<com.airbnb.lottie.LottieAnimationView
|
||||
android:id="@+id/illustration_lottie"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:scaleType="centerInside"
|
||||
android:visibility="gone"
|
||||
app:lottie_autoPlay="true"
|
||||
app:lottie_loop="true"
|
||||
app:lottie_speed=".85"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_container"
|
||||
style="@style/SudContentFrame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_horizontal|bottom"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<include layout="@layout/fingerprint_v2_udfps_enroll_view" />
|
||||
</FrameLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center|bottom"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_horizontal|bottom"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
>
|
||||
|
||||
<com.airbnb.lottie.LottieAnimationView
|
||||
android:id="@+id/illustration_lottie"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:scaleType="centerInside"
|
||||
android:visibility="gone"
|
||||
app:lottie_autoPlay="true"
|
||||
app:lottie_loop="true"
|
||||
app:lottie_speed=".85"
|
||||
/>
|
||||
|
||||
<include layout="@layout/fingerprint_v2_udfps_enroll_view" />
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</com.google.android.setupdesign.GlifLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="172dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
@@ -37,7 +38,6 @@
|
||||
android:id="@+id/usage_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="12dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView android:id="@+id/data_usage_view"
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/udfps_layout"
|
||||
style="?attr/fingerprint_layout_theme"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
@@ -27,65 +26,57 @@
|
||||
<!-- This is used to grab style attributes and apply them
|
||||
to this layout -->
|
||||
<com.google.android.setupdesign.GlifLayout
|
||||
android:id="@+id/dummy_glif_layout"
|
||||
android:id="@+id/glif_layout"
|
||||
style="?attr/fingerprint_layout_theme"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
android:layout_weight="5"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sud_layout_icon"
|
||||
style="@style/SudGlifIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="fitStart"
|
||||
android:src="@drawable/ic_lock" />
|
||||
<LinearLayout
|
||||
style="@style/SudContentFrame"
|
||||
android:id="@+id/sud_content_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/SudGlifHeaderTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="2"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
style="@style/SudDescription.Glif"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="3"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
/>
|
||||
|
||||
|
||||
<com.airbnb.lottie.LottieAnimationView
|
||||
android:id="@+id/illustration_lottie"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:scaleType="centerInside"
|
||||
app:lottie_autoPlay="true"
|
||||
app:lottie_loop="true"
|
||||
app:lottie_speed=".85" />
|
||||
<com.airbnb.lottie.LottieAnimationView
|
||||
android:id="@+id/illustration_lottie"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:scaleType="centerInside"
|
||||
app:lottie_autoPlay="true"
|
||||
app:lottie_loop="true"
|
||||
app:lottie_speed=".85" />
|
||||
</LinearLayout>
|
||||
</com.google.android.setupdesign.GlifLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_gravity="center_horizontal|bottom"
|
||||
android:layout_weight="4"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false"
|
||||
>
|
||||
|
||||
<include layout="@layout/fingerprint_v2_udfps_enroll_view" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/skip"
|
||||
style="@style/SudGlifButton.Secondary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|left"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:text="@string/security_settings_fingerprint_enroll_enrolling_skip" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
android:id="@+id/udfps_animation_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1240,6 +1240,28 @@
|
||||
<string name="private_space_fingerprint_unlock_title">Fingerprint Unlock for private space</string>
|
||||
<!-- Title for the Face unlock for private space preference. [CHAR LIMIT=60] -->
|
||||
<string name="private_space_face_unlock_title">Face Unlock for private space</string>
|
||||
<!-- Title for the Face and Fingerprint preference for private space. [CHAR LIMIT=60] -->
|
||||
<string name="private_space_biometric_unlock_title">Face & Fingerprint Unlock for private space</string>
|
||||
<!-- Introduction title shown in private space fingerprint enrollment [CHAR LIMIT=90] -->
|
||||
<string name="private_space_fingerprint_enroll_introduction_title">Set up Fingerprint Unlock for private space</string>
|
||||
<!-- Introduction detail message shown in private space fingerprint enrollment dialog [CHAR LIMIT=NONE]-->
|
||||
<string name ="private_space_fingerprint_enroll_introduction_message">Use your fingerprint to unlock your private space or verify it\u2019s you, like when you sign in to apps or approve a purchase</string>
|
||||
<!-- Introduction description message shown in private space fingerprint enrollment introduction screen. [CHAR LIMIT=NONE] -->
|
||||
<string name="private_space_fingerprint_enroll_introduction_footer_message">Your private space can be unlocked when you don\u2019t intend to, like if someone holds up your phone to your finger.</string>
|
||||
<!-- Message shown in fingerprint enrollment dialog once enrollment is completed (private space) [CHAR LIMIT=NONE] -->
|
||||
<string name="private_space_fingerprint_enroll_finish_message">Use your fingerprint to unlock your private space or to approve purchases</string>
|
||||
<!-- Introduction title shown in private space face enrollment [CHAR LIMIT=90] -->
|
||||
<string name="private_space_face_enroll_introduction_title">Set up Face Unlock for private space</string>
|
||||
<!-- Introduction detail message shown in private space face enrollment dialog [CHAR LIMIT=NONE]-->
|
||||
<string name ="private_space_face_enroll_introduction_message">Use your face to unlock your private space or verify it\u2019s you, like when you sign in to apps or approve a purchase</string>
|
||||
<!-- Message on the face enrollment introduction page for private space that provides information about what could cause the phone to unlock. [CHAR LIMIT=NONE] -->
|
||||
<string name="private_space_face_enroll_introduction_info_looking">Looking at the phone can unlock private space even when you don\u2019t intend to. Your private space can also be unlocked by someone who looks a lot like you, like an identical sibling, or if someone holds the device up to your face.</string>
|
||||
<!-- Message on the face enrollment introduction page for private space that provides information about the relative security of face for unlocking the phone. [CHAR LIMIT=NONE] -->
|
||||
<string name="private_space_face_enroll_introduction_info_less_secure">Using your face to unlock your private space may be less secure than a strong pattern, PIN, or password</string>
|
||||
<!-- Text shown on the details of a toggle which disables/enables face unlock for private space, depending if the user's eyes are open. [CHAR LIMIT=NONE] -->
|
||||
<string name="private_space_face_settings_require_attention_details">To unlock private space, your eyes must be open. For best results, take off sunglasses.</string>
|
||||
<!-- Text shown in face settings in private space explaining what your face can be used for. [CHAR LIMIT=NONE] -->
|
||||
<string name="private_space_face_settings_footer">Use your face to unlock your private space.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the phone can unlock it when you don\u2019t intend to.\n\nYour private space can be unlocked by someone else if your device is held up to your face.\n\nYour private space can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
|
||||
<!-- Biometric category title - biometric options for unlocking the device. [CHAR LIMIT=50] -->
|
||||
<string name="private_space_category_ways_to_unlock">Ways to unlock</string>
|
||||
<!-- Summary for one lock when device screen lock is used as private profile lock. [CHAR LIMIT=40] -->
|
||||
@@ -11406,7 +11428,7 @@
|
||||
<string name="media_output_default_summary">This device</string>
|
||||
|
||||
<!-- Summary for media output when audio sharing. [CHAR LIMIT=NONE] -->
|
||||
<string name="media_output_audio_sharing">Audio sharing</string>
|
||||
<string name="media_output_audio_sharing">Sharing audio</string>
|
||||
|
||||
<!-- Summary for media output settings when device is in ongoing call state. -->
|
||||
<string name="media_out_summary_ongoing_call_state">Unavailable during calls</string>
|
||||
@@ -11896,6 +11918,10 @@
|
||||
<string name="sim_onboarding_progressbar_turning_sim_on">Turning on <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>…</string>
|
||||
<!-- Title of service provider name(SPN) at mobile network settings page. [CHAR LIMIT=30] -->
|
||||
<string name="mobile_network_spn_title">Mobile network</string>
|
||||
<!-- At the mobile network page, title for primary IMEI for multi-sim devices -->
|
||||
<string name="imei_primary">IMEI (primary)</string>
|
||||
<!-- At the mobile network page, title for primary MEID for multi-sim devices -->
|
||||
<string name="meid_primary">MEID (primary)</string>
|
||||
<!-- Title of phone number at mobile network settings page. [CHAR LIMIT=30] -->
|
||||
<string name="mobile_network_phone_number_title">Phone number</string>
|
||||
<!-- Title of SIM label and color editor dialog at mobile network settings page. [CHAR LIMIT=30] -->
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/private_space_biometric_title"
|
||||
android:title="@string/private_space_biometric_unlock_title"
|
||||
settings:searchable="false">
|
||||
|
||||
<com.android.settingslib.widget.TopIntroPreference
|
||||
|
||||
@@ -1400,6 +1400,16 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
&& userManager.isQuietModeEnabled(userHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the userId is a private profile, false otherwise.
|
||||
*/
|
||||
public static boolean isPrivateProfile(int userId, @NonNull Context context) {
|
||||
final UserManager userManager = context.getSystemService(UserManager.class);
|
||||
UserInfo userInfo = userManager.getUserInfo(userId);
|
||||
return Flags.allowPrivateProfile() && android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& userInfo.isPrivateProfile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable new edge to edge feature.
|
||||
*
|
||||
|
||||
@@ -559,7 +559,8 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp
|
||||
if (android.app.Flags.appRestrictionsApi()) {
|
||||
am.noteAppRestrictionEnabled(pkgName, mAppEntry.info.uid,
|
||||
ActivityManager.RESTRICTION_LEVEL_FORCE_STOPPED, true,
|
||||
ActivityManager.RESTRICTION_REASON_USER, "settings", 0L);
|
||||
ActivityManager.RESTRICTION_REASON_USER,
|
||||
"settings", ActivityManager.RESTRICTION_SOURCE_USER, 0L);
|
||||
}
|
||||
am.forceStopPackage(pkgName);
|
||||
int userId = UserHandle.getUserId(mAppEntry.info.uid);
|
||||
|
||||
@@ -93,7 +93,10 @@ public class CombinedBiometricStatusUtils {
|
||||
public String getTitle() {
|
||||
UserManager userManager = mContext.getSystemService(UserManager.class);
|
||||
if (userManager != null && userManager.isProfile()) {
|
||||
return mContext.getString(R.string.security_settings_work_biometric_preference_title);
|
||||
return mContext.getString(
|
||||
Utils.isPrivateProfile(mUserId, mContext)
|
||||
? R.string.private_space_biometric_unlock_title
|
||||
: R.string.security_settings_work_biometric_preference_title);
|
||||
} else {
|
||||
return mContext.getString(R.string.security_settings_biometric_preference_title);
|
||||
}
|
||||
|
||||
@@ -365,7 +365,9 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
@StringRes
|
||||
protected int getInfoMessageLooking() {
|
||||
return R.string.security_settings_face_enroll_introduction_info_looking;
|
||||
return isPrivateProfile()
|
||||
? R.string.private_space_face_enroll_introduction_info_looking
|
||||
: R.string.security_settings_face_enroll_introduction_info_looking;
|
||||
}
|
||||
|
||||
@StringRes
|
||||
@@ -390,7 +392,10 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
@StringRes
|
||||
protected int getLessSecureMessage() {
|
||||
return R.string.security_settings_face_enroll_introduction_info_less_secure;
|
||||
return isPrivateProfile()
|
||||
? R.string.private_space_face_enroll_introduction_info_less_secure
|
||||
: R.string.security_settings_face_enroll_introduction_info_less_secure;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -411,6 +416,9 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
@Override
|
||||
protected int getHeaderResDefault() {
|
||||
if (isPrivateProfile()) {
|
||||
return R.string.private_space_face_enroll_introduction_title;
|
||||
}
|
||||
return R.string.security_settings_face_enroll_introduction_title;
|
||||
}
|
||||
|
||||
@@ -577,7 +585,10 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
@Override
|
||||
protected void updateDescriptionText() {
|
||||
if (mIsFaceStrong) {
|
||||
if (isPrivateProfile()) {
|
||||
setDescriptionText(getString(
|
||||
R.string.private_space_face_enroll_introduction_message));
|
||||
} else if (mIsFaceStrong) {
|
||||
setDescriptionText(getString(
|
||||
R.string.security_settings_face_enroll_introduction_message_class3));
|
||||
}
|
||||
@@ -608,4 +619,8 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
}
|
||||
updateDescriptionText();
|
||||
}
|
||||
|
||||
private boolean isPrivateProfile() {
|
||||
return Utils.isPrivateProfile(mUserId, getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,6 +211,8 @@ public class FaceSettings extends DashboardFragment {
|
||||
((FaceSettingsPreferenceController) controller).setUserId(mUserId);
|
||||
} else if (controller instanceof FaceSettingsEnrollButtonPreferenceController) {
|
||||
((FaceSettingsEnrollButtonPreferenceController) controller).setUserId(mUserId);
|
||||
} else if (controller instanceof FaceSettingsFooterPreferenceController) {
|
||||
((FaceSettingsFooterPreferenceController) controller).setUserId(mUserId);
|
||||
}
|
||||
}
|
||||
mRemoveController.setUserId(mUserId);
|
||||
@@ -367,6 +369,7 @@ public class FaceSettings extends DashboardFragment {
|
||||
controllers.add(new FaceSettingsRemoveButtonPreferenceController(context));
|
||||
controllers.add(new FaceSettingsConfirmPreferenceController(context));
|
||||
controllers.add(new FaceSettingsEnrollButtonPreferenceController(context));
|
||||
controllers.add(new FaceSettingsFooterPreferenceController(context));
|
||||
return controllers;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,12 @@ import android.hardware.face.FaceManager.GetFeatureCallback;
|
||||
import android.hardware.face.FaceManager.SetFeatureCallback;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
|
||||
/**
|
||||
@@ -98,6 +100,18 @@ public class FaceSettingsAttentionPreferenceController extends FaceSettingsPrefe
|
||||
mPreference = screen.findPreference(KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(@Nullable Preference preference) {
|
||||
if (preference == null) {
|
||||
return;
|
||||
}
|
||||
super.updateState(preference);
|
||||
if (Utils.isPrivateProfile(getUserId(), mContext)) {
|
||||
preference.setSummary(mContext.getString(
|
||||
R.string.private_space_face_settings_require_attention_details));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
if (!FaceSettings.isFaceHardwareDetected(mContext)) {
|
||||
|
||||
@@ -30,6 +30,7 @@ import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.utils.AnnotationSpan;
|
||||
@@ -41,12 +42,17 @@ import java.util.List;
|
||||
* Footer for face settings showing the help text and help link.
|
||||
*/
|
||||
public class FaceSettingsFooterPreferenceController extends BasePreferenceController {
|
||||
private static final String KEY = "security_face_footer";
|
||||
private static final String TAG = "FaceSettingsFooterPreferenceController";
|
||||
private static final String ANNOTATION_URL = "url";
|
||||
private final FaceFeatureProvider mProvider;
|
||||
private Preference mPreference;
|
||||
private boolean mIsFaceStrong;
|
||||
private int mUserId;
|
||||
|
||||
public FaceSettingsFooterPreferenceController(@NonNull Context context) {
|
||||
this(context, KEY);
|
||||
}
|
||||
public FaceSettingsFooterPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mProvider = FeatureFactory.getFeatureFactory().getFaceFeatureProvider();
|
||||
@@ -79,7 +85,9 @@ public class FaceSettingsFooterPreferenceController extends BasePreferenceContro
|
||||
|
||||
int footerRes;
|
||||
boolean isAttentionSupported = mProvider.isAttentionSupported(mContext);
|
||||
if (mIsFaceStrong) {
|
||||
if (Utils.isPrivateProfile(mUserId, mContext)) {
|
||||
footerRes = R.string.private_space_face_settings_footer;
|
||||
} else if (mIsFaceStrong) {
|
||||
footerRes = isAttentionSupported
|
||||
? R.string.security_settings_face_settings_footer_class3
|
||||
: R.string.security_settings_face_settings_footer_attention_not_supported;
|
||||
@@ -92,6 +100,10 @@ public class FaceSettingsFooterPreferenceController extends BasePreferenceContro
|
||||
mContext.getText(footerRes), linkInfo));
|
||||
}
|
||||
|
||||
public void setUserId(int userId) {
|
||||
mUserId = userId;
|
||||
}
|
||||
|
||||
private void addAuthenticatorsRegisteredCallback(Context context) {
|
||||
final FaceManager faceManager = context.getSystemService(FaceManager.class);
|
||||
faceManager.addAuthenticatorsRegisteredCallback(
|
||||
|
||||
@@ -67,7 +67,10 @@ public class FaceStatusUtils {
|
||||
public String getTitle() {
|
||||
UserManager userManager = mContext.getSystemService(UserManager.class);
|
||||
if (userManager != null && userManager.isProfile()) {
|
||||
return mContext.getString(R.string.security_settings_face_profile_preference_title);
|
||||
return mContext.getString(
|
||||
Utils.isPrivateProfile(mUserId, mContext)
|
||||
? R.string.private_space_face_unlock_title
|
||||
: R.string.security_settings_face_profile_preference_title);
|
||||
} else {
|
||||
return mContext.getString(R.string.security_settings_face_preference_title);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,10 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase {
|
||||
setContentView(R.layout.fingerprint_enroll_finish);
|
||||
}
|
||||
setHeaderText(R.string.security_settings_fingerprint_enroll_finish_title);
|
||||
setDescriptionText(R.string.security_settings_fingerprint_enroll_finish_v2_message);
|
||||
setDescriptionText(Utils.isPrivateProfile(mUserId, getApplicationContext())
|
||||
? R.string.private_space_fingerprint_enroll_finish_message
|
||||
: R.string.security_settings_fingerprint_enroll_finish_v2_message);
|
||||
|
||||
final String sfpsDescription = mSfpsRestToUnlockFeature != null
|
||||
? mSfpsRestToUnlockFeature.getDescriptionForSfps(this)
|
||||
: null;
|
||||
|
||||
@@ -176,7 +176,9 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
@Override
|
||||
protected void initViews() {
|
||||
setDescriptionText(getString(
|
||||
R.string.security_settings_fingerprint_enroll_introduction_v3_message,
|
||||
isPrivateProfile()
|
||||
? R.string.private_space_fingerprint_enroll_introduction_message
|
||||
: R.string.security_settings_fingerprint_enroll_introduction_v3_message,
|
||||
DeviceHelper.getDeviceName(this)));
|
||||
|
||||
super.initViews();
|
||||
@@ -261,6 +263,9 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
@StringRes
|
||||
protected int getFooterMessage5() {
|
||||
if (isPrivateProfile()) {
|
||||
return R.string.private_space_fingerprint_enroll_introduction_footer_message;
|
||||
}
|
||||
return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_5;
|
||||
}
|
||||
|
||||
@@ -292,6 +297,9 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
@Override
|
||||
protected int getHeaderResDefault() {
|
||||
if (isPrivateProfile()) {
|
||||
return R.string.private_space_fingerprint_enroll_introduction_title;
|
||||
}
|
||||
return R.string.security_settings_fingerprint_enroll_introduction_title;
|
||||
}
|
||||
|
||||
@@ -477,4 +485,8 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
data.putExtra(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, true);
|
||||
return data;
|
||||
}
|
||||
|
||||
private boolean isPrivateProfile() {
|
||||
return Utils.isPrivateProfile(mUserId, getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,8 +510,9 @@ public class FingerprintSettings extends SubSettings {
|
||||
mFooterColumns.add(column2);
|
||||
} else {
|
||||
final FooterColumn column = new FooterColumn();
|
||||
column.mTitle = getString(
|
||||
R.string.security_settings_fingerprint_enroll_introduction_v3_message,
|
||||
column.mTitle = getString(isPrivateProfile()
|
||||
? R.string.private_space_fingerprint_enroll_introduction_message
|
||||
: R.string.security_settings_fingerprint_enroll_introduction_v3_message,
|
||||
DeviceHelper.getDeviceName(getActivity()));
|
||||
column.mLearnMoreClickListener = learnMoreClickListener;
|
||||
column.mLearnMoreOverrideText = getText(
|
||||
@@ -1130,6 +1131,10 @@ public class FingerprintSettings extends SubSettings {
|
||||
}
|
||||
};
|
||||
|
||||
private boolean isPrivateProfile() {
|
||||
return Utils.isPrivateProfile(mUserId, getContext());
|
||||
}
|
||||
|
||||
public static class DeleteFingerprintDialog extends InstrumentedDialogFragment
|
||||
implements DialogInterface.OnClickListener {
|
||||
|
||||
|
||||
@@ -68,7 +68,10 @@ public class FingerprintStatusUtils {
|
||||
public String getTitle() {
|
||||
UserManager userManager = mContext.getSystemService(UserManager.class);
|
||||
if (userManager != null && userManager.isProfile()) {
|
||||
return mContext.getString(R.string.security_settings_work_fingerprint_preference_title);
|
||||
return mContext.getString(
|
||||
Utils.isPrivateProfile(mUserId, mContext)
|
||||
? R.string.private_space_fingerprint_unlock_title
|
||||
: R.string.security_settings_work_fingerprint_preference_title);
|
||||
} else {
|
||||
return mContext.getString(R.string.security_settings_fingerprint_preference_title);
|
||||
}
|
||||
|
||||
@@ -184,6 +184,7 @@ public class UdfpsEnrollHelper extends InstrumentedFragment {
|
||||
*/
|
||||
public void onAcquired(boolean isAcquiredGood) {
|
||||
if (mListener != null) {
|
||||
Log.e("JRM", "OnaCquired " + isAcquiredGood + " lastStepIsGood" + animateIfLastStep());
|
||||
mListener.onAcquired(isAcquiredGood && animateIfLastStep());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class DebuggingRepositoryImpl : DebuggingRepository {
|
||||
*/
|
||||
private val isBuildDebuggable = Build.IS_DEBUGGABLE
|
||||
/** This flag indicates if udfps should use debug repos to supply data to its various views. */
|
||||
private val udfpsEnrollmentDebugEnabled = true
|
||||
private val udfpsEnrollmentDebugEnabled = false
|
||||
|
||||
override fun isDebuggingEnabled(): Boolean {
|
||||
return isBuildDebuggable
|
||||
|
||||
@@ -34,10 +34,10 @@ class EnrollStageInteractorImpl() : EnrollStageInteractor {
|
||||
flowOf(
|
||||
mapOf(
|
||||
0.0f to EnrollStageModel.Center,
|
||||
0.25f to EnrollStageModel.Guided,
|
||||
0.5f to EnrollStageModel.Fingertip,
|
||||
0.75f to EnrollStageModel.LeftEdge,
|
||||
0.875f to EnrollStageModel.RightEdge,
|
||||
0.065f to EnrollStageModel.Guided,
|
||||
0.48f to EnrollStageModel.Fingertip,
|
||||
0.584f to EnrollStageModel.LeftEdge,
|
||||
0.792f to EnrollStageModel.RightEdge,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
||||
/** This repository is responsible for collecting all state related to the enroll API. */
|
||||
@@ -106,6 +107,30 @@ class FingerprintEnrollInteractorImpl(
|
||||
streamEnded = true
|
||||
enrollRequestOutstanding.update { false }
|
||||
}
|
||||
|
||||
override fun onUdfpsPointerDown(sensorId: Int) {
|
||||
trySend(FingerEnrollState.PointerDown(sensorId)).onFailure { error ->
|
||||
Log.d(TAG, "onUdfpsPointerDown failed to send, due to $error")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUdfpsPointerUp(sensorId: Int) {
|
||||
trySend(FingerEnrollState.PointerUp(sensorId)).onFailure { error ->
|
||||
Log.d(TAG, "onUdfpsPointerUp failed to send, due to $error")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUdfpsOverlayShown() {
|
||||
trySend(FingerEnrollState.OverlayShown).onFailure { error ->
|
||||
Log.d(TAG, "OverlayShown failed to send, due to $error")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAcquired(isAcquiredGood: Boolean) {
|
||||
trySend(FingerEnrollState.Acquired(isAcquiredGood)).onFailure { error ->
|
||||
Log.d(TAG, "Acquired failed to send, due to $error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val cancellationSignal = CancellationSignal()
|
||||
|
||||
@@ -17,17 +17,12 @@
|
||||
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.view.OrientationEventListener
|
||||
import com.android.internal.R
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.transform
|
||||
|
||||
/** Interactor which provides information about orientation */
|
||||
@@ -35,7 +30,9 @@ interface OrientationInteractor {
|
||||
/** A flow that contains the information about the orientation changing */
|
||||
val orientation: Flow<Int>
|
||||
/**
|
||||
* A flow that contains the rotation info
|
||||
* This indicates the surface rotation that hte view is currently in. For instance its possible to
|
||||
* rotate a view to 90 degrees but for it to still be portrait mode. In this case, this flow
|
||||
* should emit that we are in rotation 0 (SurfaceView.Rotation_0)
|
||||
*/
|
||||
val rotation: Flow<Int>
|
||||
/**
|
||||
@@ -50,8 +47,7 @@ interface OrientationInteractor {
|
||||
fun getRotationFromDefault(rotation: Int): Int
|
||||
}
|
||||
|
||||
class OrientationInteractorImpl(private val context: Context, activityScope: CoroutineScope) :
|
||||
OrientationInteractor {
|
||||
class OrientationInteractorImpl(private val context: Context) : OrientationInteractor {
|
||||
|
||||
override val orientation: Flow<Int> = callbackFlow {
|
||||
val orientationEventListener =
|
||||
@@ -62,9 +58,12 @@ class OrientationInteractorImpl(private val context: Context, activityScope: Cor
|
||||
}
|
||||
orientationEventListener.enable()
|
||||
awaitClose { orientationEventListener.disable() }
|
||||
}.shareIn(activityScope, SharingStarted.Eagerly, replay = 1)
|
||||
}
|
||||
|
||||
override val rotation: Flow<Int> = orientation.transform { emit(context.display!!.rotation) }
|
||||
override val rotation: Flow<Int> =
|
||||
orientation.transform {
|
||||
emit(context.display!!.rotation)
|
||||
}
|
||||
|
||||
override val rotationFromDefault: Flow<Int> = rotation.map { getRotationFromDefault(it) }
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
||||
|
||||
import android.graphics.PointF
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@@ -69,6 +70,7 @@ class UdfpsEnrollInteractorImpl(
|
||||
|
||||
override fun onEnrollmentStep(stepsRemaining: Int, totalStep: Int) {
|
||||
val index = (totalStep - stepsRemaining) % guidedEnrollmentPoints.size
|
||||
Log.e("JRM", "guided enroll step $index")
|
||||
_guidedEnrollment.update { guidedEnrollmentPoints[index] }
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,10 @@ sealed class FingerEnrollState {
|
||||
data class Acquired(val acquiredGood: Boolean) : FingerEnrollState()
|
||||
|
||||
/** Indicates a pointer down event has occurred */
|
||||
data object PointerDown : FingerEnrollState()
|
||||
data class PointerDown(val fingerId: Int) : FingerEnrollState()
|
||||
|
||||
/** Indicates a pointer up event has occurred */
|
||||
data object PointerUp : FingerEnrollState()
|
||||
data class PointerUp(val fingerId: Int) : FingerEnrollState()
|
||||
|
||||
/** Indicates the overlay has shown */
|
||||
data object OverlayShown : FingerEnrollState()
|
||||
|
||||
@@ -72,6 +72,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enroll
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.fragment.RFPSEnrollFragment
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment.UdfpsEnrollFragment
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsLastStepViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
|
||||
@@ -126,6 +127,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
||||
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
|
||||
private lateinit var fingerprintEnrollConfirmationViewModel:
|
||||
FingerprintEnrollConfirmationViewModel
|
||||
private lateinit var udfpsLastStepViewModel: UdfpsLastStepViewModel
|
||||
private lateinit var udfpsViewModel: UdfpsViewModel
|
||||
private lateinit var enrollStageInteractor: EnrollStageInteractor
|
||||
private val coroutineDispatcher = Dispatchers.Default
|
||||
@@ -320,7 +322,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
||||
foldStateInteractor = FoldStateInteractorImpl(context)
|
||||
foldStateInteractor.onConfigurationChange(resources.configuration)
|
||||
|
||||
orientationInteractor = OrientationInteractorImpl(context, lifecycleScope)
|
||||
orientationInteractor = OrientationInteractorImpl(context)
|
||||
vibrationInteractor =
|
||||
VibrationInteractorImpl(context.getSystemService(Vibrator::class.java)!!, context)
|
||||
|
||||
@@ -373,11 +375,15 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
||||
fingerprintEnrollEnrollingViewModel,
|
||||
navigationViewModel,
|
||||
orientationInteractor,
|
||||
fingerprintManagerInteractor,
|
||||
),
|
||||
)[RFPSViewModel::class.java]
|
||||
|
||||
enrollStageInteractor = EnrollStageInteractorImpl()
|
||||
|
||||
udfpsLastStepViewModel =
|
||||
UdfpsLastStepViewModel(fingerprintEnrollEnrollingViewModel, vibrationInteractor)
|
||||
|
||||
udfpsViewModel =
|
||||
ViewModelProvider(
|
||||
this,
|
||||
@@ -393,6 +399,9 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
||||
backgroundViewModel,
|
||||
fingerprintSensorRepo,
|
||||
udfpsEnrollInteractor,
|
||||
fingerprintManagerInteractor,
|
||||
udfpsLastStepViewModel,
|
||||
accessibilityInteractor,
|
||||
),
|
||||
)[UdfpsViewModel::class.java]
|
||||
|
||||
@@ -456,7 +465,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
||||
step.exitTransition.toAnimation(),
|
||||
)
|
||||
.setReorderingAllowed(true)
|
||||
.add(R.id.fragment_container_view, theClass::class.java, null)
|
||||
.replace(R.id.fragment_container_view, theClass::class.java, null)
|
||||
.commit()
|
||||
navigationViewModel.update(
|
||||
FingerprintAction.TRANSITION_FINISHED,
|
||||
|
||||
@@ -20,15 +20,18 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
|
||||
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -41,6 +44,7 @@ class RFPSViewModel(
|
||||
private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel,
|
||||
private val navigationViewModel: FingerprintNavigationViewModel,
|
||||
orientationInteractor: OrientationInteractor,
|
||||
private val fingerprintManager: FingerprintManagerInteractor,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _textViewIsVisible = MutableStateFlow(false)
|
||||
@@ -52,7 +56,16 @@ class RFPSViewModel(
|
||||
/** Indicates if the icon should be animating or not */
|
||||
val shouldAnimateIcon = _shouldAnimateIcon
|
||||
|
||||
private var enrollFlow: Flow<FingerEnrollState?> = fingerprintEnrollViewModel.enrollFlow
|
||||
private var enrollFlow: Flow<FingerEnrollState?> =
|
||||
fingerprintManager.sensorPropertiesInternal.filterNotNull().combine(
|
||||
fingerprintEnrollViewModel.enrollFlow
|
||||
) { props, enroll ->
|
||||
if (props.sensorType == FingerprintSensorType.REAR) {
|
||||
enroll
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enroll progress message with a replay of size 1 allowing for new subscribers to get the most
|
||||
@@ -149,6 +162,7 @@ class RFPSViewModel(
|
||||
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||
private val navigationViewModel: FingerprintNavigationViewModel,
|
||||
private val orientationInteractor: OrientationInteractor,
|
||||
private val fingerprintManager: FingerprintManagerInteractor,
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -157,6 +171,7 @@ class RFPSViewModel(
|
||||
fingerprintEnrollEnrollingViewModel,
|
||||
navigationViewModel,
|
||||
orientationInteractor,
|
||||
fingerprintManager,
|
||||
)
|
||||
as T
|
||||
}
|
||||
|
||||
@@ -21,8 +21,10 @@ import android.util.Log
|
||||
import android.view.MotionEvent
|
||||
import android.view.MotionEvent.ACTION_HOVER_MOVE
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.widget.TextView
|
||||
import android.widget.Button
|
||||
import android.widget.FrameLayout
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@@ -40,7 +42,6 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enroll
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.EducationAnimationModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget.UdfpsEnrollViewV2
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
||||
import com.google.android.setupdesign.GlifLayout
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -71,10 +72,8 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
val fragment = this
|
||||
lottie = view.findViewById(R.id.illustration_lottie)!!
|
||||
udfpsEnrollView = view.findViewById(R.id.udfps_animation_view)!!
|
||||
val titleTextView = view.findViewById<TextView>(R.id.title)!!
|
||||
val descriptionTextView = view.findViewById<TextView>(R.id.description)!!
|
||||
val glifLayout: GlifLayout = view.findViewById(R.id.glif_layout)!!
|
||||
|
||||
val glifLayout = view.findViewById<GlifLayout>(R.id.dummy_glif_layout)!!
|
||||
val backgroundColor = glifLayout.backgroundBaseColor
|
||||
val window = requireActivity().window
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||
@@ -83,6 +82,10 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
window.statusBarColor = color
|
||||
view.setBackgroundColor(color)
|
||||
|
||||
view.findViewById<Button>(R.id.skip)?.apply {
|
||||
setOnClickListener { viewModel.negativeButtonClicked() }
|
||||
}
|
||||
|
||||
udfpsEnrollView.setFinishAnimationCompleted { viewModel.finishedSuccessfully() }
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
@@ -92,32 +95,41 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
udfpsEnrollView.setSensorRect(sensor.sensorBounds, sensor.sensorType)
|
||||
}
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.headerText.collect { titleTextView.setText(it.toResource()) }
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.descriptionText.collect {
|
||||
if (it != null) {
|
||||
it.toResource()?.let { text -> descriptionTextView.setText(text) }
|
||||
} else {
|
||||
descriptionTextView.text = ""
|
||||
launch { viewModel.overlayShown.collect { udfpsEnrollView.overlayShown() } }
|
||||
launch { viewModel.headerText.collect { glifLayout.setHeaderText(it.toResource()) } }
|
||||
launch {
|
||||
viewModel.userInteractedWithSensor.collect {
|
||||
if (!it) {
|
||||
glifLayout.setHeaderText(R.string.security_settings_fingerprint_enroll_udfps_title)
|
||||
glifLayout.setDescriptionText(R.string.security_settings_udfps_enroll_start_message)
|
||||
}
|
||||
}
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
|
||||
launch {
|
||||
viewModel.descriptionText.collect {
|
||||
if (it != null) {
|
||||
it.toResource()?.let { text -> glifLayout.setDescriptionText(text) }
|
||||
} else {
|
||||
glifLayout.descriptionText = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
viewModel.shouldShowLottie.collect {
|
||||
lottie.visibility = if (it) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
launch {
|
||||
viewModel.lottie.collect { lottieModel ->
|
||||
if (lottie.visibility == View.GONE) {
|
||||
return@collect
|
||||
}
|
||||
val resource = lottieModel.toResource()
|
||||
if (resource != null) {
|
||||
glifLayout.descriptionTextView.visibility = View.GONE
|
||||
LottieCompositionFactory.fromRawRes(requireContext(), resource).addListener { comp ->
|
||||
comp?.let { composition ->
|
||||
lottie.setComposition(composition)
|
||||
@@ -126,27 +138,24 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
}
|
||||
}
|
||||
} else {
|
||||
glifLayout.descriptionTextView.visibility = View.VISIBLE
|
||||
lottie.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.DESTROYED) { viewModel.stopEnrollment() }
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
launch {
|
||||
viewModel.accessibilityEnabled.collect { enabled ->
|
||||
udfpsEnrollView.setAccessibilityEnabled(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
launch {
|
||||
viewModel.enrollState.collect {
|
||||
Log.d(TAG, "EnrollEvent $it")
|
||||
if (it is FingerEnrollState.EnrollError) {
|
||||
try {
|
||||
FingerprintErrorDialog.showInstance(it, fragment)
|
||||
viewModel.errorDialogShown(it)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Exception occurred $exception")
|
||||
}
|
||||
@@ -156,19 +165,40 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.progressSaved.collect { udfpsEnrollView.onEnrollProgressSaved(it) }
|
||||
launch { viewModel.progressSaved.collect { udfpsEnrollView.onEnrollProgressSaved(it) } }
|
||||
|
||||
launch { viewModel.guidedEnrollment.collect { udfpsEnrollView.updateGuidedEnrollment(it) } }
|
||||
launch {
|
||||
viewModel.guidedEnrollmentSaved.collect { udfpsEnrollView.onGuidedPointSaved(it) }
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.guidedEnrollment.collect {
|
||||
glifLayout.post { udfpsEnrollView.updateGuidedEnrollment(it) }
|
||||
launch { viewModel.shouldDrawIcon.collect { udfpsEnrollView.shouldDrawIcon(it) } }
|
||||
|
||||
// Hack to get the final step of enroll progress to animate.
|
||||
launch {
|
||||
viewModel.udfpsLastStepViewModel.shouldAnimateCompletion.collect {
|
||||
Log.d(TAG, "Sending fake last enroll event $it")
|
||||
udfpsEnrollView.onUdfpsEvent(it)
|
||||
}
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.guidedEnrollmentSaved.collect {
|
||||
glifLayout.post { udfpsEnrollView.onGuidedPointSaved(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.DESTROYED) { viewModel.stopEnrollment() }
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.isLandscape.collect {
|
||||
if (it) {
|
||||
changeViewToLandscape()
|
||||
}
|
||||
}
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.isReverseLandscape.collect {
|
||||
if (it) {
|
||||
changeViewToReverseLandscape()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,12 +213,70 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
viewModel.readyForEnrollment()
|
||||
}
|
||||
|
||||
private fun changeViewToReverseLandscape() {
|
||||
Log.d(TAG, "changeViewToReverseLandscape")
|
||||
val glifContainer = requireView().findViewById<GlifLayout>(R.id.glif_layout)!!
|
||||
val headerView =
|
||||
glifContainer.findViewById<View>(
|
||||
com.google.android.setupdesign.R.id.sud_landscape_header_area
|
||||
)
|
||||
// The landscape_header_area nad landscape_content_area should have the same parent
|
||||
val parent = headerView!!.parent as ViewGroup
|
||||
val sudContentFrame =
|
||||
glifContainer.findViewById<View>(
|
||||
com.google.android.setupdesign.R.id.sud_landscape_content_area
|
||||
)!!
|
||||
val udfpsContainer = requireView().findViewById<FrameLayout>(R.id.layout_container)!!
|
||||
|
||||
parent.removeView(headerView)
|
||||
parent.removeView(sudContentFrame)
|
||||
parent.addView(sudContentFrame)
|
||||
parent.addView(headerView)
|
||||
|
||||
unclipSubviewsFromParent(udfpsContainer)
|
||||
udfpsEnrollView.requestLayout()
|
||||
}
|
||||
|
||||
private fun changeViewToLandscape() {
|
||||
Log.d(TAG, "changeViewToLandscape")
|
||||
|
||||
val glifContainer = requireView().findViewById<GlifLayout>(R.id.glif_layout)!!
|
||||
val headerView =
|
||||
glifContainer.findViewById<View>(
|
||||
com.google.android.setupdesign.R.id.sud_landscape_header_area
|
||||
)
|
||||
// The landscape_header_area nad landscape_content_area should have the same parent
|
||||
val parent = headerView!!.parent as ViewGroup
|
||||
val sudContentFrame =
|
||||
glifContainer.findViewById<View>(
|
||||
com.google.android.setupdesign.R.id.sud_landscape_content_area
|
||||
)!!
|
||||
|
||||
parent.removeView(headerView)
|
||||
parent.removeView(sudContentFrame)
|
||||
parent.addView(headerView)
|
||||
parent.addView(sudContentFrame)
|
||||
|
||||
val udfpsContainer = requireView().findViewById<FrameLayout>(R.id.layout_container)!!
|
||||
unclipSubviewsFromParent(udfpsContainer)
|
||||
udfpsEnrollView.requestLayout()
|
||||
}
|
||||
|
||||
private fun unclipSubviewsFromParent(view: View) {
|
||||
var currParent = view.parent
|
||||
while (currParent is ViewGroup) {
|
||||
currParent.clipChildren = false
|
||||
currParent.clipToPadding = false
|
||||
currParent = currParent.parent
|
||||
}
|
||||
}
|
||||
|
||||
private fun HeaderText.toResource(): Int {
|
||||
return when (this.enrollStageModel) {
|
||||
EnrollStageModel.Center,
|
||||
EnrollStageModel.Guided,
|
||||
EnrollStageModel.Fingertip,
|
||||
EnrollStageModel.Unknown -> R.string.security_settings_udfps_enroll_fingertip_title
|
||||
EnrollStageModel.Guided -> R.string.security_settings_fingerprint_enroll_repeat_title
|
||||
EnrollStageModel.Fingertip -> R.string.security_settings_udfps_enroll_fingertip_title
|
||||
EnrollStageModel.Unknown -> R.string.security_settings_fingerprint_enroll_udfps_title
|
||||
EnrollStageModel.LeftEdge -> R.string.security_settings_udfps_enroll_left_edge_title
|
||||
EnrollStageModel.RightEdge -> R.string.security_settings_udfps_enroll_right_edge_title
|
||||
}
|
||||
@@ -218,6 +306,5 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
|
||||
companion object {
|
||||
private const val TAG = "UDFPSEnrollFragment"
|
||||
private val navStep = FingerprintNavigationStep.Enrollment::class
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model
|
||||
|
||||
import android.graphics.PointF
|
||||
import com.android.systemui.biometrics.shared.model.FingerprintSensor
|
||||
|
||||
data class UdfpsSensorLocation(val sensorLocation: FingerprintSensor, val offset: PointF?)
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintVibrationEffects
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* This class is responsible for determining if we should animate the last step of an enrollment. It
|
||||
* seems to be due to poor hardware implementation that the last step of a UDFPS enrollment takes
|
||||
* much longer then normal (likely due to to saving the whole enrollment/or some kind of
|
||||
* verification)
|
||||
*
|
||||
* Because of this, we will use the information of if a fingerprint was acquired(good) + enrollment
|
||||
* has reached the last step
|
||||
*/
|
||||
class UdfpsLastStepViewModel(
|
||||
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||
private val vibrationInteractor: VibrationInteractor,
|
||||
) : ViewModel() {
|
||||
|
||||
private val lastStep: MutableStateFlow<FingerEnrollState.EnrollProgress?> = MutableStateFlow(null)
|
||||
private val stepSize: MutableStateFlow<Int?> = MutableStateFlow(null)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val steps =
|
||||
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||
.filterIsInstance<FingerEnrollState.EnrollProgress>()
|
||||
.distinctUntilChanged()
|
||||
.take(2)
|
||||
.toList()
|
||||
stepSize.update { steps[0].remainingSteps - steps[1].remainingSteps }
|
||||
lastStep.update { FingerEnrollState.EnrollProgress(0, steps[0].totalStepsRequired) }
|
||||
}
|
||||
}
|
||||
|
||||
private val enrollProgress =
|
||||
fingerprintEnrollEnrollingViewModel.enrollFlow.filterIsInstance<
|
||||
FingerEnrollState.EnrollProgress
|
||||
>()
|
||||
|
||||
/** Indicates if we should animate the completion of udfps enrollment. */
|
||||
val shouldAnimateCompletion =
|
||||
enrollProgress
|
||||
.transform { it ->
|
||||
if (it.remainingSteps == stepSize.value) {
|
||||
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||
.filterIsInstance<FingerEnrollState.Acquired>()
|
||||
.filter { acquired -> acquired.acquiredGood }
|
||||
.collect {
|
||||
vibrationInteractor.vibrate(
|
||||
FingerprintVibrationEffects.UdfpsSuccess,
|
||||
"UdfpsLastStepViewModel",
|
||||
)
|
||||
|
||||
emit(lastStep.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
.filterNotNull()
|
||||
|
||||
class UdfpsLastStepViewModelFactory(
|
||||
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||
private val vibrationInteractor: VibrationInteractor,
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return UdfpsLastStepViewModel(fingerprintEnrollEnrollingViewModel, vibrationInteractor) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,10 @@ import android.view.Surface
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
||||
import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel
|
||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
||||
import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
|
||||
@@ -32,6 +33,7 @@ import com.android.settings.biometrics.fingerprint2.domain.interactor.Fingerprin
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.DescriptionText
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.HeaderText
|
||||
@@ -41,6 +43,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.combineTransform
|
||||
@@ -51,6 +54,8 @@ import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */
|
||||
@@ -66,13 +71,25 @@ class UdfpsViewModel(
|
||||
backgroundViewModel: BackgroundViewModel,
|
||||
sensorRepository: FingerprintSensorRepository,
|
||||
udfpsEnrollInteractor: UdfpsEnrollInteractor,
|
||||
fingerprintManager: FingerprintManagerInteractor,
|
||||
val udfpsLastStepViewModel: UdfpsLastStepViewModel,
|
||||
accessibilityInteractor: AccessibilityInteractor,
|
||||
) : ViewModel() {
|
||||
|
||||
private val isSetupWizard = flowOf(false)
|
||||
private var shouldResetErollment = false
|
||||
|
||||
private var _enrollState: Flow<FingerEnrollState?> =
|
||||
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||
fingerprintManager.sensorPropertiesInternal.filterNotNull().combine(
|
||||
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||
) { props, enroll ->
|
||||
if (props.sensorType.isUdfps()) {
|
||||
enroll
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/** The current state of the enrollment. */
|
||||
var enrollState: Flow<FingerEnrollState> =
|
||||
combine(fingerprintEnrollEnrollingViewModel.enrollFlowShouldBeRunning, _enrollState) {
|
||||
@@ -86,48 +103,59 @@ class UdfpsViewModel(
|
||||
}
|
||||
.filterNotNull()
|
||||
|
||||
/** Indicates that overlay has been shown */
|
||||
val overlayShown =
|
||||
enrollState
|
||||
.filterIsInstance<FingerEnrollState.OverlayShown>()
|
||||
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||
|
||||
private var _userInteractedWithSensor = MutableStateFlow(false)
|
||||
|
||||
/**
|
||||
* This indicates whether the user has interacted with the sensor or not. This indicates if we are
|
||||
* in the initial state of the UI.
|
||||
*/
|
||||
val userInteractedWithSensor: Flow<Boolean> =
|
||||
enrollState.transform {
|
||||
val interactiveMessage =
|
||||
when (it) {
|
||||
is FingerEnrollState.Acquired,
|
||||
is FingerEnrollState.EnrollError,
|
||||
is FingerEnrollState.EnrollHelp,
|
||||
is FingerEnrollState.EnrollProgress,
|
||||
is FingerEnrollState.PointerDown,
|
||||
is FingerEnrollState.PointerUp -> true
|
||||
else -> false
|
||||
}
|
||||
val hasPreviouslyInteracted = _userInteractedWithSensor.value or interactiveMessage
|
||||
_userInteractedWithSensor.update { hasPreviouslyInteracted }
|
||||
emit(hasPreviouslyInteracted)
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards the property sensor information. This is typically used to recreate views that must be
|
||||
* aligned with the sensor.
|
||||
*/
|
||||
val sensorLocation = sensorRepository.fingerprintSensor
|
||||
|
||||
/** Indicates if accessibility is enabled */
|
||||
val accessibilityEnabled = flowOf(true).shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
enrollState
|
||||
.combine(accessibilityEnabled) { event, isEnabled -> Pair(event, isEnabled) }
|
||||
.collect {
|
||||
if (
|
||||
when (it.first) {
|
||||
is FingerEnrollState.EnrollError -> true
|
||||
is FingerEnrollState.EnrollHelp -> it.second
|
||||
is FingerEnrollState.EnrollProgress -> true
|
||||
else -> false
|
||||
}
|
||||
) {
|
||||
vibrate(it.first)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
backgroundViewModel.background.filter { it }.collect { didGoToBackground() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This indicates at which point the UI should offset the fingerprint sensor icon for guided
|
||||
* enrollment.
|
||||
*/
|
||||
/** Indicates a step of guided enrollment, the ui should animate the icon to the new location. */
|
||||
val guidedEnrollment: Flow<PointF> =
|
||||
udfpsEnrollInteractor.guidedEnrollmentOffset.distinctUntilChanged()
|
||||
udfpsEnrollInteractor.guidedEnrollmentOffset
|
||||
.distinctUntilChanged()
|
||||
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
|
||||
|
||||
/** The saved version of [guidedEnrollment] */
|
||||
val guidedEnrollmentSaved: Flow<PointF> =
|
||||
guidedEnrollment.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||
private var _lastOrientation: Int? = null
|
||||
|
||||
/** In case of rotations we should ensure the UI does not re-animate the last state. */
|
||||
private val shouldReplayLastEvent =
|
||||
orientationInteractor.rotation.transform {
|
||||
if (_lastOrientation != null && _lastOrientation!! != it) {
|
||||
emit(true)
|
||||
} else {
|
||||
emit(false)
|
||||
}
|
||||
_lastOrientation = it
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the saved progress, this is for when views are recreated and need saved state for the
|
||||
@@ -136,8 +164,32 @@ class UdfpsViewModel(
|
||||
var progressSaved: Flow<FingerEnrollState.EnrollProgress> =
|
||||
enrollState
|
||||
.filterIsInstance<FingerEnrollState.EnrollProgress>()
|
||||
.filterNotNull()
|
||||
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||
.combineTransform(shouldReplayLastEvent) { enroll, shouldReplay ->
|
||||
if (shouldReplay) {
|
||||
emit(enroll)
|
||||
}
|
||||
}
|
||||
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||
|
||||
/** Indicates if accessibility is enabled */
|
||||
val accessibilityEnabled =
|
||||
accessibilityInteractor.isAccessibilityEnabled.shareIn(
|
||||
this.viewModelScope,
|
||||
SharingStarted.Eagerly,
|
||||
replay = 1,
|
||||
)
|
||||
|
||||
/** Indicates if we are in reverse landscape orientation. */
|
||||
val isReverseLandscape =
|
||||
orientationInteractor.rotation
|
||||
.transform { emit(it == Surface.ROTATION_270) }
|
||||
.distinctUntilChanged()
|
||||
|
||||
/** Indicates if we are in the landscape orientation */
|
||||
val isLandscape =
|
||||
orientationInteractor.rotation
|
||||
.transform { emit(it == Surface.ROTATION_90) }
|
||||
.distinctUntilChanged()
|
||||
|
||||
/** This sends touch exploration events only used for debugging purposes. */
|
||||
val touchExplorationDebug: Flow<Point> =
|
||||
@@ -170,6 +222,18 @@ class UdfpsViewModel(
|
||||
.filterNotNull()
|
||||
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||
|
||||
/** The saved version of [guidedEnrollment] */
|
||||
val guidedEnrollmentSaved: Flow<PointF> =
|
||||
combineTransform(guidedEnrollment, shouldReplayLastEvent, enrollStage) {
|
||||
point,
|
||||
shouldReplay,
|
||||
stage ->
|
||||
if (shouldReplay && stage is EnrollStageModel.Guided) {
|
||||
emit(point)
|
||||
}
|
||||
}
|
||||
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
enrollState
|
||||
@@ -200,7 +264,7 @@ class UdfpsViewModel(
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
backgroundViewModel.background.filter { true }.collect { didGoToBackground() }
|
||||
backgroundViewModel.background.filter { it }.collect { didGoToBackground() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,20 +274,12 @@ class UdfpsViewModel(
|
||||
displayDensityInteractor.displayDensity,
|
||||
displayDensityInteractor.defaultDisplayDensity,
|
||||
displayDensityInteractor.fontScale,
|
||||
orientationInteractor.rotation,
|
||||
) { currDisplayDensity, defaultDisplayDensity, fontScale, rotation ->
|
||||
val canShowLottieForRotation =
|
||||
when (rotation) {
|
||||
Surface.ROTATION_0 -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
canShowLottieForRotation &&
|
||||
if (fontScale > 1.0f) {
|
||||
false
|
||||
} else {
|
||||
defaultDisplayDensity == currDisplayDensity
|
||||
}
|
||||
) { currDisplayDensity, defaultDisplayDensity, fontScale ->
|
||||
if (fontScale > 1.0f) {
|
||||
false
|
||||
} else {
|
||||
defaultDisplayDensity == currDisplayDensity
|
||||
}
|
||||
}
|
||||
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||
|
||||
@@ -234,6 +290,18 @@ class UdfpsViewModel(
|
||||
}
|
||||
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||
|
||||
/** Indicates if we should or shold not draw the fingerprint icon */
|
||||
val shouldDrawIcon: Flow<Boolean> =
|
||||
enrollState.transform { state ->
|
||||
when (state) {
|
||||
is FingerEnrollState.EnrollProgress,
|
||||
is FingerEnrollState.EnrollError,
|
||||
is FingerEnrollState.PointerUp -> emit(true)
|
||||
is FingerEnrollState.PointerDown -> emit(false)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private val shouldClearDescriptionText = enrollStage.map { it is EnrollStageModel.Unknown }
|
||||
|
||||
/** The description text for UDFPS enrollment */
|
||||
@@ -267,12 +335,12 @@ class UdfpsViewModel(
|
||||
|
||||
/** Indicates the negative button has been clicked */
|
||||
fun negativeButtonClicked() {
|
||||
doReset()
|
||||
navigationViewModel.update(
|
||||
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
|
||||
navStep,
|
||||
"$TAG#negativeButtonClicked",
|
||||
)
|
||||
doReset()
|
||||
}
|
||||
|
||||
/** Indicates that an enrollment was completed */
|
||||
@@ -282,7 +350,7 @@ class UdfpsViewModel(
|
||||
}
|
||||
|
||||
/** Indicates that the application went to the background. */
|
||||
private fun didGoToBackground() {
|
||||
fun didGoToBackground() {
|
||||
navigationViewModel.update(
|
||||
FingerprintAction.DID_GO_TO_BACKGROUND,
|
||||
navStep,
|
||||
@@ -293,11 +361,7 @@ class UdfpsViewModel(
|
||||
|
||||
private fun doReset() {
|
||||
_enrollState = fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||
progressSaved =
|
||||
enrollState
|
||||
.filterIsInstance<FingerEnrollState.EnrollProgress>()
|
||||
.filterNotNull()
|
||||
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||
_userInteractedWithSensor.update { false }
|
||||
}
|
||||
|
||||
/** The lottie that should be shown for UDFPS Enrollment */
|
||||
@@ -320,6 +384,15 @@ class UdfpsViewModel(
|
||||
vibrationInteractor.vibrate(vibrationEvent, "UdfpsEnrollFragment")
|
||||
}
|
||||
|
||||
/** Indicates an error sent by the HAL has been acknowledged by the user */
|
||||
fun errorDialogShown(it: FingerEnrollState.EnrollError) {
|
||||
navigationViewModel.update(
|
||||
FingerprintAction.USER_CLICKED_FINISH,
|
||||
navStep,
|
||||
"${TAG}#userClickedStopEnrollingDialog",
|
||||
)
|
||||
}
|
||||
|
||||
class UdfpsEnrollmentFactory(
|
||||
private val vibrationInteractor: VibrationInteractor,
|
||||
private val displayDensityInteractor: DisplayDensityInteractor,
|
||||
@@ -332,6 +405,9 @@ class UdfpsViewModel(
|
||||
private val backgroundViewModel: BackgroundViewModel,
|
||||
private val sensorRepository: FingerprintSensorRepository,
|
||||
private val udfpsEnrollInteractor: UdfpsEnrollInteractor,
|
||||
private val fingerprintManager: FingerprintManagerInteractor,
|
||||
private val udfpsLastStepViewModel: UdfpsLastStepViewModel,
|
||||
private val accessibilityInteractor: AccessibilityInteractor,
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -348,6 +424,9 @@ class UdfpsViewModel(
|
||||
backgroundViewModel,
|
||||
sensorRepository,
|
||||
udfpsEnrollInteractor,
|
||||
fingerprintManager,
|
||||
udfpsLastStepViewModel,
|
||||
accessibilityInteractor,
|
||||
)
|
||||
as T
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ import android.util.PathParser
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import androidx.core.animation.addListener
|
||||
import androidx.core.graphics.toRect
|
||||
import androidx.core.graphics.toRectF
|
||||
import com.android.settings.R
|
||||
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
@@ -45,7 +45,6 @@ import kotlin.math.sin
|
||||
* various stages of enrollment
|
||||
*/
|
||||
class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeSet?) : Drawable() {
|
||||
private var targetAnimationDuration: Long = TARGET_ANIM_DURATION_LONG
|
||||
private var targetAnimatorSet: AnimatorSet? = null
|
||||
private val movingTargetFpIcon: Drawable
|
||||
private val fingerprintDrawable: ShapeDrawable
|
||||
@@ -55,7 +54,7 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
||||
@ColorInt private var movingTargetFill = 0
|
||||
private var currentScale = 1.0f
|
||||
private var alpha = 0
|
||||
private var guidedEnrollmentOffset: PointF? = null
|
||||
private var stopDrawing = false
|
||||
|
||||
/**
|
||||
* This is the physical location of the sensor. This rect will be updated by [drawSensorRectAt]
|
||||
@@ -63,15 +62,12 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
||||
private val sensorRectBounds: Rect = Rect()
|
||||
|
||||
/**
|
||||
* The following values are used to describe where the icon should be drawn. [currX] and [currY]
|
||||
* are changed based on the current guided enrollment step which is given by the
|
||||
* [UdfpsEnrollHelperV2]
|
||||
* The following values are used to describe where the icon should be drawn. [sensorLeftOffset]
|
||||
* and [sensorTopOffset] are changed based on the current guided enrollment step which is given by
|
||||
* the [UdfpsEnrollHelperV2]
|
||||
*/
|
||||
private var currX = 0f
|
||||
private var currY = 0f
|
||||
|
||||
private var sensorWidth = 0f
|
||||
private var sensorHeight = 0f
|
||||
private var sensorLeftOffset = 0f
|
||||
private var sensorTopOffset = 0f
|
||||
|
||||
init {
|
||||
fingerprintDrawable = createUdfpsIcon(context)
|
||||
@@ -131,29 +127,35 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
||||
* The [sensorRect] coordinates for the sensor area. The [sensorRect] should be the coordinates
|
||||
* with respect to its root frameview
|
||||
*/
|
||||
fun drawSensorRectAt(sensorRect: Rect) {
|
||||
Log.e(TAG, "UdfpsEnrollIcon#drawSensorRect($sensorRect)")
|
||||
fun drawSensorRectAt(overlayParams: UdfpsOverlayParams) {
|
||||
Log.e(TAG, "UdfpsEnrollIcon#drawSensorRect(${overlayParams.sensorBounds})")
|
||||
val sensorRect = overlayParams.sensorBounds
|
||||
sensorRectBounds.set(sensorRect)
|
||||
fingerprintDrawable.bounds = sensorRect
|
||||
movingTargetFpIcon.bounds = sensorRect
|
||||
currX = sensorRect.left.toFloat()
|
||||
currY = sensorRect.top.toFloat()
|
||||
sensorWidth = (sensorRect.right - sensorRect.left).toFloat()
|
||||
sensorHeight = (sensorRect.bottom - sensorRect.top).toFloat()
|
||||
|
||||
// End existing animation if we get an update of the sensor rect.
|
||||
targetAnimatorSet?.end()
|
||||
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
/** Stop drawing the fingerprint icon. */
|
||||
fun stopDrawing() {
|
||||
alpha = 0
|
||||
stopDrawing = true
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
/** Resume drawing the fingerprint icon */
|
||||
fun startDrawing() {
|
||||
alpha = 255
|
||||
stopDrawing = false
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
if (stopDrawing) {
|
||||
return
|
||||
}
|
||||
movingTargetFpIcon.alpha = alpha
|
||||
fingerprintDrawable.setAlpha(alpha)
|
||||
sensorOutlinePaint.setAlpha(alpha)
|
||||
@@ -165,23 +167,28 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
||||
fingerprintDrawable.draw(canvas)
|
||||
}
|
||||
|
||||
private fun getCurrLocation(): RectF =
|
||||
RectF(currX, currY, currX + sensorWidth, currY + sensorHeight)
|
||||
private fun getCurrLocation(): RectF {
|
||||
val x = sensorRectBounds.left + sensorLeftOffset
|
||||
val y = sensorRectBounds.top + sensorTopOffset
|
||||
return RectF(x, y, x + sensorRectBounds.width(), y + sensorRectBounds.height())
|
||||
}
|
||||
|
||||
private fun animateMovement(currentBounds: Rect, offsetRect: RectF, scaleMovement: Boolean) {
|
||||
if (currentBounds.equals(offsetRect)) {
|
||||
private fun animateMovement(leftOffset: Float, topOffset: Float, scaleMovement: Boolean) {
|
||||
if (leftOffset == sensorLeftOffset && topOffset == sensorTopOffset) {
|
||||
return
|
||||
}
|
||||
|
||||
val xAnimator = ValueAnimator.ofFloat(currentBounds.left.toFloat(), offsetRect.left)
|
||||
val currLocation = getCurrLocation()
|
||||
|
||||
val xAnimator = ValueAnimator.ofFloat(currLocation.left - sensorRectBounds.left, leftOffset)
|
||||
xAnimator.addUpdateListener {
|
||||
currX = it.animatedValue as Float
|
||||
sensorLeftOffset = it.animatedValue as Float
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
val yAnimator = ValueAnimator.ofFloat(currentBounds.top.toFloat(), offsetRect.top)
|
||||
val yAnimator = ValueAnimator.ofFloat(currLocation.top - sensorRectBounds.top, topOffset)
|
||||
yAnimator.addUpdateListener {
|
||||
currY = it.animatedValue as Float
|
||||
sensorTopOffset = it.animatedValue as Float
|
||||
invalidateSelf()
|
||||
}
|
||||
val animators = mutableListOf(xAnimator, yAnimator)
|
||||
@@ -199,6 +206,7 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
||||
animators.add(scaleAnimator)
|
||||
}
|
||||
|
||||
targetAnimatorSet?.cancel()
|
||||
targetAnimatorSet = AnimatorSet()
|
||||
|
||||
targetAnimatorSet?.let {
|
||||
@@ -209,51 +217,14 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This sets animation time to 0. This typically happens after an activity recreation, we don't
|
||||
* want to re-animate the progress/success animation with the default timer
|
||||
*/
|
||||
private fun setAnimationTimeToZero() {
|
||||
targetAnimationDuration = 0
|
||||
}
|
||||
|
||||
/** This sets animation timers back to normal, this happens after we have */
|
||||
private fun restoreAnimationTime() {
|
||||
targetAnimationDuration = TARGET_ANIM_DURATION_LONG
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates a change to guided enrollment has occurred. Also indicates if we are recreating the
|
||||
* view, in which case their is no need to animate the icon to whatever position it was in.
|
||||
*/
|
||||
fun updateGuidedEnrollment(point: PointF, isRecreating: Boolean) {
|
||||
guidedEnrollmentOffset = point
|
||||
if (isRecreating) {
|
||||
setAnimationTimeToZero()
|
||||
} else {
|
||||
restoreAnimationTime()
|
||||
}
|
||||
|
||||
val currentBounds = getCurrLocation().toRect()
|
||||
val offset = guidedEnrollmentOffset
|
||||
if (offset?.x != 0f && offset?.y != 0f) {
|
||||
val targetRect = Rect(sensorRectBounds).toRectF()
|
||||
// This is the desired location of the sensor rect, the [EnrollHelper]
|
||||
// offsets the initial sensor rect by a bit to get the user to move their finger a bit more.
|
||||
targetRect.offset(offset!!.x, offset!!.y)
|
||||
val shouldAnimateMovement = !currentBounds.equals(targetRect)
|
||||
if (shouldAnimateMovement) {
|
||||
targetAnimatorSet?.cancel()
|
||||
animateMovement(currentBounds, targetRect, true)
|
||||
} else {
|
||||
// If we are not offsetting the sensor, move it back to its original place
|
||||
animateMovement(currentBounds, sensorRectBounds.toRectF(), false)
|
||||
}
|
||||
} else {
|
||||
// If we are not offsetting the sensor, move it back to its original place
|
||||
animateMovement(currentBounds, sensorRectBounds.toRectF(), false)
|
||||
}
|
||||
invalidateSelf()
|
||||
fun updateGuidedEnrollment(point: PointF, isRecreating: Boolean) {
|
||||
val pointIsZero = point.x == 0f && point.y == 0f
|
||||
val shouldAnimateMovement = pointIsZero || !isRecreating
|
||||
animateMovement(point?.x ?: 0f, point?.y ?: 0f, shouldAnimateMovement)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -27,16 +27,15 @@ import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.Interpolator
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.animation.addListener
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.graphics.toRectF
|
||||
import com.android.internal.annotations.VisibleForTesting
|
||||
import com.android.settings.R
|
||||
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.max
|
||||
import kotlin.math.sin
|
||||
@@ -145,7 +144,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
|
||||
/** Indicates enrollment progress has occurred. */
|
||||
fun onEnrollmentProgress(remaining: Int, totalSteps: Int, isRecreating: Boolean = false) {
|
||||
|
||||
afterFirstTouch = true
|
||||
updateProgress(remaining, totalSteps, isRecreating)
|
||||
}
|
||||
@@ -216,8 +214,8 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
* Draws the progress with locations [sensorLocationX] [sensorLocationY], note these must be with
|
||||
* respect to the parent framelayout.
|
||||
*/
|
||||
fun drawProgressAt(sensorRect: Rect) {
|
||||
this.sensorRect.set(sensorRect)
|
||||
fun drawProgressAt(overlayParams: UdfpsOverlayParams) {
|
||||
this.sensorRect.set(overlayParams.sensorBounds)
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
@@ -249,8 +247,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
restoreAnimationTime()
|
||||
}
|
||||
|
||||
this.remainingSteps = remainingSteps
|
||||
this.totalSteps = totalSteps
|
||||
this.remainingSteps = remainingSteps
|
||||
this.totalSteps = totalSteps
|
||||
val targetProgress = (totalSteps - remainingSteps).toFloat().div(max(1, totalSteps))
|
||||
@@ -290,12 +286,8 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
checkMarkDrawable.bounds = newBounds
|
||||
checkMarkDrawable.setVisible(true, false)
|
||||
}
|
||||
doOnEnd {
|
||||
onFinishedCompletionAnimation?.let{
|
||||
it()
|
||||
}
|
||||
doOnEnd { onFinishedCompletionAnimation?.let { it() } }
|
||||
|
||||
}
|
||||
start()
|
||||
}
|
||||
}
|
||||
@@ -356,6 +348,7 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
private fun flashHelpFillColor() {
|
||||
if (fillColorAnimator != null && fillColorAnimator!!.isRunning) {
|
||||
fillColorAnimator!!.end()
|
||||
fillColorAnimator = null
|
||||
}
|
||||
@ColorInt val targetColor = helpColor
|
||||
fillColorAnimator =
|
||||
@@ -375,7 +368,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
* want to re-animate the progress/success animation with the default timer
|
||||
*/
|
||||
private fun setAnimationTimeToZero() {
|
||||
fillColorAnimationDuration = 0
|
||||
animateArcDuration = 0
|
||||
checkmarkAnimationDelayDuration = 0
|
||||
checkmarkAnimationDuration = 0
|
||||
@@ -383,7 +375,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
|
||||
/** This sets animation timers back to normal, this happens after we have */
|
||||
private fun restoreAnimationTime() {
|
||||
fillColorAnimationDuration = FILL_COLOR_ANIMATION_DURATION_MS
|
||||
animateArcDuration = PROGRESS_ANIMATION_DURATION_MS
|
||||
checkmarkAnimationDelayDuration = CHECKMARK_ANIMATION_DELAY_MS
|
||||
checkmarkAnimationDuration = CHECKMARK_ANIMATION_DURATION_MS
|
||||
|
||||
@@ -70,27 +70,11 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
fun setSensorRect(rect: Rect, sensorType: FingerprintSensorType) {
|
||||
this.sensorRect = rect
|
||||
this.fingerprintSensorType = sensorType
|
||||
findViewById<ImageView?>(R.id.udfps_enroll_animation_fp_progress_view)?.also {
|
||||
it.setImageDrawable(fingerprintProgressDrawable)
|
||||
}
|
||||
findViewById<ImageView>(R.id.udfps_enroll_animation_fp_view)?.also {
|
||||
it.setImageDrawable(fingerprintIcon)
|
||||
}
|
||||
|
||||
val rotation = display.rotation
|
||||
var displayInfo = DisplayInfo()
|
||||
context.display.getDisplayInfo(displayInfo)
|
||||
val rotation = displayInfo.rotation
|
||||
val scaleFactor = udfpsUtils.getScaleFactor(displayInfo)
|
||||
val overlayParams =
|
||||
UdfpsOverlayParams(
|
||||
sensorRect,
|
||||
fingerprintProgressDrawable.bounds,
|
||||
displayInfo.naturalWidth,
|
||||
displayInfo.naturalHeight,
|
||||
scaleFactor,
|
||||
rotation,
|
||||
sensorType.toInt(),
|
||||
)
|
||||
val parentView = parent as ViewGroup
|
||||
val coords = parentView.getLocationOnScreen()
|
||||
val parentLeft = coords[0]
|
||||
@@ -99,22 +83,44 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
// If the view has been rotated, we need to translate the sensor coordinates
|
||||
// to the new rotated view.
|
||||
when (rotation) {
|
||||
Surface.ROTATION_90,
|
||||
Surface.ROTATION_270 -> {
|
||||
Surface.ROTATION_90 -> {
|
||||
sensorRectOffset.set(
|
||||
sensorRectOffset.top,
|
||||
sensorRectOffset.left,
|
||||
sensorRectOffset.bottom,
|
||||
sensorRectOffset.right,
|
||||
)
|
||||
sensorRectOffset.offset(-parentLeft, -parentTop)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
// Translate the sensor position into UdfpsEnrollView's view space.
|
||||
sensorRectOffset.offset(-parentLeft, -parentTop)
|
||||
// When the view is rotated 270 degrees, 0,0 is the top corner left
|
||||
Surface.ROTATION_270 -> {
|
||||
sensorRectOffset.set(
|
||||
(displayInfo.naturalHeight - sensorRectOffset.bottom) - parentLeft,
|
||||
sensorRectOffset.left - parentTop,
|
||||
(displayInfo.naturalHeight - sensorRectOffset.top) - parentLeft,
|
||||
sensorRectOffset.right - parentTop,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
|
||||
fingerprintIcon.drawSensorRectAt(sensorRectOffset)
|
||||
fingerprintProgressDrawable.drawProgressAt(sensorRectOffset)
|
||||
sensorRectOffset.offset(-parentLeft, -parentTop)
|
||||
}
|
||||
}
|
||||
|
||||
// Translate the sensor position into UdfpsEnrollView's view space.
|
||||
val overlayParams =
|
||||
UdfpsOverlayParams(
|
||||
sensorRectOffset,
|
||||
fingerprintProgressDrawable.bounds,
|
||||
displayInfo.naturalWidth,
|
||||
displayInfo.naturalHeight,
|
||||
scaleFactor,
|
||||
rotation,
|
||||
sensorType.toInt(),
|
||||
)
|
||||
|
||||
fingerprintIcon.drawSensorRectAt(overlayParams)
|
||||
fingerprintProgressDrawable.drawProgressAt(overlayParams)
|
||||
|
||||
touchExplorationAnnouncer = TouchExplorationAnnouncer(context, this, overlayParams, udfpsUtils)
|
||||
}
|
||||
@@ -126,11 +132,8 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
onEnrollmentProgress(event.remainingSteps, event.totalStepsRequired)
|
||||
is FingerEnrollState.Acquired -> onAcquired(event.acquiredGood)
|
||||
is FingerEnrollState.EnrollHelp -> onEnrollmentHelp()
|
||||
is FingerEnrollState.PointerDown -> onPointerDown()
|
||||
is FingerEnrollState.PointerUp -> onPointerUp()
|
||||
is FingerEnrollState.OverlayShown -> overlayShown()
|
||||
is FingerEnrollState.EnrollError ->
|
||||
throw IllegalArgumentException("$TAG should not handle udfps error")
|
||||
// Else ignore
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +148,6 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
}
|
||||
}
|
||||
|
||||
private fun udfpsError(errMsgId: Int, errString: String) {}
|
||||
/**
|
||||
* Sends a touch exploration event to the [onHoverListener] this should only be used for
|
||||
* debugging.
|
||||
@@ -170,8 +172,15 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
onHoverListener = listener
|
||||
}
|
||||
|
||||
private fun overlayShown() {
|
||||
Log.e(TAG, "Implement overlayShown")
|
||||
/** Indicates the overlay has been shown */
|
||||
fun overlayShown() {
|
||||
Log.d(TAG, "Showing udfps overlay")
|
||||
findViewById<ImageView?>(R.id.udfps_enroll_animation_fp_progress_view)?.also {
|
||||
it.setImageDrawable(fingerprintProgressDrawable)
|
||||
}
|
||||
findViewById<ImageView>(R.id.udfps_enroll_animation_fp_view)?.also {
|
||||
it.setImageDrawable(fingerprintIcon)
|
||||
}
|
||||
}
|
||||
|
||||
/** Receive enroll progress event */
|
||||
@@ -190,16 +199,6 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
if (animateIfLastStepGood) fingerprintProgressDrawable.onLastStepAcquired()
|
||||
}
|
||||
|
||||
/** Receive onPointerDown event */
|
||||
private fun onPointerDown() {
|
||||
fingerprintIcon.stopDrawing()
|
||||
}
|
||||
|
||||
/** Receive onPointerUp event */
|
||||
private fun onPointerUp() {
|
||||
fingerprintIcon.startDrawing()
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
// Because the layout has changed, we need to recompute all locations.
|
||||
@@ -261,6 +260,17 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
fingerprintIcon.updateGuidedEnrollment(point, false)
|
||||
}
|
||||
|
||||
/** Indicates if the enroll icon should be drawn. */
|
||||
fun shouldDrawIcon(it: Boolean) {
|
||||
post {
|
||||
if (it) {
|
||||
fingerprintIcon.startDrawing()
|
||||
} else {
|
||||
fingerprintIcon.stopDrawing()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "UdfpsEnrollView"
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.android.systemui.biometrics.shared.model.FingerprintSensor
|
||||
@@ -68,7 +69,8 @@ class FingerprintEnrollEnrollingViewModel(
|
||||
val enrollFlow =
|
||||
enrollFlowShouldBeRunning.transformLatest {
|
||||
if (it) {
|
||||
fingerprintEnrollViewModel.enrollFlow.collect { event -> emit(event) }
|
||||
fingerprintEnrollViewModel.enrollFlow.collect { event ->
|
||||
emit(event) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,4 +84,8 @@ class FingerprintEnrollEnrollingViewModel(
|
||||
as T
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "FingerprintEnrollEnrollingViewModel"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ import java.util.List;
|
||||
public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment {
|
||||
public static final String KEY_DEVICE_ADDRESS = "device_address";
|
||||
private static final String TAG = "BTDeviceDetailsFrg";
|
||||
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
|
||||
|
||||
@VisibleForTesting
|
||||
static int EDIT_DEVICE_NAME_ITEM_ID = Menu.FIRST;
|
||||
@@ -95,11 +96,14 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
LocalBluetoothManager mManager;
|
||||
@VisibleForTesting
|
||||
CachedBluetoothDevice mCachedDevice;
|
||||
BluetoothAdapter mBluetoothAdapter;
|
||||
|
||||
@Nullable
|
||||
InputDevice mInputDevice;
|
||||
|
||||
private UserManager mUserManager;
|
||||
int mExtraControlViewWidth = 0;
|
||||
boolean mExtraControlUriLoaded = false;
|
||||
|
||||
private final BluetoothCallback mBluetoothCallback =
|
||||
new BluetoothCallback() {
|
||||
@@ -115,6 +119,16 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
}
|
||||
};
|
||||
|
||||
private final BluetoothAdapter.OnMetadataChangedListener mExtraControlMetadataListener =
|
||||
(device, key, value) -> {
|
||||
if (key == METADATA_FAST_PAIR_CUSTOMIZED_FIELDS
|
||||
&& mExtraControlViewWidth > 0
|
||||
&& !mExtraControlUriLoaded) {
|
||||
Log.i(TAG, "Update extra control UI because of metadata change.");
|
||||
updateExtraControlUri(mExtraControlViewWidth);
|
||||
}
|
||||
};
|
||||
|
||||
public BluetoothDeviceDetailsFragment() {
|
||||
super(DISALLOW_CONFIG_BLUETOOTH);
|
||||
}
|
||||
@@ -173,6 +187,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
public void onAttach(Context context) {
|
||||
mDeviceAddress = getArguments().getString(KEY_DEVICE_ADDRESS);
|
||||
mManager = getLocalBluetoothManager(context);
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
mCachedDevice = getCachedDevice(mDeviceAddress);
|
||||
mUserManager = getUserManager();
|
||||
|
||||
@@ -202,12 +217,18 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
: null);
|
||||
|
||||
mManager.getEventManager().registerCallback(mBluetoothCallback);
|
||||
mBluetoothAdapter.addOnMetadataChangedListener(
|
||||
mCachedDevice.getDevice(),
|
||||
context.getMainExecutor(),
|
||||
mExtraControlMetadataListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
mManager.getEventManager().unregisterCallback(mBluetoothCallback);
|
||||
mBluetoothAdapter.removeOnMetadataChangedListener(
|
||||
mCachedDevice.getDevice(), mExtraControlMetadataListener);
|
||||
}
|
||||
|
||||
private void updateExtraControlUri(int viewWidth) {
|
||||
@@ -222,9 +243,9 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
controlUri = Uri.parse(uri + viewWidth);
|
||||
} catch (NullPointerException exception) {
|
||||
Log.d(TAG, "unable to parse uri");
|
||||
controlUri = null;
|
||||
}
|
||||
}
|
||||
mExtraControlUriLoaded |= controlUri != null;
|
||||
final SlicePreferenceController slicePreferenceController = use(
|
||||
SlicePreferenceController.class);
|
||||
slicePreferenceController.setSliceUri(sliceEnabled ? controlUri : null);
|
||||
@@ -253,7 +274,8 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
if (view.getWidth() <= 0) {
|
||||
return;
|
||||
}
|
||||
updateExtraControlUri(view.getWidth() - getPaddingSize());
|
||||
mExtraControlViewWidth = view.getWidth() - getPaddingSize();
|
||||
updateExtraControlUri(mExtraControlViewWidth);
|
||||
view.getViewTreeObserver().removeOnGlobalLayoutListener(
|
||||
mOnGlobalLayoutListener);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.annotation.AttrRes;
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.icu.text.MessageFormat;
|
||||
import android.telephony.SubscriptionPlan;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
@@ -66,7 +67,7 @@ public class DataUsageSummaryPreference extends Preference {
|
||||
@Nullable
|
||||
private Long mCycleEndTimeMs;
|
||||
/** The time of the last update in standard milliseconds since the epoch */
|
||||
private long mSnapshotTimeMs;
|
||||
private long mSnapshotTimeMs = SubscriptionPlan.TIME_UNKNOWN;
|
||||
/** Name of carrier, or null if not available */
|
||||
private CharSequence mCarrierName;
|
||||
private CharSequence mLimitInfoText;
|
||||
|
||||
@@ -393,7 +393,7 @@ public class BatteryUtils {
|
||||
packageName, uid, ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED,
|
||||
mode == AppOpsManager.MODE_IGNORED,
|
||||
ActivityManager.RESTRICTION_REASON_USER,
|
||||
"settings", 0);
|
||||
"settings", ActivityManager.RESTRICTION_SOURCE_USER, 0L);
|
||||
}
|
||||
// Control whether app could run jobs in the background
|
||||
mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode);
|
||||
|
||||
@@ -767,6 +767,10 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
}
|
||||
|
||||
private class BatteryChartAccessibilityNodeProvider extends AccessibilityNodeProvider {
|
||||
private static final int UNDEFINED = Integer.MIN_VALUE;
|
||||
|
||||
private int mAccessibilityFocusNodeViewId = UNDEFINED;
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
|
||||
if (virtualViewId == AccessibilityNodeProvider.HOST_VIEW_ID) {
|
||||
@@ -794,6 +798,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
R.string.battery_usage_time_info_and_battery_level,
|
||||
slotTimeInfo,
|
||||
batteryLevelInfo));
|
||||
childInfo.setAccessibilityFocused(virtualViewId == mAccessibilityFocusNodeViewId);
|
||||
|
||||
final Rect bounds = new Rect();
|
||||
getBoundsOnScreen(bounds, true);
|
||||
@@ -815,10 +820,14 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
return true;
|
||||
|
||||
case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
|
||||
mAccessibilityFocusNodeViewId = virtualViewId;
|
||||
return sendAccessibilityEvent(
|
||||
virtualViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
|
||||
|
||||
case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
|
||||
if (mAccessibilityFocusNodeViewId == virtualViewId) {
|
||||
mAccessibilityFocusNodeViewId = UNDEFINED;
|
||||
}
|
||||
return sendAccessibilityEvent(
|
||||
virtualViewId,
|
||||
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
|
||||
|
||||
@@ -329,38 +329,6 @@ public class UiccSlotUtil {
|
||||
return INVALID_PHYSICAL_SLOT_ID;
|
||||
}
|
||||
|
||||
// Device | |Slot |
|
||||
// Working| |Mapping|
|
||||
// State |Type |Mode |Friendly name
|
||||
//--------------------------------------------------------------------------
|
||||
// Single |SIM pSIM [RIL 0] |1 |pSIM active
|
||||
// Single |SIM MEP Port #0 [RIL0] |2 |eSIM Port0 active
|
||||
// Single |SIM MEP Port #1 [RIL0] |2.1 |eSIM Port1 active
|
||||
// DSDS |pSIM [RIL 0] + MEP Port #0 [RIL 1] |3 |pSIM+Port0
|
||||
// DSDS |pSIM [RIL 0] + MEP Port #1 [RIL 1] |3.1 |pSIM+Port1
|
||||
// DSDS |MEP Port #0 [RIL 0] + MEP Port #1 [RIL1]|3.2 |Dual-Ports-A
|
||||
// DSDS |MEP Port #1 [RIL 0] + MEP Port #0 [RIL1]|4 |Dual-Ports-B
|
||||
//
|
||||
// The rules are:
|
||||
// 1. pSIM's logical slots always is [RIL 0].
|
||||
// 2. assign the new active port to the same stack that will be de-activated
|
||||
// For example: mode#3->mode#4
|
||||
// 3. Add an eSIM carrier or enable eSIM carrier. The cases are at the below.
|
||||
// 1) 1 => 2 / 2.1 / 3 / 3.1
|
||||
// 2) 2 => 1 / 3 / 3.2
|
||||
// 3) 2.1 => 3.1 / 4
|
||||
// 4) 3 => 4
|
||||
// 5) 3.1 => 3.2
|
||||
// Note:
|
||||
// 1) 2 <=> 2.1 blocked by LPA (reason: existing active port in SS so just re-use)
|
||||
// 2) 3 <=> 3.1 blocked by LPA (reason: if pSIM+an active port, re-use the active port)
|
||||
// 4. pSIM insertion or enabling
|
||||
// 1) 2 => 1 / 3
|
||||
// 2) 2.1 => 1 / 3.1
|
||||
// 3) 3.2 => 3 / 3.1
|
||||
// 4) 4 => 3 / 3.1
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
static Collection<UiccSlotMapping> prepareUiccSlotMappings(
|
||||
Collection<UiccSlotMapping> uiccSlotMappings, boolean isPsim, int physicalSlotId,
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.settings.network.telephony
|
||||
|
||||
import android.content.Context
|
||||
import android.os.UserManager
|
||||
import android.telephony.SubscriptionInfo
|
||||
import android.telephony.SubscriptionManager
|
||||
import android.telephony.TelephonyManager
|
||||
@@ -41,6 +40,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
/**
|
||||
* Preference controller for "IMEI"
|
||||
*/
|
||||
@@ -123,17 +123,21 @@ class MobileNetworkImeiPreferenceController(context: Context, key: String) :
|
||||
ImeiInfoDialogFragment.show(fragment, simSlot, preference.title.toString())
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getImei(): String {
|
||||
val phoneType = getPhoneType()
|
||||
return if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) mTelephonyManager.meid?: String()
|
||||
else mTelephonyManager.imei?: String()
|
||||
}
|
||||
|
||||
private fun getTitleForGsmPhone(): String {
|
||||
return mContext.getString(R.string.status_imei)
|
||||
return mContext.getString(
|
||||
if (isPrimaryImei()) R.string.imei_primary else R.string.status_imei)
|
||||
}
|
||||
|
||||
private fun getTitleForCdmaPhone(): String {
|
||||
return mContext.getString(R.string.status_meid_number)
|
||||
return mContext.getString(
|
||||
if (isPrimaryImei()) R.string.meid_primary else R.string.status_meid_number)
|
||||
}
|
||||
|
||||
private fun getTitle(): String {
|
||||
@@ -142,6 +146,28 @@ class MobileNetworkImeiPreferenceController(context: Context, key: String) :
|
||||
else getTitleForGsmPhone()
|
||||
}
|
||||
|
||||
/**
|
||||
* As per GSMA specification TS37, below Primary IMEI requirements are mandatory to support
|
||||
*
|
||||
* TS37_2.2_REQ_5
|
||||
* TS37_2.2_REQ_8 (Attached the document has description about this test cases)
|
||||
*/
|
||||
protected fun isPrimaryImei(): Boolean {
|
||||
val imei = getImei()
|
||||
var primaryImei = String()
|
||||
|
||||
try {
|
||||
primaryImei = mTelephonyManager.primaryImei
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "PrimaryImei not available. $exception")
|
||||
}
|
||||
return primaryImei == imei && isMultiSim()
|
||||
}
|
||||
|
||||
private fun isMultiSim(): Boolean {
|
||||
return mTelephonyManager.activeModemCount > 1
|
||||
}
|
||||
|
||||
fun getPhoneType(): Int {
|
||||
return mTelephonyManager.currentPhoneType
|
||||
}
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
package com.android.settings.notification.modes;
|
||||
|
||||
import android.app.AutomaticZenRule;
|
||||
import android.app.Flags;
|
||||
import android.content.Context;
|
||||
import android.service.notification.ZenPolicy;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -26,11 +27,17 @@ import androidx.preference.Preference;
|
||||
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Base class for any preference controllers pertaining to any single Zen mode.
|
||||
*/
|
||||
abstract class AbstractZenModePreferenceController extends AbstractPreferenceController {
|
||||
|
||||
private static final String TAG = "AbstractZenModePreferenceController";
|
||||
|
||||
@Nullable
|
||||
protected ZenModesBackend mBackend;
|
||||
|
||||
@@ -38,7 +45,7 @@ abstract class AbstractZenModePreferenceController extends AbstractPreferenceCon
|
||||
private ZenMode mZenMode;
|
||||
|
||||
@NonNull
|
||||
final String mKey;
|
||||
private final String mKey;
|
||||
|
||||
// ZenModesBackend should only be passed in if the preference controller may set the user's
|
||||
// policy for this zen mode. Otherwise, if the preference controller is essentially read-only
|
||||
@@ -67,20 +74,56 @@ abstract class AbstractZenModePreferenceController extends AbstractPreferenceCon
|
||||
updateState(preference);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ZenMode getMode() {
|
||||
return mZenMode;
|
||||
@Override
|
||||
public final void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
if (mZenMode != null) {
|
||||
updateState(preference, mZenMode);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AutomaticZenRule getAZR() {
|
||||
if (mZenMode == null || mZenMode.getRule() == null) {
|
||||
abstract void updateState(Preference preference, @NonNull ZenMode zenMode);
|
||||
|
||||
@Override
|
||||
public final CharSequence getSummary() {
|
||||
if (mZenMode != null) {
|
||||
return getSummary(mZenMode);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return mZenMode.getRule();
|
||||
}
|
||||
|
||||
/** Implementations of this class should override
|
||||
* {@link AbstractPreferenceController#updateState(Preference)} to specify what should
|
||||
* happen when the preference is updated */
|
||||
@Nullable
|
||||
protected CharSequence getSummary(@NonNull ZenMode zenMode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should call this method (or a more specific one, like {@link #savePolicy} from
|
||||
* their {@code onPreferenceChange()} or similar, in order to apply changes to the mode being
|
||||
* edited (e.g. {@code saveMode(mode -> { mode.setX(value); return mode; } }.
|
||||
*
|
||||
* @param updater Function to update the {@link ZenMode}. Modifying and returning the same
|
||||
* instance is ok.
|
||||
*/
|
||||
protected final boolean saveMode(Function<ZenMode, ZenMode> updater) {
|
||||
Preconditions.checkState(mBackend != null);
|
||||
ZenMode mode = mZenMode;
|
||||
if (mode == null) {
|
||||
Log.wtf(TAG, "Cannot save mode, it hasn't been loaded (" + getClass() + ")");
|
||||
return false;
|
||||
}
|
||||
mode = updater.apply(mode);
|
||||
mBackend.updateMode(mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected final boolean savePolicy(Function<ZenPolicy.Builder, ZenPolicy.Builder> updater) {
|
||||
return saveMode(mode -> {
|
||||
ZenPolicy.Builder policyBuilder = new ZenPolicy.Builder(mode.getPolicy());
|
||||
policyBuilder = updater.apply(policyBuilder);
|
||||
mode.setPolicy(policyBuilder.build());
|
||||
return mode;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.app.AutomaticZenRule;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.service.notification.ZenDeviceEffects;
|
||||
import android.service.notification.ZenPolicy;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -150,14 +151,6 @@ class ZenMode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use sparingly. If you're updating a policy field, use
|
||||
* {@link #setPolicy(android.service.notification.ZenPolicy)} instead.
|
||||
*/
|
||||
public void setAzr(@NonNull AutomaticZenRule newRule) {
|
||||
mRule = newRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the {@link ZenPolicy} of the associated {@link AutomaticZenRule} based on the
|
||||
* supplied policy. In some cases this involves conversions, so that the following call
|
||||
@@ -204,6 +197,13 @@ class ZenMode {
|
||||
mRule.setZenPolicy(policy);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ZenDeviceEffects getDeviceEffects() {
|
||||
return mRule.getDeviceEffects() != null
|
||||
? mRule.getDeviceEffects()
|
||||
: new ZenDeviceEffects.Builder().build();
|
||||
}
|
||||
|
||||
public boolean canBeDeleted() {
|
||||
return !mIsManualDnd;
|
||||
}
|
||||
|
||||
@@ -20,12 +20,15 @@ import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_I
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
|
||||
public class ZenModeCallsLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||
class ZenModeCallsLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||
|
||||
private ZenModeSummaryHelper mSummaryHelper;
|
||||
private final ZenModeSummaryHelper mSummaryHelper;
|
||||
|
||||
public ZenModeCallsLinkPreferenceController(Context context, String key,
|
||||
ZenModesBackend backend) {
|
||||
@@ -34,16 +37,15 @@ public class ZenModeCallsLinkPreferenceController extends AbstractZenModePrefere
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(MODE_ID, getMode().getId());
|
||||
bundle.putString(MODE_ID, zenMode.getId());
|
||||
// TODO(b/332937635): Update metrics category
|
||||
preference.setIntent(new SubSettingLauncher(mContext)
|
||||
.setDestination(ZenModeCallsFragment.class.getName())
|
||||
.setSourceMetricsCategory(0)
|
||||
.setArguments(bundle)
|
||||
.toIntent());
|
||||
preference.setSummary(mSummaryHelper.getCallsSettingSummary(getMode()));
|
||||
preference.setSummary(mSummaryHelper.getCallsSettingSummary(zenMode));
|
||||
}
|
||||
}
|
||||
@@ -16,16 +16,10 @@
|
||||
|
||||
package com.android.settings.notification.modes;
|
||||
|
||||
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS;
|
||||
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_EVENTS;
|
||||
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MEDIA;
|
||||
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS;
|
||||
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
|
||||
|
||||
import android.app.AutomaticZenRule;
|
||||
import android.content.Context;
|
||||
import android.service.notification.ZenDeviceEffects;
|
||||
import android.service.notification.ZenPolicy;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
@@ -38,9 +32,9 @@ public class ZenModeDisplayEffectPreferenceController extends AbstractZenModePre
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
TwoStatePreference pref = (TwoStatePreference) preference;
|
||||
ZenDeviceEffects effects = getMode().getRule().getDeviceEffects();
|
||||
ZenDeviceEffects effects = zenMode.getRule().getDeviceEffects();
|
||||
if (effects == null) {
|
||||
pref.setChecked(false);
|
||||
} else {
|
||||
@@ -62,33 +56,27 @@ public class ZenModeDisplayEffectPreferenceController extends AbstractZenModePre
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
|
||||
final boolean allow = (Boolean) newValue;
|
||||
|
||||
ZenDeviceEffects currEffects = getMode().getRule().getDeviceEffects();
|
||||
ZenDeviceEffects.Builder updatedEffects = currEffects == null
|
||||
? new ZenDeviceEffects.Builder()
|
||||
: new ZenDeviceEffects.Builder(getMode().getRule().getDeviceEffects());
|
||||
switch (getPreferenceKey()) {
|
||||
case "effect_greyscale":
|
||||
updatedEffects.setShouldDisplayGrayscale(allow);
|
||||
break;
|
||||
case "effect_aod":
|
||||
updatedEffects.setShouldSuppressAmbientDisplay(allow);
|
||||
break;
|
||||
case "effect_wallpaper":
|
||||
updatedEffects.setShouldDimWallpaper(allow);
|
||||
break;
|
||||
case "effect_dark_theme":
|
||||
updatedEffects.setShouldUseNightMode(allow);
|
||||
break;
|
||||
}
|
||||
AutomaticZenRule updatedAzr = new AutomaticZenRule.Builder(getMode().getRule())
|
||||
.setDeviceEffects(updatedEffects.build())
|
||||
.build();
|
||||
getMode().setAzr(updatedAzr);
|
||||
mBackend.updateMode(getMode());
|
||||
|
||||
return true;
|
||||
return saveMode(zenMode -> {
|
||||
ZenDeviceEffects.Builder updatedEffects = new ZenDeviceEffects.Builder(
|
||||
zenMode.getDeviceEffects());
|
||||
switch (getPreferenceKey()) {
|
||||
case "effect_greyscale":
|
||||
updatedEffects.setShouldDisplayGrayscale(allow);
|
||||
break;
|
||||
case "effect_aod":
|
||||
updatedEffects.setShouldSuppressAmbientDisplay(allow);
|
||||
break;
|
||||
case "effect_wallpaper":
|
||||
updatedEffects.setShouldDimWallpaper(allow);
|
||||
break;
|
||||
case "effect_dark_theme":
|
||||
updatedEffects.setShouldUseNightMode(allow);
|
||||
break;
|
||||
}
|
||||
zenMode.getRule().setDeviceEffects(updatedEffects.build());
|
||||
return zenMode;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,15 @@ import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_I
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
|
||||
public class ZenModeDisplayLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||
class ZenModeDisplayLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||
|
||||
ZenModeSummaryHelper mSummaryHelper;
|
||||
private final ZenModeSummaryHelper mSummaryHelper;
|
||||
|
||||
public ZenModeDisplayLinkPreferenceController(Context context, String key,
|
||||
ZenModesBackend backend) {
|
||||
@@ -34,10 +37,9 @@ public class ZenModeDisplayLinkPreferenceController extends AbstractZenModePrefe
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(MODE_ID, getMode().getId());
|
||||
bundle.putString(MODE_ID, zenMode.getId());
|
||||
// TODO(b/332937635): Update metrics category
|
||||
preference.setIntent(new SubSettingLauncher(mContext)
|
||||
.setDestination(ZenModeDisplayFragment.class.getName())
|
||||
@@ -47,7 +49,7 @@ public class ZenModeDisplayLinkPreferenceController extends AbstractZenModePrefe
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return mSummaryHelper.getDisplayEffectsSummary(getMode());
|
||||
public CharSequence getSummary(@NonNull ZenMode zenMode) {
|
||||
return mSummaryHelper.getDisplayEffectsSummary(zenMode);
|
||||
}
|
||||
}
|
||||
@@ -47,9 +47,8 @@ class ZenModeHeaderController extends AbstractZenModePreferenceController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
ZenMode mode = getMode();
|
||||
if (mode == null || mFragment == null) {
|
||||
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
if (mFragment == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -62,9 +61,9 @@ class ZenModeHeaderController extends AbstractZenModePreferenceController {
|
||||
}
|
||||
|
||||
FutureUtil.whenDone(
|
||||
mode.getIcon(IconLoader.getInstance(mContext)),
|
||||
zenMode.getIcon(IconLoader.getInstance(mContext)),
|
||||
icon -> mHeaderController.setIcon(icon)
|
||||
.setLabel(mode.getRule().getName())
|
||||
.setLabel(zenMode.getRule().getName())
|
||||
.done(false /* rebindActions */),
|
||||
mContext.getMainExecutor());
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@ import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_I
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
|
||||
public class ZenModeMessagesLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||
class ZenModeMessagesLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||
private final ZenModeSummaryHelper mSummaryHelper;
|
||||
|
||||
public ZenModeMessagesLinkPreferenceController(Context context, String key,
|
||||
@@ -33,11 +36,9 @@ public class ZenModeMessagesLinkPreferenceController extends AbstractZenModePref
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
|
||||
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(MODE_ID, getMode().getId());
|
||||
bundle.putString(MODE_ID, zenMode.getId());
|
||||
// TODO(b/332937635): Update metrics category
|
||||
preference.setIntent(new SubSettingLauncher(mContext)
|
||||
.setDestination(ZenModeMessagesFragment.class.getName())
|
||||
@@ -46,6 +47,6 @@ public class ZenModeMessagesLinkPreferenceController extends AbstractZenModePref
|
||||
.toIntent());
|
||||
|
||||
preference.setEnabled(true);
|
||||
preference.setSummary(mSummaryHelper.getMessagesSettingSummary(getMode().getPolicy()));
|
||||
preference.setSummary(mSummaryHelper.getMessagesSettingSummary(zenMode.getPolicy()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,16 @@ import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_I
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
|
||||
public class ZenModeNotifVisLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||
class ZenModeNotifVisLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||
|
||||
private final ZenModeSummaryHelper mSummaryBuilder;
|
||||
|
||||
ZenModeSummaryHelper mSummaryBuilder;
|
||||
public ZenModeNotifVisLinkPreferenceController(Context context, String key,
|
||||
ZenModesBackend backend) {
|
||||
super(context, key, backend);
|
||||
@@ -33,10 +37,9 @@ public class ZenModeNotifVisLinkPreferenceController extends AbstractZenModePref
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(MODE_ID, getMode().getId());
|
||||
bundle.putString(MODE_ID, zenMode.getId());
|
||||
// TODO(b/332937635): Update metrics category
|
||||
preference.setIntent(new SubSettingLauncher(mContext)
|
||||
.setDestination(ZenModeNotifVisFragment.class.getName())
|
||||
@@ -46,7 +49,7 @@ public class ZenModeNotifVisLinkPreferenceController extends AbstractZenModePref
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return mSummaryBuilder.getBlockedEffectsSummary(getMode());
|
||||
public CharSequence getSummary(@NonNull ZenMode zenMode) {
|
||||
return mSummaryBuilder.getBlockedEffectsSummary(zenMode);
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,12 @@ package com.android.settings.notification.modes;
|
||||
|
||||
import android.content.Context;
|
||||
import android.service.notification.ZenPolicy;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.CheckBoxPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.widget.DisabledCheckBoxPreference;
|
||||
|
||||
public class ZenModeNotifVisPreferenceController extends AbstractZenModePreferenceController
|
||||
@@ -53,13 +56,13 @@ public class ZenModeNotifVisPreferenceController extends AbstractZenModePreferen
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
|
||||
boolean suppressed = !getMode().getPolicy().isVisualEffectAllowed(mEffect, false);
|
||||
boolean suppressed = !zenMode.getPolicy().isVisualEffectAllowed(mEffect, false);
|
||||
boolean parentSuppressed = false;
|
||||
if (mParentSuppressedEffects != null) {
|
||||
for (@ZenPolicy.VisualEffect int parentEffect : mParentSuppressedEffects) {
|
||||
if (!getMode().getPolicy().isVisualEffectAllowed(parentEffect, true)) {
|
||||
if (!zenMode.getPolicy().isVisualEffectAllowed(parentEffect, true)) {
|
||||
parentSuppressed = true;
|
||||
}
|
||||
}
|
||||
@@ -77,15 +80,6 @@ public class ZenModeNotifVisPreferenceController extends AbstractZenModePreferen
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final boolean allowEffect = !((Boolean) newValue);
|
||||
|
||||
if (getMode().getPolicy().isVisualEffectAllowed(mEffect, true) != allowEffect) {
|
||||
ZenPolicy diffPolicy = new ZenPolicy.Builder()
|
||||
.showVisualEffect(mEffect, allowEffect)
|
||||
.build();
|
||||
getMode().setPolicy(diffPolicy);
|
||||
mBackend.updateMode(getMode());
|
||||
}
|
||||
|
||||
return true;
|
||||
return savePolicy(policy -> policy.showVisualEffect(mEffect, allowEffect));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,20 +16,22 @@
|
||||
|
||||
package com.android.settings.notification.modes;
|
||||
|
||||
|
||||
import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
|
||||
/**
|
||||
* Preference with a link and summary about what other sounds can break through the mode
|
||||
*/
|
||||
public class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||
class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||
|
||||
ZenModeSummaryHelper mSummaryHelper;
|
||||
private final ZenModeSummaryHelper mSummaryHelper;
|
||||
|
||||
public ZenModeOtherLinkPreferenceController(Context context, String key,
|
||||
ZenModesBackend backend) {
|
||||
@@ -38,15 +40,14 @@ public class ZenModeOtherLinkPreferenceController extends AbstractZenModePrefere
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(MODE_ID, getMode().getId());
|
||||
bundle.putString(MODE_ID, zenMode.getId());
|
||||
preference.setIntent(new SubSettingLauncher(mContext)
|
||||
.setDestination(ZenModeOtherFragment.class.getName())
|
||||
.setSourceMetricsCategory(0)
|
||||
.setArguments(bundle)
|
||||
.toIntent());
|
||||
preference.setSummary(mSummaryHelper.getOtherSoundCategoriesSummary(getMode()));
|
||||
preference.setSummary(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,12 @@ import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS
|
||||
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
|
||||
|
||||
import android.content.Context;
|
||||
import android.service.notification.ZenPolicy;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
public class ZenModeOtherPreferenceController extends AbstractZenModePreferenceController
|
||||
class ZenModeOtherPreferenceController extends AbstractZenModePreferenceController
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
public ZenModeOtherPreferenceController(Context context, String key,
|
||||
@@ -36,24 +37,15 @@ public class ZenModeOtherPreferenceController extends AbstractZenModePreferenceC
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
|
||||
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
TwoStatePreference pref = (TwoStatePreference) preference;
|
||||
pref.setChecked(getMode().getPolicy().isCategoryAllowed(getCategory(), true));
|
||||
pref.setChecked(zenMode.getPolicy().isCategoryAllowed(getCategory(), true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final boolean allow = (Boolean) newValue;
|
||||
|
||||
ZenPolicy diffPolicy = new ZenPolicy.Builder()
|
||||
.allowCategory(getCategory(), allow)
|
||||
.build();
|
||||
getMode().setPolicy(diffPolicy);
|
||||
mBackend.updateMode(getMode());
|
||||
|
||||
return true;
|
||||
return savePolicy(policy -> policy.allowCategory(getCategory(), allow));
|
||||
}
|
||||
|
||||
private int getCategory() {
|
||||
|
||||
@@ -16,21 +16,22 @@
|
||||
|
||||
package com.android.settings.notification.modes;
|
||||
|
||||
|
||||
import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
import com.android.settings.R;
|
||||
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
|
||||
/**
|
||||
* Preference with a link and summary about what calls and messages can break through the mode
|
||||
*/
|
||||
public class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||
class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||
|
||||
ZenModeSummaryHelper mSummaryHelper;
|
||||
private final ZenModeSummaryHelper mSummaryHelper;
|
||||
|
||||
public ZenModePeopleLinkPreferenceController(Context context, String key,
|
||||
ZenModesBackend backend) {
|
||||
@@ -39,16 +40,15 @@ public class ZenModePeopleLinkPreferenceController extends AbstractZenModePrefer
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(MODE_ID, getMode().getId());
|
||||
bundle.putString(MODE_ID, zenMode.getId());
|
||||
// TODO(b/332937635): Update metrics category
|
||||
preference.setIntent(new SubSettingLauncher(mContext)
|
||||
.setDestination(ZenModePeopleFragment.class.getName())
|
||||
.setSourceMetricsCategory(0)
|
||||
.setArguments(bundle)
|
||||
.toIntent());
|
||||
preference.setSummary(mSummaryHelper.getPeopleSummary(getMode()));
|
||||
preference.setSummary(mSummaryHelper.getPeopleSummary(zenMode));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,10 +36,13 @@ import android.provider.Contacts;
|
||||
import android.service.notification.ConversationChannelWrapper;
|
||||
import android.service.notification.ZenPolicy;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.notification.app.ConversationListSettings;
|
||||
@@ -59,7 +62,7 @@ import java.util.Map;
|
||||
* bypass DND for calls or messages, which may be one of the following values: starred contacts, all
|
||||
* contacts, priority conversations (for messages only), anyone, or no one.
|
||||
*/
|
||||
public class ZenModePrioritySendersPreferenceController
|
||||
class ZenModePrioritySendersPreferenceController
|
||||
extends AbstractZenModePreferenceController {
|
||||
private final boolean mIsMessages; // if this is false, then this preference is for calls
|
||||
|
||||
@@ -124,12 +127,12 @@ public class ZenModePrioritySendersPreferenceController
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
if (mIsMessages) {
|
||||
updateChannelCounts();
|
||||
}
|
||||
final int currContactsSetting = getPrioritySenders();
|
||||
final int currConversationsSetting = getPriorityConversationSenders();
|
||||
final int currContactsSetting = getPrioritySenders(zenMode.getPolicy());
|
||||
final int currConversationsSetting = getPriorityConversationSenders(zenMode.getPolicy());
|
||||
for (SelectorWithWidgetPreference pref : mSelectorPreferences) {
|
||||
// for each preference, check whether the current state matches what this state
|
||||
// would look like if the button were checked.
|
||||
@@ -173,17 +176,17 @@ public class ZenModePrioritySendersPreferenceController
|
||||
mNumImportantConversations = numImportantConversations;
|
||||
}
|
||||
|
||||
private int getPrioritySenders() {
|
||||
private int getPrioritySenders(ZenPolicy policy) {
|
||||
if (mIsMessages) {
|
||||
return getMode().getPolicy().getPriorityMessageSenders();
|
||||
return policy.getPriorityMessageSenders();
|
||||
} else {
|
||||
return getMode().getPolicy().getPriorityCallSenders();
|
||||
return policy.getPriorityCallSenders();
|
||||
}
|
||||
}
|
||||
|
||||
private int getPriorityConversationSenders() {
|
||||
private int getPriorityConversationSenders(ZenPolicy policy) {
|
||||
if (mIsMessages) {
|
||||
return getMode().getPolicy().getPriorityConversationSenders();
|
||||
return policy.getPriorityConversationSenders();
|
||||
}
|
||||
return CONVERSATION_SENDERS_UNSET;
|
||||
}
|
||||
@@ -419,29 +422,31 @@ public class ZenModePrioritySendersPreferenceController
|
||||
@VisibleForTesting
|
||||
SelectorWithWidgetPreference.OnClickListener mSelectorClickListener =
|
||||
new SelectorWithWidgetPreference.OnClickListener() {
|
||||
@Override
|
||||
public void onRadioButtonClicked(SelectorWithWidgetPreference preference) {
|
||||
// The settingsToSaveOnClick function takes whether the preference is a
|
||||
// checkbox into account to determine whether this selection is checked or unchecked.
|
||||
final int[] settingsToSave = settingsToSaveOnClick(preference,
|
||||
getPrioritySenders(), getPriorityConversationSenders());
|
||||
final int prioritySendersSetting = settingsToSave[0];
|
||||
final int priorityConvosSetting = settingsToSave[1];
|
||||
@Override
|
||||
public void onRadioButtonClicked(SelectorWithWidgetPreference preference) {
|
||||
savePolicy(policy -> {
|
||||
ZenPolicy previousPolicy = policy.build();
|
||||
// The settingsToSaveOnClick function takes whether the preference is a
|
||||
// checkbox into account to determine whether this selection is checked or
|
||||
// unchecked.
|
||||
final int[] settingsToSave = settingsToSaveOnClick(preference,
|
||||
getPrioritySenders(previousPolicy),
|
||||
getPriorityConversationSenders(previousPolicy));
|
||||
final int prioritySendersSetting = settingsToSave[0];
|
||||
final int priorityConvosSetting = settingsToSave[1];
|
||||
|
||||
ZenPolicy.Builder diffPolicy = new ZenPolicy.Builder();
|
||||
if (prioritySendersSetting != PEOPLE_TYPE_UNSET) {
|
||||
if (mIsMessages) {
|
||||
diffPolicy.allowMessages(prioritySendersSetting);
|
||||
|
||||
} else {
|
||||
diffPolicy.allowCalls(prioritySendersSetting);
|
||||
if (prioritySendersSetting != PEOPLE_TYPE_UNSET) {
|
||||
if (mIsMessages) {
|
||||
policy.allowMessages(prioritySendersSetting);
|
||||
} else {
|
||||
policy.allowCalls(prioritySendersSetting);
|
||||
}
|
||||
}
|
||||
if (mIsMessages && priorityConvosSetting != CONVERSATION_SENDERS_UNSET) {
|
||||
policy.allowConversations(priorityConvosSetting);
|
||||
}
|
||||
return policy;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (mIsMessages && priorityConvosSetting != CONVERSATION_SENDERS_UNSET) {
|
||||
diffPolicy.allowConversations(priorityConvosSetting);
|
||||
}
|
||||
getMode().setPolicy(diffPolicy.build());
|
||||
mBackend.updateMode(getMode());
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,20 +17,17 @@
|
||||
package com.android.settings.notification.modes;
|
||||
|
||||
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
|
||||
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
|
||||
import static android.service.notification.ZenPolicy.STATE_ALLOW;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.service.notification.ZenPolicy;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
public class ZenModeRepeatCallersPreferenceController extends AbstractZenModePreferenceController
|
||||
class ZenModeRepeatCallersPreferenceController extends AbstractZenModePreferenceController
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
private final int mRepeatCallersThreshold;
|
||||
@@ -43,14 +40,12 @@ public class ZenModeRepeatCallersPreferenceController extends AbstractZenModePre
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
|
||||
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
TwoStatePreference pref = (TwoStatePreference) preference;
|
||||
|
||||
boolean anyCallersCanBypassDnd =
|
||||
getMode().getPolicy().getPriorityCategoryCalls() == STATE_ALLOW
|
||||
&& getMode().getPolicy().getPriorityCallSenders() == PEOPLE_TYPE_ANYONE;
|
||||
zenMode.getPolicy().getPriorityCategoryCalls() == STATE_ALLOW
|
||||
&& zenMode.getPolicy().getPriorityCallSenders() == PEOPLE_TYPE_ANYONE;
|
||||
// if any caller can bypass dnd then repeat callers preference is disabled
|
||||
if (anyCallersCanBypassDnd) {
|
||||
pref.setEnabled(false);
|
||||
@@ -58,21 +53,16 @@ public class ZenModeRepeatCallersPreferenceController extends AbstractZenModePre
|
||||
} else {
|
||||
pref.setEnabled(true);
|
||||
pref.setChecked(
|
||||
getMode().getPolicy().getPriorityCategoryRepeatCallers() == STATE_ALLOW);
|
||||
zenMode.getPolicy().getPriorityCategoryRepeatCallers() == STATE_ALLOW);
|
||||
}
|
||||
|
||||
setRepeatCallerSummary(preference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
|
||||
final boolean allowRepeatCallers = (Boolean) newValue;
|
||||
ZenPolicy diffPolicy = new ZenPolicy.Builder()
|
||||
.allowRepeatCallers(allowRepeatCallers)
|
||||
.build();
|
||||
getMode().setPolicy(diffPolicy);
|
||||
mBackend.updateMode(getMode());
|
||||
return true;
|
||||
return savePolicy(policy -> policy.allowRepeatCallers(allowRepeatCallers));
|
||||
}
|
||||
|
||||
private void setRepeatCallerSummary(Preference preference) {
|
||||
|
||||
@@ -30,7 +30,6 @@ import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MESSAGES;
|
||||
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS;
|
||||
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS;
|
||||
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
|
||||
import static android.service.notification.ZenPolicy.STATE_DISALLOW;
|
||||
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
|
||||
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_BADGE;
|
||||
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT;
|
||||
@@ -44,7 +43,6 @@ import android.icu.text.MessageFormat;
|
||||
import android.service.notification.ZenDeviceEffects;
|
||||
import android.service.notification.ZenPolicy;
|
||||
|
||||
import android.util.SparseArray;
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -54,10 +52,10 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ZenModeSummaryHelper {
|
||||
class ZenModeSummaryHelper {
|
||||
|
||||
private Context mContext;
|
||||
private ZenModesBackend mBackend;
|
||||
private final Context mContext;
|
||||
private final ZenModesBackend mBackend;
|
||||
|
||||
public ZenModeSummaryHelper(Context context, ZenModesBackend backend) {
|
||||
mContext = context;
|
||||
|
||||
@@ -39,7 +39,7 @@ import java.util.Map;
|
||||
* 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.
|
||||
*/
|
||||
public class ZenModesListPreferenceController extends BasePreferenceController {
|
||||
class ZenModesListPreferenceController extends BasePreferenceController {
|
||||
protected static final String KEY = "zen_modes_list";
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -160,7 +160,8 @@ class PackageInfoPresenter(
|
||||
context.activityManager.noteAppRestrictionEnabled(
|
||||
packageName, uid,
|
||||
ActivityManager.RESTRICTION_LEVEL_FORCE_STOPPED, true,
|
||||
ActivityManager.RESTRICTION_REASON_USER, "settings", 0)
|
||||
ActivityManager.RESTRICTION_REASON_USER, "settings",
|
||||
ActivityManager.RESTRICTION_SOURCE_USER, 0)
|
||||
}
|
||||
context.activityManager.forceStopPackageAsUser(packageName, userId)
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.robolectric.shadows.ShadowAlarmManager;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** Tests of {@link BootBroadcastReceiver}. */
|
||||
@@ -56,6 +57,7 @@ public final class BootBroadcastReceiverTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mPeriodicJobManager = PeriodicJobManager.getInstance(mContext);
|
||||
mShadowAlarmManager = shadowOf(mContext.getSystemService(AlarmManager.class));
|
||||
@@ -136,7 +138,7 @@ public final class BootBroadcastReceiverTest {
|
||||
|
||||
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
|
||||
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
TimeUnit.MILLISECONDS.sleep(1000);
|
||||
assertThat(mDao.getAllAfter(0)).isEmpty();
|
||||
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
|
||||
}
|
||||
@@ -150,7 +152,7 @@ public final class BootBroadcastReceiverTest {
|
||||
|
||||
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
|
||||
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
TimeUnit.MILLISECONDS.sleep(1000);
|
||||
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
|
||||
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
|
||||
}
|
||||
@@ -168,7 +170,7 @@ public final class BootBroadcastReceiverTest {
|
||||
Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT,
|
||||
Intent.EXTRA_TIME_PREF_VALUE_USE_12_HOUR));
|
||||
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
TimeUnit.MILLISECONDS.sleep(1000);
|
||||
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
|
||||
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
|
||||
}
|
||||
@@ -182,7 +184,7 @@ public final class BootBroadcastReceiverTest {
|
||||
|
||||
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIMEZONE_CHANGED));
|
||||
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
TimeUnit.MILLISECONDS.sleep(1000);
|
||||
assertThat(mDao.getAllAfter(0)).isEmpty();
|
||||
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
|
||||
}
|
||||
|
||||
@@ -19,18 +19,16 @@ package com.android.settings.notification.modes;
|
||||
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
|
||||
import static android.service.notification.ZenPolicy.STATE_ALLOW;
|
||||
import static android.service.notification.ZenPolicy.STATE_DISALLOW;
|
||||
import static android.service.notification.ZenPolicy.STATE_UNSET;
|
||||
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
|
||||
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST;
|
||||
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK;
|
||||
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_STATUS_BAR;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -41,12 +39,10 @@ import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.platform.test.annotations.EnableFlags;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.service.notification.ZenDeviceEffects;
|
||||
import android.service.notification.ZenPolicy;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
import com.android.settings.notification.zen.ZenModeVisEffectPreferenceController;
|
||||
|
||||
import com.android.settings.widget.DisabledCheckBoxPreference;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -162,7 +158,7 @@ public final class ZenModeNotifVisPreferenceControllerTest {
|
||||
assertThat(captor.getValue().getPolicy().getVisualEffectStatusBar())
|
||||
.isEqualTo(STATE_DISALLOW);
|
||||
assertThat(captor.getValue().getPolicy().getVisualEffectNotificationList())
|
||||
.isEqualTo(STATE_UNSET);
|
||||
.isEqualTo(STATE_DISALLOW); // Untouched
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -211,7 +207,7 @@ public final class ZenModeNotifVisPreferenceControllerTest {
|
||||
assertThat(captor.getValue().getPolicy().getVisualEffectPeek())
|
||||
.isEqualTo(STATE_ALLOW);
|
||||
assertThat(captor.getValue().getPolicy().getVisualEffectNotificationList())
|
||||
.isEqualTo(STATE_UNSET);
|
||||
.isEqualTo(STATE_DISALLOW); // Untouched
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -236,6 +232,6 @@ public final class ZenModeNotifVisPreferenceControllerTest {
|
||||
assertThat(captor.getValue().getPolicy().getVisualEffectPeek())
|
||||
.isEqualTo(STATE_DISALLOW);
|
||||
assertThat(captor.getValue().getPolicy().getVisualEffectNotificationList())
|
||||
.isEqualTo(STATE_UNSET);
|
||||
.isEqualTo(STATE_ALLOW); // Untouched
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ public class ShadowUtils {
|
||||
private static ArraySet<String> sResultLinks = new ArraySet<>();
|
||||
private static boolean sIsBatteryPresent;
|
||||
private static boolean sIsMultipleBiometricsSupported;
|
||||
private static boolean sIsPrivateProfile;
|
||||
|
||||
@Implementation
|
||||
protected static int enforceSameOwner(Context context, int userId) {
|
||||
@@ -82,6 +83,7 @@ public class ShadowUtils {
|
||||
sResultLinks = new ArraySet<>();
|
||||
sIsBatteryPresent = true;
|
||||
sIsMultipleBiometricsSupported = false;
|
||||
sIsPrivateProfile = false;
|
||||
}
|
||||
|
||||
public static void setIsDemoUser(boolean isDemoUser) {
|
||||
@@ -188,4 +190,13 @@ public class ShadowUtils {
|
||||
public static void setIsMultipleBiometricsSupported(boolean isMultipleBiometricsSupported) {
|
||||
sIsMultipleBiometricsSupported = isMultipleBiometricsSupported;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected static boolean isPrivateProfile(int userId, Context context) {
|
||||
return sIsPrivateProfile;
|
||||
}
|
||||
|
||||
public static void setIsPrivateProfile(boolean isPrivateProfile) {
|
||||
sIsPrivateProfile = isPrivateProfile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,12 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
|
||||
|
||||
var rfpsIconTouchViewModel = RFPSIconTouchViewModel()
|
||||
var rfpsViewModel =
|
||||
RFPSViewModel(fingerprintEnrollEnrollingViewModel, navigationViewModel, orientationInteractor)
|
||||
RFPSViewModel(
|
||||
fingerprintEnrollEnrollingViewModel,
|
||||
navigationViewModel,
|
||||
orientationInteractor,
|
||||
interactor,
|
||||
)
|
||||
|
||||
val fingerprintEnrollConfirmationViewModel =
|
||||
FingerprintEnrollConfirmationViewModel(navigationViewModel, interactor)
|
||||
@@ -151,7 +156,8 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
|
||||
BackgroundViewModel::class.java -> backgroundViewModel
|
||||
RFPSIconTouchViewModel::class.java -> rfpsIconTouchViewModel
|
||||
FingerprintEnrollEnrollingViewModel::class.java -> fingerprintEnrollEnrollingViewModel
|
||||
FingerprintEnrollConfirmationViewModel::class.java -> fingerprintEnrollConfirmationViewModel
|
||||
FingerprintEnrollConfirmationViewModel::class.java ->
|
||||
fingerprintEnrollConfirmationViewModel
|
||||
else -> null
|
||||
}
|
||||
as T
|
||||
|
||||
@@ -19,18 +19,16 @@ package com.android.settings.network.telephony
|
||||
import android.content.Context
|
||||
import android.telephony.SubscriptionInfo
|
||||
import android.telephony.TelephonyManager
|
||||
import android.telephony.euicc.EuiccManager
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.dx.mockito.inline.extended.ExtendedMockito
|
||||
import com.android.internal.telephony.PhoneConstants
|
||||
import com.android.settings.R
|
||||
import com.android.settings.core.BasePreferenceController
|
||||
import com.android.settings.network.SubscriptionInfoListViewModel
|
||||
import com.android.settings.network.SubscriptionUtil
|
||||
import com.android.settingslib.CustomDialogPreferenceCompat
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.After
|
||||
@@ -62,6 +60,8 @@ class MobileNetworkImeiPreferenceControllerTest {
|
||||
on { currentPhoneType } doReturn TelephonyManager.PHONE_TYPE_GSM
|
||||
on { imei } doReturn mockImei
|
||||
on { meid } doReturn mockImei
|
||||
on { primaryImei } doReturn mockImei
|
||||
on { activeModemCount } doReturn 2
|
||||
}
|
||||
|
||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||
@@ -90,7 +90,7 @@ class MobileNetworkImeiPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshData_getPhoneNumber_preferenceSummaryIsExpected() = runBlocking {
|
||||
fun refreshData_getImei_preferenceSummaryIsExpected() = runBlocking {
|
||||
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
|
||||
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
|
||||
listOf(
|
||||
@@ -110,6 +110,50 @@ class MobileNetworkImeiPreferenceControllerTest {
|
||||
assertThat(preference.summary).isEqualTo(mockImei)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshData_getImeiTitle_showImei() = runBlocking {
|
||||
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
|
||||
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
|
||||
listOf(
|
||||
SUB_INFO_1,
|
||||
SUB_INFO_2
|
||||
)
|
||||
)
|
||||
var mockSubId = 2
|
||||
controller.init(mockFragment, mockSubId)
|
||||
mockImei = "test imei"
|
||||
mockTelephonyManager.stub {
|
||||
on { imei } doReturn mockImei
|
||||
on { primaryImei } doReturn ""
|
||||
}
|
||||
|
||||
controller.refreshData(SUB_INFO_2)
|
||||
|
||||
assertThat(preference.title).isEqualTo(context.getString(R.string.status_imei))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshData_getPrimaryImeiTitle_showPrimaryImei() = runBlocking {
|
||||
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
|
||||
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
|
||||
listOf(
|
||||
SUB_INFO_1,
|
||||
SUB_INFO_2
|
||||
)
|
||||
)
|
||||
var mockSubId = 2
|
||||
controller.init(mockFragment, mockSubId)
|
||||
mockImei = "test imei"
|
||||
mockTelephonyManager.stub {
|
||||
on { imei } doReturn mockImei
|
||||
on { primaryImei } doReturn mockImei
|
||||
}
|
||||
|
||||
controller.refreshData(SUB_INFO_2)
|
||||
|
||||
assertThat(preference.title).isEqualTo(context.getString(R.string.imei_primary))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getAvailabilityStatus_notSimHardwareVisible() {
|
||||
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(false)
|
||||
|
||||
Reference in New Issue
Block a user