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