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.keyguard;
18
19import com.android.internal.widget.LockPatternUtils;
20import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
21import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState;
22
23import android.app.ActivityManager;
24import android.app.ActivityOptions;
25import android.app.admin.DevicePolicyManager;
26import android.appwidget.AppWidgetHost;
27import android.appwidget.AppWidgetHostView;
28import android.appwidget.AppWidgetManager;
29import android.appwidget.AppWidgetProviderInfo;
30import android.content.ComponentName;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentSender;
34import android.content.pm.PackageManager.NameNotFoundException;
35import android.content.res.Resources;
36import android.graphics.Rect;
37import android.media.RemoteControlClient;
38import android.os.Bundle;
39import android.os.Looper;
40import android.os.Parcel;
41import android.os.Parcelable;
42import android.os.UserHandle;
43import android.os.UserManager;
44import android.provider.Settings;
45import android.util.AttributeSet;
46import android.util.Log;
47import android.util.Slog;
48import android.view.LayoutInflater;
49import android.view.MotionEvent;
50import android.view.View;
51import android.widget.RemoteViews.OnClickHandler;
52
53import java.lang.ref.WeakReference;
54
55public class KeyguardHostView extends KeyguardViewBase {
56    private static final String TAG = "KeyguardHostView";
57    public static boolean DEBUG = KeyguardConstants.DEBUG;
58    public static boolean DEBUGXPORT = true; // debug music transport control
59
60    // Transport control states.
61    static final int TRANSPORT_GONE = 0;
62    static final int TRANSPORT_INVISIBLE = 1;
63    static final int TRANSPORT_VISIBLE = 2;
64
65    private int mTransportState = TRANSPORT_GONE;
66
67    // Found in KeyguardAppWidgetPickActivity.java
68    static final int APPWIDGET_HOST_ID = 0x4B455947;
69    private final int MAX_WIDGETS = 5;
70
71    private AppWidgetHost mAppWidgetHost;
72    private AppWidgetManager mAppWidgetManager;
73    private KeyguardWidgetPager mAppWidgetContainer;
74    // TODO remove transport control references, these don't exist anymore
75    private KeyguardTransportControlView mTransportControl;
76    private int mAppWidgetToShow;
77
78    protected int mFailedAttempts;
79
80    private KeyguardViewStateManager mViewStateManager;
81
82    private Rect mTempRect = new Rect();
83    private int mDisabledFeatures;
84    private boolean mCameraDisabled;
85    private boolean mSafeModeEnabled;
86    private boolean mUserSetupCompleted;
87
88    // User for whom this host view was created.  Final because we should never change the
89    // id without reconstructing an instance of KeyguardHostView. See note below...
90    private final int mUserId;
91
92    private KeyguardMultiUserSelectorView mKeyguardMultiUserSelectorView;
93
94    protected int mClientGeneration;
95
96    protected boolean mShowSecurityWhenReturn;
97
98    private final Rect mInsets = new Rect();
99
100    private MyOnClickHandler mOnClickHandler = new MyOnClickHandler(this);
101
102    private Runnable mPostBootCompletedRunnable;
103
104    /*package*/ interface UserSwitcherCallback {
105        void hideSecurityView(int duration);
106        void showSecurityView();
107        void showUnlockHint();
108        void userActivity();
109    }
110
111    interface TransportControlCallback {
112        void userActivity();
113    }
114
115    public interface OnDismissAction {
116        /**
117         * @return true if the dismiss should be deferred
118         */
119        boolean onDismiss();
120    }
121
122    public KeyguardHostView(Context context) {
123        this(context, null);
124    }
125
126    public KeyguardHostView(Context context, AttributeSet attrs) {
127        super(context, attrs);
128
129        if (DEBUG) Log.e(TAG, "KeyguardHostView()");
130
131        mLockPatternUtils = new LockPatternUtils(context);
132
133        // Note: This depends on KeyguardHostView getting reconstructed every time the
134        // user switches, since mUserId will be used for the entire session.
135        // Once created, keyguard should *never* re-use this instance with another user.
136        // In other words, mUserId should never change - hence it's marked final.
137        mUserId = mLockPatternUtils.getCurrentUser();
138
139        DevicePolicyManager dpm =
140                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
141        if (dpm != null) {
142            mDisabledFeatures = getDisabledFeatures(dpm);
143            mCameraDisabled = dpm.getCameraDisabled(null);
144        }
145
146        mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled();
147
148        // These need to be created with the user context...
149        Context userContext = null;
150        try {
151            final String packageName = "system";
152            userContext = mContext.createPackageContextAsUser(packageName, 0,
153                    new UserHandle(mUserId));
154
155        } catch (NameNotFoundException e) {
156            e.printStackTrace();
157            // This should never happen, but it's better to have no widgets than to crash.
158            userContext = context;
159        }
160
161        mAppWidgetHost = new AppWidgetHost(userContext, APPWIDGET_HOST_ID, mOnClickHandler,
162                Looper.myLooper());
163
164        mAppWidgetManager = AppWidgetManager.getInstance(userContext);
165
166        mViewStateManager = new KeyguardViewStateManager(this);
167
168        mUserSetupCompleted = Settings.Secure.getIntForUser(mContext.getContentResolver(),
169                Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
170
171        // Ensure we have the current state *before* we call showAppropriateWidgetPage()
172        getInitialTransportState();
173
174        if (mSafeModeEnabled) {
175            Log.v(TAG, "Keyguard widgets disabled by safe mode");
176        }
177        if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
178            Log.v(TAG, "Keyguard widgets disabled by DPM");
179        }
180        if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0) {
181            Log.v(TAG, "Keyguard secure camera disabled by DPM");
182        }
183    }
184
185    private void getInitialTransportState() {
186        DisplayClientState dcs = KeyguardUpdateMonitor.getInstance(mContext)
187                .getCachedDisplayClientState();
188        mTransportState = (dcs.clearing ? TRANSPORT_GONE :
189            (isMusicPlaying(dcs.playbackState) ? TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
190
191        if (DEBUGXPORT) Log.v(TAG, "Initial transport state: "
192                + mTransportState + ", pbstate=" + dcs.playbackState);
193    }
194
195    private void cleanupAppWidgetIds() {
196        if (mSafeModeEnabled || widgetsDisabled()) return;
197
198        // Clean up appWidgetIds that are bound to lockscreen, but not actually used
199        // This is only to clean up after another bug: we used to not call
200        // deleteAppWidgetId when a user manually deleted a widget in keyguard. This code
201        // shouldn't have to run more than once per user. AppWidgetProviders rely on callbacks
202        // that are triggered by deleteAppWidgetId, which is why we're doing this
203        int[] appWidgetIdsInKeyguardSettings = mLockPatternUtils.getAppWidgets();
204        int[] appWidgetIdsBoundToHost = mAppWidgetHost.getAppWidgetIds();
205        for (int i = 0; i < appWidgetIdsBoundToHost.length; i++) {
206            int appWidgetId = appWidgetIdsBoundToHost[i];
207            if (!contains(appWidgetIdsInKeyguardSettings, appWidgetId)) {
208                Log.d(TAG, "Found a appWidgetId that's not being used by keyguard, deleting id "
209                        + appWidgetId);
210                mAppWidgetHost.deleteAppWidgetId(appWidgetId);
211            }
212        }
213    }
214
215    private static boolean contains(int[] array, int target) {
216        for (int value : array) {
217            if (value == target) {
218                return true;
219            }
220        }
221        return false;
222    }
223
224    private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks =
225            new KeyguardUpdateMonitorCallback() {
226        @Override
227        public void onBootCompleted() {
228            if (mPostBootCompletedRunnable != null) {
229                mPostBootCompletedRunnable.run();
230                mPostBootCompletedRunnable = null;
231            }
232        }
233        @Override
234        public void onUserSwitchComplete(int userId) {
235            if (mKeyguardMultiUserSelectorView != null) {
236                mKeyguardMultiUserSelectorView.finalizeActiveUserView(true);
237            }
238        }
239    };
240
241    private static final boolean isMusicPlaying(int playbackState) {
242        // This should agree with the list in AudioService.isPlaystateActive()
243        switch (playbackState) {
244            case RemoteControlClient.PLAYSTATE_PLAYING:
245            case RemoteControlClient.PLAYSTATE_BUFFERING:
246            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
247            case RemoteControlClient.PLAYSTATE_REWINDING:
248            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
249            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
250                return true;
251            default:
252                return false;
253        }
254    }
255
256    private SlidingChallengeLayout mSlidingChallengeLayout;
257    private MultiPaneChallengeLayout mMultiPaneChallengeLayout;
258
259    @Override
260    public boolean onTouchEvent(MotionEvent ev) {
261        boolean result = super.onTouchEvent(ev);
262        mTempRect.set(0, 0, 0, 0);
263        offsetRectIntoDescendantCoords(getSecurityContainer(), mTempRect);
264        ev.offsetLocation(mTempRect.left, mTempRect.top);
265        result = getSecurityContainer().dispatchTouchEvent(ev) || result;
266        ev.offsetLocation(-mTempRect.left, -mTempRect.top);
267        return result;
268    }
269
270    private int getWidgetPosition(int id) {
271        final KeyguardWidgetPager appWidgetContainer = mAppWidgetContainer;
272        final int children = appWidgetContainer.getChildCount();
273        for (int i = 0; i < children; i++) {
274            final View content = appWidgetContainer.getWidgetPageAt(i).getContent();
275            if (content != null && content.getId() == id) {
276                return i;
277            } else if (content == null) {
278                // Attempt to track down bug #8886916
279                Log.w(TAG, "*** Null content at " + "i=" + i + ",id=" + id + ",N=" + children);
280            }
281        }
282        return -1;
283    }
284
285    @Override
286    protected void onFinishInflate() {
287        super.onFinishInflate();
288
289        // Grab instances of and make any necessary changes to the main layouts. Create
290        // view state manager and wire up necessary listeners / callbacks.
291        View deleteDropTarget = findViewById(R.id.keyguard_widget_pager_delete_target);
292        mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
293        mAppWidgetContainer.setVisibility(VISIBLE);
294        mAppWidgetContainer.setCallbacks(mWidgetCallbacks);
295        mAppWidgetContainer.setDeleteDropTarget(deleteDropTarget);
296        mAppWidgetContainer.setMinScale(0.5f);
297
298        mSlidingChallengeLayout = (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
299        if (mSlidingChallengeLayout != null) {
300            mSlidingChallengeLayout.setOnChallengeScrolledListener(mViewStateManager);
301        }
302        mAppWidgetContainer.setViewStateManager(mViewStateManager);
303        mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils);
304
305        mMultiPaneChallengeLayout =
306                (MultiPaneChallengeLayout) findViewById(R.id.multi_pane_challenge);
307        ChallengeLayout challenge = mSlidingChallengeLayout != null ? mSlidingChallengeLayout :
308                mMultiPaneChallengeLayout;
309        challenge.setOnBouncerStateChangedListener(mViewStateManager);
310        mAppWidgetContainer.setBouncerAnimationDuration(challenge.getBouncerAnimationDuration());
311        mViewStateManager.setPagedView(mAppWidgetContainer);
312        mViewStateManager.setChallengeLayout(challenge);
313
314        mViewStateManager.setSecurityViewContainer(getSecurityContainer());
315
316        if (KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
317            updateAndAddWidgets();
318        } else {
319            // We can't add widgets until after boot completes because AppWidgetHost may try
320            // to contact the providers.  Do it later.
321            mPostBootCompletedRunnable = new Runnable() {
322                @Override
323                public void run() {
324                    updateAndAddWidgets();
325                }
326            };
327        }
328
329        getSecurityContainer().updateSecurityViews(mViewStateManager.isBouncing());
330        enableUserSelectorIfNecessary();
331    }
332
333    private void updateAndAddWidgets() {
334        cleanupAppWidgetIds();
335        addDefaultWidgets();
336        addWidgetsFromSettings();
337        maybeEnableAddButton();
338        checkAppWidgetConsistency();
339
340        // Don't let the user drag the challenge down if widgets are disabled.
341        if (mSlidingChallengeLayout != null) {
342            mSlidingChallengeLayout.setEnableChallengeDragging(!widgetsDisabled());
343        }
344
345        // Select the appropriate page
346        mSwitchPageRunnable.run();
347
348        // This needs to be called after the pages are all added.
349        mViewStateManager.showUsabilityHints();
350    }
351
352    private void maybeEnableAddButton() {
353        if (!shouldEnableAddWidget()) {
354            mAppWidgetContainer.setAddWidgetEnabled(false);
355        }
356    }
357
358    private boolean shouldEnableAddWidget() {
359        return numWidgets() < MAX_WIDGETS && mUserSetupCompleted;
360    }
361
362    @Override
363    public boolean dismiss(boolean authenticated) {
364        boolean finished = super.dismiss(authenticated);
365        if (!finished) {
366            mViewStateManager.showBouncer(true);
367
368            // Enter full screen mode if we're in SIM or Account screen
369            SecurityMode securityMode = getSecurityContainer().getSecurityMode();
370            boolean isFullScreen = getResources().getBoolean(R.bool.kg_sim_puk_account_full_screen);
371            boolean isSimOrAccount = securityMode == SecurityMode.SimPin
372                    || securityMode == SecurityMode.SimPuk
373                    || securityMode == SecurityMode.Account;
374            mAppWidgetContainer.setVisibility(
375                    isSimOrAccount && isFullScreen ? View.GONE : View.VISIBLE);
376
377            // Don't show camera or search in navbar when SIM or Account screen is showing
378            setSystemUiVisibility(isSimOrAccount ?
379                    (getSystemUiVisibility() | View.STATUS_BAR_DISABLE_SEARCH)
380                    : (getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_SEARCH));
381
382            if (mSlidingChallengeLayout != null) {
383                mSlidingChallengeLayout.setChallengeInteractive(!isFullScreen);
384            }
385        }
386        return finished;
387    }
388
389    private int getDisabledFeatures(DevicePolicyManager dpm) {
390        int disabledFeatures = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
391        if (dpm != null) {
392            final int currentUser = mLockPatternUtils.getCurrentUser();
393            disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
394        }
395        return disabledFeatures;
396    }
397
398    private boolean widgetsDisabled() {
399        boolean disabledByLowRamDevice = ActivityManager.isLowRamDeviceStatic();
400        boolean disabledByDpm =
401                (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0;
402        boolean disabledByUser = !mLockPatternUtils.getWidgetsEnabled();
403        return disabledByLowRamDevice || disabledByDpm || disabledByUser;
404    }
405
406    private boolean cameraDisabledByDpm() {
407        return mCameraDisabled
408                || (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0;
409    }
410
411    @Override
412    public void setLockPatternUtils(LockPatternUtils utils) {
413        super.setLockPatternUtils(utils);
414        getSecurityContainer().updateSecurityViews(mViewStateManager.isBouncing());
415    }
416
417    @Override
418    protected void onAttachedToWindow() {
419        super.onAttachedToWindow();
420        mAppWidgetHost.startListening();
421        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks);
422    }
423
424    @Override
425    protected void onDetachedFromWindow() {
426        super.onDetachedFromWindow();
427        mAppWidgetHost.stopListening();
428        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
429    }
430
431    void addWidget(AppWidgetHostView view, int pageIndex) {
432        mAppWidgetContainer.addWidget(view, pageIndex);
433    }
434
435    private KeyguardWidgetPager.Callbacks mWidgetCallbacks
436            = new KeyguardWidgetPager.Callbacks() {
437        @Override
438        public void userActivity() {
439            KeyguardHostView.this.userActivity();
440        }
441
442        @Override
443        public void onUserActivityTimeoutChanged() {
444            KeyguardHostView.this.onUserActivityTimeoutChanged();
445        }
446
447        @Override
448        public void onAddView(View v) {
449            if (!shouldEnableAddWidget()) {
450                mAppWidgetContainer.setAddWidgetEnabled(false);
451            }
452        }
453
454        @Override
455        public void onRemoveView(View v, boolean deletePermanently) {
456            if (deletePermanently) {
457                final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
458                if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID &&
459                        appWidgetId != LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
460                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
461                }
462            }
463        }
464
465        @Override
466        public void onRemoveViewAnimationCompleted() {
467            if (shouldEnableAddWidget()) {
468                mAppWidgetContainer.setAddWidgetEnabled(true);
469            }
470        }
471    };
472
473    @Override
474    public void onUserSwitching(boolean switching) {
475        if (!switching && mKeyguardMultiUserSelectorView != null) {
476            mKeyguardMultiUserSelectorView.finalizeActiveUserView(false);
477        }
478    }
479
480    public void userActivity() {
481        if (mViewMediatorCallback != null) {
482            mViewMediatorCallback.userActivity();
483        }
484    }
485
486    public void onUserActivityTimeoutChanged() {
487        if (mViewMediatorCallback != null) {
488            mViewMediatorCallback.onUserActivityTimeoutChanged();
489        }
490    }
491
492    @Override
493    public long getUserActivityTimeout() {
494        // Currently only considering user activity timeouts needed by widgets.
495        // Could also take into account longer timeouts for certain security views.
496        if (mAppWidgetContainer != null) {
497            return mAppWidgetContainer.getUserActivityTimeout();
498        }
499        return -1;
500    }
501
502    private static class MyOnClickHandler extends OnClickHandler {
503
504        // weak reference to the hostView to avoid keeping a live reference
505        // due to Binder GC linkages to AppWidgetHost. By the same token,
506        // this click handler should not keep references to any large
507        // objects.
508        WeakReference<KeyguardHostView> mKeyguardHostView;
509
510        MyOnClickHandler(KeyguardHostView hostView) {
511            mKeyguardHostView = new WeakReference<KeyguardHostView>(hostView);
512        }
513
514        @Override
515        public boolean onClickHandler(final View view,
516                final android.app.PendingIntent pendingIntent,
517                final Intent fillInIntent) {
518            KeyguardHostView hostView = mKeyguardHostView.get();
519            if (hostView == null) {
520                return false;
521            }
522            if (pendingIntent.isActivity()) {
523                hostView.setOnDismissAction(new OnDismissAction() {
524                    public boolean onDismiss() {
525                        try {
526                            // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
527                            Context context = view.getContext();
528                            ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
529                                    0, 0,
530                                    view.getMeasuredWidth(), view.getMeasuredHeight());
531                            context.startIntentSender(
532                                    pendingIntent.getIntentSender(), fillInIntent,
533                                    Intent.FLAG_ACTIVITY_NEW_TASK,
534                                    Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
535                        } catch (IntentSender.SendIntentException e) {
536                            android.util.Log.e(TAG, "Cannot send pending intent: ", e);
537                        } catch (Exception e) {
538                            android.util.Log.e(TAG, "Cannot send pending intent due to " +
539                                    "unknown exception: ", e);
540                        }
541                        return false;
542                    }
543                });
544
545                if (hostView.mViewStateManager.isChallengeShowing()) {
546                    hostView.mViewStateManager.showBouncer(true);
547                } else {
548                    hostView.dismiss();
549                }
550                return true;
551            } else {
552                return super.onClickHandler(view, pendingIntent, fillInIntent);
553            }
554        };
555    };
556
557    @Override
558    public void onResume() {
559        super.onResume();
560        if (mViewStateManager != null) {
561            mViewStateManager.showUsabilityHints();
562        }
563    }
564
565    @Override
566    public void onPause() {
567        super.onPause();
568        // We use mAppWidgetToShow to show a particular widget after you add it-- once the screen
569        // turns off we reset that behavior
570        clearAppWidgetToShow();
571        if (KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
572            checkAppWidgetConsistency();
573        }
574        CameraWidgetFrame cameraPage = findCameraPage();
575        if (cameraPage != null) {
576            cameraPage.onScreenTurnedOff();
577        }
578    }
579
580    public void clearAppWidgetToShow() {
581        mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
582    }
583
584    private boolean addWidget(int appId, int pageIndex, boolean updateDbIfFailed) {
585        AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appId);
586        if (appWidgetInfo != null) {
587            AppWidgetHostView view = mAppWidgetHost.createView(mContext, appId, appWidgetInfo);
588            addWidget(view, pageIndex);
589            return true;
590        } else {
591            if (updateDbIfFailed) {
592                Log.w(TAG, "*** AppWidgetInfo for app widget id " + appId + "  was null for user"
593                        + mUserId + ", deleting");
594                mAppWidgetHost.deleteAppWidgetId(appId);
595                mLockPatternUtils.removeAppWidget(appId);
596            }
597            return false;
598        }
599    }
600
601    private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks =
602        new CameraWidgetFrame.Callbacks() {
603            @Override
604            public void onLaunchingCamera() {
605                setSliderHandleAlpha(0);
606            }
607
608            @Override
609            public void onCameraLaunchedSuccessfully() {
610                if (mAppWidgetContainer.isCameraPage(mAppWidgetContainer.getCurrentPage())) {
611                    mAppWidgetContainer.scrollLeft();
612                }
613                setSliderHandleAlpha(1);
614                mShowSecurityWhenReturn = true;
615            }
616
617            @Override
618            public void onCameraLaunchedUnsuccessfully() {
619                setSliderHandleAlpha(1);
620            }
621
622            private void setSliderHandleAlpha(float alpha) {
623                SlidingChallengeLayout slider =
624                        (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
625                if (slider != null) {
626                    slider.setHandleAlpha(alpha);
627                }
628            }
629        };
630
631    private int numWidgets() {
632        final int childCount = mAppWidgetContainer.getChildCount();
633        int widgetCount = 0;
634        for (int i = 0; i < childCount; i++) {
635            if (mAppWidgetContainer.isWidgetPage(i)) {
636                widgetCount++;
637            }
638        }
639        return widgetCount;
640    }
641
642    private void addDefaultWidgets() {
643        if (!mSafeModeEnabled && !widgetsDisabled()) {
644            LayoutInflater inflater = LayoutInflater.from(mContext);
645            View addWidget = inflater.inflate(R.layout.keyguard_add_widget, this, false);
646            mAppWidgetContainer.addWidget(addWidget, 0);
647            View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
648            addWidgetButton.setOnClickListener(new OnClickListener() {
649                @Override
650                public void onClick(View v) {
651                    // Pass in an invalid widget id... the picker will allocate an ID for us
652                    getActivityLauncher().launchWidgetPicker(AppWidgetManager.INVALID_APPWIDGET_ID);
653                }
654            });
655        }
656
657        // We currently disable cameras in safe mode because we support loading 3rd party
658        // cameras we can't trust.  TODO: plumb safe mode into camera creation code and only
659        // inflate system-provided camera?
660        if (!mSafeModeEnabled && !cameraDisabledByDpm() && mUserSetupCompleted
661                && mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
662            View cameraWidget = CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks,
663                    getActivityLauncher());
664            if (cameraWidget != null) {
665                mAppWidgetContainer.addWidget(cameraWidget);
666            }
667        }
668    }
669
670    /**
671     * Create KeyguardTransportControlView on demand.
672     * @return
673     */
674    private KeyguardTransportControlView getOrCreateTransportControl() {
675        if (mTransportControl == null) {
676            LayoutInflater inflater = LayoutInflater.from(mContext);
677            mTransportControl = (KeyguardTransportControlView)
678                    inflater.inflate(R.layout.keyguard_transport_control_view, this, false);
679            mTransportControl.setTransportControlCallback(new TransportControlCallback() {
680                public void userActivity() {
681                    mViewMediatorCallback.userActivity();
682                }
683            });
684        }
685        return mTransportControl;
686    }
687
688    private int getInsertPageIndex() {
689        View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
690        int insertionIndex = mAppWidgetContainer.indexOfChild(addWidget);
691        if (insertionIndex < 0) {
692            insertionIndex = 0; // no add widget page found
693        } else {
694            insertionIndex++; // place after add widget
695        }
696        return insertionIndex;
697    }
698
699    private void addDefaultStatusWidget(int index) {
700        LayoutInflater inflater = LayoutInflater.from(mContext);
701        View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
702        mAppWidgetContainer.addWidget(statusWidget, index);
703    }
704
705    private void addWidgetsFromSettings() {
706        if (mSafeModeEnabled || widgetsDisabled()) {
707            addDefaultStatusWidget(0);
708            return;
709        }
710
711        int insertionIndex = getInsertPageIndex();
712
713        // Add user-selected widget
714        final int[] widgets = mLockPatternUtils.getAppWidgets();
715
716        if (widgets == null) {
717            Log.d(TAG, "Problem reading widgets");
718        } else {
719            for (int i = widgets.length -1; i >= 0; i--) {
720                if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
721                    addDefaultStatusWidget(insertionIndex);
722                } else {
723                    // We add the widgets from left to right, starting after the first page after
724                    // the add page. We count down, since the order will be persisted from right
725                    // to left, starting after camera.
726                    addWidget(widgets[i], insertionIndex, true);
727                }
728            }
729        }
730    }
731
732    private int allocateIdForDefaultAppWidget() {
733        int appWidgetId;
734        Resources res = getContext().getResources();
735        ComponentName defaultAppWidget = new ComponentName(
736                res.getString(R.string.widget_default_package_name),
737                res.getString(R.string.widget_default_class_name));
738
739        // Note: we don't support configuring the widget
740        appWidgetId = mAppWidgetHost.allocateAppWidgetId();
741
742        try {
743            mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget);
744        } catch (IllegalArgumentException e) {
745            Log.e(TAG, "Error when trying to bind default AppWidget: " + e);
746            mAppWidgetHost.deleteAppWidgetId(appWidgetId);
747            appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
748        }
749        return appWidgetId;
750    }
751
752    public void checkAppWidgetConsistency() {
753        final int childCount = mAppWidgetContainer.getChildCount();
754        boolean widgetPageExists = false;
755        for (int i = 0; i < childCount; i++) {
756            if (mAppWidgetContainer.isWidgetPage(i)) {
757                widgetPageExists = true;
758                break;
759            }
760        }
761        if (!widgetPageExists) {
762            final int insertPageIndex = getInsertPageIndex();
763
764            final boolean userAddedWidgetsEnabled = !widgetsDisabled();
765
766            boolean addedDefaultAppWidget = false;
767
768            if (!mSafeModeEnabled) {
769                if (userAddedWidgetsEnabled) {
770                    int appWidgetId = allocateIdForDefaultAppWidget();
771                    if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
772                        addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, true);
773                    }
774                } else {
775                    // note: even if widgetsDisabledByDpm() returns true, we still bind/create
776                    // the default appwidget if possible
777                    int appWidgetId = mLockPatternUtils.getFallbackAppWidgetId();
778                    if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
779                        appWidgetId = allocateIdForDefaultAppWidget();
780                        if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
781                            mLockPatternUtils.writeFallbackAppWidgetId(appWidgetId);
782                        }
783                    }
784                    if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
785                        addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, false);
786                        if (!addedDefaultAppWidget) {
787                            mAppWidgetHost.deleteAppWidgetId(appWidgetId);
788                            mLockPatternUtils.writeFallbackAppWidgetId(
789                                    AppWidgetManager.INVALID_APPWIDGET_ID);
790                        }
791                    }
792                }
793            }
794
795            // Use the built-in status/clock view if we can't inflate the default widget
796            if (!addedDefaultAppWidget) {
797                addDefaultStatusWidget(insertPageIndex);
798            }
799
800            // trigger DB updates only if user-added widgets are enabled
801            if (!mSafeModeEnabled && userAddedWidgetsEnabled) {
802                mAppWidgetContainer.onAddView(
803                        mAppWidgetContainer.getChildAt(insertPageIndex), insertPageIndex);
804            }
805        }
806    }
807
808    private final Runnable mSwitchPageRunnable = new Runnable() {
809        @Override
810        public void run() {
811           showAppropriateWidgetPage();
812        }
813    };
814
815    static class SavedState extends BaseSavedState {
816        int transportState;
817        int appWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
818        Rect insets = new Rect();
819
820        SavedState(Parcelable superState) {
821            super(superState);
822        }
823
824        private SavedState(Parcel in) {
825            super(in);
826            this.transportState = in.readInt();
827            this.appWidgetToShow = in.readInt();
828            this.insets = in.readParcelable(null);
829        }
830
831        @Override
832        public void writeToParcel(Parcel out, int flags) {
833            super.writeToParcel(out, flags);
834            out.writeInt(this.transportState);
835            out.writeInt(this.appWidgetToShow);
836            out.writeParcelable(insets, 0);
837        }
838
839        public static final Parcelable.Creator<SavedState> CREATOR
840                = new Parcelable.Creator<SavedState>() {
841            public SavedState createFromParcel(Parcel in) {
842                return new SavedState(in);
843            }
844
845            public SavedState[] newArray(int size) {
846                return new SavedState[size];
847            }
848        };
849    }
850
851    @Override
852    public Parcelable onSaveInstanceState() {
853        if (DEBUG) Log.d(TAG, "onSaveInstanceState, tstate=" + mTransportState);
854        Parcelable superState = super.onSaveInstanceState();
855        SavedState ss = new SavedState(superState);
856        // If the transport is showing, force it to show it on restore.
857        final boolean showing = mTransportControl != null
858                && mAppWidgetContainer.getWidgetPageIndex(mTransportControl) >= 0;
859        ss.transportState =  showing ? TRANSPORT_VISIBLE : mTransportState;
860        ss.appWidgetToShow = mAppWidgetToShow;
861        ss.insets.set(mInsets);
862        return ss;
863    }
864
865    @Override
866    public void onRestoreInstanceState(Parcelable state) {
867        if (!(state instanceof SavedState)) {
868            super.onRestoreInstanceState(state);
869            return;
870        }
871        SavedState ss = (SavedState) state;
872        super.onRestoreInstanceState(ss.getSuperState());
873        mTransportState = (ss.transportState);
874        mAppWidgetToShow = ss.appWidgetToShow;
875        setInsets(ss.insets);
876        if (DEBUG) Log.d(TAG, "onRestoreInstanceState, transport=" + mTransportState);
877        mSwitchPageRunnable.run();
878    }
879
880    @Override
881    protected boolean fitSystemWindows(Rect insets) {
882        setInsets(insets);
883        return true;
884    }
885
886    private void setInsets(Rect insets) {
887        mInsets.set(insets);
888        if (mSlidingChallengeLayout != null) mSlidingChallengeLayout.setInsets(mInsets);
889        if (mMultiPaneChallengeLayout != null) mMultiPaneChallengeLayout.setInsets(mInsets);
890
891        final CameraWidgetFrame cameraWidget = findCameraPage();
892        if (cameraWidget != null) cameraWidget.setInsets(mInsets);
893    }
894
895    @Override
896    public void onWindowFocusChanged(boolean hasWindowFocus) {
897        super.onWindowFocusChanged(hasWindowFocus);
898        if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused"));
899        if (hasWindowFocus && mShowSecurityWhenReturn) {
900            SlidingChallengeLayout slider =
901                (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
902            if (slider != null) {
903                slider.setHandleAlpha(1);
904                slider.showChallenge(true);
905            }
906            mShowSecurityWhenReturn = false;
907        }
908    }
909
910    private void showAppropriateWidgetPage() {
911        final int state = mTransportState;
912        final boolean transportAdded = ensureTransportPresentOrRemoved(state);
913        final int pageToShow = getAppropriateWidgetPage(state);
914        if (!transportAdded) {
915            mAppWidgetContainer.setCurrentPage(pageToShow);
916        } else if (state == TRANSPORT_VISIBLE) {
917            // If the transport was just added, we need to wait for layout to happen before
918            // we can set the current page.
919            post(new Runnable() {
920                @Override
921                public void run() {
922                    mAppWidgetContainer.setCurrentPage(pageToShow);
923                }
924            });
925        }
926    }
927
928    /**
929     * Examines the current state and adds the transport to the widget pager when the state changes.
930     *
931     * Showing the initial transport and keeping it around is a bit tricky because the signals
932     * coming from music players aren't always clear. Here's how the states are handled:
933     *
934     * {@link TRANSPORT_GONE} means we have no reason to show the transport - remove it if present.
935     *
936     * {@link TRANSPORT_INVISIBLE} means we have potential to show the transport because a music
937     * player is registered but not currently playing music (or we don't know the state yet). The
938     * code adds it conditionally on play state.
939     *
940     * {@link #TRANSPORT_VISIBLE} means a music player is active and transport should be showing.
941     *
942     * Once the transport is showing, we always show it until keyguard is dismissed. This state is
943     * maintained by onSave/RestoreInstanceState(). This state is cleared in
944     * {@link KeyguardViewManager#hide} when keyguard is dismissed, which causes the transport to be
945     * gone when keyguard is restarted until we get an update with the current state.
946     *
947     * @param state
948     */
949    private boolean ensureTransportPresentOrRemoved(int state) {
950        final boolean showing = getWidgetPosition(R.id.keyguard_transport_control) != -1;
951        final boolean visible = state == TRANSPORT_VISIBLE;
952        final boolean shouldBeVisible = state == TRANSPORT_INVISIBLE && isMusicPlaying(state);
953        if (!showing && (visible || shouldBeVisible)) {
954            // insert to left of camera if it exists, otherwise after right-most widget
955            int lastWidget = mAppWidgetContainer.getChildCount() - 1;
956            int position = 0; // handle no widget case
957            if (lastWidget >= 0) {
958                position = mAppWidgetContainer.isCameraPage(lastWidget) ?
959                        lastWidget : lastWidget + 1;
960            }
961            if (DEBUGXPORT) Log.v(TAG, "add transport at " + position);
962            mAppWidgetContainer.addWidget(getOrCreateTransportControl(), position);
963            return true;
964        } else if (showing && state == TRANSPORT_GONE) {
965            if (DEBUGXPORT) Log.v(TAG, "remove transport");
966            mAppWidgetContainer.removeWidget(getOrCreateTransportControl());
967            mTransportControl = null;
968            KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(null);
969        }
970        return false;
971    }
972
973    private CameraWidgetFrame findCameraPage() {
974        for (int i = mAppWidgetContainer.getChildCount() - 1; i >= 0; i--) {
975            if (mAppWidgetContainer.isCameraPage(i)) {
976                return (CameraWidgetFrame) mAppWidgetContainer.getChildAt(i);
977            }
978        }
979        return null;
980    }
981
982    boolean isMusicPage(int pageIndex) {
983        return pageIndex >= 0 && pageIndex == getWidgetPosition(R.id.keyguard_transport_control);
984    }
985
986    private int getAppropriateWidgetPage(int musicTransportState) {
987        // assumes at least one widget (besides camera + add)
988        if (mAppWidgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
989            final int childCount = mAppWidgetContainer.getChildCount();
990            for (int i = 0; i < childCount; i++) {
991                if (mAppWidgetContainer.getWidgetPageAt(i).getContentAppWidgetId()
992                        == mAppWidgetToShow) {
993                    return i;
994                }
995            }
996            mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
997        }
998        // if music playing, show transport
999        if (musicTransportState == TRANSPORT_VISIBLE) {
1000            if (DEBUG) Log.d(TAG, "Music playing, show transport");
1001            return mAppWidgetContainer.getWidgetPageIndex(getOrCreateTransportControl());
1002        }
1003
1004        // else show the right-most widget (except for camera)
1005        int rightMost = mAppWidgetContainer.getChildCount() - 1;
1006        if (mAppWidgetContainer.isCameraPage(rightMost)) {
1007            rightMost--;
1008        }
1009        if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost);
1010        return rightMost;
1011    }
1012
1013    private void enableUserSelectorIfNecessary() {
1014        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1015        if (um == null) {
1016            Throwable t = new Throwable();
1017            t.fillInStackTrace();
1018            Log.e(TAG, "user service is null.", t);
1019            return;
1020        }
1021
1022        // if there are multiple users, we need to enable to multi-user switcher
1023        if (!um.isUserSwitcherEnabled()) {
1024            return;
1025        }
1026
1027        final View multiUserView = findViewById(R.id.keyguard_user_selector);
1028        if (multiUserView == null) {
1029            if (DEBUG) Log.d(TAG, "can't find user_selector in layout.");
1030            return;
1031        }
1032
1033        if (multiUserView instanceof KeyguardMultiUserSelectorView) {
1034            mKeyguardMultiUserSelectorView = (KeyguardMultiUserSelectorView) multiUserView;
1035            mKeyguardMultiUserSelectorView.setVisibility(View.VISIBLE);
1036            mKeyguardMultiUserSelectorView.addUsers(um.getUsers(true));
1037            UserSwitcherCallback callback = new UserSwitcherCallback() {
1038                @Override
1039                public void hideSecurityView(int duration) {
1040                    getSecurityContainer().animate().alpha(0).setDuration(duration);
1041                }
1042
1043                @Override
1044                public void showSecurityView() {
1045                    getSecurityContainer().setAlpha(1.0f);
1046                }
1047
1048                @Override
1049                public void showUnlockHint() {
1050                    if (getSecurityContainer() != null) {
1051                        getSecurityContainer().showUsabilityHint();
1052                    }
1053                }
1054
1055                @Override
1056                public void userActivity() {
1057                    if (mViewMediatorCallback != null) {
1058                        mViewMediatorCallback.userActivity();
1059                    }
1060                }
1061            };
1062            mKeyguardMultiUserSelectorView.setCallback(callback);
1063        } else {
1064            Throwable t = new Throwable();
1065            t.fillInStackTrace();
1066            if (multiUserView == null) {
1067                Log.e(TAG, "could not find the user_selector.", t);
1068            } else {
1069                Log.e(TAG, "user_selector is the wrong type.", t);
1070            }
1071        }
1072    }
1073
1074    @Override
1075    public void cleanUp() {
1076        // Make sure we let go of all widgets and their package contexts promptly. If we don't do
1077        // this, and the associated application is uninstalled, it can cause a soft reboot.
1078        int count = mAppWidgetContainer.getChildCount();
1079        for (int i = 0; i < count; i++) {
1080            KeyguardWidgetFrame frame = mAppWidgetContainer.getWidgetPageAt(i);
1081            frame.removeAllViews();
1082        }
1083        getSecurityContainer().onPause(); // clean up any actions in progress
1084    }
1085
1086    public void goToWidget(int appWidgetId) {
1087        mAppWidgetToShow = appWidgetId;
1088        mSwitchPageRunnable.run();
1089    }
1090
1091    @Override
1092    protected void showBouncer(boolean show) {
1093        super.showBouncer(show);
1094        mViewStateManager.showBouncer(show);
1095    }
1096
1097    @Override
1098    public void onExternalMotionEvent(MotionEvent event) {
1099        mAppWidgetContainer.handleExternalCameraEvent(event);
1100    }
1101
1102    @Override
1103    protected void onCreateOptions(Bundle options) {
1104        if (options != null) {
1105            int widgetToShow = options.getInt(LockPatternUtils.KEYGUARD_SHOW_APPWIDGET,
1106                    AppWidgetManager.INVALID_APPWIDGET_ID);
1107            if (widgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
1108                goToWidget(widgetToShow);
1109            }
1110        }
1111    }
1112
1113}
1114