Fix Fingerprint enrollment UI when display size is largest.

1. Remove land/udfps_enroll_enrolling and use the default land layout
instead. Swap header and content when necessary to avoid overlap.
2. Add UdfpsEnrollEnrollingView.java

Test: manual test - 1. Set system display and font size largest
	            2. Launch fingerprint enrollment and check UI.
Test: atest FingerprintEnrollEnrollingTest
Bug: 269060514
Bug: 283169056
Change-Id: Ifbe6c92c4213979952f2f89a1cd595c9c4bff6ec
Merged-In: Ifbe6c92c4213979952f2f89a1cd595c9c4bff6ec
This commit is contained in:
Hao Dong
2023-05-15 19:34:07 +00:00
parent be823bc598
commit 2ca3f3232c
5 changed files with 42 additions and 299 deletions

View File

@@ -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>

View File

@@ -15,7 +15,7 @@
~ 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:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@@ -78,4 +78,4 @@
</LinearLayout>
</LinearLayout>
</com.google.android.setupdesign.GlifLayout>
</com.android.settings.biometrics.fingerprint.UdfpsEnrollEnrollingView>

View File

@@ -133,6 +133,7 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
protected long mChallenge;
protected boolean mFromSettingsSummary;
protected FooterBarMixin mFooterBarMixin;
protected boolean mShouldSetFooterBarBackground = true;
@Nullable
protected ScreenSizeFoldProvider mScreenSizeFoldProvider;
@Nullable
@@ -191,12 +192,14 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
super.onPostCreate(savedInstanceState);
initViews();
@SuppressLint("VisibleForTests")
final LinearLayout buttonContainer = mFooterBarMixin != null
? mFooterBarMixin.getButtonContainer()
: null;
if (buttonContainer != null) {
buttonContainer.setBackgroundColor(getBackgroundColor());
if (mShouldSetFooterBarBackground) {
@SuppressLint("VisibleForTests")
final LinearLayout buttonContainer = mFooterBarMixin != null
? mFooterBarMixin.getButtonContainer()
: null;
if (buttonContainer != null) {
buttonContainer.setBackgroundColor(getBackgroundColor());
}
}
}
@@ -331,7 +334,7 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
}
@ColorInt
private int getBackgroundColor() {
public int getBackgroundColor() {
final ColorStateList stateList = Utils.getColorAttr(this, android.R.attr.windowBackground);
return stateList != null ? stateList.getDefaultColor() : Color.TRANSPARENT;
}

View File

@@ -32,10 +32,8 @@ import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
@@ -48,22 +46,16 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.DisplayInfo;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -79,25 +71,20 @@ import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricsEnrollEnrolling;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
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.LottieCompositionFactory;
import com.airbnb.lottie.LottieProperty;
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.FooterButton;
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.HeaderMixin;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Locale;
/**
* Activity which handles the actual enrolling for fingerprint.
@@ -176,8 +163,6 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
@VisibleForTesting
@Nullable
UdfpsEnrollHelper mUdfpsEnrollHelper;
// TODO(b/260617060): Do not hard-code mScaleFactor, referring to AuthController.
private float mScaleFactor = 1.0f;
private ObjectAnimator mProgressAnim;
private TextView mErrorText;
private Interpolator mFastOutSlowInInterpolator;
@@ -206,7 +191,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
private boolean mHaveShownSfpsLeftEdgeLottie;
private boolean mHaveShownSfpsRightEdgeLottie;
private boolean mShouldShowLottie;
private UdfpsUtils mUdfpsUtils;
private ObjectAnimator mHelpAnimation;
private OrientationEventListener mOrientationEventListener;
@@ -251,88 +236,17 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
mAccessibilityManager = getSystemService(AccessibilityManager.class);
mIsAccessibilityEnabled = mAccessibilityManager.isEnabled();
mUdfpsUtils = new UdfpsUtils();
final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(
Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL);
listenOrientationEvent();
if (mCanAssumeUdfps) {
int rotation = getApplicationContext().getDisplay().getRotation();
final GlifLayout layout = (GlifLayout) getLayoutInflater().inflate(
R.layout.udfps_enroll_enrolling, null, false);
final UdfpsEnrollView udfpsEnrollView = layout.findViewById(R.id.udfps_animation_view);
updateUdfpsEnrollView(udfpsEnrollView, props.get(0));
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 UdfpsEnrollEnrollingView layout =
(UdfpsEnrollEnrollingView) getLayoutInflater().inflate(
R.layout.udfps_enroll_enrolling, null, false);
setUdfpsEnrollHelper();
layout.initView(props.get(0), mUdfpsEnrollHelper, mAccessibilityManager);
final LinearLayout layoutContainer = layout.findViewById(
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;
}
setContentView(layout);
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
} else if (mCanAssumeSfps) {
setContentView(R.layout.sfps_enroll_enrolling);
@@ -372,22 +286,11 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
.build()
);
if (FeatureFlagUtils.isEnabled(getApplicationContext(),
FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) {
// Remove the space view and make the width of footer button container WRAP_CONTENT
// to avoid hiding the udfps view progress bar bottom.
final LinearLayout buttonContainer = mFooterBarMixin.getButtonContainer();
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;
}
// If it's udfps, set the background color only for secondary button if necessary.
if (mCanAssumeUdfps) {
mShouldSetFooterBarBackground = false;
((UdfpsEnrollEnrollingView) getLayout()).setSecondaryButtonBackground(
getBackgroundColor());
}
final LayerDrawable fingerprintDrawable = mProgressBar != null
@@ -1237,30 +1140,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
}
}
private UdfpsEnrollView updateUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView,
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);
private void setUdfpsEnrollHelper() {
mUdfpsEnrollHelper = (UdfpsEnrollHelper) getSupportFragmentManager().findFragmentByTag(
FingerprintEnrollEnrolling.TAG_UDFPS_HELPER);
if (mUdfpsEnrollHelper == null) {
@@ -1270,57 +1150,6 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
.add(mUdfpsEnrollHelper, FingerprintEnrollEnrolling.TAG_UDFPS_HELPER)
.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 {

View File

@@ -56,6 +56,7 @@ import android.os.Vibrator;
import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -314,11 +315,17 @@ public class FingerprintEnrollEnrollingTest {
@Test
public void fingerprintUdfpsOverlayEnrollment_descriptionViewGoneWithOverlap() {
initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
doReturn(true).when(mActivity).hasOverlap(any(), any());
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
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();
defaultLayout.getViewTreeObserver().dispatchOnDraw();
@@ -328,11 +335,17 @@ public class FingerprintEnrollEnrollingTest {
@Test
public void fingerprintUdfpsOverlayEnrollment_descriptionViewVisibleWithoutOverlap() {
initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
doReturn(false).when(mActivity).hasOverlap(any(), any());
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
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();
defaultLayout.getViewTreeObserver().dispatchOnDraw();
@@ -591,7 +604,6 @@ public class FingerprintEnrollEnrollingTest {
mContext = spy(RuntimeEnvironment.application);
mActivity = spy(FingerprintEnrollEnrolling.class);
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
when(mContext.getDisplay()).thenReturn(mMockDisplay);
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);