1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view; 18 19 import android.Manifest; 20 import android.animation.LayoutTransition; 21 import android.app.ActivityManagerNative; 22 import android.content.ClipDescription; 23 import android.content.ComponentCallbacks; 24 import android.content.ComponentCallbacks2; 25 import android.content.Context; 26 import android.content.pm.ApplicationInfo; 27 import android.content.pm.PackageManager; 28 import android.content.res.CompatibilityInfo; 29 import android.content.res.Configuration; 30 import android.content.res.Resources; 31 import android.graphics.Canvas; 32 import android.graphics.Paint; 33 import android.graphics.PixelFormat; 34 import android.graphics.Point; 35 import android.graphics.PointF; 36 import android.graphics.PorterDuff; 37 import android.graphics.Rect; 38 import android.graphics.Region; 39 import android.graphics.drawable.Drawable; 40 import android.media.AudioManager; 41 import android.os.Binder; 42 import android.os.Bundle; 43 import android.os.Debug; 44 import android.os.Handler; 45 import android.os.Looper; 46 import android.os.Message; 47 import android.os.ParcelFileDescriptor; 48 import android.os.PowerManager; 49 import android.os.Process; 50 import android.os.RemoteException; 51 import android.os.SystemClock; 52 import android.os.SystemProperties; 53 import android.os.Trace; 54 import android.util.AndroidRuntimeException; 55 import android.util.DisplayMetrics; 56 import android.util.Log; 57 import android.util.Slog; 58 import android.util.TypedValue; 59 import android.view.View.AttachInfo; 60 import android.view.View.MeasureSpec; 61 import android.view.accessibility.AccessibilityEvent; 62 import android.view.accessibility.AccessibilityManager; 63 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; 64 import android.view.accessibility.AccessibilityNodeInfo; 65 import android.view.accessibility.AccessibilityNodeProvider; 66 import android.view.accessibility.IAccessibilityInteractionConnection; 67 import android.view.accessibility.IAccessibilityInteractionConnectionCallback; 68 import android.view.animation.AccelerateDecelerateInterpolator; 69 import android.view.animation.Interpolator; 70 import android.view.inputmethod.InputConnection; 71 import android.view.inputmethod.InputMethodManager; 72 import android.widget.Scroller; 73 74 import com.android.internal.R; 75 import com.android.internal.os.SomeArgs; 76 import com.android.internal.policy.PolicyManager; 77 import com.android.internal.view.BaseSurfaceHolder; 78 import com.android.internal.view.RootViewSurfaceTaker; 79 80 import java.io.IOException; 81 import java.io.OutputStream; 82 import java.lang.ref.WeakReference; 83 import java.util.ArrayList; 84 import java.util.HashSet; 85 86 /** 87 * The top of a view hierarchy, implementing the needed protocol between View 88 * and the WindowManager. This is for the most part an internal implementation 89 * detail of {@link WindowManagerGlobal}. 90 * 91 * {@hide} 92 */ 93 @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"}) 94 public final class ViewRootImpl implements ViewParent, 95 View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { 96 private static final String TAG = "ViewRootImpl"; 97 private static final boolean DBG = false; 98 private static final boolean LOCAL_LOGV = false; 99 /** @noinspection PointlessBooleanExpression*/ 100 private static final boolean DEBUG_DRAW = false || LOCAL_LOGV; 101 private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV; 102 private static final boolean DEBUG_DIALOG = false || LOCAL_LOGV; 103 private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV; 104 private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV; 105 private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV; 106 private static final boolean DEBUG_IMF = false || LOCAL_LOGV; 107 private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV; 108 private static final boolean DEBUG_FPS = false; 109 private static final boolean DEBUG_INPUT_PROCESSING = false || LOCAL_LOGV; 110 111 private static final boolean USE_RENDER_THREAD = false; 112 113 /** 114 * Set this system property to true to force the view hierarchy to render 115 * at 60 Hz. This can be used to measure the potential framerate. 116 */ 117 private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering"; 118 119 /** 120 * Maximum time we allow the user to roll the trackball enough to generate 121 * a key event, before resetting the counters. 122 */ 123 static final int MAX_TRACKBALL_DELAY = 250; 124 125 static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>(); 126 127 static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>(); 128 static boolean sFirstDrawComplete = false; 129 130 static final ArrayList<ComponentCallbacks> sConfigCallbacks 131 = new ArrayList<ComponentCallbacks>(); 132 133 private static boolean sUseRenderThread = false; 134 private static boolean sRenderThreadQueried = false; 135 private static final Object[] sRenderThreadQueryLock = new Object[0]; 136 137 final Context mContext; 138 final IWindowSession mWindowSession; 139 final Display mDisplay; 140 final String mBasePackageName; 141 142 final int[] mTmpLocation = new int[2]; 143 144 final TypedValue mTmpValue = new TypedValue(); 145 146 final Thread mThread; 147 148 final WindowLeaked mLocation; 149 150 final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams(); 151 152 final W mWindow; 153 154 final int mTargetSdkVersion; 155 156 int mSeq; 157 158 View mView; 159 160 View mAccessibilityFocusedHost; 161 AccessibilityNodeInfo mAccessibilityFocusedVirtualView; 162 163 int mViewVisibility; 164 boolean mAppVisible = true; 165 int mOrigWindowType = -1; 166 167 // Set to true if the owner of this window is in the stopped state, 168 // so the window should no longer be active. 169 boolean mStopped = false; 170 171 boolean mLastInCompatMode = false; 172 173 SurfaceHolder.Callback2 mSurfaceHolderCallback; 174 BaseSurfaceHolder mSurfaceHolder; 175 boolean mIsCreating; 176 boolean mDrawingAllowed; 177 178 final Region mTransparentRegion; 179 final Region mPreviousTransparentRegion; 180 181 int mWidth; 182 int mHeight; 183 Rect mDirty; 184 final Rect mCurrentDirty = new Rect(); 185 boolean mIsAnimating; 186 187 CompatibilityInfo.Translator mTranslator; 188 189 final View.AttachInfo mAttachInfo; 190 InputChannel mInputChannel; 191 InputQueue.Callback mInputQueueCallback; 192 InputQueue mInputQueue; 193 FallbackEventHandler mFallbackEventHandler; 194 Choreographer mChoreographer; 195 196 final Rect mTempRect; // used in the transaction to not thrash the heap. 197 final Rect mVisRect; // used to retrieve visible rect of focused view. 198 199 boolean mTraversalScheduled; 200 int mTraversalBarrier; 201 boolean mWillDrawSoon; 202 /** Set to true while in performTraversals for detecting when die(true) is called from internal 203 * callbacks such as onMeasure, onPreDraw, onDraw and deferring doDie() until later. */ 204 boolean mIsInTraversal; 205 boolean mFitSystemWindowsRequested; 206 boolean mLayoutRequested; 207 boolean mFirst; 208 boolean mReportNextDraw; 209 boolean mFullRedrawNeeded; 210 boolean mNewSurfaceNeeded; 211 boolean mHasHadWindowFocus; 212 boolean mLastWasImTarget; 213 boolean mWindowsAnimating; 214 boolean mDrawDuringWindowsAnimating; 215 boolean mIsDrawing; 216 int mLastSystemUiVisibility; 217 int mClientWindowLayoutFlags; 218 boolean mLastOverscanRequested; 219 220 // Pool of queued input events. 221 private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10; 222 private QueuedInputEvent mQueuedInputEventPool; 223 private int mQueuedInputEventPoolSize; 224 225 /* Input event queue. 226 * Pending input events are input events waiting to be delivered to the input stages 227 * and handled by the application. 228 */ 229 QueuedInputEvent mPendingInputEventHead; 230 QueuedInputEvent mPendingInputEventTail; 231 int mPendingInputEventCount; 232 boolean mProcessInputEventsScheduled; 233 String mPendingInputEventQueueLengthCounterName = "pq"; 234 235 InputStage mFirstInputStage; 236 InputStage mFirstPostImeInputStage; 237 238 boolean mWindowAttributesChanged = false; 239 int mWindowAttributesChangesFlag = 0; 240 241 // These can be accessed by any thread, must be protected with a lock. 242 // Surface can never be reassigned or cleared (use Surface.clear()). 243 private final Surface mSurface = new Surface(); 244 245 boolean mAdded; 246 boolean mAddedTouchMode; 247 248 final CompatibilityInfoHolder mCompatibilityInfo; 249 250 // These are accessed by multiple threads. 251 final Rect mWinFrame; // frame given by window manager. 252 253 final Rect mPendingOverscanInsets = new Rect(); 254 final Rect mPendingVisibleInsets = new Rect(); 255 final Rect mPendingContentInsets = new Rect(); 256 final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets 257 = new ViewTreeObserver.InternalInsetsInfo(); 258 259 final Rect mFitSystemWindowsInsets = new Rect(); 260 261 final Configuration mLastConfiguration = new Configuration(); 262 final Configuration mPendingConfiguration = new Configuration(); 263 264 boolean mScrollMayChange; 265 int mSoftInputMode; 266 WeakReference<View> mLastScrolledFocus; 267 int mScrollY; 268 int mCurScrollY; 269 Scroller mScroller; 270 HardwareLayer mResizeBuffer; 271 long mResizeBufferStartTime; 272 int mResizeBufferDuration; 273 static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator(); 274 private ArrayList<LayoutTransition> mPendingTransitions; 275 276 final ViewConfiguration mViewConfiguration; 277 278 /* Drag/drop */ 279 ClipDescription mDragDescription; 280 View mCurrentDragView; 281 volatile Object mLocalDragState; 282 final PointF mDragPoint = new PointF(); 283 final PointF mLastTouchPoint = new PointF(); 284 285 private boolean mProfileRendering; 286 private Choreographer.FrameCallback mRenderProfiler; 287 private boolean mRenderProfilingEnabled; 288 289 // Variables to track frames per second, enabled via DEBUG_FPS flag 290 private long mFpsStartTime = -1; 291 private long mFpsPrevTime = -1; 292 private int mFpsNumFrames; 293 294 private final ArrayList<DisplayList> mDisplayLists = new ArrayList<DisplayList>(); 295 296 /** 297 * see {@link #playSoundEffect(int)} 298 */ 299 AudioManager mAudioManager; 300 301 final AccessibilityManager mAccessibilityManager; 302 303 AccessibilityInteractionController mAccessibilityInteractionController; 304 305 AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager; 306 307 SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent; 308 309 HashSet<View> mTempHashSet; 310 311 private final int mDensity; 312 private final int mNoncompatDensity; 313 314 private boolean mInLayout = false; 315 ArrayList<View> mLayoutRequesters = new ArrayList<View>(); 316 boolean mHandlingLayoutInLayoutRequest = false; 317 318 private int mViewLayoutDirectionInitial; 319 320 /** 321 * Consistency verifier for debugging purposes. 322 */ 323 protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier = 324 InputEventConsistencyVerifier.isInstrumentationEnabled() ? 325 new InputEventConsistencyVerifier(this, 0) : null; 326 327 static final class SystemUiVisibilityInfo { 328 int seq; 329 int globalVisibility; 330 int localValue; 331 int localChanges; 332 } 333 ViewRootImpl(Context context, Display display)334 public ViewRootImpl(Context context, Display display) { 335 mContext = context; 336 mWindowSession = WindowManagerGlobal.getWindowSession(); 337 mDisplay = display; 338 mBasePackageName = context.getBasePackageName(); 339 340 CompatibilityInfoHolder cih = display.getCompatibilityInfo(); 341 mCompatibilityInfo = cih != null ? cih : new CompatibilityInfoHolder(); 342 343 mThread = Thread.currentThread(); 344 mLocation = new WindowLeaked(null); 345 mLocation.fillInStackTrace(); 346 mWidth = -1; 347 mHeight = -1; 348 mDirty = new Rect(); 349 mTempRect = new Rect(); 350 mVisRect = new Rect(); 351 mWinFrame = new Rect(); 352 mWindow = new W(this); 353 mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion; 354 mViewVisibility = View.GONE; 355 mTransparentRegion = new Region(); 356 mPreviousTransparentRegion = new Region(); 357 mFirst = true; // true for the first time the view is added 358 mAdded = false; 359 mAccessibilityManager = AccessibilityManager.getInstance(context); 360 mAccessibilityInteractionConnectionManager = 361 new AccessibilityInteractionConnectionManager(); 362 mAccessibilityManager.addAccessibilityStateChangeListener( 363 mAccessibilityInteractionConnectionManager); 364 mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this); 365 mViewConfiguration = ViewConfiguration.get(context); 366 mDensity = context.getResources().getDisplayMetrics().densityDpi; 367 mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi; 368 mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context); 369 mChoreographer = Choreographer.getInstance(); 370 371 PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 372 mAttachInfo.mScreenOn = powerManager.isScreenOn(); 373 loadSystemProperties(); 374 } 375 376 /** 377 * @return True if the application requests the use of a separate render thread, 378 * false otherwise 379 */ isRenderThreadRequested(Context context)380 private static boolean isRenderThreadRequested(Context context) { 381 if (USE_RENDER_THREAD) { 382 synchronized (sRenderThreadQueryLock) { 383 if (!sRenderThreadQueried) { 384 final PackageManager packageManager = context.getPackageManager(); 385 final String packageName = context.getApplicationInfo().packageName; 386 try { 387 ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 388 PackageManager.GET_META_DATA); 389 if (applicationInfo.metaData != null) { 390 sUseRenderThread = applicationInfo.metaData.getBoolean( 391 "android.graphics.renderThread", false); 392 } 393 } catch (PackageManager.NameNotFoundException e) { 394 } finally { 395 sRenderThreadQueried = true; 396 } 397 } 398 return sUseRenderThread; 399 } 400 } else { 401 return false; 402 } 403 } 404 addFirstDrawHandler(Runnable callback)405 public static void addFirstDrawHandler(Runnable callback) { 406 synchronized (sFirstDrawHandlers) { 407 if (!sFirstDrawComplete) { 408 sFirstDrawHandlers.add(callback); 409 } 410 } 411 } 412 addConfigCallback(ComponentCallbacks callback)413 public static void addConfigCallback(ComponentCallbacks callback) { 414 synchronized (sConfigCallbacks) { 415 sConfigCallbacks.add(callback); 416 } 417 } 418 419 // FIXME for perf testing only 420 private boolean mProfile = false; 421 422 /** 423 * Call this to profile the next traversal call. 424 * FIXME for perf testing only. Remove eventually 425 */ profile()426 public void profile() { 427 mProfile = true; 428 } 429 430 /** 431 * Indicates whether we are in touch mode. Calling this method triggers an IPC 432 * call and should be avoided whenever possible. 433 * 434 * @return True, if the device is in touch mode, false otherwise. 435 * 436 * @hide 437 */ isInTouchMode()438 static boolean isInTouchMode() { 439 IWindowSession windowSession = WindowManagerGlobal.peekWindowSession(); 440 if (windowSession != null) { 441 try { 442 return windowSession.getInTouchMode(); 443 } catch (RemoteException e) { 444 } 445 } 446 return false; 447 } 448 449 /** 450 * We have one child 451 */ setView(View view, WindowManager.LayoutParams attrs, View panelParentView)452 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { 453 synchronized (this) { 454 if (mView == null) { 455 mView = view; 456 mViewLayoutDirectionInitial = mView.getRawLayoutDirection(); 457 mFallbackEventHandler.setView(view); 458 mWindowAttributes.copyFrom(attrs); 459 if (mWindowAttributes.packageName == null) { 460 mWindowAttributes.packageName = mBasePackageName; 461 } 462 attrs = mWindowAttributes; 463 // Keep track of the actual window flags supplied by the client. 464 mClientWindowLayoutFlags = attrs.flags; 465 466 setAccessibilityFocus(null, null); 467 468 if (view instanceof RootViewSurfaceTaker) { 469 mSurfaceHolderCallback = 470 ((RootViewSurfaceTaker)view).willYouTakeTheSurface(); 471 if (mSurfaceHolderCallback != null) { 472 mSurfaceHolder = new TakenSurfaceHolder(); 473 mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); 474 } 475 } 476 477 CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get(); 478 mTranslator = compatibilityInfo.getTranslator(); 479 480 // If the application owns the surface, don't enable hardware acceleration 481 if (mSurfaceHolder == null) { 482 enableHardwareAcceleration(mView.getContext(), attrs); 483 } 484 485 boolean restore = false; 486 if (mTranslator != null) { 487 mSurface.setCompatibilityTranslator(mTranslator); 488 restore = true; 489 attrs.backup(); 490 mTranslator.translateWindowLayout(attrs); 491 } 492 if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs); 493 494 if (!compatibilityInfo.supportsScreen()) { 495 attrs.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; 496 mLastInCompatMode = true; 497 } 498 499 mSoftInputMode = attrs.softInputMode; 500 mWindowAttributesChanged = true; 501 mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED; 502 mAttachInfo.mRootView = view; 503 mAttachInfo.mScalingRequired = mTranslator != null; 504 mAttachInfo.mApplicationScale = 505 mTranslator == null ? 1.0f : mTranslator.applicationScale; 506 if (panelParentView != null) { 507 mAttachInfo.mPanelParentWindowToken 508 = panelParentView.getApplicationWindowToken(); 509 } 510 mAdded = true; 511 int res; /* = WindowManagerImpl.ADD_OKAY; */ 512 513 // Schedule the first layout -before- adding to the window 514 // manager, to make sure we do the relayout before receiving 515 // any other events from the system. 516 requestLayout(); 517 if ((mWindowAttributes.inputFeatures 518 & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { 519 mInputChannel = new InputChannel(); 520 } 521 try { 522 mOrigWindowType = mWindowAttributes.type; 523 mAttachInfo.mRecomputeGlobalAttributes = true; 524 collectViewAttributes(); 525 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, 526 getHostVisibility(), mDisplay.getDisplayId(), 527 mAttachInfo.mContentInsets, mInputChannel); 528 } catch (RemoteException e) { 529 mAdded = false; 530 mView = null; 531 mAttachInfo.mRootView = null; 532 mInputChannel = null; 533 mFallbackEventHandler.setView(null); 534 unscheduleTraversals(); 535 setAccessibilityFocus(null, null); 536 throw new RuntimeException("Adding window failed", e); 537 } finally { 538 if (restore) { 539 attrs.restore(); 540 } 541 } 542 543 if (mTranslator != null) { 544 mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets); 545 } 546 mPendingOverscanInsets.set(0, 0, 0, 0); 547 mPendingContentInsets.set(mAttachInfo.mContentInsets); 548 mPendingVisibleInsets.set(0, 0, 0, 0); 549 if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow); 550 if (res < WindowManagerGlobal.ADD_OKAY) { 551 mAttachInfo.mRootView = null; 552 mAdded = false; 553 mFallbackEventHandler.setView(null); 554 unscheduleTraversals(); 555 setAccessibilityFocus(null, null); 556 switch (res) { 557 case WindowManagerGlobal.ADD_BAD_APP_TOKEN: 558 case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN: 559 throw new WindowManager.BadTokenException( 560 "Unable to add window -- token " + attrs.token 561 + " is not valid; is your activity running?"); 562 case WindowManagerGlobal.ADD_NOT_APP_TOKEN: 563 throw new WindowManager.BadTokenException( 564 "Unable to add window -- token " + attrs.token 565 + " is not for an application"); 566 case WindowManagerGlobal.ADD_APP_EXITING: 567 throw new WindowManager.BadTokenException( 568 "Unable to add window -- app for token " + attrs.token 569 + " is exiting"); 570 case WindowManagerGlobal.ADD_DUPLICATE_ADD: 571 throw new WindowManager.BadTokenException( 572 "Unable to add window -- window " + mWindow 573 + " has already been added"); 574 case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED: 575 // Silently ignore -- we would have just removed it 576 // right away, anyway. 577 return; 578 case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON: 579 throw new WindowManager.BadTokenException( 580 "Unable to add window " + mWindow + 581 " -- another window of this type already exists"); 582 case WindowManagerGlobal.ADD_PERMISSION_DENIED: 583 throw new WindowManager.BadTokenException( 584 "Unable to add window " + mWindow + 585 " -- permission denied for this window type"); 586 case WindowManagerGlobal.ADD_INVALID_DISPLAY: 587 throw new WindowManager.InvalidDisplayException( 588 "Unable to add window " + mWindow + 589 " -- the specified display can not be found"); 590 } 591 throw new RuntimeException( 592 "Unable to add window -- unknown error code " + res); 593 } 594 595 if (view instanceof RootViewSurfaceTaker) { 596 mInputQueueCallback = 597 ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); 598 } 599 if (mInputChannel != null) { 600 if (mInputQueueCallback != null) { 601 mInputQueue = new InputQueue(); 602 mInputQueueCallback.onInputQueueCreated(mInputQueue); 603 } 604 mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, 605 Looper.myLooper()); 606 } 607 608 view.assignParent(this); 609 mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0; 610 mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0; 611 612 if (mAccessibilityManager.isEnabled()) { 613 mAccessibilityInteractionConnectionManager.ensureConnection(); 614 } 615 616 if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 617 view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 618 } 619 620 // Set up the input pipeline. 621 CharSequence counterSuffix = attrs.getTitle(); 622 InputStage syntheticStage = new SyntheticInputStage(); 623 InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticStage); 624 InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, 625 "aq:native-post-ime:" + counterSuffix); 626 InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); 627 InputStage imeStage = new ImeInputStage(earlyPostImeStage, 628 "aq:ime:" + counterSuffix); 629 InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage); 630 InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage, 631 "aq:native-pre-ime:" + counterSuffix); 632 633 mFirstInputStage = nativePreImeStage; 634 mFirstPostImeInputStage = earlyPostImeStage; 635 mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix; 636 } 637 } 638 } 639 destroyHardwareResources()640 void destroyHardwareResources() { 641 if (mAttachInfo.mHardwareRenderer != null) { 642 if (mAttachInfo.mHardwareRenderer.isEnabled()) { 643 mAttachInfo.mHardwareRenderer.destroyLayers(mView); 644 } 645 mAttachInfo.mHardwareRenderer.destroy(false); 646 } 647 } 648 terminateHardwareResources()649 void terminateHardwareResources() { 650 if (mAttachInfo.mHardwareRenderer != null) { 651 mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView); 652 mAttachInfo.mHardwareRenderer.destroy(false); 653 } 654 } 655 destroyHardwareLayers()656 void destroyHardwareLayers() { 657 if (mThread != Thread.currentThread()) { 658 if (mAttachInfo.mHardwareRenderer != null && 659 mAttachInfo.mHardwareRenderer.isEnabled()) { 660 HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_MODERATE); 661 } 662 } else { 663 if (mAttachInfo.mHardwareRenderer != null && 664 mAttachInfo.mHardwareRenderer.isEnabled()) { 665 mAttachInfo.mHardwareRenderer.destroyLayers(mView); 666 } 667 } 668 } 669 pushHardwareLayerUpdate(HardwareLayer layer)670 void pushHardwareLayerUpdate(HardwareLayer layer) { 671 if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { 672 mAttachInfo.mHardwareRenderer.pushLayerUpdate(layer); 673 } 674 } 675 attachFunctor(int functor)676 public boolean attachFunctor(int functor) { 677 //noinspection SimplifiableIfStatement 678 if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { 679 return mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor); 680 } 681 return false; 682 } 683 detachFunctor(int functor)684 public void detachFunctor(int functor) { 685 if (mAttachInfo.mHardwareRenderer != null) { 686 mAttachInfo.mHardwareRenderer.detachFunctor(functor); 687 } 688 } 689 enableHardwareAcceleration(Context context, WindowManager.LayoutParams attrs)690 private void enableHardwareAcceleration(Context context, WindowManager.LayoutParams attrs) { 691 mAttachInfo.mHardwareAccelerated = false; 692 mAttachInfo.mHardwareAccelerationRequested = false; 693 694 // Don't enable hardware acceleration when the application is in compatibility mode 695 if (mTranslator != null) return; 696 697 // Try to enable hardware acceleration if requested 698 final boolean hardwareAccelerated = 699 (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; 700 701 if (hardwareAccelerated) { 702 if (!HardwareRenderer.isAvailable()) { 703 return; 704 } 705 706 // Persistent processes (including the system) should not do 707 // accelerated rendering on low-end devices. In that case, 708 // sRendererDisabled will be set. In addition, the system process 709 // itself should never do accelerated rendering. In that case, both 710 // sRendererDisabled and sSystemRendererDisabled are set. When 711 // sSystemRendererDisabled is set, PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED 712 // can be used by code on the system process to escape that and enable 713 // HW accelerated drawing. (This is basically for the lock screen.) 714 715 final boolean fakeHwAccelerated = (attrs.privateFlags & 716 WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0; 717 final boolean forceHwAccelerated = (attrs.privateFlags & 718 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0; 719 720 if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled 721 && forceHwAccelerated)) { 722 // Don't enable hardware acceleration when we're not on the main thread 723 if (!HardwareRenderer.sSystemRendererDisabled && 724 Looper.getMainLooper() != Looper.myLooper()) { 725 Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware " 726 + "acceleration outside of the main thread, aborting"); 727 return; 728 } 729 730 final boolean renderThread = isRenderThreadRequested(context); 731 if (renderThread) { 732 Log.i(HardwareRenderer.LOG_TAG, "Render threat initiated"); 733 } 734 735 if (mAttachInfo.mHardwareRenderer != null) { 736 mAttachInfo.mHardwareRenderer.destroy(true); 737 } 738 739 final boolean translucent = attrs.format != PixelFormat.OPAQUE; 740 mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent); 741 if (mAttachInfo.mHardwareRenderer != null) { 742 mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString()); 743 mAttachInfo.mHardwareAccelerated = 744 mAttachInfo.mHardwareAccelerationRequested = true; 745 } 746 } else if (fakeHwAccelerated) { 747 // The window had wanted to use hardware acceleration, but this 748 // is not allowed in its process. By setting this flag, it can 749 // still render as if it was accelerated. This is basically for 750 // the preview windows the window manager shows for launching 751 // applications, so they will look more like the app being launched. 752 mAttachInfo.mHardwareAccelerationRequested = true; 753 } 754 } 755 } 756 getView()757 public View getView() { 758 return mView; 759 } 760 getLocation()761 final WindowLeaked getLocation() { 762 return mLocation; 763 } 764 setLayoutParams(WindowManager.LayoutParams attrs, boolean newView)765 void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) { 766 synchronized (this) { 767 int oldSoftInputMode = mWindowAttributes.softInputMode; 768 // Keep track of the actual window flags supplied by the client. 769 mClientWindowLayoutFlags = attrs.flags; 770 // preserve compatible window flag if exists. 771 int compatibleWindowFlag = 772 mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; 773 // transfer over system UI visibility values as they carry current state. 774 attrs.systemUiVisibility = mWindowAttributes.systemUiVisibility; 775 attrs.subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility; 776 mWindowAttributesChangesFlag = mWindowAttributes.copyFrom(attrs); 777 if (mWindowAttributes.packageName == null) { 778 mWindowAttributes.packageName = mBasePackageName; 779 } 780 mWindowAttributes.flags |= compatibleWindowFlag; 781 782 applyKeepScreenOnFlag(mWindowAttributes); 783 784 if (newView) { 785 mSoftInputMode = attrs.softInputMode; 786 requestLayout(); 787 } 788 // Don't lose the mode we last auto-computed. 789 if ((attrs.softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) 790 == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { 791 mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode 792 & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) 793 | (oldSoftInputMode 794 & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST); 795 } 796 mWindowAttributesChanged = true; 797 scheduleTraversals(); 798 } 799 } 800 handleAppVisibility(boolean visible)801 void handleAppVisibility(boolean visible) { 802 if (mAppVisible != visible) { 803 mAppVisible = visible; 804 scheduleTraversals(); 805 } 806 } 807 handleGetNewSurface()808 void handleGetNewSurface() { 809 mNewSurfaceNeeded = true; 810 mFullRedrawNeeded = true; 811 scheduleTraversals(); 812 } 813 handleScreenStateChange(boolean on)814 void handleScreenStateChange(boolean on) { 815 if (on != mAttachInfo.mScreenOn) { 816 mAttachInfo.mScreenOn = on; 817 if (mView != null) { 818 mView.dispatchScreenStateChanged(on ? View.SCREEN_STATE_ON : View.SCREEN_STATE_OFF); 819 } 820 if (on) { 821 mFullRedrawNeeded = true; 822 scheduleTraversals(); 823 } 824 } 825 } 826 827 @Override requestFitSystemWindows()828 public void requestFitSystemWindows() { 829 checkThread(); 830 mFitSystemWindowsRequested = true; 831 scheduleTraversals(); 832 } 833 834 @Override requestLayout()835 public void requestLayout() { 836 if (!mHandlingLayoutInLayoutRequest) { 837 checkThread(); 838 mLayoutRequested = true; 839 scheduleTraversals(); 840 } 841 } 842 843 @Override isLayoutRequested()844 public boolean isLayoutRequested() { 845 return mLayoutRequested; 846 } 847 invalidate()848 void invalidate() { 849 mDirty.set(0, 0, mWidth, mHeight); 850 scheduleTraversals(); 851 } 852 invalidateWorld(View view)853 void invalidateWorld(View view) { 854 view.invalidate(); 855 if (view instanceof ViewGroup) { 856 ViewGroup parent = (ViewGroup) view; 857 for (int i = 0; i < parent.getChildCount(); i++) { 858 invalidateWorld(parent.getChildAt(i)); 859 } 860 } 861 } 862 863 @Override invalidateChild(View child, Rect dirty)864 public void invalidateChild(View child, Rect dirty) { 865 invalidateChildInParent(null, dirty); 866 } 867 invalidateChildInParent(int[] location, Rect dirty)868 public ViewParent invalidateChildInParent(int[] location, Rect dirty) { 869 checkThread(); 870 if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty); 871 872 if (dirty == null) { 873 invalidate(); 874 return null; 875 } else if (dirty.isEmpty() && !mIsAnimating) { 876 return null; 877 } 878 879 if (mCurScrollY != 0 || mTranslator != null) { 880 mTempRect.set(dirty); 881 dirty = mTempRect; 882 if (mCurScrollY != 0) { 883 dirty.offset(0, -mCurScrollY); 884 } 885 if (mTranslator != null) { 886 mTranslator.translateRectInAppWindowToScreen(dirty); 887 } 888 if (mAttachInfo.mScalingRequired) { 889 dirty.inset(-1, -1); 890 } 891 } 892 893 final Rect localDirty = mDirty; 894 if (!localDirty.isEmpty() && !localDirty.contains(dirty)) { 895 mAttachInfo.mSetIgnoreDirtyState = true; 896 mAttachInfo.mIgnoreDirtyState = true; 897 } 898 899 // Add the new dirty rect to the current one 900 localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom); 901 // Intersect with the bounds of the window to skip 902 // updates that lie outside of the visible region 903 final float appScale = mAttachInfo.mApplicationScale; 904 final boolean intersected = localDirty.intersect(0, 0, 905 (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); 906 if (!intersected) { 907 localDirty.setEmpty(); 908 } 909 if (!mWillDrawSoon && (intersected || mIsAnimating)) { 910 scheduleTraversals(); 911 } 912 913 return null; 914 } 915 setStopped(boolean stopped)916 void setStopped(boolean stopped) { 917 if (mStopped != stopped) { 918 mStopped = stopped; 919 if (!stopped) { 920 scheduleTraversals(); 921 } 922 } 923 } 924 getParent()925 public ViewParent getParent() { 926 return null; 927 } 928 getChildVisibleRect(View child, Rect r, android.graphics.Point offset)929 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { 930 if (child != mView) { 931 throw new RuntimeException("child is not mine, honest!"); 932 } 933 // Note: don't apply scroll offset, because we want to know its 934 // visibility in the virtual canvas being given to the view hierarchy. 935 return r.intersect(0, 0, mWidth, mHeight); 936 } 937 bringChildToFront(View child)938 public void bringChildToFront(View child) { 939 } 940 getHostVisibility()941 int getHostVisibility() { 942 return mAppVisible ? mView.getVisibility() : View.GONE; 943 } 944 disposeResizeBuffer()945 void disposeResizeBuffer() { 946 if (mResizeBuffer != null) { 947 mResizeBuffer.destroy(); 948 mResizeBuffer = null; 949 } 950 } 951 952 /** 953 * Add LayoutTransition to the list of transitions to be started in the next traversal. 954 * This list will be cleared after the transitions on the list are start()'ed. These 955 * transitionsa re added by LayoutTransition itself when it sets up animations. The setup 956 * happens during the layout phase of traversal, which we want to complete before any of the 957 * animations are started (because those animations may side-effect properties that layout 958 * depends upon, like the bounding rectangles of the affected views). So we add the transition 959 * to the list and it is started just prior to starting the drawing phase of traversal. 960 * 961 * @param transition The LayoutTransition to be started on the next traversal. 962 * 963 * @hide 964 */ requestTransitionStart(LayoutTransition transition)965 public void requestTransitionStart(LayoutTransition transition) { 966 if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) { 967 if (mPendingTransitions == null) { 968 mPendingTransitions = new ArrayList<LayoutTransition>(); 969 } 970 mPendingTransitions.add(transition); 971 } 972 } 973 scheduleTraversals()974 void scheduleTraversals() { 975 if (!mTraversalScheduled) { 976 mTraversalScheduled = true; 977 mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); 978 mChoreographer.postCallback( 979 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); 980 scheduleConsumeBatchedInput(); 981 } 982 } 983 unscheduleTraversals()984 void unscheduleTraversals() { 985 if (mTraversalScheduled) { 986 mTraversalScheduled = false; 987 mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); 988 mChoreographer.removeCallbacks( 989 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); 990 } 991 } 992 doTraversal()993 void doTraversal() { 994 if (mTraversalScheduled) { 995 mTraversalScheduled = false; 996 mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); 997 998 if (mProfile) { 999 Debug.startMethodTracing("ViewAncestor"); 1000 } 1001 1002 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals"); 1003 try { 1004 performTraversals(); 1005 } finally { 1006 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 1007 } 1008 1009 if (mProfile) { 1010 Debug.stopMethodTracing(); 1011 mProfile = false; 1012 } 1013 } 1014 } 1015 applyKeepScreenOnFlag(WindowManager.LayoutParams params)1016 private void applyKeepScreenOnFlag(WindowManager.LayoutParams params) { 1017 // Update window's global keep screen on flag: if a view has requested 1018 // that the screen be kept on, then it is always set; otherwise, it is 1019 // set to whatever the client last requested for the global state. 1020 if (mAttachInfo.mKeepScreenOn) { 1021 params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; 1022 } else { 1023 params.flags = (params.flags&~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) 1024 | (mClientWindowLayoutFlags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1025 } 1026 } 1027 collectViewAttributes()1028 private boolean collectViewAttributes() { 1029 final View.AttachInfo attachInfo = mAttachInfo; 1030 if (attachInfo.mRecomputeGlobalAttributes) { 1031 //Log.i(TAG, "Computing view hierarchy attributes!"); 1032 attachInfo.mRecomputeGlobalAttributes = false; 1033 boolean oldScreenOn = attachInfo.mKeepScreenOn; 1034 attachInfo.mKeepScreenOn = false; 1035 attachInfo.mSystemUiVisibility = 0; 1036 attachInfo.mHasSystemUiListeners = false; 1037 mView.dispatchCollectViewAttributes(attachInfo, 0); 1038 attachInfo.mSystemUiVisibility &= ~attachInfo.mDisabledSystemUiVisibility; 1039 WindowManager.LayoutParams params = mWindowAttributes; 1040 if (attachInfo.mKeepScreenOn != oldScreenOn 1041 || attachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility 1042 || attachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) { 1043 applyKeepScreenOnFlag(params); 1044 params.subtreeSystemUiVisibility = attachInfo.mSystemUiVisibility; 1045 params.hasSystemUiListeners = attachInfo.mHasSystemUiListeners; 1046 mView.dispatchWindowSystemUiVisiblityChanged(attachInfo.mSystemUiVisibility); 1047 return true; 1048 } 1049 } 1050 return false; 1051 } 1052 measureHierarchy(final View host, final WindowManager.LayoutParams lp, final Resources res, final int desiredWindowWidth, final int desiredWindowHeight)1053 private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp, 1054 final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) { 1055 int childWidthMeasureSpec; 1056 int childHeightMeasureSpec; 1057 boolean windowSizeMayChange = false; 1058 1059 if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG, 1060 "Measuring " + host + " in display " + desiredWindowWidth 1061 + "x" + desiredWindowHeight + "..."); 1062 1063 boolean goodMeasure = false; 1064 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) { 1065 // On large screens, we don't want to allow dialogs to just 1066 // stretch to fill the entire width of the screen to display 1067 // one line of text. First try doing the layout at a smaller 1068 // size to see if it will fit. 1069 final DisplayMetrics packageMetrics = res.getDisplayMetrics(); 1070 res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true); 1071 int baseSize = 0; 1072 if (mTmpValue.type == TypedValue.TYPE_DIMENSION) { 1073 baseSize = (int)mTmpValue.getDimension(packageMetrics); 1074 } 1075 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize); 1076 if (baseSize != 0 && desiredWindowWidth > baseSize) { 1077 childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); 1078 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); 1079 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 1080 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" 1081 + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); 1082 if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { 1083 goodMeasure = true; 1084 } else { 1085 // Didn't fit in that size... try expanding a bit. 1086 baseSize = (baseSize+desiredWindowWidth)/2; 1087 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize=" 1088 + baseSize); 1089 childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); 1090 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 1091 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" 1092 + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); 1093 if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { 1094 if (DEBUG_DIALOG) Log.v(TAG, "Good!"); 1095 goodMeasure = true; 1096 } 1097 } 1098 } 1099 } 1100 1101 if (!goodMeasure) { 1102 childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); 1103 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); 1104 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 1105 if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) { 1106 windowSizeMayChange = true; 1107 } 1108 } 1109 1110 if (DBG) { 1111 System.out.println("======================================"); 1112 System.out.println("performTraversals -- after measure"); 1113 host.debug(); 1114 } 1115 1116 return windowSizeMayChange; 1117 } 1118 performTraversals()1119 private void performTraversals() { 1120 // cache mView since it is used so much below... 1121 final View host = mView; 1122 1123 if (DBG) { 1124 System.out.println("======================================"); 1125 System.out.println("performTraversals"); 1126 host.debug(); 1127 } 1128 1129 if (host == null || !mAdded) 1130 return; 1131 1132 mIsInTraversal = true; 1133 mWillDrawSoon = true; 1134 boolean windowSizeMayChange = false; 1135 boolean newSurface = false; 1136 boolean surfaceChanged = false; 1137 WindowManager.LayoutParams lp = mWindowAttributes; 1138 1139 int desiredWindowWidth; 1140 int desiredWindowHeight; 1141 1142 final View.AttachInfo attachInfo = mAttachInfo; 1143 1144 final int viewVisibility = getHostVisibility(); 1145 boolean viewVisibilityChanged = mViewVisibility != viewVisibility 1146 || mNewSurfaceNeeded; 1147 1148 WindowManager.LayoutParams params = null; 1149 if (mWindowAttributesChanged) { 1150 mWindowAttributesChanged = false; 1151 surfaceChanged = true; 1152 params = lp; 1153 } 1154 CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get(); 1155 if (compatibilityInfo.supportsScreen() == mLastInCompatMode) { 1156 params = lp; 1157 mFullRedrawNeeded = true; 1158 mLayoutRequested = true; 1159 if (mLastInCompatMode) { 1160 params.flags &= ~WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; 1161 mLastInCompatMode = false; 1162 } else { 1163 params.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; 1164 mLastInCompatMode = true; 1165 } 1166 } 1167 1168 mWindowAttributesChangesFlag = 0; 1169 1170 Rect frame = mWinFrame; 1171 if (mFirst) { 1172 mFullRedrawNeeded = true; 1173 mLayoutRequested = true; 1174 1175 if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) { 1176 // NOTE -- system code, won't try to do compat mode. 1177 Point size = new Point(); 1178 mDisplay.getRealSize(size); 1179 desiredWindowWidth = size.x; 1180 desiredWindowHeight = size.y; 1181 } else { 1182 DisplayMetrics packageMetrics = 1183 mView.getContext().getResources().getDisplayMetrics(); 1184 desiredWindowWidth = packageMetrics.widthPixels; 1185 desiredWindowHeight = packageMetrics.heightPixels; 1186 } 1187 1188 // For the very first time, tell the view hierarchy that it 1189 // is attached to the window. Note that at this point the surface 1190 // object is not initialized to its backing store, but soon it 1191 // will be (assuming the window is visible). 1192 attachInfo.mSurface = mSurface; 1193 // We used to use the following condition to choose 32 bits drawing caches: 1194 // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888 1195 // However, windows are now always 32 bits by default, so choose 32 bits 1196 attachInfo.mUse32BitDrawingCache = true; 1197 attachInfo.mHasWindowFocus = false; 1198 attachInfo.mWindowVisibility = viewVisibility; 1199 attachInfo.mRecomputeGlobalAttributes = false; 1200 viewVisibilityChanged = false; 1201 mLastConfiguration.setTo(host.getResources().getConfiguration()); 1202 mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; 1203 // Set the layout direction if it has not been set before (inherit is the default) 1204 if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) { 1205 host.setLayoutDirection(mLastConfiguration.getLayoutDirection()); 1206 } 1207 host.dispatchAttachedToWindow(attachInfo, 0); 1208 attachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true); 1209 mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets); 1210 host.fitSystemWindows(mFitSystemWindowsInsets); 1211 //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn); 1212 1213 } else { 1214 desiredWindowWidth = frame.width(); 1215 desiredWindowHeight = frame.height(); 1216 if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) { 1217 if (DEBUG_ORIENTATION) Log.v(TAG, 1218 "View " + host + " resized to: " + frame); 1219 mFullRedrawNeeded = true; 1220 mLayoutRequested = true; 1221 windowSizeMayChange = true; 1222 } 1223 } 1224 1225 if (viewVisibilityChanged) { 1226 attachInfo.mWindowVisibility = viewVisibility; 1227 host.dispatchWindowVisibilityChanged(viewVisibility); 1228 if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { 1229 destroyHardwareResources(); 1230 } 1231 if (viewVisibility == View.GONE) { 1232 // After making a window gone, we will count it as being 1233 // shown for the first time the next time it gets focus. 1234 mHasHadWindowFocus = false; 1235 } 1236 } 1237 1238 // Execute enqueued actions on every traversal in case a detached view enqueued an action 1239 getRunQueue().executeActions(attachInfo.mHandler); 1240 1241 boolean insetsChanged = false; 1242 1243 boolean layoutRequested = mLayoutRequested && !mStopped; 1244 if (layoutRequested) { 1245 1246 final Resources res = mView.getContext().getResources(); 1247 1248 if (mFirst) { 1249 // make sure touch mode code executes by setting cached value 1250 // to opposite of the added touch mode. 1251 mAttachInfo.mInTouchMode = !mAddedTouchMode; 1252 ensureTouchModeLocally(mAddedTouchMode); 1253 } else { 1254 if (!mPendingOverscanInsets.equals(mAttachInfo.mOverscanInsets)) { 1255 insetsChanged = true; 1256 } 1257 if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) { 1258 insetsChanged = true; 1259 } 1260 if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) { 1261 mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets); 1262 if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: " 1263 + mAttachInfo.mVisibleInsets); 1264 } 1265 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT 1266 || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { 1267 windowSizeMayChange = true; 1268 1269 if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) { 1270 // NOTE -- system code, won't try to do compat mode. 1271 Point size = new Point(); 1272 mDisplay.getRealSize(size); 1273 desiredWindowWidth = size.x; 1274 desiredWindowHeight = size.y; 1275 } else { 1276 DisplayMetrics packageMetrics = res.getDisplayMetrics(); 1277 desiredWindowWidth = packageMetrics.widthPixels; 1278 desiredWindowHeight = packageMetrics.heightPixels; 1279 } 1280 } 1281 } 1282 1283 // Ask host how big it wants to be 1284 windowSizeMayChange |= measureHierarchy(host, lp, res, 1285 desiredWindowWidth, desiredWindowHeight); 1286 } 1287 1288 if (collectViewAttributes()) { 1289 params = lp; 1290 } 1291 if (attachInfo.mForceReportNewAttributes) { 1292 attachInfo.mForceReportNewAttributes = false; 1293 params = lp; 1294 } 1295 1296 if (mFirst || attachInfo.mViewVisibilityChanged) { 1297 attachInfo.mViewVisibilityChanged = false; 1298 int resizeMode = mSoftInputMode & 1299 WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; 1300 // If we are in auto resize mode, then we need to determine 1301 // what mode to use now. 1302 if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { 1303 final int N = attachInfo.mScrollContainers.size(); 1304 for (int i=0; i<N; i++) { 1305 if (attachInfo.mScrollContainers.get(i).isShown()) { 1306 resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 1307 } 1308 } 1309 if (resizeMode == 0) { 1310 resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; 1311 } 1312 if ((lp.softInputMode & 1313 WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) { 1314 lp.softInputMode = (lp.softInputMode & 1315 ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) | 1316 resizeMode; 1317 params = lp; 1318 } 1319 } 1320 } 1321 1322 if (params != null) { 1323 if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) { 1324 if (!PixelFormat.formatHasAlpha(params.format)) { 1325 params.format = PixelFormat.TRANSLUCENT; 1326 } 1327 } 1328 mAttachInfo.mOverscanRequested = (params.flags 1329 & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0; 1330 } 1331 1332 if (mFitSystemWindowsRequested) { 1333 mFitSystemWindowsRequested = false; 1334 mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets); 1335 mLastOverscanRequested = mAttachInfo.mOverscanRequested; 1336 host.fitSystemWindows(mFitSystemWindowsInsets); 1337 if (mLayoutRequested) { 1338 // Short-circuit catching a new layout request here, so 1339 // we don't need to go through two layout passes when things 1340 // change due to fitting system windows, which can happen a lot. 1341 windowSizeMayChange |= measureHierarchy(host, lp, 1342 mView.getContext().getResources(), 1343 desiredWindowWidth, desiredWindowHeight); 1344 } 1345 } 1346 1347 if (layoutRequested) { 1348 // Clear this now, so that if anything requests a layout in the 1349 // rest of this function we will catch it and re-run a full 1350 // layout pass. 1351 mLayoutRequested = false; 1352 } 1353 1354 boolean windowShouldResize = layoutRequested && windowSizeMayChange 1355 && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) 1356 || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT && 1357 frame.width() < desiredWindowWidth && frame.width() != mWidth) 1358 || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT && 1359 frame.height() < desiredWindowHeight && frame.height() != mHeight)); 1360 1361 final boolean computesInternalInsets = 1362 attachInfo.mTreeObserver.hasComputeInternalInsetsListeners(); 1363 1364 boolean insetsPending = false; 1365 int relayoutResult = 0; 1366 1367 if (mFirst || windowShouldResize || insetsChanged || 1368 viewVisibilityChanged || params != null) { 1369 1370 if (viewVisibility == View.VISIBLE) { 1371 // If this window is giving internal insets to the window 1372 // manager, and it is being added or changing its visibility, 1373 // then we want to first give the window manager "fake" 1374 // insets to cause it to effectively ignore the content of 1375 // the window during layout. This avoids it briefly causing 1376 // other windows to resize/move based on the raw frame of the 1377 // window, waiting until we can finish laying out this window 1378 // and get back to the window manager with the ultimately 1379 // computed insets. 1380 insetsPending = computesInternalInsets && (mFirst || viewVisibilityChanged); 1381 } 1382 1383 if (mSurfaceHolder != null) { 1384 mSurfaceHolder.mSurfaceLock.lock(); 1385 mDrawingAllowed = true; 1386 } 1387 1388 boolean hwInitialized = false; 1389 boolean contentInsetsChanged = false; 1390 boolean hadSurface = mSurface.isValid(); 1391 1392 try { 1393 if (DEBUG_LAYOUT) { 1394 Log.i(TAG, "host=w:" + host.getMeasuredWidth() + ", h:" + 1395 host.getMeasuredHeight() + ", params=" + params); 1396 } 1397 1398 final int surfaceGenerationId = mSurface.getGenerationId(); 1399 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); 1400 if (!mDrawDuringWindowsAnimating) { 1401 mWindowsAnimating |= 1402 (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0; 1403 } 1404 1405 if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString() 1406 + " overscan=" + mPendingOverscanInsets.toShortString() 1407 + " content=" + mPendingContentInsets.toShortString() 1408 + " visible=" + mPendingVisibleInsets.toShortString() 1409 + " surface=" + mSurface); 1410 1411 if (mPendingConfiguration.seq != 0) { 1412 if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: " 1413 + mPendingConfiguration); 1414 updateConfiguration(mPendingConfiguration, !mFirst); 1415 mPendingConfiguration.seq = 0; 1416 } 1417 1418 final boolean overscanInsetsChanged = !mPendingOverscanInsets.equals( 1419 mAttachInfo.mOverscanInsets); 1420 contentInsetsChanged = !mPendingContentInsets.equals( 1421 mAttachInfo.mContentInsets); 1422 final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals( 1423 mAttachInfo.mVisibleInsets); 1424 if (contentInsetsChanged) { 1425 if (mWidth > 0 && mHeight > 0 && lp != null && 1426 ((lp.systemUiVisibility|lp.subtreeSystemUiVisibility) 1427 & View.SYSTEM_UI_LAYOUT_FLAGS) == 0 && 1428 mSurface != null && mSurface.isValid() && 1429 !mAttachInfo.mTurnOffWindowResizeAnim && 1430 mAttachInfo.mHardwareRenderer != null && 1431 mAttachInfo.mHardwareRenderer.isEnabled() && 1432 mAttachInfo.mHardwareRenderer.validate() && 1433 lp != null && !PixelFormat.formatHasAlpha(lp.format)) { 1434 1435 disposeResizeBuffer(); 1436 1437 boolean completed = false; 1438 HardwareCanvas hwRendererCanvas = mAttachInfo.mHardwareRenderer.getCanvas(); 1439 HardwareCanvas layerCanvas = null; 1440 try { 1441 if (mResizeBuffer == null) { 1442 mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer( 1443 mWidth, mHeight, false); 1444 } else if (mResizeBuffer.getWidth() != mWidth || 1445 mResizeBuffer.getHeight() != mHeight) { 1446 mResizeBuffer.resize(mWidth, mHeight); 1447 } 1448 // TODO: should handle create/resize failure 1449 layerCanvas = mResizeBuffer.start(hwRendererCanvas); 1450 final int restoreCount = layerCanvas.save(); 1451 1452 int yoff; 1453 final boolean scrolling = mScroller != null 1454 && mScroller.computeScrollOffset(); 1455 if (scrolling) { 1456 yoff = mScroller.getCurrY(); 1457 mScroller.abortAnimation(); 1458 } else { 1459 yoff = mScrollY; 1460 } 1461 1462 layerCanvas.translate(0, -yoff); 1463 if (mTranslator != null) { 1464 mTranslator.translateCanvas(layerCanvas); 1465 } 1466 1467 DisplayList displayList = mView.mDisplayList; 1468 if (displayList != null) { 1469 layerCanvas.drawDisplayList(displayList, null, 1470 DisplayList.FLAG_CLIP_CHILDREN); 1471 } else { 1472 mView.draw(layerCanvas); 1473 } 1474 1475 drawAccessibilityFocusedDrawableIfNeeded(layerCanvas); 1476 1477 mResizeBufferStartTime = SystemClock.uptimeMillis(); 1478 mResizeBufferDuration = mView.getResources().getInteger( 1479 com.android.internal.R.integer.config_mediumAnimTime); 1480 completed = true; 1481 1482 layerCanvas.restoreToCount(restoreCount); 1483 } catch (OutOfMemoryError e) { 1484 Log.w(TAG, "Not enough memory for content change anim buffer", e); 1485 } finally { 1486 if (mResizeBuffer != null) { 1487 mResizeBuffer.end(hwRendererCanvas); 1488 if (!completed) { 1489 mResizeBuffer.destroy(); 1490 mResizeBuffer = null; 1491 } 1492 } 1493 } 1494 } 1495 mAttachInfo.mContentInsets.set(mPendingContentInsets); 1496 if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: " 1497 + mAttachInfo.mContentInsets); 1498 } 1499 if (overscanInsetsChanged) { 1500 mAttachInfo.mOverscanInsets.set(mPendingOverscanInsets); 1501 if (DEBUG_LAYOUT) Log.v(TAG, "Overscan insets changing to: " 1502 + mAttachInfo.mOverscanInsets); 1503 // Need to relayout with content insets. 1504 contentInsetsChanged = true; 1505 } 1506 if (contentInsetsChanged || mLastSystemUiVisibility != 1507 mAttachInfo.mSystemUiVisibility || mFitSystemWindowsRequested 1508 || mLastOverscanRequested != mAttachInfo.mOverscanRequested) { 1509 mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; 1510 mLastOverscanRequested = mAttachInfo.mOverscanRequested; 1511 mFitSystemWindowsRequested = false; 1512 mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets); 1513 host.fitSystemWindows(mFitSystemWindowsInsets); 1514 } 1515 if (visibleInsetsChanged) { 1516 mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets); 1517 if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: " 1518 + mAttachInfo.mVisibleInsets); 1519 } 1520 1521 if (!hadSurface) { 1522 if (mSurface.isValid()) { 1523 // If we are creating a new surface, then we need to 1524 // completely redraw it. Also, when we get to the 1525 // point of drawing it we will hold off and schedule 1526 // a new traversal instead. This is so we can tell the 1527 // window manager about all of the windows being displayed 1528 // before actually drawing them, so it can display then 1529 // all at once. 1530 newSurface = true; 1531 mFullRedrawNeeded = true; 1532 mPreviousTransparentRegion.setEmpty(); 1533 1534 if (mAttachInfo.mHardwareRenderer != null) { 1535 try { 1536 hwInitialized = mAttachInfo.mHardwareRenderer.initialize( 1537 mHolder.getSurface()); 1538 } catch (Surface.OutOfResourcesException e) { 1539 handleOutOfResourcesException(e); 1540 return; 1541 } 1542 } 1543 } 1544 } else if (!mSurface.isValid()) { 1545 // If the surface has been removed, then reset the scroll 1546 // positions. 1547 if (mLastScrolledFocus != null) { 1548 mLastScrolledFocus.clear(); 1549 } 1550 mScrollY = mCurScrollY = 0; 1551 if (mScroller != null) { 1552 mScroller.abortAnimation(); 1553 } 1554 disposeResizeBuffer(); 1555 // Our surface is gone 1556 if (mAttachInfo.mHardwareRenderer != null && 1557 mAttachInfo.mHardwareRenderer.isEnabled()) { 1558 mAttachInfo.mHardwareRenderer.destroy(true); 1559 } 1560 } else if (surfaceGenerationId != mSurface.getGenerationId() && 1561 mSurfaceHolder == null && mAttachInfo.mHardwareRenderer != null) { 1562 mFullRedrawNeeded = true; 1563 try { 1564 mAttachInfo.mHardwareRenderer.updateSurface(mHolder.getSurface()); 1565 } catch (Surface.OutOfResourcesException e) { 1566 handleOutOfResourcesException(e); 1567 return; 1568 } 1569 } 1570 } catch (RemoteException e) { 1571 } 1572 1573 if (DEBUG_ORIENTATION) Log.v( 1574 TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface); 1575 1576 attachInfo.mWindowLeft = frame.left; 1577 attachInfo.mWindowTop = frame.top; 1578 1579 // !!FIXME!! This next section handles the case where we did not get the 1580 // window size we asked for. We should avoid this by getting a maximum size from 1581 // the window session beforehand. 1582 if (mWidth != frame.width() || mHeight != frame.height()) { 1583 mWidth = frame.width(); 1584 mHeight = frame.height(); 1585 } 1586 1587 if (mSurfaceHolder != null) { 1588 // The app owns the surface; tell it about what is going on. 1589 if (mSurface.isValid()) { 1590 // XXX .copyFrom() doesn't work! 1591 //mSurfaceHolder.mSurface.copyFrom(mSurface); 1592 mSurfaceHolder.mSurface = mSurface; 1593 } 1594 mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight); 1595 mSurfaceHolder.mSurfaceLock.unlock(); 1596 if (mSurface.isValid()) { 1597 if (!hadSurface) { 1598 mSurfaceHolder.ungetCallbacks(); 1599 1600 mIsCreating = true; 1601 mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder); 1602 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1603 if (callbacks != null) { 1604 for (SurfaceHolder.Callback c : callbacks) { 1605 c.surfaceCreated(mSurfaceHolder); 1606 } 1607 } 1608 surfaceChanged = true; 1609 } 1610 if (surfaceChanged) { 1611 mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder, 1612 lp.format, mWidth, mHeight); 1613 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1614 if (callbacks != null) { 1615 for (SurfaceHolder.Callback c : callbacks) { 1616 c.surfaceChanged(mSurfaceHolder, lp.format, 1617 mWidth, mHeight); 1618 } 1619 } 1620 } 1621 mIsCreating = false; 1622 } else if (hadSurface) { 1623 mSurfaceHolder.ungetCallbacks(); 1624 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1625 mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder); 1626 if (callbacks != null) { 1627 for (SurfaceHolder.Callback c : callbacks) { 1628 c.surfaceDestroyed(mSurfaceHolder); 1629 } 1630 } 1631 mSurfaceHolder.mSurfaceLock.lock(); 1632 try { 1633 mSurfaceHolder.mSurface = new Surface(); 1634 } finally { 1635 mSurfaceHolder.mSurfaceLock.unlock(); 1636 } 1637 } 1638 } 1639 1640 if (mAttachInfo.mHardwareRenderer != null && 1641 mAttachInfo.mHardwareRenderer.isEnabled()) { 1642 if (hwInitialized || 1643 mWidth != mAttachInfo.mHardwareRenderer.getWidth() || 1644 mHeight != mAttachInfo.mHardwareRenderer.getHeight()) { 1645 mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight); 1646 if (!hwInitialized) { 1647 mAttachInfo.mHardwareRenderer.invalidate(mHolder.getSurface()); 1648 mFullRedrawNeeded = true; 1649 } 1650 } 1651 } 1652 1653 if (!mStopped) { 1654 boolean focusChangedDueToTouchMode = ensureTouchModeLocally( 1655 (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0); 1656 if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() 1657 || mHeight != host.getMeasuredHeight() || contentInsetsChanged) { 1658 int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); 1659 int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); 1660 1661 if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed! mWidth=" 1662 + mWidth + " measuredWidth=" + host.getMeasuredWidth() 1663 + " mHeight=" + mHeight 1664 + " measuredHeight=" + host.getMeasuredHeight() 1665 + " coveredInsetsChanged=" + contentInsetsChanged); 1666 1667 // Ask host how big it wants to be 1668 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 1669 1670 // Implementation of weights from WindowManager.LayoutParams 1671 // We just grow the dimensions as needed and re-measure if 1672 // needs be 1673 int width = host.getMeasuredWidth(); 1674 int height = host.getMeasuredHeight(); 1675 boolean measureAgain = false; 1676 1677 if (lp.horizontalWeight > 0.0f) { 1678 width += (int) ((mWidth - width) * lp.horizontalWeight); 1679 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, 1680 MeasureSpec.EXACTLY); 1681 measureAgain = true; 1682 } 1683 if (lp.verticalWeight > 0.0f) { 1684 height += (int) ((mHeight - height) * lp.verticalWeight); 1685 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 1686 MeasureSpec.EXACTLY); 1687 measureAgain = true; 1688 } 1689 1690 if (measureAgain) { 1691 if (DEBUG_LAYOUT) Log.v(TAG, 1692 "And hey let's measure once more: width=" + width 1693 + " height=" + height); 1694 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 1695 } 1696 1697 layoutRequested = true; 1698 } 1699 } 1700 } else { 1701 // Not the first pass and no window/insets/visibility change but the window 1702 // may have moved and we need check that and if so to update the left and right 1703 // in the attach info. We translate only the window frame since on window move 1704 // the window manager tells us only for the new frame but the insets are the 1705 // same and we do not want to translate them more than once. 1706 1707 // TODO: Well, we are checking whether the frame has changed similarly 1708 // to how this is done for the insets. This is however incorrect since 1709 // the insets and the frame are translated. For example, the old frame 1710 // was (1, 1 - 1, 1) and was translated to say (2, 2 - 2, 2), now the new 1711 // reported frame is (2, 2 - 2, 2) which implies no change but this is not 1712 // true since we are comparing a not translated value to a translated one. 1713 // This scenario is rare but we may want to fix that. 1714 1715 final boolean windowMoved = (attachInfo.mWindowLeft != frame.left 1716 || attachInfo.mWindowTop != frame.top); 1717 if (windowMoved) { 1718 if (mTranslator != null) { 1719 mTranslator.translateRectInScreenToAppWinFrame(frame); 1720 } 1721 attachInfo.mWindowLeft = frame.left; 1722 attachInfo.mWindowTop = frame.top; 1723 } 1724 } 1725 1726 final boolean didLayout = layoutRequested && !mStopped; 1727 boolean triggerGlobalLayoutListener = didLayout 1728 || attachInfo.mRecomputeGlobalAttributes; 1729 if (didLayout) { 1730 performLayout(lp, desiredWindowWidth, desiredWindowHeight); 1731 1732 // By this point all views have been sized and positioned 1733 // We can compute the transparent area 1734 1735 if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) { 1736 // start out transparent 1737 // TODO: AVOID THAT CALL BY CACHING THE RESULT? 1738 host.getLocationInWindow(mTmpLocation); 1739 mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1], 1740 mTmpLocation[0] + host.mRight - host.mLeft, 1741 mTmpLocation[1] + host.mBottom - host.mTop); 1742 1743 host.gatherTransparentRegion(mTransparentRegion); 1744 if (mTranslator != null) { 1745 mTranslator.translateRegionInWindowToScreen(mTransparentRegion); 1746 } 1747 1748 if (!mTransparentRegion.equals(mPreviousTransparentRegion)) { 1749 mPreviousTransparentRegion.set(mTransparentRegion); 1750 mFullRedrawNeeded = true; 1751 // reconfigure window manager 1752 try { 1753 mWindowSession.setTransparentRegion(mWindow, mTransparentRegion); 1754 } catch (RemoteException e) { 1755 } 1756 } 1757 } 1758 1759 if (DBG) { 1760 System.out.println("======================================"); 1761 System.out.println("performTraversals -- after setFrame"); 1762 host.debug(); 1763 } 1764 } 1765 1766 if (triggerGlobalLayoutListener) { 1767 attachInfo.mRecomputeGlobalAttributes = false; 1768 attachInfo.mTreeObserver.dispatchOnGlobalLayout(); 1769 1770 if (AccessibilityManager.getInstance(host.mContext).isEnabled()) { 1771 postSendWindowContentChangedCallback(mView); 1772 } 1773 } 1774 1775 if (computesInternalInsets) { 1776 // Clear the original insets. 1777 final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets; 1778 insets.reset(); 1779 1780 // Compute new insets in place. 1781 attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets); 1782 1783 // Tell the window manager. 1784 if (insetsPending || !mLastGivenInsets.equals(insets)) { 1785 mLastGivenInsets.set(insets); 1786 1787 // Translate insets to screen coordinates if needed. 1788 final Rect contentInsets; 1789 final Rect visibleInsets; 1790 final Region touchableRegion; 1791 if (mTranslator != null) { 1792 contentInsets = mTranslator.getTranslatedContentInsets(insets.contentInsets); 1793 visibleInsets = mTranslator.getTranslatedVisibleInsets(insets.visibleInsets); 1794 touchableRegion = mTranslator.getTranslatedTouchableArea(insets.touchableRegion); 1795 } else { 1796 contentInsets = insets.contentInsets; 1797 visibleInsets = insets.visibleInsets; 1798 touchableRegion = insets.touchableRegion; 1799 } 1800 1801 try { 1802 mWindowSession.setInsets(mWindow, insets.mTouchableInsets, 1803 contentInsets, visibleInsets, touchableRegion); 1804 } catch (RemoteException e) { 1805 } 1806 } 1807 } 1808 1809 boolean skipDraw = false; 1810 1811 if (mFirst) { 1812 // handle first focus request 1813 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()=" 1814 + mView.hasFocus()); 1815 if (mView != null) { 1816 if (!mView.hasFocus()) { 1817 mView.requestFocus(View.FOCUS_FORWARD); 1818 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view=" 1819 + mView.findFocus()); 1820 } else { 1821 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view=" 1822 + mView.findFocus()); 1823 } 1824 } 1825 if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0) { 1826 // The first time we relayout the window, if the system is 1827 // doing window animations, we want to hold of on any future 1828 // draws until the animation is done. 1829 mWindowsAnimating = true; 1830 } 1831 } else if (mWindowsAnimating) { 1832 skipDraw = true; 1833 } 1834 1835 mFirst = false; 1836 mWillDrawSoon = false; 1837 mNewSurfaceNeeded = false; 1838 mViewVisibility = viewVisibility; 1839 1840 if (mAttachInfo.mHasWindowFocus) { 1841 final boolean imTarget = WindowManager.LayoutParams 1842 .mayUseInputMethod(mWindowAttributes.flags); 1843 if (imTarget != mLastWasImTarget) { 1844 mLastWasImTarget = imTarget; 1845 InputMethodManager imm = InputMethodManager.peekInstance(); 1846 if (imm != null && imTarget) { 1847 imm.startGettingWindowFocus(mView); 1848 imm.onWindowFocus(mView, mView.findFocus(), 1849 mWindowAttributes.softInputMode, 1850 !mHasHadWindowFocus, mWindowAttributes.flags); 1851 } 1852 } 1853 } 1854 1855 // Remember if we must report the next draw. 1856 if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { 1857 mReportNextDraw = true; 1858 } 1859 1860 boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() || 1861 viewVisibility != View.VISIBLE; 1862 1863 if (!cancelDraw && !newSurface) { 1864 if (!skipDraw || mReportNextDraw) { 1865 if (mPendingTransitions != null && mPendingTransitions.size() > 0) { 1866 for (int i = 0; i < mPendingTransitions.size(); ++i) { 1867 mPendingTransitions.get(i).startChangingAnimations(); 1868 } 1869 mPendingTransitions.clear(); 1870 } 1871 1872 performDraw(); 1873 } 1874 } else { 1875 if (viewVisibility == View.VISIBLE) { 1876 // Try again 1877 scheduleTraversals(); 1878 } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) { 1879 for (int i = 0; i < mPendingTransitions.size(); ++i) { 1880 mPendingTransitions.get(i).endChangingAnimations(); 1881 } 1882 mPendingTransitions.clear(); 1883 } 1884 } 1885 1886 mIsInTraversal = false; 1887 } 1888 handleOutOfResourcesException(Surface.OutOfResourcesException e)1889 private void handleOutOfResourcesException(Surface.OutOfResourcesException e) { 1890 Log.e(TAG, "OutOfResourcesException initializing HW surface", e); 1891 try { 1892 if (!mWindowSession.outOfMemory(mWindow) && 1893 Process.myUid() != Process.SYSTEM_UID) { 1894 Slog.w(TAG, "No processes killed for memory; killing self"); 1895 Process.killProcess(Process.myPid()); 1896 } 1897 } catch (RemoteException ex) { 1898 } 1899 mLayoutRequested = true; // ask wm for a new surface next time. 1900 } 1901 performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec)1902 private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { 1903 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); 1904 try { 1905 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 1906 } finally { 1907 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 1908 } 1909 } 1910 1911 /** 1912 * Called by {@link android.view.View#isInLayout()} to determine whether the view hierarchy 1913 * is currently undergoing a layout pass. 1914 * 1915 * @return whether the view hierarchy is currently undergoing a layout pass 1916 */ isInLayout()1917 boolean isInLayout() { 1918 return mInLayout; 1919 } 1920 1921 /** 1922 * Called by {@link android.view.View#requestLayout()} if the view hierarchy is currently 1923 * undergoing a layout pass. requestLayout() should not generally be called during layout, 1924 * unless the container hierarchy knows what it is doing (i.e., it is fine as long as 1925 * all children in that container hierarchy are measured and laid out at the end of the layout 1926 * pass for that container). If requestLayout() is called anyway, we handle it correctly 1927 * by registering all requesters during a frame as it proceeds. At the end of the frame, 1928 * we check all of those views to see if any still have pending layout requests, which 1929 * indicates that they were not correctly handled by their container hierarchy. If that is 1930 * the case, we clear all such flags in the tree, to remove the buggy flag state that leads 1931 * to blank containers, and force a second request/measure/layout pass in this frame. If 1932 * more requestLayout() calls are received during that second layout pass, we post those 1933 * requests to the next frame to avoid possible infinite loops. 1934 * 1935 * <p>The return value from this method indicates whether the request should proceed 1936 * (if it is a request during the first layout pass) or should be skipped and posted to the 1937 * next frame (if it is a request during the second layout pass).</p> 1938 * 1939 * @param view the view that requested the layout. 1940 * 1941 * @return true if request should proceed, false otherwise. 1942 */ requestLayoutDuringLayout(final View view)1943 boolean requestLayoutDuringLayout(final View view) { 1944 if (view.mParent == null || view.mAttachInfo == null) { 1945 // Would not normally trigger another layout, so just let it pass through as usual 1946 return true; 1947 } 1948 if (!mLayoutRequesters.contains(view)) { 1949 mLayoutRequesters.add(view); 1950 } 1951 if (!mHandlingLayoutInLayoutRequest) { 1952 // Let the request proceed normally; it will be processed in a second layout pass 1953 // if necessary 1954 return true; 1955 } else { 1956 // Don't let the request proceed during the second layout pass. 1957 // It will post to the next frame instead. 1958 return false; 1959 } 1960 } 1961 performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight)1962 private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, 1963 int desiredWindowHeight) { 1964 mLayoutRequested = false; 1965 mScrollMayChange = true; 1966 mInLayout = true; 1967 1968 final View host = mView; 1969 if (DEBUG_ORIENTATION || DEBUG_LAYOUT) { 1970 Log.v(TAG, "Laying out " + host + " to (" + 1971 host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")"); 1972 } 1973 1974 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout"); 1975 try { 1976 host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); 1977 1978 mInLayout = false; 1979 int numViewsRequestingLayout = mLayoutRequesters.size(); 1980 if (numViewsRequestingLayout > 0) { 1981 // requestLayout() was called during layout. 1982 // If no layout-request flags are set on the requesting views, there is no problem. 1983 // If some requests are still pending, then we need to clear those flags and do 1984 // a full request/measure/layout pass to handle this situation. 1985 ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, 1986 false); 1987 if (validLayoutRequesters != null) { 1988 // Set this flag to indicate that any further requests are happening during 1989 // the second pass, which may result in posting those requests to the next 1990 // frame instead 1991 mHandlingLayoutInLayoutRequest = true; 1992 1993 // Process fresh layout requests, then measure and layout 1994 int numValidRequests = validLayoutRequesters.size(); 1995 for (int i = 0; i < numValidRequests; ++i) { 1996 final View view = validLayoutRequesters.get(i); 1997 Log.w("View", "requestLayout() improperly called by " + view + 1998 " during layout: running second layout pass"); 1999 view.requestLayout(); 2000 } 2001 measureHierarchy(host, lp, mView.getContext().getResources(), 2002 desiredWindowWidth, desiredWindowHeight); 2003 mInLayout = true; 2004 host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); 2005 2006 mHandlingLayoutInLayoutRequest = false; 2007 2008 // Check the valid requests again, this time without checking/clearing the 2009 // layout flags, since requests happening during the second pass get noop'd 2010 validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true); 2011 if (validLayoutRequesters != null) { 2012 final ArrayList<View> finalRequesters = validLayoutRequesters; 2013 // Post second-pass requests to the next frame 2014 getRunQueue().post(new Runnable() { 2015 @Override 2016 public void run() { 2017 int numValidRequests = finalRequesters.size(); 2018 for (int i = 0; i < numValidRequests; ++i) { 2019 final View view = finalRequesters.get(i); 2020 Log.w("View", "requestLayout() improperly called by " + view + 2021 " during second layout pass: posting in next frame"); 2022 view.requestLayout(); 2023 } 2024 } 2025 }); 2026 } 2027 } 2028 2029 } 2030 } finally { 2031 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 2032 } 2033 mInLayout = false; 2034 } 2035 2036 /** 2037 * This method is called during layout when there have been calls to requestLayout() during 2038 * layout. It walks through the list of views that requested layout to determine which ones 2039 * still need it, based on visibility in the hierarchy and whether they have already been 2040 * handled (as is usually the case with ListView children). 2041 * 2042 * @param layoutRequesters The list of views that requested layout during layout 2043 * @param secondLayoutRequests Whether the requests were issued during the second layout pass. 2044 * If so, the FORCE_LAYOUT flag was not set on requesters. 2045 * @return A list of the actual views that still need to be laid out. 2046 */ getValidLayoutRequesters(ArrayList<View> layoutRequesters, boolean secondLayoutRequests)2047 private ArrayList<View> getValidLayoutRequesters(ArrayList<View> layoutRequesters, 2048 boolean secondLayoutRequests) { 2049 2050 int numViewsRequestingLayout = layoutRequesters.size(); 2051 ArrayList<View> validLayoutRequesters = null; 2052 for (int i = 0; i < numViewsRequestingLayout; ++i) { 2053 View view = layoutRequesters.get(i); 2054 if (view != null && view.mAttachInfo != null && view.mParent != null && 2055 (secondLayoutRequests || (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) == 2056 View.PFLAG_FORCE_LAYOUT)) { 2057 boolean gone = false; 2058 View parent = view; 2059 // Only trigger new requests for views in a non-GONE hierarchy 2060 while (parent != null) { 2061 if ((parent.mViewFlags & View.VISIBILITY_MASK) == View.GONE) { 2062 gone = true; 2063 break; 2064 } 2065 if (parent.mParent instanceof View) { 2066 parent = (View) parent.mParent; 2067 } else { 2068 parent = null; 2069 } 2070 } 2071 if (!gone) { 2072 if (validLayoutRequesters == null) { 2073 validLayoutRequesters = new ArrayList<View>(); 2074 } 2075 validLayoutRequesters.add(view); 2076 } 2077 } 2078 } 2079 if (!secondLayoutRequests) { 2080 // If we're checking the layout flags, then we need to clean them up also 2081 for (int i = 0; i < numViewsRequestingLayout; ++i) { 2082 View view = layoutRequesters.get(i); 2083 while (view != null && 2084 (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) != 0) { 2085 view.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT; 2086 if (view.mParent instanceof View) { 2087 view = (View) view.mParent; 2088 } else { 2089 view = null; 2090 } 2091 } 2092 } 2093 } 2094 layoutRequesters.clear(); 2095 return validLayoutRequesters; 2096 } 2097 requestTransparentRegion(View child)2098 public void requestTransparentRegion(View child) { 2099 // the test below should not fail unless someone is messing with us 2100 checkThread(); 2101 if (mView == child) { 2102 mView.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS; 2103 // Need to make sure we re-evaluate the window attributes next 2104 // time around, to ensure the window has the correct format. 2105 mWindowAttributesChanged = true; 2106 mWindowAttributesChangesFlag = 0; 2107 requestLayout(); 2108 } 2109 } 2110 2111 /** 2112 * Figures out the measure spec for the root view in a window based on it's 2113 * layout params. 2114 * 2115 * @param windowSize 2116 * The available width or height of the window 2117 * 2118 * @param rootDimension 2119 * The layout params for one dimension (width or height) of the 2120 * window. 2121 * 2122 * @return The measure spec to use to measure the root view. 2123 */ getRootMeasureSpec(int windowSize, int rootDimension)2124 private static int getRootMeasureSpec(int windowSize, int rootDimension) { 2125 int measureSpec; 2126 switch (rootDimension) { 2127 2128 case ViewGroup.LayoutParams.MATCH_PARENT: 2129 // Window can't resize. Force root view to be windowSize. 2130 measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); 2131 break; 2132 case ViewGroup.LayoutParams.WRAP_CONTENT: 2133 // Window can resize. Set max size for root view. 2134 measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); 2135 break; 2136 default: 2137 // Window wants to be an exact size. Force root view to be that size. 2138 measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); 2139 break; 2140 } 2141 return measureSpec; 2142 } 2143 2144 int mHardwareYOffset; 2145 int mResizeAlpha; 2146 final Paint mResizePaint = new Paint(); 2147 onHardwarePreDraw(HardwareCanvas canvas)2148 public void onHardwarePreDraw(HardwareCanvas canvas) { 2149 canvas.translate(0, -mHardwareYOffset); 2150 } 2151 onHardwarePostDraw(HardwareCanvas canvas)2152 public void onHardwarePostDraw(HardwareCanvas canvas) { 2153 if (mResizeBuffer != null) { 2154 mResizePaint.setAlpha(mResizeAlpha); 2155 canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint); 2156 } 2157 drawAccessibilityFocusedDrawableIfNeeded(canvas); 2158 } 2159 2160 /** 2161 * @hide 2162 */ outputDisplayList(View view)2163 void outputDisplayList(View view) { 2164 if (mAttachInfo != null && mAttachInfo.mHardwareCanvas != null) { 2165 DisplayList displayList = view.getDisplayList(); 2166 if (displayList != null) { 2167 mAttachInfo.mHardwareCanvas.outputDisplayList(displayList); 2168 } 2169 } 2170 } 2171 2172 /** 2173 * @see #PROPERTY_PROFILE_RENDERING 2174 */ profileRendering(boolean enabled)2175 private void profileRendering(boolean enabled) { 2176 if (mProfileRendering) { 2177 mRenderProfilingEnabled = enabled; 2178 2179 if (mRenderProfiler != null) { 2180 mChoreographer.removeFrameCallback(mRenderProfiler); 2181 } 2182 if (mRenderProfilingEnabled) { 2183 if (mRenderProfiler == null) { 2184 mRenderProfiler = new Choreographer.FrameCallback() { 2185 @Override 2186 public void doFrame(long frameTimeNanos) { 2187 mDirty.set(0, 0, mWidth, mHeight); 2188 scheduleTraversals(); 2189 if (mRenderProfilingEnabled) { 2190 mChoreographer.postFrameCallback(mRenderProfiler); 2191 } 2192 } 2193 }; 2194 } 2195 mChoreographer.postFrameCallback(mRenderProfiler); 2196 } else { 2197 mRenderProfiler = null; 2198 } 2199 } 2200 } 2201 2202 /** 2203 * Called from draw() when DEBUG_FPS is enabled 2204 */ trackFPS()2205 private void trackFPS() { 2206 // Tracks frames per second drawn. First value in a series of draws may be bogus 2207 // because it down not account for the intervening idle time 2208 long nowTime = System.currentTimeMillis(); 2209 if (mFpsStartTime < 0) { 2210 mFpsStartTime = mFpsPrevTime = nowTime; 2211 mFpsNumFrames = 0; 2212 } else { 2213 ++mFpsNumFrames; 2214 String thisHash = Integer.toHexString(System.identityHashCode(this)); 2215 long frameTime = nowTime - mFpsPrevTime; 2216 long totalTime = nowTime - mFpsStartTime; 2217 Log.v(TAG, "0x" + thisHash + "\tFrame time:\t" + frameTime); 2218 mFpsPrevTime = nowTime; 2219 if (totalTime > 1000) { 2220 float fps = (float) mFpsNumFrames * 1000 / totalTime; 2221 Log.v(TAG, "0x" + thisHash + "\tFPS:\t" + fps); 2222 mFpsStartTime = nowTime; 2223 mFpsNumFrames = 0; 2224 } 2225 } 2226 } 2227 performDraw()2228 private void performDraw() { 2229 if (!mAttachInfo.mScreenOn && !mReportNextDraw) { 2230 return; 2231 } 2232 2233 final boolean fullRedrawNeeded = mFullRedrawNeeded; 2234 mFullRedrawNeeded = false; 2235 2236 mIsDrawing = true; 2237 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw"); 2238 try { 2239 draw(fullRedrawNeeded); 2240 } finally { 2241 mIsDrawing = false; 2242 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 2243 } 2244 2245 if (mReportNextDraw) { 2246 mReportNextDraw = false; 2247 2248 if (LOCAL_LOGV) { 2249 Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle()); 2250 } 2251 if (mSurfaceHolder != null && mSurface.isValid()) { 2252 mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder); 2253 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 2254 if (callbacks != null) { 2255 for (SurfaceHolder.Callback c : callbacks) { 2256 if (c instanceof SurfaceHolder.Callback2) { 2257 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 2258 mSurfaceHolder); 2259 } 2260 } 2261 } 2262 } 2263 try { 2264 mWindowSession.finishDrawing(mWindow); 2265 } catch (RemoteException e) { 2266 } 2267 } 2268 } 2269 draw(boolean fullRedrawNeeded)2270 private void draw(boolean fullRedrawNeeded) { 2271 Surface surface = mSurface; 2272 if (!surface.isValid()) { 2273 return; 2274 } 2275 2276 if (DEBUG_FPS) { 2277 trackFPS(); 2278 } 2279 2280 if (!sFirstDrawComplete) { 2281 synchronized (sFirstDrawHandlers) { 2282 sFirstDrawComplete = true; 2283 final int count = sFirstDrawHandlers.size(); 2284 for (int i = 0; i< count; i++) { 2285 mHandler.post(sFirstDrawHandlers.get(i)); 2286 } 2287 } 2288 } 2289 2290 scrollToRectOrFocus(null, false); 2291 2292 final AttachInfo attachInfo = mAttachInfo; 2293 if (attachInfo.mViewScrollChanged) { 2294 attachInfo.mViewScrollChanged = false; 2295 attachInfo.mTreeObserver.dispatchOnScrollChanged(); 2296 } 2297 2298 int yoff; 2299 boolean animating = mScroller != null && mScroller.computeScrollOffset(); 2300 if (animating) { 2301 yoff = mScroller.getCurrY(); 2302 } else { 2303 yoff = mScrollY; 2304 } 2305 if (mCurScrollY != yoff) { 2306 mCurScrollY = yoff; 2307 fullRedrawNeeded = true; 2308 } 2309 2310 final float appScale = attachInfo.mApplicationScale; 2311 final boolean scalingRequired = attachInfo.mScalingRequired; 2312 2313 int resizeAlpha = 0; 2314 if (mResizeBuffer != null) { 2315 long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime; 2316 if (deltaTime < mResizeBufferDuration) { 2317 float amt = deltaTime/(float) mResizeBufferDuration; 2318 amt = mResizeInterpolator.getInterpolation(amt); 2319 animating = true; 2320 resizeAlpha = 255 - (int)(amt*255); 2321 } else { 2322 disposeResizeBuffer(); 2323 } 2324 } 2325 2326 final Rect dirty = mDirty; 2327 if (mSurfaceHolder != null) { 2328 // The app owns the surface, we won't draw. 2329 dirty.setEmpty(); 2330 if (animating) { 2331 if (mScroller != null) { 2332 mScroller.abortAnimation(); 2333 } 2334 disposeResizeBuffer(); 2335 } 2336 return; 2337 } 2338 2339 if (fullRedrawNeeded) { 2340 attachInfo.mIgnoreDirtyState = true; 2341 dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); 2342 } 2343 2344 if (DEBUG_ORIENTATION || DEBUG_DRAW) { 2345 Log.v(TAG, "Draw " + mView + "/" 2346 + mWindowAttributes.getTitle() 2347 + ": dirty={" + dirty.left + "," + dirty.top 2348 + "," + dirty.right + "," + dirty.bottom + "} surface=" 2349 + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" + 2350 appScale + ", width=" + mWidth + ", height=" + mHeight); 2351 } 2352 2353 invalidateDisplayLists(); 2354 2355 attachInfo.mTreeObserver.dispatchOnDraw(); 2356 2357 if (!dirty.isEmpty() || mIsAnimating) { 2358 if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) { 2359 // Draw with hardware renderer. 2360 mIsAnimating = false; 2361 mHardwareYOffset = yoff; 2362 mResizeAlpha = resizeAlpha; 2363 2364 mCurrentDirty.set(dirty); 2365 dirty.setEmpty(); 2366 2367 attachInfo.mHardwareRenderer.draw(mView, attachInfo, this, 2368 animating ? null : mCurrentDirty); 2369 } else { 2370 // If we get here with a disabled & requested hardware renderer, something went 2371 // wrong (an invalidate posted right before we destroyed the hardware surface 2372 // for instance) so we should just bail out. Locking the surface with software 2373 // rendering at this point would lock it forever and prevent hardware renderer 2374 // from doing its job when it comes back. 2375 // Before we request a new frame we must however attempt to reinitiliaze the 2376 // hardware renderer if it's in requested state. This would happen after an 2377 // eglTerminate() for instance. 2378 if (attachInfo.mHardwareRenderer != null && 2379 !attachInfo.mHardwareRenderer.isEnabled() && 2380 attachInfo.mHardwareRenderer.isRequested()) { 2381 2382 try { 2383 attachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight, 2384 mHolder.getSurface()); 2385 } catch (Surface.OutOfResourcesException e) { 2386 handleOutOfResourcesException(e); 2387 return; 2388 } 2389 2390 mFullRedrawNeeded = true; 2391 scheduleTraversals(); 2392 return; 2393 } 2394 2395 if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) { 2396 return; 2397 } 2398 } 2399 } 2400 2401 if (animating) { 2402 mFullRedrawNeeded = true; 2403 scheduleTraversals(); 2404 } 2405 } 2406 2407 /** 2408 * @return true if drawing was succesfull, false if an error occurred 2409 */ drawSoftware(Surface surface, AttachInfo attachInfo, int yoff, boolean scalingRequired, Rect dirty)2410 private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff, 2411 boolean scalingRequired, Rect dirty) { 2412 2413 // Draw with software renderer. 2414 Canvas canvas; 2415 try { 2416 int left = dirty.left; 2417 int top = dirty.top; 2418 int right = dirty.right; 2419 int bottom = dirty.bottom; 2420 2421 canvas = mSurface.lockCanvas(dirty); 2422 2423 // The dirty rectangle can be modified by Surface.lockCanvas() 2424 //noinspection ConstantConditions 2425 if (left != dirty.left || top != dirty.top || right != dirty.right || 2426 bottom != dirty.bottom) { 2427 attachInfo.mIgnoreDirtyState = true; 2428 } 2429 2430 // TODO: Do this in native 2431 canvas.setDensity(mDensity); 2432 } catch (Surface.OutOfResourcesException e) { 2433 handleOutOfResourcesException(e); 2434 return false; 2435 } catch (IllegalArgumentException e) { 2436 Log.e(TAG, "Could not lock surface", e); 2437 // Don't assume this is due to out of memory, it could be 2438 // something else, and if it is something else then we could 2439 // kill stuff (or ourself) for no reason. 2440 mLayoutRequested = true; // ask wm for a new surface next time. 2441 return false; 2442 } 2443 2444 try { 2445 if (DEBUG_ORIENTATION || DEBUG_DRAW) { 2446 Log.v(TAG, "Surface " + surface + " drawing to bitmap w=" 2447 + canvas.getWidth() + ", h=" + canvas.getHeight()); 2448 //canvas.drawARGB(255, 255, 0, 0); 2449 } 2450 2451 // If this bitmap's format includes an alpha channel, we 2452 // need to clear it before drawing so that the child will 2453 // properly re-composite its drawing on a transparent 2454 // background. This automatically respects the clip/dirty region 2455 // or 2456 // If we are applying an offset, we need to clear the area 2457 // where the offset doesn't appear to avoid having garbage 2458 // left in the blank areas. 2459 if (!canvas.isOpaque() || yoff != 0) { 2460 canvas.drawColor(0, PorterDuff.Mode.CLEAR); 2461 } 2462 2463 dirty.setEmpty(); 2464 mIsAnimating = false; 2465 attachInfo.mDrawingTime = SystemClock.uptimeMillis(); 2466 mView.mPrivateFlags |= View.PFLAG_DRAWN; 2467 2468 if (DEBUG_DRAW) { 2469 Context cxt = mView.getContext(); 2470 Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + 2471 ", metrics=" + cxt.getResources().getDisplayMetrics() + 2472 ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo()); 2473 } 2474 try { 2475 canvas.translate(0, -yoff); 2476 if (mTranslator != null) { 2477 mTranslator.translateCanvas(canvas); 2478 } 2479 canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0); 2480 attachInfo.mSetIgnoreDirtyState = false; 2481 2482 mView.draw(canvas); 2483 2484 drawAccessibilityFocusedDrawableIfNeeded(canvas); 2485 } finally { 2486 if (!attachInfo.mSetIgnoreDirtyState) { 2487 // Only clear the flag if it was not set during the mView.draw() call 2488 attachInfo.mIgnoreDirtyState = false; 2489 } 2490 } 2491 } finally { 2492 try { 2493 surface.unlockCanvasAndPost(canvas); 2494 } catch (IllegalArgumentException e) { 2495 Log.e(TAG, "Could not unlock surface", e); 2496 mLayoutRequested = true; // ask wm for a new surface next time. 2497 //noinspection ReturnInsideFinallyBlock 2498 return false; 2499 } 2500 2501 if (LOCAL_LOGV) { 2502 Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost"); 2503 } 2504 } 2505 return true; 2506 } 2507 2508 /** 2509 * We want to draw a highlight around the current accessibility focused. 2510 * Since adding a style for all possible view is not a viable option we 2511 * have this specialized drawing method. 2512 * 2513 * Note: We are doing this here to be able to draw the highlight for 2514 * virtual views in addition to real ones. 2515 * 2516 * @param canvas The canvas on which to draw. 2517 */ drawAccessibilityFocusedDrawableIfNeeded(Canvas canvas)2518 private void drawAccessibilityFocusedDrawableIfNeeded(Canvas canvas) { 2519 AccessibilityManager manager = AccessibilityManager.getInstance(mView.mContext); 2520 if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) { 2521 return; 2522 } 2523 if (mAccessibilityFocusedHost == null || mAccessibilityFocusedHost.mAttachInfo == null) { 2524 return; 2525 } 2526 Drawable drawable = getAccessibilityFocusedDrawable(); 2527 if (drawable == null) { 2528 return; 2529 } 2530 AccessibilityNodeProvider provider = 2531 mAccessibilityFocusedHost.getAccessibilityNodeProvider(); 2532 Rect bounds = mView.mAttachInfo.mTmpInvalRect; 2533 if (provider == null) { 2534 mAccessibilityFocusedHost.getBoundsOnScreen(bounds); 2535 } else { 2536 if (mAccessibilityFocusedVirtualView == null) { 2537 return; 2538 } 2539 mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds); 2540 } 2541 bounds.offset(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop); 2542 bounds.intersect(0, 0, mAttachInfo.mViewRootImpl.mWidth, mAttachInfo.mViewRootImpl.mHeight); 2543 drawable.setBounds(bounds); 2544 drawable.draw(canvas); 2545 } 2546 getAccessibilityFocusedDrawable()2547 private Drawable getAccessibilityFocusedDrawable() { 2548 if (mAttachInfo != null) { 2549 // Lazily load the accessibility focus drawable. 2550 if (mAttachInfo.mAccessibilityFocusDrawable == null) { 2551 TypedValue value = new TypedValue(); 2552 final boolean resolved = mView.mContext.getTheme().resolveAttribute( 2553 R.attr.accessibilityFocusedDrawable, value, true); 2554 if (resolved) { 2555 mAttachInfo.mAccessibilityFocusDrawable = 2556 mView.mContext.getResources().getDrawable(value.resourceId); 2557 } 2558 } 2559 return mAttachInfo.mAccessibilityFocusDrawable; 2560 } 2561 return null; 2562 } 2563 invalidateDisplayLists()2564 void invalidateDisplayLists() { 2565 final ArrayList<DisplayList> displayLists = mDisplayLists; 2566 final int count = displayLists.size(); 2567 2568 for (int i = 0; i < count; i++) { 2569 final DisplayList displayList = displayLists.get(i); 2570 if (displayList.isDirty()) { 2571 displayList.clear(); 2572 } 2573 } 2574 2575 displayLists.clear(); 2576 } 2577 2578 /** 2579 * @hide 2580 */ setDrawDuringWindowsAnimating(boolean value)2581 public void setDrawDuringWindowsAnimating(boolean value) { 2582 mDrawDuringWindowsAnimating = value; 2583 if (value) { 2584 handleDispatchDoneAnimating(); 2585 } 2586 } 2587 scrollToRectOrFocus(Rect rectangle, boolean immediate)2588 boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) { 2589 final View.AttachInfo attachInfo = mAttachInfo; 2590 final Rect ci = attachInfo.mContentInsets; 2591 final Rect vi = attachInfo.mVisibleInsets; 2592 int scrollY = 0; 2593 boolean handled = false; 2594 2595 if (vi.left > ci.left || vi.top > ci.top 2596 || vi.right > ci.right || vi.bottom > ci.bottom) { 2597 // We'll assume that we aren't going to change the scroll 2598 // offset, since we want to avoid that unless it is actually 2599 // going to make the focus visible... otherwise we scroll 2600 // all over the place. 2601 scrollY = mScrollY; 2602 // We can be called for two different situations: during a draw, 2603 // to update the scroll position if the focus has changed (in which 2604 // case 'rectangle' is null), or in response to a 2605 // requestChildRectangleOnScreen() call (in which case 'rectangle' 2606 // is non-null and we just want to scroll to whatever that 2607 // rectangle is). 2608 final View focus = mView.findFocus(); 2609 if (focus == null) { 2610 return false; 2611 } 2612 View lastScrolledFocus = (mLastScrolledFocus != null) ? mLastScrolledFocus.get() : null; 2613 if (focus != lastScrolledFocus) { 2614 // If the focus has changed, then ignore any requests to scroll 2615 // to a rectangle; first we want to make sure the entire focus 2616 // view is visible. 2617 rectangle = null; 2618 } 2619 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus 2620 + " rectangle=" + rectangle + " ci=" + ci 2621 + " vi=" + vi); 2622 if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) { 2623 // Optimization: if the focus hasn't changed since last 2624 // time, and no layout has happened, then just leave things 2625 // as they are. 2626 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Keeping scroll y=" 2627 + mScrollY + " vi=" + vi.toShortString()); 2628 } else { 2629 // We need to determine if the currently focused view is 2630 // within the visible part of the window and, if not, apply 2631 // a pan so it can be seen. 2632 mLastScrolledFocus = new WeakReference<View>(focus); 2633 mScrollMayChange = false; 2634 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?"); 2635 // Try to find the rectangle from the focus view. 2636 if (focus.getGlobalVisibleRect(mVisRect, null)) { 2637 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Root w=" 2638 + mView.getWidth() + " h=" + mView.getHeight() 2639 + " ci=" + ci.toShortString() 2640 + " vi=" + vi.toShortString()); 2641 if (rectangle == null) { 2642 focus.getFocusedRect(mTempRect); 2643 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Focus " + focus 2644 + ": focusRect=" + mTempRect.toShortString()); 2645 if (mView instanceof ViewGroup) { 2646 ((ViewGroup) mView).offsetDescendantRectToMyCoords( 2647 focus, mTempRect); 2648 } 2649 if (DEBUG_INPUT_RESIZE) Log.v(TAG, 2650 "Focus in window: focusRect=" 2651 + mTempRect.toShortString() 2652 + " visRect=" + mVisRect.toShortString()); 2653 } else { 2654 mTempRect.set(rectangle); 2655 if (DEBUG_INPUT_RESIZE) Log.v(TAG, 2656 "Request scroll to rect: " 2657 + mTempRect.toShortString() 2658 + " visRect=" + mVisRect.toShortString()); 2659 } 2660 if (mTempRect.intersect(mVisRect)) { 2661 if (DEBUG_INPUT_RESIZE) Log.v(TAG, 2662 "Focus window visible rect: " 2663 + mTempRect.toShortString()); 2664 if (mTempRect.height() > 2665 (mView.getHeight()-vi.top-vi.bottom)) { 2666 // If the focus simply is not going to fit, then 2667 // best is probably just to leave things as-is. 2668 if (DEBUG_INPUT_RESIZE) Log.v(TAG, 2669 "Too tall; leaving scrollY=" + scrollY); 2670 } else if ((mTempRect.top-scrollY) < vi.top) { 2671 scrollY -= vi.top - (mTempRect.top-scrollY); 2672 if (DEBUG_INPUT_RESIZE) Log.v(TAG, 2673 "Top covered; scrollY=" + scrollY); 2674 } else if ((mTempRect.bottom-scrollY) 2675 > (mView.getHeight()-vi.bottom)) { 2676 scrollY += (mTempRect.bottom-scrollY) 2677 - (mView.getHeight()-vi.bottom); 2678 if (DEBUG_INPUT_RESIZE) Log.v(TAG, 2679 "Bottom covered; scrollY=" + scrollY); 2680 } 2681 handled = true; 2682 } 2683 } 2684 } 2685 } 2686 2687 if (scrollY != mScrollY) { 2688 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old=" 2689 + mScrollY + " , new=" + scrollY); 2690 if (!immediate && mResizeBuffer == null) { 2691 if (mScroller == null) { 2692 mScroller = new Scroller(mView.getContext()); 2693 } 2694 mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY); 2695 } else if (mScroller != null) { 2696 mScroller.abortAnimation(); 2697 } 2698 mScrollY = scrollY; 2699 } 2700 2701 return handled; 2702 } 2703 2704 /** 2705 * @hide 2706 */ getAccessibilityFocusedHost()2707 public View getAccessibilityFocusedHost() { 2708 return mAccessibilityFocusedHost; 2709 } 2710 2711 /** 2712 * @hide 2713 */ getAccessibilityFocusedVirtualView()2714 public AccessibilityNodeInfo getAccessibilityFocusedVirtualView() { 2715 return mAccessibilityFocusedVirtualView; 2716 } 2717 setAccessibilityFocus(View view, AccessibilityNodeInfo node)2718 void setAccessibilityFocus(View view, AccessibilityNodeInfo node) { 2719 // If we have a virtual view with accessibility focus we need 2720 // to clear the focus and invalidate the virtual view bounds. 2721 if (mAccessibilityFocusedVirtualView != null) { 2722 2723 AccessibilityNodeInfo focusNode = mAccessibilityFocusedVirtualView; 2724 View focusHost = mAccessibilityFocusedHost; 2725 2726 // Wipe the state of the current accessibility focus since 2727 // the call into the provider to clear accessibility focus 2728 // will fire an accessibility event which will end up calling 2729 // this method and we want to have clean state when this 2730 // invocation happens. 2731 mAccessibilityFocusedHost = null; 2732 mAccessibilityFocusedVirtualView = null; 2733 2734 // Clear accessibility focus on the host after clearing state since 2735 // this method may be reentrant. 2736 focusHost.clearAccessibilityFocusNoCallbacks(); 2737 2738 AccessibilityNodeProvider provider = focusHost.getAccessibilityNodeProvider(); 2739 if (provider != null) { 2740 // Invalidate the area of the cleared accessibility focus. 2741 focusNode.getBoundsInParent(mTempRect); 2742 focusHost.invalidate(mTempRect); 2743 // Clear accessibility focus in the virtual node. 2744 final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId( 2745 focusNode.getSourceNodeId()); 2746 provider.performAction(virtualNodeId, 2747 AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null); 2748 } 2749 focusNode.recycle(); 2750 } 2751 if (mAccessibilityFocusedHost != null) { 2752 // Clear accessibility focus in the view. 2753 mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks(); 2754 } 2755 2756 // Set the new focus host and node. 2757 mAccessibilityFocusedHost = view; 2758 mAccessibilityFocusedVirtualView = node; 2759 } 2760 requestChildFocus(View child, View focused)2761 public void requestChildFocus(View child, View focused) { 2762 if (DEBUG_INPUT_RESIZE) { 2763 Log.v(TAG, "Request child focus: focus now " + focused); 2764 } 2765 checkThread(); 2766 scheduleTraversals(); 2767 } 2768 clearChildFocus(View child)2769 public void clearChildFocus(View child) { 2770 if (DEBUG_INPUT_RESIZE) { 2771 Log.v(TAG, "Clearing child focus"); 2772 } 2773 checkThread(); 2774 scheduleTraversals(); 2775 } 2776 2777 @Override getParentForAccessibility()2778 public ViewParent getParentForAccessibility() { 2779 return null; 2780 } 2781 focusableViewAvailable(View v)2782 public void focusableViewAvailable(View v) { 2783 checkThread(); 2784 if (mView != null) { 2785 if (!mView.hasFocus()) { 2786 v.requestFocus(); 2787 } else { 2788 // the one case where will transfer focus away from the current one 2789 // is if the current view is a view group that prefers to give focus 2790 // to its children first AND the view is a descendant of it. 2791 View focused = mView.findFocus(); 2792 if (focused instanceof ViewGroup) { 2793 ViewGroup group = (ViewGroup) focused; 2794 if (group.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS 2795 && isViewDescendantOf(v, focused)) { 2796 v.requestFocus(); 2797 } 2798 } 2799 } 2800 } 2801 } 2802 recomputeViewAttributes(View child)2803 public void recomputeViewAttributes(View child) { 2804 checkThread(); 2805 if (mView == child) { 2806 mAttachInfo.mRecomputeGlobalAttributes = true; 2807 if (!mWillDrawSoon) { 2808 scheduleTraversals(); 2809 } 2810 } 2811 } 2812 dispatchDetachedFromWindow()2813 void dispatchDetachedFromWindow() { 2814 if (mView != null && mView.mAttachInfo != null) { 2815 if (mAttachInfo.mHardwareRenderer != null && 2816 mAttachInfo.mHardwareRenderer.isEnabled()) { 2817 mAttachInfo.mHardwareRenderer.validate(); 2818 } 2819 mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false); 2820 mView.dispatchDetachedFromWindow(); 2821 } 2822 2823 mAccessibilityInteractionConnectionManager.ensureNoConnection(); 2824 mAccessibilityManager.removeAccessibilityStateChangeListener( 2825 mAccessibilityInteractionConnectionManager); 2826 removeSendWindowContentChangedCallback(); 2827 2828 destroyHardwareRenderer(); 2829 2830 setAccessibilityFocus(null, null); 2831 2832 mView.assignParent(null); 2833 mView = null; 2834 mAttachInfo.mRootView = null; 2835 mAttachInfo.mSurface = null; 2836 2837 mSurface.release(); 2838 2839 if (mInputQueueCallback != null && mInputQueue != null) { 2840 mInputQueueCallback.onInputQueueDestroyed(mInputQueue); 2841 mInputQueue.dispose(); 2842 mInputQueueCallback = null; 2843 mInputQueue = null; 2844 } 2845 if (mInputEventReceiver != null) { 2846 mInputEventReceiver.dispose(); 2847 mInputEventReceiver = null; 2848 } 2849 try { 2850 mWindowSession.remove(mWindow); 2851 } catch (RemoteException e) { 2852 } 2853 2854 // Dispose the input channel after removing the window so the Window Manager 2855 // doesn't interpret the input channel being closed as an abnormal termination. 2856 if (mInputChannel != null) { 2857 mInputChannel.dispose(); 2858 mInputChannel = null; 2859 } 2860 2861 unscheduleTraversals(); 2862 } 2863 updateConfiguration(Configuration config, boolean force)2864 void updateConfiguration(Configuration config, boolean force) { 2865 if (DEBUG_CONFIGURATION) Log.v(TAG, 2866 "Applying new config to window " 2867 + mWindowAttributes.getTitle() 2868 + ": " + config); 2869 2870 CompatibilityInfo ci = mCompatibilityInfo.getIfNeeded(); 2871 if (ci != null) { 2872 config = new Configuration(config); 2873 ci.applyToConfiguration(mNoncompatDensity, config); 2874 } 2875 2876 synchronized (sConfigCallbacks) { 2877 for (int i=sConfigCallbacks.size()-1; i>=0; i--) { 2878 sConfigCallbacks.get(i).onConfigurationChanged(config); 2879 } 2880 } 2881 if (mView != null) { 2882 // At this point the resources have been updated to 2883 // have the most recent config, whatever that is. Use 2884 // the one in them which may be newer. 2885 config = mView.getResources().getConfiguration(); 2886 if (force || mLastConfiguration.diff(config) != 0) { 2887 final int lastLayoutDirection = mLastConfiguration.getLayoutDirection(); 2888 final int currentLayoutDirection = config.getLayoutDirection(); 2889 mLastConfiguration.setTo(config); 2890 if (lastLayoutDirection != currentLayoutDirection && 2891 mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) { 2892 mView.setLayoutDirection(currentLayoutDirection); 2893 } 2894 mView.dispatchConfigurationChanged(config); 2895 } 2896 } 2897 } 2898 2899 /** 2900 * Return true if child is an ancestor of parent, (or equal to the parent). 2901 */ isViewDescendantOf(View child, View parent)2902 public static boolean isViewDescendantOf(View child, View parent) { 2903 if (child == parent) { 2904 return true; 2905 } 2906 2907 final ViewParent theParent = child.getParent(); 2908 return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); 2909 } 2910 forceLayout(View view)2911 private static void forceLayout(View view) { 2912 view.forceLayout(); 2913 if (view instanceof ViewGroup) { 2914 ViewGroup group = (ViewGroup) view; 2915 final int count = group.getChildCount(); 2916 for (int i = 0; i < count; i++) { 2917 forceLayout(group.getChildAt(i)); 2918 } 2919 } 2920 } 2921 2922 private final static int MSG_INVALIDATE = 1; 2923 private final static int MSG_INVALIDATE_RECT = 2; 2924 private final static int MSG_DIE = 3; 2925 private final static int MSG_RESIZED = 4; 2926 private final static int MSG_RESIZED_REPORT = 5; 2927 private final static int MSG_WINDOW_FOCUS_CHANGED = 6; 2928 private final static int MSG_DISPATCH_KEY = 7; 2929 private final static int MSG_DISPATCH_APP_VISIBILITY = 8; 2930 private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9; 2931 private final static int MSG_DISPATCH_KEY_FROM_IME = 11; 2932 private final static int MSG_FINISH_INPUT_CONNECTION = 12; 2933 private final static int MSG_CHECK_FOCUS = 13; 2934 private final static int MSG_CLOSE_SYSTEM_DIALOGS = 14; 2935 private final static int MSG_DISPATCH_DRAG_EVENT = 15; 2936 private final static int MSG_DISPATCH_DRAG_LOCATION_EVENT = 16; 2937 private final static int MSG_DISPATCH_SYSTEM_UI_VISIBILITY = 17; 2938 private final static int MSG_UPDATE_CONFIGURATION = 18; 2939 private final static int MSG_PROCESS_INPUT_EVENTS = 19; 2940 private final static int MSG_DISPATCH_SCREEN_STATE = 20; 2941 private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 21; 2942 private final static int MSG_DISPATCH_DONE_ANIMATING = 22; 2943 private final static int MSG_INVALIDATE_WORLD = 23; 2944 private final static int MSG_WINDOW_MOVED = 24; 2945 2946 final class ViewRootHandler extends Handler { 2947 @Override getMessageName(Message message)2948 public String getMessageName(Message message) { 2949 switch (message.what) { 2950 case MSG_INVALIDATE: 2951 return "MSG_INVALIDATE"; 2952 case MSG_INVALIDATE_RECT: 2953 return "MSG_INVALIDATE_RECT"; 2954 case MSG_DIE: 2955 return "MSG_DIE"; 2956 case MSG_RESIZED: 2957 return "MSG_RESIZED"; 2958 case MSG_RESIZED_REPORT: 2959 return "MSG_RESIZED_REPORT"; 2960 case MSG_WINDOW_FOCUS_CHANGED: 2961 return "MSG_WINDOW_FOCUS_CHANGED"; 2962 case MSG_DISPATCH_KEY: 2963 return "MSG_DISPATCH_KEY"; 2964 case MSG_DISPATCH_APP_VISIBILITY: 2965 return "MSG_DISPATCH_APP_VISIBILITY"; 2966 case MSG_DISPATCH_GET_NEW_SURFACE: 2967 return "MSG_DISPATCH_GET_NEW_SURFACE"; 2968 case MSG_DISPATCH_KEY_FROM_IME: 2969 return "MSG_DISPATCH_KEY_FROM_IME"; 2970 case MSG_FINISH_INPUT_CONNECTION: 2971 return "MSG_FINISH_INPUT_CONNECTION"; 2972 case MSG_CHECK_FOCUS: 2973 return "MSG_CHECK_FOCUS"; 2974 case MSG_CLOSE_SYSTEM_DIALOGS: 2975 return "MSG_CLOSE_SYSTEM_DIALOGS"; 2976 case MSG_DISPATCH_DRAG_EVENT: 2977 return "MSG_DISPATCH_DRAG_EVENT"; 2978 case MSG_DISPATCH_DRAG_LOCATION_EVENT: 2979 return "MSG_DISPATCH_DRAG_LOCATION_EVENT"; 2980 case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: 2981 return "MSG_DISPATCH_SYSTEM_UI_VISIBILITY"; 2982 case MSG_UPDATE_CONFIGURATION: 2983 return "MSG_UPDATE_CONFIGURATION"; 2984 case MSG_PROCESS_INPUT_EVENTS: 2985 return "MSG_PROCESS_INPUT_EVENTS"; 2986 case MSG_DISPATCH_SCREEN_STATE: 2987 return "MSG_DISPATCH_SCREEN_STATE"; 2988 case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: 2989 return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST"; 2990 case MSG_DISPATCH_DONE_ANIMATING: 2991 return "MSG_DISPATCH_DONE_ANIMATING"; 2992 case MSG_WINDOW_MOVED: 2993 return "MSG_WINDOW_MOVED"; 2994 } 2995 return super.getMessageName(message); 2996 } 2997 2998 @Override handleMessage(Message msg)2999 public void handleMessage(Message msg) { 3000 switch (msg.what) { 3001 case MSG_INVALIDATE: 3002 ((View) msg.obj).invalidate(); 3003 break; 3004 case MSG_INVALIDATE_RECT: 3005 final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj; 3006 info.target.invalidate(info.left, info.top, info.right, info.bottom); 3007 info.recycle(); 3008 break; 3009 case MSG_PROCESS_INPUT_EVENTS: 3010 mProcessInputEventsScheduled = false; 3011 doProcessInputEvents(); 3012 break; 3013 case MSG_DISPATCH_APP_VISIBILITY: 3014 handleAppVisibility(msg.arg1 != 0); 3015 break; 3016 case MSG_DISPATCH_GET_NEW_SURFACE: 3017 handleGetNewSurface(); 3018 break; 3019 case MSG_RESIZED: { 3020 // Recycled in the fall through... 3021 SomeArgs args = (SomeArgs) msg.obj; 3022 if (mWinFrame.equals(args.arg1) 3023 && mPendingOverscanInsets.equals(args.arg5) 3024 && mPendingContentInsets.equals(args.arg2) 3025 && mPendingVisibleInsets.equals(args.arg3) 3026 && args.arg4 == null) { 3027 break; 3028 } 3029 } // fall through... 3030 case MSG_RESIZED_REPORT: 3031 if (mAdded) { 3032 SomeArgs args = (SomeArgs) msg.obj; 3033 3034 Configuration config = (Configuration) args.arg4; 3035 if (config != null) { 3036 updateConfiguration(config, false); 3037 } 3038 3039 mWinFrame.set((Rect) args.arg1); 3040 mPendingOverscanInsets.set((Rect) args.arg5); 3041 mPendingContentInsets.set((Rect) args.arg2); 3042 mPendingVisibleInsets.set((Rect) args.arg3); 3043 3044 args.recycle(); 3045 3046 if (msg.what == MSG_RESIZED_REPORT) { 3047 mReportNextDraw = true; 3048 } 3049 3050 if (mView != null) { 3051 forceLayout(mView); 3052 } 3053 3054 requestLayout(); 3055 } 3056 break; 3057 case MSG_WINDOW_MOVED: 3058 if (mAdded) { 3059 final int w = mWinFrame.width(); 3060 final int h = mWinFrame.height(); 3061 final int l = msg.arg1; 3062 final int t = msg.arg2; 3063 mWinFrame.left = l; 3064 mWinFrame.right = l + w; 3065 mWinFrame.top = t; 3066 mWinFrame.bottom = t + h; 3067 3068 if (mView != null) { 3069 forceLayout(mView); 3070 } 3071 requestLayout(); 3072 } 3073 break; 3074 case MSG_WINDOW_FOCUS_CHANGED: { 3075 if (mAdded) { 3076 boolean hasWindowFocus = msg.arg1 != 0; 3077 mAttachInfo.mHasWindowFocus = hasWindowFocus; 3078 3079 profileRendering(hasWindowFocus); 3080 3081 if (hasWindowFocus) { 3082 boolean inTouchMode = msg.arg2 != 0; 3083 ensureTouchModeLocally(inTouchMode); 3084 3085 if (mAttachInfo.mHardwareRenderer != null && mSurface.isValid()){ 3086 mFullRedrawNeeded = true; 3087 try { 3088 mAttachInfo.mHardwareRenderer.initializeIfNeeded( 3089 mWidth, mHeight, mHolder.getSurface()); 3090 } catch (Surface.OutOfResourcesException e) { 3091 Log.e(TAG, "OutOfResourcesException locking surface", e); 3092 try { 3093 if (!mWindowSession.outOfMemory(mWindow)) { 3094 Slog.w(TAG, "No processes killed for memory; killing self"); 3095 Process.killProcess(Process.myPid()); 3096 } 3097 } catch (RemoteException ex) { 3098 } 3099 // Retry in a bit. 3100 sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500); 3101 return; 3102 } 3103 } 3104 } 3105 3106 mLastWasImTarget = WindowManager.LayoutParams 3107 .mayUseInputMethod(mWindowAttributes.flags); 3108 3109 InputMethodManager imm = InputMethodManager.peekInstance(); 3110 if (mView != null) { 3111 if (hasWindowFocus && imm != null && mLastWasImTarget) { 3112 imm.startGettingWindowFocus(mView); 3113 } 3114 mAttachInfo.mKeyDispatchState.reset(); 3115 mView.dispatchWindowFocusChanged(hasWindowFocus); 3116 mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus); 3117 } 3118 3119 // Note: must be done after the focus change callbacks, 3120 // so all of the view state is set up correctly. 3121 if (hasWindowFocus) { 3122 if (imm != null && mLastWasImTarget) { 3123 imm.onWindowFocus(mView, mView.findFocus(), 3124 mWindowAttributes.softInputMode, 3125 !mHasHadWindowFocus, mWindowAttributes.flags); 3126 } 3127 // Clear the forward bit. We can just do this directly, since 3128 // the window manager doesn't care about it. 3129 mWindowAttributes.softInputMode &= 3130 ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; 3131 ((WindowManager.LayoutParams)mView.getLayoutParams()) 3132 .softInputMode &= 3133 ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; 3134 mHasHadWindowFocus = true; 3135 } 3136 3137 setAccessibilityFocus(null, null); 3138 3139 if (mView != null && mAccessibilityManager.isEnabled()) { 3140 if (hasWindowFocus) { 3141 mView.sendAccessibilityEvent( 3142 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 3143 } 3144 } 3145 } 3146 } break; 3147 case MSG_DIE: 3148 doDie(); 3149 break; 3150 case MSG_DISPATCH_KEY: { 3151 KeyEvent event = (KeyEvent)msg.obj; 3152 enqueueInputEvent(event, null, 0, true); 3153 } break; 3154 case MSG_DISPATCH_KEY_FROM_IME: { 3155 if (LOCAL_LOGV) Log.v( 3156 TAG, "Dispatching key " 3157 + msg.obj + " from IME to " + mView); 3158 KeyEvent event = (KeyEvent)msg.obj; 3159 if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) { 3160 // The IME is trying to say this event is from the 3161 // system! Bad bad bad! 3162 //noinspection UnusedAssignment 3163 event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM); 3164 } 3165 enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true); 3166 } break; 3167 case MSG_FINISH_INPUT_CONNECTION: { 3168 InputMethodManager imm = InputMethodManager.peekInstance(); 3169 if (imm != null) { 3170 imm.reportFinishInputConnection((InputConnection)msg.obj); 3171 } 3172 } break; 3173 case MSG_CHECK_FOCUS: { 3174 InputMethodManager imm = InputMethodManager.peekInstance(); 3175 if (imm != null) { 3176 imm.checkFocus(); 3177 } 3178 } break; 3179 case MSG_CLOSE_SYSTEM_DIALOGS: { 3180 if (mView != null) { 3181 mView.onCloseSystemDialogs((String)msg.obj); 3182 } 3183 } break; 3184 case MSG_DISPATCH_DRAG_EVENT: 3185 case MSG_DISPATCH_DRAG_LOCATION_EVENT: { 3186 DragEvent event = (DragEvent)msg.obj; 3187 event.mLocalState = mLocalDragState; // only present when this app called startDrag() 3188 handleDragEvent(event); 3189 } break; 3190 case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: { 3191 handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj); 3192 } break; 3193 case MSG_UPDATE_CONFIGURATION: { 3194 Configuration config = (Configuration)msg.obj; 3195 if (config.isOtherSeqNewer(mLastConfiguration)) { 3196 config = mLastConfiguration; 3197 } 3198 updateConfiguration(config, false); 3199 } break; 3200 case MSG_DISPATCH_SCREEN_STATE: { 3201 if (mView != null) { 3202 handleScreenStateChange(msg.arg1 == 1); 3203 } 3204 } break; 3205 case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: { 3206 setAccessibilityFocus(null, null); 3207 } break; 3208 case MSG_DISPATCH_DONE_ANIMATING: { 3209 handleDispatchDoneAnimating(); 3210 } break; 3211 case MSG_INVALIDATE_WORLD: { 3212 if (mView != null) { 3213 invalidateWorld(mView); 3214 } 3215 } break; 3216 } 3217 } 3218 } 3219 3220 final ViewRootHandler mHandler = new ViewRootHandler(); 3221 3222 /** 3223 * Something in the current window tells us we need to change the touch mode. For 3224 * example, we are not in touch mode, and the user touches the screen. 3225 * 3226 * If the touch mode has changed, tell the window manager, and handle it locally. 3227 * 3228 * @param inTouchMode Whether we want to be in touch mode. 3229 * @return True if the touch mode changed and focus changed was changed as a result 3230 */ ensureTouchMode(boolean inTouchMode)3231 boolean ensureTouchMode(boolean inTouchMode) { 3232 if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current " 3233 + "touch mode is " + mAttachInfo.mInTouchMode); 3234 if (mAttachInfo.mInTouchMode == inTouchMode) return false; 3235 3236 // tell the window manager 3237 try { 3238 mWindowSession.setInTouchMode(inTouchMode); 3239 } catch (RemoteException e) { 3240 throw new RuntimeException(e); 3241 } 3242 3243 // handle the change 3244 return ensureTouchModeLocally(inTouchMode); 3245 } 3246 3247 /** 3248 * Ensure that the touch mode for this window is set, and if it is changing, 3249 * take the appropriate action. 3250 * @param inTouchMode Whether we want to be in touch mode. 3251 * @return True if the touch mode changed and focus changed was changed as a result 3252 */ ensureTouchModeLocally(boolean inTouchMode)3253 private boolean ensureTouchModeLocally(boolean inTouchMode) { 3254 if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current " 3255 + "touch mode is " + mAttachInfo.mInTouchMode); 3256 3257 if (mAttachInfo.mInTouchMode == inTouchMode) return false; 3258 3259 mAttachInfo.mInTouchMode = inTouchMode; 3260 mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode); 3261 3262 return (inTouchMode) ? enterTouchMode() : leaveTouchMode(); 3263 } 3264 enterTouchMode()3265 private boolean enterTouchMode() { 3266 if (mView != null) { 3267 if (mView.hasFocus()) { 3268 // note: not relying on mFocusedView here because this could 3269 // be when the window is first being added, and mFocused isn't 3270 // set yet. 3271 final View focused = mView.findFocus(); 3272 if (focused != null && !focused.isFocusableInTouchMode()) { 3273 final ViewGroup ancestorToTakeFocus = 3274 findAncestorToTakeFocusInTouchMode(focused); 3275 if (ancestorToTakeFocus != null) { 3276 // there is an ancestor that wants focus after its descendants that 3277 // is focusable in touch mode.. give it focus 3278 return ancestorToTakeFocus.requestFocus(); 3279 } else { 3280 // nothing appropriate to have focus in touch mode, clear it out 3281 focused.unFocus(); 3282 return true; 3283 } 3284 } 3285 } 3286 } 3287 return false; 3288 } 3289 3290 /** 3291 * Find an ancestor of focused that wants focus after its descendants and is 3292 * focusable in touch mode. 3293 * @param focused The currently focused view. 3294 * @return An appropriate view, or null if no such view exists. 3295 */ findAncestorToTakeFocusInTouchMode(View focused)3296 private static ViewGroup findAncestorToTakeFocusInTouchMode(View focused) { 3297 ViewParent parent = focused.getParent(); 3298 while (parent instanceof ViewGroup) { 3299 final ViewGroup vgParent = (ViewGroup) parent; 3300 if (vgParent.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS 3301 && vgParent.isFocusableInTouchMode()) { 3302 return vgParent; 3303 } 3304 if (vgParent.isRootNamespace()) { 3305 return null; 3306 } else { 3307 parent = vgParent.getParent(); 3308 } 3309 } 3310 return null; 3311 } 3312 leaveTouchMode()3313 private boolean leaveTouchMode() { 3314 if (mView != null) { 3315 if (mView.hasFocus()) { 3316 View focusedView = mView.findFocus(); 3317 if (!(focusedView instanceof ViewGroup)) { 3318 // some view has focus, let it keep it 3319 return false; 3320 } else if (((ViewGroup) focusedView).getDescendantFocusability() != 3321 ViewGroup.FOCUS_AFTER_DESCENDANTS) { 3322 // some view group has focus, and doesn't prefer its children 3323 // over itself for focus, so let them keep it. 3324 return false; 3325 } 3326 } 3327 3328 // find the best view to give focus to in this brave new non-touch-mode 3329 // world 3330 final View focused = focusSearch(null, View.FOCUS_DOWN); 3331 if (focused != null) { 3332 return focused.requestFocus(View.FOCUS_DOWN); 3333 } 3334 } 3335 return false; 3336 } 3337 3338 /** 3339 * Base class for implementing a stage in the chain of responsibility 3340 * for processing input events. 3341 * <p> 3342 * Events are delivered to the stage by the {@link #deliver} method. The stage 3343 * then has the choice of finishing the event or forwarding it to the next stage. 3344 * </p> 3345 */ 3346 abstract class InputStage { 3347 private final InputStage mNext; 3348 3349 protected static final int FORWARD = 0; 3350 protected static final int FINISH_HANDLED = 1; 3351 protected static final int FINISH_NOT_HANDLED = 2; 3352 3353 /** 3354 * Creates an input stage. 3355 * @param next The next stage to which events should be forwarded. 3356 */ InputStage(InputStage next)3357 public InputStage(InputStage next) { 3358 mNext = next; 3359 } 3360 3361 /** 3362 * Delivers an event to be processed. 3363 */ deliver(QueuedInputEvent q)3364 public final void deliver(QueuedInputEvent q) { 3365 if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) { 3366 forward(q); 3367 } else if (mView == null || !mAdded) { 3368 Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent); 3369 finish(q, false); 3370 } else if (!mAttachInfo.mHasWindowFocus && 3371 !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) && 3372 !isTerminalInputEvent(q.mEvent)) { 3373 // If this is a focused event and the window doesn't currently have input focus, 3374 // then drop this event. This could be an event that came back from the previous 3375 // stage but the window has lost focus in the meantime. 3376 Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent); 3377 finish(q, false); 3378 } else { 3379 apply(q, onProcess(q)); 3380 } 3381 } 3382 3383 /** 3384 * Marks the the input event as finished then forwards it to the next stage. 3385 */ finish(QueuedInputEvent q, boolean handled)3386 protected void finish(QueuedInputEvent q, boolean handled) { 3387 q.mFlags |= QueuedInputEvent.FLAG_FINISHED; 3388 if (handled) { 3389 q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED; 3390 } 3391 forward(q); 3392 } 3393 3394 /** 3395 * Forwards the event to the next stage. 3396 */ forward(QueuedInputEvent q)3397 protected void forward(QueuedInputEvent q) { 3398 onDeliverToNext(q); 3399 } 3400 3401 /** 3402 * Applies a result code from {@link #onProcess} to the specified event. 3403 */ apply(QueuedInputEvent q, int result)3404 protected void apply(QueuedInputEvent q, int result) { 3405 if (result == FORWARD) { 3406 forward(q); 3407 } else if (result == FINISH_HANDLED) { 3408 finish(q, true); 3409 } else if (result == FINISH_NOT_HANDLED) { 3410 finish(q, false); 3411 } else { 3412 throw new IllegalArgumentException("Invalid result: " + result); 3413 } 3414 } 3415 3416 /** 3417 * Called when an event is ready to be processed. 3418 * @return A result code indicating how the event was handled. 3419 */ onProcess(QueuedInputEvent q)3420 protected int onProcess(QueuedInputEvent q) { 3421 return FORWARD; 3422 } 3423 3424 /** 3425 * Called when an event is being delivered to the next stage. 3426 */ onDeliverToNext(QueuedInputEvent q)3427 protected void onDeliverToNext(QueuedInputEvent q) { 3428 if (mNext != null) { 3429 mNext.deliver(q); 3430 } else { 3431 finishInputEvent(q); 3432 } 3433 } 3434 } 3435 3436 /** 3437 * Base class for implementing an input pipeline stage that supports 3438 * asynchronous and out-of-order processing of input events. 3439 * <p> 3440 * In addition to what a normal input stage can do, an asynchronous 3441 * input stage may also defer an input event that has been delivered to it 3442 * and finish or forward it later. 3443 * </p> 3444 */ 3445 abstract class AsyncInputStage extends InputStage { 3446 private final String mTraceCounter; 3447 3448 private QueuedInputEvent mQueueHead; 3449 private QueuedInputEvent mQueueTail; 3450 private int mQueueLength; 3451 3452 protected static final int DEFER = 3; 3453 3454 /** 3455 * Creates an asynchronous input stage. 3456 * @param next The next stage to which events should be forwarded. 3457 * @param traceCounter The name of a counter to record the size of 3458 * the queue of pending events. 3459 */ AsyncInputStage(InputStage next, String traceCounter)3460 public AsyncInputStage(InputStage next, String traceCounter) { 3461 super(next); 3462 mTraceCounter = traceCounter; 3463 } 3464 3465 /** 3466 * Marks the event as deferred, which is to say that it will be handled 3467 * asynchronously. The caller is responsible for calling {@link #forward} 3468 * or {@link #finish} later when it is done handling the event. 3469 */ defer(QueuedInputEvent q)3470 protected void defer(QueuedInputEvent q) { 3471 q.mFlags |= QueuedInputEvent.FLAG_DEFERRED; 3472 enqueue(q); 3473 } 3474 3475 @Override forward(QueuedInputEvent q)3476 protected void forward(QueuedInputEvent q) { 3477 // Clear the deferred flag. 3478 q.mFlags &= ~QueuedInputEvent.FLAG_DEFERRED; 3479 3480 // Fast path if the queue is empty. 3481 QueuedInputEvent curr = mQueueHead; 3482 if (curr == null) { 3483 super.forward(q); 3484 return; 3485 } 3486 3487 // Determine whether the event must be serialized behind any others 3488 // before it can be delivered to the next stage. This is done because 3489 // deferred events might be handled out of order by the stage. 3490 final int deviceId = q.mEvent.getDeviceId(); 3491 QueuedInputEvent prev = null; 3492 boolean blocked = false; 3493 while (curr != null && curr != q) { 3494 if (!blocked && deviceId == curr.mEvent.getDeviceId()) { 3495 blocked = true; 3496 } 3497 prev = curr; 3498 curr = curr.mNext; 3499 } 3500 3501 // If the event is blocked, then leave it in the queue to be delivered later. 3502 // Note that the event might not yet be in the queue if it was not previously 3503 // deferred so we will enqueue it if needed. 3504 if (blocked) { 3505 if (curr == null) { 3506 enqueue(q); 3507 } 3508 return; 3509 } 3510 3511 // The event is not blocked. Deliver it immediately. 3512 if (curr != null) { 3513 curr = curr.mNext; 3514 dequeue(q, prev); 3515 } 3516 super.forward(q); 3517 3518 // Dequeuing this event may have unblocked successors. Deliver them. 3519 while (curr != null) { 3520 if (deviceId == curr.mEvent.getDeviceId()) { 3521 if ((curr.mFlags & QueuedInputEvent.FLAG_DEFERRED) != 0) { 3522 break; 3523 } 3524 QueuedInputEvent next = curr.mNext; 3525 dequeue(curr, prev); 3526 super.forward(curr); 3527 curr = next; 3528 } else { 3529 prev = curr; 3530 curr = curr.mNext; 3531 } 3532 } 3533 } 3534 3535 @Override apply(QueuedInputEvent q, int result)3536 protected void apply(QueuedInputEvent q, int result) { 3537 if (result == DEFER) { 3538 defer(q); 3539 } else { 3540 super.apply(q, result); 3541 } 3542 } 3543 enqueue(QueuedInputEvent q)3544 private void enqueue(QueuedInputEvent q) { 3545 if (mQueueTail == null) { 3546 mQueueHead = q; 3547 mQueueTail = q; 3548 } else { 3549 mQueueTail.mNext = q; 3550 mQueueTail = q; 3551 } 3552 3553 mQueueLength += 1; 3554 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength); 3555 } 3556 dequeue(QueuedInputEvent q, QueuedInputEvent prev)3557 private void dequeue(QueuedInputEvent q, QueuedInputEvent prev) { 3558 if (prev == null) { 3559 mQueueHead = q.mNext; 3560 } else { 3561 prev.mNext = q.mNext; 3562 } 3563 if (mQueueTail == q) { 3564 mQueueTail = prev; 3565 } 3566 q.mNext = null; 3567 3568 mQueueLength -= 1; 3569 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength); 3570 } 3571 } 3572 3573 /** 3574 * Delivers pre-ime input events to a native activity. 3575 * Does not support pointer events. 3576 */ 3577 final class NativePreImeInputStage extends AsyncInputStage 3578 implements InputQueue.FinishedInputEventCallback { NativePreImeInputStage(InputStage next, String traceCounter)3579 public NativePreImeInputStage(InputStage next, String traceCounter) { 3580 super(next, traceCounter); 3581 } 3582 3583 @Override onProcess(QueuedInputEvent q)3584 protected int onProcess(QueuedInputEvent q) { 3585 if (mInputQueue != null && q.mEvent instanceof KeyEvent) { 3586 mInputQueue.sendInputEvent(q.mEvent, q, true, this); 3587 return DEFER; 3588 } 3589 return FORWARD; 3590 } 3591 3592 @Override onFinishedInputEvent(Object token, boolean handled)3593 public void onFinishedInputEvent(Object token, boolean handled) { 3594 QueuedInputEvent q = (QueuedInputEvent)token; 3595 if (handled) { 3596 finish(q, true); 3597 return; 3598 } 3599 forward(q); 3600 } 3601 } 3602 3603 /** 3604 * Delivers pre-ime input events to the view hierarchy. 3605 * Does not support pointer events. 3606 */ 3607 final class ViewPreImeInputStage extends InputStage { ViewPreImeInputStage(InputStage next)3608 public ViewPreImeInputStage(InputStage next) { 3609 super(next); 3610 } 3611 3612 @Override onProcess(QueuedInputEvent q)3613 protected int onProcess(QueuedInputEvent q) { 3614 if (q.mEvent instanceof KeyEvent) { 3615 return processKeyEvent(q); 3616 } 3617 return FORWARD; 3618 } 3619 processKeyEvent(QueuedInputEvent q)3620 private int processKeyEvent(QueuedInputEvent q) { 3621 final KeyEvent event = (KeyEvent)q.mEvent; 3622 if (mView.dispatchKeyEventPreIme(event)) { 3623 return FINISH_HANDLED; 3624 } 3625 return FORWARD; 3626 } 3627 } 3628 3629 /** 3630 * Delivers input events to the ime. 3631 * Does not support pointer events. 3632 */ 3633 final class ImeInputStage extends AsyncInputStage 3634 implements InputMethodManager.FinishedInputEventCallback { ImeInputStage(InputStage next, String traceCounter)3635 public ImeInputStage(InputStage next, String traceCounter) { 3636 super(next, traceCounter); 3637 } 3638 3639 @Override onProcess(QueuedInputEvent q)3640 protected int onProcess(QueuedInputEvent q) { 3641 if (mLastWasImTarget) { 3642 InputMethodManager imm = InputMethodManager.peekInstance(); 3643 if (imm != null) { 3644 final InputEvent event = q.mEvent; 3645 if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event); 3646 int result = imm.dispatchInputEvent(event, q, this, mHandler); 3647 if (result == InputMethodManager.DISPATCH_HANDLED) { 3648 return FINISH_HANDLED; 3649 } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) { 3650 return FINISH_NOT_HANDLED; 3651 } else { 3652 return DEFER; // callback will be invoked later 3653 } 3654 } 3655 } 3656 return FORWARD; 3657 } 3658 3659 @Override onFinishedInputEvent(Object token, boolean handled)3660 public void onFinishedInputEvent(Object token, boolean handled) { 3661 QueuedInputEvent q = (QueuedInputEvent)token; 3662 if (handled) { 3663 finish(q, true); 3664 return; 3665 } 3666 forward(q); 3667 } 3668 } 3669 3670 /** 3671 * Performs early processing of post-ime input events. 3672 */ 3673 final class EarlyPostImeInputStage extends InputStage { EarlyPostImeInputStage(InputStage next)3674 public EarlyPostImeInputStage(InputStage next) { 3675 super(next); 3676 } 3677 3678 @Override onProcess(QueuedInputEvent q)3679 protected int onProcess(QueuedInputEvent q) { 3680 if (q.mEvent instanceof KeyEvent) { 3681 return processKeyEvent(q); 3682 } else { 3683 final int source = q.mEvent.getSource(); 3684 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { 3685 return processPointerEvent(q); 3686 } 3687 } 3688 return FORWARD; 3689 } 3690 processKeyEvent(QueuedInputEvent q)3691 private int processKeyEvent(QueuedInputEvent q) { 3692 final KeyEvent event = (KeyEvent)q.mEvent; 3693 3694 // If the key's purpose is to exit touch mode then we consume it 3695 // and consider it handled. 3696 if (checkForLeavingTouchModeAndConsume(event)) { 3697 return FINISH_HANDLED; 3698 } 3699 3700 // Make sure the fallback event policy sees all keys that will be 3701 // delivered to the view hierarchy. 3702 mFallbackEventHandler.preDispatchKeyEvent(event); 3703 return FORWARD; 3704 } 3705 processPointerEvent(QueuedInputEvent q)3706 private int processPointerEvent(QueuedInputEvent q) { 3707 final MotionEvent event = (MotionEvent)q.mEvent; 3708 3709 // Translate the pointer event for compatibility, if needed. 3710 if (mTranslator != null) { 3711 mTranslator.translateEventInScreenToAppWindow(event); 3712 } 3713 3714 // Enter touch mode on down or scroll. 3715 final int action = event.getAction(); 3716 if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { 3717 ensureTouchMode(true); 3718 } 3719 3720 // Offset the scroll position. 3721 if (mCurScrollY != 0) { 3722 event.offsetLocation(0, mCurScrollY); 3723 } 3724 3725 // Remember the touch position for possible drag-initiation. 3726 if (event.isTouchEvent()) { 3727 mLastTouchPoint.x = event.getRawX(); 3728 mLastTouchPoint.y = event.getRawY(); 3729 } 3730 return FORWARD; 3731 } 3732 } 3733 3734 /** 3735 * Delivers post-ime input events to a native activity. 3736 */ 3737 final class NativePostImeInputStage extends AsyncInputStage 3738 implements InputQueue.FinishedInputEventCallback { NativePostImeInputStage(InputStage next, String traceCounter)3739 public NativePostImeInputStage(InputStage next, String traceCounter) { 3740 super(next, traceCounter); 3741 } 3742 3743 @Override onProcess(QueuedInputEvent q)3744 protected int onProcess(QueuedInputEvent q) { 3745 if (mInputQueue != null) { 3746 mInputQueue.sendInputEvent(q.mEvent, q, false, this); 3747 return DEFER; 3748 } 3749 return FORWARD; 3750 } 3751 3752 @Override onFinishedInputEvent(Object token, boolean handled)3753 public void onFinishedInputEvent(Object token, boolean handled) { 3754 QueuedInputEvent q = (QueuedInputEvent)token; 3755 if (handled) { 3756 finish(q, true); 3757 return; 3758 } 3759 forward(q); 3760 } 3761 } 3762 3763 /** 3764 * Delivers post-ime input events to the view hierarchy. 3765 */ 3766 final class ViewPostImeInputStage extends InputStage { ViewPostImeInputStage(InputStage next)3767 public ViewPostImeInputStage(InputStage next) { 3768 super(next); 3769 } 3770 3771 @Override onProcess(QueuedInputEvent q)3772 protected int onProcess(QueuedInputEvent q) { 3773 if (q.mEvent instanceof KeyEvent) { 3774 return processKeyEvent(q); 3775 } else { 3776 final int source = q.mEvent.getSource(); 3777 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { 3778 return processPointerEvent(q); 3779 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 3780 return processTrackballEvent(q); 3781 } else { 3782 return processGenericMotionEvent(q); 3783 } 3784 } 3785 } 3786 processKeyEvent(QueuedInputEvent q)3787 private int processKeyEvent(QueuedInputEvent q) { 3788 final KeyEvent event = (KeyEvent)q.mEvent; 3789 3790 // Deliver the key to the view hierarchy. 3791 if (mView.dispatchKeyEvent(event)) { 3792 return FINISH_HANDLED; 3793 } 3794 3795 // If the Control modifier is held, try to interpret the key as a shortcut. 3796 if (event.getAction() == KeyEvent.ACTION_DOWN 3797 && event.isCtrlPressed() 3798 && event.getRepeatCount() == 0 3799 && !KeyEvent.isModifierKey(event.getKeyCode())) { 3800 if (mView.dispatchKeyShortcutEvent(event)) { 3801 return FINISH_HANDLED; 3802 } 3803 } 3804 3805 // Apply the fallback event policy. 3806 if (mFallbackEventHandler.dispatchKeyEvent(event)) { 3807 return FINISH_HANDLED; 3808 } 3809 3810 // Handle automatic focus changes. 3811 if (event.getAction() == KeyEvent.ACTION_DOWN) { 3812 int direction = 0; 3813 switch (event.getKeyCode()) { 3814 case KeyEvent.KEYCODE_DPAD_LEFT: 3815 if (event.hasNoModifiers()) { 3816 direction = View.FOCUS_LEFT; 3817 } 3818 break; 3819 case KeyEvent.KEYCODE_DPAD_RIGHT: 3820 if (event.hasNoModifiers()) { 3821 direction = View.FOCUS_RIGHT; 3822 } 3823 break; 3824 case KeyEvent.KEYCODE_DPAD_UP: 3825 if (event.hasNoModifiers()) { 3826 direction = View.FOCUS_UP; 3827 } 3828 break; 3829 case KeyEvent.KEYCODE_DPAD_DOWN: 3830 if (event.hasNoModifiers()) { 3831 direction = View.FOCUS_DOWN; 3832 } 3833 break; 3834 case KeyEvent.KEYCODE_TAB: 3835 if (event.hasNoModifiers()) { 3836 direction = View.FOCUS_FORWARD; 3837 } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) { 3838 direction = View.FOCUS_BACKWARD; 3839 } 3840 break; 3841 } 3842 if (direction != 0) { 3843 View focused = mView.findFocus(); 3844 if (focused != null) { 3845 View v = focused.focusSearch(direction); 3846 if (v != null && v != focused) { 3847 // do the math the get the interesting rect 3848 // of previous focused into the coord system of 3849 // newly focused view 3850 focused.getFocusedRect(mTempRect); 3851 if (mView instanceof ViewGroup) { 3852 ((ViewGroup) mView).offsetDescendantRectToMyCoords( 3853 focused, mTempRect); 3854 ((ViewGroup) mView).offsetRectIntoDescendantCoords( 3855 v, mTempRect); 3856 } 3857 if (v.requestFocus(direction, mTempRect)) { 3858 playSoundEffect(SoundEffectConstants 3859 .getContantForFocusDirection(direction)); 3860 return FINISH_HANDLED; 3861 } 3862 } 3863 3864 // Give the focused view a last chance to handle the dpad key. 3865 if (mView.dispatchUnhandledMove(focused, direction)) { 3866 return FINISH_HANDLED; 3867 } 3868 } else { 3869 // find the best view to give focus to in this non-touch-mode with no-focus 3870 View v = focusSearch(null, direction); 3871 if (v != null && v.requestFocus(direction)) { 3872 return FINISH_HANDLED; 3873 } 3874 } 3875 } 3876 } 3877 return FORWARD; 3878 } 3879 processPointerEvent(QueuedInputEvent q)3880 private int processPointerEvent(QueuedInputEvent q) { 3881 final MotionEvent event = (MotionEvent)q.mEvent; 3882 3883 if (mView.dispatchPointerEvent(event)) { 3884 return FINISH_HANDLED; 3885 } 3886 return FORWARD; 3887 } 3888 processTrackballEvent(QueuedInputEvent q)3889 private int processTrackballEvent(QueuedInputEvent q) { 3890 final MotionEvent event = (MotionEvent)q.mEvent; 3891 3892 if (mView.dispatchTrackballEvent(event)) { 3893 return FINISH_HANDLED; 3894 } 3895 return FORWARD; 3896 } 3897 processGenericMotionEvent(QueuedInputEvent q)3898 private int processGenericMotionEvent(QueuedInputEvent q) { 3899 final MotionEvent event = (MotionEvent)q.mEvent; 3900 3901 // Deliver the event to the view. 3902 if (mView.dispatchGenericMotionEvent(event)) { 3903 return FINISH_HANDLED; 3904 } 3905 return FORWARD; 3906 } 3907 } 3908 3909 /** 3910 * Performs synthesis of new input events from unhandled input events. 3911 */ 3912 final class SyntheticInputStage extends InputStage { 3913 private final SyntheticTrackballHandler mTrackball = new SyntheticTrackballHandler(); 3914 private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler(); 3915 private final SyntheticTouchNavigationHandler mTouchNavigation = 3916 new SyntheticTouchNavigationHandler(); 3917 SyntheticInputStage()3918 public SyntheticInputStage() { 3919 super(null); 3920 } 3921 3922 @Override onProcess(QueuedInputEvent q)3923 protected int onProcess(QueuedInputEvent q) { 3924 q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED; 3925 if (q.mEvent instanceof MotionEvent) { 3926 final MotionEvent event = (MotionEvent)q.mEvent; 3927 final int source = event.getSource(); 3928 if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 3929 mTrackball.process(event); 3930 return FINISH_HANDLED; 3931 } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { 3932 mJoystick.process(event); 3933 return FINISH_HANDLED; 3934 } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION) 3935 == InputDevice.SOURCE_TOUCH_NAVIGATION) { 3936 mTouchNavigation.process(event); 3937 return FINISH_HANDLED; 3938 } 3939 } 3940 return FORWARD; 3941 } 3942 3943 @Override onDeliverToNext(QueuedInputEvent q)3944 protected void onDeliverToNext(QueuedInputEvent q) { 3945 if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) { 3946 // Cancel related synthetic events if any prior stage has handled the event. 3947 if (q.mEvent instanceof MotionEvent) { 3948 final MotionEvent event = (MotionEvent)q.mEvent; 3949 final int source = event.getSource(); 3950 if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 3951 mTrackball.cancel(event); 3952 } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { 3953 mJoystick.cancel(event); 3954 } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION) 3955 == InputDevice.SOURCE_TOUCH_NAVIGATION) { 3956 mTouchNavigation.cancel(event); 3957 } 3958 } 3959 } 3960 super.onDeliverToNext(q); 3961 } 3962 } 3963 3964 /** 3965 * Creates dpad events from unhandled trackball movements. 3966 */ 3967 final class SyntheticTrackballHandler { 3968 private final TrackballAxis mX = new TrackballAxis(); 3969 private final TrackballAxis mY = new TrackballAxis(); 3970 private long mLastTime; 3971 process(MotionEvent event)3972 public void process(MotionEvent event) { 3973 // Translate the trackball event into DPAD keys and try to deliver those. 3974 long curTime = SystemClock.uptimeMillis(); 3975 if ((mLastTime + MAX_TRACKBALL_DELAY) < curTime) { 3976 // It has been too long since the last movement, 3977 // so restart at the beginning. 3978 mX.reset(0); 3979 mY.reset(0); 3980 mLastTime = curTime; 3981 } 3982 3983 final int action = event.getAction(); 3984 final int metaState = event.getMetaState(); 3985 switch (action) { 3986 case MotionEvent.ACTION_DOWN: 3987 mX.reset(2); 3988 mY.reset(2); 3989 enqueueInputEvent(new KeyEvent(curTime, curTime, 3990 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState, 3991 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 3992 InputDevice.SOURCE_KEYBOARD)); 3993 break; 3994 case MotionEvent.ACTION_UP: 3995 mX.reset(2); 3996 mY.reset(2); 3997 enqueueInputEvent(new KeyEvent(curTime, curTime, 3998 KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState, 3999 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 4000 InputDevice.SOURCE_KEYBOARD)); 4001 break; 4002 } 4003 4004 if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + mX.position + " step=" 4005 + mX.step + " dir=" + mX.dir + " acc=" + mX.acceleration 4006 + " move=" + event.getX() 4007 + " / Y=" + mY.position + " step=" 4008 + mY.step + " dir=" + mY.dir + " acc=" + mY.acceleration 4009 + " move=" + event.getY()); 4010 final float xOff = mX.collect(event.getX(), event.getEventTime(), "X"); 4011 final float yOff = mY.collect(event.getY(), event.getEventTime(), "Y"); 4012 4013 // Generate DPAD events based on the trackball movement. 4014 // We pick the axis that has moved the most as the direction of 4015 // the DPAD. When we generate DPAD events for one axis, then the 4016 // other axis is reset -- we don't want to perform DPAD jumps due 4017 // to slight movements in the trackball when making major movements 4018 // along the other axis. 4019 int keycode = 0; 4020 int movement = 0; 4021 float accel = 1; 4022 if (xOff > yOff) { 4023 movement = mX.generate(); 4024 if (movement != 0) { 4025 keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT 4026 : KeyEvent.KEYCODE_DPAD_LEFT; 4027 accel = mX.acceleration; 4028 mY.reset(2); 4029 } 4030 } else if (yOff > 0) { 4031 movement = mY.generate(); 4032 if (movement != 0) { 4033 keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN 4034 : KeyEvent.KEYCODE_DPAD_UP; 4035 accel = mY.acceleration; 4036 mX.reset(2); 4037 } 4038 } 4039 4040 if (keycode != 0) { 4041 if (movement < 0) movement = -movement; 4042 int accelMovement = (int)(movement * accel); 4043 if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement 4044 + " accelMovement=" + accelMovement 4045 + " accel=" + accel); 4046 if (accelMovement > movement) { 4047 if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: " 4048 + keycode); 4049 movement--; 4050 int repeatCount = accelMovement - movement; 4051 enqueueInputEvent(new KeyEvent(curTime, curTime, 4052 KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState, 4053 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 4054 InputDevice.SOURCE_KEYBOARD)); 4055 } 4056 while (movement > 0) { 4057 if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: " 4058 + keycode); 4059 movement--; 4060 curTime = SystemClock.uptimeMillis(); 4061 enqueueInputEvent(new KeyEvent(curTime, curTime, 4062 KeyEvent.ACTION_DOWN, keycode, 0, metaState, 4063 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 4064 InputDevice.SOURCE_KEYBOARD)); 4065 enqueueInputEvent(new KeyEvent(curTime, curTime, 4066 KeyEvent.ACTION_UP, keycode, 0, metaState, 4067 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 4068 InputDevice.SOURCE_KEYBOARD)); 4069 } 4070 mLastTime = curTime; 4071 } 4072 } 4073 cancel(MotionEvent event)4074 public void cancel(MotionEvent event) { 4075 mLastTime = Integer.MIN_VALUE; 4076 4077 // If we reach this, we consumed a trackball event. 4078 // Because we will not translate the trackball event into a key event, 4079 // touch mode will not exit, so we exit touch mode here. 4080 if (mView != null && mAdded) { 4081 ensureTouchMode(false); 4082 } 4083 } 4084 } 4085 4086 /** 4087 * Maintains state information for a single trackball axis, generating 4088 * discrete (DPAD) movements based on raw trackball motion. 4089 */ 4090 static final class TrackballAxis { 4091 /** 4092 * The maximum amount of acceleration we will apply. 4093 */ 4094 static final float MAX_ACCELERATION = 20; 4095 4096 /** 4097 * The maximum amount of time (in milliseconds) between events in order 4098 * for us to consider the user to be doing fast trackball movements, 4099 * and thus apply an acceleration. 4100 */ 4101 static final long FAST_MOVE_TIME = 150; 4102 4103 /** 4104 * Scaling factor to the time (in milliseconds) between events to how 4105 * much to multiple/divide the current acceleration. When movement 4106 * is < FAST_MOVE_TIME this multiplies the acceleration; when > 4107 * FAST_MOVE_TIME it divides it. 4108 */ 4109 static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40); 4110 4111 static final float FIRST_MOVEMENT_THRESHOLD = 0.5f; 4112 static final float SECOND_CUMULATIVE_MOVEMENT_THRESHOLD = 2.0f; 4113 static final float SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD = 1.0f; 4114 4115 float position; 4116 float acceleration = 1; 4117 long lastMoveTime = 0; 4118 int step; 4119 int dir; 4120 int nonAccelMovement; 4121 reset(int _step)4122 void reset(int _step) { 4123 position = 0; 4124 acceleration = 1; 4125 lastMoveTime = 0; 4126 step = _step; 4127 dir = 0; 4128 } 4129 4130 /** 4131 * Add trackball movement into the state. If the direction of movement 4132 * has been reversed, the state is reset before adding the 4133 * movement (so that you don't have to compensate for any previously 4134 * collected movement before see the result of the movement in the 4135 * new direction). 4136 * 4137 * @return Returns the absolute value of the amount of movement 4138 * collected so far. 4139 */ collect(float off, long time, String axis)4140 float collect(float off, long time, String axis) { 4141 long normTime; 4142 if (off > 0) { 4143 normTime = (long)(off * FAST_MOVE_TIME); 4144 if (dir < 0) { 4145 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!"); 4146 position = 0; 4147 step = 0; 4148 acceleration = 1; 4149 lastMoveTime = 0; 4150 } 4151 dir = 1; 4152 } else if (off < 0) { 4153 normTime = (long)((-off) * FAST_MOVE_TIME); 4154 if (dir > 0) { 4155 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!"); 4156 position = 0; 4157 step = 0; 4158 acceleration = 1; 4159 lastMoveTime = 0; 4160 } 4161 dir = -1; 4162 } else { 4163 normTime = 0; 4164 } 4165 4166 // The number of milliseconds between each movement that is 4167 // considered "normal" and will not result in any acceleration 4168 // or deceleration, scaled by the offset we have here. 4169 if (normTime > 0) { 4170 long delta = time - lastMoveTime; 4171 lastMoveTime = time; 4172 float acc = acceleration; 4173 if (delta < normTime) { 4174 // The user is scrolling rapidly, so increase acceleration. 4175 float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR; 4176 if (scale > 1) acc *= scale; 4177 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off=" 4178 + off + " normTime=" + normTime + " delta=" + delta 4179 + " scale=" + scale + " acc=" + acc); 4180 acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION; 4181 } else { 4182 // The user is scrolling slowly, so decrease acceleration. 4183 float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR; 4184 if (scale > 1) acc /= scale; 4185 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off=" 4186 + off + " normTime=" + normTime + " delta=" + delta 4187 + " scale=" + scale + " acc=" + acc); 4188 acceleration = acc > 1 ? acc : 1; 4189 } 4190 } 4191 position += off; 4192 return Math.abs(position); 4193 } 4194 4195 /** 4196 * Generate the number of discrete movement events appropriate for 4197 * the currently collected trackball movement. 4198 * 4199 * @return Returns the number of discrete movements, either positive 4200 * or negative, or 0 if there is not enough trackball movement yet 4201 * for a discrete movement. 4202 */ generate()4203 int generate() { 4204 int movement = 0; 4205 nonAccelMovement = 0; 4206 do { 4207 final int dir = position >= 0 ? 1 : -1; 4208 switch (step) { 4209 // If we are going to execute the first step, then we want 4210 // to do this as soon as possible instead of waiting for 4211 // a full movement, in order to make things look responsive. 4212 case 0: 4213 if (Math.abs(position) < FIRST_MOVEMENT_THRESHOLD) { 4214 return movement; 4215 } 4216 movement += dir; 4217 nonAccelMovement += dir; 4218 step = 1; 4219 break; 4220 // If we have generated the first movement, then we need 4221 // to wait for the second complete trackball motion before 4222 // generating the second discrete movement. 4223 case 1: 4224 if (Math.abs(position) < SECOND_CUMULATIVE_MOVEMENT_THRESHOLD) { 4225 return movement; 4226 } 4227 movement += dir; 4228 nonAccelMovement += dir; 4229 position -= SECOND_CUMULATIVE_MOVEMENT_THRESHOLD * dir; 4230 step = 2; 4231 break; 4232 // After the first two, we generate discrete movements 4233 // consistently with the trackball, applying an acceleration 4234 // if the trackball is moving quickly. This is a simple 4235 // acceleration on top of what we already compute based 4236 // on how quickly the wheel is being turned, to apply 4237 // a longer increasing acceleration to continuous movement 4238 // in one direction. 4239 default: 4240 if (Math.abs(position) < SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD) { 4241 return movement; 4242 } 4243 movement += dir; 4244 position -= dir * SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD; 4245 float acc = acceleration; 4246 acc *= 1.1f; 4247 acceleration = acc < MAX_ACCELERATION ? acc : acceleration; 4248 break; 4249 } 4250 } while (true); 4251 } 4252 } 4253 4254 /** 4255 * Creates dpad events from unhandled joystick movements. 4256 */ 4257 final class SyntheticJoystickHandler extends Handler { 4258 private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 1; 4259 private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 2; 4260 4261 private int mLastXDirection; 4262 private int mLastYDirection; 4263 private int mLastXKeyCode; 4264 private int mLastYKeyCode; 4265 4266 public SyntheticJoystickHandler() { 4267 super(true); 4268 } 4269 4270 @Override 4271 public void handleMessage(Message msg) { 4272 switch (msg.what) { 4273 case MSG_ENQUEUE_X_AXIS_KEY_REPEAT: 4274 case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: { 4275 KeyEvent oldEvent = (KeyEvent)msg.obj; 4276 KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent, 4277 SystemClock.uptimeMillis(), 4278 oldEvent.getRepeatCount() + 1); 4279 if (mAttachInfo.mHasWindowFocus) { 4280 enqueueInputEvent(e); 4281 Message m = obtainMessage(msg.what, e); 4282 m.setAsynchronous(true); 4283 sendMessageDelayed(m, ViewConfiguration.getKeyRepeatDelay()); 4284 } 4285 } break; 4286 } 4287 } 4288 4289 public void process(MotionEvent event) { 4290 update(event, true); 4291 } 4292 4293 public void cancel(MotionEvent event) { 4294 update(event, false); 4295 } 4296 4297 private void update(MotionEvent event, boolean synthesizeNewKeys) { 4298 final long time = event.getEventTime(); 4299 final int metaState = event.getMetaState(); 4300 final int deviceId = event.getDeviceId(); 4301 final int source = event.getSource(); 4302 4303 int xDirection = joystickAxisValueToDirection( 4304 event.getAxisValue(MotionEvent.AXIS_HAT_X)); 4305 if (xDirection == 0) { 4306 xDirection = joystickAxisValueToDirection(event.getX()); 4307 } 4308 4309 int yDirection = joystickAxisValueToDirection( 4310 event.getAxisValue(MotionEvent.AXIS_HAT_Y)); 4311 if (yDirection == 0) { 4312 yDirection = joystickAxisValueToDirection(event.getY()); 4313 } 4314 4315 if (xDirection != mLastXDirection) { 4316 if (mLastXKeyCode != 0) { 4317 removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT); 4318 enqueueInputEvent(new KeyEvent(time, time, 4319 KeyEvent.ACTION_UP, mLastXKeyCode, 0, metaState, 4320 deviceId, 0, KeyEvent.FLAG_FALLBACK, source)); 4321 mLastXKeyCode = 0; 4322 } 4323 4324 mLastXDirection = xDirection; 4325 4326 if (xDirection != 0 && synthesizeNewKeys) { 4327 mLastXKeyCode = xDirection > 0 4328 ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT; 4329 final KeyEvent e = new KeyEvent(time, time, 4330 KeyEvent.ACTION_DOWN, mLastXKeyCode, 0, metaState, 4331 deviceId, 0, KeyEvent.FLAG_FALLBACK, source); 4332 enqueueInputEvent(e); 4333 Message m = obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e); 4334 m.setAsynchronous(true); 4335 sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout()); 4336 } 4337 } 4338 4339 if (yDirection != mLastYDirection) { 4340 if (mLastYKeyCode != 0) { 4341 removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT); 4342 enqueueInputEvent(new KeyEvent(time, time, 4343 KeyEvent.ACTION_UP, mLastYKeyCode, 0, metaState, 4344 deviceId, 0, KeyEvent.FLAG_FALLBACK, source)); 4345 mLastYKeyCode = 0; 4346 } 4347 4348 mLastYDirection = yDirection; 4349 4350 if (yDirection != 0 && synthesizeNewKeys) { 4351 mLastYKeyCode = yDirection > 0 4352 ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP; 4353 final KeyEvent e = new KeyEvent(time, time, 4354 KeyEvent.ACTION_DOWN, mLastYKeyCode, 0, metaState, 4355 deviceId, 0, KeyEvent.FLAG_FALLBACK, source); 4356 enqueueInputEvent(e); 4357 Message m = obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e); 4358 m.setAsynchronous(true); 4359 sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout()); 4360 } 4361 } 4362 } 4363 joystickAxisValueToDirection(float value)4364 private int joystickAxisValueToDirection(float value) { 4365 if (value >= 0.5f) { 4366 return 1; 4367 } else if (value <= -0.5f) { 4368 return -1; 4369 } else { 4370 return 0; 4371 } 4372 } 4373 } 4374 4375 /** 4376 * Creates dpad events from unhandled touch navigation movements. 4377 */ 4378 final class SyntheticTouchNavigationHandler extends Handler { 4379 private static final String LOCAL_TAG = "SyntheticTouchNavigationHandler"; 4380 private static final boolean LOCAL_DEBUG = false; 4381 4382 // Assumed nominal width and height in millimeters of a touch navigation pad, 4383 // if no resolution information is available from the input system. 4384 private static final float DEFAULT_WIDTH_MILLIMETERS = 48; 4385 private static final float DEFAULT_HEIGHT_MILLIMETERS = 48; 4386 4387 /* TODO: These constants should eventually be moved to ViewConfiguration. */ 4388 4389 // Tap timeout in milliseconds. 4390 private static final int TAP_TIMEOUT = 250; 4391 4392 // The maximum distance traveled for a gesture to be considered a tap in millimeters. 4393 private static final int TAP_SLOP_MILLIMETERS = 5; 4394 4395 // The nominal distance traveled to move by one unit. 4396 private static final int TICK_DISTANCE_MILLIMETERS = 12; 4397 4398 // Minimum and maximum fling velocity in ticks per second. 4399 // The minimum velocity should be set such that we perform enough ticks per 4400 // second that the fling appears to be fluid. For example, if we set the minimum 4401 // to 2 ticks per second, then there may be up to half a second delay between the next 4402 // to last and last ticks which is noticeably discrete and jerky. This value should 4403 // probably not be set to anything less than about 4. 4404 // If fling accuracy is a problem then consider tuning the tick distance instead. 4405 private static final float MIN_FLING_VELOCITY_TICKS_PER_SECOND = 6f; 4406 private static final float MAX_FLING_VELOCITY_TICKS_PER_SECOND = 20f; 4407 4408 // Fling velocity decay factor applied after each new key is emitted. 4409 // This parameter controls the deceleration and overall duration of the fling. 4410 // The fling stops automatically when its velocity drops below the minimum 4411 // fling velocity defined above. 4412 private static final float FLING_TICK_DECAY = 0.8f; 4413 4414 /* The input device that we are tracking. */ 4415 4416 private int mCurrentDeviceId = -1; 4417 private int mCurrentSource; 4418 private boolean mCurrentDeviceSupported; 4419 4420 /* Configuration for the current input device. */ 4421 4422 // The tap timeout and scaled slop. 4423 private int mConfigTapTimeout; 4424 private float mConfigTapSlop; 4425 4426 // The scaled tick distance. A movement of this amount should generally translate 4427 // into a single dpad event in a given direction. 4428 private float mConfigTickDistance; 4429 4430 // The minimum and maximum scaled fling velocity. 4431 private float mConfigMinFlingVelocity; 4432 private float mConfigMaxFlingVelocity; 4433 4434 /* Tracking state. */ 4435 4436 // The velocity tracker for detecting flings. 4437 private VelocityTracker mVelocityTracker; 4438 4439 // The active pointer id, or -1 if none. 4440 private int mActivePointerId = -1; 4441 4442 // Time and location where tracking started. 4443 private long mStartTime; 4444 private float mStartX; 4445 private float mStartY; 4446 4447 // Most recently observed position. 4448 private float mLastX; 4449 private float mLastY; 4450 4451 // Accumulated movement delta since the last direction key was sent. 4452 private float mAccumulatedX; 4453 private float mAccumulatedY; 4454 4455 // Set to true if any movement was delivered to the app. 4456 // Implies that tap slop was exceeded. 4457 private boolean mConsumedMovement; 4458 4459 // The most recently sent key down event. 4460 // The keycode remains set until the direction changes or a fling ends 4461 // so that repeated key events may be generated as required. 4462 private long mPendingKeyDownTime; 4463 private int mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN; 4464 private int mPendingKeyRepeatCount; 4465 private int mPendingKeyMetaState; 4466 4467 // The current fling velocity while a fling is in progress. 4468 private boolean mFlinging; 4469 private float mFlingVelocity; 4470 SyntheticTouchNavigationHandler()4471 public SyntheticTouchNavigationHandler() { 4472 super(true); 4473 } 4474 process(MotionEvent event)4475 public void process(MotionEvent event) { 4476 // Update the current device information. 4477 final long time = event.getEventTime(); 4478 final int deviceId = event.getDeviceId(); 4479 final int source = event.getSource(); 4480 if (mCurrentDeviceId != deviceId || mCurrentSource != source) { 4481 finishKeys(time); 4482 finishTracking(time); 4483 mCurrentDeviceId = deviceId; 4484 mCurrentSource = source; 4485 mCurrentDeviceSupported = false; 4486 InputDevice device = event.getDevice(); 4487 if (device != null) { 4488 // In order to support an input device, we must know certain 4489 // characteristics about it, such as its size and resolution. 4490 InputDevice.MotionRange xRange = device.getMotionRange(MotionEvent.AXIS_X); 4491 InputDevice.MotionRange yRange = device.getMotionRange(MotionEvent.AXIS_Y); 4492 if (xRange != null && yRange != null) { 4493 mCurrentDeviceSupported = true; 4494 4495 // Infer the resolution if it not actually known. 4496 float xRes = xRange.getResolution(); 4497 if (xRes <= 0) { 4498 xRes = xRange.getRange() / DEFAULT_WIDTH_MILLIMETERS; 4499 } 4500 float yRes = yRange.getResolution(); 4501 if (yRes <= 0) { 4502 yRes = yRange.getRange() / DEFAULT_HEIGHT_MILLIMETERS; 4503 } 4504 float nominalRes = (xRes + yRes) * 0.5f; 4505 4506 // Precompute all of the configuration thresholds we will need. 4507 mConfigTapTimeout = TAP_TIMEOUT; 4508 mConfigTapSlop = TAP_SLOP_MILLIMETERS * nominalRes; 4509 mConfigTickDistance = TICK_DISTANCE_MILLIMETERS * nominalRes; 4510 mConfigMinFlingVelocity = 4511 MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance; 4512 mConfigMaxFlingVelocity = 4513 MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance; 4514 4515 if (LOCAL_DEBUG) { 4516 Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId 4517 + " (" + Integer.toHexString(mCurrentSource) + "): " 4518 + "mConfigTapTimeout=" + mConfigTapTimeout 4519 + ", mConfigTapSlop=" + mConfigTapSlop 4520 + ", mConfigTickDistance=" + mConfigTickDistance 4521 + ", mConfigMinFlingVelocity=" + mConfigMinFlingVelocity 4522 + ", mConfigMaxFlingVelocity=" + mConfigMaxFlingVelocity); 4523 } 4524 } 4525 } 4526 } 4527 if (!mCurrentDeviceSupported) { 4528 return; 4529 } 4530 4531 // Handle the event. 4532 final int action = event.getActionMasked(); 4533 switch (action) { 4534 case MotionEvent.ACTION_DOWN: { 4535 boolean caughtFling = mFlinging; 4536 finishKeys(time); 4537 finishTracking(time); 4538 mActivePointerId = event.getPointerId(0); 4539 mVelocityTracker = VelocityTracker.obtain(); 4540 mVelocityTracker.addMovement(event); 4541 mStartTime = time; 4542 mStartX = event.getX(); 4543 mStartY = event.getY(); 4544 mLastX = mStartX; 4545 mLastY = mStartY; 4546 mAccumulatedX = 0; 4547 mAccumulatedY = 0; 4548 4549 // If we caught a fling, then pretend that the tap slop has already 4550 // been exceeded to suppress taps whose only purpose is to stop the fling. 4551 mConsumedMovement = caughtFling; 4552 break; 4553 } 4554 4555 case MotionEvent.ACTION_MOVE: 4556 case MotionEvent.ACTION_UP: { 4557 if (mActivePointerId < 0) { 4558 break; 4559 } 4560 final int index = event.findPointerIndex(mActivePointerId); 4561 if (index < 0) { 4562 finishKeys(time); 4563 finishTracking(time); 4564 break; 4565 } 4566 4567 mVelocityTracker.addMovement(event); 4568 final float x = event.getX(index); 4569 final float y = event.getY(index); 4570 mAccumulatedX += x - mLastX; 4571 mAccumulatedY += y - mLastY; 4572 mLastX = x; 4573 mLastY = y; 4574 4575 // Consume any accumulated movement so far. 4576 final int metaState = event.getMetaState(); 4577 consumeAccumulatedMovement(time, metaState); 4578 4579 // Detect taps and flings. 4580 if (action == MotionEvent.ACTION_UP) { 4581 if (!mConsumedMovement 4582 && Math.hypot(mLastX - mStartX, mLastY - mStartY) < mConfigTapSlop 4583 && time <= mStartTime + mConfigTapTimeout) { 4584 // It's a tap! 4585 finishKeys(time); 4586 sendKeyDownOrRepeat(time, KeyEvent.KEYCODE_DPAD_CENTER, metaState); 4587 sendKeyUp(time); 4588 } else if (mConsumedMovement 4589 && mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) { 4590 // It might be a fling. 4591 mVelocityTracker.computeCurrentVelocity(1000, mConfigMaxFlingVelocity); 4592 final float vx = mVelocityTracker.getXVelocity(mActivePointerId); 4593 final float vy = mVelocityTracker.getYVelocity(mActivePointerId); 4594 if (!startFling(time, vx, vy)) { 4595 finishKeys(time); 4596 } 4597 } 4598 finishTracking(time); 4599 } 4600 break; 4601 } 4602 4603 case MotionEvent.ACTION_CANCEL: { 4604 finishKeys(time); 4605 finishTracking(time); 4606 break; 4607 } 4608 } 4609 } 4610 cancel(MotionEvent event)4611 public void cancel(MotionEvent event) { 4612 if (mCurrentDeviceId == event.getDeviceId() 4613 && mCurrentSource == event.getSource()) { 4614 final long time = event.getEventTime(); 4615 finishKeys(time); 4616 finishTracking(time); 4617 } 4618 } 4619 finishKeys(long time)4620 private void finishKeys(long time) { 4621 cancelFling(); 4622 sendKeyUp(time); 4623 } 4624 finishTracking(long time)4625 private void finishTracking(long time) { 4626 if (mActivePointerId >= 0) { 4627 mActivePointerId = -1; 4628 mVelocityTracker.recycle(); 4629 mVelocityTracker = null; 4630 } 4631 } 4632 consumeAccumulatedMovement(long time, int metaState)4633 private void consumeAccumulatedMovement(long time, int metaState) { 4634 final float absX = Math.abs(mAccumulatedX); 4635 final float absY = Math.abs(mAccumulatedY); 4636 if (absX >= absY) { 4637 if (absX >= mConfigTickDistance) { 4638 mAccumulatedX = consumeAccumulatedMovement(time, metaState, mAccumulatedX, 4639 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT); 4640 mAccumulatedY = 0; 4641 mConsumedMovement = true; 4642 } 4643 } else { 4644 if (absY >= mConfigTickDistance) { 4645 mAccumulatedY = consumeAccumulatedMovement(time, metaState, mAccumulatedY, 4646 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN); 4647 mAccumulatedX = 0; 4648 mConsumedMovement = true; 4649 } 4650 } 4651 } 4652 consumeAccumulatedMovement(long time, int metaState, float accumulator, int negativeKeyCode, int positiveKeyCode)4653 private float consumeAccumulatedMovement(long time, int metaState, 4654 float accumulator, int negativeKeyCode, int positiveKeyCode) { 4655 while (accumulator <= -mConfigTickDistance) { 4656 sendKeyDownOrRepeat(time, negativeKeyCode, metaState); 4657 accumulator += mConfigTickDistance; 4658 } 4659 while (accumulator >= mConfigTickDistance) { 4660 sendKeyDownOrRepeat(time, positiveKeyCode, metaState); 4661 accumulator -= mConfigTickDistance; 4662 } 4663 return accumulator; 4664 } 4665 sendKeyDownOrRepeat(long time, int keyCode, int metaState)4666 private void sendKeyDownOrRepeat(long time, int keyCode, int metaState) { 4667 if (mPendingKeyCode != keyCode) { 4668 sendKeyUp(time); 4669 mPendingKeyDownTime = time; 4670 mPendingKeyCode = keyCode; 4671 mPendingKeyRepeatCount = 0; 4672 } else { 4673 mPendingKeyRepeatCount += 1; 4674 } 4675 mPendingKeyMetaState = metaState; 4676 4677 // Note: Normally we would pass FLAG_LONG_PRESS when the repeat count is 1 4678 // but it doesn't quite make sense when simulating the events in this way. 4679 if (LOCAL_DEBUG) { 4680 Log.d(LOCAL_TAG, "Sending key down: keyCode=" + mPendingKeyCode 4681 + ", repeatCount=" + mPendingKeyRepeatCount 4682 + ", metaState=" + Integer.toHexString(mPendingKeyMetaState)); 4683 } 4684 enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time, 4685 KeyEvent.ACTION_DOWN, mPendingKeyCode, mPendingKeyRepeatCount, 4686 mPendingKeyMetaState, mCurrentDeviceId, 4687 KeyEvent.FLAG_FALLBACK, mCurrentSource)); 4688 } 4689 sendKeyUp(long time)4690 private void sendKeyUp(long time) { 4691 if (mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) { 4692 if (LOCAL_DEBUG) { 4693 Log.d(LOCAL_TAG, "Sending key up: keyCode=" + mPendingKeyCode 4694 + ", metaState=" + Integer.toHexString(mPendingKeyMetaState)); 4695 } 4696 enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time, 4697 KeyEvent.ACTION_UP, mPendingKeyCode, 0, mPendingKeyMetaState, 4698 mCurrentDeviceId, 0, KeyEvent.FLAG_FALLBACK, 4699 mCurrentSource)); 4700 mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN; 4701 } 4702 } 4703 startFling(long time, float vx, float vy)4704 private boolean startFling(long time, float vx, float vy) { 4705 if (LOCAL_DEBUG) { 4706 Log.d(LOCAL_TAG, "Considering fling: vx=" + vx + ", vy=" + vy 4707 + ", min=" + mConfigMinFlingVelocity); 4708 } 4709 4710 // Flings must be oriented in the same direction as the preceding movements. 4711 switch (mPendingKeyCode) { 4712 case KeyEvent.KEYCODE_DPAD_LEFT: 4713 if (-vx >= mConfigMinFlingVelocity 4714 && Math.abs(vy) < mConfigMinFlingVelocity) { 4715 mFlingVelocity = -vx; 4716 break; 4717 } 4718 return false; 4719 4720 case KeyEvent.KEYCODE_DPAD_RIGHT: 4721 if (vx >= mConfigMinFlingVelocity 4722 && Math.abs(vy) < mConfigMinFlingVelocity) { 4723 mFlingVelocity = vx; 4724 break; 4725 } 4726 return false; 4727 4728 case KeyEvent.KEYCODE_DPAD_UP: 4729 if (-vy >= mConfigMinFlingVelocity 4730 && Math.abs(vx) < mConfigMinFlingVelocity) { 4731 mFlingVelocity = -vy; 4732 break; 4733 } 4734 return false; 4735 4736 case KeyEvent.KEYCODE_DPAD_DOWN: 4737 if (vy >= mConfigMinFlingVelocity 4738 && Math.abs(vx) < mConfigMinFlingVelocity) { 4739 mFlingVelocity = vy; 4740 break; 4741 } 4742 return false; 4743 } 4744 4745 // Post the first fling event. 4746 mFlinging = postFling(time); 4747 return mFlinging; 4748 } 4749 postFling(long time)4750 private boolean postFling(long time) { 4751 // The idea here is to estimate the time when the pointer would have 4752 // traveled one tick distance unit given the current fling velocity. 4753 // This effect creates continuity of motion. 4754 if (mFlingVelocity >= mConfigMinFlingVelocity) { 4755 long delay = (long)(mConfigTickDistance / mFlingVelocity * 1000); 4756 postAtTime(mFlingRunnable, time + delay); 4757 if (LOCAL_DEBUG) { 4758 Log.d(LOCAL_TAG, "Posted fling: velocity=" 4759 + mFlingVelocity + ", delay=" + delay 4760 + ", keyCode=" + mPendingKeyCode); 4761 } 4762 return true; 4763 } 4764 return false; 4765 } 4766 cancelFling()4767 private void cancelFling() { 4768 if (mFlinging) { 4769 removeCallbacks(mFlingRunnable); 4770 mFlinging = false; 4771 } 4772 } 4773 4774 private final Runnable mFlingRunnable = new Runnable() { 4775 @Override 4776 public void run() { 4777 final long time = SystemClock.uptimeMillis(); 4778 sendKeyDownOrRepeat(time, mPendingKeyCode, mPendingKeyMetaState); 4779 mFlingVelocity *= FLING_TICK_DECAY; 4780 if (!postFling(time)) { 4781 mFlinging = false; 4782 finishKeys(time); 4783 } 4784 } 4785 }; 4786 } 4787 4788 /** 4789 * Returns true if the key is used for keyboard navigation. 4790 * @param keyEvent The key event. 4791 * @return True if the key is used for keyboard navigation. 4792 */ isNavigationKey(KeyEvent keyEvent)4793 private static boolean isNavigationKey(KeyEvent keyEvent) { 4794 switch (keyEvent.getKeyCode()) { 4795 case KeyEvent.KEYCODE_DPAD_LEFT: 4796 case KeyEvent.KEYCODE_DPAD_RIGHT: 4797 case KeyEvent.KEYCODE_DPAD_UP: 4798 case KeyEvent.KEYCODE_DPAD_DOWN: 4799 case KeyEvent.KEYCODE_DPAD_CENTER: 4800 case KeyEvent.KEYCODE_PAGE_UP: 4801 case KeyEvent.KEYCODE_PAGE_DOWN: 4802 case KeyEvent.KEYCODE_MOVE_HOME: 4803 case KeyEvent.KEYCODE_MOVE_END: 4804 case KeyEvent.KEYCODE_TAB: 4805 case KeyEvent.KEYCODE_SPACE: 4806 case KeyEvent.KEYCODE_ENTER: 4807 return true; 4808 } 4809 return false; 4810 } 4811 4812 /** 4813 * Returns true if the key is used for typing. 4814 * @param keyEvent The key event. 4815 * @return True if the key is used for typing. 4816 */ isTypingKey(KeyEvent keyEvent)4817 private static boolean isTypingKey(KeyEvent keyEvent) { 4818 return keyEvent.getUnicodeChar() > 0; 4819 } 4820 4821 /** 4822 * See if the key event means we should leave touch mode (and leave touch mode if so). 4823 * @param event The key event. 4824 * @return Whether this key event should be consumed (meaning the act of 4825 * leaving touch mode alone is considered the event). 4826 */ checkForLeavingTouchModeAndConsume(KeyEvent event)4827 private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) { 4828 // Only relevant in touch mode. 4829 if (!mAttachInfo.mInTouchMode) { 4830 return false; 4831 } 4832 4833 // Only consider leaving touch mode on DOWN or MULTIPLE actions, never on UP. 4834 final int action = event.getAction(); 4835 if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) { 4836 return false; 4837 } 4838 4839 // Don't leave touch mode if the IME told us not to. 4840 if ((event.getFlags() & KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) { 4841 return false; 4842 } 4843 4844 // If the key can be used for keyboard navigation then leave touch mode 4845 // and select a focused view if needed (in ensureTouchMode). 4846 // When a new focused view is selected, we consume the navigation key because 4847 // navigation doesn't make much sense unless a view already has focus so 4848 // the key's purpose is to set focus. 4849 if (isNavigationKey(event)) { 4850 return ensureTouchMode(false); 4851 } 4852 4853 // If the key can be used for typing then leave touch mode 4854 // and select a focused view if needed (in ensureTouchMode). 4855 // Always allow the view to process the typing key. 4856 if (isTypingKey(event)) { 4857 ensureTouchMode(false); 4858 return false; 4859 } 4860 4861 return false; 4862 } 4863 4864 /* drag/drop */ setLocalDragState(Object obj)4865 void setLocalDragState(Object obj) { 4866 mLocalDragState = obj; 4867 } 4868 handleDragEvent(DragEvent event)4869 private void handleDragEvent(DragEvent event) { 4870 // From the root, only drag start/end/location are dispatched. entered/exited 4871 // are determined and dispatched by the viewgroup hierarchy, who then report 4872 // that back here for ultimate reporting back to the framework. 4873 if (mView != null && mAdded) { 4874 final int what = event.mAction; 4875 4876 if (what == DragEvent.ACTION_DRAG_EXITED) { 4877 // A direct EXITED event means that the window manager knows we've just crossed 4878 // a window boundary, so the current drag target within this one must have 4879 // just been exited. Send it the usual notifications and then we're done 4880 // for now. 4881 mView.dispatchDragEvent(event); 4882 } else { 4883 // Cache the drag description when the operation starts, then fill it in 4884 // on subsequent calls as a convenience 4885 if (what == DragEvent.ACTION_DRAG_STARTED) { 4886 mCurrentDragView = null; // Start the current-recipient tracking 4887 mDragDescription = event.mClipDescription; 4888 } else { 4889 event.mClipDescription = mDragDescription; 4890 } 4891 4892 // For events with a [screen] location, translate into window coordinates 4893 if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) { 4894 mDragPoint.set(event.mX, event.mY); 4895 if (mTranslator != null) { 4896 mTranslator.translatePointInScreenToAppWindow(mDragPoint); 4897 } 4898 4899 if (mCurScrollY != 0) { 4900 mDragPoint.offset(0, mCurScrollY); 4901 } 4902 4903 event.mX = mDragPoint.x; 4904 event.mY = mDragPoint.y; 4905 } 4906 4907 // Remember who the current drag target is pre-dispatch 4908 final View prevDragView = mCurrentDragView; 4909 4910 // Now dispatch the drag/drop event 4911 boolean result = mView.dispatchDragEvent(event); 4912 4913 // If we changed apparent drag target, tell the OS about it 4914 if (prevDragView != mCurrentDragView) { 4915 try { 4916 if (prevDragView != null) { 4917 mWindowSession.dragRecipientExited(mWindow); 4918 } 4919 if (mCurrentDragView != null) { 4920 mWindowSession.dragRecipientEntered(mWindow); 4921 } 4922 } catch (RemoteException e) { 4923 Slog.e(TAG, "Unable to note drag target change"); 4924 } 4925 } 4926 4927 // Report the drop result when we're done 4928 if (what == DragEvent.ACTION_DROP) { 4929 mDragDescription = null; 4930 try { 4931 Log.i(TAG, "Reporting drop result: " + result); 4932 mWindowSession.reportDropResult(mWindow, result); 4933 } catch (RemoteException e) { 4934 Log.e(TAG, "Unable to report drop result"); 4935 } 4936 } 4937 4938 // When the drag operation ends, release any local state object 4939 // that may have been in use 4940 if (what == DragEvent.ACTION_DRAG_ENDED) { 4941 setLocalDragState(null); 4942 } 4943 } 4944 } 4945 event.recycle(); 4946 } 4947 handleDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args)4948 public void handleDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args) { 4949 if (mSeq != args.seq) { 4950 // The sequence has changed, so we need to update our value and make 4951 // sure to do a traversal afterward so the window manager is given our 4952 // most recent data. 4953 mSeq = args.seq; 4954 mAttachInfo.mForceReportNewAttributes = true; 4955 scheduleTraversals(); 4956 } 4957 if (mView == null) return; 4958 if (args.localChanges != 0) { 4959 mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges); 4960 } 4961 if (mAttachInfo != null) { 4962 int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS; 4963 if (visibility != mAttachInfo.mGlobalSystemUiVisibility) { 4964 mAttachInfo.mGlobalSystemUiVisibility = visibility; 4965 mView.dispatchSystemUiVisibilityChanged(visibility); 4966 } 4967 } 4968 } 4969 handleDispatchDoneAnimating()4970 public void handleDispatchDoneAnimating() { 4971 if (mWindowsAnimating) { 4972 mWindowsAnimating = false; 4973 if (!mDirty.isEmpty() || mIsAnimating || mFullRedrawNeeded) { 4974 scheduleTraversals(); 4975 } 4976 } 4977 } 4978 getLastTouchPoint(Point outLocation)4979 public void getLastTouchPoint(Point outLocation) { 4980 outLocation.x = (int) mLastTouchPoint.x; 4981 outLocation.y = (int) mLastTouchPoint.y; 4982 } 4983 setDragFocus(View newDragTarget)4984 public void setDragFocus(View newDragTarget) { 4985 if (mCurrentDragView != newDragTarget) { 4986 mCurrentDragView = newDragTarget; 4987 } 4988 } 4989 getAudioManager()4990 private AudioManager getAudioManager() { 4991 if (mView == null) { 4992 throw new IllegalStateException("getAudioManager called when there is no mView"); 4993 } 4994 if (mAudioManager == null) { 4995 mAudioManager = (AudioManager) mView.getContext().getSystemService(Context.AUDIO_SERVICE); 4996 } 4997 return mAudioManager; 4998 } 4999 getAccessibilityInteractionController()5000 public AccessibilityInteractionController getAccessibilityInteractionController() { 5001 if (mView == null) { 5002 throw new IllegalStateException("getAccessibilityInteractionController" 5003 + " called when there is no mView"); 5004 } 5005 if (mAccessibilityInteractionController == null) { 5006 mAccessibilityInteractionController = new AccessibilityInteractionController(this); 5007 } 5008 return mAccessibilityInteractionController; 5009 } 5010 relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending)5011 private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, 5012 boolean insetsPending) throws RemoteException { 5013 5014 float appScale = mAttachInfo.mApplicationScale; 5015 boolean restore = false; 5016 if (params != null && mTranslator != null) { 5017 restore = true; 5018 params.backup(); 5019 mTranslator.translateWindowLayout(params); 5020 } 5021 if (params != null) { 5022 if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params); 5023 } 5024 mPendingConfiguration.seq = 0; 5025 //Log.d(TAG, ">>>>>> CALLING relayout"); 5026 if (params != null && mOrigWindowType != params.type) { 5027 // For compatibility with old apps, don't crash here. 5028 if (mTargetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 5029 Slog.w(TAG, "Window type can not be changed after " 5030 + "the window is added; ignoring change of " + mView); 5031 params.type = mOrigWindowType; 5032 } 5033 } 5034 int relayoutResult = mWindowSession.relayout( 5035 mWindow, mSeq, params, 5036 (int) (mView.getMeasuredWidth() * appScale + 0.5f), 5037 (int) (mView.getMeasuredHeight() * appScale + 0.5f), 5038 viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, 5039 mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, 5040 mPendingConfiguration, mSurface); 5041 //Log.d(TAG, "<<<<<< BACK FROM relayout"); 5042 if (restore) { 5043 params.restore(); 5044 } 5045 5046 if (mTranslator != null) { 5047 mTranslator.translateRectInScreenToAppWinFrame(mWinFrame); 5048 mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets); 5049 mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets); 5050 mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets); 5051 } 5052 return relayoutResult; 5053 } 5054 5055 /** 5056 * {@inheritDoc} 5057 */ playSoundEffect(int effectId)5058 public void playSoundEffect(int effectId) { 5059 checkThread(); 5060 5061 try { 5062 final AudioManager audioManager = getAudioManager(); 5063 5064 switch (effectId) { 5065 case SoundEffectConstants.CLICK: 5066 audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); 5067 return; 5068 case SoundEffectConstants.NAVIGATION_DOWN: 5069 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN); 5070 return; 5071 case SoundEffectConstants.NAVIGATION_LEFT: 5072 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT); 5073 return; 5074 case SoundEffectConstants.NAVIGATION_RIGHT: 5075 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT); 5076 return; 5077 case SoundEffectConstants.NAVIGATION_UP: 5078 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP); 5079 return; 5080 default: 5081 throw new IllegalArgumentException("unknown effect id " + effectId + 5082 " not defined in " + SoundEffectConstants.class.getCanonicalName()); 5083 } 5084 } catch (IllegalStateException e) { 5085 // Exception thrown by getAudioManager() when mView is null 5086 Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e); 5087 e.printStackTrace(); 5088 } 5089 } 5090 5091 /** 5092 * {@inheritDoc} 5093 */ performHapticFeedback(int effectId, boolean always)5094 public boolean performHapticFeedback(int effectId, boolean always) { 5095 try { 5096 return mWindowSession.performHapticFeedback(mWindow, effectId, always); 5097 } catch (RemoteException e) { 5098 return false; 5099 } 5100 } 5101 5102 /** 5103 * {@inheritDoc} 5104 */ focusSearch(View focused, int direction)5105 public View focusSearch(View focused, int direction) { 5106 checkThread(); 5107 if (!(mView instanceof ViewGroup)) { 5108 return null; 5109 } 5110 return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction); 5111 } 5112 debug()5113 public void debug() { 5114 mView.debug(); 5115 } 5116 dumpGfxInfo(int[] info)5117 public void dumpGfxInfo(int[] info) { 5118 info[0] = info[1] = 0; 5119 if (mView != null) { 5120 getGfxInfo(mView, info); 5121 } 5122 } 5123 getGfxInfo(View view, int[] info)5124 private static void getGfxInfo(View view, int[] info) { 5125 DisplayList displayList = view.mDisplayList; 5126 info[0]++; 5127 if (displayList != null) { 5128 info[1] += displayList.getSize(); 5129 } 5130 5131 if (view instanceof ViewGroup) { 5132 ViewGroup group = (ViewGroup) view; 5133 5134 int count = group.getChildCount(); 5135 for (int i = 0; i < count; i++) { 5136 getGfxInfo(group.getChildAt(i), info); 5137 } 5138 } 5139 } 5140 die(boolean immediate)5141 public void die(boolean immediate) { 5142 // Make sure we do execute immediately if we are in the middle of a traversal or the damage 5143 // done by dispatchDetachedFromWindow will cause havoc on return. 5144 if (immediate && !mIsInTraversal) { 5145 doDie(); 5146 } else { 5147 if (!mIsDrawing) { 5148 destroyHardwareRenderer(); 5149 } else { 5150 Log.e(TAG, "Attempting to destroy the window while drawing!\n" + 5151 " window=" + this + ", title=" + mWindowAttributes.getTitle()); 5152 } 5153 mHandler.sendEmptyMessage(MSG_DIE); 5154 } 5155 } 5156 doDie()5157 void doDie() { 5158 checkThread(); 5159 if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface); 5160 synchronized (this) { 5161 if (mAdded) { 5162 dispatchDetachedFromWindow(); 5163 } 5164 5165 if (mAdded && !mFirst) { 5166 invalidateDisplayLists(); 5167 destroyHardwareRenderer(); 5168 5169 if (mView != null) { 5170 int viewVisibility = mView.getVisibility(); 5171 boolean viewVisibilityChanged = mViewVisibility != viewVisibility; 5172 if (mWindowAttributesChanged || viewVisibilityChanged) { 5173 // If layout params have been changed, first give them 5174 // to the window manager to make sure it has the correct 5175 // animation info. 5176 try { 5177 if ((relayoutWindow(mWindowAttributes, viewVisibility, false) 5178 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { 5179 mWindowSession.finishDrawing(mWindow); 5180 } 5181 } catch (RemoteException e) { 5182 } 5183 } 5184 5185 mSurface.release(); 5186 } 5187 } 5188 5189 mAdded = false; 5190 } 5191 } 5192 requestUpdateConfiguration(Configuration config)5193 public void requestUpdateConfiguration(Configuration config) { 5194 Message msg = mHandler.obtainMessage(MSG_UPDATE_CONFIGURATION, config); 5195 mHandler.sendMessage(msg); 5196 } 5197 loadSystemProperties()5198 public void loadSystemProperties() { 5199 mHandler.post(new Runnable() { 5200 @Override 5201 public void run() { 5202 // Profiling 5203 mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false); 5204 profileRendering(mAttachInfo.mHasWindowFocus); 5205 5206 // Hardware rendering 5207 if (mAttachInfo.mHardwareRenderer != null) { 5208 if (mAttachInfo.mHardwareRenderer.loadSystemProperties(mHolder.getSurface())) { 5209 invalidate(); 5210 } 5211 } 5212 5213 // Layout debugging 5214 boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false); 5215 if (layout != mAttachInfo.mDebugLayout) { 5216 mAttachInfo.mDebugLayout = layout; 5217 if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) { 5218 mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200); 5219 } 5220 } 5221 } 5222 }); 5223 } 5224 destroyHardwareRenderer()5225 private void destroyHardwareRenderer() { 5226 AttachInfo attachInfo = mAttachInfo; 5227 HardwareRenderer hardwareRenderer = attachInfo.mHardwareRenderer; 5228 5229 if (hardwareRenderer != null) { 5230 if (mView != null) { 5231 hardwareRenderer.destroyHardwareResources(mView); 5232 } 5233 hardwareRenderer.destroy(true); 5234 hardwareRenderer.setRequested(false); 5235 5236 attachInfo.mHardwareRenderer = null; 5237 attachInfo.mHardwareAccelerated = false; 5238 } 5239 } 5240 dispatchFinishInputConnection(InputConnection connection)5241 public void dispatchFinishInputConnection(InputConnection connection) { 5242 Message msg = mHandler.obtainMessage(MSG_FINISH_INPUT_CONNECTION, connection); 5243 mHandler.sendMessage(msg); 5244 } 5245 dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, boolean reportDraw, Configuration newConfig)5246 public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets, 5247 Rect visibleInsets, boolean reportDraw, Configuration newConfig) { 5248 if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": frame=" + frame.toShortString() 5249 + " contentInsets=" + contentInsets.toShortString() 5250 + " visibleInsets=" + visibleInsets.toShortString() 5251 + " reportDraw=" + reportDraw); 5252 Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED); 5253 if (mTranslator != null) { 5254 mTranslator.translateRectInScreenToAppWindow(frame); 5255 mTranslator.translateRectInScreenToAppWindow(overscanInsets); 5256 mTranslator.translateRectInScreenToAppWindow(contentInsets); 5257 mTranslator.translateRectInScreenToAppWindow(visibleInsets); 5258 } 5259 SomeArgs args = SomeArgs.obtain(); 5260 final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid()); 5261 args.arg1 = sameProcessCall ? new Rect(frame) : frame; 5262 args.arg2 = sameProcessCall ? new Rect(contentInsets) : contentInsets; 5263 args.arg3 = sameProcessCall ? new Rect(visibleInsets) : visibleInsets; 5264 args.arg4 = sameProcessCall && newConfig != null ? new Configuration(newConfig) : newConfig; 5265 args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets; 5266 msg.obj = args; 5267 mHandler.sendMessage(msg); 5268 } 5269 dispatchMoved(int newX, int newY)5270 public void dispatchMoved(int newX, int newY) { 5271 if (DEBUG_LAYOUT) Log.v(TAG, "Window moved " + this + ": newX=" + newX + " newY=" + newY); 5272 if (mTranslator != null) { 5273 PointF point = new PointF(newX, newY); 5274 mTranslator.translatePointInScreenToAppWindow(point); 5275 newX = (int) (point.x + 0.5); 5276 newY = (int) (point.y + 0.5); 5277 } 5278 Message msg = mHandler.obtainMessage(MSG_WINDOW_MOVED, newX, newY); 5279 mHandler.sendMessage(msg); 5280 } 5281 5282 /** 5283 * Represents a pending input event that is waiting in a queue. 5284 * 5285 * Input events are processed in serial order by the timestamp specified by 5286 * {@link InputEvent#getEventTimeNano()}. In general, the input dispatcher delivers 5287 * one input event to the application at a time and waits for the application 5288 * to finish handling it before delivering the next one. 5289 * 5290 * However, because the application or IME can synthesize and inject multiple 5291 * key events at a time without going through the input dispatcher, we end up 5292 * needing a queue on the application's side. 5293 */ 5294 private static final class QueuedInputEvent { 5295 public static final int FLAG_DELIVER_POST_IME = 1 << 0; 5296 public static final int FLAG_DEFERRED = 1 << 1; 5297 public static final int FLAG_FINISHED = 1 << 2; 5298 public static final int FLAG_FINISHED_HANDLED = 1 << 3; 5299 public static final int FLAG_RESYNTHESIZED = 1 << 4; 5300 5301 public QueuedInputEvent mNext; 5302 5303 public InputEvent mEvent; 5304 public InputEventReceiver mReceiver; 5305 public int mFlags; 5306 shouldSkipIme()5307 public boolean shouldSkipIme() { 5308 if ((mFlags & FLAG_DELIVER_POST_IME) != 0) { 5309 return true; 5310 } 5311 return mEvent instanceof MotionEvent 5312 && mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER); 5313 } 5314 } 5315 obtainQueuedInputEvent(InputEvent event, InputEventReceiver receiver, int flags)5316 private QueuedInputEvent obtainQueuedInputEvent(InputEvent event, 5317 InputEventReceiver receiver, int flags) { 5318 QueuedInputEvent q = mQueuedInputEventPool; 5319 if (q != null) { 5320 mQueuedInputEventPoolSize -= 1; 5321 mQueuedInputEventPool = q.mNext; 5322 q.mNext = null; 5323 } else { 5324 q = new QueuedInputEvent(); 5325 } 5326 5327 q.mEvent = event; 5328 q.mReceiver = receiver; 5329 q.mFlags = flags; 5330 return q; 5331 } 5332 recycleQueuedInputEvent(QueuedInputEvent q)5333 private void recycleQueuedInputEvent(QueuedInputEvent q) { 5334 q.mEvent = null; 5335 q.mReceiver = null; 5336 5337 if (mQueuedInputEventPoolSize < MAX_QUEUED_INPUT_EVENT_POOL_SIZE) { 5338 mQueuedInputEventPoolSize += 1; 5339 q.mNext = mQueuedInputEventPool; 5340 mQueuedInputEventPool = q; 5341 } 5342 } 5343 enqueueInputEvent(InputEvent event)5344 void enqueueInputEvent(InputEvent event) { 5345 enqueueInputEvent(event, null, 0, false); 5346 } 5347 enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately)5348 void enqueueInputEvent(InputEvent event, 5349 InputEventReceiver receiver, int flags, boolean processImmediately) { 5350 QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); 5351 5352 // Always enqueue the input event in order, regardless of its time stamp. 5353 // We do this because the application or the IME may inject key events 5354 // in response to touch events and we want to ensure that the injected keys 5355 // are processed in the order they were received and we cannot trust that 5356 // the time stamp of injected events are monotonic. 5357 QueuedInputEvent last = mPendingInputEventTail; 5358 if (last == null) { 5359 mPendingInputEventHead = q; 5360 mPendingInputEventTail = q; 5361 } else { 5362 last.mNext = q; 5363 mPendingInputEventTail = q; 5364 } 5365 mPendingInputEventCount += 1; 5366 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, 5367 mPendingInputEventCount); 5368 5369 if (processImmediately) { 5370 doProcessInputEvents(); 5371 } else { 5372 scheduleProcessInputEvents(); 5373 } 5374 } 5375 scheduleProcessInputEvents()5376 private void scheduleProcessInputEvents() { 5377 if (!mProcessInputEventsScheduled) { 5378 mProcessInputEventsScheduled = true; 5379 Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS); 5380 msg.setAsynchronous(true); 5381 mHandler.sendMessage(msg); 5382 } 5383 } 5384 doProcessInputEvents()5385 void doProcessInputEvents() { 5386 // Deliver all pending input events in the queue. 5387 while (mPendingInputEventHead != null) { 5388 QueuedInputEvent q = mPendingInputEventHead; 5389 mPendingInputEventHead = q.mNext; 5390 if (mPendingInputEventHead == null) { 5391 mPendingInputEventTail = null; 5392 } 5393 q.mNext = null; 5394 5395 mPendingInputEventCount -= 1; 5396 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, 5397 mPendingInputEventCount); 5398 5399 deliverInputEvent(q); 5400 } 5401 5402 // We are done processing all input events that we can process right now 5403 // so we can clear the pending flag immediately. 5404 if (mProcessInputEventsScheduled) { 5405 mProcessInputEventsScheduled = false; 5406 mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS); 5407 } 5408 } 5409 deliverInputEvent(QueuedInputEvent q)5410 private void deliverInputEvent(QueuedInputEvent q) { 5411 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent"); 5412 try { 5413 if (mInputEventConsistencyVerifier != null) { 5414 mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0); 5415 } 5416 5417 InputStage stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; 5418 if (stage != null) { 5419 stage.deliver(q); 5420 } else { 5421 finishInputEvent(q); 5422 } 5423 } finally { 5424 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 5425 } 5426 } 5427 finishInputEvent(QueuedInputEvent q)5428 private void finishInputEvent(QueuedInputEvent q) { 5429 if (q.mReceiver != null) { 5430 boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0; 5431 q.mReceiver.finishInputEvent(q.mEvent, handled); 5432 } else { 5433 q.mEvent.recycleIfNeededAfterDispatch(); 5434 } 5435 5436 recycleQueuedInputEvent(q); 5437 } 5438 isTerminalInputEvent(InputEvent event)5439 static boolean isTerminalInputEvent(InputEvent event) { 5440 if (event instanceof KeyEvent) { 5441 final KeyEvent keyEvent = (KeyEvent)event; 5442 return keyEvent.getAction() == KeyEvent.ACTION_UP; 5443 } else { 5444 final MotionEvent motionEvent = (MotionEvent)event; 5445 final int action = motionEvent.getAction(); 5446 return action == MotionEvent.ACTION_UP 5447 || action == MotionEvent.ACTION_CANCEL 5448 || action == MotionEvent.ACTION_HOVER_EXIT; 5449 } 5450 } 5451 scheduleConsumeBatchedInput()5452 void scheduleConsumeBatchedInput() { 5453 if (!mConsumeBatchedInputScheduled) { 5454 mConsumeBatchedInputScheduled = true; 5455 mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, 5456 mConsumedBatchedInputRunnable, null); 5457 } 5458 } 5459 unscheduleConsumeBatchedInput()5460 void unscheduleConsumeBatchedInput() { 5461 if (mConsumeBatchedInputScheduled) { 5462 mConsumeBatchedInputScheduled = false; 5463 mChoreographer.removeCallbacks(Choreographer.CALLBACK_INPUT, 5464 mConsumedBatchedInputRunnable, null); 5465 } 5466 } 5467 doConsumeBatchedInput(long frameTimeNanos)5468 void doConsumeBatchedInput(long frameTimeNanos) { 5469 if (mConsumeBatchedInputScheduled) { 5470 mConsumeBatchedInputScheduled = false; 5471 if (mInputEventReceiver != null) { 5472 mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos); 5473 } 5474 doProcessInputEvents(); 5475 } 5476 } 5477 5478 final class TraversalRunnable implements Runnable { 5479 @Override run()5480 public void run() { 5481 doTraversal(); 5482 } 5483 } 5484 final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); 5485 5486 final class WindowInputEventReceiver extends InputEventReceiver { WindowInputEventReceiver(InputChannel inputChannel, Looper looper)5487 public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { 5488 super(inputChannel, looper); 5489 } 5490 5491 @Override onInputEvent(InputEvent event)5492 public void onInputEvent(InputEvent event) { 5493 enqueueInputEvent(event, this, 0, true); 5494 } 5495 5496 @Override onBatchedInputEventPending()5497 public void onBatchedInputEventPending() { 5498 scheduleConsumeBatchedInput(); 5499 } 5500 5501 @Override dispose()5502 public void dispose() { 5503 unscheduleConsumeBatchedInput(); 5504 super.dispose(); 5505 } 5506 } 5507 WindowInputEventReceiver mInputEventReceiver; 5508 5509 final class ConsumeBatchedInputRunnable implements Runnable { 5510 @Override run()5511 public void run() { 5512 doConsumeBatchedInput(mChoreographer.getFrameTimeNanos()); 5513 } 5514 } 5515 final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable = 5516 new ConsumeBatchedInputRunnable(); 5517 boolean mConsumeBatchedInputScheduled; 5518 5519 final class InvalidateOnAnimationRunnable implements Runnable { 5520 private boolean mPosted; 5521 private ArrayList<View> mViews = new ArrayList<View>(); 5522 private ArrayList<AttachInfo.InvalidateInfo> mViewRects = 5523 new ArrayList<AttachInfo.InvalidateInfo>(); 5524 private View[] mTempViews; 5525 private AttachInfo.InvalidateInfo[] mTempViewRects; 5526 addView(View view)5527 public void addView(View view) { 5528 synchronized (this) { 5529 mViews.add(view); 5530 postIfNeededLocked(); 5531 } 5532 } 5533 addViewRect(AttachInfo.InvalidateInfo info)5534 public void addViewRect(AttachInfo.InvalidateInfo info) { 5535 synchronized (this) { 5536 mViewRects.add(info); 5537 postIfNeededLocked(); 5538 } 5539 } 5540 removeView(View view)5541 public void removeView(View view) { 5542 synchronized (this) { 5543 mViews.remove(view); 5544 5545 for (int i = mViewRects.size(); i-- > 0; ) { 5546 AttachInfo.InvalidateInfo info = mViewRects.get(i); 5547 if (info.target == view) { 5548 mViewRects.remove(i); 5549 info.recycle(); 5550 } 5551 } 5552 5553 if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) { 5554 mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, this, null); 5555 mPosted = false; 5556 } 5557 } 5558 } 5559 5560 @Override run()5561 public void run() { 5562 final int viewCount; 5563 final int viewRectCount; 5564 synchronized (this) { 5565 mPosted = false; 5566 5567 viewCount = mViews.size(); 5568 if (viewCount != 0) { 5569 mTempViews = mViews.toArray(mTempViews != null 5570 ? mTempViews : new View[viewCount]); 5571 mViews.clear(); 5572 } 5573 5574 viewRectCount = mViewRects.size(); 5575 if (viewRectCount != 0) { 5576 mTempViewRects = mViewRects.toArray(mTempViewRects != null 5577 ? mTempViewRects : new AttachInfo.InvalidateInfo[viewRectCount]); 5578 mViewRects.clear(); 5579 } 5580 } 5581 5582 for (int i = 0; i < viewCount; i++) { 5583 mTempViews[i].invalidate(); 5584 mTempViews[i] = null; 5585 } 5586 5587 for (int i = 0; i < viewRectCount; i++) { 5588 final View.AttachInfo.InvalidateInfo info = mTempViewRects[i]; 5589 info.target.invalidate(info.left, info.top, info.right, info.bottom); 5590 info.recycle(); 5591 } 5592 } 5593 postIfNeededLocked()5594 private void postIfNeededLocked() { 5595 if (!mPosted) { 5596 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); 5597 mPosted = true; 5598 } 5599 } 5600 } 5601 final InvalidateOnAnimationRunnable mInvalidateOnAnimationRunnable = 5602 new InvalidateOnAnimationRunnable(); 5603 dispatchInvalidateDelayed(View view, long delayMilliseconds)5604 public void dispatchInvalidateDelayed(View view, long delayMilliseconds) { 5605 Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); 5606 mHandler.sendMessageDelayed(msg, delayMilliseconds); 5607 } 5608 dispatchInvalidateRectDelayed(AttachInfo.InvalidateInfo info, long delayMilliseconds)5609 public void dispatchInvalidateRectDelayed(AttachInfo.InvalidateInfo info, 5610 long delayMilliseconds) { 5611 final Message msg = mHandler.obtainMessage(MSG_INVALIDATE_RECT, info); 5612 mHandler.sendMessageDelayed(msg, delayMilliseconds); 5613 } 5614 dispatchInvalidateOnAnimation(View view)5615 public void dispatchInvalidateOnAnimation(View view) { 5616 mInvalidateOnAnimationRunnable.addView(view); 5617 } 5618 dispatchInvalidateRectOnAnimation(AttachInfo.InvalidateInfo info)5619 public void dispatchInvalidateRectOnAnimation(AttachInfo.InvalidateInfo info) { 5620 mInvalidateOnAnimationRunnable.addViewRect(info); 5621 } 5622 enqueueDisplayList(DisplayList displayList)5623 public void enqueueDisplayList(DisplayList displayList) { 5624 mDisplayLists.add(displayList); 5625 } 5626 cancelInvalidate(View view)5627 public void cancelInvalidate(View view) { 5628 mHandler.removeMessages(MSG_INVALIDATE, view); 5629 // fixme: might leak the AttachInfo.InvalidateInfo objects instead of returning 5630 // them to the pool 5631 mHandler.removeMessages(MSG_INVALIDATE_RECT, view); 5632 mInvalidateOnAnimationRunnable.removeView(view); 5633 } 5634 dispatchKey(KeyEvent event)5635 public void dispatchKey(KeyEvent event) { 5636 Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY, event); 5637 msg.setAsynchronous(true); 5638 mHandler.sendMessage(msg); 5639 } 5640 dispatchKeyFromIme(KeyEvent event)5641 public void dispatchKeyFromIme(KeyEvent event) { 5642 Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_IME, event); 5643 msg.setAsynchronous(true); 5644 mHandler.sendMessage(msg); 5645 } 5646 dispatchUnhandledKey(KeyEvent event)5647 public void dispatchUnhandledKey(KeyEvent event) { 5648 if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { 5649 final KeyCharacterMap kcm = event.getKeyCharacterMap(); 5650 final int keyCode = event.getKeyCode(); 5651 final int metaState = event.getMetaState(); 5652 5653 // Check for fallback actions specified by the key character map. 5654 KeyCharacterMap.FallbackAction fallbackAction = 5655 kcm.getFallbackAction(keyCode, metaState); 5656 if (fallbackAction != null) { 5657 final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK; 5658 KeyEvent fallbackEvent = KeyEvent.obtain( 5659 event.getDownTime(), event.getEventTime(), 5660 event.getAction(), fallbackAction.keyCode, 5661 event.getRepeatCount(), fallbackAction.metaState, 5662 event.getDeviceId(), event.getScanCode(), 5663 flags, event.getSource(), null); 5664 fallbackAction.recycle(); 5665 5666 dispatchKey(fallbackEvent); 5667 } 5668 } 5669 } 5670 dispatchAppVisibility(boolean visible)5671 public void dispatchAppVisibility(boolean visible) { 5672 Message msg = mHandler.obtainMessage(MSG_DISPATCH_APP_VISIBILITY); 5673 msg.arg1 = visible ? 1 : 0; 5674 mHandler.sendMessage(msg); 5675 } 5676 dispatchScreenStateChange(boolean on)5677 public void dispatchScreenStateChange(boolean on) { 5678 Message msg = mHandler.obtainMessage(MSG_DISPATCH_SCREEN_STATE); 5679 msg.arg1 = on ? 1 : 0; 5680 mHandler.sendMessage(msg); 5681 } 5682 dispatchGetNewSurface()5683 public void dispatchGetNewSurface() { 5684 Message msg = mHandler.obtainMessage(MSG_DISPATCH_GET_NEW_SURFACE); 5685 mHandler.sendMessage(msg); 5686 } 5687 windowFocusChanged(boolean hasFocus, boolean inTouchMode)5688 public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { 5689 Message msg = Message.obtain(); 5690 msg.what = MSG_WINDOW_FOCUS_CHANGED; 5691 msg.arg1 = hasFocus ? 1 : 0; 5692 msg.arg2 = inTouchMode ? 1 : 0; 5693 mHandler.sendMessage(msg); 5694 } 5695 dispatchCloseSystemDialogs(String reason)5696 public void dispatchCloseSystemDialogs(String reason) { 5697 Message msg = Message.obtain(); 5698 msg.what = MSG_CLOSE_SYSTEM_DIALOGS; 5699 msg.obj = reason; 5700 mHandler.sendMessage(msg); 5701 } 5702 dispatchDragEvent(DragEvent event)5703 public void dispatchDragEvent(DragEvent event) { 5704 final int what; 5705 if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) { 5706 what = MSG_DISPATCH_DRAG_LOCATION_EVENT; 5707 mHandler.removeMessages(what); 5708 } else { 5709 what = MSG_DISPATCH_DRAG_EVENT; 5710 } 5711 Message msg = mHandler.obtainMessage(what, event); 5712 mHandler.sendMessage(msg); 5713 } 5714 dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, int localValue, int localChanges)5715 public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, 5716 int localValue, int localChanges) { 5717 SystemUiVisibilityInfo args = new SystemUiVisibilityInfo(); 5718 args.seq = seq; 5719 args.globalVisibility = globalVisibility; 5720 args.localValue = localValue; 5721 args.localChanges = localChanges; 5722 mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args)); 5723 } 5724 dispatchDoneAnimating()5725 public void dispatchDoneAnimating() { 5726 mHandler.sendEmptyMessage(MSG_DISPATCH_DONE_ANIMATING); 5727 } 5728 dispatchCheckFocus()5729 public void dispatchCheckFocus() { 5730 if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) { 5731 // This will result in a call to checkFocus() below. 5732 mHandler.sendEmptyMessage(MSG_CHECK_FOCUS); 5733 } 5734 } 5735 5736 /** 5737 * Post a callback to send a 5738 * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. 5739 * This event is send at most once every 5740 * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}. 5741 */ postSendWindowContentChangedCallback(View source)5742 private void postSendWindowContentChangedCallback(View source) { 5743 if (mSendWindowContentChangedAccessibilityEvent == null) { 5744 mSendWindowContentChangedAccessibilityEvent = 5745 new SendWindowContentChangedAccessibilityEvent(); 5746 } 5747 View oldSource = mSendWindowContentChangedAccessibilityEvent.mSource; 5748 if (oldSource == null) { 5749 mSendWindowContentChangedAccessibilityEvent.mSource = source; 5750 mHandler.postDelayed(mSendWindowContentChangedAccessibilityEvent, 5751 ViewConfiguration.getSendRecurringAccessibilityEventsInterval()); 5752 } else { 5753 mSendWindowContentChangedAccessibilityEvent.mSource = 5754 getCommonPredecessor(oldSource, source); 5755 } 5756 } 5757 5758 /** 5759 * Remove a posted callback to send a 5760 * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. 5761 */ removeSendWindowContentChangedCallback()5762 private void removeSendWindowContentChangedCallback() { 5763 if (mSendWindowContentChangedAccessibilityEvent != null) { 5764 mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent); 5765 } 5766 } 5767 showContextMenuForChild(View originalView)5768 public boolean showContextMenuForChild(View originalView) { 5769 return false; 5770 } 5771 startActionModeForChild(View originalView, ActionMode.Callback callback)5772 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) { 5773 return null; 5774 } 5775 createContextMenu(ContextMenu menu)5776 public void createContextMenu(ContextMenu menu) { 5777 } 5778 childDrawableStateChanged(View child)5779 public void childDrawableStateChanged(View child) { 5780 } 5781 requestSendAccessibilityEvent(View child, AccessibilityEvent event)5782 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { 5783 if (mView == null) { 5784 return false; 5785 } 5786 // Intercept accessibility focus events fired by virtual nodes to keep 5787 // track of accessibility focus position in such nodes. 5788 final int eventType = event.getEventType(); 5789 switch (eventType) { 5790 case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { 5791 final long sourceNodeId = event.getSourceNodeId(); 5792 final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId( 5793 sourceNodeId); 5794 View source = mView.findViewByAccessibilityId(accessibilityViewId); 5795 if (source != null) { 5796 AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider(); 5797 if (provider != null) { 5798 AccessibilityNodeInfo node = provider.createAccessibilityNodeInfo( 5799 AccessibilityNodeInfo.getVirtualDescendantId(sourceNodeId)); 5800 setAccessibilityFocus(source, node); 5801 } 5802 } 5803 } break; 5804 case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: { 5805 final long sourceNodeId = event.getSourceNodeId(); 5806 final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId( 5807 sourceNodeId); 5808 View source = mView.findViewByAccessibilityId(accessibilityViewId); 5809 if (source != null) { 5810 AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider(); 5811 if (provider != null) { 5812 setAccessibilityFocus(null, null); 5813 } 5814 } 5815 } break; 5816 } 5817 mAccessibilityManager.sendAccessibilityEvent(event); 5818 return true; 5819 } 5820 5821 @Override childAccessibilityStateChanged(View child)5822 public void childAccessibilityStateChanged(View child) { 5823 postSendWindowContentChangedCallback(child); 5824 } 5825 5826 @Override canResolveLayoutDirection()5827 public boolean canResolveLayoutDirection() { 5828 return true; 5829 } 5830 5831 @Override isLayoutDirectionResolved()5832 public boolean isLayoutDirectionResolved() { 5833 return true; 5834 } 5835 5836 @Override getLayoutDirection()5837 public int getLayoutDirection() { 5838 return View.LAYOUT_DIRECTION_RESOLVED_DEFAULT; 5839 } 5840 5841 @Override canResolveTextDirection()5842 public boolean canResolveTextDirection() { 5843 return true; 5844 } 5845 5846 @Override isTextDirectionResolved()5847 public boolean isTextDirectionResolved() { 5848 return true; 5849 } 5850 5851 @Override getTextDirection()5852 public int getTextDirection() { 5853 return View.TEXT_DIRECTION_RESOLVED_DEFAULT; 5854 } 5855 5856 @Override canResolveTextAlignment()5857 public boolean canResolveTextAlignment() { 5858 return true; 5859 } 5860 5861 @Override isTextAlignmentResolved()5862 public boolean isTextAlignmentResolved() { 5863 return true; 5864 } 5865 5866 @Override getTextAlignment()5867 public int getTextAlignment() { 5868 return View.TEXT_ALIGNMENT_RESOLVED_DEFAULT; 5869 } 5870 getCommonPredecessor(View first, View second)5871 private View getCommonPredecessor(View first, View second) { 5872 if (mAttachInfo != null) { 5873 if (mTempHashSet == null) { 5874 mTempHashSet = new HashSet<View>(); 5875 } 5876 HashSet<View> seen = mTempHashSet; 5877 seen.clear(); 5878 View firstCurrent = first; 5879 while (firstCurrent != null) { 5880 seen.add(firstCurrent); 5881 ViewParent firstCurrentParent = firstCurrent.mParent; 5882 if (firstCurrentParent instanceof View) { 5883 firstCurrent = (View) firstCurrentParent; 5884 } else { 5885 firstCurrent = null; 5886 } 5887 } 5888 View secondCurrent = second; 5889 while (secondCurrent != null) { 5890 if (seen.contains(secondCurrent)) { 5891 seen.clear(); 5892 return secondCurrent; 5893 } 5894 ViewParent secondCurrentParent = secondCurrent.mParent; 5895 if (secondCurrentParent instanceof View) { 5896 secondCurrent = (View) secondCurrentParent; 5897 } else { 5898 secondCurrent = null; 5899 } 5900 } 5901 seen.clear(); 5902 } 5903 return null; 5904 } 5905 checkThread()5906 void checkThread() { 5907 if (mThread != Thread.currentThread()) { 5908 throw new CalledFromWrongThreadException( 5909 "Only the original thread that created a view hierarchy can touch its views."); 5910 } 5911 } 5912 requestDisallowInterceptTouchEvent(boolean disallowIntercept)5913 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 5914 // ViewAncestor never intercepts touch event, so this can be a no-op 5915 } 5916 requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate)5917 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 5918 final boolean scrolled = scrollToRectOrFocus(rectangle, immediate); 5919 if (rectangle != null) { 5920 mTempRect.set(rectangle); 5921 mTempRect.offset(0, -mCurScrollY); 5922 mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop); 5923 try { 5924 mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect, immediate); 5925 } catch (RemoteException re) { 5926 /* ignore */ 5927 } 5928 } 5929 return scrolled; 5930 } 5931 childHasTransientStateChanged(View child, boolean hasTransientState)5932 public void childHasTransientStateChanged(View child, boolean hasTransientState) { 5933 // Do nothing. 5934 } 5935 5936 class TakenSurfaceHolder extends BaseSurfaceHolder { 5937 @Override onAllowLockCanvas()5938 public boolean onAllowLockCanvas() { 5939 return mDrawingAllowed; 5940 } 5941 5942 @Override onRelayoutContainer()5943 public void onRelayoutContainer() { 5944 // Not currently interesting -- from changing between fixed and layout size. 5945 } 5946 setFormat(int format)5947 public void setFormat(int format) { 5948 ((RootViewSurfaceTaker)mView).setSurfaceFormat(format); 5949 } 5950 setType(int type)5951 public void setType(int type) { 5952 ((RootViewSurfaceTaker)mView).setSurfaceType(type); 5953 } 5954 5955 @Override onUpdateSurface()5956 public void onUpdateSurface() { 5957 // We take care of format and type changes on our own. 5958 throw new IllegalStateException("Shouldn't be here"); 5959 } 5960 isCreating()5961 public boolean isCreating() { 5962 return mIsCreating; 5963 } 5964 5965 @Override setFixedSize(int width, int height)5966 public void setFixedSize(int width, int height) { 5967 throw new UnsupportedOperationException( 5968 "Currently only support sizing from layout"); 5969 } 5970 setKeepScreenOn(boolean screenOn)5971 public void setKeepScreenOn(boolean screenOn) { 5972 ((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn); 5973 } 5974 } 5975 5976 static class W extends IWindow.Stub { 5977 private final WeakReference<ViewRootImpl> mViewAncestor; 5978 private final IWindowSession mWindowSession; 5979 W(ViewRootImpl viewAncestor)5980 W(ViewRootImpl viewAncestor) { 5981 mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor); 5982 mWindowSession = viewAncestor.mWindowSession; 5983 } 5984 resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, boolean reportDraw, Configuration newConfig)5985 public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, 5986 Rect visibleInsets, boolean reportDraw, Configuration newConfig) { 5987 final ViewRootImpl viewAncestor = mViewAncestor.get(); 5988 if (viewAncestor != null) { 5989 viewAncestor.dispatchResized(frame, overscanInsets, contentInsets, 5990 visibleInsets, reportDraw, newConfig); 5991 } 5992 } 5993 5994 @Override moved(int newX, int newY)5995 public void moved(int newX, int newY) { 5996 final ViewRootImpl viewAncestor = mViewAncestor.get(); 5997 if (viewAncestor != null) { 5998 viewAncestor.dispatchMoved(newX, newY); 5999 } 6000 } 6001 dispatchAppVisibility(boolean visible)6002 public void dispatchAppVisibility(boolean visible) { 6003 final ViewRootImpl viewAncestor = mViewAncestor.get(); 6004 if (viewAncestor != null) { 6005 viewAncestor.dispatchAppVisibility(visible); 6006 } 6007 } 6008 dispatchScreenState(boolean on)6009 public void dispatchScreenState(boolean on) { 6010 final ViewRootImpl viewAncestor = mViewAncestor.get(); 6011 if (viewAncestor != null) { 6012 viewAncestor.dispatchScreenStateChange(on); 6013 } 6014 } 6015 dispatchGetNewSurface()6016 public void dispatchGetNewSurface() { 6017 final ViewRootImpl viewAncestor = mViewAncestor.get(); 6018 if (viewAncestor != null) { 6019 viewAncestor.dispatchGetNewSurface(); 6020 } 6021 } 6022 windowFocusChanged(boolean hasFocus, boolean inTouchMode)6023 public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { 6024 final ViewRootImpl viewAncestor = mViewAncestor.get(); 6025 if (viewAncestor != null) { 6026 viewAncestor.windowFocusChanged(hasFocus, inTouchMode); 6027 } 6028 } 6029 checkCallingPermission(String permission)6030 private static int checkCallingPermission(String permission) { 6031 try { 6032 return ActivityManagerNative.getDefault().checkPermission( 6033 permission, Binder.getCallingPid(), Binder.getCallingUid()); 6034 } catch (RemoteException e) { 6035 return PackageManager.PERMISSION_DENIED; 6036 } 6037 } 6038 executeCommand(String command, String parameters, ParcelFileDescriptor out)6039 public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { 6040 final ViewRootImpl viewAncestor = mViewAncestor.get(); 6041 if (viewAncestor != null) { 6042 final View view = viewAncestor.mView; 6043 if (view != null) { 6044 if (checkCallingPermission(Manifest.permission.DUMP) != 6045 PackageManager.PERMISSION_GRANTED) { 6046 throw new SecurityException("Insufficient permissions to invoke" 6047 + " executeCommand() from pid=" + Binder.getCallingPid() 6048 + ", uid=" + Binder.getCallingUid()); 6049 } 6050 6051 OutputStream clientStream = null; 6052 try { 6053 clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out); 6054 ViewDebug.dispatchCommand(view, command, parameters, clientStream); 6055 } catch (IOException e) { 6056 e.printStackTrace(); 6057 } finally { 6058 if (clientStream != null) { 6059 try { 6060 clientStream.close(); 6061 } catch (IOException e) { 6062 e.printStackTrace(); 6063 } 6064 } 6065 } 6066 } 6067 } 6068 } 6069 closeSystemDialogs(String reason)6070 public void closeSystemDialogs(String reason) { 6071 final ViewRootImpl viewAncestor = mViewAncestor.get(); 6072 if (viewAncestor != null) { 6073 viewAncestor.dispatchCloseSystemDialogs(reason); 6074 } 6075 } 6076 dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync)6077 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 6078 boolean sync) { 6079 if (sync) { 6080 try { 6081 mWindowSession.wallpaperOffsetsComplete(asBinder()); 6082 } catch (RemoteException e) { 6083 } 6084 } 6085 } 6086 dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras, boolean sync)6087 public void dispatchWallpaperCommand(String action, int x, int y, 6088 int z, Bundle extras, boolean sync) { 6089 if (sync) { 6090 try { 6091 mWindowSession.wallpaperCommandComplete(asBinder(), null); 6092 } catch (RemoteException e) { 6093 } 6094 } 6095 } 6096 6097 /* Drag/drop */ dispatchDragEvent(DragEvent event)6098 public void dispatchDragEvent(DragEvent event) { 6099 final ViewRootImpl viewAncestor = mViewAncestor.get(); 6100 if (viewAncestor != null) { 6101 viewAncestor.dispatchDragEvent(event); 6102 } 6103 } 6104 dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, int localValue, int localChanges)6105 public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, 6106 int localValue, int localChanges) { 6107 final ViewRootImpl viewAncestor = mViewAncestor.get(); 6108 if (viewAncestor != null) { 6109 viewAncestor.dispatchSystemUiVisibilityChanged(seq, globalVisibility, 6110 localValue, localChanges); 6111 } 6112 } 6113 doneAnimating()6114 public void doneAnimating() { 6115 final ViewRootImpl viewAncestor = mViewAncestor.get(); 6116 if (viewAncestor != null) { 6117 viewAncestor.dispatchDoneAnimating(); 6118 } 6119 } 6120 } 6121 6122 public static final class CalledFromWrongThreadException extends AndroidRuntimeException { CalledFromWrongThreadException(String msg)6123 public CalledFromWrongThreadException(String msg) { 6124 super(msg); 6125 } 6126 } 6127 6128 private SurfaceHolder mHolder = new SurfaceHolder() { 6129 // we only need a SurfaceHolder for opengl. it would be nice 6130 // to implement everything else though, especially the callback 6131 // support (opengl doesn't make use of it right now, but eventually 6132 // will). 6133 public Surface getSurface() { 6134 return mSurface; 6135 } 6136 6137 public boolean isCreating() { 6138 return false; 6139 } 6140 6141 public void addCallback(Callback callback) { 6142 } 6143 6144 public void removeCallback(Callback callback) { 6145 } 6146 6147 public void setFixedSize(int width, int height) { 6148 } 6149 6150 public void setSizeFromLayout() { 6151 } 6152 6153 public void setFormat(int format) { 6154 } 6155 6156 public void setType(int type) { 6157 } 6158 6159 public void setKeepScreenOn(boolean screenOn) { 6160 } 6161 6162 public Canvas lockCanvas() { 6163 return null; 6164 } 6165 6166 public Canvas lockCanvas(Rect dirty) { 6167 return null; 6168 } 6169 6170 public void unlockCanvasAndPost(Canvas canvas) { 6171 } 6172 public Rect getSurfaceFrame() { 6173 return null; 6174 } 6175 }; 6176 getRunQueue()6177 static RunQueue getRunQueue() { 6178 RunQueue rq = sRunQueues.get(); 6179 if (rq != null) { 6180 return rq; 6181 } 6182 rq = new RunQueue(); 6183 sRunQueues.set(rq); 6184 return rq; 6185 } 6186 6187 /** 6188 * The run queue is used to enqueue pending work from Views when no Handler is 6189 * attached. The work is executed during the next call to performTraversals on 6190 * the thread. 6191 * @hide 6192 */ 6193 static final class RunQueue { 6194 private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>(); 6195 post(Runnable action)6196 void post(Runnable action) { 6197 postDelayed(action, 0); 6198 } 6199 postDelayed(Runnable action, long delayMillis)6200 void postDelayed(Runnable action, long delayMillis) { 6201 HandlerAction handlerAction = new HandlerAction(); 6202 handlerAction.action = action; 6203 handlerAction.delay = delayMillis; 6204 6205 synchronized (mActions) { 6206 mActions.add(handlerAction); 6207 } 6208 } 6209 removeCallbacks(Runnable action)6210 void removeCallbacks(Runnable action) { 6211 final HandlerAction handlerAction = new HandlerAction(); 6212 handlerAction.action = action; 6213 6214 synchronized (mActions) { 6215 final ArrayList<HandlerAction> actions = mActions; 6216 6217 while (actions.remove(handlerAction)) { 6218 // Keep going 6219 } 6220 } 6221 } 6222 executeActions(Handler handler)6223 void executeActions(Handler handler) { 6224 synchronized (mActions) { 6225 final ArrayList<HandlerAction> actions = mActions; 6226 final int count = actions.size(); 6227 6228 for (int i = 0; i < count; i++) { 6229 final HandlerAction handlerAction = actions.get(i); 6230 handler.postDelayed(handlerAction.action, handlerAction.delay); 6231 } 6232 6233 actions.clear(); 6234 } 6235 } 6236 6237 private static class HandlerAction { 6238 Runnable action; 6239 long delay; 6240 6241 @Override equals(Object o)6242 public boolean equals(Object o) { 6243 if (this == o) return true; 6244 if (o == null || getClass() != o.getClass()) return false; 6245 6246 HandlerAction that = (HandlerAction) o; 6247 return !(action != null ? !action.equals(that.action) : that.action != null); 6248 6249 } 6250 6251 @Override hashCode()6252 public int hashCode() { 6253 int result = action != null ? action.hashCode() : 0; 6254 result = 31 * result + (int) (delay ^ (delay >>> 32)); 6255 return result; 6256 } 6257 } 6258 } 6259 6260 /** 6261 * Class for managing the accessibility interaction connection 6262 * based on the global accessibility state. 6263 */ 6264 final class AccessibilityInteractionConnectionManager 6265 implements AccessibilityStateChangeListener { onAccessibilityStateChanged(boolean enabled)6266 public void onAccessibilityStateChanged(boolean enabled) { 6267 if (enabled) { 6268 ensureConnection(); 6269 if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) { 6270 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 6271 View focusedView = mView.findFocus(); 6272 if (focusedView != null && focusedView != mView) { 6273 focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 6274 } 6275 } 6276 } else { 6277 ensureNoConnection(); 6278 mHandler.obtainMessage(MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST).sendToTarget(); 6279 } 6280 } 6281 ensureConnection()6282 public void ensureConnection() { 6283 if (mAttachInfo != null) { 6284 final boolean registered = 6285 mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED; 6286 if (!registered) { 6287 mAttachInfo.mAccessibilityWindowId = 6288 mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, 6289 new AccessibilityInteractionConnection(ViewRootImpl.this)); 6290 } 6291 } 6292 } 6293 ensureNoConnection()6294 public void ensureNoConnection() { 6295 final boolean registered = 6296 mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED; 6297 if (registered) { 6298 mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED; 6299 mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow); 6300 } 6301 } 6302 } 6303 6304 /** 6305 * This class is an interface this ViewAncestor provides to the 6306 * AccessibilityManagerService to the latter can interact with 6307 * the view hierarchy in this ViewAncestor. 6308 */ 6309 static final class AccessibilityInteractionConnection 6310 extends IAccessibilityInteractionConnection.Stub { 6311 private final WeakReference<ViewRootImpl> mViewRootImpl; 6312 AccessibilityInteractionConnection(ViewRootImpl viewRootImpl)6313 AccessibilityInteractionConnection(ViewRootImpl viewRootImpl) { 6314 mViewRootImpl = new WeakReference<ViewRootImpl>(viewRootImpl); 6315 } 6316 6317 @Override findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)6318 public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, 6319 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, 6320 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 6321 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 6322 if (viewRootImpl != null && viewRootImpl.mView != null) { 6323 viewRootImpl.getAccessibilityInteractionController() 6324 .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId, 6325 interactionId, callback, flags, interrogatingPid, interrogatingTid, 6326 spec); 6327 } else { 6328 // We cannot make the call and notify the caller so it does not wait. 6329 try { 6330 callback.setFindAccessibilityNodeInfosResult(null, interactionId); 6331 } catch (RemoteException re) { 6332 /* best effort - ignore */ 6333 } 6334 } 6335 } 6336 6337 @Override performAccessibilityAction(long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid, long interrogatingTid)6338 public void performAccessibilityAction(long accessibilityNodeId, int action, 6339 Bundle arguments, int interactionId, 6340 IAccessibilityInteractionConnectionCallback callback, int flags, 6341 int interogatingPid, long interrogatingTid) { 6342 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 6343 if (viewRootImpl != null && viewRootImpl.mView != null) { 6344 viewRootImpl.getAccessibilityInteractionController() 6345 .performAccessibilityActionClientThread(accessibilityNodeId, action, arguments, 6346 interactionId, callback, flags, interogatingPid, interrogatingTid); 6347 } else { 6348 // We cannot make the call and notify the caller so it does not wait. 6349 try { 6350 callback.setPerformAccessibilityActionResult(false, interactionId); 6351 } catch (RemoteException re) { 6352 /* best effort - ignore */ 6353 } 6354 } 6355 } 6356 6357 @Override findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)6358 public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, 6359 String viewId, int interactionId, 6360 IAccessibilityInteractionConnectionCallback callback, int flags, 6361 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 6362 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 6363 if (viewRootImpl != null && viewRootImpl.mView != null) { 6364 viewRootImpl.getAccessibilityInteractionController() 6365 .findAccessibilityNodeInfosByViewIdClientThread(accessibilityNodeId, 6366 viewId, interactionId, callback, flags, interrogatingPid, 6367 interrogatingTid, spec); 6368 } else { 6369 // We cannot make the call and notify the caller so it does not wait. 6370 try { 6371 callback.setFindAccessibilityNodeInfoResult(null, interactionId); 6372 } catch (RemoteException re) { 6373 /* best effort - ignore */ 6374 } 6375 } 6376 } 6377 6378 @Override findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)6379 public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, 6380 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, 6381 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 6382 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 6383 if (viewRootImpl != null && viewRootImpl.mView != null) { 6384 viewRootImpl.getAccessibilityInteractionController() 6385 .findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text, 6386 interactionId, callback, flags, interrogatingPid, interrogatingTid, 6387 spec); 6388 } else { 6389 // We cannot make the call and notify the caller so it does not wait. 6390 try { 6391 callback.setFindAccessibilityNodeInfosResult(null, interactionId); 6392 } catch (RemoteException re) { 6393 /* best effort - ignore */ 6394 } 6395 } 6396 } 6397 6398 @Override findFocus(long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)6399 public void findFocus(long accessibilityNodeId, int focusType, int interactionId, 6400 IAccessibilityInteractionConnectionCallback callback, int flags, 6401 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 6402 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 6403 if (viewRootImpl != null && viewRootImpl.mView != null) { 6404 viewRootImpl.getAccessibilityInteractionController() 6405 .findFocusClientThread(accessibilityNodeId, focusType, interactionId, callback, 6406 flags, interrogatingPid, interrogatingTid, spec); 6407 } else { 6408 // We cannot make the call and notify the caller so it does not wait. 6409 try { 6410 callback.setFindAccessibilityNodeInfoResult(null, interactionId); 6411 } catch (RemoteException re) { 6412 /* best effort - ignore */ 6413 } 6414 } 6415 } 6416 6417 @Override focusSearch(long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)6418 public void focusSearch(long accessibilityNodeId, int direction, int interactionId, 6419 IAccessibilityInteractionConnectionCallback callback, int flags, 6420 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 6421 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 6422 if (viewRootImpl != null && viewRootImpl.mView != null) { 6423 viewRootImpl.getAccessibilityInteractionController() 6424 .focusSearchClientThread(accessibilityNodeId, direction, interactionId, 6425 callback, flags, interrogatingPid, interrogatingTid, spec); 6426 } else { 6427 // We cannot make the call and notify the caller so it does not wait. 6428 try { 6429 callback.setFindAccessibilityNodeInfoResult(null, interactionId); 6430 } catch (RemoteException re) { 6431 /* best effort - ignore */ 6432 } 6433 } 6434 } 6435 } 6436 6437 private class SendWindowContentChangedAccessibilityEvent implements Runnable { 6438 public View mSource; 6439 run()6440 public void run() { 6441 if (mSource != null) { 6442 mSource.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 6443 mSource.resetAccessibilityStateChanged(); 6444 mSource = null; 6445 } 6446 } 6447 } 6448 } 6449