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