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