Merge "Fix Fingerprint enrollment UI when display size is largest." into udc-qpr-dev am: 6cb1f9401d
am: 81587b1864
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/23418521 Change-Id: I47013f77ec1436048a41afbba34bdb4eeaa6ac6b Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -1,101 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright (C) 2021 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<com.google.android.setupdesign.GlifLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/setup_wizard_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout="@layout/sud_glif_blank_template"
|
|
||||||
style="?attr/fingerprint_layout_theme">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:clipChildren="false">
|
|
||||||
|
|
||||||
<!-- Both texts are kept as separate text views so it doesn't jump around in portrait.
|
|
||||||
See layouts/fingerprint_enroll_enrolling_base.xml. -->
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/layout_container"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginStart="?attr/sudMarginStart"
|
|
||||||
android:layout_marginEnd="@dimen/enroll_margin_end"
|
|
||||||
android:layout_marginBottom="@dimen/sud_content_frame_padding_bottom"
|
|
||||||
android:paddingStart="@dimen/enroll_padding_start"
|
|
||||||
android:paddingEnd="@dimen/enroll_padding_end"
|
|
||||||
android:clipChildren="false"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:clipChildren="false"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:fillViewport="true">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:clipChildren="false"
|
|
||||||
android:clipToPadding="false">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/sud_layout_icon"
|
|
||||||
style="@style/SudGlifIcon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:scaleType="fitStart"
|
|
||||||
android:layout_marginStart="0dp"
|
|
||||||
android:layout_marginEnd="0dp"
|
|
||||||
android:src="@drawable/ic_lock" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/suc_layout_title"
|
|
||||||
style="@style/SudGlifHeaderTitle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="0dp"
|
|
||||||
android:layout_marginEnd="0dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/SudDescription.Glif"
|
|
||||||
android:id="@+id/sud_layout_subtitle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"/>
|
|
||||||
|
|
||||||
<Space
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<include layout="@layout/udfps_enroll_view" />
|
|
||||||
</com.google.android.setupdesign.GlifLayout>
|
|
@@ -15,7 +15,7 @@
|
|||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<com.google.android.setupdesign.GlifLayout
|
<com.android.settings.biometrics.fingerprint.UdfpsEnrollEnrollingView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
@@ -78,4 +78,4 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</com.google.android.setupdesign.GlifLayout>
|
</com.android.settings.biometrics.fingerprint.UdfpsEnrollEnrollingView>
|
||||||
|
@@ -133,6 +133,7 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
|||||||
protected long mChallenge;
|
protected long mChallenge;
|
||||||
protected boolean mFromSettingsSummary;
|
protected boolean mFromSettingsSummary;
|
||||||
protected FooterBarMixin mFooterBarMixin;
|
protected FooterBarMixin mFooterBarMixin;
|
||||||
|
protected boolean mShouldSetFooterBarBackground = true;
|
||||||
@Nullable
|
@Nullable
|
||||||
protected ScreenSizeFoldProvider mScreenSizeFoldProvider;
|
protected ScreenSizeFoldProvider mScreenSizeFoldProvider;
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -191,12 +192,14 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
|||||||
super.onPostCreate(savedInstanceState);
|
super.onPostCreate(savedInstanceState);
|
||||||
initViews();
|
initViews();
|
||||||
|
|
||||||
@SuppressLint("VisibleForTests")
|
if (mShouldSetFooterBarBackground) {
|
||||||
final LinearLayout buttonContainer = mFooterBarMixin != null
|
@SuppressLint("VisibleForTests")
|
||||||
? mFooterBarMixin.getButtonContainer()
|
final LinearLayout buttonContainer = mFooterBarMixin != null
|
||||||
: null;
|
? mFooterBarMixin.getButtonContainer()
|
||||||
if (buttonContainer != null) {
|
: null;
|
||||||
buttonContainer.setBackgroundColor(getBackgroundColor());
|
if (buttonContainer != null) {
|
||||||
|
buttonContainer.setBackgroundColor(getBackgroundColor());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +334,7 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ColorInt
|
@ColorInt
|
||||||
private int getBackgroundColor() {
|
public int getBackgroundColor() {
|
||||||
final ColorStateList stateList = Utils.getColorAttr(this, android.R.attr.windowBackground);
|
final ColorStateList stateList = Utils.getColorAttr(this, android.R.attr.windowBackground);
|
||||||
return stateList != null ? stateList.getDefaultColor() : Color.TRANSPARENT;
|
return stateList != null ? stateList.getDefaultColor() : Color.TRANSPARENT;
|
||||||
}
|
}
|
||||||
|
@@ -32,10 +32,8 @@ import android.content.Intent;
|
|||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Point;
|
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.PorterDuffColorFilter;
|
import android.graphics.PorterDuffColorFilter;
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.drawable.Animatable2;
|
import android.graphics.drawable.Animatable2;
|
||||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
@@ -48,22 +46,16 @@ import android.os.VibrationAttributes;
|
|||||||
import android.os.VibrationEffect;
|
import android.os.VibrationEffect;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.FeatureFlagUtils;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.DisplayInfo;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.OrientationEventListener;
|
import android.view.OrientationEventListener;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||||
import android.view.animation.AnimationUtils;
|
import android.view.animation.AnimationUtils;
|
||||||
import android.view.animation.Interpolator;
|
import android.view.animation.Interpolator;
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -79,25 +71,20 @@ import com.android.settings.biometrics.BiometricUtils;
|
|||||||
import com.android.settings.biometrics.BiometricsEnrollEnrolling;
|
import com.android.settings.biometrics.BiometricsEnrollEnrolling;
|
||||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||||
import com.android.settingslib.display.DisplayDensityUtils;
|
import com.android.settingslib.display.DisplayDensityUtils;
|
||||||
import com.android.settingslib.udfps.UdfpsOverlayParams;
|
|
||||||
import com.android.settingslib.udfps.UdfpsUtils;
|
|
||||||
|
|
||||||
import com.airbnb.lottie.LottieAnimationView;
|
import com.airbnb.lottie.LottieAnimationView;
|
||||||
import com.airbnb.lottie.LottieCompositionFactory;
|
import com.airbnb.lottie.LottieCompositionFactory;
|
||||||
import com.airbnb.lottie.LottieProperty;
|
import com.airbnb.lottie.LottieProperty;
|
||||||
import com.airbnb.lottie.model.KeyPath;
|
import com.airbnb.lottie.model.KeyPath;
|
||||||
import com.google.android.setupcompat.template.FooterActionButton;
|
|
||||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||||
import com.google.android.setupcompat.template.FooterButton;
|
import com.google.android.setupcompat.template.FooterButton;
|
||||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||||
import com.google.android.setupdesign.GlifLayout;
|
|
||||||
import com.google.android.setupdesign.template.DescriptionMixin;
|
import com.google.android.setupdesign.template.DescriptionMixin;
|
||||||
import com.google.android.setupdesign.template.HeaderMixin;
|
import com.google.android.setupdesign.template.HeaderMixin;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity which handles the actual enrolling for fingerprint.
|
* Activity which handles the actual enrolling for fingerprint.
|
||||||
@@ -176,8 +163,6 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@Nullable
|
@Nullable
|
||||||
UdfpsEnrollHelper mUdfpsEnrollHelper;
|
UdfpsEnrollHelper mUdfpsEnrollHelper;
|
||||||
// TODO(b/260617060): Do not hard-code mScaleFactor, referring to AuthController.
|
|
||||||
private float mScaleFactor = 1.0f;
|
|
||||||
private ObjectAnimator mProgressAnim;
|
private ObjectAnimator mProgressAnim;
|
||||||
private TextView mErrorText;
|
private TextView mErrorText;
|
||||||
private Interpolator mFastOutSlowInInterpolator;
|
private Interpolator mFastOutSlowInInterpolator;
|
||||||
@@ -206,7 +191,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
|||||||
private boolean mHaveShownSfpsLeftEdgeLottie;
|
private boolean mHaveShownSfpsLeftEdgeLottie;
|
||||||
private boolean mHaveShownSfpsRightEdgeLottie;
|
private boolean mHaveShownSfpsRightEdgeLottie;
|
||||||
private boolean mShouldShowLottie;
|
private boolean mShouldShowLottie;
|
||||||
private UdfpsUtils mUdfpsUtils;
|
|
||||||
private ObjectAnimator mHelpAnimation;
|
private ObjectAnimator mHelpAnimation;
|
||||||
|
|
||||||
private OrientationEventListener mOrientationEventListener;
|
private OrientationEventListener mOrientationEventListener;
|
||||||
@@ -251,88 +236,17 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
|||||||
|
|
||||||
mAccessibilityManager = getSystemService(AccessibilityManager.class);
|
mAccessibilityManager = getSystemService(AccessibilityManager.class);
|
||||||
mIsAccessibilityEnabled = mAccessibilityManager.isEnabled();
|
mIsAccessibilityEnabled = mAccessibilityManager.isEnabled();
|
||||||
mUdfpsUtils = new UdfpsUtils();
|
|
||||||
|
|
||||||
final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(
|
|
||||||
Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL);
|
|
||||||
listenOrientationEvent();
|
listenOrientationEvent();
|
||||||
|
|
||||||
if (mCanAssumeUdfps) {
|
if (mCanAssumeUdfps) {
|
||||||
int rotation = getApplicationContext().getDisplay().getRotation();
|
final UdfpsEnrollEnrollingView layout =
|
||||||
final GlifLayout layout = (GlifLayout) getLayoutInflater().inflate(
|
(UdfpsEnrollEnrollingView) getLayoutInflater().inflate(
|
||||||
R.layout.udfps_enroll_enrolling, null, false);
|
R.layout.udfps_enroll_enrolling, null, false);
|
||||||
final UdfpsEnrollView udfpsEnrollView = layout.findViewById(R.id.udfps_animation_view);
|
setUdfpsEnrollHelper();
|
||||||
updateUdfpsEnrollView(udfpsEnrollView, props.get(0));
|
layout.initView(props.get(0), mUdfpsEnrollHelper, mAccessibilityManager);
|
||||||
switch (rotation) {
|
|
||||||
case Surface.ROTATION_90:
|
|
||||||
final View sudContent = layout.findViewById(R.id.sud_layout_content);
|
|
||||||
if (sudContent != null) {
|
|
||||||
sudContent.setPadding(sudContent.getPaddingLeft(), 0,
|
|
||||||
sudContent.getPaddingRight(), sudContent.getPaddingBottom());
|
|
||||||
}
|
|
||||||
|
|
||||||
final LinearLayout layoutContainer = layout.findViewById(
|
setContentView(layout);
|
||||||
R.id.layout_container);
|
|
||||||
final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT);
|
|
||||||
|
|
||||||
lp.setMarginEnd((int) getResources().getDimension(
|
|
||||||
R.dimen.rotation_90_enroll_margin_end));
|
|
||||||
layoutContainer.setPaddingRelative((int) getResources().getDimension(
|
|
||||||
R.dimen.rotation_90_enroll_padding_start), 0, isLayoutRtl
|
|
||||||
? 0 : (int) getResources().getDimension(
|
|
||||||
R.dimen.rotation_90_enroll_padding_end), 0);
|
|
||||||
layoutContainer.setLayoutParams(lp);
|
|
||||||
|
|
||||||
setOnHoverListener(true, layout, udfpsEnrollView);
|
|
||||||
setContentView(layout, lp);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Surface.ROTATION_0:
|
|
||||||
case Surface.ROTATION_180:
|
|
||||||
// In the portrait mode, layout_container's height is 0, so it's
|
|
||||||
// always shown at the bottom of the screen.
|
|
||||||
final FrameLayout portraitLayoutContainer = layout.findViewById(
|
|
||||||
R.id.layout_container);
|
|
||||||
|
|
||||||
// In the portrait mode, the title and lottie animation view may
|
|
||||||
// overlap when title needs three lines, so adding some paddings
|
|
||||||
// between them, and adjusting the fp progress view here accordingly.
|
|
||||||
final int layoutLottieAnimationPadding = (int) getResources()
|
|
||||||
.getDimension(R.dimen.udfps_lottie_padding_top);
|
|
||||||
portraitLayoutContainer.setPadding(0,
|
|
||||||
layoutLottieAnimationPadding, 0, 0);
|
|
||||||
final ImageView progressView = udfpsEnrollView.findViewById(
|
|
||||||
R.id.udfps_enroll_animation_fp_progress_view);
|
|
||||||
progressView.setPadding(0, -(layoutLottieAnimationPadding),
|
|
||||||
0, layoutLottieAnimationPadding);
|
|
||||||
final ImageView fingerprintView = udfpsEnrollView.findViewById(
|
|
||||||
R.id.udfps_enroll_animation_fp_view);
|
|
||||||
fingerprintView.setPadding(0, -layoutLottieAnimationPadding,
|
|
||||||
0, layoutLottieAnimationPadding);
|
|
||||||
|
|
||||||
// TODO(b/260970216) Instead of hiding the description text view, we should
|
|
||||||
// make the header view scrollable if the text is too long.
|
|
||||||
// If description text view has overlap with udfps progress view, hide it.
|
|
||||||
View view = layout.getDescriptionTextView();
|
|
||||||
layout.getViewTreeObserver().addOnDrawListener(() -> {
|
|
||||||
if (view.getVisibility() == View.VISIBLE
|
|
||||||
&& hasOverlap(view, udfpsEnrollView)) {
|
|
||||||
view.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setOnHoverListener(false, layout, udfpsEnrollView);
|
|
||||||
setContentView(layout);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Surface.ROTATION_270:
|
|
||||||
default:
|
|
||||||
setOnHoverListener(true, layout, udfpsEnrollView);
|
|
||||||
setContentView(layout);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
|
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
|
||||||
} else if (mCanAssumeSfps) {
|
} else if (mCanAssumeSfps) {
|
||||||
setContentView(R.layout.sfps_enroll_enrolling);
|
setContentView(R.layout.sfps_enroll_enrolling);
|
||||||
@@ -372,22 +286,11 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
|||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FeatureFlagUtils.isEnabled(getApplicationContext(),
|
// If it's udfps, set the background color only for secondary button if necessary.
|
||||||
FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) {
|
if (mCanAssumeUdfps) {
|
||||||
// Remove the space view and make the width of footer button container WRAP_CONTENT
|
mShouldSetFooterBarBackground = false;
|
||||||
// to avoid hiding the udfps view progress bar bottom.
|
((UdfpsEnrollEnrollingView) getLayout()).setSecondaryButtonBackground(
|
||||||
final LinearLayout buttonContainer = mFooterBarMixin.getButtonContainer();
|
getBackgroundColor());
|
||||||
View spaceView = null;
|
|
||||||
for (int i = 0; i < buttonContainer.getChildCount(); i++) {
|
|
||||||
if (!(buttonContainer.getChildAt(i) instanceof FooterActionButton)) {
|
|
||||||
spaceView = buttonContainer.getChildAt(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (spaceView != null) {
|
|
||||||
spaceView.setVisibility(View.GONE);
|
|
||||||
buttonContainer.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final LayerDrawable fingerprintDrawable = mProgressBar != null
|
final LayerDrawable fingerprintDrawable = mProgressBar != null
|
||||||
@@ -1230,30 +1133,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private UdfpsEnrollView updateUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView,
|
private void setUdfpsEnrollHelper() {
|
||||||
FingerprintSensorPropertiesInternal udfpsProps) {
|
|
||||||
DisplayInfo displayInfo = new DisplayInfo();
|
|
||||||
getDisplay().getDisplayInfo(displayInfo);
|
|
||||||
mScaleFactor = mUdfpsUtils.getScaleFactor(displayInfo);
|
|
||||||
Rect udfpsBounds = udfpsProps.getLocation().getRect();
|
|
||||||
udfpsBounds.scale(mScaleFactor);
|
|
||||||
|
|
||||||
final Rect overlayBounds = new Rect(
|
|
||||||
0, /* left */
|
|
||||||
displayInfo.getNaturalHeight() / 2, /* top */
|
|
||||||
displayInfo.getNaturalWidth(), /* right */
|
|
||||||
displayInfo.getNaturalHeight() /* botom */);
|
|
||||||
|
|
||||||
UdfpsOverlayParams params = new UdfpsOverlayParams(
|
|
||||||
udfpsBounds,
|
|
||||||
overlayBounds,
|
|
||||||
displayInfo.getNaturalWidth(),
|
|
||||||
displayInfo.getNaturalHeight(),
|
|
||||||
mScaleFactor,
|
|
||||||
displayInfo.rotation);
|
|
||||||
|
|
||||||
udfpsEnrollView.setOverlayParams(params);
|
|
||||||
|
|
||||||
mUdfpsEnrollHelper = (UdfpsEnrollHelper) getSupportFragmentManager().findFragmentByTag(
|
mUdfpsEnrollHelper = (UdfpsEnrollHelper) getSupportFragmentManager().findFragmentByTag(
|
||||||
FingerprintEnrollEnrolling.TAG_UDFPS_HELPER);
|
FingerprintEnrollEnrolling.TAG_UDFPS_HELPER);
|
||||||
if (mUdfpsEnrollHelper == null) {
|
if (mUdfpsEnrollHelper == null) {
|
||||||
@@ -1263,57 +1143,6 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
|||||||
.add(mUdfpsEnrollHelper, FingerprintEnrollEnrolling.TAG_UDFPS_HELPER)
|
.add(mUdfpsEnrollHelper, FingerprintEnrollEnrolling.TAG_UDFPS_HELPER)
|
||||||
.commitAllowingStateLoss();
|
.commitAllowingStateLoss();
|
||||||
}
|
}
|
||||||
udfpsEnrollView.setEnrollHelper(mUdfpsEnrollHelper);
|
|
||||||
|
|
||||||
return udfpsEnrollView;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setOnHoverListener(boolean isLandscape, GlifLayout enrollLayout,
|
|
||||||
UdfpsEnrollView udfpsEnrollView) {
|
|
||||||
if (!mIsAccessibilityEnabled) return;
|
|
||||||
|
|
||||||
final Context context = getApplicationContext();
|
|
||||||
final View.OnHoverListener onHoverListener = (v, event) -> {
|
|
||||||
// Map the touch to portrait mode if the device is in
|
|
||||||
// landscape mode.
|
|
||||||
final Point scaledTouch =
|
|
||||||
mUdfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0),
|
|
||||||
event, udfpsEnrollView.getOverlayParams());
|
|
||||||
|
|
||||||
if (mUdfpsUtils.isWithinSensorArea(event.getPointerId(0), event,
|
|
||||||
udfpsEnrollView.getOverlayParams())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String theStr = mUdfpsUtils.onTouchOutsideOfSensorArea(
|
|
||||||
mAccessibilityManager.isTouchExplorationEnabled(), context,
|
|
||||||
scaledTouch.x, scaledTouch.y, udfpsEnrollView.getOverlayParams());
|
|
||||||
if (theStr != null) {
|
|
||||||
v.announceForAccessibility(theStr);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
enrollLayout.findManagedViewById(isLandscape ? R.id.sud_landscape_content_area
|
|
||||||
: R.id.sud_layout_content).setOnHoverListener(onHoverListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@VisibleForTesting boolean hasOverlap(View view1, View view2) {
|
|
||||||
int[] firstPosition = new int[2];
|
|
||||||
int[] secondPosition = new int[2];
|
|
||||||
|
|
||||||
view1.getLocationOnScreen(firstPosition);
|
|
||||||
view2.getLocationOnScreen(secondPosition);
|
|
||||||
|
|
||||||
// Rect constructor parameters: left, top, right, bottom
|
|
||||||
Rect rectView1 = new Rect(firstPosition[0], firstPosition[1],
|
|
||||||
firstPosition[0] + view1.getMeasuredWidth(),
|
|
||||||
firstPosition[1] + view1.getMeasuredHeight());
|
|
||||||
Rect rectView2 = new Rect(secondPosition[0], secondPosition[1],
|
|
||||||
secondPosition[0] + view2.getMeasuredWidth(),
|
|
||||||
secondPosition[1] + view2.getMeasuredHeight());
|
|
||||||
return rectView1.intersect(rectView2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class IconTouchDialog extends InstrumentedDialogFragment {
|
public static class IconTouchDialog extends InstrumentedDialogFragment {
|
||||||
|
@@ -0,0 +1,237 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.fingerprint;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.DisplayInfo;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.accessibility.AccessibilityManager;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.ColorInt;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settingslib.udfps.UdfpsOverlayParams;
|
||||||
|
import com.android.settingslib.udfps.UdfpsUtils;
|
||||||
|
|
||||||
|
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||||
|
import com.google.android.setupdesign.GlifLayout;
|
||||||
|
import com.google.android.setupdesign.view.BottomScrollView;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View for udfps enrolling.
|
||||||
|
*/
|
||||||
|
public class UdfpsEnrollEnrollingView extends GlifLayout {
|
||||||
|
private final UdfpsUtils mUdfpsUtils;
|
||||||
|
private final Context mContext;
|
||||||
|
// We don't need to listen to onConfigurationChanged() for mRotation here because
|
||||||
|
// FingerprintEnrollEnrolling is always recreated once the configuration is changed.
|
||||||
|
private final int mRotation;
|
||||||
|
private final boolean mIsLandscape;
|
||||||
|
private final boolean mShouldUseReverseLandscape;
|
||||||
|
private UdfpsEnrollView mUdfpsEnrollView;
|
||||||
|
private View mHeaderView;
|
||||||
|
private AccessibilityManager mAccessibilityManager;
|
||||||
|
|
||||||
|
|
||||||
|
public UdfpsEnrollEnrollingView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
mContext = context;
|
||||||
|
mRotation = mContext.getDisplay().getRotation();
|
||||||
|
mIsLandscape = mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270;
|
||||||
|
final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
|
||||||
|
== View.LAYOUT_DIRECTION_RTL);
|
||||||
|
mShouldUseReverseLandscape = (mRotation == Surface.ROTATION_90 && isLayoutRtl)
|
||||||
|
|| (mRotation == Surface.ROTATION_270 && !isLayoutRtl);
|
||||||
|
|
||||||
|
mUdfpsUtils = new UdfpsUtils();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinishInflate() {
|
||||||
|
super.onFinishInflate();
|
||||||
|
mHeaderView = findViewById(R.id.sud_landscape_header_area);
|
||||||
|
mUdfpsEnrollView = findViewById(R.id.udfps_animation_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initView(FingerprintSensorPropertiesInternal udfpsProps,
|
||||||
|
UdfpsEnrollHelper udfpsEnrollHelper,
|
||||||
|
AccessibilityManager accessibilityManager) {
|
||||||
|
mAccessibilityManager = accessibilityManager;
|
||||||
|
initUdfpsEnrollView(mUdfpsEnrollView, udfpsProps, udfpsEnrollHelper);
|
||||||
|
|
||||||
|
if (!mIsLandscape) {
|
||||||
|
adjustPortraitPaddings();
|
||||||
|
} else if (mShouldUseReverseLandscape) {
|
||||||
|
swapHeaderAndContent();
|
||||||
|
}
|
||||||
|
setOnHoverListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSecondaryButtonBackground(@ColorInt int color) {
|
||||||
|
// Set the button background only when the button is not under udfps overlay to avoid UI
|
||||||
|
// overlap.
|
||||||
|
if (!mIsLandscape || mShouldUseReverseLandscape) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Button secondaryButtonView =
|
||||||
|
getMixin(FooterBarMixin.class).getSecondaryButtonView();
|
||||||
|
secondaryButtonView.setBackgroundColor(color);
|
||||||
|
if (mRotation == Surface.ROTATION_90) {
|
||||||
|
secondaryButtonView.setGravity(Gravity.START);
|
||||||
|
} else {
|
||||||
|
secondaryButtonView.setGravity(Gravity.END);
|
||||||
|
}
|
||||||
|
mHeaderView.post(() -> {
|
||||||
|
secondaryButtonView.setLayoutParams(
|
||||||
|
new LinearLayout.LayoutParams(mHeaderView.getMeasuredWidth(),
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView,
|
||||||
|
FingerprintSensorPropertiesInternal udfpsProps,
|
||||||
|
UdfpsEnrollHelper udfpsEnrollHelper) {
|
||||||
|
DisplayInfo displayInfo = new DisplayInfo();
|
||||||
|
mContext.getDisplay().getDisplayInfo(displayInfo);
|
||||||
|
|
||||||
|
final float scaleFactor = mUdfpsUtils.getScaleFactor(displayInfo);
|
||||||
|
Rect udfpsBounds = udfpsProps.getLocation().getRect();
|
||||||
|
udfpsBounds.scale(scaleFactor);
|
||||||
|
|
||||||
|
final Rect overlayBounds = new Rect(
|
||||||
|
0, /* left */
|
||||||
|
displayInfo.getNaturalHeight() / 2, /* top */
|
||||||
|
displayInfo.getNaturalWidth(), /* right */
|
||||||
|
displayInfo.getNaturalHeight() /* botom */);
|
||||||
|
|
||||||
|
UdfpsOverlayParams params = new UdfpsOverlayParams(
|
||||||
|
udfpsBounds,
|
||||||
|
overlayBounds,
|
||||||
|
displayInfo.getNaturalWidth(),
|
||||||
|
displayInfo.getNaturalHeight(),
|
||||||
|
scaleFactor,
|
||||||
|
displayInfo.rotation);
|
||||||
|
|
||||||
|
udfpsEnrollView.setOverlayParams(params);
|
||||||
|
udfpsEnrollView.setEnrollHelper(udfpsEnrollHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustPortraitPaddings() {
|
||||||
|
// In the portrait mode, layout_container's height is 0, so it's
|
||||||
|
// always shown at the bottom of the screen.
|
||||||
|
final FrameLayout portraitLayoutContainer = findViewById(R.id.layout_container);
|
||||||
|
|
||||||
|
// In the portrait mode, the title and lottie animation view may
|
||||||
|
// overlap when title needs three lines, so adding some paddings
|
||||||
|
// between them, and adjusting the fp progress view here accordingly.
|
||||||
|
final int layoutLottieAnimationPadding = (int) getResources()
|
||||||
|
.getDimension(R.dimen.udfps_lottie_padding_top);
|
||||||
|
portraitLayoutContainer.setPadding(0,
|
||||||
|
layoutLottieAnimationPadding, 0, 0);
|
||||||
|
final ImageView progressView = mUdfpsEnrollView.findViewById(
|
||||||
|
R.id.udfps_enroll_animation_fp_progress_view);
|
||||||
|
progressView.setPadding(0, -(layoutLottieAnimationPadding),
|
||||||
|
0, layoutLottieAnimationPadding);
|
||||||
|
final ImageView fingerprintView = mUdfpsEnrollView.findViewById(
|
||||||
|
R.id.udfps_enroll_animation_fp_view);
|
||||||
|
fingerprintView.setPadding(0, -layoutLottieAnimationPadding,
|
||||||
|
0, layoutLottieAnimationPadding);
|
||||||
|
|
||||||
|
// TODO(b/260970216) Instead of hiding the description text view, we should
|
||||||
|
// make the header view scrollable if the text is too long.
|
||||||
|
// If description text view has overlap with udfps progress view, hide it.
|
||||||
|
final View descView = getDescriptionTextView();
|
||||||
|
getViewTreeObserver().addOnDrawListener(() -> {
|
||||||
|
if (descView.getVisibility() == View.VISIBLE
|
||||||
|
&& hasOverlap(descView, mUdfpsEnrollView)) {
|
||||||
|
descView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setOnHoverListener() {
|
||||||
|
if (!mAccessibilityManager.isEnabled()) return;
|
||||||
|
|
||||||
|
final View.OnHoverListener onHoverListener = (v, event) -> {
|
||||||
|
// Map the touch to portrait mode if the device is in
|
||||||
|
// landscape mode.
|
||||||
|
final Point scaledTouch =
|
||||||
|
mUdfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0),
|
||||||
|
event, mUdfpsEnrollView.getOverlayParams());
|
||||||
|
|
||||||
|
if (mUdfpsUtils.isWithinSensorArea(event.getPointerId(0), event,
|
||||||
|
mUdfpsEnrollView.getOverlayParams())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String theStr = mUdfpsUtils.onTouchOutsideOfSensorArea(
|
||||||
|
mAccessibilityManager.isTouchExplorationEnabled(), mContext,
|
||||||
|
scaledTouch.x, scaledTouch.y, mUdfpsEnrollView.getOverlayParams());
|
||||||
|
if (theStr != null) {
|
||||||
|
v.announceForAccessibility(theStr);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
findManagedViewById(mIsLandscape ? R.id.sud_landscape_content_area
|
||||||
|
: R.id.sud_layout_content).setOnHoverListener(onHoverListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void swapHeaderAndContent() {
|
||||||
|
// Reverse header and body
|
||||||
|
ViewGroup parentView = (ViewGroup) mHeaderView.getParent();
|
||||||
|
parentView.removeView(mHeaderView);
|
||||||
|
parentView.addView(mHeaderView);
|
||||||
|
|
||||||
|
// Hide scroll indicators
|
||||||
|
BottomScrollView headerScrollView = mHeaderView.findViewById(R.id.sud_header_scroll_view);
|
||||||
|
headerScrollView.setScrollIndicators(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean hasOverlap(View view1, View view2) {
|
||||||
|
int[] firstPosition = new int[2];
|
||||||
|
int[] secondPosition = new int[2];
|
||||||
|
|
||||||
|
view1.getLocationOnScreen(firstPosition);
|
||||||
|
view2.getLocationOnScreen(secondPosition);
|
||||||
|
|
||||||
|
// Rect constructor parameters: left, top, right, bottom
|
||||||
|
Rect rectView1 = new Rect(firstPosition[0], firstPosition[1],
|
||||||
|
firstPosition[0] + view1.getMeasuredWidth(),
|
||||||
|
firstPosition[1] + view1.getMeasuredHeight());
|
||||||
|
Rect rectView2 = new Rect(secondPosition[0], secondPosition[1],
|
||||||
|
secondPosition[0] + view2.getMeasuredWidth(),
|
||||||
|
secondPosition[1] + view2.getMeasuredHeight());
|
||||||
|
return rectView1.intersect(rectView2);
|
||||||
|
}
|
||||||
|
}
|
@@ -56,6 +56,7 @@ import android.os.Vibrator;
|
|||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.accessibility.AccessibilityManager;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -314,11 +315,17 @@ public class FingerprintEnrollEnrollingTest {
|
|||||||
@Test
|
@Test
|
||||||
public void fingerprintUdfpsOverlayEnrollment_descriptionViewGoneWithOverlap() {
|
public void fingerprintUdfpsOverlayEnrollment_descriptionViewGoneWithOverlap() {
|
||||||
initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
|
initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
|
||||||
doReturn(true).when(mActivity).hasOverlap(any(), any());
|
|
||||||
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
|
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
|
||||||
createActivity();
|
createActivity();
|
||||||
|
|
||||||
final GlifLayout defaultLayout = spy(mActivity.findViewById(R.id.setup_wizard_layout));
|
final UdfpsEnrollEnrollingView defaultLayout = spy(
|
||||||
|
mActivity.findViewById(R.id.setup_wizard_layout));
|
||||||
|
doReturn(true).when(defaultLayout).hasOverlap(any(), any());
|
||||||
|
|
||||||
|
// Somehow spy doesn't work, and we need to call initView manually.
|
||||||
|
defaultLayout.initView(mFingerprintManager.getSensorPropertiesInternal().get(0),
|
||||||
|
mActivity.mUdfpsEnrollHelper,
|
||||||
|
mActivity.getSystemService(AccessibilityManager.class));
|
||||||
final TextView descriptionTextView = defaultLayout.getDescriptionTextView();
|
final TextView descriptionTextView = defaultLayout.getDescriptionTextView();
|
||||||
|
|
||||||
defaultLayout.getViewTreeObserver().dispatchOnDraw();
|
defaultLayout.getViewTreeObserver().dispatchOnDraw();
|
||||||
@@ -328,11 +335,17 @@ public class FingerprintEnrollEnrollingTest {
|
|||||||
@Test
|
@Test
|
||||||
public void fingerprintUdfpsOverlayEnrollment_descriptionViewVisibleWithoutOverlap() {
|
public void fingerprintUdfpsOverlayEnrollment_descriptionViewVisibleWithoutOverlap() {
|
||||||
initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
|
initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
|
||||||
doReturn(false).when(mActivity).hasOverlap(any(), any());
|
|
||||||
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
|
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
|
||||||
createActivity();
|
createActivity();
|
||||||
|
|
||||||
final GlifLayout defaultLayout = spy(mActivity.findViewById(R.id.setup_wizard_layout));
|
final UdfpsEnrollEnrollingView defaultLayout = spy(
|
||||||
|
mActivity.findViewById(R.id.setup_wizard_layout));
|
||||||
|
doReturn(false).when(defaultLayout).hasOverlap(any(), any());
|
||||||
|
|
||||||
|
// Somehow spy doesn't work, and we need to call initView manually.
|
||||||
|
defaultLayout.initView(mFingerprintManager.getSensorPropertiesInternal().get(0),
|
||||||
|
mActivity.mUdfpsEnrollHelper,
|
||||||
|
mActivity.getSystemService(AccessibilityManager.class));
|
||||||
final TextView descriptionTextView = defaultLayout.getDescriptionTextView();
|
final TextView descriptionTextView = defaultLayout.getDescriptionTextView();
|
||||||
|
|
||||||
defaultLayout.getViewTreeObserver().dispatchOnDraw();
|
defaultLayout.getViewTreeObserver().dispatchOnDraw();
|
||||||
@@ -578,7 +591,6 @@ public class FingerprintEnrollEnrollingTest {
|
|||||||
mContext = spy(RuntimeEnvironment.application);
|
mContext = spy(RuntimeEnvironment.application);
|
||||||
mActivity = spy(FingerprintEnrollEnrolling.class);
|
mActivity = spy(FingerprintEnrollEnrolling.class);
|
||||||
|
|
||||||
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
|
|
||||||
when(mContext.getDisplay()).thenReturn(mMockDisplay);
|
when(mContext.getDisplay()).thenReturn(mMockDisplay);
|
||||||
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
|
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user