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