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