1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar.phone;
18
19import android.content.ComponentCallbacks2;
20import android.content.Context;
21import android.os.Bundle;
22import android.os.SystemClock;
23import android.os.Trace;
24import android.view.KeyEvent;
25import android.view.View;
26import android.view.ViewGroup;
27import android.view.ViewRootImpl;
28import android.view.WindowManagerGlobal;
29
30import com.android.internal.widget.LockPatternUtils;
31import com.android.keyguard.KeyguardUpdateMonitor;
32import com.android.keyguard.KeyguardUpdateMonitorCallback;
33import com.android.keyguard.ViewMediatorCallback;
34import com.android.systemui.DejankUtils;
35import com.android.keyguard.LatencyTracker;
36import com.android.systemui.Dependency;
37import com.android.systemui.SystemUIFactory;
38import com.android.systemui.keyguard.DismissCallbackRegistry;
39import com.android.systemui.statusbar.CommandQueue;
40import com.android.systemui.statusbar.RemoteInputController;
41
42import static com.android.keyguard.KeyguardHostView.OnDismissAction;
43import static com.android.systemui.statusbar.phone.FingerprintUnlockController.*;
44
45import java.util.ArrayList;
46
47/**
48 * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
49 * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
50 * which is in turn, reported to this class by the current
51 * {@link com.android.keyguard.KeyguardViewBase}.
52 */
53public class StatusBarKeyguardViewManager implements RemoteInputController.Callback {
54
55    // When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
56    private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3;
57
58    // Delay for showing the navigation bar when the bouncer appears. This should be kept in sync
59    // with the appear animations of the PIN/pattern/password views.
60    private static final long NAV_BAR_SHOW_DELAY_BOUNCER = 320;
61
62    private static final long WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS = 200;
63
64    // Duration of the Keyguard dismissal animation in case the user is currently locked. This is to
65    // make everything a bit slower to bridge a gap until the user is unlocked and home screen has
66    // dranw its first frame.
67    private static final long KEYGUARD_DISMISS_DURATION_LOCKED = 2000;
68
69    private static String TAG = "StatusBarKeyguardViewManager";
70
71    protected final Context mContext;
72    private final StatusBarWindowManager mStatusBarWindowManager;
73
74    protected LockPatternUtils mLockPatternUtils;
75    protected ViewMediatorCallback mViewMediatorCallback;
76    protected StatusBar mStatusBar;
77    private ScrimController mScrimController;
78    private FingerprintUnlockController mFingerprintUnlockController;
79
80    private ViewGroup mContainer;
81
82    private boolean mDeviceInteractive = false;
83    private boolean mScreenTurnedOn;
84    protected KeyguardBouncer mBouncer;
85    protected boolean mShowing;
86    protected boolean mOccluded;
87    protected boolean mRemoteInputActive;
88
89    protected boolean mFirstUpdate = true;
90    protected boolean mLastShowing;
91    protected boolean mLastOccluded;
92    private boolean mLastBouncerShowing;
93    private boolean mLastBouncerDismissible;
94    protected boolean mLastRemoteInputActive;
95    private boolean mLastDeferScrimFadeOut;
96
97    private OnDismissAction mAfterKeyguardGoneAction;
98    private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
99    private boolean mDeviceWillWakeUp;
100    private boolean mDeferScrimFadeOut;
101
102    private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
103            new KeyguardUpdateMonitorCallback() {
104        @Override
105        public void onEmergencyCallAction() {
106
107            // Since we won't get a setOccluded call we have to reset the view manually such that
108            // the bouncer goes away.
109            if (mOccluded) {
110                reset(false /* hideBouncerWhenShowing */);
111            }
112        }
113    };
114
115    public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback,
116            LockPatternUtils lockPatternUtils) {
117        mContext = context;
118        mViewMediatorCallback = callback;
119        mLockPatternUtils = lockPatternUtils;
120        mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
121        KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
122    }
123
124    public void registerStatusBar(StatusBar statusBar,
125            ViewGroup container,
126            ScrimController scrimController,
127            FingerprintUnlockController fingerprintUnlockController,
128            DismissCallbackRegistry dismissCallbackRegistry) {
129        mStatusBar = statusBar;
130        mContainer = container;
131        mScrimController = scrimController;
132        mFingerprintUnlockController = fingerprintUnlockController;
133        mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
134                mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry);
135    }
136
137    /**
138     * Show the keyguard.  Will handle creating and attaching to the view manager
139     * lazily.
140     */
141    public void show(Bundle options) {
142        mShowing = true;
143        mStatusBarWindowManager.setKeyguardShowing(true);
144        mScrimController.abortKeyguardFadingOut();
145        reset(true /* hideBouncerWhenShowing */);
146    }
147
148    /**
149     * Shows the notification keyguard or the bouncer depending on
150     * {@link KeyguardBouncer#needsFullscreenBouncer()}.
151     */
152    protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
153        if (mBouncer.needsFullscreenBouncer()) {
154
155            // The keyguard might be showing (already). So we need to hide it.
156            mStatusBar.hideKeyguard();
157            mBouncer.show(true /* resetSecuritySelection */);
158        } else {
159            mStatusBar.showKeyguard();
160            if (hideBouncerWhenShowing) {
161                mBouncer.hide(false /* destroyView */);
162                mBouncer.prepare();
163            }
164        }
165    }
166
167    private void showBouncer() {
168        if (mShowing) {
169            mBouncer.show(false /* resetSecuritySelection */);
170        }
171        updateStates();
172    }
173
174    public void dismissWithAction(OnDismissAction r, Runnable cancelAction,
175            boolean afterKeyguardGone) {
176        if (mShowing) {
177            if (!afterKeyguardGone) {
178                mBouncer.showWithDismissAction(r, cancelAction);
179            } else {
180                mAfterKeyguardGoneAction = r;
181                mBouncer.show(false /* resetSecuritySelection */);
182            }
183        }
184        updateStates();
185    }
186
187    /**
188     * Adds a {@param runnable} to be executed after Keyguard is gone.
189     */
190    public void addAfterKeyguardGoneRunnable(Runnable runnable) {
191        mAfterKeyguardGoneRunnables.add(runnable);
192    }
193
194    /**
195     * Reset the state of the view.
196     */
197    public void reset(boolean hideBouncerWhenShowing) {
198        if (mShowing) {
199            if (mOccluded) {
200                mStatusBar.hideKeyguard();
201                mStatusBar.stopWaitingForKeyguardExit();
202                mBouncer.hide(false /* destroyView */);
203            } else {
204                showBouncerOrKeyguard(hideBouncerWhenShowing);
205            }
206            KeyguardUpdateMonitor.getInstance(mContext).sendKeyguardReset();
207            updateStates();
208        }
209    }
210
211    public void onStartedGoingToSleep() {
212        mStatusBar.onStartedGoingToSleep();
213    }
214
215    public void onFinishedGoingToSleep() {
216        mDeviceInteractive = false;
217        mStatusBar.onFinishedGoingToSleep();
218        mBouncer.onScreenTurnedOff();
219    }
220
221    public void onStartedWakingUp() {
222        Trace.beginSection("StatusBarKeyguardViewManager#onStartedWakingUp");
223        mDeviceInteractive = true;
224        mDeviceWillWakeUp = false;
225        mStatusBar.onStartedWakingUp();
226        Trace.endSection();
227    }
228
229    public void onScreenTurningOn() {
230        Trace.beginSection("StatusBarKeyguardViewManager#onScreenTurningOn");
231        mStatusBar.onScreenTurningOn();
232        Trace.endSection();
233    }
234
235    public boolean isScreenTurnedOn() {
236        return mScreenTurnedOn;
237    }
238
239    public void onScreenTurnedOn() {
240        Trace.beginSection("StatusBarKeyguardViewManager#onScreenTurnedOn");
241        mScreenTurnedOn = true;
242        if (mDeferScrimFadeOut) {
243            mDeferScrimFadeOut = false;
244            animateScrimControllerKeyguardFadingOut(0, WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
245                    true /* skipFirstFrame */);
246            updateStates();
247        }
248        mStatusBar.onScreenTurnedOn();
249        Trace.endSection();
250    }
251
252    @Override
253    public void onRemoteInputActive(boolean active) {
254        mRemoteInputActive = active;
255        updateStates();
256    }
257
258    public void onScreenTurnedOff() {
259        mScreenTurnedOn = false;
260        mStatusBar.onScreenTurnedOff();
261    }
262
263    public void notifyDeviceWakeUpRequested() {
264        mDeviceWillWakeUp = !mDeviceInteractive;
265    }
266
267    public void setNeedsInput(boolean needsInput) {
268        mStatusBarWindowManager.setKeyguardNeedsInput(needsInput);
269    }
270
271    public boolean isUnlockWithWallpaper() {
272        return mStatusBarWindowManager.isShowingWallpaper();
273    }
274
275    public void setOccluded(boolean occluded, boolean animate) {
276        if (occluded != mOccluded) {
277            mStatusBar.onKeyguardOccludedChanged(occluded);
278        }
279        if (occluded && !mOccluded && mShowing) {
280            if (mStatusBar.isInLaunchTransition()) {
281                mOccluded = true;
282                mStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
283                        new Runnable() {
284                            @Override
285                            public void run() {
286                                mStatusBarWindowManager.setKeyguardOccluded(mOccluded);
287                                reset(true /* hideBouncerWhenShowing */);
288                            }
289                        });
290                return;
291            }
292        }
293        mOccluded = occluded;
294        if (mShowing) {
295            mStatusBar.updateMediaMetaData(false, animate && !occluded);
296        }
297        mStatusBarWindowManager.setKeyguardOccluded(occluded);
298
299        // If Keyguard is reshown, don't hide the bouncer as it might just have been requested by
300        // a FLAG_DISMISS_KEYGUARD_ACTIVITY.
301        reset(false /* hideBouncerWhenShowing*/);
302        if (animate && !occluded && mShowing) {
303            mStatusBar.animateKeyguardUnoccluding();
304        }
305    }
306
307    public boolean isOccluded() {
308        return mOccluded;
309    }
310
311    /**
312     * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
313     * security view of the bouncer.
314     *
315     * @param finishRunnable the runnable to be run after the animation finished, or {@code null} if
316     *                       no action should be run
317     */
318    public void startPreHideAnimation(Runnable finishRunnable) {
319        if (mBouncer.isShowing()) {
320            mBouncer.startPreHideAnimation(finishRunnable);
321        } else if (finishRunnable != null) {
322            finishRunnable.run();
323        }
324    }
325
326    /**
327     * Hides the keyguard view
328     */
329    public void hide(long startTime, long fadeoutDuration) {
330        mShowing = false;
331
332        if (KeyguardUpdateMonitor.getInstance(mContext).needsSlowUnlockTransition()) {
333            fadeoutDuration = KEYGUARD_DISMISS_DURATION_LOCKED;
334        }
335        long uptimeMillis = SystemClock.uptimeMillis();
336        long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
337
338        if (mStatusBar.isInLaunchTransition() ) {
339            mStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
340                @Override
341                public void run() {
342                    mStatusBarWindowManager.setKeyguardShowing(false);
343                    mStatusBarWindowManager.setKeyguardFadingAway(true);
344                    mBouncer.hide(true /* destroyView */);
345                    updateStates();
346                    mScrimController.animateKeyguardFadingOut(
347                            StatusBar.FADE_KEYGUARD_START_DELAY,
348                            StatusBar.FADE_KEYGUARD_DURATION, null,
349                            false /* skipFirstFrame */);
350                }
351            }, new Runnable() {
352                @Override
353                public void run() {
354                    mStatusBar.hideKeyguard();
355                    mStatusBarWindowManager.setKeyguardFadingAway(false);
356                    mViewMediatorCallback.keyguardGone();
357                    executeAfterKeyguardGoneAction();
358                }
359            });
360        } else {
361            executeAfterKeyguardGoneAction();
362            boolean wakeUnlockPulsing =
363                    mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
364            if (wakeUnlockPulsing) {
365                delay = 0;
366                fadeoutDuration = 240;
367            }
368            mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
369            mFingerprintUnlockController.startKeyguardFadingAway();
370            mBouncer.hide(true /* destroyView */);
371            if (wakeUnlockPulsing) {
372                mStatusBarWindowManager.setKeyguardFadingAway(true);
373                mStatusBar.fadeKeyguardWhilePulsing();
374                animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration,
375                        mStatusBar::hideKeyguard, false /* skipFirstFrame */);
376            } else {
377                mFingerprintUnlockController.startKeyguardFadingAway();
378                mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
379                boolean staying = mStatusBar.hideKeyguard();
380                if (!staying) {
381                    mStatusBarWindowManager.setKeyguardFadingAway(true);
382                    if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK) {
383                        if (!mScreenTurnedOn) {
384                            mDeferScrimFadeOut = true;
385                        } else {
386
387                            // Screen is already on, don't defer with fading out.
388                            animateScrimControllerKeyguardFadingOut(0,
389                                    WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
390                                    true /* skipFirstFrame */);
391                        }
392                    } else {
393                        animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration,
394                                false /* skipFirstFrame */);
395                    }
396                } else {
397                    mScrimController.animateGoingToFullShade(delay, fadeoutDuration);
398                    mStatusBar.finishKeyguardFadingAway();
399                    mFingerprintUnlockController.finishKeyguardFadingAway();
400                }
401            }
402            updateStates();
403            mStatusBarWindowManager.setKeyguardShowing(false);
404            mViewMediatorCallback.keyguardGone();
405        }
406    }
407
408    public void onDensityOrFontScaleChanged() {
409        mBouncer.hide(true /* destroyView */);
410    }
411
412    private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
413            boolean skipFirstFrame) {
414        animateScrimControllerKeyguardFadingOut(delay, duration, null /* endRunnable */,
415                skipFirstFrame);
416    }
417
418    private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
419            final Runnable endRunnable, boolean skipFirstFrame) {
420        Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "Fading out", 0);
421        mScrimController.animateKeyguardFadingOut(delay, duration, new Runnable() {
422            @Override
423            public void run() {
424                if (endRunnable != null) {
425                    endRunnable.run();
426                }
427                mContainer.postDelayed(() -> mStatusBarWindowManager.setKeyguardFadingAway(false),
428                        100);
429                mStatusBar.finishKeyguardFadingAway();
430                mFingerprintUnlockController.finishKeyguardFadingAway();
431                WindowManagerGlobal.getInstance().trimMemory(
432                        ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
433                Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "Fading out", 0);
434            }
435        }, skipFirstFrame);
436        if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
437                && LatencyTracker.isEnabled(mContext)) {
438            DejankUtils.postAfterTraversal(() ->
439                    LatencyTracker.getInstance(mContext).onActionEnd(
440                            LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK));
441        }
442    }
443
444    private void executeAfterKeyguardGoneAction() {
445        if (mAfterKeyguardGoneAction != null) {
446            mAfterKeyguardGoneAction.onDismiss();
447            mAfterKeyguardGoneAction = null;
448        }
449        for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) {
450            mAfterKeyguardGoneRunnables.get(i).run();
451        }
452        mAfterKeyguardGoneRunnables.clear();
453    }
454
455    /**
456     * Dismisses the keyguard by going to the next screen or making it gone.
457     */
458    public void dismissAndCollapse() {
459        mStatusBar.executeRunnableDismissingKeyguard(null, null, true, false, true);
460    }
461
462    public void dismiss() {
463        showBouncer();
464    }
465
466    /**
467     * WARNING: This method might cause Binder calls.
468     */
469    public boolean isSecure() {
470        return mBouncer.isSecure();
471    }
472
473    /**
474     * @return Whether the keyguard is showing
475     */
476    public boolean isShowing() {
477        return mShowing;
478    }
479
480    /**
481     * Notifies this manager that the back button has been pressed.
482     *
483     * @return whether the back press has been handled
484     */
485    public boolean onBackPressed() {
486        if (mBouncer.isShowing()) {
487            mStatusBar.endAffordanceLaunch();
488            reset(true /* hideBouncerWhenShowing */);
489            return true;
490        }
491        return false;
492    }
493
494    public boolean isBouncerShowing() {
495        return mBouncer.isShowing();
496    }
497
498    private long getNavBarShowDelay() {
499        if (mStatusBar.isKeyguardFadingAway()) {
500            return mStatusBar.getKeyguardFadingAwayDelay();
501        } else {
502
503            // Keyguard is not going away, thus we are showing the navigation bar because the
504            // bouncer is appearing.
505            return NAV_BAR_SHOW_DELAY_BOUNCER;
506        }
507    }
508
509    private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
510        @Override
511        public void run() {
512            mStatusBar.getNavigationBarView().getRootView().setVisibility(View.VISIBLE);
513        }
514    };
515
516    protected void updateStates() {
517        int vis = mContainer.getSystemUiVisibility();
518        boolean showing = mShowing;
519        boolean occluded = mOccluded;
520        boolean bouncerShowing = mBouncer.isShowing();
521        boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
522        boolean remoteInputActive = mRemoteInputActive;
523
524        if ((bouncerDismissible || !showing || remoteInputActive) !=
525                (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
526                || mFirstUpdate) {
527            if (bouncerDismissible || !showing || remoteInputActive) {
528                mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK);
529            } else {
530                mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK);
531            }
532        }
533
534        boolean navBarVisible = isNavBarVisible();
535        boolean lastNavBarVisible = getLastNavBarVisible();
536        if (navBarVisible != lastNavBarVisible || mFirstUpdate) {
537            if (mStatusBar.getNavigationBarView() != null) {
538                if (navBarVisible) {
539                    long delay = getNavBarShowDelay();
540                    if (delay == 0) {
541                        mMakeNavigationBarVisibleRunnable.run();
542                    } else {
543                        mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
544                                delay);
545                    }
546                } else {
547                    mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
548                    mStatusBar.getNavigationBarView().getRootView().setVisibility(View.GONE);
549                }
550            }
551        }
552
553        if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
554            mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
555            mStatusBar.setBouncerShowing(bouncerShowing);
556            mScrimController.setBouncerShowing(bouncerShowing);
557        }
558
559        KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
560        if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
561            updateMonitor.onKeyguardVisibilityChanged(showing && !occluded);
562        }
563        if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
564            updateMonitor.sendKeyguardBouncerChanged(bouncerShowing);
565        }
566
567        mFirstUpdate = false;
568        mLastShowing = showing;
569        mLastOccluded = occluded;
570        mLastBouncerShowing = bouncerShowing;
571        mLastBouncerDismissible = bouncerDismissible;
572        mLastRemoteInputActive = remoteInputActive;
573        mLastDeferScrimFadeOut = mDeferScrimFadeOut;
574        mStatusBar.onKeyguardViewManagerStatesUpdated();
575    }
576
577    /**
578     * @return Whether the navigation bar should be made visible based on the current state.
579     */
580    protected boolean isNavBarVisible() {
581        return (!(mShowing && !mOccluded) || mBouncer.isShowing() || mRemoteInputActive)
582                && !mDeferScrimFadeOut;
583    }
584
585    /**
586     * @return Whether the navigation bar was made visible based on the last known state.
587     */
588    protected boolean getLastNavBarVisible() {
589        return (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing || mLastRemoteInputActive)
590                && !mLastDeferScrimFadeOut;
591    }
592
593    public boolean shouldDismissOnMenuPressed() {
594        return mBouncer.shouldDismissOnMenuPressed();
595    }
596
597    public boolean interceptMediaKey(KeyEvent event) {
598        return mBouncer.interceptMediaKey(event);
599    }
600
601    public void readyForKeyguardDone() {
602        mViewMediatorCallback.readyForKeyguardDone();
603    }
604
605    public boolean shouldDisableWindowAnimationsForUnlock() {
606        return mStatusBar.isInLaunchTransition();
607    }
608
609    public boolean isGoingToNotificationShade() {
610        return mStatusBar.isGoingToNotificationShade();
611    }
612
613    public boolean isSecure(int userId) {
614        return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
615    }
616
617    public void keyguardGoingAway() {
618        mStatusBar.keyguardGoingAway();
619    }
620
621    public void animateCollapsePanels(float speedUpFactor) {
622        mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */,
623                false /* delayed */, speedUpFactor);
624    }
625
626    /**
627     * Notifies that the user has authenticated by other means than using the bouncer, for example,
628     * fingerprint.
629     */
630    public void notifyKeyguardAuthenticated(boolean strongAuth) {
631        mBouncer.notifyKeyguardAuthenticated(strongAuth);
632    }
633
634    public void showBouncerMessage(String message, int color) {
635        mBouncer.showMessage(message, color);
636    }
637
638    public ViewRootImpl getViewRootImpl() {
639        return mStatusBar.getStatusBarView().getViewRootImpl();
640    }
641}
642