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