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