1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.keyguard;
18 
19 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED;
20 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
21 
22 import android.content.Context;
23 import android.content.res.Configuration;
24 import android.content.res.Resources;
25 import android.util.AttributeSet;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.view.animation.AnimationUtils;
29 
30 import androidx.constraintlayout.helper.widget.Flow;
31 import androidx.constraintlayout.widget.ConstraintLayout;
32 import androidx.constraintlayout.widget.ConstraintSet;
33 
34 import com.android.internal.jank.InteractionJankMonitor;
35 import com.android.settingslib.animation.AppearAnimationUtils;
36 import com.android.settingslib.animation.DisappearAnimationUtils;
37 import com.android.systemui.R;
38 import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
39 
40 /**
41  * Displays a PIN pad for unlocking.
42  */
43 public class KeyguardPINView extends KeyguardPinBasedInputView {
44 
45     private final AppearAnimationUtils mAppearAnimationUtils;
46     private final DisappearAnimationUtils mDisappearAnimationUtils;
47     private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
48     private ConstraintLayout mContainer;
49     private int mDisappearYTranslation;
50     private View[][] mViews;
51     @DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
52 
KeyguardPINView(Context context)53     public KeyguardPINView(Context context) {
54         this(context, null);
55     }
56 
KeyguardPINView(Context context, AttributeSet attrs)57     public KeyguardPINView(Context context, AttributeSet attrs) {
58         super(context, attrs);
59         mAppearAnimationUtils = new AppearAnimationUtils(context);
60         mDisappearAnimationUtils = new DisappearAnimationUtils(context,
61                 125, 0.6f /* translationScale */,
62                 0.45f /* delayScale */, AnimationUtils.loadInterpolator(
63                         mContext, android.R.interpolator.fast_out_linear_in));
64         mDisappearAnimationUtilsLocked = new DisappearAnimationUtils(context,
65                 (long) (125 * KeyguardPatternView.DISAPPEAR_MULTIPLIER_LOCKED),
66                 0.6f /* translationScale */,
67                 0.45f /* delayScale */, AnimationUtils.loadInterpolator(
68                         mContext, android.R.interpolator.fast_out_linear_in));
69         mDisappearYTranslation = getResources().getDimensionPixelSize(
70                 R.dimen.disappear_y_translation);
71     }
72 
73     @Override
onConfigurationChanged(Configuration newConfig)74     protected void onConfigurationChanged(Configuration newConfig) {
75         updateMargins();
76     }
77 
onDevicePostureChanged(@evicePostureInt int posture)78     void onDevicePostureChanged(@DevicePostureInt int posture) {
79         mLastDevicePosture = posture;
80         updateMargins();
81     }
82 
83     @Override
resetState()84     protected void resetState() {
85     }
86 
87     @Override
getPasswordTextViewId()88     protected int getPasswordTextViewId() {
89         return R.id.pinEntry;
90     }
91 
updateMargins()92     private void updateMargins() {
93         Resources res = mContext.getResources();
94 
95         // Re-apply everything to the keys...
96         int verticalMargin = res.getDimensionPixelSize(R.dimen.num_pad_entry_row_margin_bottom);
97         int horizontalMargin = res.getDimensionPixelSize(R.dimen.num_pad_key_margin_end);
98         String ratio = res.getString(R.string.num_pad_key_ratio);
99 
100         Flow flow = (Flow) mContainer.findViewById(R.id.flow1);
101         flow.setHorizontalGap(horizontalMargin);
102         flow.setVerticalGap(verticalMargin);
103 
104         // Update the guideline based on the device posture...
105         float halfOpenPercentage = res.getFloat(R.dimen.half_opened_bouncer_height_ratio);
106 
107         ConstraintSet cs = new ConstraintSet();
108         cs.clone(mContainer);
109         cs.setGuidelinePercent(R.id.pin_pad_top_guideline,
110                 mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f);
111         cs.applyTo(mContainer);
112 
113         // Password entry area
114         int passwordHeight = res.getDimensionPixelSize(R.dimen.keyguard_password_height);
115         View pinEntry = mContainer.findViewById(R.id.pinEntry);
116         ViewGroup.LayoutParams lp = pinEntry.getLayoutParams();
117         lp.height = passwordHeight;
118         pinEntry.setLayoutParams(lp);
119 
120         // Below row0
121         View row0 = mContainer.findViewById(R.id.row0);
122         row0.setPadding(0, 0, 0, verticalMargin);
123 
124         // Above the emergency contact area
125         int marginTop = res.getDimensionPixelSize(R.dimen.keyguard_eca_top_margin);
126         View eca = findViewById(R.id.keyguard_selector_fade_container);
127         if (eca != null) {
128             ViewGroup.MarginLayoutParams mLp = (ViewGroup.MarginLayoutParams) eca.getLayoutParams();
129             mLp.topMargin = marginTop;
130             eca.setLayoutParams(mLp);
131         }
132     }
133 
134     @Override
onFinishInflate()135     protected void onFinishInflate() {
136         super.onFinishInflate();
137 
138         mContainer = findViewById(R.id.pin_container);
139         mViews = new View[][]{
140                 new View[]{
141                         findViewById(R.id.row0), null, null
142                 },
143                 new View[]{
144                         findViewById(R.id.key1), findViewById(R.id.key2),
145                         findViewById(R.id.key3)
146                 },
147                 new View[]{
148                         findViewById(R.id.key4), findViewById(R.id.key5),
149                         findViewById(R.id.key6)
150                 },
151                 new View[]{
152                         findViewById(R.id.key7), findViewById(R.id.key8),
153                         findViewById(R.id.key9)
154                 },
155                 new View[]{
156                         findViewById(R.id.delete_button), findViewById(R.id.key0),
157                         findViewById(R.id.key_enter)
158                 },
159                 new View[]{
160                         null, mEcaView, null
161                 }};
162     }
163 
164     @Override
getWrongPasswordStringId()165     public int getWrongPasswordStringId() {
166         return R.string.kg_wrong_pin;
167     }
168 
169     @Override
startAppearAnimation()170     public void startAppearAnimation() {
171         enableClipping(false);
172         setAlpha(1f);
173         setTranslationY(mAppearAnimationUtils.getStartTranslation());
174         AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 500 /* duration */,
175                 0, mAppearAnimationUtils.getInterpolator(),
176                 getAnimationListener(InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_APPEAR));
177         mAppearAnimationUtils.startAnimation2d(mViews,
178                 new Runnable() {
179                     @Override
180                     public void run() {
181                         enableClipping(true);
182                     }
183                 });
184     }
185 
startDisappearAnimation(boolean needsSlowUnlockTransition, final Runnable finishRunnable)186     public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
187             final Runnable finishRunnable) {
188 
189         enableClipping(false);
190         setTranslationY(0);
191         AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 280 /* duration */,
192                 mDisappearYTranslation, mDisappearAnimationUtils.getInterpolator(),
193                 getAnimationListener(InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_DISAPPEAR));
194         DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
195                         ? mDisappearAnimationUtilsLocked
196                         : mDisappearAnimationUtils;
197         disappearAnimationUtils.startAnimation2d(mViews,
198                 () -> {
199                     enableClipping(true);
200                     if (finishRunnable != null) {
201                         finishRunnable.run();
202                     }
203                 });
204         return true;
205     }
206 
enableClipping(boolean enable)207     private void enableClipping(boolean enable) {
208         mContainer.setClipToPadding(enable);
209         mContainer.setClipChildren(enable);
210         setClipChildren(enable);
211     }
212 
213     @Override
hasOverlappingRendering()214     public boolean hasOverlappingRendering() {
215         return false;
216     }
217 }
218