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 {
hideSecurityView(int duration)105         void hideSecurityView(int duration);
showSecurityView()106         void showSecurityView();
showUnlockHint()107         void showUnlockHint();
userActivity()108         void userActivity();
109     }
110 
111     interface TransportControlCallback {
userActivity()112         void userActivity();
113     }
114 
115     public interface OnDismissAction {
116         /**
117          * @return true if the dismiss should be deferred
118          */
onDismiss()119         boolean onDismiss();
120     }
121 
KeyguardHostView(Context context)122     public KeyguardHostView(Context context) {
123         this(context, null);
124     }
125 
KeyguardHostView(Context context, AttributeSet attrs)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 
getInitialTransportState()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 
cleanupAppWidgetIds()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 
contains(int[] array, int target)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 
isMusicPlaying(int playbackState)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
onTouchEvent(MotionEvent ev)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 
getWidgetPosition(int id)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
onFinishInflate()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 
updateAndAddWidgets()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 
maybeEnableAddButton()352     private void maybeEnableAddButton() {
353         if (!shouldEnableAddWidget()) {
354             mAppWidgetContainer.setAddWidgetEnabled(false);
355         }
356     }
357 
shouldEnableAddWidget()358     private boolean shouldEnableAddWidget() {
359         return numWidgets() < MAX_WIDGETS && mUserSetupCompleted;
360     }
361 
362     @Override
dismiss(boolean authenticated)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 
getDisabledFeatures(DevicePolicyManager dpm)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 
widgetsDisabled()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 
cameraDisabledByDpm()406     private boolean cameraDisabledByDpm() {
407         return mCameraDisabled
408                 || (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0;
409     }
410 
411     @Override
setLockPatternUtils(LockPatternUtils utils)412     public void setLockPatternUtils(LockPatternUtils utils) {
413         super.setLockPatternUtils(utils);
414         getSecurityContainer().updateSecurityViews(mViewStateManager.isBouncing());
415     }
416 
417     @Override
onAttachedToWindow()418     protected void onAttachedToWindow() {
419         super.onAttachedToWindow();
420         mAppWidgetHost.startListening();
421         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks);
422     }
423 
424     @Override
onDetachedFromWindow()425     protected void onDetachedFromWindow() {
426         super.onDetachedFromWindow();
427         mAppWidgetHost.stopListening();
428         KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
429     }
430 
addWidget(AppWidgetHostView view, int pageIndex)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
onUserSwitching(boolean switching)474     public void onUserSwitching(boolean switching) {
475         if (!switching && mKeyguardMultiUserSelectorView != null) {
476             mKeyguardMultiUserSelectorView.finalizeActiveUserView(false);
477         }
478     }
479 
userActivity()480     public void userActivity() {
481         if (mViewMediatorCallback != null) {
482             mViewMediatorCallback.userActivity();
483         }
484     }
485 
onUserActivityTimeoutChanged()486     public void onUserActivityTimeoutChanged() {
487         if (mViewMediatorCallback != null) {
488             mViewMediatorCallback.onUserActivityTimeoutChanged();
489         }
490     }
491 
492     @Override
getUserActivityTimeout()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 
MyOnClickHandler(KeyguardHostView hostView)510         MyOnClickHandler(KeyguardHostView hostView) {
511             mKeyguardHostView = new WeakReference<KeyguardHostView>(hostView);
512         }
513 
514         @Override
onClickHandler(final View view, final android.app.PendingIntent pendingIntent, final Intent fillInIntent)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
onResume()558     public void onResume() {
559         super.onResume();
560         if (mViewStateManager != null) {
561             mViewStateManager.showUsabilityHints();
562         }
563     }
564 
565     @Override
onPause()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 
clearAppWidgetToShow()580     public void clearAppWidgetToShow() {
581         mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
582     }
583 
addWidget(int appId, int pageIndex, boolean updateDbIfFailed)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 
numWidgets()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 
addDefaultWidgets()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      */
getOrCreateTransportControl()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 
getInsertPageIndex()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 
addDefaultStatusWidget(int index)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 
addWidgetsFromSettings()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 
allocateIdForDefaultAppWidget()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 
checkAppWidgetConsistency()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 
SavedState(Parcelable superState)820         SavedState(Parcelable superState) {
821             super(superState);
822         }
823 
SavedState(Parcel in)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
writeToParcel(Parcel out, int flags)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
onSaveInstanceState()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
onRestoreInstanceState(Parcelable state)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
fitSystemWindows(Rect insets)881     protected boolean fitSystemWindows(Rect insets) {
882         setInsets(insets);
883         return true;
884     }
885 
setInsets(Rect insets)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
onWindowFocusChanged(boolean hasWindowFocus)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 
showAppropriateWidgetPage()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      */
ensureTransportPresentOrRemoved(int state)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 
findCameraPage()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 
isMusicPage(int pageIndex)982     boolean isMusicPage(int pageIndex) {
983         return pageIndex >= 0 && pageIndex == getWidgetPosition(R.id.keyguard_transport_control);
984     }
985 
getAppropriateWidgetPage(int musicTransportState)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 
enableUserSelectorIfNecessary()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
cleanUp()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 
goToWidget(int appWidgetId)1086     public void goToWidget(int appWidgetId) {
1087         mAppWidgetToShow = appWidgetId;
1088         mSwitchPageRunnable.run();
1089     }
1090 
1091     @Override
showBouncer(boolean show)1092     protected void showBouncer(boolean show) {
1093         super.showBouncer(show);
1094         mViewStateManager.showBouncer(show);
1095     }
1096 
1097     @Override
onExternalMotionEvent(MotionEvent event)1098     public void onExternalMotionEvent(MotionEvent event) {
1099         mAppWidgetContainer.handleExternalCameraEvent(event);
1100     }
1101 
1102     @Override
onCreateOptions(Bundle options)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