• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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 android.view;
18 
19 import com.android.internal.view.IInputMethodCallback;
20 import com.android.internal.view.IInputMethodSession;
21 
22 import android.graphics.Canvas;
23 import android.graphics.PixelFormat;
24 import android.graphics.PorterDuff;
25 import android.graphics.Rect;
26 import android.graphics.Region;
27 import android.os.*;
28 import android.os.Process;
29 import android.os.SystemProperties;
30 import android.util.AndroidRuntimeException;
31 import android.util.Config;
32 import android.util.DisplayMetrics;
33 import android.util.Log;
34 import android.util.EventLog;
35 import android.util.SparseArray;
36 import android.view.View.MeasureSpec;
37 import android.view.accessibility.AccessibilityEvent;
38 import android.view.accessibility.AccessibilityManager;
39 import android.view.inputmethod.InputConnection;
40 import android.view.inputmethod.InputMethodManager;
41 import android.widget.Scroller;
42 import android.content.pm.PackageManager;
43 import android.content.res.CompatibilityInfo;
44 import android.content.res.Resources;
45 import android.content.Context;
46 import android.app.ActivityManagerNative;
47 import android.Manifest;
48 import android.media.AudioManager;
49 
50 import java.lang.ref.WeakReference;
51 import java.io.IOException;
52 import java.io.OutputStream;
53 import java.util.ArrayList;
54 
55 import javax.microedition.khronos.egl.*;
56 import javax.microedition.khronos.opengles.*;
57 import static javax.microedition.khronos.opengles.GL10.*;
58 
59 /**
60  * The top of a view hierarchy, implementing the needed protocol between View
61  * and the WindowManager.  This is for the most part an internal implementation
62  * detail of {@link WindowManagerImpl}.
63  *
64  * {@hide}
65  */
66 @SuppressWarnings({"EmptyCatchBlock"})
67 public final class ViewRoot extends Handler implements ViewParent,
68         View.AttachInfo.Callbacks {
69     private static final String TAG = "ViewRoot";
70     private static final boolean DBG = false;
71     @SuppressWarnings({"ConstantConditionalExpression"})
72     private static final boolean LOCAL_LOGV = false ? Config.LOGD : Config.LOGV;
73     /** @noinspection PointlessBooleanExpression*/
74     private static final boolean DEBUG_DRAW = false || LOCAL_LOGV;
75     private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV;
76     private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV;
77     private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV;
78     private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;
79     private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
80     private static final boolean WATCH_POINTER = false;
81 
82     private static final boolean MEASURE_LATENCY = false;
83     private static LatencyTimer lt;
84 
85     /**
86      * Maximum time we allow the user to roll the trackball enough to generate
87      * a key event, before resetting the counters.
88      */
89     static final int MAX_TRACKBALL_DELAY = 250;
90 
91     static long sInstanceCount = 0;
92 
93     static IWindowSession sWindowSession;
94 
95     static final Object mStaticInit = new Object();
96     static boolean mInitialized = false;
97 
98     static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
99 
100     private static int sDrawTime;
101 
102     long mLastTrackballTime = 0;
103     final TrackballAxis mTrackballAxisX = new TrackballAxis();
104     final TrackballAxis mTrackballAxisY = new TrackballAxis();
105 
106     final int[] mTmpLocation = new int[2];
107 
108     final InputMethodCallback mInputMethodCallback;
109     final SparseArray<Object> mPendingEvents = new SparseArray<Object>();
110     int mPendingEventSeq = 0;
111 
112     final Thread mThread;
113 
114     final WindowLeaked mLocation;
115 
116     final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
117 
118     final W mWindow;
119 
120     View mView;
121     View mFocusedView;
122     View mRealFocusedView;  // this is not set to null in touch mode
123     int mViewVisibility;
124     boolean mAppVisible = true;
125 
126     final Region mTransparentRegion;
127     final Region mPreviousTransparentRegion;
128 
129     int mWidth;
130     int mHeight;
131     Rect mDirty; // will be a graphics.Region soon
132     boolean mIsAnimating;
133 
134     CompatibilityInfo.Translator mTranslator;
135 
136     final View.AttachInfo mAttachInfo;
137 
138     final Rect mTempRect; // used in the transaction to not thrash the heap.
139     final Rect mVisRect; // used to retrieve visible rect of focused view.
140 
141     boolean mTraversalScheduled;
142     boolean mWillDrawSoon;
143     boolean mLayoutRequested;
144     boolean mFirst;
145     boolean mReportNextDraw;
146     boolean mFullRedrawNeeded;
147     boolean mNewSurfaceNeeded;
148     boolean mHasHadWindowFocus;
149     boolean mLastWasImTarget;
150 
151     boolean mWindowAttributesChanged = false;
152 
153     // These can be accessed by any thread, must be protected with a lock.
154     // Surface can never be reassigned or cleared (use Surface.clear()).
155     private final Surface mSurface = new Surface();
156 
157     boolean mAdded;
158     boolean mAddedTouchMode;
159 
160     /*package*/ int mAddNesting;
161 
162     // These are accessed by multiple threads.
163     final Rect mWinFrame; // frame given by window manager.
164 
165     final Rect mPendingVisibleInsets = new Rect();
166     final Rect mPendingContentInsets = new Rect();
167     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
168             = new ViewTreeObserver.InternalInsetsInfo();
169 
170     boolean mScrollMayChange;
171     int mSoftInputMode;
172     View mLastScrolledFocus;
173     int mScrollY;
174     int mCurScrollY;
175     Scroller mScroller;
176 
177     EGL10 mEgl;
178     EGLDisplay mEglDisplay;
179     EGLContext mEglContext;
180     EGLSurface mEglSurface;
181     GL11 mGL;
182     Canvas mGlCanvas;
183     boolean mUseGL;
184     boolean mGlWanted;
185 
186     final ViewConfiguration mViewConfiguration;
187 
188     /**
189      * see {@link #playSoundEffect(int)}
190      */
191     AudioManager mAudioManager;
192 
193     private final int mDensity;
194 
getWindowSession(Looper mainLooper)195     public static IWindowSession getWindowSession(Looper mainLooper) {
196         synchronized (mStaticInit) {
197             if (!mInitialized) {
198                 try {
199                     InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
200                     sWindowSession = IWindowManager.Stub.asInterface(
201                             ServiceManager.getService("window"))
202                             .openSession(imm.getClient(), imm.getInputContext());
203                     mInitialized = true;
204                 } catch (RemoteException e) {
205                 }
206             }
207             return sWindowSession;
208         }
209     }
210 
ViewRoot(Context context)211     public ViewRoot(Context context) {
212         super();
213 
214         if (MEASURE_LATENCY && lt == null) {
215             lt = new LatencyTimer(100, 1000);
216         }
217 
218         ++sInstanceCount;
219 
220         // Initialize the statics when this class is first instantiated. This is
221         // done here instead of in the static block because Zygote does not
222         // allow the spawning of threads.
223         getWindowSession(context.getMainLooper());
224 
225         mThread = Thread.currentThread();
226         mLocation = new WindowLeaked(null);
227         mLocation.fillInStackTrace();
228         mWidth = -1;
229         mHeight = -1;
230         mDirty = new Rect();
231         mTempRect = new Rect();
232         mVisRect = new Rect();
233         mWinFrame = new Rect();
234         mWindow = new W(this, context);
235         mInputMethodCallback = new InputMethodCallback(this);
236         mViewVisibility = View.GONE;
237         mTransparentRegion = new Region();
238         mPreviousTransparentRegion = new Region();
239         mFirst = true; // true for the first time the view is added
240         mAdded = false;
241         mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
242         mViewConfiguration = ViewConfiguration.get(context);
243         mDensity = context.getResources().getDisplayMetrics().densityDpi;
244     }
245 
246     @Override
finalize()247     protected void finalize() throws Throwable {
248         super.finalize();
249         --sInstanceCount;
250     }
251 
getInstanceCount()252     public static long getInstanceCount() {
253         return sInstanceCount;
254     }
255 
256     // FIXME for perf testing only
257     private boolean mProfile = false;
258 
259     /**
260      * Call this to profile the next traversal call.
261      * FIXME for perf testing only. Remove eventually
262      */
profile()263     public void profile() {
264         mProfile = true;
265     }
266 
267     /**
268      * Indicates whether we are in touch mode. Calling this method triggers an IPC
269      * call and should be avoided whenever possible.
270      *
271      * @return True, if the device is in touch mode, false otherwise.
272      *
273      * @hide
274      */
isInTouchMode()275     static boolean isInTouchMode() {
276         if (mInitialized) {
277             try {
278                 return sWindowSession.getInTouchMode();
279             } catch (RemoteException e) {
280             }
281         }
282         return false;
283     }
284 
initializeGL()285     private void initializeGL() {
286         initializeGLInner();
287         int err = mEgl.eglGetError();
288         if (err != EGL10.EGL_SUCCESS) {
289             // give-up on using GL
290             destroyGL();
291             mGlWanted = false;
292         }
293     }
294 
initializeGLInner()295     private void initializeGLInner() {
296         final EGL10 egl = (EGL10) EGLContext.getEGL();
297         mEgl = egl;
298 
299         /*
300          * Get to the default display.
301          */
302         final EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
303         mEglDisplay = eglDisplay;
304 
305         /*
306          * We can now initialize EGL for that display
307          */
308         int[] version = new int[2];
309         egl.eglInitialize(eglDisplay, version);
310 
311         /*
312          * Specify a configuration for our opengl session
313          * and grab the first configuration that matches is
314          */
315         final int[] configSpec = {
316                 EGL10.EGL_RED_SIZE,      5,
317                 EGL10.EGL_GREEN_SIZE,    6,
318                 EGL10.EGL_BLUE_SIZE,     5,
319                 EGL10.EGL_DEPTH_SIZE,    0,
320                 EGL10.EGL_NONE
321         };
322         final EGLConfig[] configs = new EGLConfig[1];
323         final int[] num_config = new int[1];
324         egl.eglChooseConfig(eglDisplay, configSpec, configs, 1, num_config);
325         final EGLConfig config = configs[0];
326 
327         /*
328          * Create an OpenGL ES context. This must be done only once, an
329          * OpenGL context is a somewhat heavy object.
330          */
331         final EGLContext context = egl.eglCreateContext(eglDisplay, config,
332                 EGL10.EGL_NO_CONTEXT, null);
333         mEglContext = context;
334 
335         /*
336          * Create an EGL surface we can render into.
337          */
338         final EGLSurface surface = egl.eglCreateWindowSurface(eglDisplay, config, mHolder, null);
339         mEglSurface = surface;
340 
341         /*
342          * Before we can issue GL commands, we need to make sure
343          * the context is current and bound to a surface.
344          */
345         egl.eglMakeCurrent(eglDisplay, surface, surface, context);
346 
347         /*
348          * Get to the appropriate GL interface.
349          * This is simply done by casting the GL context to either
350          * GL10 or GL11.
351          */
352         final GL11 gl = (GL11) context.getGL();
353         mGL = gl;
354         mGlCanvas = new Canvas(gl);
355         mUseGL = true;
356     }
357 
destroyGL()358     private void destroyGL() {
359         // inform skia that the context is gone
360         nativeAbandonGlCaches();
361 
362         mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
363                 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
364         mEgl.eglDestroyContext(mEglDisplay, mEglContext);
365         mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
366         mEgl.eglTerminate(mEglDisplay);
367         mEglContext = null;
368         mEglSurface = null;
369         mEglDisplay = null;
370         mEgl = null;
371         mGlCanvas = null;
372         mGL = null;
373         mUseGL = false;
374     }
375 
checkEglErrors()376     private void checkEglErrors() {
377         if (mUseGL) {
378             int err = mEgl.eglGetError();
379             if (err != EGL10.EGL_SUCCESS) {
380                 // something bad has happened revert to
381                 // normal rendering.
382                 destroyGL();
383                 if (err != EGL11.EGL_CONTEXT_LOST) {
384                     // we'll try again if it was context lost
385                     mGlWanted = false;
386                 }
387             }
388         }
389     }
390 
391     /**
392      * We have one child
393      */
setView(View view, WindowManager.LayoutParams attrs, View panelParentView)394     public void setView(View view, WindowManager.LayoutParams attrs,
395             View panelParentView) {
396         synchronized (this) {
397             if (mView == null) {
398                 mView = view;
399                 mWindowAttributes.copyFrom(attrs);
400                 attrs = mWindowAttributes;
401                 Resources resources = mView.getContext().getResources();
402                 CompatibilityInfo compatibilityInfo = resources.getCompatibilityInfo();
403                 mTranslator = compatibilityInfo.getTranslator();
404 
405                 if (mTranslator != null || !compatibilityInfo.supportsScreen()) {
406                     mSurface.setCompatibleDisplayMetrics(resources.getDisplayMetrics(),
407                             mTranslator);
408                 }
409 
410                 boolean restore = false;
411                 if (mTranslator != null) {
412                     restore = true;
413                     attrs.backup();
414                     mTranslator.translateWindowLayout(attrs);
415                 }
416                 if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
417 
418                 if (!compatibilityInfo.supportsScreen()) {
419                     attrs.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
420                 }
421 
422                 mSoftInputMode = attrs.softInputMode;
423                 mWindowAttributesChanged = true;
424                 mAttachInfo.mRootView = view;
425                 mAttachInfo.mScalingRequired = mTranslator != null;
426                 mAttachInfo.mApplicationScale =
427                         mTranslator == null ? 1.0f : mTranslator.applicationScale;
428                 if (panelParentView != null) {
429                     mAttachInfo.mPanelParentWindowToken
430                             = panelParentView.getApplicationWindowToken();
431                 }
432                 mAdded = true;
433                 int res; /* = WindowManagerImpl.ADD_OKAY; */
434 
435                 // Schedule the first layout -before- adding to the window
436                 // manager, to make sure we do the relayout before receiving
437                 // any other events from the system.
438                 requestLayout();
439                 try {
440                     res = sWindowSession.add(mWindow, mWindowAttributes,
441                             getHostVisibility(), mAttachInfo.mContentInsets);
442                 } catch (RemoteException e) {
443                     mAdded = false;
444                     mView = null;
445                     mAttachInfo.mRootView = null;
446                     unscheduleTraversals();
447                     throw new RuntimeException("Adding window failed", e);
448                 } finally {
449                     if (restore) {
450                         attrs.restore();
451                     }
452                 }
453 
454                 if (mTranslator != null) {
455                     mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
456                 }
457                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
458                 mPendingVisibleInsets.set(0, 0, 0, 0);
459                 if (Config.LOGV) Log.v("ViewRoot", "Added window " + mWindow);
460                 if (res < WindowManagerImpl.ADD_OKAY) {
461                     mView = null;
462                     mAttachInfo.mRootView = null;
463                     mAdded = false;
464                     unscheduleTraversals();
465                     switch (res) {
466                         case WindowManagerImpl.ADD_BAD_APP_TOKEN:
467                         case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN:
468                             throw new WindowManagerImpl.BadTokenException(
469                                 "Unable to add window -- token " + attrs.token
470                                 + " is not valid; is your activity running?");
471                         case WindowManagerImpl.ADD_NOT_APP_TOKEN:
472                             throw new WindowManagerImpl.BadTokenException(
473                                 "Unable to add window -- token " + attrs.token
474                                 + " is not for an application");
475                         case WindowManagerImpl.ADD_APP_EXITING:
476                             throw new WindowManagerImpl.BadTokenException(
477                                 "Unable to add window -- app for token " + attrs.token
478                                 + " is exiting");
479                         case WindowManagerImpl.ADD_DUPLICATE_ADD:
480                             throw new WindowManagerImpl.BadTokenException(
481                                 "Unable to add window -- window " + mWindow
482                                 + " has already been added");
483                         case WindowManagerImpl.ADD_STARTING_NOT_NEEDED:
484                             // Silently ignore -- we would have just removed it
485                             // right away, anyway.
486                             return;
487                         case WindowManagerImpl.ADD_MULTIPLE_SINGLETON:
488                             throw new WindowManagerImpl.BadTokenException(
489                                 "Unable to add window " + mWindow +
490                                 " -- another window of this type already exists");
491                         case WindowManagerImpl.ADD_PERMISSION_DENIED:
492                             throw new WindowManagerImpl.BadTokenException(
493                                 "Unable to add window " + mWindow +
494                                 " -- permission denied for this window type");
495                     }
496                     throw new RuntimeException(
497                         "Unable to add window -- unknown error code " + res);
498                 }
499                 view.assignParent(this);
500                 mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0;
501                 mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0;
502             }
503         }
504     }
505 
getView()506     public View getView() {
507         return mView;
508     }
509 
getLocation()510     final WindowLeaked getLocation() {
511         return mLocation;
512     }
513 
setLayoutParams(WindowManager.LayoutParams attrs, boolean newView)514     void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
515         synchronized (this) {
516             int oldSoftInputMode = mWindowAttributes.softInputMode;
517             // preserve compatible window flag if exists.
518             int compatibleWindowFlag =
519                 mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
520             mWindowAttributes.copyFrom(attrs);
521             mWindowAttributes.flags |= compatibleWindowFlag;
522 
523             if (newView) {
524                 mSoftInputMode = attrs.softInputMode;
525                 requestLayout();
526             }
527             // Don't lose the mode we last auto-computed.
528             if ((attrs.softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
529                     == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
530                 mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode
531                         & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
532                         | (oldSoftInputMode
533                                 & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST);
534             }
535             mWindowAttributesChanged = true;
536             scheduleTraversals();
537         }
538     }
539 
handleAppVisibility(boolean visible)540     void handleAppVisibility(boolean visible) {
541         if (mAppVisible != visible) {
542             mAppVisible = visible;
543             scheduleTraversals();
544         }
545     }
546 
handleGetNewSurface()547     void handleGetNewSurface() {
548         mNewSurfaceNeeded = true;
549         mFullRedrawNeeded = true;
550         scheduleTraversals();
551     }
552 
553     /**
554      * {@inheritDoc}
555      */
requestLayout()556     public void requestLayout() {
557         checkThread();
558         mLayoutRequested = true;
559         scheduleTraversals();
560     }
561 
562     /**
563      * {@inheritDoc}
564      */
isLayoutRequested()565     public boolean isLayoutRequested() {
566         return mLayoutRequested;
567     }
568 
invalidateChild(View child, Rect dirty)569     public void invalidateChild(View child, Rect dirty) {
570         checkThread();
571         if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
572         if (mCurScrollY != 0 || mTranslator != null) {
573             mTempRect.set(dirty);
574             dirty = mTempRect;
575             if (mCurScrollY != 0) {
576                dirty.offset(0, -mCurScrollY);
577             }
578             if (mTranslator != null) {
579                 mTranslator.translateRectInAppWindowToScreen(dirty);
580             }
581             if (mAttachInfo.mScalingRequired) {
582                 dirty.inset(-1, -1);
583             }
584         }
585         mDirty.union(dirty);
586         if (!mWillDrawSoon) {
587             scheduleTraversals();
588         }
589     }
590 
getParent()591     public ViewParent getParent() {
592         return null;
593     }
594 
invalidateChildInParent(final int[] location, final Rect dirty)595     public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
596         invalidateChild(null, dirty);
597         return null;
598     }
599 
getChildVisibleRect(View child, Rect r, android.graphics.Point offset)600     public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
601         if (child != mView) {
602             throw new RuntimeException("child is not mine, honest!");
603         }
604         // Note: don't apply scroll offset, because we want to know its
605         // visibility in the virtual canvas being given to the view hierarchy.
606         return r.intersect(0, 0, mWidth, mHeight);
607     }
608 
bringChildToFront(View child)609     public void bringChildToFront(View child) {
610     }
611 
scheduleTraversals()612     public void scheduleTraversals() {
613         if (!mTraversalScheduled) {
614             mTraversalScheduled = true;
615             sendEmptyMessage(DO_TRAVERSAL);
616         }
617     }
618 
unscheduleTraversals()619     public void unscheduleTraversals() {
620         if (mTraversalScheduled) {
621             mTraversalScheduled = false;
622             removeMessages(DO_TRAVERSAL);
623         }
624     }
625 
getHostVisibility()626     int getHostVisibility() {
627         return mAppVisible ? mView.getVisibility() : View.GONE;
628     }
629 
performTraversals()630     private void performTraversals() {
631         // cache mView since it is used so much below...
632         final View host = mView;
633 
634         if (DBG) {
635             System.out.println("======================================");
636             System.out.println("performTraversals");
637             host.debug();
638         }
639 
640         if (host == null || !mAdded)
641             return;
642 
643         mTraversalScheduled = false;
644         mWillDrawSoon = true;
645         boolean windowResizesToFitContent = false;
646         boolean fullRedrawNeeded = mFullRedrawNeeded;
647         boolean newSurface = false;
648         WindowManager.LayoutParams lp = mWindowAttributes;
649 
650         int desiredWindowWidth;
651         int desiredWindowHeight;
652         int childWidthMeasureSpec;
653         int childHeightMeasureSpec;
654 
655         final View.AttachInfo attachInfo = mAttachInfo;
656 
657         final int viewVisibility = getHostVisibility();
658         boolean viewVisibilityChanged = mViewVisibility != viewVisibility
659                 || mNewSurfaceNeeded;
660 
661         float appScale = mAttachInfo.mApplicationScale;
662 
663         WindowManager.LayoutParams params = null;
664         if (mWindowAttributesChanged) {
665             mWindowAttributesChanged = false;
666             params = lp;
667         }
668         Rect frame = mWinFrame;
669         if (mFirst) {
670             fullRedrawNeeded = true;
671             mLayoutRequested = true;
672 
673             DisplayMetrics packageMetrics =
674                 mView.getContext().getResources().getDisplayMetrics();
675             desiredWindowWidth = packageMetrics.widthPixels;
676             desiredWindowHeight = packageMetrics.heightPixels;
677 
678             // For the very first time, tell the view hierarchy that it
679             // is attached to the window.  Note that at this point the surface
680             // object is not initialized to its backing store, but soon it
681             // will be (assuming the window is visible).
682             attachInfo.mSurface = mSurface;
683             attachInfo.mTranslucentWindow = lp.format != PixelFormat.OPAQUE;
684             attachInfo.mHasWindowFocus = false;
685             attachInfo.mWindowVisibility = viewVisibility;
686             attachInfo.mRecomputeGlobalAttributes = false;
687             attachInfo.mKeepScreenOn = false;
688             viewVisibilityChanged = false;
689             host.dispatchAttachedToWindow(attachInfo, 0);
690             //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
691 
692         } else {
693             desiredWindowWidth = frame.width();
694             desiredWindowHeight = frame.height();
695             if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
696                 if (DEBUG_ORIENTATION) Log.v("ViewRoot",
697                         "View " + host + " resized to: " + frame);
698                 fullRedrawNeeded = true;
699                 mLayoutRequested = true;
700                 windowResizesToFitContent = true;
701             }
702         }
703 
704         if (viewVisibilityChanged) {
705             attachInfo.mWindowVisibility = viewVisibility;
706             host.dispatchWindowVisibilityChanged(viewVisibility);
707             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
708                 if (mUseGL) {
709                     destroyGL();
710                 }
711             }
712             if (viewVisibility == View.GONE) {
713                 // After making a window gone, we will count it as being
714                 // shown for the first time the next time it gets focus.
715                 mHasHadWindowFocus = false;
716             }
717         }
718 
719         boolean insetsChanged = false;
720 
721         if (mLayoutRequested) {
722             // Execute enqueued actions on every layout in case a view that was detached
723             // enqueued an action after being detached
724             getRunQueue().executeActions(attachInfo.mHandler);
725 
726             if (mFirst) {
727                 host.fitSystemWindows(mAttachInfo.mContentInsets);
728                 // make sure touch mode code executes by setting cached value
729                 // to opposite of the added touch mode.
730                 mAttachInfo.mInTouchMode = !mAddedTouchMode;
731                 ensureTouchModeLocally(mAddedTouchMode);
732             } else {
733                 if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) {
734                     mAttachInfo.mContentInsets.set(mPendingContentInsets);
735                     host.fitSystemWindows(mAttachInfo.mContentInsets);
736                     insetsChanged = true;
737                     if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
738                             + mAttachInfo.mContentInsets);
739                 }
740                 if (!mAttachInfo.mVisibleInsets.equals(mPendingVisibleInsets)) {
741                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
742                     if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
743                             + mAttachInfo.mVisibleInsets);
744                 }
745                 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
746                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
747                     windowResizesToFitContent = true;
748 
749                     DisplayMetrics packageMetrics =
750                         mView.getContext().getResources().getDisplayMetrics();
751                     desiredWindowWidth = packageMetrics.widthPixels;
752                     desiredWindowHeight = packageMetrics.heightPixels;
753                 }
754             }
755 
756             childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
757             childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
758 
759             // Ask host how big it wants to be
760             if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v("ViewRoot",
761                     "Measuring " + host + " in display " + desiredWindowWidth
762                     + "x" + desiredWindowHeight + "...");
763             host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
764 
765             if (DBG) {
766                 System.out.println("======================================");
767                 System.out.println("performTraversals -- after measure");
768                 host.debug();
769             }
770         }
771 
772         if (attachInfo.mRecomputeGlobalAttributes) {
773             //Log.i(TAG, "Computing screen on!");
774             attachInfo.mRecomputeGlobalAttributes = false;
775             boolean oldVal = attachInfo.mKeepScreenOn;
776             attachInfo.mKeepScreenOn = false;
777             host.dispatchCollectViewAttributes(0);
778             if (attachInfo.mKeepScreenOn != oldVal) {
779                 params = lp;
780                 //Log.i(TAG, "Keep screen on changed: " + attachInfo.mKeepScreenOn);
781             }
782         }
783 
784         if (mFirst || attachInfo.mViewVisibilityChanged) {
785             attachInfo.mViewVisibilityChanged = false;
786             int resizeMode = mSoftInputMode &
787                     WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
788             // If we are in auto resize mode, then we need to determine
789             // what mode to use now.
790             if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
791                 final int N = attachInfo.mScrollContainers.size();
792                 for (int i=0; i<N; i++) {
793                     if (attachInfo.mScrollContainers.get(i).isShown()) {
794                         resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
795                     }
796                 }
797                 if (resizeMode == 0) {
798                     resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
799                 }
800                 if ((lp.softInputMode &
801                         WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) {
802                     lp.softInputMode = (lp.softInputMode &
803                             ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) |
804                             resizeMode;
805                     params = lp;
806                 }
807             }
808         }
809 
810         if (params != null && (host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
811             if (!PixelFormat.formatHasAlpha(params.format)) {
812                 params.format = PixelFormat.TRANSLUCENT;
813             }
814         }
815 
816         boolean windowShouldResize = mLayoutRequested && windowResizesToFitContent
817             && (mWidth != host.mMeasuredWidth || mHeight != host.mMeasuredHeight);
818 
819         final boolean computesInternalInsets =
820                 attachInfo.mTreeObserver.hasComputeInternalInsetsListeners();
821         boolean insetsPending = false;
822         int relayoutResult = 0;
823         if (mFirst || windowShouldResize || insetsChanged
824                 || viewVisibilityChanged || params != null) {
825 
826             if (viewVisibility == View.VISIBLE) {
827                 // If this window is giving internal insets to the window
828                 // manager, and it is being added or changing its visibility,
829                 // then we want to first give the window manager "fake"
830                 // insets to cause it to effectively ignore the content of
831                 // the window during layout.  This avoids it briefly causing
832                 // other windows to resize/move based on the raw frame of the
833                 // window, waiting until we can finish laying out this window
834                 // and get back to the window manager with the ultimately
835                 // computed insets.
836                 insetsPending = computesInternalInsets
837                         && (mFirst || viewVisibilityChanged);
838 
839                 if (mWindowAttributes.memoryType == WindowManager.LayoutParams.MEMORY_TYPE_GPU) {
840                     if (params == null) {
841                         params = mWindowAttributes;
842                     }
843                     mGlWanted = true;
844                 }
845             }
846 
847             boolean initialized = false;
848             boolean contentInsetsChanged = false;
849             boolean visibleInsetsChanged;
850             try {
851                 boolean hadSurface = mSurface.isValid();
852                 int fl = 0;
853                 if (params != null) {
854                     fl = params.flags;
855                     if (attachInfo.mKeepScreenOn) {
856                         params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
857                     }
858                 }
859                 if (DEBUG_LAYOUT) {
860                     Log.i(TAG, "host=w:" + host.mMeasuredWidth + ", h:" +
861                             host.mMeasuredHeight + ", params=" + params);
862                 }
863                 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
864 
865                 if (params != null) {
866                     params.flags = fl;
867                 }
868 
869                 if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString()
870                         + " content=" + mPendingContentInsets.toShortString()
871                         + " visible=" + mPendingVisibleInsets.toShortString()
872                         + " surface=" + mSurface);
873 
874                 contentInsetsChanged = !mPendingContentInsets.equals(
875                         mAttachInfo.mContentInsets);
876                 visibleInsetsChanged = !mPendingVisibleInsets.equals(
877                         mAttachInfo.mVisibleInsets);
878                 if (contentInsetsChanged) {
879                     mAttachInfo.mContentInsets.set(mPendingContentInsets);
880                     host.fitSystemWindows(mAttachInfo.mContentInsets);
881                     if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
882                             + mAttachInfo.mContentInsets);
883                 }
884                 if (visibleInsetsChanged) {
885                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
886                     if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
887                             + mAttachInfo.mVisibleInsets);
888                 }
889 
890                 if (!hadSurface) {
891                     if (mSurface.isValid()) {
892                         // If we are creating a new surface, then we need to
893                         // completely redraw it.  Also, when we get to the
894                         // point of drawing it we will hold off and schedule
895                         // a new traversal instead.  This is so we can tell the
896                         // window manager about all of the windows being displayed
897                         // before actually drawing them, so it can display then
898                         // all at once.
899                         newSurface = true;
900                         fullRedrawNeeded = true;
901                         mPreviousTransparentRegion.setEmpty();
902 
903                         if (mGlWanted && !mUseGL) {
904                             initializeGL();
905                             initialized = mGlCanvas != null;
906                         }
907                     }
908                 } else if (!mSurface.isValid()) {
909                     // If the surface has been removed, then reset the scroll
910                     // positions.
911                     mLastScrolledFocus = null;
912                     mScrollY = mCurScrollY = 0;
913                     if (mScroller != null) {
914                         mScroller.abortAnimation();
915                     }
916                 }
917             } catch (RemoteException e) {
918             }
919             if (DEBUG_ORIENTATION) Log.v(
920                     "ViewRoot", "Relayout returned: frame=" + frame + ", surface=" + mSurface);
921 
922             attachInfo.mWindowLeft = frame.left;
923             attachInfo.mWindowTop = frame.top;
924 
925             // !!FIXME!! This next section handles the case where we did not get the
926             // window size we asked for. We should avoid this by getting a maximum size from
927             // the window session beforehand.
928             mWidth = frame.width();
929             mHeight = frame.height();
930 
931             if (initialized) {
932                 mGlCanvas.setViewport((int) (mWidth * appScale + 0.5f),
933                         (int) (mHeight * appScale + 0.5f));
934             }
935 
936             boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
937                     (relayoutResult&WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE) != 0);
938             if (focusChangedDueToTouchMode || mWidth != host.mMeasuredWidth
939                     || mHeight != host.mMeasuredHeight || contentInsetsChanged) {
940                 childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
941                 childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
942 
943                 if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed!  mWidth="
944                         + mWidth + " measuredWidth=" + host.mMeasuredWidth
945                         + " mHeight=" + mHeight
946                         + " measuredHeight" + host.mMeasuredHeight
947                         + " coveredInsetsChanged=" + contentInsetsChanged);
948 
949                  // Ask host how big it wants to be
950                 host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
951 
952                 // Implementation of weights from WindowManager.LayoutParams
953                 // We just grow the dimensions as needed and re-measure if
954                 // needs be
955                 int width = host.mMeasuredWidth;
956                 int height = host.mMeasuredHeight;
957                 boolean measureAgain = false;
958 
959                 if (lp.horizontalWeight > 0.0f) {
960                     width += (int) ((mWidth - width) * lp.horizontalWeight);
961                     childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
962                             MeasureSpec.EXACTLY);
963                     measureAgain = true;
964                 }
965                 if (lp.verticalWeight > 0.0f) {
966                     height += (int) ((mHeight - height) * lp.verticalWeight);
967                     childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
968                             MeasureSpec.EXACTLY);
969                     measureAgain = true;
970                 }
971 
972                 if (measureAgain) {
973                     if (DEBUG_LAYOUT) Log.v(TAG,
974                             "And hey let's measure once more: width=" + width
975                             + " height=" + height);
976                     host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
977                 }
978 
979                 mLayoutRequested = true;
980             }
981         }
982 
983         final boolean didLayout = mLayoutRequested;
984         boolean triggerGlobalLayoutListener = didLayout
985                 || attachInfo.mRecomputeGlobalAttributes;
986         if (didLayout) {
987             mLayoutRequested = false;
988             mScrollMayChange = true;
989             if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(
990                 "ViewRoot", "Laying out " + host + " to (" +
991                 host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")");
992             long startTime = 0L;
993             if (Config.DEBUG && ViewDebug.profileLayout) {
994                 startTime = SystemClock.elapsedRealtime();
995             }
996             host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
997 
998             if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
999                 if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) {
1000                     throw new IllegalStateException("The view hierarchy is an inconsistent state,"
1001                             + "please refer to the logs with the tag "
1002                             + ViewDebug.CONSISTENCY_LOG_TAG + " for more infomation.");
1003                 }
1004             }
1005 
1006             if (Config.DEBUG && ViewDebug.profileLayout) {
1007                 EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime);
1008             }
1009 
1010             // By this point all views have been sized and positionned
1011             // We can compute the transparent area
1012 
1013             if ((host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
1014                 // start out transparent
1015                 // TODO: AVOID THAT CALL BY CACHING THE RESULT?
1016                 host.getLocationInWindow(mTmpLocation);
1017                 mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
1018                         mTmpLocation[0] + host.mRight - host.mLeft,
1019                         mTmpLocation[1] + host.mBottom - host.mTop);
1020 
1021                 host.gatherTransparentRegion(mTransparentRegion);
1022                 if (mTranslator != null) {
1023                     mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
1024                 }
1025 
1026                 if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
1027                     mPreviousTransparentRegion.set(mTransparentRegion);
1028                     // reconfigure window manager
1029                     try {
1030                         sWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
1031                     } catch (RemoteException e) {
1032                     }
1033                 }
1034             }
1035 
1036             if (DBG) {
1037                 System.out.println("======================================");
1038                 System.out.println("performTraversals -- after setFrame");
1039                 host.debug();
1040             }
1041         }
1042 
1043         if (triggerGlobalLayoutListener) {
1044             attachInfo.mRecomputeGlobalAttributes = false;
1045             attachInfo.mTreeObserver.dispatchOnGlobalLayout();
1046         }
1047 
1048         if (computesInternalInsets) {
1049             ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
1050             final Rect givenContent = attachInfo.mGivenInternalInsets.contentInsets;
1051             final Rect givenVisible = attachInfo.mGivenInternalInsets.visibleInsets;
1052             givenContent.left = givenContent.top = givenContent.right
1053                     = givenContent.bottom = givenVisible.left = givenVisible.top
1054                     = givenVisible.right = givenVisible.bottom = 0;
1055             attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
1056             Rect contentInsets = insets.contentInsets;
1057             Rect visibleInsets = insets.visibleInsets;
1058             if (mTranslator != null) {
1059                 contentInsets = mTranslator.getTranslatedContentInsets(contentInsets);
1060                 visibleInsets = mTranslator.getTranslatedVisbileInsets(visibleInsets);
1061             }
1062             if (insetsPending || !mLastGivenInsets.equals(insets)) {
1063                 mLastGivenInsets.set(insets);
1064                 try {
1065                     sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
1066                             contentInsets, visibleInsets);
1067                 } catch (RemoteException e) {
1068                 }
1069             }
1070         }
1071 
1072         if (mFirst) {
1073             // handle first focus request
1074             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
1075                     + mView.hasFocus());
1076             if (mView != null) {
1077                 if (!mView.hasFocus()) {
1078                     mView.requestFocus(View.FOCUS_FORWARD);
1079                     mFocusedView = mRealFocusedView = mView.findFocus();
1080                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
1081                             + mFocusedView);
1082                 } else {
1083                     mRealFocusedView = mView.findFocus();
1084                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
1085                             + mRealFocusedView);
1086                 }
1087             }
1088         }
1089 
1090         mFirst = false;
1091         mWillDrawSoon = false;
1092         mNewSurfaceNeeded = false;
1093         mViewVisibility = viewVisibility;
1094 
1095         if (mAttachInfo.mHasWindowFocus) {
1096             final boolean imTarget = WindowManager.LayoutParams
1097                     .mayUseInputMethod(mWindowAttributes.flags);
1098             if (imTarget != mLastWasImTarget) {
1099                 mLastWasImTarget = imTarget;
1100                 InputMethodManager imm = InputMethodManager.peekInstance();
1101                 if (imm != null && imTarget) {
1102                     imm.startGettingWindowFocus(mView);
1103                     imm.onWindowFocus(mView, mView.findFocus(),
1104                             mWindowAttributes.softInputMode,
1105                             !mHasHadWindowFocus, mWindowAttributes.flags);
1106                 }
1107             }
1108         }
1109 
1110         boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
1111 
1112         if (!cancelDraw && !newSurface) {
1113             mFullRedrawNeeded = false;
1114             draw(fullRedrawNeeded);
1115 
1116             if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
1117                     || mReportNextDraw) {
1118                 if (LOCAL_LOGV) {
1119                     Log.v("ViewRoot", "FINISHED DRAWING: " + mWindowAttributes.getTitle());
1120                 }
1121                 mReportNextDraw = false;
1122                 try {
1123                     sWindowSession.finishDrawing(mWindow);
1124                 } catch (RemoteException e) {
1125                 }
1126             }
1127         } else {
1128             // We were supposed to report when we are done drawing. Since we canceled the
1129             // draw, remember it here.
1130             if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
1131                 mReportNextDraw = true;
1132             }
1133             if (fullRedrawNeeded) {
1134                 mFullRedrawNeeded = true;
1135             }
1136             // Try again
1137             scheduleTraversals();
1138         }
1139     }
1140 
requestTransparentRegion(View child)1141     public void requestTransparentRegion(View child) {
1142         // the test below should not fail unless someone is messing with us
1143         checkThread();
1144         if (mView == child) {
1145             mView.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
1146             // Need to make sure we re-evaluate the window attributes next
1147             // time around, to ensure the window has the correct format.
1148             mWindowAttributesChanged = true;
1149         }
1150     }
1151 
1152     /**
1153      * Figures out the measure spec for the root view in a window based on it's
1154      * layout params.
1155      *
1156      * @param windowSize
1157      *            The available width or height of the window
1158      *
1159      * @param rootDimension
1160      *            The layout params for one dimension (width or height) of the
1161      *            window.
1162      *
1163      * @return The measure spec to use to measure the root view.
1164      */
getRootMeasureSpec(int windowSize, int rootDimension)1165     private int getRootMeasureSpec(int windowSize, int rootDimension) {
1166         int measureSpec;
1167         switch (rootDimension) {
1168 
1169         case ViewGroup.LayoutParams.FILL_PARENT:
1170             // Window can't resize. Force root view to be windowSize.
1171             measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
1172             break;
1173         case ViewGroup.LayoutParams.WRAP_CONTENT:
1174             // Window can resize. Set max size for root view.
1175             measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
1176             break;
1177         default:
1178             // Window wants to be an exact size. Force root view to be that size.
1179             measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
1180             break;
1181         }
1182         return measureSpec;
1183     }
1184 
draw(boolean fullRedrawNeeded)1185     private void draw(boolean fullRedrawNeeded) {
1186         Surface surface = mSurface;
1187         if (surface == null || !surface.isValid()) {
1188             return;
1189         }
1190 
1191         scrollToRectOrFocus(null, false);
1192 
1193         if (mAttachInfo.mViewScrollChanged) {
1194             mAttachInfo.mViewScrollChanged = false;
1195             mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
1196         }
1197 
1198         int yoff;
1199         final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();
1200         if (scrolling) {
1201             yoff = mScroller.getCurrY();
1202         } else {
1203             yoff = mScrollY;
1204         }
1205         if (mCurScrollY != yoff) {
1206             mCurScrollY = yoff;
1207             fullRedrawNeeded = true;
1208         }
1209         float appScale = mAttachInfo.mApplicationScale;
1210         boolean scalingRequired = mAttachInfo.mScalingRequired;
1211 
1212         Rect dirty = mDirty;
1213         if (mUseGL) {
1214             if (!dirty.isEmpty()) {
1215                 Canvas canvas = mGlCanvas;
1216                 if (mGL != null && canvas != null) {
1217                     mGL.glDisable(GL_SCISSOR_TEST);
1218                     mGL.glClearColor(0, 0, 0, 0);
1219                     mGL.glClear(GL_COLOR_BUFFER_BIT);
1220                     mGL.glEnable(GL_SCISSOR_TEST);
1221 
1222                     mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
1223                     mAttachInfo.mIgnoreDirtyState = true;
1224                     mView.mPrivateFlags |= View.DRAWN;
1225 
1226                     int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
1227                     try {
1228                         canvas.translate(0, -yoff);
1229                         if (mTranslator != null) {
1230                             mTranslator.translateCanvas(canvas);
1231                         }
1232                         canvas.setScreenDensity(scalingRequired
1233                                 ? DisplayMetrics.DENSITY_DEVICE : 0);
1234                         mView.draw(canvas);
1235                         if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
1236                             mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
1237                         }
1238                     } finally {
1239                         canvas.restoreToCount(saveCount);
1240                     }
1241 
1242                     mAttachInfo.mIgnoreDirtyState = false;
1243 
1244                     mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
1245                     checkEglErrors();
1246 
1247                     if (Config.DEBUG && ViewDebug.showFps) {
1248                         int now = (int)SystemClock.elapsedRealtime();
1249                         if (sDrawTime != 0) {
1250                             nativeShowFPS(canvas, now - sDrawTime);
1251                         }
1252                         sDrawTime = now;
1253                     }
1254                 }
1255             }
1256             if (scrolling) {
1257                 mFullRedrawNeeded = true;
1258                 scheduleTraversals();
1259             }
1260             return;
1261         }
1262 
1263         if (fullRedrawNeeded) {
1264             mAttachInfo.mIgnoreDirtyState = true;
1265             dirty.union(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
1266         }
1267 
1268         if (DEBUG_ORIENTATION || DEBUG_DRAW) {
1269             Log.v("ViewRoot", "Draw " + mView + "/"
1270                     + mWindowAttributes.getTitle()
1271                     + ": dirty={" + dirty.left + "," + dirty.top
1272                     + "," + dirty.right + "," + dirty.bottom + "} surface="
1273                     + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" +
1274                     appScale + ", width=" + mWidth + ", height=" + mHeight);
1275         }
1276 
1277         Canvas canvas;
1278         try {
1279             int left = dirty.left;
1280             int top = dirty.top;
1281             int right = dirty.right;
1282             int bottom = dirty.bottom;
1283             canvas = surface.lockCanvas(dirty);
1284 
1285             if (left != dirty.left || top != dirty.top || right != dirty.right ||
1286                     bottom != dirty.bottom) {
1287                 mAttachInfo.mIgnoreDirtyState = true;
1288             }
1289 
1290             // TODO: Do this in native
1291             canvas.setDensity(mDensity);
1292         } catch (Surface.OutOfResourcesException e) {
1293             Log.e("ViewRoot", "OutOfResourcesException locking surface", e);
1294             // TODO: we should ask the window manager to do something!
1295             // for now we just do nothing
1296             return;
1297         } catch (IllegalArgumentException e) {
1298             Log.e("ViewRoot", "IllegalArgumentException locking surface", e);
1299             // TODO: we should ask the window manager to do something!
1300             // for now we just do nothing
1301             return;
1302         }
1303 
1304         try {
1305             if (!dirty.isEmpty() || mIsAnimating) {
1306                 long startTime = 0L;
1307 
1308                 if (DEBUG_ORIENTATION || DEBUG_DRAW) {
1309                     Log.v("ViewRoot", "Surface " + surface + " drawing to bitmap w="
1310                             + canvas.getWidth() + ", h=" + canvas.getHeight());
1311                     //canvas.drawARGB(255, 255, 0, 0);
1312                 }
1313 
1314                 if (Config.DEBUG && ViewDebug.profileDrawing) {
1315                     startTime = SystemClock.elapsedRealtime();
1316                 }
1317 
1318                 // If this bitmap's format includes an alpha channel, we
1319                 // need to clear it before drawing so that the child will
1320                 // properly re-composite its drawing on a transparent
1321                 // background. This automatically respects the clip/dirty region
1322                 // or
1323                 // If we are applying an offset, we need to clear the area
1324                 // where the offset doesn't appear to avoid having garbage
1325                 // left in the blank areas.
1326                 if (!canvas.isOpaque() || yoff != 0) {
1327                     canvas.drawColor(0, PorterDuff.Mode.CLEAR);
1328                 }
1329 
1330                 dirty.setEmpty();
1331                 mIsAnimating = false;
1332                 mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
1333                 mView.mPrivateFlags |= View.DRAWN;
1334 
1335                 if (DEBUG_DRAW) {
1336                     Context cxt = mView.getContext();
1337                     Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
1338                             ", metrics=" + cxt.getResources().getDisplayMetrics() +
1339                             ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
1340                 }
1341                 int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
1342                 try {
1343                     canvas.translate(0, -yoff);
1344                     if (mTranslator != null) {
1345                         mTranslator.translateCanvas(canvas);
1346                     }
1347                     canvas.setScreenDensity(scalingRequired
1348                             ? DisplayMetrics.DENSITY_DEVICE : 0);
1349                     mView.draw(canvas);
1350                 } finally {
1351                     mAttachInfo.mIgnoreDirtyState = false;
1352                     canvas.restoreToCount(saveCount);
1353                 }
1354 
1355                 if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
1356                     mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
1357                 }
1358 
1359                 if (Config.DEBUG && ViewDebug.showFps) {
1360                     int now = (int)SystemClock.elapsedRealtime();
1361                     if (sDrawTime != 0) {
1362                         nativeShowFPS(canvas, now - sDrawTime);
1363                     }
1364                     sDrawTime = now;
1365                 }
1366 
1367                 if (Config.DEBUG && ViewDebug.profileDrawing) {
1368                     EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
1369                 }
1370             }
1371 
1372         } finally {
1373             surface.unlockCanvasAndPost(canvas);
1374         }
1375 
1376         if (LOCAL_LOGV) {
1377             Log.v("ViewRoot", "Surface " + surface + " unlockCanvasAndPost");
1378         }
1379 
1380         if (scrolling) {
1381             mFullRedrawNeeded = true;
1382             scheduleTraversals();
1383         }
1384     }
1385 
scrollToRectOrFocus(Rect rectangle, boolean immediate)1386     boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
1387         final View.AttachInfo attachInfo = mAttachInfo;
1388         final Rect ci = attachInfo.mContentInsets;
1389         final Rect vi = attachInfo.mVisibleInsets;
1390         int scrollY = 0;
1391         boolean handled = false;
1392 
1393         if (vi.left > ci.left || vi.top > ci.top
1394                 || vi.right > ci.right || vi.bottom > ci.bottom) {
1395             // We'll assume that we aren't going to change the scroll
1396             // offset, since we want to avoid that unless it is actually
1397             // going to make the focus visible...  otherwise we scroll
1398             // all over the place.
1399             scrollY = mScrollY;
1400             // We can be called for two different situations: during a draw,
1401             // to update the scroll position if the focus has changed (in which
1402             // case 'rectangle' is null), or in response to a
1403             // requestChildRectangleOnScreen() call (in which case 'rectangle'
1404             // is non-null and we just want to scroll to whatever that
1405             // rectangle is).
1406             View focus = mRealFocusedView;
1407 
1408             // When in touch mode, focus points to the previously focused view,
1409             // which may have been removed from the view hierarchy. The following
1410             // line checks whether the view is still in the hierarchy
1411             if (focus == null || focus.getParent() == null) {
1412                 mRealFocusedView = null;
1413                 return false;
1414             }
1415 
1416             if (focus != mLastScrolledFocus) {
1417                 // If the focus has changed, then ignore any requests to scroll
1418                 // to a rectangle; first we want to make sure the entire focus
1419                 // view is visible.
1420                 rectangle = null;
1421             }
1422             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus
1423                     + " rectangle=" + rectangle + " ci=" + ci
1424                     + " vi=" + vi);
1425             if (focus == mLastScrolledFocus && !mScrollMayChange
1426                     && rectangle == null) {
1427                 // Optimization: if the focus hasn't changed since last
1428                 // time, and no layout has happened, then just leave things
1429                 // as they are.
1430                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Keeping scroll y="
1431                         + mScrollY + " vi=" + vi.toShortString());
1432             } else if (focus != null) {
1433                 // We need to determine if the currently focused view is
1434                 // within the visible part of the window and, if not, apply
1435                 // a pan so it can be seen.
1436                 mLastScrolledFocus = focus;
1437                 mScrollMayChange = false;
1438                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?");
1439                 // Try to find the rectangle from the focus view.
1440                 if (focus.getGlobalVisibleRect(mVisRect, null)) {
1441                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Root w="
1442                             + mView.getWidth() + " h=" + mView.getHeight()
1443                             + " ci=" + ci.toShortString()
1444                             + " vi=" + vi.toShortString());
1445                     if (rectangle == null) {
1446                         focus.getFocusedRect(mTempRect);
1447                         if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Focus " + focus
1448                                 + ": focusRect=" + mTempRect.toShortString());
1449                         ((ViewGroup) mView).offsetDescendantRectToMyCoords(
1450                                 focus, mTempRect);
1451                         if (DEBUG_INPUT_RESIZE) Log.v(TAG,
1452                                 "Focus in window: focusRect="
1453                                 + mTempRect.toShortString()
1454                                 + " visRect=" + mVisRect.toShortString());
1455                     } else {
1456                         mTempRect.set(rectangle);
1457                         if (DEBUG_INPUT_RESIZE) Log.v(TAG,
1458                                 "Request scroll to rect: "
1459                                 + mTempRect.toShortString()
1460                                 + " visRect=" + mVisRect.toShortString());
1461                     }
1462                     if (mTempRect.intersect(mVisRect)) {
1463                         if (DEBUG_INPUT_RESIZE) Log.v(TAG,
1464                                 "Focus window visible rect: "
1465                                 + mTempRect.toShortString());
1466                         if (mTempRect.height() >
1467                                 (mView.getHeight()-vi.top-vi.bottom)) {
1468                             // If the focus simply is not going to fit, then
1469                             // best is probably just to leave things as-is.
1470                             if (DEBUG_INPUT_RESIZE) Log.v(TAG,
1471                                     "Too tall; leaving scrollY=" + scrollY);
1472                         } else if ((mTempRect.top-scrollY) < vi.top) {
1473                             scrollY -= vi.top - (mTempRect.top-scrollY);
1474                             if (DEBUG_INPUT_RESIZE) Log.v(TAG,
1475                                     "Top covered; scrollY=" + scrollY);
1476                         } else if ((mTempRect.bottom-scrollY)
1477                                 > (mView.getHeight()-vi.bottom)) {
1478                             scrollY += (mTempRect.bottom-scrollY)
1479                                     - (mView.getHeight()-vi.bottom);
1480                             if (DEBUG_INPUT_RESIZE) Log.v(TAG,
1481                                     "Bottom covered; scrollY=" + scrollY);
1482                         }
1483                         handled = true;
1484                     }
1485                 }
1486             }
1487         }
1488 
1489         if (scrollY != mScrollY) {
1490             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
1491                     + mScrollY + " , new=" + scrollY);
1492             if (!immediate) {
1493                 if (mScroller == null) {
1494                     mScroller = new Scroller(mView.getContext());
1495                 }
1496                 mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY);
1497             } else if (mScroller != null) {
1498                 mScroller.abortAnimation();
1499             }
1500             mScrollY = scrollY;
1501         }
1502 
1503         return handled;
1504     }
1505 
requestChildFocus(View child, View focused)1506     public void requestChildFocus(View child, View focused) {
1507         checkThread();
1508         if (mFocusedView != focused) {
1509             mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused);
1510             scheduleTraversals();
1511         }
1512         mFocusedView = mRealFocusedView = focused;
1513         if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now "
1514                 + mFocusedView);
1515     }
1516 
clearChildFocus(View child)1517     public void clearChildFocus(View child) {
1518         checkThread();
1519 
1520         View oldFocus = mFocusedView;
1521 
1522         if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus");
1523         mFocusedView = mRealFocusedView = null;
1524         if (mView != null && !mView.hasFocus()) {
1525             // If a view gets the focus, the listener will be invoked from requestChildFocus()
1526             if (!mView.requestFocus(View.FOCUS_FORWARD)) {
1527                 mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
1528             }
1529         } else if (oldFocus != null) {
1530             mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
1531         }
1532     }
1533 
1534 
focusableViewAvailable(View v)1535     public void focusableViewAvailable(View v) {
1536         checkThread();
1537 
1538         if (mView != null && !mView.hasFocus()) {
1539             v.requestFocus();
1540         } else {
1541             // the one case where will transfer focus away from the current one
1542             // is if the current view is a view group that prefers to give focus
1543             // to its children first AND the view is a descendant of it.
1544             mFocusedView = mView.findFocus();
1545             boolean descendantsHaveDibsOnFocus =
1546                     (mFocusedView instanceof ViewGroup) &&
1547                         (((ViewGroup) mFocusedView).getDescendantFocusability() ==
1548                                 ViewGroup.FOCUS_AFTER_DESCENDANTS);
1549             if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
1550                 // If a view gets the focus, the listener will be invoked from requestChildFocus()
1551                 v.requestFocus();
1552             }
1553         }
1554     }
1555 
recomputeViewAttributes(View child)1556     public void recomputeViewAttributes(View child) {
1557         checkThread();
1558         if (mView == child) {
1559             mAttachInfo.mRecomputeGlobalAttributes = true;
1560             if (!mWillDrawSoon) {
1561                 scheduleTraversals();
1562             }
1563         }
1564     }
1565 
dispatchDetachedFromWindow()1566     void dispatchDetachedFromWindow() {
1567         if (Config.LOGV) Log.v("ViewRoot", "Detaching in " + this + " of " + mSurface);
1568 
1569         if (mView != null) {
1570             mView.dispatchDetachedFromWindow();
1571         }
1572 
1573         mView = null;
1574         mAttachInfo.mRootView = null;
1575         mAttachInfo.mSurface = null;
1576 
1577         if (mUseGL) {
1578             destroyGL();
1579         }
1580         mSurface.release();
1581 
1582         try {
1583             sWindowSession.remove(mWindow);
1584         } catch (RemoteException e) {
1585         }
1586     }
1587 
1588     /**
1589      * Return true if child is an ancestor of parent, (or equal to the parent).
1590      */
isViewDescendantOf(View child, View parent)1591     private static boolean isViewDescendantOf(View child, View parent) {
1592         if (child == parent) {
1593             return true;
1594         }
1595 
1596         final ViewParent theParent = child.getParent();
1597         return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
1598     }
1599 
1600 
1601     public final static int DO_TRAVERSAL = 1000;
1602     public final static int DIE = 1001;
1603     public final static int RESIZED = 1002;
1604     public final static int RESIZED_REPORT = 1003;
1605     public final static int WINDOW_FOCUS_CHANGED = 1004;
1606     public final static int DISPATCH_KEY = 1005;
1607     public final static int DISPATCH_POINTER = 1006;
1608     public final static int DISPATCH_TRACKBALL = 1007;
1609     public final static int DISPATCH_APP_VISIBILITY = 1008;
1610     public final static int DISPATCH_GET_NEW_SURFACE = 1009;
1611     public final static int FINISHED_EVENT = 1010;
1612     public final static int DISPATCH_KEY_FROM_IME = 1011;
1613     public final static int FINISH_INPUT_CONNECTION = 1012;
1614     public final static int CHECK_FOCUS = 1013;
1615     public final static int CLOSE_SYSTEM_DIALOGS = 1014;
1616 
1617     @Override
handleMessage(Message msg)1618     public void handleMessage(Message msg) {
1619         switch (msg.what) {
1620         case View.AttachInfo.INVALIDATE_MSG:
1621             ((View) msg.obj).invalidate();
1622             break;
1623         case View.AttachInfo.INVALIDATE_RECT_MSG:
1624             final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
1625             info.target.invalidate(info.left, info.top, info.right, info.bottom);
1626             info.release();
1627             break;
1628         case DO_TRAVERSAL:
1629             if (mProfile) {
1630                 Debug.startMethodTracing("ViewRoot");
1631             }
1632 
1633             performTraversals();
1634 
1635             if (mProfile) {
1636                 Debug.stopMethodTracing();
1637                 mProfile = false;
1638             }
1639             break;
1640         case FINISHED_EVENT:
1641             handleFinishedEvent(msg.arg1, msg.arg2 != 0);
1642             break;
1643         case DISPATCH_KEY:
1644             if (LOCAL_LOGV) Log.v(
1645                 "ViewRoot", "Dispatching key "
1646                 + msg.obj + " to " + mView);
1647             deliverKeyEvent((KeyEvent)msg.obj, true);
1648             break;
1649         case DISPATCH_POINTER: {
1650             MotionEvent event = (MotionEvent)msg.obj;
1651             boolean callWhenDone = msg.arg1 != 0;
1652 
1653             if (event == null) {
1654                 try {
1655                     long timeBeforeGettingEvents;
1656                     if (MEASURE_LATENCY) {
1657                         timeBeforeGettingEvents = System.nanoTime();
1658                     }
1659 
1660                     event = sWindowSession.getPendingPointerMove(mWindow);
1661 
1662                     if (MEASURE_LATENCY && event != null) {
1663                         lt.sample("9 Client got events      ", System.nanoTime() - event.getEventTimeNano());
1664                         lt.sample("8 Client getting events  ", timeBeforeGettingEvents - event.getEventTimeNano());
1665                     }
1666                 } catch (RemoteException e) {
1667                 }
1668                 callWhenDone = false;
1669             }
1670             if (event != null && mTranslator != null) {
1671                 mTranslator.translateEventInScreenToAppWindow(event);
1672             }
1673             try {
1674                 boolean handled;
1675                 if (mView != null && mAdded && event != null) {
1676 
1677                     // enter touch mode on the down
1678                     boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
1679                     if (isDown) {
1680                         ensureTouchMode(true);
1681                     }
1682                     if(Config.LOGV) {
1683                         captureMotionLog("captureDispatchPointer", event);
1684                     }
1685                     if (mCurScrollY != 0) {
1686                         event.offsetLocation(0, mCurScrollY);
1687                     }
1688                     if (MEASURE_LATENCY) {
1689                         lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
1690                     }
1691                     handled = mView.dispatchTouchEvent(event);
1692                     if (MEASURE_LATENCY) {
1693                         lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
1694                     }
1695                     if (!handled && isDown) {
1696                         int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
1697 
1698                         final int edgeFlags = event.getEdgeFlags();
1699                         int direction = View.FOCUS_UP;
1700                         int x = (int)event.getX();
1701                         int y = (int)event.getY();
1702                         final int[] deltas = new int[2];
1703 
1704                         if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) {
1705                             direction = View.FOCUS_DOWN;
1706                             if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
1707                                 deltas[0] = edgeSlop;
1708                                 x += edgeSlop;
1709                             } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
1710                                 deltas[0] = -edgeSlop;
1711                                 x -= edgeSlop;
1712                             }
1713                         } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) {
1714                             direction = View.FOCUS_UP;
1715                             if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
1716                                 deltas[0] = edgeSlop;
1717                                 x += edgeSlop;
1718                             } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
1719                                 deltas[0] = -edgeSlop;
1720                                 x -= edgeSlop;
1721                             }
1722                         } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
1723                             direction = View.FOCUS_RIGHT;
1724                         } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
1725                             direction = View.FOCUS_LEFT;
1726                         }
1727 
1728                         if (edgeFlags != 0 && mView instanceof ViewGroup) {
1729                             View nearest = FocusFinder.getInstance().findNearestTouchable(
1730                                     ((ViewGroup) mView), x, y, direction, deltas);
1731                             if (nearest != null) {
1732                                 event.offsetLocation(deltas[0], deltas[1]);
1733                                 event.setEdgeFlags(0);
1734                                 mView.dispatchTouchEvent(event);
1735                             }
1736                         }
1737                     }
1738                 }
1739             } finally {
1740                 if (callWhenDone) {
1741                     try {
1742                         sWindowSession.finishKey(mWindow);
1743                     } catch (RemoteException e) {
1744                     }
1745                 }
1746                 if (event != null) {
1747                     event.recycle();
1748                 }
1749                 if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
1750                 // Let the exception fall through -- the looper will catch
1751                 // it and take care of the bad app for us.
1752             }
1753         } break;
1754         case DISPATCH_TRACKBALL:
1755             deliverTrackballEvent((MotionEvent)msg.obj, msg.arg1 != 0);
1756             break;
1757         case DISPATCH_APP_VISIBILITY:
1758             handleAppVisibility(msg.arg1 != 0);
1759             break;
1760         case DISPATCH_GET_NEW_SURFACE:
1761             handleGetNewSurface();
1762             break;
1763         case RESIZED:
1764             Rect coveredInsets = ((Rect[])msg.obj)[0];
1765             Rect visibleInsets = ((Rect[])msg.obj)[1];
1766 
1767             if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2
1768                     && mPendingContentInsets.equals(coveredInsets)
1769                     && mPendingVisibleInsets.equals(visibleInsets)) {
1770                 break;
1771             }
1772             // fall through...
1773         case RESIZED_REPORT:
1774             if (mAdded) {
1775                 mWinFrame.left = 0;
1776                 mWinFrame.right = msg.arg1;
1777                 mWinFrame.top = 0;
1778                 mWinFrame.bottom = msg.arg2;
1779                 mPendingContentInsets.set(((Rect[])msg.obj)[0]);
1780                 mPendingVisibleInsets.set(((Rect[])msg.obj)[1]);
1781                 if (msg.what == RESIZED_REPORT) {
1782                     mReportNextDraw = true;
1783                 }
1784                 requestLayout();
1785             }
1786             break;
1787         case WINDOW_FOCUS_CHANGED: {
1788             if (mAdded) {
1789                 boolean hasWindowFocus = msg.arg1 != 0;
1790                 mAttachInfo.mHasWindowFocus = hasWindowFocus;
1791                 if (hasWindowFocus) {
1792                     boolean inTouchMode = msg.arg2 != 0;
1793                     ensureTouchModeLocally(inTouchMode);
1794 
1795                     if (mGlWanted) {
1796                         checkEglErrors();
1797                         // we lost the gl context, so recreate it.
1798                         if (mGlWanted && !mUseGL) {
1799                             initializeGL();
1800                             if (mGlCanvas != null) {
1801                                 float appScale = mAttachInfo.mApplicationScale;
1802                                 mGlCanvas.setViewport(
1803                                         (int) (mWidth * appScale + 0.5f),
1804                                         (int) (mHeight * appScale + 0.5f));
1805                             }
1806                         }
1807                     }
1808                 }
1809 
1810                 mLastWasImTarget = WindowManager.LayoutParams
1811                         .mayUseInputMethod(mWindowAttributes.flags);
1812 
1813                 InputMethodManager imm = InputMethodManager.peekInstance();
1814                 if (mView != null) {
1815                     if (hasWindowFocus && imm != null && mLastWasImTarget) {
1816                         imm.startGettingWindowFocus(mView);
1817                     }
1818                     mAttachInfo.mKeyDispatchState.reset();
1819                     mView.dispatchWindowFocusChanged(hasWindowFocus);
1820                 }
1821 
1822                 // Note: must be done after the focus change callbacks,
1823                 // so all of the view state is set up correctly.
1824                 if (hasWindowFocus) {
1825                     if (imm != null && mLastWasImTarget) {
1826                         imm.onWindowFocus(mView, mView.findFocus(),
1827                                 mWindowAttributes.softInputMode,
1828                                 !mHasHadWindowFocus, mWindowAttributes.flags);
1829                     }
1830                     // Clear the forward bit.  We can just do this directly, since
1831                     // the window manager doesn't care about it.
1832                     mWindowAttributes.softInputMode &=
1833                             ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
1834                     ((WindowManager.LayoutParams)mView.getLayoutParams())
1835                             .softInputMode &=
1836                                 ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
1837                     mHasHadWindowFocus = true;
1838                 }
1839 
1840                 if (hasWindowFocus && mView != null) {
1841                     sendAccessibilityEvents();
1842                 }
1843             }
1844         } break;
1845         case DIE:
1846             doDie();
1847             break;
1848         case DISPATCH_KEY_FROM_IME: {
1849             if (LOCAL_LOGV) Log.v(
1850                 "ViewRoot", "Dispatching key "
1851                 + msg.obj + " from IME to " + mView);
1852             KeyEvent event = (KeyEvent)msg.obj;
1853             if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
1854                 // The IME is trying to say this event is from the
1855                 // system!  Bad bad bad!
1856                 event = KeyEvent.changeFlags(event,
1857                         event.getFlags()&~KeyEvent.FLAG_FROM_SYSTEM);
1858             }
1859             deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false);
1860         } break;
1861         case FINISH_INPUT_CONNECTION: {
1862             InputMethodManager imm = InputMethodManager.peekInstance();
1863             if (imm != null) {
1864                 imm.reportFinishInputConnection((InputConnection)msg.obj);
1865             }
1866         } break;
1867         case CHECK_FOCUS: {
1868             InputMethodManager imm = InputMethodManager.peekInstance();
1869             if (imm != null) {
1870                 imm.checkFocus();
1871             }
1872         } break;
1873         case CLOSE_SYSTEM_DIALOGS: {
1874             if (mView != null) {
1875                 mView.onCloseSystemDialogs((String)msg.obj);
1876             }
1877         } break;
1878         }
1879     }
1880 
1881     /**
1882      * Something in the current window tells us we need to change the touch mode.  For
1883      * example, we are not in touch mode, and the user touches the screen.
1884      *
1885      * If the touch mode has changed, tell the window manager, and handle it locally.
1886      *
1887      * @param inTouchMode Whether we want to be in touch mode.
1888      * @return True if the touch mode changed and focus changed was changed as a result
1889      */
ensureTouchMode(boolean inTouchMode)1890     boolean ensureTouchMode(boolean inTouchMode) {
1891         if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current "
1892                 + "touch mode is " + mAttachInfo.mInTouchMode);
1893         if (mAttachInfo.mInTouchMode == inTouchMode) return false;
1894 
1895         // tell the window manager
1896         try {
1897             sWindowSession.setInTouchMode(inTouchMode);
1898         } catch (RemoteException e) {
1899             throw new RuntimeException(e);
1900         }
1901 
1902         // handle the change
1903         return ensureTouchModeLocally(inTouchMode);
1904     }
1905 
1906     /**
1907      * Ensure that the touch mode for this window is set, and if it is changing,
1908      * take the appropriate action.
1909      * @param inTouchMode Whether we want to be in touch mode.
1910      * @return True if the touch mode changed and focus changed was changed as a result
1911      */
ensureTouchModeLocally(boolean inTouchMode)1912     private boolean ensureTouchModeLocally(boolean inTouchMode) {
1913         if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current "
1914                 + "touch mode is " + mAttachInfo.mInTouchMode);
1915 
1916         if (mAttachInfo.mInTouchMode == inTouchMode) return false;
1917 
1918         mAttachInfo.mInTouchMode = inTouchMode;
1919         mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode);
1920 
1921         return (inTouchMode) ? enterTouchMode() : leaveTouchMode();
1922     }
1923 
enterTouchMode()1924     private boolean enterTouchMode() {
1925         if (mView != null) {
1926             if (mView.hasFocus()) {
1927                 // note: not relying on mFocusedView here because this could
1928                 // be when the window is first being added, and mFocused isn't
1929                 // set yet.
1930                 final View focused = mView.findFocus();
1931                 if (focused != null && !focused.isFocusableInTouchMode()) {
1932 
1933                     final ViewGroup ancestorToTakeFocus =
1934                             findAncestorToTakeFocusInTouchMode(focused);
1935                     if (ancestorToTakeFocus != null) {
1936                         // there is an ancestor that wants focus after its descendants that
1937                         // is focusable in touch mode.. give it focus
1938                         return ancestorToTakeFocus.requestFocus();
1939                     } else {
1940                         // nothing appropriate to have focus in touch mode, clear it out
1941                         mView.unFocus();
1942                         mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
1943                         mFocusedView = null;
1944                         return true;
1945                     }
1946                 }
1947             }
1948         }
1949         return false;
1950     }
1951 
1952 
1953     /**
1954      * Find an ancestor of focused that wants focus after its descendants and is
1955      * focusable in touch mode.
1956      * @param focused The currently focused view.
1957      * @return An appropriate view, or null if no such view exists.
1958      */
findAncestorToTakeFocusInTouchMode(View focused)1959     private ViewGroup findAncestorToTakeFocusInTouchMode(View focused) {
1960         ViewParent parent = focused.getParent();
1961         while (parent instanceof ViewGroup) {
1962             final ViewGroup vgParent = (ViewGroup) parent;
1963             if (vgParent.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS
1964                     && vgParent.isFocusableInTouchMode()) {
1965                 return vgParent;
1966             }
1967             if (vgParent.isRootNamespace()) {
1968                 return null;
1969             } else {
1970                 parent = vgParent.getParent();
1971             }
1972         }
1973         return null;
1974     }
1975 
leaveTouchMode()1976     private boolean leaveTouchMode() {
1977         if (mView != null) {
1978             if (mView.hasFocus()) {
1979                 // i learned the hard way to not trust mFocusedView :)
1980                 mFocusedView = mView.findFocus();
1981                 if (!(mFocusedView instanceof ViewGroup)) {
1982                     // some view has focus, let it keep it
1983                     return false;
1984                 } else if (((ViewGroup)mFocusedView).getDescendantFocusability() !=
1985                         ViewGroup.FOCUS_AFTER_DESCENDANTS) {
1986                     // some view group has focus, and doesn't prefer its children
1987                     // over itself for focus, so let them keep it.
1988                     return false;
1989                 }
1990             }
1991 
1992             // find the best view to give focus to in this brave new non-touch-mode
1993             // world
1994             final View focused = focusSearch(null, View.FOCUS_DOWN);
1995             if (focused != null) {
1996                 return focused.requestFocus(View.FOCUS_DOWN);
1997             }
1998         }
1999         return false;
2000     }
2001 
2002 
deliverTrackballEvent(MotionEvent event, boolean callWhenDone)2003     private void deliverTrackballEvent(MotionEvent event, boolean callWhenDone) {
2004         if (event == null) {
2005             try {
2006                 event = sWindowSession.getPendingTrackballMove(mWindow);
2007             } catch (RemoteException e) {
2008             }
2009             callWhenDone = false;
2010         }
2011 
2012         if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
2013 
2014         boolean handled = false;
2015         try {
2016             if (event == null) {
2017                 handled = true;
2018             } else if (mView != null && mAdded) {
2019                 handled = mView.dispatchTrackballEvent(event);
2020                 if (!handled) {
2021                     // we could do something here, like changing the focus
2022                     // or something?
2023                 }
2024             }
2025         } finally {
2026             if (handled) {
2027                 if (callWhenDone) {
2028                     try {
2029                         sWindowSession.finishKey(mWindow);
2030                     } catch (RemoteException e) {
2031                     }
2032                 }
2033                 if (event != null) {
2034                     event.recycle();
2035                 }
2036                 // If we reach this, we delivered a trackball event to mView and
2037                 // mView consumed it. Because we will not translate the trackball
2038                 // event into a key event, touch mode will not exit, so we exit
2039                 // touch mode here.
2040                 ensureTouchMode(false);
2041                 //noinspection ReturnInsideFinallyBlock
2042                 return;
2043             }
2044             // Let the exception fall through -- the looper will catch
2045             // it and take care of the bad app for us.
2046         }
2047 
2048         final TrackballAxis x = mTrackballAxisX;
2049         final TrackballAxis y = mTrackballAxisY;
2050 
2051         long curTime = SystemClock.uptimeMillis();
2052         if ((mLastTrackballTime+MAX_TRACKBALL_DELAY) < curTime) {
2053             // It has been too long since the last movement,
2054             // so restart at the beginning.
2055             x.reset(0);
2056             y.reset(0);
2057             mLastTrackballTime = curTime;
2058         }
2059 
2060         try {
2061             final int action = event.getAction();
2062             final int metastate = event.getMetaState();
2063             switch (action) {
2064                 case MotionEvent.ACTION_DOWN:
2065                     x.reset(2);
2066                     y.reset(2);
2067                     deliverKeyEvent(new KeyEvent(curTime, curTime,
2068                             KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER,
2069                             0, metastate), false);
2070                     break;
2071                 case MotionEvent.ACTION_UP:
2072                     x.reset(2);
2073                     y.reset(2);
2074                     deliverKeyEvent(new KeyEvent(curTime, curTime,
2075                             KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER,
2076                             0, metastate), false);
2077                     break;
2078             }
2079 
2080             if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
2081                     + x.step + " dir=" + x.dir + " acc=" + x.acceleration
2082                     + " move=" + event.getX()
2083                     + " / Y=" + y.position + " step="
2084                     + y.step + " dir=" + y.dir + " acc=" + y.acceleration
2085                     + " move=" + event.getY());
2086             final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
2087             final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
2088 
2089             // Generate DPAD events based on the trackball movement.
2090             // We pick the axis that has moved the most as the direction of
2091             // the DPAD.  When we generate DPAD events for one axis, then the
2092             // other axis is reset -- we don't want to perform DPAD jumps due
2093             // to slight movements in the trackball when making major movements
2094             // along the other axis.
2095             int keycode = 0;
2096             int movement = 0;
2097             float accel = 1;
2098             if (xOff > yOff) {
2099                 movement = x.generate((2/event.getXPrecision()));
2100                 if (movement != 0) {
2101                     keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
2102                             : KeyEvent.KEYCODE_DPAD_LEFT;
2103                     accel = x.acceleration;
2104                     y.reset(2);
2105                 }
2106             } else if (yOff > 0) {
2107                 movement = y.generate((2/event.getYPrecision()));
2108                 if (movement != 0) {
2109                     keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
2110                             : KeyEvent.KEYCODE_DPAD_UP;
2111                     accel = y.acceleration;
2112                     x.reset(2);
2113                 }
2114             }
2115 
2116             if (keycode != 0) {
2117                 if (movement < 0) movement = -movement;
2118                 int accelMovement = (int)(movement * accel);
2119                 if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
2120                         + " accelMovement=" + accelMovement
2121                         + " accel=" + accel);
2122                 if (accelMovement > movement) {
2123                     if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
2124                             + keycode);
2125                     movement--;
2126                     deliverKeyEvent(new KeyEvent(curTime, curTime,
2127                             KeyEvent.ACTION_MULTIPLE, keycode,
2128                             accelMovement-movement, metastate), false);
2129                 }
2130                 while (movement > 0) {
2131                     if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
2132                             + keycode);
2133                     movement--;
2134                     curTime = SystemClock.uptimeMillis();
2135                     deliverKeyEvent(new KeyEvent(curTime, curTime,
2136                             KeyEvent.ACTION_DOWN, keycode, 0, event.getMetaState()), false);
2137                     deliverKeyEvent(new KeyEvent(curTime, curTime,
2138                             KeyEvent.ACTION_UP, keycode, 0, metastate), false);
2139                 }
2140                 mLastTrackballTime = curTime;
2141             }
2142         } finally {
2143             if (callWhenDone) {
2144                 try {
2145                     sWindowSession.finishKey(mWindow);
2146                 } catch (RemoteException e) {
2147                 }
2148                 if (event != null) {
2149                     event.recycle();
2150                 }
2151             }
2152             // Let the exception fall through -- the looper will catch
2153             // it and take care of the bad app for us.
2154         }
2155     }
2156 
2157     /**
2158      * @param keyCode The key code
2159      * @return True if the key is directional.
2160      */
isDirectional(int keyCode)2161     static boolean isDirectional(int keyCode) {
2162         switch (keyCode) {
2163         case KeyEvent.KEYCODE_DPAD_LEFT:
2164         case KeyEvent.KEYCODE_DPAD_RIGHT:
2165         case KeyEvent.KEYCODE_DPAD_UP:
2166         case KeyEvent.KEYCODE_DPAD_DOWN:
2167             return true;
2168         }
2169         return false;
2170     }
2171 
2172     /**
2173      * Returns true if this key is a keyboard key.
2174      * @param keyEvent The key event.
2175      * @return whether this key is a keyboard key.
2176      */
isKeyboardKey(KeyEvent keyEvent)2177     private static boolean isKeyboardKey(KeyEvent keyEvent) {
2178       final int convertedKey = keyEvent.getUnicodeChar();
2179         return convertedKey > 0;
2180     }
2181 
2182 
2183 
2184     /**
2185      * See if the key event means we should leave touch mode (and leave touch
2186      * mode if so).
2187      * @param event The key event.
2188      * @return Whether this key event should be consumed (meaning the act of
2189      *   leaving touch mode alone is considered the event).
2190      */
checkForLeavingTouchModeAndConsume(KeyEvent event)2191     private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) {
2192         if (event.getAction() != KeyEvent.ACTION_DOWN) {
2193             return false;
2194         }
2195         if ((event.getFlags()&KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) {
2196             return false;
2197         }
2198 
2199         // only relevant if we are in touch mode
2200         if (!mAttachInfo.mInTouchMode) {
2201             return false;
2202         }
2203 
2204         // if something like an edit text has focus and the user is typing,
2205         // leave touch mode
2206         //
2207         // note: the condition of not being a keyboard key is kind of a hacky
2208         // approximation of whether we think the focused view will want the
2209         // key; if we knew for sure whether the focused view would consume
2210         // the event, that would be better.
2211         if (isKeyboardKey(event) && mView != null && mView.hasFocus()) {
2212             mFocusedView = mView.findFocus();
2213             if ((mFocusedView instanceof ViewGroup)
2214                     && ((ViewGroup) mFocusedView).getDescendantFocusability() ==
2215                     ViewGroup.FOCUS_AFTER_DESCENDANTS) {
2216                 // something has focus, but is holding it weakly as a container
2217                 return false;
2218             }
2219             if (ensureTouchMode(false)) {
2220                 throw new IllegalStateException("should not have changed focus "
2221                         + "when leaving touch mode while a view has focus.");
2222             }
2223             return false;
2224         }
2225 
2226         if (isDirectional(event.getKeyCode())) {
2227             // no view has focus, so we leave touch mode (and find something
2228             // to give focus to).  the event is consumed if we were able to
2229             // find something to give focus to.
2230             return ensureTouchMode(false);
2231         }
2232         return false;
2233     }
2234 
2235     /**
2236      * log motion events
2237      */
captureMotionLog(String subTag, MotionEvent ev)2238     private static void captureMotionLog(String subTag, MotionEvent ev) {
2239         //check dynamic switch
2240         if (ev == null ||
2241                 SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) {
2242             return;
2243         }
2244 
2245         StringBuilder sb = new StringBuilder(subTag + ": ");
2246         sb.append(ev.getDownTime()).append(',');
2247         sb.append(ev.getEventTime()).append(',');
2248         sb.append(ev.getAction()).append(',');
2249         sb.append(ev.getX()).append(',');
2250         sb.append(ev.getY()).append(',');
2251         sb.append(ev.getPressure()).append(',');
2252         sb.append(ev.getSize()).append(',');
2253         sb.append(ev.getMetaState()).append(',');
2254         sb.append(ev.getXPrecision()).append(',');
2255         sb.append(ev.getYPrecision()).append(',');
2256         sb.append(ev.getDeviceId()).append(',');
2257         sb.append(ev.getEdgeFlags());
2258         Log.d(TAG, sb.toString());
2259     }
2260     /**
2261      * log motion events
2262      */
captureKeyLog(String subTag, KeyEvent ev)2263     private static void captureKeyLog(String subTag, KeyEvent ev) {
2264         //check dynamic switch
2265         if (ev == null ||
2266                 SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) {
2267             return;
2268         }
2269         StringBuilder sb = new StringBuilder(subTag + ": ");
2270         sb.append(ev.getDownTime()).append(',');
2271         sb.append(ev.getEventTime()).append(',');
2272         sb.append(ev.getAction()).append(',');
2273         sb.append(ev.getKeyCode()).append(',');
2274         sb.append(ev.getRepeatCount()).append(',');
2275         sb.append(ev.getMetaState()).append(',');
2276         sb.append(ev.getDeviceId()).append(',');
2277         sb.append(ev.getScanCode());
2278         Log.d(TAG, sb.toString());
2279     }
2280 
enqueuePendingEvent(Object event, boolean sendDone)2281     int enqueuePendingEvent(Object event, boolean sendDone) {
2282         int seq = mPendingEventSeq+1;
2283         if (seq < 0) seq = 0;
2284         mPendingEventSeq = seq;
2285         mPendingEvents.put(seq, event);
2286         return sendDone ? seq : -seq;
2287     }
2288 
retrievePendingEvent(int seq)2289     Object retrievePendingEvent(int seq) {
2290         if (seq < 0) seq = -seq;
2291         Object event = mPendingEvents.get(seq);
2292         if (event != null) {
2293             mPendingEvents.remove(seq);
2294         }
2295         return event;
2296     }
2297 
deliverKeyEvent(KeyEvent event, boolean sendDone)2298     private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
2299         // If mView is null, we just consume the key event because it doesn't
2300         // make sense to do anything else with it.
2301         boolean handled = mView != null
2302                 ? mView.dispatchKeyEventPreIme(event) : true;
2303         if (handled) {
2304             if (sendDone) {
2305                 if (LOCAL_LOGV) Log.v(
2306                     "ViewRoot", "Telling window manager key is finished");
2307                 try {
2308                     sWindowSession.finishKey(mWindow);
2309                 } catch (RemoteException e) {
2310                 }
2311             }
2312             return;
2313         }
2314         // If it is possible for this window to interact with the input
2315         // method window, then we want to first dispatch our key events
2316         // to the input method.
2317         if (mLastWasImTarget) {
2318             InputMethodManager imm = InputMethodManager.peekInstance();
2319             if (imm != null && mView != null) {
2320                 int seq = enqueuePendingEvent(event, sendDone);
2321                 if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
2322                         + seq + " event=" + event);
2323                 imm.dispatchKeyEvent(mView.getContext(), seq, event,
2324                         mInputMethodCallback);
2325                 return;
2326             }
2327         }
2328         deliverKeyEventToViewHierarchy(event, sendDone);
2329     }
2330 
handleFinishedEvent(int seq, boolean handled)2331     void handleFinishedEvent(int seq, boolean handled) {
2332         final KeyEvent event = (KeyEvent)retrievePendingEvent(seq);
2333         if (DEBUG_IMF) Log.v(TAG, "IME finished event: seq=" + seq
2334                 + " handled=" + handled + " event=" + event);
2335         if (event != null) {
2336             final boolean sendDone = seq >= 0;
2337             if (!handled) {
2338                 deliverKeyEventToViewHierarchy(event, sendDone);
2339                 return;
2340             } else if (sendDone) {
2341                 if (LOCAL_LOGV) Log.v(
2342                         "ViewRoot", "Telling window manager key is finished");
2343                 try {
2344                     sWindowSession.finishKey(mWindow);
2345                 } catch (RemoteException e) {
2346                 }
2347             } else {
2348                 Log.w("ViewRoot", "handleFinishedEvent(seq=" + seq
2349                         + " handled=" + handled + " ev=" + event
2350                         + ") neither delivering nor finishing key");
2351             }
2352         }
2353     }
2354 
deliverKeyEventToViewHierarchy(KeyEvent event, boolean sendDone)2355     private void deliverKeyEventToViewHierarchy(KeyEvent event, boolean sendDone) {
2356         try {
2357             if (mView != null && mAdded) {
2358                 final int action = event.getAction();
2359                 boolean isDown = (action == KeyEvent.ACTION_DOWN);
2360 
2361                 if (checkForLeavingTouchModeAndConsume(event)) {
2362                     return;
2363                 }
2364 
2365                 if (Config.LOGV) {
2366                     captureKeyLog("captureDispatchKeyEvent", event);
2367                 }
2368                 boolean keyHandled = mView.dispatchKeyEvent(event);
2369 
2370                 if (!keyHandled && isDown) {
2371                     int direction = 0;
2372                     switch (event.getKeyCode()) {
2373                     case KeyEvent.KEYCODE_DPAD_LEFT:
2374                         direction = View.FOCUS_LEFT;
2375                         break;
2376                     case KeyEvent.KEYCODE_DPAD_RIGHT:
2377                         direction = View.FOCUS_RIGHT;
2378                         break;
2379                     case KeyEvent.KEYCODE_DPAD_UP:
2380                         direction = View.FOCUS_UP;
2381                         break;
2382                     case KeyEvent.KEYCODE_DPAD_DOWN:
2383                         direction = View.FOCUS_DOWN;
2384                         break;
2385                     }
2386 
2387                     if (direction != 0) {
2388 
2389                         View focused = mView != null ? mView.findFocus() : null;
2390                         if (focused != null) {
2391                             View v = focused.focusSearch(direction);
2392                             boolean focusPassed = false;
2393                             if (v != null && v != focused) {
2394                                 // do the math the get the interesting rect
2395                                 // of previous focused into the coord system of
2396                                 // newly focused view
2397                                 focused.getFocusedRect(mTempRect);
2398                                 ((ViewGroup) mView).offsetDescendantRectToMyCoords(focused, mTempRect);
2399                                 ((ViewGroup) mView).offsetRectIntoDescendantCoords(v, mTempRect);
2400                                 focusPassed = v.requestFocus(direction, mTempRect);
2401                             }
2402 
2403                             if (!focusPassed) {
2404                                 mView.dispatchUnhandledMove(focused, direction);
2405                             } else {
2406                                 playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
2407                             }
2408                         }
2409                     }
2410                 }
2411             }
2412 
2413         } finally {
2414             if (sendDone) {
2415                 if (LOCAL_LOGV) Log.v(
2416                     "ViewRoot", "Telling window manager key is finished");
2417                 try {
2418                     sWindowSession.finishKey(mWindow);
2419                 } catch (RemoteException e) {
2420                 }
2421             }
2422             // Let the exception fall through -- the looper will catch
2423             // it and take care of the bad app for us.
2424         }
2425     }
2426 
getAudioManager()2427     private AudioManager getAudioManager() {
2428         if (mView == null) {
2429             throw new IllegalStateException("getAudioManager called when there is no mView");
2430         }
2431         if (mAudioManager == null) {
2432             mAudioManager = (AudioManager) mView.getContext().getSystemService(Context.AUDIO_SERVICE);
2433         }
2434         return mAudioManager;
2435     }
2436 
relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending)2437     private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
2438             boolean insetsPending) throws RemoteException {
2439 
2440         float appScale = mAttachInfo.mApplicationScale;
2441         boolean restore = false;
2442         if (params != null && mTranslator != null) {
2443             restore = true;
2444             params.backup();
2445             mTranslator.translateWindowLayout(params);
2446         }
2447         if (params != null) {
2448             if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
2449         }
2450         int relayoutResult = sWindowSession.relayout(
2451                 mWindow, params,
2452                 (int) (mView.mMeasuredWidth * appScale + 0.5f),
2453                 (int) (mView.mMeasuredHeight * appScale + 0.5f),
2454                 viewVisibility, insetsPending, mWinFrame,
2455                 mPendingContentInsets, mPendingVisibleInsets, mSurface);
2456         if (restore) {
2457             params.restore();
2458         }
2459 
2460         if (mTranslator != null) {
2461             mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
2462             mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
2463             mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
2464         }
2465         return relayoutResult;
2466     }
2467 
2468     /**
2469      * {@inheritDoc}
2470      */
playSoundEffect(int effectId)2471     public void playSoundEffect(int effectId) {
2472         checkThread();
2473 
2474         final AudioManager audioManager = getAudioManager();
2475 
2476         switch (effectId) {
2477             case SoundEffectConstants.CLICK:
2478                 audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
2479                 return;
2480             case SoundEffectConstants.NAVIGATION_DOWN:
2481                 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);
2482                 return;
2483             case SoundEffectConstants.NAVIGATION_LEFT:
2484                 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);
2485                 return;
2486             case SoundEffectConstants.NAVIGATION_RIGHT:
2487                 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);
2488                 return;
2489             case SoundEffectConstants.NAVIGATION_UP:
2490                 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);
2491                 return;
2492             default:
2493                 throw new IllegalArgumentException("unknown effect id " + effectId +
2494                         " not defined in " + SoundEffectConstants.class.getCanonicalName());
2495         }
2496     }
2497 
2498     /**
2499      * {@inheritDoc}
2500      */
performHapticFeedback(int effectId, boolean always)2501     public boolean performHapticFeedback(int effectId, boolean always) {
2502         try {
2503             return sWindowSession.performHapticFeedback(mWindow, effectId, always);
2504         } catch (RemoteException e) {
2505             return false;
2506         }
2507     }
2508 
2509     /**
2510      * {@inheritDoc}
2511      */
focusSearch(View focused, int direction)2512     public View focusSearch(View focused, int direction) {
2513         checkThread();
2514         if (!(mView instanceof ViewGroup)) {
2515             return null;
2516         }
2517         return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
2518     }
2519 
debug()2520     public void debug() {
2521         mView.debug();
2522     }
2523 
die(boolean immediate)2524     public void die(boolean immediate) {
2525         if (immediate) {
2526             doDie();
2527         } else {
2528             sendEmptyMessage(DIE);
2529         }
2530     }
2531 
doDie()2532     void doDie() {
2533         checkThread();
2534         if (Config.LOGV) Log.v("ViewRoot", "DIE in " + this + " of " + mSurface);
2535         synchronized (this) {
2536             if (mAdded && !mFirst) {
2537                 int viewVisibility = mView.getVisibility();
2538                 boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
2539                 if (mWindowAttributesChanged || viewVisibilityChanged) {
2540                     // If layout params have been changed, first give them
2541                     // to the window manager to make sure it has the correct
2542                     // animation info.
2543                     try {
2544                         if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
2545                                 & WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
2546                             sWindowSession.finishDrawing(mWindow);
2547                         }
2548                     } catch (RemoteException e) {
2549                     }
2550                 }
2551 
2552                 mSurface.release();
2553             }
2554             if (mAdded) {
2555                 mAdded = false;
2556                 dispatchDetachedFromWindow();
2557             }
2558         }
2559     }
2560 
dispatchFinishedEvent(int seq, boolean handled)2561     public void dispatchFinishedEvent(int seq, boolean handled) {
2562         Message msg = obtainMessage(FINISHED_EVENT);
2563         msg.arg1 = seq;
2564         msg.arg2 = handled ? 1 : 0;
2565         sendMessage(msg);
2566     }
2567 
dispatchResized(int w, int h, Rect coveredInsets, Rect visibleInsets, boolean reportDraw)2568     public void dispatchResized(int w, int h, Rect coveredInsets,
2569             Rect visibleInsets, boolean reportDraw) {
2570         if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": w=" + w
2571                 + " h=" + h + " coveredInsets=" + coveredInsets.toShortString()
2572                 + " visibleInsets=" + visibleInsets.toShortString()
2573                 + " reportDraw=" + reportDraw);
2574         Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED);
2575         if (mTranslator != null) {
2576             mTranslator.translateRectInScreenToAppWindow(coveredInsets);
2577             mTranslator.translateRectInScreenToAppWindow(visibleInsets);
2578             w *= mTranslator.applicationInvertedScale;
2579             h *= mTranslator.applicationInvertedScale;
2580         }
2581         msg.arg1 = w;
2582         msg.arg2 = h;
2583         msg.obj = new Rect[] { new Rect(coveredInsets), new Rect(visibleInsets) };
2584         sendMessage(msg);
2585     }
2586 
dispatchKey(KeyEvent event)2587     public void dispatchKey(KeyEvent event) {
2588         if (event.getAction() == KeyEvent.ACTION_DOWN) {
2589             //noinspection ConstantConditions
2590             if (false && event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
2591                 if (Config.LOGD) Log.d("keydisp",
2592                         "===================================================");
2593                 if (Config.LOGD) Log.d("keydisp", "Focused view Hierarchy is:");
2594                 debug();
2595 
2596                 if (Config.LOGD) Log.d("keydisp",
2597                         "===================================================");
2598             }
2599         }
2600 
2601         Message msg = obtainMessage(DISPATCH_KEY);
2602         msg.obj = event;
2603 
2604         if (LOCAL_LOGV) Log.v(
2605             "ViewRoot", "sending key " + event + " to " + mView);
2606 
2607         sendMessageAtTime(msg, event.getEventTime());
2608     }
2609 
dispatchPointer(MotionEvent event, long eventTime, boolean callWhenDone)2610     public void dispatchPointer(MotionEvent event, long eventTime,
2611             boolean callWhenDone) {
2612         Message msg = obtainMessage(DISPATCH_POINTER);
2613         msg.obj = event;
2614         msg.arg1 = callWhenDone ? 1 : 0;
2615         sendMessageAtTime(msg, eventTime);
2616     }
2617 
dispatchTrackball(MotionEvent event, long eventTime, boolean callWhenDone)2618     public void dispatchTrackball(MotionEvent event, long eventTime,
2619             boolean callWhenDone) {
2620         Message msg = obtainMessage(DISPATCH_TRACKBALL);
2621         msg.obj = event;
2622         msg.arg1 = callWhenDone ? 1 : 0;
2623         sendMessageAtTime(msg, eventTime);
2624     }
2625 
dispatchAppVisibility(boolean visible)2626     public void dispatchAppVisibility(boolean visible) {
2627         Message msg = obtainMessage(DISPATCH_APP_VISIBILITY);
2628         msg.arg1 = visible ? 1 : 0;
2629         sendMessage(msg);
2630     }
2631 
dispatchGetNewSurface()2632     public void dispatchGetNewSurface() {
2633         Message msg = obtainMessage(DISPATCH_GET_NEW_SURFACE);
2634         sendMessage(msg);
2635     }
2636 
windowFocusChanged(boolean hasFocus, boolean inTouchMode)2637     public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
2638         Message msg = Message.obtain();
2639         msg.what = WINDOW_FOCUS_CHANGED;
2640         msg.arg1 = hasFocus ? 1 : 0;
2641         msg.arg2 = inTouchMode ? 1 : 0;
2642         sendMessage(msg);
2643     }
2644 
dispatchCloseSystemDialogs(String reason)2645     public void dispatchCloseSystemDialogs(String reason) {
2646         Message msg = Message.obtain();
2647         msg.what = CLOSE_SYSTEM_DIALOGS;
2648         msg.obj = reason;
2649         sendMessage(msg);
2650     }
2651 
2652     /**
2653      * The window is getting focus so if there is anything focused/selected
2654      * send an {@link AccessibilityEvent} to announce that.
2655      */
sendAccessibilityEvents()2656     private void sendAccessibilityEvents() {
2657         if (!AccessibilityManager.getInstance(mView.getContext()).isEnabled()) {
2658             return;
2659         }
2660         mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
2661         View focusedView = mView.findFocus();
2662         if (focusedView != null && focusedView != mView) {
2663             focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
2664         }
2665     }
2666 
showContextMenuForChild(View originalView)2667     public boolean showContextMenuForChild(View originalView) {
2668         return false;
2669     }
2670 
createContextMenu(ContextMenu menu)2671     public void createContextMenu(ContextMenu menu) {
2672     }
2673 
childDrawableStateChanged(View child)2674     public void childDrawableStateChanged(View child) {
2675     }
2676 
getWindowFrame()2677     protected Rect getWindowFrame() {
2678         return mWinFrame;
2679     }
2680 
checkThread()2681     void checkThread() {
2682         if (mThread != Thread.currentThread()) {
2683             throw new CalledFromWrongThreadException(
2684                     "Only the original thread that created a view hierarchy can touch its views.");
2685         }
2686     }
2687 
requestDisallowInterceptTouchEvent(boolean disallowIntercept)2688     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2689         // ViewRoot never intercepts touch event, so this can be a no-op
2690     }
2691 
requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate)2692     public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
2693             boolean immediate) {
2694         return scrollToRectOrFocus(rectangle, immediate);
2695     }
2696 
2697     static class InputMethodCallback extends IInputMethodCallback.Stub {
2698         private WeakReference<ViewRoot> mViewRoot;
2699 
InputMethodCallback(ViewRoot viewRoot)2700         public InputMethodCallback(ViewRoot viewRoot) {
2701             mViewRoot = new WeakReference<ViewRoot>(viewRoot);
2702         }
2703 
finishedEvent(int seq, boolean handled)2704         public void finishedEvent(int seq, boolean handled) {
2705             final ViewRoot viewRoot = mViewRoot.get();
2706             if (viewRoot != null) {
2707                 viewRoot.dispatchFinishedEvent(seq, handled);
2708             }
2709         }
2710 
sessionCreated(IInputMethodSession session)2711         public void sessionCreated(IInputMethodSession session) throws RemoteException {
2712             // Stub -- not for use in the client.
2713         }
2714     }
2715 
2716     static class EventCompletion extends Handler {
2717         final IWindow mWindow;
2718         final KeyEvent mKeyEvent;
2719         final boolean mIsPointer;
2720         final MotionEvent mMotionEvent;
2721 
EventCompletion(Looper looper, IWindow window, KeyEvent key, boolean isPointer, MotionEvent motion)2722         EventCompletion(Looper looper, IWindow window, KeyEvent key,
2723                 boolean isPointer, MotionEvent motion) {
2724             super(looper);
2725             mWindow = window;
2726             mKeyEvent = key;
2727             mIsPointer = isPointer;
2728             mMotionEvent = motion;
2729             sendEmptyMessage(0);
2730         }
2731 
2732         @Override
handleMessage(Message msg)2733         public void handleMessage(Message msg) {
2734             if (mKeyEvent != null) {
2735                 try {
2736                     sWindowSession.finishKey(mWindow);
2737                  } catch (RemoteException e) {
2738                  }
2739            } else if (mIsPointer) {
2740                 boolean didFinish;
2741                 MotionEvent event = mMotionEvent;
2742                 if (event == null) {
2743                     try {
2744                         event = sWindowSession.getPendingPointerMove(mWindow);
2745                     } catch (RemoteException e) {
2746                     }
2747                     didFinish = true;
2748                 } else {
2749                     didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE;
2750                 }
2751                 if (!didFinish) {
2752                     try {
2753                         sWindowSession.finishKey(mWindow);
2754                      } catch (RemoteException e) {
2755                      }
2756                 }
2757             } else {
2758                 MotionEvent event = mMotionEvent;
2759                 if (event == null) {
2760                     try {
2761                         event = sWindowSession.getPendingTrackballMove(mWindow);
2762                     } catch (RemoteException e) {
2763                     }
2764                 } else {
2765                     try {
2766                         sWindowSession.finishKey(mWindow);
2767                      } catch (RemoteException e) {
2768                      }
2769                 }
2770             }
2771         }
2772     }
2773 
2774     static class W extends IWindow.Stub {
2775         private final WeakReference<ViewRoot> mViewRoot;
2776         private final Looper mMainLooper;
2777 
W(ViewRoot viewRoot, Context context)2778         public W(ViewRoot viewRoot, Context context) {
2779             mViewRoot = new WeakReference<ViewRoot>(viewRoot);
2780             mMainLooper = context.getMainLooper();
2781         }
2782 
resized(int w, int h, Rect coveredInsets, Rect visibleInsets, boolean reportDraw)2783         public void resized(int w, int h, Rect coveredInsets,
2784                 Rect visibleInsets, boolean reportDraw) {
2785             final ViewRoot viewRoot = mViewRoot.get();
2786             if (viewRoot != null) {
2787                 viewRoot.dispatchResized(w, h, coveredInsets,
2788                         visibleInsets, reportDraw);
2789             }
2790         }
2791 
dispatchKey(KeyEvent event)2792         public void dispatchKey(KeyEvent event) {
2793             final ViewRoot viewRoot = mViewRoot.get();
2794             if (viewRoot != null) {
2795                 viewRoot.dispatchKey(event);
2796             } else {
2797                 Log.w("ViewRoot.W", "Key event " + event + " but no ViewRoot available!");
2798                 new EventCompletion(mMainLooper, this, event, false, null);
2799             }
2800         }
2801 
dispatchPointer(MotionEvent event, long eventTime, boolean callWhenDone)2802         public void dispatchPointer(MotionEvent event, long eventTime,
2803                 boolean callWhenDone) {
2804             final ViewRoot viewRoot = mViewRoot.get();
2805             if (viewRoot != null) {
2806                 if (MEASURE_LATENCY) {
2807                     // Note: eventTime is in milliseconds
2808                     ViewRoot.lt.sample("* ViewRoot b4 dispatchPtr", System.nanoTime() - eventTime * 1000000);
2809                 }
2810                 viewRoot.dispatchPointer(event, eventTime, callWhenDone);
2811             } else {
2812                 new EventCompletion(mMainLooper, this, null, true, event);
2813             }
2814         }
2815 
dispatchTrackball(MotionEvent event, long eventTime, boolean callWhenDone)2816         public void dispatchTrackball(MotionEvent event, long eventTime,
2817                 boolean callWhenDone) {
2818             final ViewRoot viewRoot = mViewRoot.get();
2819             if (viewRoot != null) {
2820                 viewRoot.dispatchTrackball(event, eventTime, callWhenDone);
2821             } else {
2822                 new EventCompletion(mMainLooper, this, null, false, event);
2823             }
2824         }
2825 
dispatchAppVisibility(boolean visible)2826         public void dispatchAppVisibility(boolean visible) {
2827             final ViewRoot viewRoot = mViewRoot.get();
2828             if (viewRoot != null) {
2829                 viewRoot.dispatchAppVisibility(visible);
2830             }
2831         }
2832 
dispatchGetNewSurface()2833         public void dispatchGetNewSurface() {
2834             final ViewRoot viewRoot = mViewRoot.get();
2835             if (viewRoot != null) {
2836                 viewRoot.dispatchGetNewSurface();
2837             }
2838         }
2839 
windowFocusChanged(boolean hasFocus, boolean inTouchMode)2840         public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
2841             final ViewRoot viewRoot = mViewRoot.get();
2842             if (viewRoot != null) {
2843                 viewRoot.windowFocusChanged(hasFocus, inTouchMode);
2844             }
2845         }
2846 
checkCallingPermission(String permission)2847         private static int checkCallingPermission(String permission) {
2848             if (!Process.supportsProcesses()) {
2849                 return PackageManager.PERMISSION_GRANTED;
2850             }
2851 
2852             try {
2853                 return ActivityManagerNative.getDefault().checkPermission(
2854                         permission, Binder.getCallingPid(), Binder.getCallingUid());
2855             } catch (RemoteException e) {
2856                 return PackageManager.PERMISSION_DENIED;
2857             }
2858         }
2859 
executeCommand(String command, String parameters, ParcelFileDescriptor out)2860         public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
2861             final ViewRoot viewRoot = mViewRoot.get();
2862             if (viewRoot != null) {
2863                 final View view = viewRoot.mView;
2864                 if (view != null) {
2865                     if (checkCallingPermission(Manifest.permission.DUMP) !=
2866                             PackageManager.PERMISSION_GRANTED) {
2867                         throw new SecurityException("Insufficient permissions to invoke"
2868                                 + " executeCommand() from pid=" + Binder.getCallingPid()
2869                                 + ", uid=" + Binder.getCallingUid());
2870                     }
2871 
2872                     OutputStream clientStream = null;
2873                     try {
2874                         clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out);
2875                         ViewDebug.dispatchCommand(view, command, parameters, clientStream);
2876                     } catch (IOException e) {
2877                         e.printStackTrace();
2878                     } finally {
2879                         if (clientStream != null) {
2880                             try {
2881                                 clientStream.close();
2882                             } catch (IOException e) {
2883                                 e.printStackTrace();
2884                             }
2885                         }
2886                     }
2887                 }
2888             }
2889         }
2890 
closeSystemDialogs(String reason)2891         public void closeSystemDialogs(String reason) {
2892             final ViewRoot viewRoot = mViewRoot.get();
2893             if (viewRoot != null) {
2894                 viewRoot.dispatchCloseSystemDialogs(reason);
2895             }
2896         }
2897 
dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync)2898         public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
2899                 boolean sync) {
2900             if (sync) {
2901                 try {
2902                     sWindowSession.wallpaperOffsetsComplete(asBinder());
2903                 } catch (RemoteException e) {
2904                 }
2905             }
2906         }
2907 
dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras, boolean sync)2908         public void dispatchWallpaperCommand(String action, int x, int y,
2909                 int z, Bundle extras, boolean sync) {
2910             if (sync) {
2911                 try {
2912                     sWindowSession.wallpaperCommandComplete(asBinder(), null);
2913                 } catch (RemoteException e) {
2914                 }
2915             }
2916         }
2917     }
2918 
2919     /**
2920      * Maintains state information for a single trackball axis, generating
2921      * discrete (DPAD) movements based on raw trackball motion.
2922      */
2923     static final class TrackballAxis {
2924         /**
2925          * The maximum amount of acceleration we will apply.
2926          */
2927         static final float MAX_ACCELERATION = 20;
2928 
2929         /**
2930          * The maximum amount of time (in milliseconds) between events in order
2931          * for us to consider the user to be doing fast trackball movements,
2932          * and thus apply an acceleration.
2933          */
2934         static final long FAST_MOVE_TIME = 150;
2935 
2936         /**
2937          * Scaling factor to the time (in milliseconds) between events to how
2938          * much to multiple/divide the current acceleration.  When movement
2939          * is < FAST_MOVE_TIME this multiplies the acceleration; when >
2940          * FAST_MOVE_TIME it divides it.
2941          */
2942         static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
2943 
2944         float position;
2945         float absPosition;
2946         float acceleration = 1;
2947         long lastMoveTime = 0;
2948         int step;
2949         int dir;
2950         int nonAccelMovement;
2951 
reset(int _step)2952         void reset(int _step) {
2953             position = 0;
2954             acceleration = 1;
2955             lastMoveTime = 0;
2956             step = _step;
2957             dir = 0;
2958         }
2959 
2960         /**
2961          * Add trackball movement into the state.  If the direction of movement
2962          * has been reversed, the state is reset before adding the
2963          * movement (so that you don't have to compensate for any previously
2964          * collected movement before see the result of the movement in the
2965          * new direction).
2966          *
2967          * @return Returns the absolute value of the amount of movement
2968          * collected so far.
2969          */
collect(float off, long time, String axis)2970         float collect(float off, long time, String axis) {
2971             long normTime;
2972             if (off > 0) {
2973                 normTime = (long)(off * FAST_MOVE_TIME);
2974                 if (dir < 0) {
2975                     if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!");
2976                     position = 0;
2977                     step = 0;
2978                     acceleration = 1;
2979                     lastMoveTime = 0;
2980                 }
2981                 dir = 1;
2982             } else if (off < 0) {
2983                 normTime = (long)((-off) * FAST_MOVE_TIME);
2984                 if (dir > 0) {
2985                     if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!");
2986                     position = 0;
2987                     step = 0;
2988                     acceleration = 1;
2989                     lastMoveTime = 0;
2990                 }
2991                 dir = -1;
2992             } else {
2993                 normTime = 0;
2994             }
2995 
2996             // The number of milliseconds between each movement that is
2997             // considered "normal" and will not result in any acceleration
2998             // or deceleration, scaled by the offset we have here.
2999             if (normTime > 0) {
3000                 long delta = time - lastMoveTime;
3001                 lastMoveTime = time;
3002                 float acc = acceleration;
3003                 if (delta < normTime) {
3004                     // The user is scrolling rapidly, so increase acceleration.
3005                     float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR;
3006                     if (scale > 1) acc *= scale;
3007                     if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off="
3008                             + off + " normTime=" + normTime + " delta=" + delta
3009                             + " scale=" + scale + " acc=" + acc);
3010                     acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION;
3011                 } else {
3012                     // The user is scrolling slowly, so decrease acceleration.
3013                     float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR;
3014                     if (scale > 1) acc /= scale;
3015                     if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off="
3016                             + off + " normTime=" + normTime + " delta=" + delta
3017                             + " scale=" + scale + " acc=" + acc);
3018                     acceleration = acc > 1 ? acc : 1;
3019                 }
3020             }
3021             position += off;
3022             return (absPosition = Math.abs(position));
3023         }
3024 
3025         /**
3026          * Generate the number of discrete movement events appropriate for
3027          * the currently collected trackball movement.
3028          *
3029          * @param precision The minimum movement required to generate the
3030          * first discrete movement.
3031          *
3032          * @return Returns the number of discrete movements, either positive
3033          * or negative, or 0 if there is not enough trackball movement yet
3034          * for a discrete movement.
3035          */
generate(float precision)3036         int generate(float precision) {
3037             int movement = 0;
3038             nonAccelMovement = 0;
3039             do {
3040                 final int dir = position >= 0 ? 1 : -1;
3041                 switch (step) {
3042                     // If we are going to execute the first step, then we want
3043                     // to do this as soon as possible instead of waiting for
3044                     // a full movement, in order to make things look responsive.
3045                     case 0:
3046                         if (absPosition < precision) {
3047                             return movement;
3048                         }
3049                         movement += dir;
3050                         nonAccelMovement += dir;
3051                         step = 1;
3052                         break;
3053                     // If we have generated the first movement, then we need
3054                     // to wait for the second complete trackball motion before
3055                     // generating the second discrete movement.
3056                     case 1:
3057                         if (absPosition < 2) {
3058                             return movement;
3059                         }
3060                         movement += dir;
3061                         nonAccelMovement += dir;
3062                         position += dir > 0 ? -2 : 2;
3063                         absPosition = Math.abs(position);
3064                         step = 2;
3065                         break;
3066                     // After the first two, we generate discrete movements
3067                     // consistently with the trackball, applying an acceleration
3068                     // if the trackball is moving quickly.  This is a simple
3069                     // acceleration on top of what we already compute based
3070                     // on how quickly the wheel is being turned, to apply
3071                     // a longer increasing acceleration to continuous movement
3072                     // in one direction.
3073                     default:
3074                         if (absPosition < 1) {
3075                             return movement;
3076                         }
3077                         movement += dir;
3078                         position += dir >= 0 ? -1 : 1;
3079                         absPosition = Math.abs(position);
3080                         float acc = acceleration;
3081                         acc *= 1.1f;
3082                         acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
3083                         break;
3084                 }
3085             } while (true);
3086         }
3087     }
3088 
3089     public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
3090         public CalledFromWrongThreadException(String msg) {
3091             super(msg);
3092         }
3093     }
3094 
3095     private SurfaceHolder mHolder = new SurfaceHolder() {
3096         // we only need a SurfaceHolder for opengl. it would be nice
3097         // to implement everything else though, especially the callback
3098         // support (opengl doesn't make use of it right now, but eventually
3099         // will).
3100         public Surface getSurface() {
3101             return mSurface;
3102         }
3103 
3104         public boolean isCreating() {
3105             return false;
3106         }
3107 
3108         public void addCallback(Callback callback) {
3109         }
3110 
3111         public void removeCallback(Callback callback) {
3112         }
3113 
3114         public void setFixedSize(int width, int height) {
3115         }
3116 
3117         public void setSizeFromLayout() {
3118         }
3119 
3120         public void setFormat(int format) {
3121         }
3122 
3123         public void setType(int type) {
3124         }
3125 
3126         public void setKeepScreenOn(boolean screenOn) {
3127         }
3128 
3129         public Canvas lockCanvas() {
3130             return null;
3131         }
3132 
3133         public Canvas lockCanvas(Rect dirty) {
3134             return null;
3135         }
3136 
3137         public void unlockCanvasAndPost(Canvas canvas) {
3138         }
3139         public Rect getSurfaceFrame() {
3140             return null;
3141         }
3142     };
3143 
3144     static RunQueue getRunQueue() {
3145         RunQueue rq = sRunQueues.get();
3146         if (rq != null) {
3147             return rq;
3148         }
3149         rq = new RunQueue();
3150         sRunQueues.set(rq);
3151         return rq;
3152     }
3153 
3154     /**
3155      * @hide
3156      */
3157     static final class RunQueue {
3158         private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();
3159 
3160         void post(Runnable action) {
3161             postDelayed(action, 0);
3162         }
3163 
3164         void postDelayed(Runnable action, long delayMillis) {
3165             HandlerAction handlerAction = new HandlerAction();
3166             handlerAction.action = action;
3167             handlerAction.delay = delayMillis;
3168 
3169             synchronized (mActions) {
3170                 mActions.add(handlerAction);
3171             }
3172         }
3173 
3174         void removeCallbacks(Runnable action) {
3175             final HandlerAction handlerAction = new HandlerAction();
3176             handlerAction.action = action;
3177 
3178             synchronized (mActions) {
3179                 final ArrayList<HandlerAction> actions = mActions;
3180 
3181                 while (actions.remove(handlerAction)) {
3182                     // Keep going
3183                 }
3184             }
3185         }
3186 
3187         void executeActions(Handler handler) {
3188             synchronized (mActions) {
3189                 final ArrayList<HandlerAction> actions = mActions;
3190                 final int count = actions.size();
3191 
3192                 for (int i = 0; i < count; i++) {
3193                     final HandlerAction handlerAction = actions.get(i);
3194                     handler.postDelayed(handlerAction.action, handlerAction.delay);
3195                 }
3196 
3197                 actions.clear();
3198             }
3199         }
3200 
3201         private static class HandlerAction {
3202             Runnable action;
3203             long delay;
3204 
3205             @Override
3206             public boolean equals(Object o) {
3207                 if (this == o) return true;
3208                 if (o == null || getClass() != o.getClass()) return false;
3209 
3210                 HandlerAction that = (HandlerAction) o;
3211                 return !(action != null ? !action.equals(that.action) : that.action != null);
3212 
3213             }
3214 
3215             @Override
3216             public int hashCode() {
3217                 int result = action != null ? action.hashCode() : 0;
3218                 result = 31 * result + (int) (delay ^ (delay >>> 32));
3219                 return result;
3220             }
3221         }
3222     }
3223 
nativeShowFPS(Canvas canvas, int durationMillis)3224     private static native void nativeShowFPS(Canvas canvas, int durationMillis);
3225 
3226     // inform skia to just abandon its texture cache IDs
3227     // doesn't call glDeleteTextures
nativeAbandonGlCaches()3228     private static native void nativeAbandonGlCaches();
3229 }
3230