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