1/* 2 * Copyright (C) 2008 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.internal.policy.impl.keyguard_obsolete; 18 19import com.android.internal.R; 20import com.android.internal.telephony.IccCardConstants; 21import com.android.internal.widget.LockPatternUtils; 22import com.android.internal.widget.SlidingTab; 23import com.android.internal.widget.WaveView; 24import com.android.internal.widget.multiwaveview.GlowPadView; 25 26import android.app.ActivityManager; 27import android.app.ActivityManagerNative; 28import android.app.SearchManager; 29import android.content.ActivityNotFoundException; 30import android.content.ComponentName; 31import android.content.Context; 32import android.content.Intent; 33import android.content.res.Configuration; 34import android.content.res.Resources; 35import android.os.UserHandle; 36import android.os.Vibrator; 37import android.view.KeyEvent; 38import android.view.LayoutInflater; 39import android.view.View; 40import android.view.ViewGroup; 41import android.widget.*; 42import android.util.Log; 43import android.util.Slog; 44import android.media.AudioManager; 45import android.os.RemoteException; 46import android.provider.MediaStore; 47 48import java.io.File; 49 50/** 51 * The screen within {@link LockPatternKeyguardView} that shows general 52 * information about the device depending on its state, and how to get 53 * past it, as applicable. 54 */ 55class LockScreen extends LinearLayout implements KeyguardScreen { 56 57 private static final int ON_RESUME_PING_DELAY = 500; // delay first ping until the screen is on 58 private static final boolean DBG = false; 59 private static final String TAG = "LockScreen"; 60 private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; 61 private static final int WAIT_FOR_ANIMATION_TIMEOUT = 0; 62 private static final int STAY_ON_WHILE_GRABBED_TIMEOUT = 30000; 63 private static final String ASSIST_ICON_METADATA_NAME = 64 "com.android.systemui.action_assist_icon"; 65 66 private LockPatternUtils mLockPatternUtils; 67 private KeyguardUpdateMonitor mUpdateMonitor; 68 private KeyguardScreenCallback mCallback; 69 70 // set to 'true' to show the ring/silence target when camera isn't available 71 private boolean mEnableRingSilenceFallback = false; 72 73 // current configuration state of keyboard and display 74 private int mCreationOrientation; 75 76 private boolean mSilentMode; 77 private AudioManager mAudioManager; 78 private boolean mEnableMenuKeyInLockScreen; 79 80 private KeyguardStatusViewManager mStatusViewManager; 81 private UnlockWidgetCommonMethods mUnlockWidgetMethods; 82 private View mUnlockWidget; 83 private boolean mCameraDisabled; 84 private boolean mSearchDisabled; 85 // Is there a vibrator 86 private final boolean mHasVibrator; 87 88 KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { 89 90 @Override 91 public void onRingerModeChanged(int state) { 92 boolean silent = AudioManager.RINGER_MODE_NORMAL != state; 93 if (silent != mSilentMode) { 94 mSilentMode = silent; 95 mUnlockWidgetMethods.updateResources(); 96 } 97 } 98 99 @Override 100 public void onDevicePolicyManagerStateChanged() { 101 updateTargets(); 102 } 103 104 @Override 105 public void onSimStateChanged(IccCardConstants.State simState) { 106 updateTargets(); 107 } 108 }; 109 110 private interface UnlockWidgetCommonMethods { 111 // Update resources based on phone state 112 public void updateResources(); 113 114 // Get the view associated with this widget 115 public View getView(); 116 117 // Reset the view 118 public void reset(boolean animate); 119 120 // Animate the widget if it supports ping() 121 public void ping(); 122 123 // Enable or disable a target. ResourceId is the id of the *drawable* associated with the 124 // target. 125 public void setEnabled(int resourceId, boolean enabled); 126 127 // Get the target position for the given resource. Returns -1 if not found. 128 public int getTargetPosition(int resourceId); 129 130 // Clean up when this widget is going away 131 public void cleanUp(); 132 } 133 134 class SlidingTabMethods implements SlidingTab.OnTriggerListener, UnlockWidgetCommonMethods { 135 private final SlidingTab mSlidingTab; 136 137 SlidingTabMethods(SlidingTab slidingTab) { 138 mSlidingTab = slidingTab; 139 } 140 141 public void updateResources() { 142 boolean vibe = mSilentMode 143 && (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE); 144 145 mSlidingTab.setRightTabResources( 146 mSilentMode ? ( vibe ? R.drawable.ic_jog_dial_vibrate_on 147 : R.drawable.ic_jog_dial_sound_off ) 148 : R.drawable.ic_jog_dial_sound_on, 149 mSilentMode ? R.drawable.jog_tab_target_yellow 150 : R.drawable.jog_tab_target_gray, 151 mSilentMode ? R.drawable.jog_tab_bar_right_sound_on 152 : R.drawable.jog_tab_bar_right_sound_off, 153 mSilentMode ? R.drawable.jog_tab_right_sound_on 154 : R.drawable.jog_tab_right_sound_off); 155 } 156 157 /** {@inheritDoc} */ 158 public void onTrigger(View v, int whichHandle) { 159 if (whichHandle == SlidingTab.OnTriggerListener.LEFT_HANDLE) { 160 mCallback.goToUnlockScreen(); 161 } else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) { 162 toggleRingMode(); 163 mCallback.pokeWakelock(); 164 } 165 } 166 167 /** {@inheritDoc} */ 168 public void onGrabbedStateChange(View v, int grabbedState) { 169 if (grabbedState == SlidingTab.OnTriggerListener.RIGHT_HANDLE) { 170 mSilentMode = isSilentMode(); 171 mSlidingTab.setRightHintText(mSilentMode ? R.string.lockscreen_sound_on_label 172 : R.string.lockscreen_sound_off_label); 173 } 174 // Don't poke the wake lock when returning to a state where the handle is 175 // not grabbed since that can happen when the system (instead of the user) 176 // cancels the grab. 177 if (grabbedState != SlidingTab.OnTriggerListener.NO_HANDLE) { 178 mCallback.pokeWakelock(); 179 } 180 } 181 182 public View getView() { 183 return mSlidingTab; 184 } 185 186 public void reset(boolean animate) { 187 mSlidingTab.reset(animate); 188 } 189 190 public void ping() { 191 } 192 193 public void setEnabled(int resourceId, boolean enabled) { 194 // Not used 195 } 196 197 public int getTargetPosition(int resourceId) { 198 return -1; // Not supported 199 } 200 201 public void cleanUp() { 202 mSlidingTab.setOnTriggerListener(null); 203 } 204 } 205 206 class WaveViewMethods implements WaveView.OnTriggerListener, UnlockWidgetCommonMethods { 207 208 private final WaveView mWaveView; 209 210 WaveViewMethods(WaveView waveView) { 211 mWaveView = waveView; 212 } 213 /** {@inheritDoc} */ 214 public void onTrigger(View v, int whichHandle) { 215 if (whichHandle == WaveView.OnTriggerListener.CENTER_HANDLE) { 216 requestUnlockScreen(); 217 } 218 } 219 220 /** {@inheritDoc} */ 221 public void onGrabbedStateChange(View v, int grabbedState) { 222 // Don't poke the wake lock when returning to a state where the handle is 223 // not grabbed since that can happen when the system (instead of the user) 224 // cancels the grab. 225 if (grabbedState == WaveView.OnTriggerListener.CENTER_HANDLE) { 226 mCallback.pokeWakelock(STAY_ON_WHILE_GRABBED_TIMEOUT); 227 } 228 } 229 230 public void updateResources() { 231 } 232 233 public View getView() { 234 return mWaveView; 235 } 236 public void reset(boolean animate) { 237 mWaveView.reset(); 238 } 239 public void ping() { 240 } 241 public void setEnabled(int resourceId, boolean enabled) { 242 // Not used 243 } 244 public int getTargetPosition(int resourceId) { 245 return -1; // Not supported 246 } 247 public void cleanUp() { 248 mWaveView.setOnTriggerListener(null); 249 } 250 } 251 252 class GlowPadViewMethods implements GlowPadView.OnTriggerListener, 253 UnlockWidgetCommonMethods { 254 private final GlowPadView mGlowPadView; 255 256 GlowPadViewMethods(GlowPadView glowPadView) { 257 mGlowPadView = glowPadView; 258 } 259 260 public boolean isTargetPresent(int resId) { 261 return mGlowPadView.getTargetPosition(resId) != -1; 262 } 263 264 public void updateResources() { 265 int resId; 266 if (mCameraDisabled && mEnableRingSilenceFallback) { 267 // Fall back to showing ring/silence if camera is disabled... 268 resId = mSilentMode ? R.array.lockscreen_targets_when_silent 269 : R.array.lockscreen_targets_when_soundon; 270 } else { 271 resId = R.array.lockscreen_targets_with_camera; 272 } 273 if (mGlowPadView.getTargetResourceId() != resId) { 274 mGlowPadView.setTargetResources(resId); 275 } 276 277 // Update the search icon with drawable from the search .apk 278 if (!mSearchDisabled) { 279 Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) 280 .getAssistIntent(mContext, UserHandle.USER_CURRENT); 281 if (intent != null) { 282 // XXX Hack. We need to substitute the icon here but haven't formalized 283 // the public API. The "_google" metadata will be going away, so 284 // DON'T USE IT! 285 ComponentName component = intent.getComponent(); 286 boolean replaced = mGlowPadView.replaceTargetDrawablesIfPresent(component, 287 ASSIST_ICON_METADATA_NAME + "_google", 288 com.android.internal.R.drawable.ic_action_assist_generic); 289 290 if (!replaced && !mGlowPadView.replaceTargetDrawablesIfPresent(component, 291 ASSIST_ICON_METADATA_NAME, 292 com.android.internal.R.drawable.ic_action_assist_generic)) { 293 Slog.w(TAG, "Couldn't grab icon from package " + component); 294 } 295 } 296 } 297 298 setEnabled(com.android.internal.R.drawable.ic_lockscreen_camera, !mCameraDisabled); 299 setEnabled(com.android.internal.R.drawable.ic_action_assist_generic, !mSearchDisabled); 300 } 301 302 public void onGrabbed(View v, int handle) { 303 304 } 305 306 public void onReleased(View v, int handle) { 307 308 } 309 310 public void onTrigger(View v, int target) { 311 final int resId = mGlowPadView.getResourceIdForTarget(target); 312 switch (resId) { 313 case com.android.internal.R.drawable.ic_action_assist_generic: 314 Intent assistIntent = 315 ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) 316 .getAssistIntent(mContext, UserHandle.USER_CURRENT); 317 if (assistIntent != null) { 318 launchActivity(assistIntent); 319 } else { 320 Log.w(TAG, "Failed to get intent for assist activity"); 321 } 322 mCallback.pokeWakelock(); 323 break; 324 325 case com.android.internal.R.drawable.ic_lockscreen_camera: 326 launchActivity(new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)); 327 mCallback.pokeWakelock(); 328 break; 329 330 case com.android.internal.R.drawable.ic_lockscreen_silent: 331 toggleRingMode(); 332 mCallback.pokeWakelock(); 333 break; 334 335 case com.android.internal.R.drawable.ic_lockscreen_unlock_phantom: 336 case com.android.internal.R.drawable.ic_lockscreen_unlock: 337 mCallback.goToUnlockScreen(); 338 break; 339 } 340 } 341 342 /** 343 * Launches the said intent for the current foreground user. 344 * @param intent 345 */ 346 private void launchActivity(Intent intent) { 347 intent.setFlags( 348 Intent.FLAG_ACTIVITY_NEW_TASK 349 | Intent.FLAG_ACTIVITY_SINGLE_TOP 350 | Intent.FLAG_ACTIVITY_CLEAR_TOP); 351 try { 352 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); 353 } catch (RemoteException e) { 354 Log.w(TAG, "can't dismiss keyguard on launch"); 355 } 356 try { 357 mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); 358 } catch (ActivityNotFoundException e) { 359 Log.w(TAG, "Activity not found for intent + " + intent.getAction()); 360 } 361 } 362 363 public void onGrabbedStateChange(View v, int handle) { 364 // Don't poke the wake lock when returning to a state where the handle is 365 // not grabbed since that can happen when the system (instead of the user) 366 // cancels the grab. 367 if (handle != GlowPadView.OnTriggerListener.NO_HANDLE) { 368 mCallback.pokeWakelock(); 369 } 370 } 371 372 public View getView() { 373 return mGlowPadView; 374 } 375 376 public void reset(boolean animate) { 377 mGlowPadView.reset(animate); 378 } 379 380 public void ping() { 381 mGlowPadView.ping(); 382 } 383 384 public void setEnabled(int resourceId, boolean enabled) { 385 mGlowPadView.setEnableTarget(resourceId, enabled); 386 } 387 388 public int getTargetPosition(int resourceId) { 389 return mGlowPadView.getTargetPosition(resourceId); 390 } 391 392 public void cleanUp() { 393 mGlowPadView.setOnTriggerListener(null); 394 } 395 396 public void onFinishFinalAnimation() { 397 398 } 399 } 400 401 private void requestUnlockScreen() { 402 // Delay hiding lock screen long enough for animation to finish 403 postDelayed(new Runnable() { 404 public void run() { 405 mCallback.goToUnlockScreen(); 406 } 407 }, WAIT_FOR_ANIMATION_TIMEOUT); 408 } 409 410 private void toggleRingMode() { 411 // toggle silent mode 412 mSilentMode = !mSilentMode; 413 if (mSilentMode) { 414 mAudioManager.setRingerMode(mHasVibrator 415 ? AudioManager.RINGER_MODE_VIBRATE 416 : AudioManager.RINGER_MODE_SILENT); 417 } else { 418 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL); 419 } 420 } 421 422 /** 423 * In general, we enable unlocking the insecure key guard with the menu key. However, there are 424 * some cases where we wish to disable it, notably when the menu button placement or technology 425 * is prone to false positives. 426 * 427 * @return true if the menu key should be enabled 428 */ 429 private boolean shouldEnableMenuKey() { 430 final Resources res = getResources(); 431 final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen); 432 final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); 433 final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); 434 return !configDisabled || isTestHarness || fileOverride; 435 } 436 437 /** 438 * @param context Used to setup the view. 439 * @param configuration The current configuration. Used to use when selecting layout, etc. 440 * @param lockPatternUtils Used to know the state of the lock pattern settings. 441 * @param updateMonitor Used to register for updates on various keyguard related 442 * state, and query the initial state at setup. 443 * @param callback Used to communicate back to the host keyguard view. 444 */ 445 LockScreen(Context context, Configuration configuration, LockPatternUtils lockPatternUtils, 446 KeyguardUpdateMonitor updateMonitor, 447 KeyguardScreenCallback callback) { 448 super(context); 449 mLockPatternUtils = lockPatternUtils; 450 mUpdateMonitor = updateMonitor; 451 mCallback = callback; 452 mEnableMenuKeyInLockScreen = shouldEnableMenuKey(); 453 mCreationOrientation = configuration.orientation; 454 455 if (LockPatternKeyguardView.DEBUG_CONFIGURATION) { 456 Log.v(TAG, "***** CREATING LOCK SCREEN", new RuntimeException()); 457 Log.v(TAG, "Cur orient=" + mCreationOrientation 458 + " res orient=" + context.getResources().getConfiguration().orientation); 459 } 460 461 final LayoutInflater inflater = LayoutInflater.from(context); 462 if (DBG) Log.v(TAG, "Creation orientation = " + mCreationOrientation); 463 if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) { 464 inflater.inflate(R.layout.keyguard_screen_tab_unlock, this, true); 465 } else { 466 inflater.inflate(R.layout.keyguard_screen_tab_unlock_land, this, true); 467 } 468 469 mStatusViewManager = new KeyguardStatusViewManager(this, mUpdateMonitor, mLockPatternUtils, 470 mCallback, false); 471 472 setFocusable(true); 473 setFocusableInTouchMode(true); 474 setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); 475 476 Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); 477 mHasVibrator = vibrator == null ? false : vibrator.hasVibrator(); 478 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 479 mSilentMode = isSilentMode(); 480 mUnlockWidget = findViewById(R.id.unlock_widget); 481 mUnlockWidgetMethods = createUnlockMethods(mUnlockWidget); 482 483 if (DBG) Log.v(TAG, "*** LockScreen accel is " 484 + (mUnlockWidget.isHardwareAccelerated() ? "on":"off")); 485 } 486 487 private UnlockWidgetCommonMethods createUnlockMethods(View unlockWidget) { 488 if (unlockWidget instanceof SlidingTab) { 489 SlidingTab slidingTabView = (SlidingTab) unlockWidget; 490 slidingTabView.setHoldAfterTrigger(true, false); 491 slidingTabView.setLeftHintText(R.string.lockscreen_unlock_label); 492 slidingTabView.setLeftTabResources( 493 R.drawable.ic_jog_dial_unlock, 494 R.drawable.jog_tab_target_green, 495 R.drawable.jog_tab_bar_left_unlock, 496 R.drawable.jog_tab_left_unlock); 497 SlidingTabMethods slidingTabMethods = new SlidingTabMethods(slidingTabView); 498 slidingTabView.setOnTriggerListener(slidingTabMethods); 499 return slidingTabMethods; 500 } else if (unlockWidget instanceof WaveView) { 501 WaveView waveView = (WaveView) unlockWidget; 502 WaveViewMethods waveViewMethods = new WaveViewMethods(waveView); 503 waveView.setOnTriggerListener(waveViewMethods); 504 return waveViewMethods; 505 } else if (unlockWidget instanceof GlowPadView) { 506 GlowPadView glowPadView = (GlowPadView) unlockWidget; 507 GlowPadViewMethods glowPadViewMethods = new GlowPadViewMethods(glowPadView); 508 glowPadView.setOnTriggerListener(glowPadViewMethods); 509 return glowPadViewMethods; 510 } else { 511 throw new IllegalStateException("Unrecognized unlock widget: " + unlockWidget); 512 } 513 } 514 515 private void updateTargets() { 516 boolean disabledByAdmin = mLockPatternUtils.getDevicePolicyManager() 517 .getCameraDisabled(null); 518 boolean disabledBySimState = mUpdateMonitor.isSimLocked(); 519 boolean cameraTargetPresent = (mUnlockWidgetMethods instanceof GlowPadViewMethods) 520 ? ((GlowPadViewMethods) mUnlockWidgetMethods) 521 .isTargetPresent(com.android.internal.R.drawable.ic_lockscreen_camera) 522 : false; 523 boolean searchTargetPresent = (mUnlockWidgetMethods instanceof GlowPadViewMethods) 524 ? ((GlowPadViewMethods) mUnlockWidgetMethods) 525 .isTargetPresent(com.android.internal.R.drawable.ic_action_assist_generic) 526 : false; 527 528 if (disabledByAdmin) { 529 Log.v(TAG, "Camera disabled by Device Policy"); 530 } else if (disabledBySimState) { 531 Log.v(TAG, "Camera disabled by Sim State"); 532 } 533 boolean searchActionAvailable = 534 ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) 535 .getAssistIntent(mContext, UserHandle.USER_CURRENT) != null; 536 mCameraDisabled = disabledByAdmin || disabledBySimState || !cameraTargetPresent; 537 mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent; 538 mUnlockWidgetMethods.updateResources(); 539 } 540 541 private boolean isSilentMode() { 542 return mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL; 543 } 544 545 @Override 546 public boolean onKeyDown(int keyCode, KeyEvent event) { 547 if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKeyInLockScreen) { 548 mCallback.goToUnlockScreen(); 549 } 550 return false; 551 } 552 553 void updateConfiguration() { 554 Configuration newConfig = getResources().getConfiguration(); 555 if (newConfig.orientation != mCreationOrientation) { 556 mCallback.recreateMe(newConfig); 557 } 558 } 559 560 @Override 561 protected void onAttachedToWindow() { 562 super.onAttachedToWindow(); 563 if (LockPatternKeyguardView.DEBUG_CONFIGURATION) { 564 Log.v(TAG, "***** LOCK ATTACHED TO WINDOW"); 565 Log.v(TAG, "Cur orient=" + mCreationOrientation 566 + ", new config=" + getResources().getConfiguration()); 567 } 568 updateConfiguration(); 569 } 570 571 /** {@inheritDoc} */ 572 @Override 573 protected void onConfigurationChanged(Configuration newConfig) { 574 super.onConfigurationChanged(newConfig); 575 if (LockPatternKeyguardView.DEBUG_CONFIGURATION) { 576 Log.w(TAG, "***** LOCK CONFIG CHANGING", new RuntimeException()); 577 Log.v(TAG, "Cur orient=" + mCreationOrientation 578 + ", new config=" + newConfig); 579 } 580 updateConfiguration(); 581 } 582 583 /** {@inheritDoc} */ 584 public boolean needsInput() { 585 return false; 586 } 587 588 /** {@inheritDoc} */ 589 public void onPause() { 590 mUpdateMonitor.removeCallback(mInfoCallback); 591 mStatusViewManager.onPause(); 592 mUnlockWidgetMethods.reset(false); 593 } 594 595 private final Runnable mOnResumePing = new Runnable() { 596 public void run() { 597 mUnlockWidgetMethods.ping(); 598 } 599 }; 600 601 /** {@inheritDoc} */ 602 public void onResume() { 603 // We don't want to show the camera target if SIM state prevents us from 604 // launching the camera. So watch for SIM changes... 605 mUpdateMonitor.registerCallback(mInfoCallback); 606 607 mStatusViewManager.onResume(); 608 postDelayed(mOnResumePing, ON_RESUME_PING_DELAY); 609 } 610 611 /** {@inheritDoc} */ 612 public void cleanUp() { 613 mUpdateMonitor.removeCallback(mInfoCallback); // this must be first 614 mUnlockWidgetMethods.cleanUp(); 615 mLockPatternUtils = null; 616 mUpdateMonitor = null; 617 mCallback = null; 618 } 619} 620