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