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