KeyguardHostView.java revision d6f8960b68c14e7946ff8f8286c484c28296ae39
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
17package com.android.internal.policy.impl.keyguard;
18
19import android.app.Activity;
20import android.app.ActivityManager;
21import android.app.ActivityOptions;
22import android.app.AlertDialog;
23import android.app.admin.DevicePolicyManager;
24import android.appwidget.AppWidgetHost;
25import android.appwidget.AppWidgetHostView;
26import android.appwidget.AppWidgetManager;
27import android.appwidget.AppWidgetProviderInfo;
28import android.content.ComponentName;
29import android.content.Context;
30import android.content.Intent;
31import android.content.IntentSender;
32import android.content.pm.UserInfo;
33import android.content.res.Resources;
34import android.graphics.Canvas;
35import android.graphics.Rect;
36import android.os.Bundle;
37import android.os.Looper;
38import android.os.Parcel;
39import android.os.Parcelable;
40import android.os.SystemClock;
41import android.os.UserHandle;
42import android.os.UserManager;
43import android.util.AttributeSet;
44import android.util.Log;
45import android.util.Slog;
46import android.view.KeyEvent;
47import android.view.LayoutInflater;
48import android.view.MotionEvent;
49import android.view.View;
50import android.view.WindowManager;
51import android.view.animation.AnimationUtils;
52import android.widget.RemoteViews.OnClickHandler;
53
54import com.android.internal.R;
55import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
56import com.android.internal.widget.LockPatternUtils;
57
58import java.io.File;
59import java.util.List;
60
61public class KeyguardHostView extends KeyguardViewBase {
62    private static final String TAG = "KeyguardViewHost";
63
64    // Use this to debug all of keyguard
65    public static boolean DEBUG = KeyguardViewMediator.DEBUG;
66
67    static final int APPWIDGET_HOST_ID = 0x4B455947;
68
69    private AppWidgetHost mAppWidgetHost;
70    private AppWidgetManager mAppWidgetManager;
71    private KeyguardWidgetPager mAppWidgetContainer;
72    private KeyguardSecurityViewFlipper mSecurityViewContainer;
73    private KeyguardSelectorView mKeyguardSelectorView;
74    private KeyguardTransportControlView mTransportControl;
75    private boolean mIsVerifyUnlockOnly;
76    private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
77    private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
78
79    protected Runnable mLaunchRunnable;
80
81    protected int mFailedAttempts;
82    private LockPatternUtils mLockPatternUtils;
83
84    private KeyguardSecurityModel mSecurityModel;
85    private KeyguardViewStateManager mViewStateManager;
86
87    int mLocalStickyWidget = -1;
88    boolean mPersitentStickyWidgetLoaded = false;
89
90    private Rect mTempRect = new Rect();
91
92    /*package*/ interface TransportCallback {
93        void onListenerDetached();
94        void onListenerAttached();
95        void onPlayStateChanged();
96    }
97
98    /*package*/ interface UserSwitcherCallback {
99        void hideSecurityView(int duration);
100        void showSecurityView();
101        void showUnlockHint();
102        void userActivity();
103    }
104
105    public KeyguardHostView(Context context) {
106        this(context, null);
107    }
108
109    public KeyguardHostView(Context context, AttributeSet attrs) {
110        super(context, attrs);
111        mLockPatternUtils = new LockPatternUtils(context);
112        mAppWidgetHost = new AppWidgetHost(
113                context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper());
114        mAppWidgetManager = AppWidgetManager.getInstance(mContext);
115        mSecurityModel = new KeyguardSecurityModel(context);
116
117        mViewStateManager = new KeyguardViewStateManager(this);
118    }
119
120    @Override
121    public boolean onTouchEvent(MotionEvent ev) {
122        boolean result = super.onTouchEvent(ev);
123        mTempRect.set(0, 0, 0, 0);
124        offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect);
125        ev.offsetLocation(mTempRect.left, mTempRect.top);
126        result = mSecurityViewContainer.dispatchTouchEvent(ev) || result;
127        ev.offsetLocation(-mTempRect.left, -mTempRect.top);
128        return result;
129    }
130
131    @Override
132    protected void dispatchDraw(Canvas canvas) {
133        super.dispatchDraw(canvas);
134        if (mViewMediatorCallback != null) {
135            mViewMediatorCallback.keyguardDoneDrawing();
136        }
137    }
138
139    private int getWidgetPosition(int id) {
140        final int children = mAppWidgetContainer.getChildCount();
141        for (int i = 0; i < children; i++) {
142            if (mAppWidgetContainer.getWidgetPageAt(i).getContent().getId() == id) {
143                return i;
144            }
145        }
146        return -1;
147    }
148
149    @Override
150    protected void onFinishInflate() {
151        // Grab instances of and make any necessary changes to the main layouts. Create
152        // view state manager and wire up necessary listeners / callbacks.
153        View deleteDropTarget = findViewById(R.id.keyguard_widget_pager_delete_target);
154        mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
155        mAppWidgetContainer.setVisibility(VISIBLE);
156        mAppWidgetContainer.setCallbacks(mWidgetCallbacks);
157        mAppWidgetContainer.setDeleteDropTarget(deleteDropTarget);
158        mAppWidgetContainer.setMinScale(0.5f);
159
160        SlidingChallengeLayout slider =
161                (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
162        if (slider != null) {
163            slider.setOnChallengeScrolledListener(mViewStateManager);
164        }
165        mAppWidgetContainer.setViewStateManager(mViewStateManager);
166        mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils);
167
168        mViewStateManager.setPagedView(mAppWidgetContainer);
169        mViewStateManager.setChallengeLayout(slider != null ? slider :
170                (ChallengeLayout) findViewById(R.id.multi_pane_challenge));
171        mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
172        mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
173        mViewStateManager.setSecurityViewContainer(mSecurityViewContainer);
174
175        if (!(mContext instanceof Activity)) {
176            setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
177        }
178
179        addDefaultWidgets();
180        addWidgetsFromSettings();
181        mSwitchPageRunnable.run();
182
183        // This needs to be called after the pages are all added.
184        mViewStateManager.showUsabilityHints();
185
186        showPrimarySecurityScreen(false);
187        updateSecurityViews();
188    }
189
190    private void updateSecurityViews() {
191        int children = mSecurityViewContainer.getChildCount();
192        for (int i = 0; i < children; i++) {
193            updateSecurityView(mSecurityViewContainer.getChildAt(i));
194        }
195    }
196
197    private void updateSecurityView(View view) {
198        if (view instanceof KeyguardSecurityView) {
199            KeyguardSecurityView ksv = (KeyguardSecurityView) view;
200            ksv.setKeyguardCallback(mCallback);
201            ksv.setLockPatternUtils(mLockPatternUtils);
202            if (mViewStateManager.isBouncing()) {
203                ksv.showBouncer(0);
204            } else {
205                ksv.hideBouncer(0);
206            }
207        } else {
208            Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
209        }
210    }
211
212    void setLockPatternUtils(LockPatternUtils utils) {
213        mSecurityModel.setLockPatternUtils(utils);
214        mLockPatternUtils = utils;
215        updateSecurityViews();
216    }
217
218    @Override
219    protected void onAttachedToWindow() {
220        super.onAttachedToWindow();
221        mAppWidgetHost.startListening();
222    }
223
224    @Override
225    protected void onDetachedFromWindow() {
226        super.onDetachedFromWindow();
227        mAppWidgetHost.stopListening();
228    }
229
230    private AppWidgetHost getAppWidgetHost() {
231        return mAppWidgetHost;
232    }
233
234    void addWidget(AppWidgetHostView view, int pageIndex) {
235        mAppWidgetContainer.addWidget(view, pageIndex);
236    }
237
238    private KeyguardWidgetPager.Callbacks mWidgetCallbacks
239            = new KeyguardWidgetPager.Callbacks() {
240        @Override
241        public void userActivity() {
242            if (mViewMediatorCallback != null) {
243                mViewMediatorCallback.userActivity();
244            }
245        }
246
247        @Override
248        public void onUserActivityTimeoutChanged() {
249            if (mViewMediatorCallback != null) {
250                mViewMediatorCallback.onUserActivityTimeoutChanged();
251            }
252        }
253    };
254
255    @Override
256    public long getUserActivityTimeout() {
257        // Currently only considering user activity timeouts needed by widgets.
258        // Could also take into account longer timeouts for certain security views.
259        if (mAppWidgetContainer != null) {
260            return mAppWidgetContainer.getUserActivityTimeout();
261        }
262        return -1;
263    }
264
265    private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
266
267        public void userActivity(long timeout) {
268            if (mViewMediatorCallback != null) {
269                mViewMediatorCallback.userActivity(timeout);
270            }
271        }
272
273        public void dismiss(boolean authenticated) {
274            showNextSecurityScreenOrFinish(authenticated);
275        }
276
277        public boolean isVerifyUnlockOnly() {
278            return mIsVerifyUnlockOnly;
279        }
280
281        public void reportSuccessfulUnlockAttempt() {
282            KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts();
283            mLockPatternUtils.reportSuccessfulPasswordAttempt();
284        }
285
286        public void reportFailedUnlockAttempt() {
287            if (mCurrentSecuritySelection == SecurityMode.Biometric) {
288                KeyguardUpdateMonitor.getInstance(mContext).reportFailedBiometricUnlockAttempt();
289            } else {
290                KeyguardHostView.this.reportFailedUnlockAttempt();
291            }
292        }
293
294        public int getFailedAttempts() {
295            return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
296        }
297
298        @Override
299        public void showBackupSecurity() {
300            KeyguardHostView.this.showBackupSecurityScreen();
301        }
302
303        @Override
304        public void setOnDismissRunnable(Runnable runnable) {
305            KeyguardHostView.this.setOnDismissRunnable(runnable);
306        }
307
308    };
309
310    private void showDialog(String title, String message) {
311        final AlertDialog dialog = new AlertDialog.Builder(mContext)
312            .setTitle(title)
313            .setMessage(message)
314            .setNeutralButton(com.android.internal.R.string.ok, null)
315            .create();
316        if (!(mContext instanceof Activity)) {
317            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
318        }
319        dialog.show();
320    }
321
322    private void showTimeoutDialog() {
323        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
324        int messageId = 0;
325
326        switch (mSecurityModel.getSecurityMode()) {
327            case Pattern:
328                messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
329                break;
330            case PIN:
331                messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
332                break;
333            case Password:
334                messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
335                break;
336        }
337
338        if (messageId != 0) {
339            final String message = mContext.getString(messageId,
340                    KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
341                    timeoutInSeconds);
342            showDialog(null, message);
343        }
344    }
345
346    private void showAlmostAtWipeDialog(int attempts, int remaining) {
347        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
348        String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
349                attempts, remaining);
350        showDialog(null, message);
351    }
352
353    private void showWipeDialog(int attempts) {
354        String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts);
355        showDialog(null, message);
356    }
357
358    private void showAlmostAtAccountLoginDialog() {
359        final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
360        final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
361                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
362        String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
363                count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
364        showDialog(null, message);
365    }
366
367    private void reportFailedUnlockAttempt() {
368        final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
369        final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
370
371        if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
372
373        SecurityMode mode = mSecurityModel.getSecurityMode();
374        final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
375
376        final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
377                .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser());
378
379        final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
380                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
381
382        final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
383                (failedAttemptsBeforeWipe - failedAttempts)
384                : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
385
386        boolean showTimeout = false;
387        if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
388            // If we reach this code, it means the user has installed a DevicePolicyManager
389            // that requests device wipe after N attempts.  Once we get below the grace
390            // period, we'll post this dialog every time as a clear warning until the
391            // bombshell hits and the device is wiped.
392            if (remainingBeforeWipe > 0) {
393                showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
394            } else {
395                // Too many attempts. The device will be wiped shortly.
396                Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
397                showWipeDialog(failedAttempts);
398            }
399        } else {
400            showTimeout =
401                (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
402            if (usingPattern && mEnableFallback) {
403                if (failedAttempts == failedAttemptWarning) {
404                    showAlmostAtAccountLoginDialog();
405                    showTimeout = false; // don't show both dialogs
406                } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
407                    mLockPatternUtils.setPermanentlyLocked(true);
408                    showSecurityScreen(SecurityMode.Account);
409                    // don't show timeout dialog because we show account unlock screen next
410                    showTimeout = false;
411                }
412            }
413        }
414        monitor.reportFailedUnlockAttempt();
415        mLockPatternUtils.reportFailedPasswordAttempt();
416        if (showTimeout) {
417            showTimeoutDialog();
418        }
419    }
420
421    /**
422     * Shows the primary security screen for the user. This will be either the multi-selector
423     * or the user's security method.
424     * @param turningOff true if the device is being turned off
425     */
426    void showPrimarySecurityScreen(boolean turningOff) {
427        SecurityMode securityMode = mSecurityModel.getSecurityMode();
428        if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
429        if (!turningOff &&
430                KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
431            // If we're not turning off, then allow biometric alternate.
432            // We'll reload it when the device comes back on.
433            securityMode = mSecurityModel.getAlternateFor(securityMode);
434        }
435        showSecurityScreen(securityMode);
436    }
437
438    /**
439     * Shows the backup security screen for the current security mode.  This could be used for
440     * password recovery screens but is currently only used for pattern unlock to show the
441     * account unlock screen and biometric unlock to show the user's normal unlock.
442     */
443    private void showBackupSecurityScreen() {
444        if (DEBUG) Log.d(TAG, "showBackupSecurity()");
445        SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection);
446        showSecurityScreen(backup);
447    }
448
449    public boolean showNextSecurityScreenIfPresent() {
450        SecurityMode securityMode = mSecurityModel.getSecurityMode();
451        // Allow an alternate, such as biometric unlock
452        securityMode = mSecurityModel.getAlternateFor(securityMode);
453        if (SecurityMode.None == securityMode) {
454            return false;
455        } else {
456            showSecurityScreen(securityMode); // switch to the alternate security view
457            return true;
458        }
459    }
460
461    private void showNextSecurityScreenOrFinish(boolean authenticated) {
462        if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
463        boolean finish = false;
464        if (SecurityMode.None == mCurrentSecuritySelection) {
465            SecurityMode securityMode = mSecurityModel.getSecurityMode();
466            // Allow an alternate, such as biometric unlock
467            securityMode = mSecurityModel.getAlternateFor(securityMode);
468            if (SecurityMode.None == securityMode) {
469                finish = true; // no security required
470            } else {
471                showSecurityScreen(securityMode); // switch to the alternate security view
472            }
473        } else if (authenticated) {
474            switch (mCurrentSecuritySelection) {
475                case Pattern:
476                case Password:
477                case PIN:
478                case Account:
479                case Biometric:
480                    finish = true;
481                    break;
482
483                case SimPin:
484                case SimPuk:
485                    // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
486                    SecurityMode securityMode = mSecurityModel.getSecurityMode();
487                    if (securityMode != SecurityMode.None) {
488                        showSecurityScreen(securityMode);
489                    } else {
490                        finish = true;
491                    }
492                    break;
493
494                default:
495                    Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
496                    showPrimarySecurityScreen(false);
497                    break;
498            }
499        } else {
500            showPrimarySecurityScreen(false);
501        }
502        if (finish) {
503            // If the alternate unlock was suppressed, it can now be safely
504            // enabled because the user has left keyguard.
505            KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
506
507            // If there's a pending runnable because the user interacted with a widget
508            // and we're leaving keyguard, then run it.
509            if (mLaunchRunnable != null) {
510                mLaunchRunnable.run();
511                mLaunchRunnable = null;
512            }
513            if (mViewMediatorCallback != null) {
514                mViewMediatorCallback.keyguardDone(true);
515            }
516        } else {
517            mViewStateManager.showBouncer(true);
518        }
519    }
520
521    private OnClickHandler mOnClickHandler = new OnClickHandler() {
522        @Override
523        public boolean onClickHandler(final View view,
524                final android.app.PendingIntent pendingIntent,
525                final Intent fillInIntent) {
526            if (pendingIntent.isActivity()) {
527                setOnDismissRunnable(new Runnable() {
528                    public void run() {
529                        try {
530                              // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
531                              Context context = view.getContext();
532                              ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
533                                      0, 0,
534                                      view.getMeasuredWidth(), view.getMeasuredHeight());
535                              context.startIntentSender(
536                                      pendingIntent.getIntentSender(), fillInIntent,
537                                      Intent.FLAG_ACTIVITY_NEW_TASK,
538                                      Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
539                          } catch (IntentSender.SendIntentException e) {
540                              android.util.Log.e(TAG, "Cannot send pending intent: ", e);
541                          } catch (Exception e) {
542                              android.util.Log.e(TAG, "Cannot send pending intent due to " +
543                                      "unknown exception: ", e);
544                          }
545                    }
546                });
547
548                if (mViewStateManager.isChallengeShowing()) {
549                    mViewStateManager.showBouncer(true);
550                } else {
551                    mCallback.dismiss(false);
552                }
553                return true;
554            } else {
555                return super.onClickHandler(view, pendingIntent, fillInIntent);
556            }
557        };
558    };
559
560    // Used to ignore callbacks from methods that are no longer current (e.g. face unlock).
561    // This avoids unwanted asynchronous events from messing with the state.
562    private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
563
564        @Override
565        public void userActivity(long timeout) {
566        }
567
568        @Override
569        public void showBackupSecurity() {
570        }
571
572        @Override
573        public void setOnDismissRunnable(Runnable runnable) {
574        }
575
576        @Override
577        public void reportSuccessfulUnlockAttempt() {
578        }
579
580        @Override
581        public void reportFailedUnlockAttempt() {
582        }
583
584        @Override
585        public boolean isVerifyUnlockOnly() {
586            return false;
587        }
588
589        @Override
590        public int getFailedAttempts() {
591            return 0;
592        }
593
594        @Override
595        public void dismiss(boolean securityVerified) {
596        }
597    };
598
599    protected boolean mShowSecurityWhenReturn;
600
601    @Override
602    public void reset() {
603        mIsVerifyUnlockOnly = false;
604        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
605    }
606
607    /**
608     *  Sets a runnable to run when keyguard is dismissed
609     * @param runnable
610     */
611    protected void setOnDismissRunnable(Runnable runnable) {
612        mLaunchRunnable = runnable;
613    }
614
615    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
616        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
617        KeyguardSecurityView view = null;
618        final int children = mSecurityViewContainer.getChildCount();
619        for (int child = 0; child < children; child++) {
620            if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
621                view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
622                break;
623            }
624        }
625        int layoutId = getLayoutIdFor(securityMode);
626        if (view == null && layoutId != 0) {
627            final LayoutInflater inflater = LayoutInflater.from(mContext);
628            if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
629            View v = inflater.inflate(layoutId, this, false);
630            mSecurityViewContainer.addView(v);
631            updateSecurityView(v);
632            view = (KeyguardSecurityView)v;
633        }
634
635        if (view instanceof KeyguardSelectorView) {
636            KeyguardSelectorView selectorView = (KeyguardSelectorView) view;
637            View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container);
638            selectorView.setCarrierArea(carrierText);
639        }
640
641        return view;
642    }
643
644    /**
645     * Switches to the given security view unless it's already being shown, in which case
646     * this is a no-op.
647     *
648     * @param securityMode
649     */
650    private void showSecurityScreen(SecurityMode securityMode) {
651        if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
652
653        if (securityMode == mCurrentSecuritySelection) return;
654
655        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
656        KeyguardSecurityView newView = getSecurityView(securityMode);
657
658        // Enter full screen mode if we're in SIM or Account screen
659        boolean fullScreenEnabled = getResources().getBoolean(
660                com.android.internal.R.bool.kg_sim_puk_account_full_screen);
661        boolean isSimOrAccount = securityMode == SecurityMode.SimPin
662                || securityMode == SecurityMode.SimPuk
663                || securityMode == SecurityMode.Account;
664        mAppWidgetContainer.setVisibility(
665                isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE);
666
667        // Emulate Activity life cycle
668        if (oldView != null) {
669            oldView.onPause();
670            oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
671        }
672        newView.onResume();
673        newView.setKeyguardCallback(mCallback);
674
675        final boolean needsInput = newView.needsInput();
676        if (mViewMediatorCallback != null) {
677            mViewMediatorCallback.setNeedsInput(needsInput);
678        }
679
680        // Find and show this child.
681        final int childCount = mSecurityViewContainer.getChildCount();
682
683        mSecurityViewContainer.setInAnimation(
684                AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_in));
685        mSecurityViewContainer.setOutAnimation(
686                AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_out));
687        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
688        for (int i = 0; i < childCount; i++) {
689            if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
690                mSecurityViewContainer.setDisplayedChild(i);
691                break;
692            }
693        }
694
695        if (securityMode == SecurityMode.None) {
696            // Discard current runnable if we're switching back to the selector view
697            setOnDismissRunnable(null);
698        }
699        mCurrentSecuritySelection = securityMode;
700    }
701
702    @Override
703    public void onScreenTurnedOn() {
704        if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
705        showPrimarySecurityScreen(false);
706        getSecurityView(mCurrentSecuritySelection).onResume();
707
708        // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
709        // layout is blank but forcing a layout causes it to reappear (e.g. with with
710        // hierarchyviewer).
711        requestLayout();
712
713        if (mViewStateManager != null) {
714            mViewStateManager.showUsabilityHints();
715        }
716    }
717
718    @Override
719    public void onScreenTurnedOff() {
720        if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s",
721                Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
722        // Once the screen turns off, we no longer consider this to be first boot and we want the
723        // biometric unlock to start next time keyguard is shown.
724        KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
725        saveStickyWidgetIndex();
726        checkAppWidgetConsistency();
727        showPrimarySecurityScreen(true);
728        getSecurityView(mCurrentSecuritySelection).onPause();
729        CameraWidgetFrame cameraPage = findCameraPage();
730        if (cameraPage != null) {
731            cameraPage.onScreenTurnedOff();
732        }
733    }
734
735    @Override
736    public void show() {
737        if (DEBUG) Log.d(TAG, "show()");
738        showPrimarySecurityScreen(false);
739    }
740
741    private boolean isSecure() {
742        SecurityMode mode = mSecurityModel.getSecurityMode();
743        switch (mode) {
744            case Pattern:
745                return mLockPatternUtils.isLockPatternEnabled();
746            case Password:
747            case PIN:
748                return mLockPatternUtils.isLockPasswordEnabled();
749            case SimPin:
750            case SimPuk:
751            case Account:
752                return true;
753            case None:
754                return false;
755            default:
756                throw new IllegalStateException("Unknown security mode " + mode);
757        }
758    }
759
760    @Override
761    public void wakeWhenReadyTq(int keyCode) {
762        if (DEBUG) Log.d(TAG, "onWakeKey");
763        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) {
764            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
765            showSecurityScreen(SecurityMode.None);
766        } else {
767            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
768        }
769        if (mViewMediatorCallback != null) {
770            mViewMediatorCallback.wakeUp();
771        }
772    }
773
774    @Override
775    public void verifyUnlock() {
776        SecurityMode securityMode = mSecurityModel.getSecurityMode();
777        if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
778            if (mViewMediatorCallback != null) {
779                mViewMediatorCallback.keyguardDone(true);
780            }
781        } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
782                && securityMode != KeyguardSecurityModel.SecurityMode.PIN
783                && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
784            // can only verify unlock when in pattern/password mode
785            if (mViewMediatorCallback != null) {
786                mViewMediatorCallback.keyguardDone(false);
787            }
788        } else {
789            // otherwise, go to the unlock screen, see if they can verify it
790            mIsVerifyUnlockOnly = true;
791            showSecurityScreen(securityMode);
792        }
793    }
794
795    private int getSecurityViewIdForMode(SecurityMode securityMode) {
796        switch (securityMode) {
797            case None: return R.id.keyguard_selector_view;
798            case Pattern: return R.id.keyguard_pattern_view;
799            case PIN: return R.id.keyguard_pin_view;
800            case Password: return R.id.keyguard_password_view;
801            case Biometric: return R.id.keyguard_face_unlock_view;
802            case Account: return R.id.keyguard_account_view;
803            case SimPin: return R.id.keyguard_sim_pin_view;
804            case SimPuk: return R.id.keyguard_sim_puk_view;
805        }
806        return 0;
807    }
808
809    private int getLayoutIdFor(SecurityMode securityMode) {
810        switch (securityMode) {
811            case None: return R.layout.keyguard_selector_view;
812            case Pattern: return R.layout.keyguard_pattern_view;
813            case PIN: return R.layout.keyguard_pin_view;
814            case Password: return R.layout.keyguard_password_view;
815            case Biometric: return R.layout.keyguard_face_unlock_view;
816            case Account: return R.layout.keyguard_account_view;
817            case SimPin: return R.layout.keyguard_sim_pin_view;
818            case SimPuk: return R.layout.keyguard_sim_puk_view;
819            default:
820                return 0;
821        }
822    }
823
824    private boolean addWidget(int appId, int pageIndex) {
825        AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appId);
826        if (appWidgetInfo != null) {
827            AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
828            addWidget(view, pageIndex);
829            return true;
830        } else {
831            Log.w(TAG, "AppWidgetInfo for app widget id " + appId + " was null, deleting");
832            mLockPatternUtils.removeAppWidget(appId);
833            return false;
834        }
835    }
836
837    private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks =
838        new CameraWidgetFrame.Callbacks() {
839            @Override
840            public void onLaunchingCamera() {
841                setSliderHandleAlpha(0);
842            }
843
844            @Override
845            public void onCameraLaunchedSuccessfully() {
846                if (isCameraPage(mAppWidgetContainer.getCurrentPage())) {
847                    mAppWidgetContainer.scrollLeft();
848                }
849                setSliderHandleAlpha(1);
850                mShowSecurityWhenReturn = true;
851            }
852
853            @Override
854            public void onCameraLaunchedUnsuccessfully() {
855                setSliderHandleAlpha(1);
856            }
857
858            private void setSliderHandleAlpha(float alpha) {
859                SlidingChallengeLayout slider =
860                        (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
861                if (slider != null) {
862                    slider.setHandleAlpha(alpha);
863                }
864            }
865        };
866
867    private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
868        @Override
869        Context getContext() {
870            return mContext;
871        }
872
873        @Override
874        KeyguardSecurityCallback getCallback() {
875            return mCallback;
876        }
877
878        @Override
879        LockPatternUtils getLockPatternUtils() {
880            return mLockPatternUtils;
881        }
882    };
883
884    private void addDefaultWidgets() {
885        LayoutInflater inflater = LayoutInflater.from(mContext);
886        inflater.inflate(R.layout.keyguard_transport_control_view, this, true);
887
888        View addWidget = inflater.inflate(R.layout.keyguard_add_widget, null, true);
889        mAppWidgetContainer.addWidget(addWidget);
890        if (mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
891            View cameraWidget =
892                    CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher);
893            if (cameraWidget != null) {
894                mAppWidgetContainer.addWidget(cameraWidget);
895            }
896        }
897
898        View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
899        addWidgetButton.setOnClickListener(new OnClickListener() {
900            @Override
901            public void onClick(View v) {
902                mCallback.setOnDismissRunnable(new Runnable() {
903
904                    @Override
905                    public void run() {
906                        launchPickActivityIntent();
907                    }
908                });
909                mCallback.dismiss(false);
910            }
911        });
912
913        enableUserSelectorIfNecessary();
914        initializeTransportControl();
915    }
916
917    private void launchPickActivityIntent() {
918        // Create intent to pick widget
919        Intent pickIntent = new Intent(AppWidgetManager.ACTION_KEYGUARD_APPWIDGET_PICK);
920
921        int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
922        if (appWidgetId != -1) {
923            pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
924            pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, false);
925            pickIntent.putExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER,
926                    AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
927
928            pickIntent.putExtra(Intent.EXTRA_INTENT, getBaseIntent());
929            pickIntent.addFlags(
930                    Intent.FLAG_ACTIVITY_NEW_TASK
931                    | Intent.FLAG_ACTIVITY_SINGLE_TOP
932                    | Intent.FLAG_ACTIVITY_CLEAR_TOP
933                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
934            mContext.startActivityAsUser(pickIntent,
935                    new UserHandle(UserHandle.USER_CURRENT));
936        } else {
937            Log.e(TAG, "Unable to allocate an AppWidget id in lock screen");
938        }
939    }
940
941    private Intent getBaseIntent() {
942        Intent baseIntent = new Intent(Intent.ACTION_MAIN, null);
943        baseIntent.addCategory(Intent.CATEGORY_DEFAULT);
944
945        Bundle options = new Bundle();
946        options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
947                AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
948        baseIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
949        return baseIntent;
950    }
951
952    private void removeTransportFromWidgetPager() {
953        int page = getWidgetPosition(R.id.keyguard_transport_control);
954        if (page != -1) {
955            mAppWidgetContainer.removeWidget(mTransportControl);
956
957            // XXX keep view attached so we still get show/hide events from AudioManager
958            KeyguardHostView.this.addView(mTransportControl);
959            mTransportControl.setVisibility(View.GONE);
960            mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_GONE);
961            mTransportControl.post(mSwitchPageRunnable);
962        }
963    }
964
965    private void addTransportToWidgetPager() {
966        if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
967            KeyguardHostView.this.removeView(mTransportControl);
968            // insert to left of camera if it exists, otherwise after right-most widget
969            int lastWidget = mAppWidgetContainer.getChildCount() - 1;
970            int position = 0; // handle no widget case
971            if (lastWidget >= 0) {
972                position = isCameraPage(lastWidget) ? lastWidget : lastWidget + 1;
973            }
974            mAppWidgetContainer.addWidget(mTransportControl, position);
975            mTransportControl.setVisibility(View.VISIBLE);
976        }
977    }
978
979    private void initializeTransportControl() {
980        mTransportControl =
981            (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
982        mTransportControl.setVisibility(View.GONE);
983
984        // This code manages showing/hiding the transport control. We keep it around and only
985        // add it to the hierarchy if it needs to be present.
986        if (mTransportControl != null) {
987            mTransportControl.setKeyguardCallback(new TransportCallback() {
988                @Override
989                public void onListenerDetached() {
990                    removeTransportFromWidgetPager();
991                    mTransportControl.post(mSwitchPageRunnable);
992                }
993
994                @Override
995                public void onListenerAttached() {
996                    // Transport will be added when playstate changes...
997                    mTransportControl.post(mSwitchPageRunnable);
998                }
999
1000                @Override
1001                public void onPlayStateChanged() {
1002                    mTransportControl.post(mSwitchPageRunnable);
1003                }
1004            });
1005        }
1006    }
1007
1008    private int getAddPageIndex() {
1009        View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
1010        int addPageIndex = mAppWidgetContainer.indexOfChild(addWidget);
1011        // This shouldn't happen, but just to be safe!
1012        if (addPageIndex < 0) {
1013            addPageIndex = 0;
1014        }
1015        return addPageIndex;
1016    }
1017
1018    private void addDefaultStatusWidget(int index) {
1019        LayoutInflater inflater = LayoutInflater.from(mContext);
1020        View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
1021        mAppWidgetContainer.addWidget(statusWidget, index);
1022    }
1023
1024    private void addWidgetsFromSettings() {
1025        DevicePolicyManager dpm =
1026                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
1027        if (dpm != null) {
1028            final int currentUser = mLockPatternUtils.getCurrentUser();
1029            final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
1030            if ((disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
1031                Log.v(TAG, "Keyguard widgets disabled because of device policy admin");
1032                return;
1033            }
1034        }
1035
1036        int addPageIndex = getAddPageIndex();
1037
1038        // Add user-selected widget
1039        final int[] widgets = mLockPatternUtils.getAppWidgets();
1040
1041        if (widgets == null) {
1042            Log.d(TAG, "Problem reading widgets");
1043        } else {
1044            for (int i = widgets.length -1; i >= 0; i--) {
1045                if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
1046                    addDefaultStatusWidget(addPageIndex + 1);
1047                } else {
1048                    // We add the widgets from left to right, starting after the first page after
1049                    // the add page. We count down, since the order will be persisted from right
1050                    // to left, starting after camera.
1051                    addWidget(widgets[i], addPageIndex + 1);
1052                }
1053            }
1054        }
1055        checkAppWidgetConsistency();
1056    }
1057
1058    public void checkAppWidgetConsistency() {
1059        final int childCount = mAppWidgetContainer.getChildCount();
1060        boolean widgetPageExists = false;
1061        for (int i = 0; i < childCount; i++) {
1062            if (isWidgetPage(i)) {
1063                widgetPageExists = true;
1064                break;
1065            }
1066        }
1067        if (!widgetPageExists) {
1068            final int addPageIndex = getAddPageIndex();
1069
1070            Resources res = getContext().getResources();
1071            ComponentName defaultAppWidget = new ComponentName(
1072                    res.getString(R.string.widget_default_package_name),
1073                    res.getString(R.string.widget_default_class_name));
1074
1075            // Note: we don't support configuring the widget
1076            int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
1077            boolean bindSuccessful = false;
1078            try {
1079                mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget);
1080                bindSuccessful = true;
1081            } catch (IllegalArgumentException e) {
1082                Log.e(TAG, "Error when trying to bind default AppWidget: " + e);
1083            }
1084            // Use the built-in status/clock view if we can't inflate the default widget
1085            if (!(bindSuccessful && addWidget(appWidgetId, addPageIndex + 1))) {
1086                addDefaultStatusWidget(addPageIndex + 1);
1087            }
1088            mAppWidgetContainer.onAddView(
1089                    mAppWidgetContainer.getChildAt(addPageIndex + 1), addPageIndex + 1);
1090        }
1091    }
1092
1093    Runnable mSwitchPageRunnable = new Runnable() {
1094        @Override
1095        public void run() {
1096           showAppropriateWidgetPage();
1097        }
1098    };
1099
1100    static class SavedState extends BaseSavedState {
1101        int transportState;
1102
1103        SavedState(Parcelable superState) {
1104            super(superState);
1105        }
1106
1107        private SavedState(Parcel in) {
1108            super(in);
1109            this.transportState = in.readInt();
1110        }
1111
1112        @Override
1113        public void writeToParcel(Parcel out, int flags) {
1114            super.writeToParcel(out, flags);
1115            out.writeInt(this.transportState);
1116        }
1117
1118        public static final Parcelable.Creator<SavedState> CREATOR
1119                = new Parcelable.Creator<SavedState>() {
1120            public SavedState createFromParcel(Parcel in) {
1121                return new SavedState(in);
1122            }
1123
1124            public SavedState[] newArray(int size) {
1125                return new SavedState[size];
1126            }
1127        };
1128    }
1129
1130    @Override
1131    public Parcelable onSaveInstanceState() {
1132        if (DEBUG) Log.d(TAG, "onSaveInstanceState");
1133        saveStickyWidgetIndex();
1134        Parcelable superState = super.onSaveInstanceState();
1135        SavedState ss = new SavedState(superState);
1136        ss.transportState = mViewStateManager.getTransportState();
1137        return ss;
1138    }
1139
1140    @Override
1141    public void onRestoreInstanceState(Parcelable state) {
1142        if (DEBUG) Log.d(TAG, "onRestoreInstanceState");
1143        if (!(state instanceof SavedState)) {
1144            super.onRestoreInstanceState(state);
1145            return;
1146        }
1147        SavedState ss = (SavedState) state;
1148        super.onRestoreInstanceState(ss.getSuperState());
1149        mViewStateManager.setTransportState(ss.transportState);
1150        post(mSwitchPageRunnable);
1151    }
1152
1153    @Override
1154    public void onWindowFocusChanged(boolean hasWindowFocus) {
1155        super.onWindowFocusChanged(hasWindowFocus);
1156        if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused"));
1157        if (!hasWindowFocus) {
1158            saveStickyWidgetIndex();
1159        } else if (mShowSecurityWhenReturn) {
1160            SlidingChallengeLayout slider =
1161                (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
1162            if (slider != null) {
1163                slider.setHandleAlpha(1);
1164                slider.showChallenge(true);
1165            }
1166            mShowSecurityWhenReturn = false;
1167        }
1168    }
1169
1170    private void showAppropriateWidgetPage() {
1171        int state = mViewStateManager.getTransportState();
1172        boolean isMusicPlaying = mTransportControl.isMusicPlaying()
1173                || state == KeyguardViewStateManager.TRANSPORT_VISIBLE;
1174        if (isMusicPlaying) {
1175            mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_VISIBLE);
1176            addTransportToWidgetPager();
1177        } else if (state == KeyguardViewStateManager.TRANSPORT_VISIBLE) {
1178            mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_INVISIBLE);
1179        }
1180        int pageToShow = getAppropriateWidgetPage(isMusicPlaying);
1181        mAppWidgetContainer.setCurrentPage(pageToShow);
1182    }
1183
1184    private CameraWidgetFrame findCameraPage() {
1185        for (int i = mAppWidgetContainer.getChildCount() - 1; i >= 0; i--) {
1186            if (isCameraPage(i)) {
1187                return (CameraWidgetFrame) mAppWidgetContainer.getChildAt(i);
1188            }
1189        }
1190        return null;
1191    }
1192
1193    private boolean isWidgetPage(int pageIndex) {
1194        View v = mAppWidgetContainer.getChildAt(pageIndex);
1195        if (v != null && v instanceof KeyguardWidgetFrame) {
1196            KeyguardWidgetFrame kwf = (KeyguardWidgetFrame) v;
1197            return kwf.getContentAppWidgetId() != AppWidgetManager.INVALID_APPWIDGET_ID;
1198        }
1199        return false;
1200    }
1201
1202    private boolean isCameraPage(int pageIndex) {
1203        View v = mAppWidgetContainer.getChildAt(pageIndex);
1204        return v != null && v instanceof CameraWidgetFrame;
1205    }
1206
1207    private boolean isAddPage(int pageIndex) {
1208        View v = mAppWidgetContainer.getChildAt(pageIndex);
1209        return v != null && v.getId() == R.id.keyguard_add_widget;
1210    }
1211
1212    private boolean isMusicPage(int pageIndex) {
1213        return pageIndex >= 0 && pageIndex == getWidgetPosition(R.id.keyguard_transport_control);
1214    }
1215
1216    private int getStickyWidget() {
1217        // The first time we query the persistent state. From that point, we use a locally updated
1218        // notion of the sticky widget page.
1219        if (!mPersitentStickyWidgetLoaded) {
1220            mLocalStickyWidget = mLockPatternUtils.getStickyAppWidgetIndex();
1221            mPersitentStickyWidgetLoaded = true;
1222        }
1223        return mLocalStickyWidget;
1224    }
1225
1226    public void updateStickyWidget(int index) {
1227        if (index < 0 || index >= mAppWidgetContainer.getChildCount()) {
1228            return;
1229        }
1230        if (isAddPage(index)) {
1231            return;
1232        }
1233        if (isCameraPage(index)) {
1234            return;
1235        }
1236        if (isMusicPage(index)) {
1237            return;
1238        }
1239
1240        mLocalStickyWidget = index;
1241    }
1242
1243    private int getAppropriateWidgetPage(boolean isMusicPlaying) {
1244        // assumes at least one widget (besides camera + add)
1245
1246        // if music playing, show transport
1247        if (isMusicPlaying) {
1248            if (DEBUG) Log.d(TAG, "Music playing, show transport");
1249            return mAppWidgetContainer.getWidgetPageIndex(mTransportControl);
1250        }
1251
1252        // if we have a valid sticky widget, show it
1253        int stickyWidgetIndex = getStickyWidget();
1254        if (stickyWidgetIndex > -1
1255                && stickyWidgetIndex < mAppWidgetContainer.getChildCount()
1256                && !isAddPage(stickyWidgetIndex)
1257                && !isCameraPage(stickyWidgetIndex)) {
1258            if (DEBUG) Log.d(TAG, "Valid sticky widget found, show page " + stickyWidgetIndex);
1259            return stickyWidgetIndex;
1260        }
1261
1262        // else show the right-most widget (except for camera)
1263        int rightMost = mAppWidgetContainer.getChildCount() - 1;
1264        if (isCameraPage(rightMost)) {
1265            rightMost--;
1266        }
1267        if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost);
1268        return rightMost;
1269    }
1270
1271    private void saveStickyWidgetIndex() {
1272        if (DEBUG) Log.d(TAG, "saveStickyWidgetIndex: " + mLocalStickyWidget);
1273        if (mPersitentStickyWidgetLoaded && mLocalStickyWidget >= 0) {
1274            mLockPatternUtils.setStickyAppWidgetIndex(mLocalStickyWidget);
1275        }
1276    }
1277
1278    private void enableUserSelectorIfNecessary() {
1279        if (!UserManager.supportsMultipleUsers()) {
1280            return; // device doesn't support multi-user mode
1281        }
1282
1283        // if there are multiple users, we need to enable to multi-user switcher
1284        UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1285        List<UserInfo> users = mUm.getUsers(true);
1286
1287        if (users.size() > 1) {
1288            KeyguardMultiUserSelectorView multiUser =
1289                    (KeyguardMultiUserSelectorView) findViewById(R.id.keyguard_user_selector);
1290            multiUser.setVisibility(View.VISIBLE);
1291            multiUser.addUsers(mUm.getUsers(true));
1292            UserSwitcherCallback callback = new UserSwitcherCallback() {
1293                @Override
1294                public void hideSecurityView(int duration) {
1295                    mSecurityViewContainer.animate().alpha(0).setDuration(duration);
1296                }
1297
1298                @Override
1299                public void showSecurityView() {
1300                    mSecurityViewContainer.setAlpha(1.0f);
1301                }
1302
1303                @Override
1304                public void showUnlockHint() {
1305                    if (mKeyguardSelectorView != null) {
1306                        mKeyguardSelectorView.showUsabilityHint();
1307                    }
1308                }
1309
1310                @Override
1311                public void userActivity() {
1312                    if (mViewMediatorCallback != null) {
1313                        mViewMediatorCallback.userActivity();
1314                    }
1315                }
1316            };
1317            multiUser.setCallback(callback);
1318        }
1319    }
1320
1321    @Override
1322    public void cleanUp() {
1323
1324    }
1325
1326    /**
1327     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
1328     * some cases where we wish to disable it, notably when the menu button placement or technology
1329     * is prone to false positives.
1330     *
1331     * @return true if the menu key should be enabled
1332     */
1333    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
1334    private boolean shouldEnableMenuKey() {
1335        final Resources res = getResources();
1336        final boolean configDisabled = res.getBoolean(
1337                com.android.internal.R.bool.config_disableMenuKeyInLockScreen);
1338        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
1339        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
1340        return !configDisabled || isTestHarness || fileOverride;
1341    }
1342
1343
1344
1345    public void goToUserSwitcher() {
1346        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector));
1347    }
1348
1349    public boolean handleMenuKey() {
1350        // The following enables the MENU key to work for testing automation
1351        if (shouldEnableMenuKey()) {
1352            showNextSecurityScreenOrFinish(false);
1353            return true;
1354        }
1355        return false;
1356    }
1357
1358    public boolean handleBackKey() {
1359        if (mCurrentSecuritySelection != SecurityMode.None) {
1360            mCallback.dismiss(false);
1361            return true;
1362        }
1363        return false;
1364    }
1365
1366    /**
1367     *  Dismisses the keyguard by going to the next screen or making it gone.
1368     */
1369    public void dismiss() {
1370        showNextSecurityScreenOrFinish(false);
1371    }
1372}
1373