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:
Hao Dong
2023-05-27 00:32:26 +00:00
committed by Automerger Merge Worker
6 changed files with 279 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. ~ 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>

View File

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

View File

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

View File

@@ -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);
}
}

View File

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