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