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