Home | History | Annotate | Download | only in keyguard
      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 
     17 package com.android.keyguard;
     18 
     19 import com.android.internal.widget.LockPatternUtils;
     20 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
     21 import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState;
     22 
     23 import android.app.ActivityManager;
     24 import android.app.ActivityOptions;
     25 import android.app.admin.DevicePolicyManager;
     26 import android.appwidget.AppWidgetHost;
     27 import android.appwidget.AppWidgetHostView;
     28 import android.appwidget.AppWidgetManager;
     29 import android.appwidget.AppWidgetProviderInfo;
     30 import android.content.ComponentName;
     31 import android.content.Context;
     32 import android.content.Intent;
     33 import android.content.IntentSender;
     34 import android.content.pm.PackageManager.NameNotFoundException;
     35 import android.content.res.Resources;
     36 import android.graphics.Rect;
     37 import android.media.RemoteControlClient;
     38 import android.os.Bundle;
     39 import android.os.Looper;
     40 import android.os.Parcel;
     41 import android.os.Parcelable;
     42 import android.os.UserHandle;
     43 import android.os.UserManager;
     44 import android.provider.Settings;
     45 import android.util.AttributeSet;
     46 import android.util.Log;
     47 import android.util.Slog;
     48 import android.view.LayoutInflater;
     49 import android.view.MotionEvent;
     50 import android.view.View;
     51 import android.widget.RemoteViews.OnClickHandler;
     52 
     53 import java.lang.ref.WeakReference;
     54 
     55 public 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