[Bouncer] Add ui changes to auto confirmation.
Add pin shape avd animations to non 6 digit and 6 digit auto confirmation screens. Add transparent backspace key that hides and shows when text length > 0 and is auto confirmation. Fixes: 264190150 Test: Changing pin digit size from 6 to > 6 to < 6. Test: Inputting wrong pin code and resetting. Test: Feature flag on/off Change-Id: I47ce330e15e01f3a8a435eadaa1bb2d4926cbbe6
This commit is contained in:
@@ -1,25 +0,0 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="200dp"
|
||||
android:height="200dp"
|
||||
android:viewportWidth="200"
|
||||
android:viewportHeight="200">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M100,100m-100,0a100,100 0,1 1,200 0a100,100 0,1 1,-200 0"/>
|
||||
</vector>
|
||||
@@ -32,4 +32,13 @@
|
||||
<item>TUV</item><!-- 8 -->
|
||||
<item>WXYZ</item><!-- 9 -->
|
||||
</string-array>
|
||||
|
||||
<integer-array name="bouncer_pin_shapes">
|
||||
<item>@drawable/pin_dot_shape_1_avd</item>
|
||||
<item>@drawable/pin_dot_shape_2_avd</item>
|
||||
<item>@drawable/pin_dot_shape_3_avd</item>
|
||||
<item>@drawable/pin_dot_shape_4_avd</item>
|
||||
<item>@drawable/pin_dot_shape_5_avd</item>
|
||||
<item>@drawable/pin_dot_shape_6_avd</item>
|
||||
</integer-array>
|
||||
</resources>
|
||||
|
||||
1
packages/SystemUI/res/drawable/pin_dot_delete_avd.xml
Normal file
1
packages/SystemUI/res/drawable/pin_dot_delete_avd.xml
Normal file
File diff suppressed because one or more lines are too long
1
packages/SystemUI/res/drawable/pin_dot_shape_1_avd.xml
Normal file
1
packages/SystemUI/res/drawable/pin_dot_shape_1_avd.xml
Normal file
File diff suppressed because one or more lines are too long
1
packages/SystemUI/res/drawable/pin_dot_shape_2_avd.xml
Normal file
1
packages/SystemUI/res/drawable/pin_dot_shape_2_avd.xml
Normal file
File diff suppressed because one or more lines are too long
1
packages/SystemUI/res/drawable/pin_dot_shape_3_avd.xml
Normal file
1
packages/SystemUI/res/drawable/pin_dot_shape_3_avd.xml
Normal file
File diff suppressed because one or more lines are too long
1
packages/SystemUI/res/drawable/pin_dot_shape_4_avd.xml
Normal file
1
packages/SystemUI/res/drawable/pin_dot_shape_4_avd.xml
Normal file
File diff suppressed because one or more lines are too long
1
packages/SystemUI/res/drawable/pin_dot_shape_5_avd.xml
Normal file
1
packages/SystemUI/res/drawable/pin_dot_shape_5_avd.xml
Normal file
File diff suppressed because one or more lines are too long
1
packages/SystemUI/res/drawable/pin_dot_shape_6_avd.xml
Normal file
1
packages/SystemUI/res/drawable/pin_dot_shape_6_avd.xml
Normal file
File diff suppressed because one or more lines are too long
@@ -14,10 +14,10 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License
|
||||
-->
|
||||
<com.android.keyguard.PINShapeSixDigitView
|
||||
<com.android.keyguard.PinShapeHintingView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
</com.android.keyguard.PINShapeSixDigitView>
|
||||
</com.android.keyguard.PinShapeHintingView>
|
||||
@@ -14,10 +14,10 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License
|
||||
-->
|
||||
<com.android.keyguard.PINShapeNonSixDigitView
|
||||
<com.android.keyguard.PinShapeNonHintingView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
</com.android.keyguard.PINShapeNonSixDigitView>
|
||||
</com.android.keyguard.PinShapeNonHintingView>
|
||||
@@ -37,6 +37,8 @@ public class KeyguardPinViewController
|
||||
mView.onDevicePostureChanged(posture);
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
private static final int DEFAULT_PIN_LENGTH = 6;
|
||||
private NumPadButton mBackspaceKey;
|
||||
private View mOkButton = mView.findViewById(R.id.key_enter);
|
||||
|
||||
private int mUserId;
|
||||
@@ -59,6 +61,7 @@ public class KeyguardPinViewController
|
||||
mPostureController = postureController;
|
||||
mLockPatternUtils = lockPatternUtils;
|
||||
mFeatureFlags = featureFlags;
|
||||
mBackspaceKey = view.findViewById(R.id.delete_button);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,6 +82,7 @@ public class KeyguardPinViewController
|
||||
protected void onUserInput() {
|
||||
super.onUserInput();
|
||||
if (isAutoConfirmation()) {
|
||||
updateBackSpaceVisibility();
|
||||
if (mPasswordEntry.getText().length() == mPinLength) {
|
||||
verifyPasswordAndUnlock();
|
||||
}
|
||||
@@ -96,12 +100,11 @@ public class KeyguardPinViewController
|
||||
if (mFeatureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)) {
|
||||
mUserId = KeyguardUpdateMonitor.getCurrentUser();
|
||||
mPinLength = mLockPatternUtils.getPinLength(mUserId);
|
||||
if (isAutoConfirmation()) {
|
||||
mOkButton.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
mOkButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
mPasswordEntry.setIsPinHinting(true, isPinHinting());
|
||||
mBackspaceKey.setTransparentMode(/* isTransparentMode= */ isAutoConfirmation());
|
||||
mOkButton.setVisibility(isAutoConfirmation() ? View.INVISIBLE : View.VISIBLE);
|
||||
updateBackSpaceVisibility();
|
||||
mPasswordEntry.setUsePinShapes(true);
|
||||
mPasswordEntry.setIsPinHinting(isAutoConfirmation() && isPinHinting());
|
||||
}
|
||||
super.startAppearAnimation();
|
||||
}
|
||||
@@ -112,15 +115,33 @@ public class KeyguardPinViewController
|
||||
mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
|
||||
}
|
||||
|
||||
/*
|
||||
Responsible for identifying if PIN hinting is to be enabled or not
|
||||
//
|
||||
|
||||
/**
|
||||
* Updates the visibility and the enabled state of the backspace.
|
||||
* Visibility changes are only for auto confirmation configuration.
|
||||
*/
|
||||
private void updateBackSpaceVisibility() {
|
||||
if (!isAutoConfirmation()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPasswordEntry.getText().length() > 0) {
|
||||
mBackspaceKey.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mBackspaceKey.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for identifying if PIN hinting is to be enabled or not
|
||||
*/
|
||||
private boolean isPinHinting() {
|
||||
return mLockPatternUtils.getPinLength(mUserId) == DEFAULT_PIN_LENGTH;
|
||||
}
|
||||
|
||||
/*
|
||||
Responsible for identifying if auto confirm is enabled or not in Settings
|
||||
/**
|
||||
* Responsible for identifying if auto confirm is enabled or not in Settings
|
||||
*/
|
||||
private boolean isAutoConfirmation() {
|
||||
//Checks if user has enabled the auto confirm in Settings
|
||||
|
||||
@@ -95,7 +95,6 @@ class NumPadAnimator {
|
||||
mHeight = height;
|
||||
mStartRadius = height / 2f;
|
||||
mEndRadius = height / 4f;
|
||||
mBackground.setCornerRadius(mStartRadius);
|
||||
mExpandAnimator.setFloatValues(mStartRadius, mEndRadius);
|
||||
mContractAnimator.setFloatValues(mEndRadius, mStartRadius);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* Similar to the {@link NumPadKey}, but displays an image.
|
||||
*/
|
||||
@@ -35,18 +37,13 @@ public class NumPadButton extends AlphaOptimizedImageButton implements NumPadAni
|
||||
@Nullable
|
||||
private NumPadAnimator mAnimator;
|
||||
private int mOrientation;
|
||||
private int mStyleAttr;
|
||||
private boolean mIsTransparentMode;
|
||||
|
||||
public NumPadButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
Drawable background = getBackground();
|
||||
if (background instanceof GradientDrawable) {
|
||||
mAnimator = new NumPadAnimator(context, background.mutate(),
|
||||
attrs.getStyleAttribute(), getDrawable());
|
||||
} else {
|
||||
mAnimator = null;
|
||||
}
|
||||
|
||||
mStyleAttr = attrs.getStyleAttribute();
|
||||
setupAnimator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,7 +95,9 @@ public class NumPadButton extends AlphaOptimizedImageButton implements NumPadAni
|
||||
public void reloadColors() {
|
||||
if (mAnimator != null) mAnimator.reloadColors(getContext());
|
||||
|
||||
int[] customAttrs = {android.R.attr.textColorPrimaryInverse};
|
||||
int textColorResId = mIsTransparentMode ? android.R.attr.textColorPrimary
|
||||
: android.R.attr.textColorPrimaryInverse;
|
||||
int[] customAttrs = {textColorResId};
|
||||
TypedArray a = getContext().obtainStyledAttributes(customAttrs);
|
||||
int imageColor = a.getColor(0, 0);
|
||||
a.recycle();
|
||||
@@ -111,4 +110,34 @@ public class NumPadButton extends AlphaOptimizedImageButton implements NumPadAni
|
||||
mAnimator.setProgress(progress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether button is transparent mode.
|
||||
*
|
||||
* @param isTransparentMode
|
||||
*/
|
||||
public void setTransparentMode(boolean isTransparentMode) {
|
||||
mIsTransparentMode = isTransparentMode;
|
||||
if (isTransparentMode) {
|
||||
setBackgroundColor(android.R.color.transparent);
|
||||
} else {
|
||||
setBackground(getContext().getDrawable(R.drawable.num_pad_key_background));
|
||||
}
|
||||
setupAnimator();
|
||||
reloadColors();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the animator for the NumPadButton.
|
||||
*/
|
||||
private void setupAnimator() {
|
||||
Drawable background = getBackground();
|
||||
if (background instanceof GradientDrawable) {
|
||||
mAnimator = new NumPadAnimator(getContext(), background.mutate(),
|
||||
mStyleAttr, getDrawable());
|
||||
} else {
|
||||
mAnimator = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +57,8 @@ public class PasswordTextView extends FrameLayout {
|
||||
|
||||
private static final float DOT_OVERSHOOT_FACTOR = 1.5f;
|
||||
private static final long DOT_APPEAR_DURATION_OVERSHOOT = 320;
|
||||
private static final long APPEAR_DURATION = 160;
|
||||
private static final long DISAPPEAR_DURATION = 160;
|
||||
public static final long APPEAR_DURATION = 160;
|
||||
public static final long DISAPPEAR_DURATION = 160;
|
||||
private static final long RESET_DELAY_PER_ELEMENT = 40;
|
||||
private static final long RESET_MAX_DELAY = 200;
|
||||
|
||||
@@ -284,8 +284,8 @@ public class PasswordTextView extends FrameLayout {
|
||||
sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length(), 0, 1);
|
||||
}
|
||||
|
||||
public void setUserActivityListener(UserActivityListener userActivitiListener) {
|
||||
mUserActivityListener = userActivitiListener;
|
||||
public void setUserActivityListener(UserActivityListener userActivityListener) {
|
||||
mUserActivityListener = userActivityListener;
|
||||
}
|
||||
|
||||
private void userActivity() {
|
||||
@@ -361,6 +361,8 @@ public class PasswordTextView extends FrameLayout {
|
||||
}
|
||||
if (!animated) {
|
||||
mTextChars.clear();
|
||||
} else {
|
||||
userActivity();
|
||||
}
|
||||
if (mPinShapeInput != null) {
|
||||
mPinShapeInput.reset();
|
||||
@@ -410,15 +412,20 @@ public class PasswordTextView extends FrameLayout {
|
||||
info.setInputType(InputType.TYPE_NUMBER_VARIATION_PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to use pin shapes.
|
||||
*/
|
||||
public void setUsePinShapes(boolean usePinShapes) {
|
||||
mUsePinShapes = usePinShapes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether AutoConfirmation feature is on.
|
||||
*
|
||||
* @param usePinShapes
|
||||
* @param isPinHinting
|
||||
*/
|
||||
public void setIsPinHinting(boolean usePinShapes, boolean isPinHinting) {
|
||||
mUsePinShapes = usePinShapes;
|
||||
|
||||
public void setIsPinHinting(boolean isPinHinting) {
|
||||
if (mPinShapeInput != null) {
|
||||
removeView(mPinShapeInput.getView());
|
||||
mPinShapeInput = null;
|
||||
@@ -426,10 +433,10 @@ public class PasswordTextView extends FrameLayout {
|
||||
|
||||
if (isPinHinting) {
|
||||
mPinShapeInput = (PinShapeInput) LayoutInflater.from(mContext).inflate(
|
||||
R.layout.keyguard_pin_shape_six_digit_view, null);
|
||||
R.layout.keyguard_pin_shape_hinting_view, null);
|
||||
} else {
|
||||
mPinShapeInput = (PinShapeInput) LayoutInflater.from(mContext).inflate(
|
||||
R.layout.keyguard_pin_shape_non_six_digit_view, null);
|
||||
R.layout.keyguard_pin_shape_non_hinting_view, null);
|
||||
}
|
||||
addView(mPinShapeInput.getView());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.keyguard
|
||||
|
||||
import android.content.Context
|
||||
import com.android.systemui.R
|
||||
import kotlin.random.Random
|
||||
|
||||
class PinShapeAdapter {
|
||||
var shapes: MutableList<Int> = ArrayList()
|
||||
val random = Random(System.currentTimeMillis())
|
||||
|
||||
constructor(context: Context) {
|
||||
val availableShapes = context.resources.obtainTypedArray(R.array.bouncer_pin_shapes)
|
||||
|
||||
for (i in 0 until availableShapes.length()) {
|
||||
val shape = availableShapes.getResourceId(i, 0)
|
||||
shapes.add(shape)
|
||||
}
|
||||
|
||||
shapes.shuffle()
|
||||
availableShapes.recycle()
|
||||
}
|
||||
|
||||
fun getShape(pos: Int): Int {
|
||||
return shapes[pos.mod(shapes.size)]
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.keyguard;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
@@ -41,13 +42,11 @@ public class PinShapeHintingView extends LinearLayout implements PinShapeInput {
|
||||
.getDefaultColor();
|
||||
private int mPosition = 0;
|
||||
private static final int DEFAULT_PIN_LENGTH = 6;
|
||||
|
||||
public PinShapeHintingView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
private PinShapeAdapter mPinShapeAdapter;
|
||||
|
||||
public PinShapeHintingView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mPinShapeAdapter = new PinShapeAdapter(context);
|
||||
mPinLength = DEFAULT_PIN_LENGTH;
|
||||
mDotDiameter = context.getResources().getDimensionPixelSize(R.dimen.default_dot_diameter);
|
||||
mDotSpacing = context.getResources().getDimensionPixelSize(R.dimen.default_dot_spacing);
|
||||
@@ -65,20 +64,12 @@ public class PinShapeHintingView extends LinearLayout implements PinShapeInput {
|
||||
}
|
||||
}
|
||||
|
||||
public PinShapeHintingView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public PinShapeHintingView(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append() {
|
||||
if (mPosition == DEFAULT_PIN_LENGTH) {
|
||||
return;
|
||||
}
|
||||
setAnimatedDrawable(mPosition, mPinShapeAdapter.getShape(mPosition));
|
||||
mPosition++;
|
||||
}
|
||||
|
||||
@@ -88,6 +79,7 @@ public class PinShapeHintingView extends LinearLayout implements PinShapeInput {
|
||||
return;
|
||||
}
|
||||
mPosition--;
|
||||
setAnimatedDrawable(mPosition, R.drawable.pin_dot_delete_avd);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,4 +100,16 @@ public class PinShapeHintingView extends LinearLayout implements PinShapeInput {
|
||||
public View getView() {
|
||||
return this;
|
||||
}
|
||||
|
||||
private void setAnimatedDrawable(int position, int drawableResId) {
|
||||
ImageView pinDot = (ImageView) getChildAt(position);
|
||||
pinDot.setImageResource(drawableResId);
|
||||
if (pinDot.getDrawable() != null) {
|
||||
Drawable drawable = DrawableCompat.wrap(pinDot.getDrawable());
|
||||
DrawableCompat.setTint(drawable, mColor);
|
||||
}
|
||||
if (pinDot.getDrawable() instanceof AnimatedVectorDrawable) {
|
||||
((AnimatedVectorDrawable) pinDot.getDrawable()).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,29 @@
|
||||
|
||||
package com.android.keyguard;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.transition.Transition;
|
||||
import android.transition.TransitionManager;
|
||||
import android.transition.TransitionValues;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.animation.Interpolators;
|
||||
|
||||
/**
|
||||
* This class contains implementation for methods that will be used when user has set a
|
||||
@@ -32,36 +49,58 @@ public class PinShapeNonHintingView extends LinearLayout implements PinShapeInpu
|
||||
private int mColor = Utils.getColorAttr(getContext(),
|
||||
android.R.attr.textColorPrimary).getDefaultColor();
|
||||
private int mPosition = 0;
|
||||
|
||||
public PinShapeNonHintingView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
private final PinShapeAdapter mPinShapeAdapter;
|
||||
private Animation mCurrentPlayingAnimation;
|
||||
public PinShapeNonHintingView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public PinShapeNonHintingView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public PinShapeNonHintingView(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mPinShapeAdapter = new PinShapeAdapter(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append() {
|
||||
int size = getResources().getDimensionPixelSize(R.dimen.password_shape_size);
|
||||
ImageView pinDot = new ImageView(getContext());
|
||||
pinDot.setLayoutParams(new LayoutParams(size, size));
|
||||
pinDot.setImageResource(mPinShapeAdapter.getShape(mPosition));
|
||||
if (pinDot.getDrawable() != null) {
|
||||
Drawable wrappedDrawable = DrawableCompat.wrap(pinDot.getDrawable());
|
||||
DrawableCompat.setTint(wrappedDrawable, mColor);
|
||||
}
|
||||
if (pinDot.getDrawable() instanceof AnimatedVectorDrawable) {
|
||||
((AnimatedVectorDrawable) pinDot.getDrawable()).start();
|
||||
}
|
||||
TransitionManager.beginDelayedTransition(this, new PinShapeViewTransition());
|
||||
addView(pinDot);
|
||||
mPosition++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
if (mPosition == 0) {
|
||||
Log.e(getClass().getName(), "Trying to delete a non-existent char");
|
||||
return;
|
||||
} else {
|
||||
mPosition--;
|
||||
}
|
||||
mPosition--;
|
||||
ImageView pinDot = (ImageView) getChildAt(mPosition);
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(1f, 0f);
|
||||
animator.addUpdateListener(valueAnimator -> {
|
||||
float value = (float) valueAnimator.getAnimatedValue();
|
||||
pinDot.setScaleX(value);
|
||||
pinDot.setScaleY(value);
|
||||
});
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
TransitionManager.beginDelayedTransition(
|
||||
PinShapeNonHintingView.this,
|
||||
new PinShapeViewTransition());
|
||||
removeView(pinDot);
|
||||
mCurrentPlayingAnimation = null;
|
||||
}
|
||||
});
|
||||
animator.setDuration(PasswordTextView.DISAPPEAR_DURATION);
|
||||
animator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,12 +110,74 @@ public class PinShapeNonHintingView extends LinearLayout implements PinShapeInpu
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
removeAllViews();
|
||||
mPosition = 0;
|
||||
final int position = mPosition;
|
||||
for (int i = 0; i < position; i++) {
|
||||
delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
return this;
|
||||
}
|
||||
|
||||
class PinShapeViewTransition extends Transition {
|
||||
private static final String PROP_BOUNDS = "PinShapeViewTransition:bounds";
|
||||
|
||||
@Override
|
||||
public void captureEndValues(TransitionValues transitionValues) {
|
||||
if (transitionValues != null) {
|
||||
captureValues(transitionValues);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureStartValues(TransitionValues transitionValues) {
|
||||
if (transitionValues != null) {
|
||||
captureValues(transitionValues);
|
||||
}
|
||||
}
|
||||
|
||||
private void captureValues(TransitionValues values) {
|
||||
Rect boundsRect = new Rect();
|
||||
boundsRect.left = values.view.getLeft();
|
||||
boundsRect.top = values.view.getTop();
|
||||
boundsRect.right = values.view.getRight();
|
||||
boundsRect.bottom = values.view.getBottom();
|
||||
values.values.put(PROP_BOUNDS, boundsRect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getTransitionProperties() {
|
||||
return new String[] { PROP_BOUNDS };
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
|
||||
TransitionValues endValues) {
|
||||
if (sceneRoot == null || startValues == null || endValues == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Rect startRect = (Rect) startValues.values.get(PROP_BOUNDS);
|
||||
Rect endRect = (Rect) endValues.values.get(PROP_BOUNDS);
|
||||
View v = startValues.view;
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
|
||||
animator.setDuration(PasswordTextView.APPEAR_DURATION);
|
||||
animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
|
||||
animator.addUpdateListener(valueAnimator -> {
|
||||
float value = (float) valueAnimator.getAnimatedValue();
|
||||
int diff = startRect.left - endRect.left;
|
||||
int currentTranslation = (int) ((diff) * value);
|
||||
v.setLeftTopRightBottom(
|
||||
startRect.left - currentTranslation,
|
||||
startRect.top,
|
||||
startRect.right - currentTranslation,
|
||||
startRect.bottom
|
||||
);
|
||||
});
|
||||
animator.start();
|
||||
return animator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
|
||||
|
||||
@Mock lateinit var featureFlags: FeatureFlags
|
||||
@Mock lateinit var passwordTextView: PasswordTextView
|
||||
@Mock lateinit var deleteButton: NumPadButton
|
||||
@Mock lateinit var enterButton: View
|
||||
|
||||
lateinit var pinViewController: KeyguardPinViewController
|
||||
|
||||
@@ -92,6 +94,9 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
|
||||
`when`(keyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
|
||||
.thenReturn(passwordTextView)
|
||||
`when`(keyguardPinView.resources).thenReturn(context.resources)
|
||||
`when`(keyguardPinView.findViewById<NumPadButton>(R.id.delete_button))
|
||||
.thenReturn(deleteButton)
|
||||
`when`(keyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton)
|
||||
pinViewController =
|
||||
KeyguardPinViewController(
|
||||
keyguardPinView,
|
||||
@@ -126,8 +131,14 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun startAppearAnimation_withAutoPinConfirmation() {
|
||||
`when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
|
||||
`when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
|
||||
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
|
||||
`when`(passwordTextView.text).thenReturn("")
|
||||
|
||||
pinViewController.startAppearAnimation()
|
||||
verify(passwordTextView).setIsPinHinting(true, true)
|
||||
verify(deleteButton).visibility = View.INVISIBLE
|
||||
verify(enterButton).visibility = View.INVISIBLE
|
||||
verify(passwordTextView).setUsePinShapes(true)
|
||||
verify(passwordTextView).setIsPinHinting(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,30 +16,49 @@
|
||||
|
||||
package com.android.keyguard
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.view.LayoutInflater
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.google.common.truth.Truth
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
class PinShapeHintingViewTest : SysuiTestCase() {
|
||||
lateinit var mPinShapeHintingView: PinShapeHintingView
|
||||
lateinit var underTest: PinShapeHintingView
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mPinShapeHintingView =
|
||||
LayoutInflater.from(context).inflate(R.layout.keyguard_pin_shape_six_digit_view, null)
|
||||
underTest =
|
||||
LayoutInflater.from(context).inflate(R.layout.keyguard_pin_shape_hinting_view, null)
|
||||
as PinShapeHintingView
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAppend() {
|
||||
// Add more when animation part is complete
|
||||
mPinShapeHintingView.append()
|
||||
underTest.append()
|
||||
Truth.assertThat(underTest.childCount).isEqualTo(6)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDelete() {
|
||||
mPinShapeHintingView.delete()
|
||||
underTest.delete()
|
||||
Truth.assertThat(underTest.childCount).isEqualTo(6)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReset() {
|
||||
for (i in 0 until 3) {
|
||||
underTest.append()
|
||||
}
|
||||
underTest.reset()
|
||||
Truth.assertThat(underTest.childCount).isEqualTo(6)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,31 +16,59 @@
|
||||
|
||||
package com.android.keyguard
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.view.LayoutInflater
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.google.common.truth.Truth
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
class PinShapeNonHintingViewTest : SysuiTestCase() {
|
||||
lateinit var mPinShapeNonHintingView: PinShapeNonHintingView
|
||||
lateinit var underTest: PinShapeNonHintingView
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mPinShapeNonHintingView =
|
||||
LayoutInflater.from(context)
|
||||
.inflate(R.layout.keyguard_pin_shape_non_six_digit_view, null)
|
||||
underTest =
|
||||
LayoutInflater.from(context).inflate(R.layout.keyguard_pin_shape_non_hinting_view, null)
|
||||
as PinShapeNonHintingView
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAppend() {
|
||||
// Add more when animation part is complete
|
||||
mPinShapeNonHintingView.append()
|
||||
underTest.append()
|
||||
Truth.assertThat(underTest.childCount).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDelete() {
|
||||
mPinShapeNonHintingView.delete()
|
||||
for (i in 0 until 3) {
|
||||
underTest.append()
|
||||
}
|
||||
underTest.delete()
|
||||
|
||||
underTest.postDelayed(
|
||||
{ Truth.assertThat(underTest.childCount).isEqualTo(2) },
|
||||
PasswordTextView.DISAPPEAR_DURATION + 100L
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReset() {
|
||||
for (i in 0 until 3) {
|
||||
underTest.append()
|
||||
}
|
||||
underTest.reset()
|
||||
underTest.postDelayed(
|
||||
{ Truth.assertThat(underTest.childCount).isEqualTo(0) },
|
||||
PasswordTextView.DISAPPEAR_DURATION + 100L
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user