Merge changes I47ce330e,Ic9bde6d1
* changes: [Bouncer] Add ui changes to auto confirmation. Changes to lockscreen for laying out the 6 digit view
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -69,6 +69,10 @@
|
||||
|
||||
<!-- The size of the dots in the PIN unlock method. -->
|
||||
<dimen name="password_dot_size">9dp</dimen>
|
||||
|
||||
<!-- The size of the shape in the PIN unlock method. -->
|
||||
<dimen name="password_shape_size">34dp</dimen>
|
||||
|
||||
<!-- The size of PIN text in the PIN unlock method. -->
|
||||
<integer name="scaled_password_text_size">40</integer>
|
||||
|
||||
@@ -139,4 +143,9 @@
|
||||
|
||||
<!-- Translation y for appear animation -->
|
||||
<dimen name="keyguard_host_view_translation_y">80dp</dimen>
|
||||
|
||||
<!-- Attributes for placeholder dots in 6 digit PIN view -->
|
||||
<dimen name="default_dot_diameter">34dp</dimen>
|
||||
<dimen name="default_dot_spacing">0dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
||||
56
packages/SystemUI/res/drawable/pin_dot_avd.xml
Normal file
56
packages/SystemUI/res/drawable/pin_dot_avd.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt">
|
||||
<target android:name="time_group">
|
||||
<aapt:attr name="android:animation">
|
||||
<set android:ordering="together">
|
||||
<objectAnimator
|
||||
android:duration="500"
|
||||
android:propertyName="translateX"
|
||||
android:startOffset="0"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="1"
|
||||
android:valueType="floatType" />
|
||||
</set>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<aapt:attr name="android:drawable">
|
||||
<vector
|
||||
android:width="60dp"
|
||||
android:height="61dp"
|
||||
android:viewportHeight="60"
|
||||
android:viewportWidth="60">
|
||||
<group android:name="_R_G">
|
||||
<group
|
||||
android:name="_R_G_L_0_G"
|
||||
android:translateX="43.237"
|
||||
android:translateY="38.112">
|
||||
<path
|
||||
android:name="_R_G_L_0_G_D_0_P_0"
|
||||
android:pathData=" M-13.24 -12.11 C-11.03,-12.11 -9.24,-10.32 -9.24,-8.11 C-9.24,-5.9 -11.03,-4.11 -13.24,-4.11 C-15.44,-4.11 -17.24,-5.9 -17.24,-8.11 C-17.24,-10.32 -15.44,-12.11 -13.24,-12.11c "
|
||||
android:strokeAlpha="1"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2" />
|
||||
</group>
|
||||
</group>
|
||||
<group android:name="time_group" />
|
||||
</vector>
|
||||
</aapt:attr>
|
||||
</animated-vector>
|
||||
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
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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
|
||||
-->
|
||||
<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.PinShapeHintingView>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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
|
||||
-->
|
||||
<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.PinShapeNonHintingView>
|
||||
@@ -30,6 +30,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.classifier.FalsingCollector;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.statusbar.policy.DevicePostureController;
|
||||
import com.android.systemui.util.ViewController;
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||
@@ -184,6 +185,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
|
||||
private final FalsingCollector mFalsingCollector;
|
||||
private final DevicePostureController mDevicePostureController;
|
||||
private final KeyguardViewController mKeyguardViewController;
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
|
||||
@Inject
|
||||
public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
|
||||
@@ -195,7 +197,8 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
|
||||
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
|
||||
EmergencyButtonController.Factory emergencyButtonControllerFactory,
|
||||
DevicePostureController devicePostureController,
|
||||
KeyguardViewController keyguardViewController) {
|
||||
KeyguardViewController keyguardViewController,
|
||||
FeatureFlags featureFlags) {
|
||||
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
||||
mLockPatternUtils = lockPatternUtils;
|
||||
mLatencyTracker = latencyTracker;
|
||||
@@ -209,6 +212,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
|
||||
mFalsingCollector = falsingCollector;
|
||||
mDevicePostureController = devicePostureController;
|
||||
mKeyguardViewController = keyguardViewController;
|
||||
mFeatureFlags = featureFlags;
|
||||
}
|
||||
|
||||
/** Create a new {@link KeyguardInputViewController}. */
|
||||
@@ -236,7 +240,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
|
||||
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
|
||||
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
|
||||
mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
|
||||
mDevicePostureController);
|
||||
mDevicePostureController, mFeatureFlags);
|
||||
} else if (keyguardInputView instanceof KeyguardSimPinView) {
|
||||
return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
|
||||
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.keyguard;
|
||||
|
||||
import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.DEFAULT_PIN_LENGTH;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.android.internal.util.LatencyTracker;
|
||||
@@ -23,6 +25,8 @@ import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.classifier.FalsingCollector;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.statusbar.policy.DevicePostureController;
|
||||
|
||||
public class KeyguardPinViewController
|
||||
@@ -31,6 +35,14 @@ public class KeyguardPinViewController
|
||||
private final DevicePostureController mPostureController;
|
||||
private final DevicePostureController.Callback mPostureCallback = posture ->
|
||||
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;
|
||||
private long mPinLength;
|
||||
|
||||
protected KeyguardPinViewController(KeyguardPINView view,
|
||||
KeyguardUpdateMonitor keyguardUpdateMonitor,
|
||||
@@ -40,12 +52,16 @@ public class KeyguardPinViewController
|
||||
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
|
||||
EmergencyButtonController emergencyButtonController,
|
||||
FalsingCollector falsingCollector,
|
||||
DevicePostureController postureController) {
|
||||
DevicePostureController postureController,
|
||||
FeatureFlags featureFlags) {
|
||||
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
|
||||
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
|
||||
emergencyButtonController, falsingCollector);
|
||||
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
||||
mPostureController = postureController;
|
||||
mLockPatternUtils = lockPatternUtils;
|
||||
mFeatureFlags = featureFlags;
|
||||
mBackspaceKey = view.findViewById(R.id.delete_button);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,19 +75,76 @@ public class KeyguardPinViewController
|
||||
getKeyguardSecurityCallback().onCancelClicked();
|
||||
});
|
||||
}
|
||||
|
||||
mPasswordEntry.setUserActivityListener(this::onUserInput);
|
||||
mPostureController.addCallback(mPostureCallback);
|
||||
}
|
||||
|
||||
protected void onUserInput() {
|
||||
super.onUserInput();
|
||||
if (isAutoConfirmation()) {
|
||||
updateBackSpaceVisibility();
|
||||
if (mPasswordEntry.getText().length() == mPinLength) {
|
||||
verifyPasswordAndUnlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewDetached() {
|
||||
super.onViewDetached();
|
||||
mPostureController.removeCallback(mPostureCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAppearAnimation() {
|
||||
if (mFeatureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)) {
|
||||
mUserId = KeyguardUpdateMonitor.getCurrentUser();
|
||||
mPinLength = mLockPatternUtils.getPinLength(mUserId);
|
||||
mBackspaceKey.setTransparentMode(/* isTransparentMode= */ isAutoConfirmation());
|
||||
mOkButton.setVisibility(isAutoConfirmation() ? View.INVISIBLE : View.VISIBLE);
|
||||
updateBackSpaceVisibility();
|
||||
mPasswordEntry.setUsePinShapes(true);
|
||||
mPasswordEntry.setIsPinHinting(isAutoConfirmation() && isPinHinting());
|
||||
}
|
||||
super.startAppearAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startDisappearAnimation(Runnable finishRunnable) {
|
||||
return mView.startDisappearAnimation(
|
||||
mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private boolean isAutoConfirmation() {
|
||||
//Checks if user has enabled the auto confirm in Settings
|
||||
return mLockPatternUtils.isAutoPinConfirmEnabled(mUserId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,13 +35,14 @@ import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.systemui.R;
|
||||
@@ -52,12 +53,12 @@ import java.util.ArrayList;
|
||||
* A View similar to a textView which contains password text and can animate when the text is
|
||||
* changed
|
||||
*/
|
||||
public class PasswordTextView extends View {
|
||||
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;
|
||||
|
||||
@@ -95,11 +96,14 @@ public class PasswordTextView extends View {
|
||||
private PowerManager mPM;
|
||||
private int mCharPadding;
|
||||
private final Paint mDrawPaint = new Paint();
|
||||
private int mDrawColor;
|
||||
private Interpolator mAppearInterpolator;
|
||||
private Interpolator mDisappearInterpolator;
|
||||
private Interpolator mFastOutSlowInInterpolator;
|
||||
private boolean mShowPassword;
|
||||
private UserActivityListener mUserActivityListener;
|
||||
private PinShapeInput mPinShapeInput;
|
||||
private boolean mUsePinShapes = false;
|
||||
|
||||
public interface UserActivityListener {
|
||||
void onUserActivity();
|
||||
@@ -141,8 +145,10 @@ public class PasswordTextView extends View {
|
||||
mCharPadding = a.getDimensionPixelSize(R.styleable.PasswordTextView_charPadding,
|
||||
getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.password_char_padding));
|
||||
mDrawPaint.setColor(a.getColor(R.styleable.PasswordTextView_android_textColor,
|
||||
Color.WHITE));
|
||||
mDrawColor = a.getColor(R.styleable.PasswordTextView_android_textColor,
|
||||
Color.WHITE);
|
||||
mDrawPaint.setColor(mDrawColor);
|
||||
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
@@ -161,6 +167,7 @@ public class PasswordTextView extends View {
|
||||
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
|
||||
android.R.interpolator.fast_out_slow_in);
|
||||
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -171,6 +178,12 @@ public class PasswordTextView extends View {
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
// Do not use legacy draw animations for pin shapes.
|
||||
if (mUsePinShapes) {
|
||||
super.onDraw(canvas);
|
||||
return;
|
||||
}
|
||||
|
||||
float totalDrawingWidth = getDrawingWidth();
|
||||
float currentDrawPosition;
|
||||
if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT) {
|
||||
@@ -205,9 +218,12 @@ public class PasswordTextView extends View {
|
||||
* Reload colors from resources.
|
||||
**/
|
||||
public void reloadColors() {
|
||||
int textColor = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary)
|
||||
.getDefaultColor();
|
||||
mDrawPaint.setColor(textColor);
|
||||
mDrawColor = Utils.getColorAttr(getContext(),
|
||||
android.R.attr.textColorPrimary).getDefaultColor();
|
||||
mDrawPaint.setColor(mDrawColor);
|
||||
if (mPinShapeInput != null) {
|
||||
mPinShapeInput.setDrawColor(mDrawColor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -252,6 +268,9 @@ public class PasswordTextView extends View {
|
||||
charState = mTextChars.get(newLength - 1);
|
||||
charState.whichChar = c;
|
||||
}
|
||||
if (mPinShapeInput != null) {
|
||||
mPinShapeInput.append();
|
||||
}
|
||||
charState.startAppearAnimation();
|
||||
|
||||
// ensure that the previous element is being swapped
|
||||
@@ -265,8 +284,8 @@ public class PasswordTextView extends View {
|
||||
sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length(), 0, 1);
|
||||
}
|
||||
|
||||
public void setUserActivityListener(UserActivityListener userActivitiListener) {
|
||||
mUserActivityListener = userActivitiListener;
|
||||
public void setUserActivityListener(UserActivityListener userActivityListener) {
|
||||
mUserActivityListener = userActivityListener;
|
||||
}
|
||||
|
||||
private void userActivity() {
|
||||
@@ -284,6 +303,9 @@ public class PasswordTextView extends View {
|
||||
CharState charState = mTextChars.get(length - 1);
|
||||
charState.startRemoveAnimation(0, 0);
|
||||
sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length() - 1, 1, 0);
|
||||
if (mPinShapeInput != null) {
|
||||
mPinShapeInput.delete();
|
||||
}
|
||||
}
|
||||
userActivity();
|
||||
}
|
||||
@@ -339,6 +361,11 @@ public class PasswordTextView extends View {
|
||||
}
|
||||
if (!animated) {
|
||||
mTextChars.clear();
|
||||
} else {
|
||||
userActivity();
|
||||
}
|
||||
if (mPinShapeInput != null) {
|
||||
mPinShapeInput.reset();
|
||||
}
|
||||
if (announce) {
|
||||
sendAccessibilityEventTypeViewTextChanged(textbefore, 0, textbefore.length(), 0);
|
||||
@@ -385,6 +412,35 @@ public class PasswordTextView extends View {
|
||||
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 isPinHinting) {
|
||||
if (mPinShapeInput != null) {
|
||||
removeView(mPinShapeInput.getView());
|
||||
mPinShapeInput = null;
|
||||
}
|
||||
|
||||
if (isPinHinting) {
|
||||
mPinShapeInput = (PinShapeInput) LayoutInflater.from(mContext).inflate(
|
||||
R.layout.keyguard_pin_shape_hinting_view, null);
|
||||
} else {
|
||||
mPinShapeInput = (PinShapeInput) LayoutInflater.from(mContext).inflate(
|
||||
R.layout.keyguard_pin_shape_non_hinting_view, null);
|
||||
}
|
||||
addView(mPinShapeInput.getView());
|
||||
}
|
||||
|
||||
private class CharState {
|
||||
char whichChar;
|
||||
ValueAnimator textAnimator;
|
||||
|
||||
@@ -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)]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 android.graphics.drawable.AnimatedVectorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* This class contains implementation for methods that will be used when user has set a
|
||||
* six digit pin on their device
|
||||
*/
|
||||
public class PinShapeHintingView extends LinearLayout implements PinShapeInput {
|
||||
|
||||
private int mPinLength;
|
||||
private int mDotDiameter;
|
||||
private int mDotSpacing;
|
||||
private int mColor = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary)
|
||||
.getDefaultColor();
|
||||
private int mPosition = 0;
|
||||
private static final int DEFAULT_PIN_LENGTH = 6;
|
||||
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);
|
||||
|
||||
for (int i = 0; i < mPinLength; i++) {
|
||||
ImageView pinDot = new ImageView(context, attrs);
|
||||
LayoutParams layoutParams = new LayoutParams(mDotDiameter, mDotDiameter);
|
||||
pinDot.setLayoutParams(layoutParams);
|
||||
pinDot.setImageResource(R.drawable.pin_dot_avd);
|
||||
if (pinDot.getDrawable() != null) {
|
||||
Drawable drawable = DrawableCompat.wrap(pinDot.getDrawable());
|
||||
DrawableCompat.setTint(drawable, mColor);
|
||||
}
|
||||
addView(pinDot);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append() {
|
||||
if (mPosition == DEFAULT_PIN_LENGTH) {
|
||||
return;
|
||||
}
|
||||
setAnimatedDrawable(mPosition, mPinShapeAdapter.getShape(mPosition));
|
||||
mPosition++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
if (mPosition == 0) {
|
||||
return;
|
||||
}
|
||||
mPosition--;
|
||||
setAnimatedDrawable(mPosition, R.drawable.pin_dot_delete_avd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawColor(int color) {
|
||||
this.mColor = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
int size = mPosition;
|
||||
for (int i = 0; i < size; i++) {
|
||||
delete();
|
||||
}
|
||||
mPosition = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.view.View;
|
||||
|
||||
/**
|
||||
* A common interface for classes that provide functionality for the PIN type view
|
||||
*/
|
||||
public interface PinShapeInput {
|
||||
|
||||
/**
|
||||
* This is the method that is triggered when user types in a character
|
||||
*/
|
||||
void append();
|
||||
|
||||
/**
|
||||
* This is the method that is triggered when user deletes a character
|
||||
*/
|
||||
void delete();
|
||||
|
||||
/**
|
||||
* This is the method that is triggered for setting the color of the view
|
||||
*/
|
||||
void setDrawColor(int color);
|
||||
|
||||
/**
|
||||
* This is the method that is triggered for resetting the view
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* This is the method that is triggered for getting the view
|
||||
*/
|
||||
View getView();
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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.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
|
||||
* non six digit pin on their device
|
||||
*/
|
||||
public class PinShapeNonHintingView extends LinearLayout implements PinShapeInput {
|
||||
|
||||
private int mColor = Utils.getColorAttr(getContext(),
|
||||
android.R.attr.textColorPrimary).getDefaultColor();
|
||||
private int mPosition = 0;
|
||||
private final PinShapeAdapter mPinShapeAdapter;
|
||||
private Animation mCurrentPlayingAnimation;
|
||||
public PinShapeNonHintingView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
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;
|
||||
}
|
||||
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
|
||||
public void setDrawColor(int color) {
|
||||
this.mColor = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,4 +26,10 @@ object KeyguardBouncerConstants {
|
||||
const val EXPANSION_HIDDEN = 1f
|
||||
const val EXPANSION_VISIBLE = 0f
|
||||
const val ALPHA_EXPANSION_THRESHOLD = 0.95f
|
||||
|
||||
/**
|
||||
* This value is used for denoting the PIN length at which we want to layout the view in which
|
||||
* PIN hinting is enabled
|
||||
*/
|
||||
const val DEFAULT_PIN_LENGTH = 6
|
||||
}
|
||||
|
||||
@@ -27,11 +27,14 @@ import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.classifier.FalsingCollector
|
||||
import com.android.systemui.classifier.FalsingCollectorFake
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.statusbar.policy.DevicePostureController
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.anyBoolean
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
@@ -71,6 +74,11 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
|
||||
private val falsingCollector: FalsingCollector = FalsingCollectorFake()
|
||||
@Mock lateinit var postureController: DevicePostureController
|
||||
|
||||
@Mock lateinit var featureFlags: FeatureFlags
|
||||
@Mock lateinit var passwordTextView: PasswordTextView
|
||||
@Mock lateinit var deleteButton: NumPadButton
|
||||
@Mock lateinit var enterButton: View
|
||||
|
||||
lateinit var pinViewController: KeyguardPinViewController
|
||||
|
||||
@Before
|
||||
@@ -82,7 +90,13 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
|
||||
keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
|
||||
)
|
||||
.thenReturn(keyguardMessageAreaController)
|
||||
`when`(keyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
|
||||
`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,
|
||||
@@ -95,7 +109,8 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
|
||||
liftToActivateListener,
|
||||
mEmergencyButtonController,
|
||||
falsingCollector,
|
||||
postureController
|
||||
postureController,
|
||||
featureFlags
|
||||
)
|
||||
}
|
||||
|
||||
@@ -112,4 +127,18 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
|
||||
pinViewController.startAppearAnimation()
|
||||
verify(keyguardMessageAreaController, Mockito.never()).setMessage(anyString(), anyBoolean())
|
||||
}
|
||||
|
||||
@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(deleteButton).visibility = View.INVISIBLE
|
||||
verify(enterButton).visibility = View.INVISIBLE
|
||||
verify(passwordTextView).setUsePinShapes(true)
|
||||
verify(passwordTextView).setIsPinHinting(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.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 underTest: PinShapeHintingView
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
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
|
||||
underTest.append()
|
||||
Truth.assertThat(underTest.childCount).isEqualTo(6)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDelete() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.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 underTest: PinShapeNonHintingView
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
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
|
||||
underTest.append()
|
||||
Truth.assertThat(underTest.childCount).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDelete() {
|
||||
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