1/* 2 * Copyright (C) 2014 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 17package com.android.systemui.statusbar.phone; 18 19import android.content.Context; 20import android.view.Choreographer; 21import android.view.KeyEvent; 22import android.view.LayoutInflater; 23import android.view.View; 24import android.view.ViewGroup; 25import android.view.accessibility.AccessibilityEvent; 26 27import com.android.internal.widget.LockPatternUtils; 28import com.android.keyguard.KeyguardHostView; 29import com.android.keyguard.KeyguardSecurityView; 30import com.android.keyguard.R; 31import com.android.keyguard.ViewMediatorCallback; 32import com.android.systemui.DejankUtils; 33 34import static com.android.keyguard.KeyguardHostView.OnDismissAction; 35import static com.android.keyguard.KeyguardSecurityModel.SecurityMode; 36 37/** 38 * A class which manages the bouncer on the lockscreen. 39 */ 40public class KeyguardBouncer { 41 42 private Context mContext; 43 private ViewMediatorCallback mCallback; 44 private LockPatternUtils mLockPatternUtils; 45 private ViewGroup mContainer; 46 private StatusBarWindowManager mWindowManager; 47 private KeyguardHostView mKeyguardView; 48 private ViewGroup mRoot; 49 private boolean mShowingSoon; 50 private int mBouncerPromptReason; 51 52 public KeyguardBouncer(Context context, ViewMediatorCallback callback, 53 LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager, 54 ViewGroup container) { 55 mContext = context; 56 mCallback = callback; 57 mLockPatternUtils = lockPatternUtils; 58 mContainer = container; 59 mWindowManager = windowManager; 60 } 61 62 public void show(boolean resetSecuritySelection) { 63 ensureView(); 64 if (resetSecuritySelection) { 65 // showPrimarySecurityScreen() updates the current security method. This is needed in 66 // case we are already showing and the current security method changed. 67 mKeyguardView.showPrimarySecurityScreen(); 68 } 69 if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) { 70 return; 71 } 72 73 // Try to dismiss the Keyguard. If no security pattern is set, this will dismiss the whole 74 // Keyguard. If we need to authenticate, show the bouncer. 75 if (!mKeyguardView.dismiss()) { 76 mShowingSoon = true; 77 78 // Split up the work over multiple frames. 79 DejankUtils.postAfterTraversal(mShowRunnable); 80 } 81 } 82 83 private final Runnable mShowRunnable = new Runnable() { 84 @Override 85 public void run() { 86 mRoot.setVisibility(View.VISIBLE); 87 mKeyguardView.onResume(); 88 showPromptReason(mBouncerPromptReason); 89 mKeyguardView.startAppearAnimation(); 90 mShowingSoon = false; 91 mKeyguardView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 92 } 93 }; 94 95 /** 96 * Show a string explaining why the security view needs to be solved. 97 * 98 * @param reason a flag indicating which string should be shown, see 99 * {@link KeyguardSecurityView#PROMPT_REASON_NONE} 100 * and {@link KeyguardSecurityView#PROMPT_REASON_RESTART} 101 */ 102 public void showPromptReason(int reason) { 103 mKeyguardView.showPromptReason(reason); 104 } 105 106 private void cancelShowRunnable() { 107 DejankUtils.removeCallbacks(mShowRunnable); 108 mShowingSoon = false; 109 } 110 111 public void showWithDismissAction(OnDismissAction r, Runnable cancelAction) { 112 ensureView(); 113 mKeyguardView.setOnDismissAction(r, cancelAction); 114 show(false /* resetSecuritySelection */); 115 } 116 117 public void hide(boolean destroyView) { 118 cancelShowRunnable(); 119 if (mKeyguardView != null) { 120 mKeyguardView.cancelDismissAction(); 121 mKeyguardView.cleanUp(); 122 } 123 if (destroyView) { 124 removeView(); 125 } else if (mRoot != null) { 126 mRoot.setVisibility(View.INVISIBLE); 127 } 128 } 129 130 /** 131 * See {@link StatusBarKeyguardViewManager#startPreHideAnimation}. 132 */ 133 public void startPreHideAnimation(Runnable runnable) { 134 if (mKeyguardView != null) { 135 mKeyguardView.startDisappearAnimation(runnable); 136 } else if (runnable != null) { 137 runnable.run(); 138 } 139 } 140 141 /** 142 * Reset the state of the view. 143 */ 144 public void reset() { 145 cancelShowRunnable(); 146 inflateView(); 147 } 148 149 public void onScreenTurnedOff() { 150 if (mKeyguardView != null && mRoot != null && mRoot.getVisibility() == View.VISIBLE) { 151 mKeyguardView.onPause(); 152 } 153 } 154 155 public boolean isShowing() { 156 return mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE); 157 } 158 159 public void prepare() { 160 boolean wasInitialized = mRoot != null; 161 ensureView(); 162 if (wasInitialized) { 163 mKeyguardView.showPrimarySecurityScreen(); 164 } 165 mBouncerPromptReason = mCallback.getBouncerPromptReason(); 166 } 167 168 private void ensureView() { 169 if (mRoot == null) { 170 inflateView(); 171 } 172 } 173 174 private void inflateView() { 175 removeView(); 176 mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null); 177 mKeyguardView = (KeyguardHostView) mRoot.findViewById(R.id.keyguard_host_view); 178 mKeyguardView.setLockPatternUtils(mLockPatternUtils); 179 mKeyguardView.setViewMediatorCallback(mCallback); 180 mContainer.addView(mRoot, mContainer.getChildCount()); 181 mRoot.setVisibility(View.INVISIBLE); 182 mRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME); 183 } 184 185 private void removeView() { 186 if (mRoot != null && mRoot.getParent() == mContainer) { 187 mContainer.removeView(mRoot); 188 mRoot = null; 189 } 190 } 191 192 public boolean onBackPressed() { 193 return mKeyguardView != null && mKeyguardView.handleBackKey(); 194 } 195 196 /** 197 * @return True if and only if the security method should be shown before showing the 198 * notifications on Keyguard, like SIM PIN/PUK. 199 */ 200 public boolean needsFullscreenBouncer() { 201 ensureView(); 202 if (mKeyguardView != null) { 203 SecurityMode mode = mKeyguardView.getSecurityMode(); 204 return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk; 205 } 206 return false; 207 } 208 209 /** 210 * Like {@link #needsFullscreenBouncer}, but uses the currently visible security method, which 211 * makes this method much faster. 212 */ 213 public boolean isFullscreenBouncer() { 214 if (mKeyguardView != null) { 215 SecurityMode mode = mKeyguardView.getCurrentSecurityMode(); 216 return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk; 217 } 218 return false; 219 } 220 221 /** 222 * WARNING: This method might cause Binder calls. 223 */ 224 public boolean isSecure() { 225 return mKeyguardView == null || mKeyguardView.getSecurityMode() != SecurityMode.None; 226 } 227 228 public boolean onMenuPressed() { 229 ensureView(); 230 if (mKeyguardView.handleMenuKey()) { 231 232 // We need to show it in case it is secure. If not, it will get dismissed in any case. 233 mRoot.setVisibility(View.VISIBLE); 234 mKeyguardView.requestFocus(); 235 mKeyguardView.onResume(); 236 return true; 237 } else { 238 return false; 239 } 240 } 241 242 public boolean interceptMediaKey(KeyEvent event) { 243 ensureView(); 244 return mKeyguardView.interceptMediaKey(event); 245 } 246 247 public void notifyKeyguardAuthenticated() { 248 ensureView(); 249 mKeyguardView.finish(); 250 } 251} 252