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