KeyguardSelectorView.java revision d54281061c1d610fde54cbc4e7408b73a82e6378
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 */ 16package com.android.keyguard; 17 18import android.animation.ObjectAnimator; 19import android.app.ActivityManager; 20import android.app.PendingIntent; 21import android.app.SearchManager; 22import android.app.admin.DevicePolicyManager; 23import android.content.ComponentName; 24import android.content.Context; 25import android.content.Intent; 26import android.graphics.drawable.Drawable; 27import android.os.Bundle; 28import android.os.PowerManager; 29import android.os.UserHandle; 30import android.provider.Settings; 31import android.speech.hotword.HotwordRecognitionListener; 32import android.speech.hotword.HotwordRecognizer; 33import android.telephony.TelephonyManager; 34import android.util.AttributeSet; 35import android.util.Log; 36import android.util.Slog; 37import android.view.View; 38import android.widget.LinearLayout; 39 40import com.android.internal.telephony.IccCardConstants.State; 41import com.android.internal.widget.LockPatternUtils; 42import com.android.internal.widget.multiwaveview.GlowPadView; 43import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener; 44 45public class KeyguardSelectorView extends LinearLayout implements KeyguardSecurityView { 46 private static final boolean DEBUG = KeyguardHostView.DEBUG; 47 private static final String TAG = "SecuritySelectorView"; 48 private static final String ASSIST_ICON_METADATA_NAME = 49 "com.android.systemui.action_assist_icon"; 50 51 // Don't enable hotword on limited-memory devices. 52 private static final boolean ENABLE_HOTWORD = !ActivityManager.isLowRamDeviceStatic(); 53 54 // TODO: Fix this to be non-static. 55 private static HotwordRecognizer sHotwordClient; 56 57 private KeyguardSecurityCallback mCallback; 58 private GlowPadView mGlowPadView; 59 private ObjectAnimator mAnim; 60 private View mFadeView; 61 private boolean mIsBouncing; 62 private boolean mCameraDisabled; 63 private boolean mSearchDisabled; 64 private LockPatternUtils mLockPatternUtils; 65 private SecurityMessageDisplay mSecurityMessageDisplay; 66 private Drawable mBouncerFrame; 67 68 OnTriggerListener mOnTriggerListener = new OnTriggerListener() { 69 70 public void onTrigger(View v, int target) { 71 final int resId = mGlowPadView.getResourceIdForTarget(target); 72 maybeStopHotwordDetector(); 73 74 switch (resId) { 75 case R.drawable.ic_action_assist_generic: 76 Intent assistIntent = 77 ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) 78 .getAssistIntent(mContext, true, UserHandle.USER_CURRENT); 79 if (assistIntent != null) { 80 mActivityLauncher.launchActivity(assistIntent, false, true, null, null); 81 } else { 82 Log.w(TAG, "Failed to get intent for assist activity"); 83 } 84 mCallback.userActivity(0); 85 break; 86 87 case R.drawable.ic_lockscreen_camera: 88 mActivityLauncher.launchCamera(null, null); 89 mCallback.userActivity(0); 90 break; 91 92 case R.drawable.ic_lockscreen_unlock_phantom: 93 case R.drawable.ic_lockscreen_unlock: 94 mCallback.userActivity(0); 95 mCallback.dismiss(false); 96 break; 97 } 98 } 99 100 public void onReleased(View v, int handle) { 101 if (!mIsBouncing) { 102 doTransition(mFadeView, 1.0f); 103 } 104 } 105 106 public void onGrabbed(View v, int handle) { 107 mCallback.userActivity(0); 108 doTransition(mFadeView, 0.0f); 109 } 110 111 public void onGrabbedStateChange(View v, int handle) { 112 113 } 114 115 public void onFinishFinalAnimation() { 116 117 } 118 119 }; 120 121 KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { 122 123 @Override 124 public void onDevicePolicyManagerStateChanged() { 125 updateTargets(); 126 } 127 128 @Override 129 public void onSimStateChanged(State simState) { 130 updateTargets(); 131 } 132 133 @Override 134 public void onPhoneStateChanged(int phoneState) { 135 if (ENABLE_HOTWORD) { 136 // We need to stop hotword detection when a call state is not idle anymore. 137 if (phoneState != TelephonyManager.CALL_STATE_IDLE) { 138 if (DEBUG) Log.d(TAG, "Stopping due to call state not being idle"); 139 maybeStopHotwordDetector(); 140 } 141 } 142 } 143 144 @Override 145 public void onUserSwitching(int userId) { 146 maybeStopHotwordDetector(); 147 } 148 }; 149 150 private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() { 151 152 @Override 153 KeyguardSecurityCallback getCallback() { 154 return mCallback; 155 } 156 157 @Override 158 LockPatternUtils getLockPatternUtils() { 159 return mLockPatternUtils; 160 } 161 162 @Override 163 Context getContext() { 164 return mContext; 165 }}; 166 167 public KeyguardSelectorView(Context context) { 168 this(context, null); 169 } 170 171 public KeyguardSelectorView(Context context, AttributeSet attrs) { 172 super(context, attrs); 173 mLockPatternUtils = new LockPatternUtils(getContext()); 174 } 175 176 @Override 177 protected void onFinishInflate() { 178 super.onFinishInflate(); 179 mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view); 180 mGlowPadView.setOnTriggerListener(mOnTriggerListener); 181 updateTargets(); 182 183 mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); 184 View bouncerFrameView = findViewById(R.id.keyguard_selector_view_frame); 185 mBouncerFrame = bouncerFrameView.getBackground(); 186 if (ENABLE_HOTWORD && sHotwordClient == null) { 187 sHotwordClient = HotwordRecognizer.createHotwordRecognizer(getContext()); 188 } 189 } 190 191 public void setCarrierArea(View carrierArea) { 192 mFadeView = carrierArea; 193 } 194 195 public boolean isTargetPresent(int resId) { 196 return mGlowPadView.getTargetPosition(resId) != -1; 197 } 198 199 @Override 200 public void showUsabilityHint() { 201 mGlowPadView.ping(); 202 } 203 204 private void updateTargets() { 205 int currentUserHandle = mLockPatternUtils.getCurrentUser(); 206 DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager(); 207 int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUserHandle); 208 boolean secureCameraDisabled = mLockPatternUtils.isSecure() 209 && (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0; 210 boolean cameraDisabledByAdmin = dpm.getCameraDisabled(null, currentUserHandle) 211 || secureCameraDisabled; 212 final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(getContext()); 213 boolean disabledBySimState = monitor.isSimLocked(); 214 boolean cameraTargetPresent = 215 isTargetPresent(R.drawable.ic_lockscreen_camera); 216 boolean searchTargetPresent = 217 isTargetPresent(R.drawable.ic_action_assist_generic); 218 219 if (cameraDisabledByAdmin) { 220 Log.v(TAG, "Camera disabled by Device Policy"); 221 } else if (disabledBySimState) { 222 Log.v(TAG, "Camera disabled by Sim State"); 223 } 224 boolean currentUserSetup = 0 != Settings.Secure.getIntForUser( 225 mContext.getContentResolver(), 226 Settings.Secure.USER_SETUP_COMPLETE, 227 0 /*default */, 228 currentUserHandle); 229 boolean searchActionAvailable = 230 ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) 231 .getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null; 232 mCameraDisabled = cameraDisabledByAdmin || disabledBySimState || !cameraTargetPresent 233 || !currentUserSetup; 234 mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent 235 || !currentUserSetup; 236 updateResources(); 237 } 238 239 public void updateResources() { 240 // Update the search icon with drawable from the search .apk 241 if (!mSearchDisabled) { 242 Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) 243 .getAssistIntent(mContext, false, UserHandle.USER_CURRENT); 244 if (intent != null) { 245 // XXX Hack. We need to substitute the icon here but haven't formalized 246 // the public API. The "_google" metadata will be going away, so 247 // DON'T USE IT! 248 ComponentName component = intent.getComponent(); 249 boolean replaced = mGlowPadView.replaceTargetDrawablesIfPresent(component, 250 ASSIST_ICON_METADATA_NAME + "_google", R.drawable.ic_action_assist_generic); 251 252 if (!replaced && !mGlowPadView.replaceTargetDrawablesIfPresent(component, 253 ASSIST_ICON_METADATA_NAME, R.drawable.ic_action_assist_generic)) { 254 Slog.w(TAG, "Couldn't grab icon from package " + component); 255 } 256 } 257 } 258 259 mGlowPadView.setEnableTarget(R.drawable.ic_lockscreen_camera, !mCameraDisabled); 260 mGlowPadView.setEnableTarget(R.drawable.ic_action_assist_generic, !mSearchDisabled); 261 } 262 263 void doTransition(View view, float to) { 264 if (mAnim != null) { 265 mAnim.cancel(); 266 } 267 mAnim = ObjectAnimator.ofFloat(view, "alpha", to); 268 mAnim.start(); 269 } 270 271 public void setKeyguardCallback(KeyguardSecurityCallback callback) { 272 mCallback = callback; 273 } 274 275 public void setLockPatternUtils(LockPatternUtils utils) { 276 mLockPatternUtils = utils; 277 } 278 279 @Override 280 public void reset() { 281 mGlowPadView.reset(false); 282 } 283 284 @Override 285 public boolean needsInput() { 286 return false; 287 } 288 289 @Override 290 public void onPause() { 291 KeyguardUpdateMonitor.getInstance(getContext()).removeCallback(mUpdateCallback); 292 maybeStopHotwordDetector(); 293 } 294 295 @Override 296 public void onResume(int reason) { 297 KeyguardUpdateMonitor.getInstance(getContext()).registerCallback(mUpdateCallback); 298 // TODO: Figure out if there's a better way to do it. 299 // onResume gets called multiple times, however we are interested in 300 // the reason to figure out when to start/stop hotword detection. 301 if (reason == SCREEN_ON) { 302 if (!KeyguardUpdateMonitor.getInstance(getContext()).isSwitchingUser()) { 303 maybeStartHotwordDetector(); 304 } 305 } 306 } 307 308 @Override 309 public KeyguardSecurityCallback getCallback() { 310 return mCallback; 311 } 312 313 @Override 314 public void showBouncer(int duration) { 315 mIsBouncing = true; 316 KeyguardSecurityViewHelper. 317 showBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration); 318 } 319 320 @Override 321 public void hideBouncer(int duration) { 322 mIsBouncing = false; 323 KeyguardSecurityViewHelper. 324 hideBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration); 325 } 326 327 /** 328 * Start the hotword detector if: 329 * <li> FLAG_HOTWORD is true and 330 * <li> Hotword detection is not already running and 331 * <li> TelephonyManager is in CALL_STATE_IDLE 332 * 333 * If this method is called when the screen is off, 334 * it attempts to stop hotword detection if it's running. 335 */ 336 private void maybeStartHotwordDetector() { 337 if (ENABLE_HOTWORD && sHotwordClient != null) { 338 if (DEBUG) Log.d(TAG, "maybeStartHotwordDetector()"); 339 // Don't start it if the screen is off or not showing 340 PowerManager powerManager = (PowerManager) getContext().getSystemService( 341 Context.POWER_SERVICE); 342 if (!powerManager.isScreenOn()) { 343 if (DEBUG) Log.d(TAG, "screen was off, not starting"); 344 return; 345 } 346 347 KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(getContext()); 348 if (monitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE) { 349 if (DEBUG) Log.d(TAG, "Call underway, not starting"); 350 return; 351 } 352 353 try { 354 sHotwordClient.startRecognition(mHotwordCallback); 355 } catch(Exception ex) { 356 // Don't allow hotword errors to make the keyguard unusable 357 Log.e(TAG, "Failed to start hotword recognition", ex); 358 sHotwordClient = null; 359 } 360 } 361 } 362 363 /** 364 * Stop hotword detector if HOTWORDING_ENABLED is true. 365 */ 366 private void maybeStopHotwordDetector() { 367 if (ENABLE_HOTWORD && sHotwordClient != null) { 368 if (DEBUG) Log.d(TAG, "maybeStopHotwordDetector()"); 369 try { 370 sHotwordClient.stopRecognition(); 371 } catch(Exception ex) { 372 // Don't allow hotword errors to make the keyguard unusable 373 Log.e(TAG, "Failed to start hotword recognition", ex); 374 } finally { 375 sHotwordClient = null; 376 } 377 } 378 } 379 380 private final HotwordRecognitionListener mHotwordCallback = new HotwordRecognitionListener() { 381 private static final String TAG = "HotwordRecognitionListener"; 382 383 public void onHotwordRecognitionStarted() { 384 if (DEBUG) Log.d(TAG, "onHotwordRecognitionStarted()"); 385 } 386 387 public void onHotwordRecognitionStopped() { 388 if (DEBUG) Log.d(TAG, "onHotwordRecognitionStopped()"); 389 } 390 391 public void onHotwordEvent(int eventType, Bundle eventBundle) { 392 if (DEBUG) Log.d(TAG, "onHotwordEvent: " + eventType); 393 if (eventType == HotwordRecognizer.EVENT_TYPE_STATE_CHANGED) { 394 if (eventBundle != null && eventBundle.containsKey(HotwordRecognizer.PROMPT_TEXT)) { 395 mSecurityMessageDisplay.setMessage( 396 eventBundle.getString(HotwordRecognizer.PROMPT_TEXT), true); 397 } 398 } 399 } 400 401 public void onHotwordRecognized(PendingIntent intent) { 402 if (DEBUG) Log.d(TAG, "onHotwordRecognized"); 403 maybeStopHotwordDetector(); 404 if (intent != null) { 405 try { 406 intent.send(); 407 } catch (PendingIntent.CanceledException e) { 408 Log.w(TAG, "Failed to launch PendingIntent. Encountered CanceledException"); 409 } 410 } 411 mCallback.userActivity(0); 412 mCallback.dismiss(false); 413 } 414 415 public void onHotwordError(int errorCode) { 416 if (DEBUG) Log.d(TAG, "onHotwordError: " + errorCode); 417 // TODO: Inspect the error code and handle the errors appropriately 418 // instead of blindly failing. 419 maybeStopHotwordDetector(); 420 } 421 }; 422} 423