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