ViewRootImpl.java revision 07213e6d8895af10951851435adf96a779863f6c
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.view; 18 19import android.Manifest; 20import android.animation.LayoutTransition; 21import android.app.ActivityManagerNative; 22import android.content.ClipDescription; 23import android.content.ComponentCallbacks; 24import android.content.ComponentCallbacks2; 25import android.content.Context; 26import android.content.pm.PackageManager; 27import android.content.res.CompatibilityInfo; 28import android.content.res.Configuration; 29import android.content.res.Resources; 30import android.graphics.Canvas; 31import android.graphics.Paint; 32import android.graphics.PixelFormat; 33import android.graphics.Point; 34import android.graphics.PointF; 35import android.graphics.PorterDuff; 36import android.graphics.Rect; 37import android.graphics.Region; 38import android.media.AudioManager; 39import android.os.Binder; 40import android.os.Bundle; 41import android.os.Debug; 42import android.os.Handler; 43import android.os.LatencyTimer; 44import android.os.Looper; 45import android.os.Message; 46import android.os.ParcelFileDescriptor; 47import android.os.Process; 48import android.os.RemoteException; 49import android.os.SystemClock; 50import android.os.SystemProperties; 51import android.util.AndroidRuntimeException; 52import android.util.DisplayMetrics; 53import android.util.EventLog; 54import android.util.Log; 55import android.util.Pool; 56import android.util.Poolable; 57import android.util.PoolableManager; 58import android.util.Pools; 59import android.util.Slog; 60import android.util.SparseArray; 61import android.util.TypedValue; 62import android.view.View.MeasureSpec; 63import android.view.accessibility.AccessibilityEvent; 64import android.view.accessibility.AccessibilityManager; 65import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; 66import android.view.accessibility.AccessibilityNodeInfo; 67import android.view.accessibility.IAccessibilityInteractionConnection; 68import android.view.accessibility.IAccessibilityInteractionConnectionCallback; 69import android.view.animation.AccelerateDecelerateInterpolator; 70import android.view.animation.Interpolator; 71import android.view.inputmethod.InputConnection; 72import android.view.inputmethod.InputMethodManager; 73import android.widget.Scroller; 74 75import com.android.internal.policy.PolicyManager; 76import com.android.internal.util.Predicate; 77import com.android.internal.view.BaseSurfaceHolder; 78import com.android.internal.view.IInputMethodCallback; 79import com.android.internal.view.IInputMethodSession; 80import com.android.internal.view.RootViewSurfaceTaker; 81 82import java.io.IOException; 83import java.io.OutputStream; 84import java.io.PrintWriter; 85import java.lang.ref.WeakReference; 86import java.util.ArrayList; 87import java.util.List; 88 89/** 90 * The top of a view hierarchy, implementing the needed protocol between View 91 * and the WindowManager. This is for the most part an internal implementation 92 * detail of {@link WindowManagerImpl}. 93 * 94 * {@hide} 95 */ 96@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"}) 97public final class ViewRootImpl extends Handler implements ViewParent, 98 View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { 99 private static final String TAG = "ViewRootImpl"; 100 private static final boolean DBG = false; 101 private static final boolean LOCAL_LOGV = false; 102 /** @noinspection PointlessBooleanExpression*/ 103 private static final boolean DEBUG_DRAW = false || LOCAL_LOGV; 104 private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV; 105 private static final boolean DEBUG_DIALOG = false || LOCAL_LOGV; 106 private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV; 107 private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV; 108 private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV; 109 private static final boolean DEBUG_IMF = false || LOCAL_LOGV; 110 private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV; 111 private static final boolean WATCH_POINTER = false; 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 = "viewancestor.profile_rendering"; 118 119 private static final boolean MEASURE_LATENCY = false; 120 private static LatencyTimer lt; 121 122 /** 123 * Maximum time we allow the user to roll the trackball enough to generate 124 * a key event, before resetting the counters. 125 */ 126 static final int MAX_TRACKBALL_DELAY = 250; 127 128 static IWindowSession sWindowSession; 129 130 static final Object mStaticInit = new Object(); 131 static boolean mInitialized = false; 132 133 static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>(); 134 135 static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>(); 136 static boolean sFirstDrawComplete = false; 137 138 static final ArrayList<ComponentCallbacks> sConfigCallbacks 139 = new ArrayList<ComponentCallbacks>(); 140 141 long mLastTrackballTime = 0; 142 final TrackballAxis mTrackballAxisX = new TrackballAxis(); 143 final TrackballAxis mTrackballAxisY = new TrackballAxis(); 144 145 int mLastJoystickXDirection; 146 int mLastJoystickYDirection; 147 int mLastJoystickXKeyCode; 148 int mLastJoystickYKeyCode; 149 150 final int[] mTmpLocation = new int[2]; 151 152 final TypedValue mTmpValue = new TypedValue(); 153 154 final InputMethodCallback mInputMethodCallback; 155 final SparseArray<Object> mPendingEvents = new SparseArray<Object>(); 156 int mPendingEventSeq = 0; 157 158 final Thread mThread; 159 160 final WindowLeaked mLocation; 161 162 final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams(); 163 164 final W mWindow; 165 166 View mView; 167 View mFocusedView; 168 View mRealFocusedView; // this is not set to null in touch mode 169 int mViewVisibility; 170 boolean mAppVisible = true; 171 172 // Set to true if the owner of this window is in the stopped state, 173 // so the window should no longer be active. 174 boolean mStopped = false; 175 176 boolean mLastInCompatMode = false; 177 178 SurfaceHolder.Callback2 mSurfaceHolderCallback; 179 BaseSurfaceHolder mSurfaceHolder; 180 boolean mIsCreating; 181 boolean mDrawingAllowed; 182 183 final Region mTransparentRegion; 184 final Region mPreviousTransparentRegion; 185 186 int mWidth; 187 int mHeight; 188 Rect mDirty; 189 final Rect mCurrentDirty = new Rect(); 190 final Rect mPreviousDirty = new Rect(); 191 boolean mIsAnimating; 192 193 CompatibilityInfo.Translator mTranslator; 194 195 final View.AttachInfo mAttachInfo; 196 InputChannel mInputChannel; 197 InputQueue.Callback mInputQueueCallback; 198 InputQueue mInputQueue; 199 FallbackEventHandler mFallbackEventHandler; 200 201 final Rect mTempRect; // used in the transaction to not thrash the heap. 202 final Rect mVisRect; // used to retrieve visible rect of focused view. 203 204 boolean mTraversalScheduled; 205 long mLastTraversalFinishedTimeNanos; 206 long mLastDrawDurationNanos; 207 boolean mWillDrawSoon; 208 boolean mLayoutRequested; 209 boolean mFirst; 210 boolean mReportNextDraw; 211 boolean mFullRedrawNeeded; 212 boolean mNewSurfaceNeeded; 213 boolean mHasHadWindowFocus; 214 boolean mLastWasImTarget; 215 216 boolean mWindowAttributesChanged = false; 217 218 // These can be accessed by any thread, must be protected with a lock. 219 // Surface can never be reassigned or cleared (use Surface.clear()). 220 private final Surface mSurface = new Surface(); 221 222 boolean mAdded; 223 boolean mAddedTouchMode; 224 225 CompatibilityInfoHolder mCompatibilityInfo; 226 227 /*package*/ int mAddNesting; 228 229 // These are accessed by multiple threads. 230 final Rect mWinFrame; // frame given by window manager. 231 232 final Rect mPendingVisibleInsets = new Rect(); 233 final Rect mPendingContentInsets = new Rect(); 234 final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets 235 = new ViewTreeObserver.InternalInsetsInfo(); 236 237 final Configuration mLastConfiguration = new Configuration(); 238 final Configuration mPendingConfiguration = new Configuration(); 239 240 class ResizedInfo { 241 Rect coveredInsets; 242 Rect visibleInsets; 243 Configuration newConfig; 244 } 245 246 boolean mScrollMayChange; 247 int mSoftInputMode; 248 View mLastScrolledFocus; 249 int mScrollY; 250 int mCurScrollY; 251 Scroller mScroller; 252 HardwareLayer mResizeBuffer; 253 long mResizeBufferStartTime; 254 int mResizeBufferDuration; 255 static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator(); 256 private ArrayList<LayoutTransition> mPendingTransitions; 257 258 final ViewConfiguration mViewConfiguration; 259 260 /* Drag/drop */ 261 ClipDescription mDragDescription; 262 View mCurrentDragView; 263 volatile Object mLocalDragState; 264 final PointF mDragPoint = new PointF(); 265 final PointF mLastTouchPoint = new PointF(); 266 267 private boolean mProfileRendering; 268 private Thread mRenderProfiler; 269 private volatile boolean mRenderProfilingEnabled; 270 271 /** 272 * see {@link #playSoundEffect(int)} 273 */ 274 AudioManager mAudioManager; 275 276 final AccessibilityManager mAccessibilityManager; 277 278 AccessibilityInteractionController mAccessibilityInteractionContrtoller; 279 280 AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager; 281 282 SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent; 283 284 private final int mDensity; 285 286 /** 287 * Consistency verifier for debugging purposes. 288 */ 289 protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier = 290 InputEventConsistencyVerifier.isInstrumentationEnabled() ? 291 new InputEventConsistencyVerifier(this, 0) : null; 292 293 public static IWindowSession getWindowSession(Looper mainLooper) { 294 synchronized (mStaticInit) { 295 if (!mInitialized) { 296 try { 297 InputMethodManager imm = InputMethodManager.getInstance(mainLooper); 298 sWindowSession = Display.getWindowManager().openSession( 299 imm.getClient(), imm.getInputContext()); 300 mInitialized = true; 301 } catch (RemoteException e) { 302 } 303 } 304 return sWindowSession; 305 } 306 } 307 308 public ViewRootImpl(Context context) { 309 super(); 310 311 if (MEASURE_LATENCY) { 312 if (lt == null) { 313 lt = new LatencyTimer(100, 1000); 314 } 315 } 316 317 // Initialize the statics when this class is first instantiated. This is 318 // done here instead of in the static block because Zygote does not 319 // allow the spawning of threads. 320 getWindowSession(context.getMainLooper()); 321 322 mThread = Thread.currentThread(); 323 mLocation = new WindowLeaked(null); 324 mLocation.fillInStackTrace(); 325 mWidth = -1; 326 mHeight = -1; 327 mDirty = new Rect(); 328 mTempRect = new Rect(); 329 mVisRect = new Rect(); 330 mWinFrame = new Rect(); 331 mWindow = new W(this); 332 mInputMethodCallback = new InputMethodCallback(this); 333 mViewVisibility = View.GONE; 334 mTransparentRegion = new Region(); 335 mPreviousTransparentRegion = new Region(); 336 mFirst = true; // true for the first time the view is added 337 mAdded = false; 338 mAccessibilityManager = AccessibilityManager.getInstance(context); 339 mAccessibilityInteractionConnectionManager = 340 new AccessibilityInteractionConnectionManager(); 341 mAccessibilityManager.addAccessibilityStateChangeListener( 342 mAccessibilityInteractionConnectionManager); 343 mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this); 344 mViewConfiguration = ViewConfiguration.get(context); 345 mDensity = context.getResources().getDisplayMetrics().densityDpi; 346 mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context); 347 mProfileRendering = Boolean.parseBoolean( 348 SystemProperties.get(PROPERTY_PROFILE_RENDERING, "false")); 349 } 350 351 public static void addFirstDrawHandler(Runnable callback) { 352 synchronized (sFirstDrawHandlers) { 353 if (!sFirstDrawComplete) { 354 sFirstDrawHandlers.add(callback); 355 } 356 } 357 } 358 359 public static void addConfigCallback(ComponentCallbacks callback) { 360 synchronized (sConfigCallbacks) { 361 sConfigCallbacks.add(callback); 362 } 363 } 364 365 // FIXME for perf testing only 366 private boolean mProfile = false; 367 368 /** 369 * Call this to profile the next traversal call. 370 * FIXME for perf testing only. Remove eventually 371 */ 372 public void profile() { 373 mProfile = true; 374 } 375 376 /** 377 * Indicates whether we are in touch mode. Calling this method triggers an IPC 378 * call and should be avoided whenever possible. 379 * 380 * @return True, if the device is in touch mode, false otherwise. 381 * 382 * @hide 383 */ 384 static boolean isInTouchMode() { 385 if (mInitialized) { 386 try { 387 return sWindowSession.getInTouchMode(); 388 } catch (RemoteException e) { 389 } 390 } 391 return false; 392 } 393 394 /** 395 * We have one child 396 */ 397 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { 398 synchronized (this) { 399 if (mView == null) { 400 mView = view; 401 mFallbackEventHandler.setView(view); 402 mWindowAttributes.copyFrom(attrs); 403 attrs = mWindowAttributes; 404 405 if (view instanceof RootViewSurfaceTaker) { 406 mSurfaceHolderCallback = 407 ((RootViewSurfaceTaker)view).willYouTakeTheSurface(); 408 if (mSurfaceHolderCallback != null) { 409 mSurfaceHolder = new TakenSurfaceHolder(); 410 mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); 411 } 412 } 413 414 // If the application owns the surface, don't enable hardware acceleration 415 if (mSurfaceHolder == null) { 416 enableHardwareAcceleration(attrs); 417 } 418 419 CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get(); 420 mTranslator = compatibilityInfo.getTranslator(); 421 422 if (mTranslator != null) { 423 mSurface.setCompatibilityTranslator(mTranslator); 424 } 425 426 boolean restore = false; 427 if (mTranslator != null) { 428 restore = true; 429 attrs.backup(); 430 mTranslator.translateWindowLayout(attrs); 431 } 432 if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs); 433 434 if (!compatibilityInfo.supportsScreen()) { 435 attrs.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; 436 mLastInCompatMode = true; 437 } 438 439 mSoftInputMode = attrs.softInputMode; 440 mWindowAttributesChanged = true; 441 mAttachInfo.mRootView = view; 442 mAttachInfo.mScalingRequired = mTranslator != null; 443 mAttachInfo.mApplicationScale = 444 mTranslator == null ? 1.0f : mTranslator.applicationScale; 445 if (panelParentView != null) { 446 mAttachInfo.mPanelParentWindowToken 447 = panelParentView.getApplicationWindowToken(); 448 } 449 mAdded = true; 450 int res; /* = WindowManagerImpl.ADD_OKAY; */ 451 452 // Schedule the first layout -before- adding to the window 453 // manager, to make sure we do the relayout before receiving 454 // any other events from the system. 455 requestLayout(); 456 mInputChannel = new InputChannel(); 457 try { 458 res = sWindowSession.add(mWindow, mWindowAttributes, 459 getHostVisibility(), mAttachInfo.mContentInsets, 460 mInputChannel); 461 } catch (RemoteException e) { 462 mAdded = false; 463 mView = null; 464 mAttachInfo.mRootView = null; 465 mInputChannel = null; 466 mFallbackEventHandler.setView(null); 467 unscheduleTraversals(); 468 throw new RuntimeException("Adding window failed", e); 469 } finally { 470 if (restore) { 471 attrs.restore(); 472 } 473 } 474 475 if (mTranslator != null) { 476 mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets); 477 } 478 mPendingContentInsets.set(mAttachInfo.mContentInsets); 479 mPendingVisibleInsets.set(0, 0, 0, 0); 480 if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow); 481 if (res < WindowManagerImpl.ADD_OKAY) { 482 mView = null; 483 mAttachInfo.mRootView = null; 484 mAdded = false; 485 mFallbackEventHandler.setView(null); 486 unscheduleTraversals(); 487 switch (res) { 488 case WindowManagerImpl.ADD_BAD_APP_TOKEN: 489 case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN: 490 throw new WindowManagerImpl.BadTokenException( 491 "Unable to add window -- token " + attrs.token 492 + " is not valid; is your activity running?"); 493 case WindowManagerImpl.ADD_NOT_APP_TOKEN: 494 throw new WindowManagerImpl.BadTokenException( 495 "Unable to add window -- token " + attrs.token 496 + " is not for an application"); 497 case WindowManagerImpl.ADD_APP_EXITING: 498 throw new WindowManagerImpl.BadTokenException( 499 "Unable to add window -- app for token " + attrs.token 500 + " is exiting"); 501 case WindowManagerImpl.ADD_DUPLICATE_ADD: 502 throw new WindowManagerImpl.BadTokenException( 503 "Unable to add window -- window " + mWindow 504 + " has already been added"); 505 case WindowManagerImpl.ADD_STARTING_NOT_NEEDED: 506 // Silently ignore -- we would have just removed it 507 // right away, anyway. 508 return; 509 case WindowManagerImpl.ADD_MULTIPLE_SINGLETON: 510 throw new WindowManagerImpl.BadTokenException( 511 "Unable to add window " + mWindow + 512 " -- another window of this type already exists"); 513 case WindowManagerImpl.ADD_PERMISSION_DENIED: 514 throw new WindowManagerImpl.BadTokenException( 515 "Unable to add window " + mWindow + 516 " -- permission denied for this window type"); 517 } 518 throw new RuntimeException( 519 "Unable to add window -- unknown error code " + res); 520 } 521 522 if (view instanceof RootViewSurfaceTaker) { 523 mInputQueueCallback = 524 ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); 525 } 526 if (mInputQueueCallback != null) { 527 mInputQueue = new InputQueue(mInputChannel); 528 mInputQueueCallback.onInputQueueCreated(mInputQueue); 529 } else { 530 InputQueue.registerInputChannel(mInputChannel, mInputHandler, 531 Looper.myQueue()); 532 } 533 534 view.assignParent(this); 535 mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0; 536 mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0; 537 538 if (mAccessibilityManager.isEnabled()) { 539 mAccessibilityInteractionConnectionManager.ensureConnection(); 540 } 541 } 542 } 543 } 544 545 private void destroyHardwareResources() { 546 if (mAttachInfo.mHardwareRenderer != null) { 547 if (mAttachInfo.mHardwareRenderer.isEnabled()) { 548 mAttachInfo.mHardwareRenderer.destroyLayers(mView); 549 } 550 mAttachInfo.mHardwareRenderer.destroy(false); 551 } 552 } 553 554 void destroyHardwareLayers() { 555 if (mThread != Thread.currentThread()) { 556 if (mAttachInfo.mHardwareRenderer != null && 557 mAttachInfo.mHardwareRenderer.isEnabled()) { 558 HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_MODERATE); 559 } 560 } else { 561 if (mAttachInfo.mHardwareRenderer != null && 562 mAttachInfo.mHardwareRenderer.isEnabled()) { 563 mAttachInfo.mHardwareRenderer.destroyLayers(mView); 564 } 565 } 566 } 567 568 private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { 569 mAttachInfo.mHardwareAccelerated = false; 570 mAttachInfo.mHardwareAccelerationRequested = false; 571 572 // Try to enable hardware acceleration if requested 573 final boolean hardwareAccelerated = 574 (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; 575 576 if (hardwareAccelerated) { 577 if (!HardwareRenderer.isAvailable()) { 578 return; 579 } 580 581 // Only enable hardware acceleration if we are not in the system process 582 // The window manager creates ViewAncestors to display animated preview windows 583 // of launching apps and we don't want those to be hardware accelerated 584 585 final boolean systemHwAccelerated = 586 (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED_SYSTEM) != 0; 587 588 if (!HardwareRenderer.sRendererDisabled || systemHwAccelerated) { 589 // Don't enable hardware acceleration when we're not on the main thread 590 if (!systemHwAccelerated && Looper.getMainLooper() != Looper.myLooper()) { 591 Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware " 592 + "acceleration outside of the main thread, aborting"); 593 return; 594 } 595 596 final boolean translucent = attrs.format != PixelFormat.OPAQUE; 597 if (mAttachInfo.mHardwareRenderer != null) { 598 mAttachInfo.mHardwareRenderer.destroy(true); 599 } 600 mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent); 601 mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested 602 = mAttachInfo.mHardwareRenderer != null; 603 } else { 604 // We would normally have enabled hardware acceleration, but 605 // haven't because we are in the system process. We still want 606 // what is drawn on the screen to behave as if it is accelerated, 607 // so that our preview starting windows visually match what will 608 // actually be drawn by the app. 609 mAttachInfo.mHardwareAccelerationRequested = true; 610 } 611 } 612 } 613 614 public View getView() { 615 return mView; 616 } 617 618 final WindowLeaked getLocation() { 619 return mLocation; 620 } 621 622 void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) { 623 synchronized (this) { 624 int oldSoftInputMode = mWindowAttributes.softInputMode; 625 // preserve compatible window flag if exists. 626 int compatibleWindowFlag = 627 mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; 628 mWindowAttributes.copyFrom(attrs); 629 mWindowAttributes.flags |= compatibleWindowFlag; 630 631 if (newView) { 632 mSoftInputMode = attrs.softInputMode; 633 requestLayout(); 634 } 635 // Don't lose the mode we last auto-computed. 636 if ((attrs.softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) 637 == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { 638 mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode 639 & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) 640 | (oldSoftInputMode 641 & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST); 642 } 643 mWindowAttributesChanged = true; 644 scheduleTraversals(); 645 } 646 } 647 648 void handleAppVisibility(boolean visible) { 649 if (mAppVisible != visible) { 650 mAppVisible = visible; 651 scheduleTraversals(); 652 } 653 } 654 655 void handleGetNewSurface() { 656 mNewSurfaceNeeded = true; 657 mFullRedrawNeeded = true; 658 scheduleTraversals(); 659 } 660 661 /** 662 * {@inheritDoc} 663 */ 664 public void requestLayout() { 665 checkThread(); 666 mLayoutRequested = true; 667 scheduleTraversals(); 668 } 669 670 /** 671 * {@inheritDoc} 672 */ 673 public boolean isLayoutRequested() { 674 return mLayoutRequested; 675 } 676 677 public void invalidateChild(View child, Rect dirty) { 678 checkThread(); 679 if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty); 680 if (dirty == null) { 681 // Fast invalidation for GL-enabled applications; GL must redraw everything 682 invalidate(); 683 return; 684 } 685 if (mCurScrollY != 0 || mTranslator != null) { 686 mTempRect.set(dirty); 687 dirty = mTempRect; 688 if (mCurScrollY != 0) { 689 dirty.offset(0, -mCurScrollY); 690 } 691 if (mTranslator != null) { 692 mTranslator.translateRectInAppWindowToScreen(dirty); 693 } 694 if (mAttachInfo.mScalingRequired) { 695 dirty.inset(-1, -1); 696 } 697 } 698 if (!mDirty.isEmpty() && !mDirty.contains(dirty)) { 699 mAttachInfo.mSetIgnoreDirtyState = true; 700 mAttachInfo.mIgnoreDirtyState = true; 701 } 702 mDirty.union(dirty); 703 if (!mWillDrawSoon) { 704 scheduleTraversals(); 705 } 706 } 707 708 void invalidate() { 709 mDirty.set(0, 0, mWidth, mHeight); 710 scheduleTraversals(); 711 } 712 713 void setStopped(boolean stopped) { 714 if (mStopped != stopped) { 715 mStopped = stopped; 716 if (!stopped) { 717 scheduleTraversals(); 718 } 719 } 720 } 721 722 public ViewParent getParent() { 723 return null; 724 } 725 726 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { 727 invalidateChild(null, dirty); 728 return null; 729 } 730 731 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { 732 if (child != mView) { 733 throw new RuntimeException("child is not mine, honest!"); 734 } 735 // Note: don't apply scroll offset, because we want to know its 736 // visibility in the virtual canvas being given to the view hierarchy. 737 return r.intersect(0, 0, mWidth, mHeight); 738 } 739 740 public void bringChildToFront(View child) { 741 } 742 743 public void scheduleTraversals() { 744 if (!mTraversalScheduled) { 745 mTraversalScheduled = true; 746 747 //noinspection ConstantConditions 748 if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) { 749 final long now = System.nanoTime(); 750 Log.d(TAG, "Latency: Scheduled traversal, it has been " 751 + ((now - mLastTraversalFinishedTimeNanos) * 0.000001f) 752 + "ms since the last traversal finished."); 753 } 754 755 sendEmptyMessage(DO_TRAVERSAL); 756 } 757 } 758 759 public void unscheduleTraversals() { 760 if (mTraversalScheduled) { 761 mTraversalScheduled = false; 762 removeMessages(DO_TRAVERSAL); 763 } 764 } 765 766 int getHostVisibility() { 767 return mAppVisible ? mView.getVisibility() : View.GONE; 768 } 769 770 void disposeResizeBuffer() { 771 if (mResizeBuffer != null) { 772 mResizeBuffer.destroy(); 773 mResizeBuffer = null; 774 } 775 } 776 777 /** 778 * Add LayoutTransition to the list of transitions to be started in the next traversal. 779 * This list will be cleared after the transitions on the list are start()'ed. These 780 * transitionsa re added by LayoutTransition itself when it sets up animations. The setup 781 * happens during the layout phase of traversal, which we want to complete before any of the 782 * animations are started (because those animations may side-effect properties that layout 783 * depends upon, like the bounding rectangles of the affected views). So we add the transition 784 * to the list and it is started just prior to starting the drawing phase of traversal. 785 * 786 * @param transition The LayoutTransition to be started on the next traversal. 787 * 788 * @hide 789 */ 790 public void requestTransitionStart(LayoutTransition transition) { 791 if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) { 792 if (mPendingTransitions == null) { 793 mPendingTransitions = new ArrayList<LayoutTransition>(); 794 } 795 mPendingTransitions.add(transition); 796 } 797 } 798 799 private void performTraversals() { 800 // cache mView since it is used so much below... 801 final View host = mView; 802 803 if (DBG) { 804 System.out.println("======================================"); 805 System.out.println("performTraversals"); 806 host.debug(); 807 } 808 809 if (host == null || !mAdded) 810 return; 811 812 mTraversalScheduled = false; 813 mWillDrawSoon = true; 814 boolean windowSizeMayChange = false; 815 boolean fullRedrawNeeded = mFullRedrawNeeded; 816 boolean newSurface = false; 817 boolean surfaceChanged = false; 818 WindowManager.LayoutParams lp = mWindowAttributes; 819 820 int desiredWindowWidth; 821 int desiredWindowHeight; 822 int childWidthMeasureSpec; 823 int childHeightMeasureSpec; 824 825 final View.AttachInfo attachInfo = mAttachInfo; 826 827 final int viewVisibility = getHostVisibility(); 828 boolean viewVisibilityChanged = mViewVisibility != viewVisibility 829 || mNewSurfaceNeeded; 830 831 WindowManager.LayoutParams params = null; 832 if (mWindowAttributesChanged) { 833 mWindowAttributesChanged = false; 834 surfaceChanged = true; 835 params = lp; 836 } 837 CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get(); 838 if (compatibilityInfo.supportsScreen() == mLastInCompatMode) { 839 params = lp; 840 fullRedrawNeeded = true; 841 mLayoutRequested = true; 842 if (mLastInCompatMode) { 843 params.flags &= ~WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; 844 mLastInCompatMode = false; 845 } else { 846 params.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; 847 mLastInCompatMode = true; 848 } 849 } 850 Rect frame = mWinFrame; 851 if (mFirst) { 852 fullRedrawNeeded = true; 853 mLayoutRequested = true; 854 855 if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) { 856 // NOTE -- system code, won't try to do compat mode. 857 Display disp = WindowManagerImpl.getDefault().getDefaultDisplay(); 858 Point size = new Point(); 859 disp.getRealSize(size); 860 desiredWindowWidth = size.x; 861 desiredWindowHeight = size.y; 862 } else { 863 DisplayMetrics packageMetrics = 864 mView.getContext().getResources().getDisplayMetrics(); 865 desiredWindowWidth = packageMetrics.widthPixels; 866 desiredWindowHeight = packageMetrics.heightPixels; 867 } 868 869 // For the very first time, tell the view hierarchy that it 870 // is attached to the window. Note that at this point the surface 871 // object is not initialized to its backing store, but soon it 872 // will be (assuming the window is visible). 873 attachInfo.mSurface = mSurface; 874 // We used to use the following condition to choose 32 bits drawing caches: 875 // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888 876 // However, windows are now always 32 bits by default, so choose 32 bits 877 attachInfo.mUse32BitDrawingCache = true; 878 attachInfo.mHasWindowFocus = false; 879 attachInfo.mWindowVisibility = viewVisibility; 880 attachInfo.mRecomputeGlobalAttributes = false; 881 attachInfo.mKeepScreenOn = false; 882 attachInfo.mSystemUiVisibility = 0; 883 viewVisibilityChanged = false; 884 mLastConfiguration.setTo(host.getResources().getConfiguration()); 885 host.dispatchAttachedToWindow(attachInfo, 0); 886 //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn); 887 888 } else { 889 desiredWindowWidth = frame.width(); 890 desiredWindowHeight = frame.height(); 891 if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) { 892 if (DEBUG_ORIENTATION) Log.v(TAG, 893 "View " + host + " resized to: " + frame); 894 fullRedrawNeeded = true; 895 mLayoutRequested = true; 896 windowSizeMayChange = true; 897 } 898 } 899 900 if (viewVisibilityChanged) { 901 attachInfo.mWindowVisibility = viewVisibility; 902 host.dispatchWindowVisibilityChanged(viewVisibility); 903 if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { 904 destroyHardwareResources(); 905 } 906 if (viewVisibility == View.GONE) { 907 // After making a window gone, we will count it as being 908 // shown for the first time the next time it gets focus. 909 mHasHadWindowFocus = false; 910 } 911 } 912 913 boolean insetsChanged = false; 914 915 if (mLayoutRequested && !mStopped) { 916 // Execute enqueued actions on every layout in case a view that was detached 917 // enqueued an action after being detached 918 getRunQueue().executeActions(attachInfo.mHandler); 919 920 final Resources res = mView.getContext().getResources(); 921 922 if (mFirst) { 923 host.fitSystemWindows(mAttachInfo.mContentInsets); 924 // make sure touch mode code executes by setting cached value 925 // to opposite of the added touch mode. 926 mAttachInfo.mInTouchMode = !mAddedTouchMode; 927 ensureTouchModeLocally(mAddedTouchMode); 928 } else { 929 if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) { 930 if (mWidth > 0 && mHeight > 0 && 931 mSurface != null && mSurface.isValid() && 932 !mAttachInfo.mTurnOffWindowResizeAnim && 933 mAttachInfo.mHardwareRenderer != null && 934 mAttachInfo.mHardwareRenderer.isEnabled() && 935 mAttachInfo.mHardwareRenderer.validate() && 936 lp != null && !PixelFormat.formatHasAlpha(lp.format)) { 937 938 disposeResizeBuffer(); 939 940 boolean completed = false; 941 HardwareCanvas canvas = null; 942 try { 943 if (mResizeBuffer == null) { 944 mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer( 945 mWidth, mHeight, false); 946 } else if (mResizeBuffer.getWidth() != mWidth || 947 mResizeBuffer.getHeight() != mHeight) { 948 mResizeBuffer.resize(mWidth, mHeight); 949 } 950 canvas = mResizeBuffer.start(mAttachInfo.mHardwareCanvas); 951 canvas.setViewport(mWidth, mHeight); 952 canvas.onPreDraw(null); 953 final int restoreCount = canvas.save(); 954 955 canvas.drawColor(0xff000000, PorterDuff.Mode.SRC); 956 957 int yoff; 958 final boolean scrolling = mScroller != null 959 && mScroller.computeScrollOffset(); 960 if (scrolling) { 961 yoff = mScroller.getCurrY(); 962 mScroller.abortAnimation(); 963 } else { 964 yoff = mScrollY; 965 } 966 967 canvas.translate(0, -yoff); 968 if (mTranslator != null) { 969 mTranslator.translateCanvas(canvas); 970 } 971 972 mView.draw(canvas); 973 974 mResizeBufferStartTime = SystemClock.uptimeMillis(); 975 mResizeBufferDuration = mView.getResources().getInteger( 976 com.android.internal.R.integer.config_mediumAnimTime); 977 completed = true; 978 979 canvas.restoreToCount(restoreCount); 980 } catch (OutOfMemoryError e) { 981 Log.w(TAG, "Not enough memory for content change anim buffer", e); 982 } finally { 983 if (canvas != null) { 984 canvas.onPostDraw(); 985 } 986 if (mResizeBuffer != null) { 987 mResizeBuffer.end(mAttachInfo.mHardwareCanvas); 988 if (!completed) { 989 mResizeBuffer.destroy(); 990 mResizeBuffer = null; 991 } 992 } 993 } 994 } 995 mAttachInfo.mContentInsets.set(mPendingContentInsets); 996 host.fitSystemWindows(mAttachInfo.mContentInsets); 997 insetsChanged = true; 998 if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: " 999 + mAttachInfo.mContentInsets); 1000 } 1001 if (!mAttachInfo.mVisibleInsets.equals(mPendingVisibleInsets)) { 1002 mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets); 1003 if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: " 1004 + mAttachInfo.mVisibleInsets); 1005 } 1006 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT 1007 || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { 1008 windowSizeMayChange = true; 1009 1010 if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) { 1011 // NOTE -- system code, won't try to do compat mode. 1012 Display disp = WindowManagerImpl.getDefault().getDefaultDisplay(); 1013 Point size = new Point(); 1014 disp.getRealSize(size); 1015 desiredWindowWidth = size.x; 1016 desiredWindowHeight = size.y; 1017 } else { 1018 DisplayMetrics packageMetrics = res.getDisplayMetrics(); 1019 desiredWindowWidth = packageMetrics.widthPixels; 1020 desiredWindowHeight = packageMetrics.heightPixels; 1021 } 1022 } 1023 } 1024 1025 // Ask host how big it wants to be 1026 if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG, 1027 "Measuring " + host + " in display " + desiredWindowWidth 1028 + "x" + desiredWindowHeight + "..."); 1029 1030 boolean goodMeasure = false; 1031 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) { 1032 // On large screens, we don't want to allow dialogs to just 1033 // stretch to fill the entire width of the screen to display 1034 // one line of text. First try doing the layout at a smaller 1035 // size to see if it will fit. 1036 final DisplayMetrics packageMetrics = res.getDisplayMetrics(); 1037 res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true); 1038 int baseSize = 0; 1039 if (mTmpValue.type == TypedValue.TYPE_DIMENSION) { 1040 baseSize = (int)mTmpValue.getDimension(packageMetrics); 1041 } 1042 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize); 1043 if (baseSize != 0 && desiredWindowWidth > baseSize) { 1044 childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); 1045 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); 1046 host.measure(childWidthMeasureSpec, childHeightMeasureSpec); 1047 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" 1048 + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); 1049 if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { 1050 goodMeasure = true; 1051 } else { 1052 // Didn't fit in that size... try expanding a bit. 1053 baseSize = (baseSize+desiredWindowWidth)/2; 1054 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize=" 1055 + baseSize); 1056 childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); 1057 host.measure(childWidthMeasureSpec, childHeightMeasureSpec); 1058 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" 1059 + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); 1060 if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { 1061 if (DEBUG_DIALOG) Log.v(TAG, "Good!"); 1062 goodMeasure = true; 1063 } 1064 } 1065 } 1066 } 1067 1068 if (!goodMeasure) { 1069 childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); 1070 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); 1071 host.measure(childWidthMeasureSpec, childHeightMeasureSpec); 1072 if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) { 1073 windowSizeMayChange = true; 1074 } 1075 } 1076 1077 if (DBG) { 1078 System.out.println("======================================"); 1079 System.out.println("performTraversals -- after measure"); 1080 host.debug(); 1081 } 1082 } 1083 1084 if (attachInfo.mRecomputeGlobalAttributes && host.mAttachInfo != null) { 1085 //Log.i(TAG, "Computing view hierarchy attributes!"); 1086 attachInfo.mRecomputeGlobalAttributes = false; 1087 boolean oldScreenOn = attachInfo.mKeepScreenOn; 1088 int oldVis = attachInfo.mSystemUiVisibility; 1089 attachInfo.mKeepScreenOn = false; 1090 attachInfo.mSystemUiVisibility = 0; 1091 attachInfo.mHasSystemUiListeners = false; 1092 host.dispatchCollectViewAttributes(0); 1093 if (attachInfo.mKeepScreenOn != oldScreenOn 1094 || attachInfo.mSystemUiVisibility != oldVis 1095 || attachInfo.mHasSystemUiListeners) { 1096 params = lp; 1097 } 1098 } 1099 1100 if (mFirst || attachInfo.mViewVisibilityChanged) { 1101 attachInfo.mViewVisibilityChanged = false; 1102 int resizeMode = mSoftInputMode & 1103 WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; 1104 // If we are in auto resize mode, then we need to determine 1105 // what mode to use now. 1106 if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { 1107 final int N = attachInfo.mScrollContainers.size(); 1108 for (int i=0; i<N; i++) { 1109 if (attachInfo.mScrollContainers.get(i).isShown()) { 1110 resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 1111 } 1112 } 1113 if (resizeMode == 0) { 1114 resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; 1115 } 1116 if ((lp.softInputMode & 1117 WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) { 1118 lp.softInputMode = (lp.softInputMode & 1119 ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) | 1120 resizeMode; 1121 params = lp; 1122 } 1123 } 1124 } 1125 1126 if (params != null && (host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) { 1127 if (!PixelFormat.formatHasAlpha(params.format)) { 1128 params.format = PixelFormat.TRANSLUCENT; 1129 } 1130 } 1131 1132 boolean windowShouldResize = mLayoutRequested && windowSizeMayChange 1133 && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) 1134 || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT && 1135 frame.width() < desiredWindowWidth && frame.width() != mWidth) 1136 || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT && 1137 frame.height() < desiredWindowHeight && frame.height() != mHeight)); 1138 1139 final boolean computesInternalInsets = 1140 attachInfo.mTreeObserver.hasComputeInternalInsetsListeners(); 1141 1142 boolean insetsPending = false; 1143 int relayoutResult = 0; 1144 1145 if (mFirst || windowShouldResize || insetsChanged || 1146 viewVisibilityChanged || params != null) { 1147 1148 if (viewVisibility == View.VISIBLE) { 1149 // If this window is giving internal insets to the window 1150 // manager, and it is being added or changing its visibility, 1151 // then we want to first give the window manager "fake" 1152 // insets to cause it to effectively ignore the content of 1153 // the window during layout. This avoids it briefly causing 1154 // other windows to resize/move based on the raw frame of the 1155 // window, waiting until we can finish laying out this window 1156 // and get back to the window manager with the ultimately 1157 // computed insets. 1158 insetsPending = computesInternalInsets && (mFirst || viewVisibilityChanged); 1159 } 1160 1161 if (mSurfaceHolder != null) { 1162 mSurfaceHolder.mSurfaceLock.lock(); 1163 mDrawingAllowed = true; 1164 } 1165 1166 boolean hwInitialized = false; 1167 boolean contentInsetsChanged = false; 1168 boolean visibleInsetsChanged; 1169 boolean hadSurface = mSurface.isValid(); 1170 1171 try { 1172 int fl = 0; 1173 if (params != null) { 1174 fl = params.flags; 1175 if (attachInfo.mKeepScreenOn) { 1176 params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; 1177 } 1178 params.subtreeSystemUiVisibility = attachInfo.mSystemUiVisibility; 1179 params.hasSystemUiListeners = attachInfo.mHasSystemUiListeners 1180 || params.subtreeSystemUiVisibility != 0 1181 || params.systemUiVisibility != 0; 1182 } 1183 if (DEBUG_LAYOUT) { 1184 Log.i(TAG, "host=w:" + host.getMeasuredWidth() + ", h:" + 1185 host.getMeasuredHeight() + ", params=" + params); 1186 } 1187 1188 final int surfaceGenerationId = mSurface.getGenerationId(); 1189 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); 1190 1191 if (params != null) { 1192 params.flags = fl; 1193 } 1194 1195 if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString() 1196 + " content=" + mPendingContentInsets.toShortString() 1197 + " visible=" + mPendingVisibleInsets.toShortString() 1198 + " surface=" + mSurface); 1199 1200 if (mPendingConfiguration.seq != 0) { 1201 if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: " 1202 + mPendingConfiguration); 1203 updateConfiguration(mPendingConfiguration, !mFirst); 1204 mPendingConfiguration.seq = 0; 1205 } 1206 1207 contentInsetsChanged = !mPendingContentInsets.equals( 1208 mAttachInfo.mContentInsets); 1209 visibleInsetsChanged = !mPendingVisibleInsets.equals( 1210 mAttachInfo.mVisibleInsets); 1211 if (contentInsetsChanged) { 1212 mAttachInfo.mContentInsets.set(mPendingContentInsets); 1213 host.fitSystemWindows(mAttachInfo.mContentInsets); 1214 if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: " 1215 + mAttachInfo.mContentInsets); 1216 } 1217 if (visibleInsetsChanged) { 1218 mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets); 1219 if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: " 1220 + mAttachInfo.mVisibleInsets); 1221 } 1222 1223 if (!hadSurface) { 1224 if (mSurface.isValid()) { 1225 // If we are creating a new surface, then we need to 1226 // completely redraw it. Also, when we get to the 1227 // point of drawing it we will hold off and schedule 1228 // a new traversal instead. This is so we can tell the 1229 // window manager about all of the windows being displayed 1230 // before actually drawing them, so it can display then 1231 // all at once. 1232 newSurface = true; 1233 fullRedrawNeeded = true; 1234 mPreviousTransparentRegion.setEmpty(); 1235 1236 if (mAttachInfo.mHardwareRenderer != null) { 1237 try { 1238 hwInitialized = mAttachInfo.mHardwareRenderer.initialize(mHolder); 1239 } catch (Surface.OutOfResourcesException e) { 1240 Log.e(TAG, "OutOfResourcesException initializing HW surface", e); 1241 try { 1242 if (!sWindowSession.outOfMemory(mWindow)) { 1243 Slog.w(TAG, "No processes killed for memory; killing self"); 1244 Process.killProcess(Process.myPid()); 1245 } 1246 } catch (RemoteException ex) { 1247 } 1248 mLayoutRequested = true; // ask wm for a new surface next time. 1249 return; 1250 } 1251 } 1252 } 1253 } else if (!mSurface.isValid()) { 1254 // If the surface has been removed, then reset the scroll 1255 // positions. 1256 mLastScrolledFocus = null; 1257 mScrollY = mCurScrollY = 0; 1258 if (mScroller != null) { 1259 mScroller.abortAnimation(); 1260 } 1261 disposeResizeBuffer(); 1262 // Our surface is gone 1263 if (mAttachInfo.mHardwareRenderer != null && 1264 mAttachInfo.mHardwareRenderer.isEnabled()) { 1265 mAttachInfo.mHardwareRenderer.destroy(true); 1266 } 1267 } else if (surfaceGenerationId != mSurface.getGenerationId() && 1268 mSurfaceHolder == null && mAttachInfo.mHardwareRenderer != null) { 1269 fullRedrawNeeded = true; 1270 try { 1271 mAttachInfo.mHardwareRenderer.updateSurface(mHolder); 1272 } catch (Surface.OutOfResourcesException e) { 1273 Log.e(TAG, "OutOfResourcesException updating HW surface", e); 1274 try { 1275 if (!sWindowSession.outOfMemory(mWindow)) { 1276 Slog.w(TAG, "No processes killed for memory; killing self"); 1277 Process.killProcess(Process.myPid()); 1278 } 1279 } catch (RemoteException ex) { 1280 } 1281 mLayoutRequested = true; // ask wm for a new surface next time. 1282 return; 1283 } 1284 } 1285 } catch (RemoteException e) { 1286 } 1287 1288 if (DEBUG_ORIENTATION) Log.v( 1289 TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface); 1290 1291 attachInfo.mWindowLeft = frame.left; 1292 attachInfo.mWindowTop = frame.top; 1293 1294 // !!FIXME!! This next section handles the case where we did not get the 1295 // window size we asked for. We should avoid this by getting a maximum size from 1296 // the window session beforehand. 1297 mWidth = frame.width(); 1298 mHeight = frame.height(); 1299 1300 if (mSurfaceHolder != null) { 1301 // The app owns the surface; tell it about what is going on. 1302 if (mSurface.isValid()) { 1303 // XXX .copyFrom() doesn't work! 1304 //mSurfaceHolder.mSurface.copyFrom(mSurface); 1305 mSurfaceHolder.mSurface = mSurface; 1306 } 1307 mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight); 1308 mSurfaceHolder.mSurfaceLock.unlock(); 1309 if (mSurface.isValid()) { 1310 if (!hadSurface) { 1311 mSurfaceHolder.ungetCallbacks(); 1312 1313 mIsCreating = true; 1314 mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder); 1315 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1316 if (callbacks != null) { 1317 for (SurfaceHolder.Callback c : callbacks) { 1318 c.surfaceCreated(mSurfaceHolder); 1319 } 1320 } 1321 surfaceChanged = true; 1322 } 1323 if (surfaceChanged) { 1324 mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder, 1325 lp.format, mWidth, mHeight); 1326 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1327 if (callbacks != null) { 1328 for (SurfaceHolder.Callback c : callbacks) { 1329 c.surfaceChanged(mSurfaceHolder, lp.format, 1330 mWidth, mHeight); 1331 } 1332 } 1333 } 1334 mIsCreating = false; 1335 } else if (hadSurface) { 1336 mSurfaceHolder.ungetCallbacks(); 1337 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1338 mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder); 1339 if (callbacks != null) { 1340 for (SurfaceHolder.Callback c : callbacks) { 1341 c.surfaceDestroyed(mSurfaceHolder); 1342 } 1343 } 1344 mSurfaceHolder.mSurfaceLock.lock(); 1345 try { 1346 mSurfaceHolder.mSurface = new Surface(); 1347 } finally { 1348 mSurfaceHolder.mSurfaceLock.unlock(); 1349 } 1350 } 1351 } 1352 1353 if (hwInitialized || ((windowShouldResize || params != null) && 1354 mAttachInfo.mHardwareRenderer != null && 1355 mAttachInfo.mHardwareRenderer.isEnabled())) { 1356 mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight); 1357 if (!hwInitialized) { 1358 mAttachInfo.mHardwareRenderer.invalidate(mHolder); 1359 } 1360 } 1361 1362 if (!mStopped) { 1363 boolean focusChangedDueToTouchMode = ensureTouchModeLocally( 1364 (relayoutResult&WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE) != 0); 1365 if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() 1366 || mHeight != host.getMeasuredHeight() || contentInsetsChanged) { 1367 childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); 1368 childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); 1369 1370 if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed! mWidth=" 1371 + mWidth + " measuredWidth=" + host.getMeasuredWidth() 1372 + " mHeight=" + mHeight 1373 + " measuredHeight=" + host.getMeasuredHeight() 1374 + " coveredInsetsChanged=" + contentInsetsChanged); 1375 1376 // Ask host how big it wants to be 1377 host.measure(childWidthMeasureSpec, childHeightMeasureSpec); 1378 1379 // Implementation of weights from WindowManager.LayoutParams 1380 // We just grow the dimensions as needed and re-measure if 1381 // needs be 1382 int width = host.getMeasuredWidth(); 1383 int height = host.getMeasuredHeight(); 1384 boolean measureAgain = false; 1385 1386 if (lp.horizontalWeight > 0.0f) { 1387 width += (int) ((mWidth - width) * lp.horizontalWeight); 1388 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, 1389 MeasureSpec.EXACTLY); 1390 measureAgain = true; 1391 } 1392 if (lp.verticalWeight > 0.0f) { 1393 height += (int) ((mHeight - height) * lp.verticalWeight); 1394 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 1395 MeasureSpec.EXACTLY); 1396 measureAgain = true; 1397 } 1398 1399 if (measureAgain) { 1400 if (DEBUG_LAYOUT) Log.v(TAG, 1401 "And hey let's measure once more: width=" + width 1402 + " height=" + height); 1403 host.measure(childWidthMeasureSpec, childHeightMeasureSpec); 1404 } 1405 1406 mLayoutRequested = true; 1407 } 1408 } 1409 } 1410 1411 final boolean didLayout = mLayoutRequested && !mStopped; 1412 boolean triggerGlobalLayoutListener = didLayout 1413 || attachInfo.mRecomputeGlobalAttributes; 1414 if (didLayout) { 1415 mLayoutRequested = false; 1416 mScrollMayChange = true; 1417 if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v( 1418 TAG, "Laying out " + host + " to (" + 1419 host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")"); 1420 long startTime = 0L; 1421 if (ViewDebug.DEBUG_PROFILE_LAYOUT) { 1422 startTime = SystemClock.elapsedRealtime(); 1423 } 1424 host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); 1425 1426 if (false && ViewDebug.consistencyCheckEnabled) { 1427 if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) { 1428 throw new IllegalStateException("The view hierarchy is an inconsistent state," 1429 + "please refer to the logs with the tag " 1430 + ViewDebug.CONSISTENCY_LOG_TAG + " for more infomation."); 1431 } 1432 } 1433 1434 if (ViewDebug.DEBUG_PROFILE_LAYOUT) { 1435 EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime); 1436 } 1437 1438 // By this point all views have been sized and positionned 1439 // We can compute the transparent area 1440 1441 if ((host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) { 1442 // start out transparent 1443 // TODO: AVOID THAT CALL BY CACHING THE RESULT? 1444 host.getLocationInWindow(mTmpLocation); 1445 mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1], 1446 mTmpLocation[0] + host.mRight - host.mLeft, 1447 mTmpLocation[1] + host.mBottom - host.mTop); 1448 1449 host.gatherTransparentRegion(mTransparentRegion); 1450 if (mTranslator != null) { 1451 mTranslator.translateRegionInWindowToScreen(mTransparentRegion); 1452 } 1453 1454 if (!mTransparentRegion.equals(mPreviousTransparentRegion)) { 1455 mPreviousTransparentRegion.set(mTransparentRegion); 1456 // reconfigure window manager 1457 try { 1458 sWindowSession.setTransparentRegion(mWindow, mTransparentRegion); 1459 } catch (RemoteException e) { 1460 } 1461 } 1462 } 1463 1464 if (DBG) { 1465 System.out.println("======================================"); 1466 System.out.println("performTraversals -- after setFrame"); 1467 host.debug(); 1468 } 1469 } 1470 1471 if (triggerGlobalLayoutListener) { 1472 attachInfo.mRecomputeGlobalAttributes = false; 1473 attachInfo.mTreeObserver.dispatchOnGlobalLayout(); 1474 1475 if (AccessibilityManager.getInstance(host.mContext).isEnabled()) { 1476 postSendWindowContentChangedCallback(); 1477 } 1478 } 1479 1480 if (computesInternalInsets) { 1481 // Clear the original insets. 1482 final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets; 1483 insets.reset(); 1484 1485 // Compute new insets in place. 1486 attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets); 1487 1488 // Tell the window manager. 1489 if (insetsPending || !mLastGivenInsets.equals(insets)) { 1490 mLastGivenInsets.set(insets); 1491 1492 // Translate insets to screen coordinates if needed. 1493 final Rect contentInsets; 1494 final Rect visibleInsets; 1495 final Region touchableRegion; 1496 if (mTranslator != null) { 1497 contentInsets = mTranslator.getTranslatedContentInsets(insets.contentInsets); 1498 visibleInsets = mTranslator.getTranslatedVisibleInsets(insets.visibleInsets); 1499 touchableRegion = mTranslator.getTranslatedTouchableArea(insets.touchableRegion); 1500 } else { 1501 contentInsets = insets.contentInsets; 1502 visibleInsets = insets.visibleInsets; 1503 touchableRegion = insets.touchableRegion; 1504 } 1505 1506 try { 1507 sWindowSession.setInsets(mWindow, insets.mTouchableInsets, 1508 contentInsets, visibleInsets, touchableRegion); 1509 } catch (RemoteException e) { 1510 } 1511 } 1512 } 1513 1514 if (mFirst) { 1515 // handle first focus request 1516 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()=" 1517 + mView.hasFocus()); 1518 if (mView != null) { 1519 if (!mView.hasFocus()) { 1520 mView.requestFocus(View.FOCUS_FORWARD); 1521 mFocusedView = mRealFocusedView = mView.findFocus(); 1522 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view=" 1523 + mFocusedView); 1524 } else { 1525 mRealFocusedView = mView.findFocus(); 1526 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view=" 1527 + mRealFocusedView); 1528 } 1529 } 1530 } 1531 1532 mFirst = false; 1533 mWillDrawSoon = false; 1534 mNewSurfaceNeeded = false; 1535 mViewVisibility = viewVisibility; 1536 1537 if (mAttachInfo.mHasWindowFocus) { 1538 final boolean imTarget = WindowManager.LayoutParams 1539 .mayUseInputMethod(mWindowAttributes.flags); 1540 if (imTarget != mLastWasImTarget) { 1541 mLastWasImTarget = imTarget; 1542 InputMethodManager imm = InputMethodManager.peekInstance(); 1543 if (imm != null && imTarget) { 1544 imm.startGettingWindowFocus(mView); 1545 imm.onWindowFocus(mView, mView.findFocus(), 1546 mWindowAttributes.softInputMode, 1547 !mHasHadWindowFocus, mWindowAttributes.flags); 1548 } 1549 } 1550 } 1551 1552 boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() || 1553 viewVisibility != View.VISIBLE; 1554 1555 if (!cancelDraw && !newSurface) { 1556 if (mPendingTransitions != null && mPendingTransitions.size() > 0) { 1557 for (int i = 0; i < mPendingTransitions.size(); ++i) { 1558 mPendingTransitions.get(i).startChangingAnimations(); 1559 } 1560 mPendingTransitions.clear(); 1561 } 1562 mFullRedrawNeeded = false; 1563 1564 final long drawStartTime; 1565 if (ViewDebug.DEBUG_LATENCY) { 1566 drawStartTime = System.nanoTime(); 1567 } 1568 1569 draw(fullRedrawNeeded); 1570 1571 if (ViewDebug.DEBUG_LATENCY) { 1572 mLastDrawDurationNanos = System.nanoTime() - drawStartTime; 1573 } 1574 1575 if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0 1576 || mReportNextDraw) { 1577 if (LOCAL_LOGV) { 1578 Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle()); 1579 } 1580 mReportNextDraw = false; 1581 if (mSurfaceHolder != null && mSurface.isValid()) { 1582 mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder); 1583 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1584 if (callbacks != null) { 1585 for (SurfaceHolder.Callback c : callbacks) { 1586 if (c instanceof SurfaceHolder.Callback2) { 1587 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 1588 mSurfaceHolder); 1589 } 1590 } 1591 } 1592 } 1593 try { 1594 sWindowSession.finishDrawing(mWindow); 1595 } catch (RemoteException e) { 1596 } 1597 } 1598 } else { 1599 // We were supposed to report when we are done drawing. Since we canceled the 1600 // draw, remember it here. 1601 if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { 1602 mReportNextDraw = true; 1603 } 1604 if (fullRedrawNeeded) { 1605 mFullRedrawNeeded = true; 1606 } 1607 1608 if (viewVisibility == View.VISIBLE) { 1609 // Try again 1610 scheduleTraversals(); 1611 } 1612 } 1613 } 1614 1615 public void requestTransparentRegion(View child) { 1616 // the test below should not fail unless someone is messing with us 1617 checkThread(); 1618 if (mView == child) { 1619 mView.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS; 1620 // Need to make sure we re-evaluate the window attributes next 1621 // time around, to ensure the window has the correct format. 1622 mWindowAttributesChanged = true; 1623 requestLayout(); 1624 } 1625 } 1626 1627 /** 1628 * Figures out the measure spec for the root view in a window based on it's 1629 * layout params. 1630 * 1631 * @param windowSize 1632 * The available width or height of the window 1633 * 1634 * @param rootDimension 1635 * The layout params for one dimension (width or height) of the 1636 * window. 1637 * 1638 * @return The measure spec to use to measure the root view. 1639 */ 1640 private int getRootMeasureSpec(int windowSize, int rootDimension) { 1641 int measureSpec; 1642 switch (rootDimension) { 1643 1644 case ViewGroup.LayoutParams.MATCH_PARENT: 1645 // Window can't resize. Force root view to be windowSize. 1646 measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); 1647 break; 1648 case ViewGroup.LayoutParams.WRAP_CONTENT: 1649 // Window can resize. Set max size for root view. 1650 measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); 1651 break; 1652 default: 1653 // Window wants to be an exact size. Force root view to be that size. 1654 measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); 1655 break; 1656 } 1657 return measureSpec; 1658 } 1659 1660 int mHardwareYOffset; 1661 int mResizeAlpha; 1662 final Paint mResizePaint = new Paint(); 1663 1664 public void onHardwarePreDraw(HardwareCanvas canvas) { 1665 canvas.translate(0, -mHardwareYOffset); 1666 } 1667 1668 public void onHardwarePostDraw(HardwareCanvas canvas) { 1669 if (mResizeBuffer != null) { 1670 mResizePaint.setAlpha(mResizeAlpha); 1671 canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint); 1672 } 1673 } 1674 1675 /** 1676 * @hide 1677 */ 1678 void outputDisplayList(View view) { 1679 if (mAttachInfo != null && mAttachInfo.mHardwareCanvas != null) { 1680 DisplayList displayList = view.getDisplayList(); 1681 if (displayList != null) { 1682 mAttachInfo.mHardwareCanvas.outputDisplayList(displayList); 1683 } 1684 } 1685 } 1686 1687 /** 1688 * @see #PROPERTY_PROFILE_RENDERING 1689 */ 1690 private void profileRendering(boolean enabled) { 1691 if (mProfileRendering) { 1692 mRenderProfilingEnabled = enabled; 1693 if (mRenderProfiler == null) { 1694 mRenderProfiler = new Thread(new Runnable() { 1695 @Override 1696 public void run() { 1697 Log.d(TAG, "Starting profiling thread"); 1698 while (mRenderProfilingEnabled) { 1699 mAttachInfo.mHandler.post(new Runnable() { 1700 @Override 1701 public void run() { 1702 mDirty.set(0, 0, mWidth, mHeight); 1703 scheduleTraversals(); 1704 } 1705 }); 1706 try { 1707 // TODO: This should use vsync when we get an API 1708 Thread.sleep(15); 1709 } catch (InterruptedException e) { 1710 Log.d(TAG, "Exiting profiling thread"); 1711 } 1712 } 1713 } 1714 }, "Rendering Profiler"); 1715 mRenderProfiler.start(); 1716 } else { 1717 mRenderProfiler.interrupt(); 1718 mRenderProfiler = null; 1719 } 1720 } 1721 } 1722 1723 private void draw(boolean fullRedrawNeeded) { 1724 Surface surface = mSurface; 1725 if (surface == null || !surface.isValid()) { 1726 return; 1727 } 1728 1729 if (!sFirstDrawComplete) { 1730 synchronized (sFirstDrawHandlers) { 1731 sFirstDrawComplete = true; 1732 final int count = sFirstDrawHandlers.size(); 1733 for (int i = 0; i< count; i++) { 1734 post(sFirstDrawHandlers.get(i)); 1735 } 1736 } 1737 } 1738 1739 scrollToRectOrFocus(null, false); 1740 1741 if (mAttachInfo.mViewScrollChanged) { 1742 mAttachInfo.mViewScrollChanged = false; 1743 mAttachInfo.mTreeObserver.dispatchOnScrollChanged(); 1744 } 1745 1746 int yoff; 1747 boolean animating = mScroller != null && mScroller.computeScrollOffset(); 1748 if (animating) { 1749 yoff = mScroller.getCurrY(); 1750 } else { 1751 yoff = mScrollY; 1752 } 1753 if (mCurScrollY != yoff) { 1754 mCurScrollY = yoff; 1755 fullRedrawNeeded = true; 1756 } 1757 float appScale = mAttachInfo.mApplicationScale; 1758 boolean scalingRequired = mAttachInfo.mScalingRequired; 1759 1760 int resizeAlpha = 0; 1761 if (mResizeBuffer != null) { 1762 long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime; 1763 if (deltaTime < mResizeBufferDuration) { 1764 float amt = deltaTime/(float) mResizeBufferDuration; 1765 amt = mResizeInterpolator.getInterpolation(amt); 1766 animating = true; 1767 resizeAlpha = 255 - (int)(amt*255); 1768 } else { 1769 disposeResizeBuffer(); 1770 } 1771 } 1772 1773 Rect dirty = mDirty; 1774 if (mSurfaceHolder != null) { 1775 // The app owns the surface, we won't draw. 1776 dirty.setEmpty(); 1777 if (animating) { 1778 if (mScroller != null) { 1779 mScroller.abortAnimation(); 1780 } 1781 disposeResizeBuffer(); 1782 } 1783 return; 1784 } 1785 1786 if (fullRedrawNeeded) { 1787 mAttachInfo.mIgnoreDirtyState = true; 1788 dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); 1789 } 1790 1791 if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { 1792 if (!dirty.isEmpty() || mIsAnimating) { 1793 mIsAnimating = false; 1794 mHardwareYOffset = yoff; 1795 mResizeAlpha = resizeAlpha; 1796 1797 mCurrentDirty.set(dirty); 1798 mCurrentDirty.union(mPreviousDirty); 1799 mPreviousDirty.set(dirty); 1800 dirty.setEmpty(); 1801 1802 Rect currentDirty = mCurrentDirty; 1803 if (animating) { 1804 currentDirty = null; 1805 } 1806 1807 if (mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this, currentDirty)) { 1808 mPreviousDirty.set(0, 0, mWidth, mHeight); 1809 } 1810 } 1811 1812 if (animating) { 1813 mFullRedrawNeeded = true; 1814 scheduleTraversals(); 1815 } 1816 1817 return; 1818 } 1819 1820 if (DEBUG_ORIENTATION || DEBUG_DRAW) { 1821 Log.v(TAG, "Draw " + mView + "/" 1822 + mWindowAttributes.getTitle() 1823 + ": dirty={" + dirty.left + "," + dirty.top 1824 + "," + dirty.right + "," + dirty.bottom + "} surface=" 1825 + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" + 1826 appScale + ", width=" + mWidth + ", height=" + mHeight); 1827 } 1828 1829 if (!dirty.isEmpty() || mIsAnimating) { 1830 Canvas canvas; 1831 try { 1832 int left = dirty.left; 1833 int top = dirty.top; 1834 int right = dirty.right; 1835 int bottom = dirty.bottom; 1836 1837 final long lockCanvasStartTime; 1838 if (ViewDebug.DEBUG_LATENCY) { 1839 lockCanvasStartTime = System.nanoTime(); 1840 } 1841 1842 canvas = surface.lockCanvas(dirty); 1843 1844 if (ViewDebug.DEBUG_LATENCY) { 1845 long now = System.nanoTime(); 1846 Log.d(TAG, "Latency: Spent " 1847 + ((now - lockCanvasStartTime) * 0.000001f) 1848 + "ms waiting for surface.lockCanvas()"); 1849 } 1850 1851 if (left != dirty.left || top != dirty.top || right != dirty.right || 1852 bottom != dirty.bottom) { 1853 mAttachInfo.mIgnoreDirtyState = true; 1854 } 1855 1856 // TODO: Do this in native 1857 canvas.setDensity(mDensity); 1858 } catch (Surface.OutOfResourcesException e) { 1859 Log.e(TAG, "OutOfResourcesException locking surface", e); 1860 try { 1861 if (!sWindowSession.outOfMemory(mWindow)) { 1862 Slog.w(TAG, "No processes killed for memory; killing self"); 1863 Process.killProcess(Process.myPid()); 1864 } 1865 } catch (RemoteException ex) { 1866 } 1867 mLayoutRequested = true; // ask wm for a new surface next time. 1868 return; 1869 } catch (IllegalArgumentException e) { 1870 Log.e(TAG, "IllegalArgumentException locking surface", e); 1871 // Don't assume this is due to out of memory, it could be 1872 // something else, and if it is something else then we could 1873 // kill stuff (or ourself) for no reason. 1874 mLayoutRequested = true; // ask wm for a new surface next time. 1875 return; 1876 } 1877 1878 try { 1879 if (!dirty.isEmpty() || mIsAnimating) { 1880 long startTime = 0L; 1881 1882 if (DEBUG_ORIENTATION || DEBUG_DRAW) { 1883 Log.v(TAG, "Surface " + surface + " drawing to bitmap w=" 1884 + canvas.getWidth() + ", h=" + canvas.getHeight()); 1885 //canvas.drawARGB(255, 255, 0, 0); 1886 } 1887 1888 if (ViewDebug.DEBUG_PROFILE_DRAWING) { 1889 startTime = SystemClock.elapsedRealtime(); 1890 } 1891 1892 // If this bitmap's format includes an alpha channel, we 1893 // need to clear it before drawing so that the child will 1894 // properly re-composite its drawing on a transparent 1895 // background. This automatically respects the clip/dirty region 1896 // or 1897 // If we are applying an offset, we need to clear the area 1898 // where the offset doesn't appear to avoid having garbage 1899 // left in the blank areas. 1900 if (!canvas.isOpaque() || yoff != 0) { 1901 canvas.drawColor(0, PorterDuff.Mode.CLEAR); 1902 } 1903 1904 dirty.setEmpty(); 1905 mIsAnimating = false; 1906 mAttachInfo.mDrawingTime = SystemClock.uptimeMillis(); 1907 mView.mPrivateFlags |= View.DRAWN; 1908 1909 if (DEBUG_DRAW) { 1910 Context cxt = mView.getContext(); 1911 Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + 1912 ", metrics=" + cxt.getResources().getDisplayMetrics() + 1913 ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo()); 1914 } 1915 try { 1916 canvas.translate(0, -yoff); 1917 if (mTranslator != null) { 1918 mTranslator.translateCanvas(canvas); 1919 } 1920 canvas.setScreenDensity(scalingRequired 1921 ? DisplayMetrics.DENSITY_DEVICE : 0); 1922 mAttachInfo.mSetIgnoreDirtyState = false; 1923 mView.draw(canvas); 1924 } finally { 1925 if (!mAttachInfo.mSetIgnoreDirtyState) { 1926 // Only clear the flag if it was not set during the mView.draw() call 1927 mAttachInfo.mIgnoreDirtyState = false; 1928 } 1929 } 1930 1931 if (false && ViewDebug.consistencyCheckEnabled) { 1932 mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING); 1933 } 1934 1935 if (ViewDebug.DEBUG_PROFILE_DRAWING) { 1936 EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime); 1937 } 1938 } 1939 1940 } finally { 1941 surface.unlockCanvasAndPost(canvas); 1942 } 1943 } 1944 1945 if (LOCAL_LOGV) { 1946 Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost"); 1947 } 1948 1949 if (animating) { 1950 mFullRedrawNeeded = true; 1951 scheduleTraversals(); 1952 } 1953 } 1954 1955 boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) { 1956 final View.AttachInfo attachInfo = mAttachInfo; 1957 final Rect ci = attachInfo.mContentInsets; 1958 final Rect vi = attachInfo.mVisibleInsets; 1959 int scrollY = 0; 1960 boolean handled = false; 1961 1962 if (vi.left > ci.left || vi.top > ci.top 1963 || vi.right > ci.right || vi.bottom > ci.bottom) { 1964 // We'll assume that we aren't going to change the scroll 1965 // offset, since we want to avoid that unless it is actually 1966 // going to make the focus visible... otherwise we scroll 1967 // all over the place. 1968 scrollY = mScrollY; 1969 // We can be called for two different situations: during a draw, 1970 // to update the scroll position if the focus has changed (in which 1971 // case 'rectangle' is null), or in response to a 1972 // requestChildRectangleOnScreen() call (in which case 'rectangle' 1973 // is non-null and we just want to scroll to whatever that 1974 // rectangle is). 1975 View focus = mRealFocusedView; 1976 1977 // When in touch mode, focus points to the previously focused view, 1978 // which may have been removed from the view hierarchy. The following 1979 // line checks whether the view is still in our hierarchy. 1980 if (focus == null || focus.mAttachInfo != mAttachInfo) { 1981 mRealFocusedView = null; 1982 return false; 1983 } 1984 1985 if (focus != mLastScrolledFocus) { 1986 // If the focus has changed, then ignore any requests to scroll 1987 // to a rectangle; first we want to make sure the entire focus 1988 // view is visible. 1989 rectangle = null; 1990 } 1991 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus 1992 + " rectangle=" + rectangle + " ci=" + ci 1993 + " vi=" + vi); 1994 if (focus == mLastScrolledFocus && !mScrollMayChange 1995 && rectangle == null) { 1996 // Optimization: if the focus hasn't changed since last 1997 // time, and no layout has happened, then just leave things 1998 // as they are. 1999 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Keeping scroll y=" 2000 + mScrollY + " vi=" + vi.toShortString()); 2001 } else if (focus != null) { 2002 // We need to determine if the currently focused view is 2003 // within the visible part of the window and, if not, apply 2004 // a pan so it can be seen. 2005 mLastScrolledFocus = focus; 2006 mScrollMayChange = false; 2007 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?"); 2008 // Try to find the rectangle from the focus view. 2009 if (focus.getGlobalVisibleRect(mVisRect, null)) { 2010 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Root w=" 2011 + mView.getWidth() + " h=" + mView.getHeight() 2012 + " ci=" + ci.toShortString() 2013 + " vi=" + vi.toShortString()); 2014 if (rectangle == null) { 2015 focus.getFocusedRect(mTempRect); 2016 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Focus " + focus 2017 + ": focusRect=" + mTempRect.toShortString()); 2018 if (mView instanceof ViewGroup) { 2019 ((ViewGroup) mView).offsetDescendantRectToMyCoords( 2020 focus, mTempRect); 2021 } 2022 if (DEBUG_INPUT_RESIZE) Log.v(TAG, 2023 "Focus in window: focusRect=" 2024 + mTempRect.toShortString() 2025 + " visRect=" + mVisRect.toShortString()); 2026 } else { 2027 mTempRect.set(rectangle); 2028 if (DEBUG_INPUT_RESIZE) Log.v(TAG, 2029 "Request scroll to rect: " 2030 + mTempRect.toShortString() 2031 + " visRect=" + mVisRect.toShortString()); 2032 } 2033 if (mTempRect.intersect(mVisRect)) { 2034 if (DEBUG_INPUT_RESIZE) Log.v(TAG, 2035 "Focus window visible rect: " 2036 + mTempRect.toShortString()); 2037 if (mTempRect.height() > 2038 (mView.getHeight()-vi.top-vi.bottom)) { 2039 // If the focus simply is not going to fit, then 2040 // best is probably just to leave things as-is. 2041 if (DEBUG_INPUT_RESIZE) Log.v(TAG, 2042 "Too tall; leaving scrollY=" + scrollY); 2043 } else if ((mTempRect.top-scrollY) < vi.top) { 2044 scrollY -= vi.top - (mTempRect.top-scrollY); 2045 if (DEBUG_INPUT_RESIZE) Log.v(TAG, 2046 "Top covered; scrollY=" + scrollY); 2047 } else if ((mTempRect.bottom-scrollY) 2048 > (mView.getHeight()-vi.bottom)) { 2049 scrollY += (mTempRect.bottom-scrollY) 2050 - (mView.getHeight()-vi.bottom); 2051 if (DEBUG_INPUT_RESIZE) Log.v(TAG, 2052 "Bottom covered; scrollY=" + scrollY); 2053 } 2054 handled = true; 2055 } 2056 } 2057 } 2058 } 2059 2060 if (scrollY != mScrollY) { 2061 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old=" 2062 + mScrollY + " , new=" + scrollY); 2063 if (!immediate && mResizeBuffer == null) { 2064 if (mScroller == null) { 2065 mScroller = new Scroller(mView.getContext()); 2066 } 2067 mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY); 2068 } else if (mScroller != null) { 2069 mScroller.abortAnimation(); 2070 } 2071 mScrollY = scrollY; 2072 } 2073 2074 return handled; 2075 } 2076 2077 public void requestChildFocus(View child, View focused) { 2078 checkThread(); 2079 if (mFocusedView != focused) { 2080 mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused); 2081 scheduleTraversals(); 2082 } 2083 mFocusedView = mRealFocusedView = focused; 2084 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now " 2085 + mFocusedView); 2086 } 2087 2088 public void clearChildFocus(View child) { 2089 checkThread(); 2090 2091 View oldFocus = mFocusedView; 2092 2093 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus"); 2094 mFocusedView = mRealFocusedView = null; 2095 if (mView != null && !mView.hasFocus()) { 2096 // If a view gets the focus, the listener will be invoked from requestChildFocus() 2097 if (!mView.requestFocus(View.FOCUS_FORWARD)) { 2098 mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); 2099 } 2100 } else if (oldFocus != null) { 2101 mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); 2102 } 2103 } 2104 2105 2106 public void focusableViewAvailable(View v) { 2107 checkThread(); 2108 2109 if (mView != null) { 2110 if (!mView.hasFocus()) { 2111 v.requestFocus(); 2112 } else { 2113 // the one case where will transfer focus away from the current one 2114 // is if the current view is a view group that prefers to give focus 2115 // to its children first AND the view is a descendant of it. 2116 mFocusedView = mView.findFocus(); 2117 boolean descendantsHaveDibsOnFocus = 2118 (mFocusedView instanceof ViewGroup) && 2119 (((ViewGroup) mFocusedView).getDescendantFocusability() == 2120 ViewGroup.FOCUS_AFTER_DESCENDANTS); 2121 if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) { 2122 // If a view gets the focus, the listener will be invoked from requestChildFocus() 2123 v.requestFocus(); 2124 } 2125 } 2126 } 2127 } 2128 2129 public void recomputeViewAttributes(View child) { 2130 checkThread(); 2131 if (mView == child) { 2132 mAttachInfo.mRecomputeGlobalAttributes = true; 2133 if (!mWillDrawSoon) { 2134 scheduleTraversals(); 2135 } 2136 } 2137 } 2138 2139 void dispatchDetachedFromWindow() { 2140 if (mView != null && mView.mAttachInfo != null) { 2141 mView.dispatchDetachedFromWindow(); 2142 } 2143 2144 mAccessibilityInteractionConnectionManager.ensureNoConnection(); 2145 mAccessibilityManager.removeAccessibilityStateChangeListener( 2146 mAccessibilityInteractionConnectionManager); 2147 removeSendWindowContentChangedCallback(); 2148 2149 mView = null; 2150 mAttachInfo.mRootView = null; 2151 mAttachInfo.mSurface = null; 2152 2153 destroyHardwareRenderer(); 2154 2155 mSurface.release(); 2156 2157 if (mInputChannel != null) { 2158 if (mInputQueueCallback != null) { 2159 mInputQueueCallback.onInputQueueDestroyed(mInputQueue); 2160 mInputQueueCallback = null; 2161 } else { 2162 InputQueue.unregisterInputChannel(mInputChannel); 2163 } 2164 } 2165 try { 2166 sWindowSession.remove(mWindow); 2167 } catch (RemoteException e) { 2168 } 2169 2170 // Dispose the input channel after removing the window so the Window Manager 2171 // doesn't interpret the input channel being closed as an abnormal termination. 2172 if (mInputChannel != null) { 2173 mInputChannel.dispose(); 2174 mInputChannel = null; 2175 } 2176 } 2177 2178 void updateConfiguration(Configuration config, boolean force) { 2179 if (DEBUG_CONFIGURATION) Log.v(TAG, 2180 "Applying new config to window " 2181 + mWindowAttributes.getTitle() 2182 + ": " + config); 2183 2184 CompatibilityInfo ci = mCompatibilityInfo.getIfNeeded(); 2185 if (ci != null) { 2186 config = new Configuration(config); 2187 ci.applyToConfiguration(config); 2188 } 2189 2190 synchronized (sConfigCallbacks) { 2191 for (int i=sConfigCallbacks.size()-1; i>=0; i--) { 2192 sConfigCallbacks.get(i).onConfigurationChanged(config); 2193 } 2194 } 2195 if (mView != null) { 2196 // At this point the resources have been updated to 2197 // have the most recent config, whatever that is. Use 2198 // the on in them which may be newer. 2199 config = mView.getResources().getConfiguration(); 2200 if (force || mLastConfiguration.diff(config) != 0) { 2201 mLastConfiguration.setTo(config); 2202 mView.dispatchConfigurationChanged(config); 2203 } 2204 } 2205 } 2206 2207 /** 2208 * Return true if child is an ancestor of parent, (or equal to the parent). 2209 */ 2210 private static boolean isViewDescendantOf(View child, View parent) { 2211 if (child == parent) { 2212 return true; 2213 } 2214 2215 final ViewParent theParent = child.getParent(); 2216 return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); 2217 } 2218 2219 private static void forceLayout(View view) { 2220 view.forceLayout(); 2221 if (view instanceof ViewGroup) { 2222 ViewGroup group = (ViewGroup) view; 2223 final int count = group.getChildCount(); 2224 for (int i = 0; i < count; i++) { 2225 forceLayout(group.getChildAt(i)); 2226 } 2227 } 2228 } 2229 2230 public final static int DO_TRAVERSAL = 1000; 2231 public final static int DIE = 1001; 2232 public final static int RESIZED = 1002; 2233 public final static int RESIZED_REPORT = 1003; 2234 public final static int WINDOW_FOCUS_CHANGED = 1004; 2235 public final static int DISPATCH_KEY = 1005; 2236 public final static int DISPATCH_POINTER = 1006; 2237 public final static int DISPATCH_TRACKBALL = 1007; 2238 public final static int DISPATCH_APP_VISIBILITY = 1008; 2239 public final static int DISPATCH_GET_NEW_SURFACE = 1009; 2240 public final static int FINISHED_EVENT = 1010; 2241 public final static int DISPATCH_KEY_FROM_IME = 1011; 2242 public final static int FINISH_INPUT_CONNECTION = 1012; 2243 public final static int CHECK_FOCUS = 1013; 2244 public final static int CLOSE_SYSTEM_DIALOGS = 1014; 2245 public final static int DISPATCH_DRAG_EVENT = 1015; 2246 public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016; 2247 public final static int DISPATCH_SYSTEM_UI_VISIBILITY = 1017; 2248 public final static int DISPATCH_GENERIC_MOTION = 1018; 2249 public final static int UPDATE_CONFIGURATION = 1019; 2250 public final static int DO_PERFORM_ACCESSIBILITY_ACTION = 1020; 2251 public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1021; 2252 public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022; 2253 public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1023; 2254 2255 @Override 2256 public String getMessageName(Message message) { 2257 switch (message.what) { 2258 case DO_TRAVERSAL: 2259 return "DO_TRAVERSAL"; 2260 case DIE: 2261 return "DIE"; 2262 case RESIZED: 2263 return "RESIZED"; 2264 case RESIZED_REPORT: 2265 return "RESIZED_REPORT"; 2266 case WINDOW_FOCUS_CHANGED: 2267 return "WINDOW_FOCUS_CHANGED"; 2268 case DISPATCH_KEY: 2269 return "DISPATCH_KEY"; 2270 case DISPATCH_POINTER: 2271 return "DISPATCH_POINTER"; 2272 case DISPATCH_TRACKBALL: 2273 return "DISPATCH_TRACKBALL"; 2274 case DISPATCH_APP_VISIBILITY: 2275 return "DISPATCH_APP_VISIBILITY"; 2276 case DISPATCH_GET_NEW_SURFACE: 2277 return "DISPATCH_GET_NEW_SURFACE"; 2278 case FINISHED_EVENT: 2279 return "FINISHED_EVENT"; 2280 case DISPATCH_KEY_FROM_IME: 2281 return "DISPATCH_KEY_FROM_IME"; 2282 case FINISH_INPUT_CONNECTION: 2283 return "FINISH_INPUT_CONNECTION"; 2284 case CHECK_FOCUS: 2285 return "CHECK_FOCUS"; 2286 case CLOSE_SYSTEM_DIALOGS: 2287 return "CLOSE_SYSTEM_DIALOGS"; 2288 case DISPATCH_DRAG_EVENT: 2289 return "DISPATCH_DRAG_EVENT"; 2290 case DISPATCH_DRAG_LOCATION_EVENT: 2291 return "DISPATCH_DRAG_LOCATION_EVENT"; 2292 case DISPATCH_SYSTEM_UI_VISIBILITY: 2293 return "DISPATCH_SYSTEM_UI_VISIBILITY"; 2294 case DISPATCH_GENERIC_MOTION: 2295 return "DISPATCH_GENERIC_MOTION"; 2296 case UPDATE_CONFIGURATION: 2297 return "UPDATE_CONFIGURATION"; 2298 case DO_PERFORM_ACCESSIBILITY_ACTION: 2299 return "DO_PERFORM_ACCESSIBILITY_ACTION"; 2300 case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: 2301 return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID"; 2302 case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: 2303 return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID"; 2304 case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: 2305 return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT"; 2306 2307 } 2308 return super.getMessageName(message); 2309 } 2310 2311 @Override 2312 public void handleMessage(Message msg) { 2313 switch (msg.what) { 2314 case View.AttachInfo.INVALIDATE_MSG: 2315 ((View) msg.obj).invalidate(); 2316 break; 2317 case View.AttachInfo.INVALIDATE_RECT_MSG: 2318 final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj; 2319 info.target.invalidate(info.left, info.top, info.right, info.bottom); 2320 info.release(); 2321 break; 2322 case DO_TRAVERSAL: 2323 if (mProfile) { 2324 Debug.startMethodTracing("ViewAncestor"); 2325 } 2326 2327 final long traversalStartTime; 2328 if (ViewDebug.DEBUG_LATENCY) { 2329 traversalStartTime = System.nanoTime(); 2330 mLastDrawDurationNanos = 0; 2331 } 2332 2333 performTraversals(); 2334 2335 if (ViewDebug.DEBUG_LATENCY) { 2336 long now = System.nanoTime(); 2337 Log.d(TAG, "Latency: Spent " 2338 + ((now - traversalStartTime) * 0.000001f) 2339 + "ms in performTraversals(), with " 2340 + (mLastDrawDurationNanos * 0.000001f) 2341 + "ms of that time in draw()"); 2342 mLastTraversalFinishedTimeNanos = now; 2343 } 2344 2345 if (mProfile) { 2346 Debug.stopMethodTracing(); 2347 mProfile = false; 2348 } 2349 break; 2350 case FINISHED_EVENT: 2351 handleFinishedEvent(msg.arg1, msg.arg2 != 0); 2352 break; 2353 case DISPATCH_KEY: 2354 deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0); 2355 break; 2356 case DISPATCH_POINTER: 2357 deliverPointerEvent((MotionEvent) msg.obj, msg.arg1 != 0); 2358 break; 2359 case DISPATCH_TRACKBALL: 2360 deliverTrackballEvent((MotionEvent) msg.obj, msg.arg1 != 0); 2361 break; 2362 case DISPATCH_GENERIC_MOTION: 2363 deliverGenericMotionEvent((MotionEvent) msg.obj, msg.arg1 != 0); 2364 break; 2365 case DISPATCH_APP_VISIBILITY: 2366 handleAppVisibility(msg.arg1 != 0); 2367 break; 2368 case DISPATCH_GET_NEW_SURFACE: 2369 handleGetNewSurface(); 2370 break; 2371 case RESIZED: 2372 ResizedInfo ri = (ResizedInfo)msg.obj; 2373 2374 if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2 2375 && mPendingContentInsets.equals(ri.coveredInsets) 2376 && mPendingVisibleInsets.equals(ri.visibleInsets) 2377 && ((ResizedInfo)msg.obj).newConfig == null) { 2378 break; 2379 } 2380 // fall through... 2381 case RESIZED_REPORT: 2382 if (mAdded) { 2383 Configuration config = ((ResizedInfo)msg.obj).newConfig; 2384 if (config != null) { 2385 updateConfiguration(config, false); 2386 } 2387 mWinFrame.left = 0; 2388 mWinFrame.right = msg.arg1; 2389 mWinFrame.top = 0; 2390 mWinFrame.bottom = msg.arg2; 2391 mPendingContentInsets.set(((ResizedInfo)msg.obj).coveredInsets); 2392 mPendingVisibleInsets.set(((ResizedInfo)msg.obj).visibleInsets); 2393 if (msg.what == RESIZED_REPORT) { 2394 mReportNextDraw = true; 2395 } 2396 2397 if (mView != null) { 2398 forceLayout(mView); 2399 } 2400 requestLayout(); 2401 } 2402 break; 2403 case WINDOW_FOCUS_CHANGED: { 2404 if (mAdded) { 2405 boolean hasWindowFocus = msg.arg1 != 0; 2406 mAttachInfo.mHasWindowFocus = hasWindowFocus; 2407 2408 profileRendering(hasWindowFocus); 2409 2410 if (hasWindowFocus) { 2411 boolean inTouchMode = msg.arg2 != 0; 2412 ensureTouchModeLocally(inTouchMode); 2413 2414 if (mAttachInfo.mHardwareRenderer != null && 2415 mSurface != null && mSurface.isValid()) { 2416 mFullRedrawNeeded = true; 2417 try { 2418 mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight, 2419 mAttachInfo, mHolder); 2420 } catch (Surface.OutOfResourcesException e) { 2421 Log.e(TAG, "OutOfResourcesException locking surface", e); 2422 try { 2423 if (!sWindowSession.outOfMemory(mWindow)) { 2424 Slog.w(TAG, "No processes killed for memory; killing self"); 2425 Process.killProcess(Process.myPid()); 2426 } 2427 } catch (RemoteException ex) { 2428 } 2429 // Retry in a bit. 2430 sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500); 2431 return; 2432 } 2433 } 2434 } 2435 2436 mLastWasImTarget = WindowManager.LayoutParams 2437 .mayUseInputMethod(mWindowAttributes.flags); 2438 2439 InputMethodManager imm = InputMethodManager.peekInstance(); 2440 if (mView != null) { 2441 if (hasWindowFocus && imm != null && mLastWasImTarget) { 2442 imm.startGettingWindowFocus(mView); 2443 } 2444 mAttachInfo.mKeyDispatchState.reset(); 2445 mView.dispatchWindowFocusChanged(hasWindowFocus); 2446 } 2447 2448 // Note: must be done after the focus change callbacks, 2449 // so all of the view state is set up correctly. 2450 if (hasWindowFocus) { 2451 if (imm != null && mLastWasImTarget) { 2452 imm.onWindowFocus(mView, mView.findFocus(), 2453 mWindowAttributes.softInputMode, 2454 !mHasHadWindowFocus, mWindowAttributes.flags); 2455 } 2456 // Clear the forward bit. We can just do this directly, since 2457 // the window manager doesn't care about it. 2458 mWindowAttributes.softInputMode &= 2459 ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; 2460 ((WindowManager.LayoutParams)mView.getLayoutParams()) 2461 .softInputMode &= 2462 ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; 2463 mHasHadWindowFocus = true; 2464 } 2465 2466 if (hasWindowFocus && mView != null) { 2467 sendAccessibilityEvents(); 2468 } 2469 } 2470 } break; 2471 case DIE: 2472 doDie(); 2473 break; 2474 case DISPATCH_KEY_FROM_IME: { 2475 if (LOCAL_LOGV) Log.v( 2476 TAG, "Dispatching key " 2477 + msg.obj + " from IME to " + mView); 2478 KeyEvent event = (KeyEvent)msg.obj; 2479 if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) { 2480 // The IME is trying to say this event is from the 2481 // system! Bad bad bad! 2482 //noinspection UnusedAssignment 2483 event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM); 2484 } 2485 deliverKeyEventPostIme((KeyEvent)msg.obj, false); 2486 } break; 2487 case FINISH_INPUT_CONNECTION: { 2488 InputMethodManager imm = InputMethodManager.peekInstance(); 2489 if (imm != null) { 2490 imm.reportFinishInputConnection((InputConnection)msg.obj); 2491 } 2492 } break; 2493 case CHECK_FOCUS: { 2494 InputMethodManager imm = InputMethodManager.peekInstance(); 2495 if (imm != null) { 2496 imm.checkFocus(); 2497 } 2498 } break; 2499 case CLOSE_SYSTEM_DIALOGS: { 2500 if (mView != null) { 2501 mView.onCloseSystemDialogs((String)msg.obj); 2502 } 2503 } break; 2504 case DISPATCH_DRAG_EVENT: 2505 case DISPATCH_DRAG_LOCATION_EVENT: { 2506 DragEvent event = (DragEvent)msg.obj; 2507 event.mLocalState = mLocalDragState; // only present when this app called startDrag() 2508 handleDragEvent(event); 2509 } break; 2510 case DISPATCH_SYSTEM_UI_VISIBILITY: { 2511 handleDispatchSystemUiVisibilityChanged(msg.arg1); 2512 } break; 2513 case UPDATE_CONFIGURATION: { 2514 Configuration config = (Configuration)msg.obj; 2515 if (config.isOtherSeqNewer(mLastConfiguration)) { 2516 config = mLastConfiguration; 2517 } 2518 updateConfiguration(config, false); 2519 } break; 2520 case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: { 2521 if (mView != null) { 2522 getAccessibilityInteractionController() 2523 .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg); 2524 } 2525 } break; 2526 case DO_PERFORM_ACCESSIBILITY_ACTION: { 2527 if (mView != null) { 2528 getAccessibilityInteractionController() 2529 .perfromAccessibilityActionUiThread(msg); 2530 } 2531 } break; 2532 case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: { 2533 if (mView != null) { 2534 getAccessibilityInteractionController() 2535 .findAccessibilityNodeInfoByViewIdUiThread(msg); 2536 } 2537 } break; 2538 case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: { 2539 if (mView != null) { 2540 getAccessibilityInteractionController() 2541 .findAccessibilityNodeInfosByViewTextUiThread(msg); 2542 } 2543 } break; 2544 } 2545 } 2546 2547 private void startInputEvent(InputQueue.FinishedCallback finishedCallback) { 2548 if (mFinishedCallback != null) { 2549 Slog.w(TAG, "Received a new input event from the input queue but there is " 2550 + "already an unfinished input event in progress."); 2551 } 2552 2553 if (ViewDebug.DEBUG_LATENCY) { 2554 mInputEventReceiveTimeNanos = System.nanoTime(); 2555 mInputEventDeliverTimeNanos = 0; 2556 mInputEventDeliverPostImeTimeNanos = 0; 2557 } 2558 2559 mFinishedCallback = finishedCallback; 2560 } 2561 2562 private void finishInputEvent(InputEvent event, boolean handled) { 2563 if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished"); 2564 2565 if (mFinishedCallback == null) { 2566 Slog.w(TAG, "Attempted to tell the input queue that the current input event " 2567 + "is finished but there is no input event actually in progress."); 2568 return; 2569 } 2570 2571 if (ViewDebug.DEBUG_LATENCY) { 2572 final long now = System.nanoTime(); 2573 final long eventTime = event.getEventTimeNano(); 2574 final StringBuilder msg = new StringBuilder(); 2575 msg.append("Latency: Spent "); 2576 msg.append((now - mInputEventReceiveTimeNanos) * 0.000001f); 2577 msg.append("ms processing "); 2578 if (event instanceof KeyEvent) { 2579 final KeyEvent keyEvent = (KeyEvent)event; 2580 msg.append("key event, action="); 2581 msg.append(KeyEvent.actionToString(keyEvent.getAction())); 2582 } else { 2583 final MotionEvent motionEvent = (MotionEvent)event; 2584 msg.append("motion event, action="); 2585 msg.append(MotionEvent.actionToString(motionEvent.getAction())); 2586 msg.append(", historySize="); 2587 msg.append(motionEvent.getHistorySize()); 2588 } 2589 msg.append(", handled="); 2590 msg.append(handled); 2591 msg.append(", received at +"); 2592 msg.append((mInputEventReceiveTimeNanos - eventTime) * 0.000001f); 2593 if (mInputEventDeliverTimeNanos != 0) { 2594 msg.append("ms, delivered at +"); 2595 msg.append((mInputEventDeliverTimeNanos - eventTime) * 0.000001f); 2596 } 2597 if (mInputEventDeliverPostImeTimeNanos != 0) { 2598 msg.append("ms, delivered post IME at +"); 2599 msg.append((mInputEventDeliverPostImeTimeNanos - eventTime) * 0.000001f); 2600 } 2601 msg.append("ms, finished at +"); 2602 msg.append((now - eventTime) * 0.000001f); 2603 msg.append("ms."); 2604 Log.d(TAG, msg.toString()); 2605 } 2606 2607 mFinishedCallback.finished(handled); 2608 mFinishedCallback = null; 2609 } 2610 2611 /** 2612 * Something in the current window tells us we need to change the touch mode. For 2613 * example, we are not in touch mode, and the user touches the screen. 2614 * 2615 * If the touch mode has changed, tell the window manager, and handle it locally. 2616 * 2617 * @param inTouchMode Whether we want to be in touch mode. 2618 * @return True if the touch mode changed and focus changed was changed as a result 2619 */ 2620 boolean ensureTouchMode(boolean inTouchMode) { 2621 if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current " 2622 + "touch mode is " + mAttachInfo.mInTouchMode); 2623 if (mAttachInfo.mInTouchMode == inTouchMode) return false; 2624 2625 // tell the window manager 2626 try { 2627 sWindowSession.setInTouchMode(inTouchMode); 2628 } catch (RemoteException e) { 2629 throw new RuntimeException(e); 2630 } 2631 2632 // handle the change 2633 return ensureTouchModeLocally(inTouchMode); 2634 } 2635 2636 /** 2637 * Ensure that the touch mode for this window is set, and if it is changing, 2638 * take the appropriate action. 2639 * @param inTouchMode Whether we want to be in touch mode. 2640 * @return True if the touch mode changed and focus changed was changed as a result 2641 */ 2642 private boolean ensureTouchModeLocally(boolean inTouchMode) { 2643 if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current " 2644 + "touch mode is " + mAttachInfo.mInTouchMode); 2645 2646 if (mAttachInfo.mInTouchMode == inTouchMode) return false; 2647 2648 mAttachInfo.mInTouchMode = inTouchMode; 2649 mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode); 2650 2651 return (inTouchMode) ? enterTouchMode() : leaveTouchMode(); 2652 } 2653 2654 private boolean enterTouchMode() { 2655 if (mView != null) { 2656 if (mView.hasFocus()) { 2657 // note: not relying on mFocusedView here because this could 2658 // be when the window is first being added, and mFocused isn't 2659 // set yet. 2660 final View focused = mView.findFocus(); 2661 if (focused != null && !focused.isFocusableInTouchMode()) { 2662 2663 final ViewGroup ancestorToTakeFocus = 2664 findAncestorToTakeFocusInTouchMode(focused); 2665 if (ancestorToTakeFocus != null) { 2666 // there is an ancestor that wants focus after its descendants that 2667 // is focusable in touch mode.. give it focus 2668 return ancestorToTakeFocus.requestFocus(); 2669 } else { 2670 // nothing appropriate to have focus in touch mode, clear it out 2671 mView.unFocus(); 2672 mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null); 2673 mFocusedView = null; 2674 return true; 2675 } 2676 } 2677 } 2678 } 2679 return false; 2680 } 2681 2682 2683 /** 2684 * Find an ancestor of focused that wants focus after its descendants and is 2685 * focusable in touch mode. 2686 * @param focused The currently focused view. 2687 * @return An appropriate view, or null if no such view exists. 2688 */ 2689 private ViewGroup findAncestorToTakeFocusInTouchMode(View focused) { 2690 ViewParent parent = focused.getParent(); 2691 while (parent instanceof ViewGroup) { 2692 final ViewGroup vgParent = (ViewGroup) parent; 2693 if (vgParent.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS 2694 && vgParent.isFocusableInTouchMode()) { 2695 return vgParent; 2696 } 2697 if (vgParent.isRootNamespace()) { 2698 return null; 2699 } else { 2700 parent = vgParent.getParent(); 2701 } 2702 } 2703 return null; 2704 } 2705 2706 private boolean leaveTouchMode() { 2707 if (mView != null) { 2708 if (mView.hasFocus()) { 2709 // i learned the hard way to not trust mFocusedView :) 2710 mFocusedView = mView.findFocus(); 2711 if (!(mFocusedView instanceof ViewGroup)) { 2712 // some view has focus, let it keep it 2713 return false; 2714 } else if (((ViewGroup)mFocusedView).getDescendantFocusability() != 2715 ViewGroup.FOCUS_AFTER_DESCENDANTS) { 2716 // some view group has focus, and doesn't prefer its children 2717 // over itself for focus, so let them keep it. 2718 return false; 2719 } 2720 } 2721 2722 // find the best view to give focus to in this brave new non-touch-mode 2723 // world 2724 final View focused = focusSearch(null, View.FOCUS_DOWN); 2725 if (focused != null) { 2726 return focused.requestFocus(View.FOCUS_DOWN); 2727 } 2728 } 2729 return false; 2730 } 2731 2732 private void deliverPointerEvent(MotionEvent event, boolean sendDone) { 2733 if (ViewDebug.DEBUG_LATENCY) { 2734 mInputEventDeliverTimeNanos = System.nanoTime(); 2735 } 2736 2737 final boolean isTouchEvent = event.isTouchEvent(); 2738 if (mInputEventConsistencyVerifier != null) { 2739 if (isTouchEvent) { 2740 mInputEventConsistencyVerifier.onTouchEvent(event, 0); 2741 } else { 2742 mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); 2743 } 2744 } 2745 2746 // If there is no view, then the event will not be handled. 2747 if (mView == null || !mAdded) { 2748 finishMotionEvent(event, sendDone, false); 2749 return; 2750 } 2751 2752 // Translate the pointer event for compatibility, if needed. 2753 if (mTranslator != null) { 2754 mTranslator.translateEventInScreenToAppWindow(event); 2755 } 2756 2757 // Enter touch mode on down or scroll. 2758 final int action = event.getAction(); 2759 if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { 2760 ensureTouchMode(true); 2761 } 2762 2763 // Offset the scroll position. 2764 if (mCurScrollY != 0) { 2765 event.offsetLocation(0, mCurScrollY); 2766 } 2767 if (MEASURE_LATENCY) { 2768 lt.sample("A Dispatching PointerEvents", System.nanoTime() - event.getEventTimeNano()); 2769 } 2770 2771 // Remember the touch position for possible drag-initiation. 2772 if (isTouchEvent) { 2773 mLastTouchPoint.x = event.getRawX(); 2774 mLastTouchPoint.y = event.getRawY(); 2775 } 2776 2777 // Dispatch touch to view hierarchy. 2778 boolean handled = mView.dispatchPointerEvent(event); 2779 if (MEASURE_LATENCY) { 2780 lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano()); 2781 } 2782 if (handled) { 2783 finishMotionEvent(event, sendDone, true); 2784 return; 2785 } 2786 2787 // Pointer event was unhandled. 2788 finishMotionEvent(event, sendDone, false); 2789 } 2790 2791 private void finishMotionEvent(MotionEvent event, boolean sendDone, boolean handled) { 2792 event.recycle(); 2793 if (sendDone) { 2794 finishInputEvent(event, handled); 2795 } 2796 //noinspection ConstantConditions 2797 if (LOCAL_LOGV || WATCH_POINTER) { 2798 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 2799 Log.i(TAG, "Done dispatching!"); 2800 } 2801 } 2802 } 2803 2804 private void deliverTrackballEvent(MotionEvent event, boolean sendDone) { 2805 if (ViewDebug.DEBUG_LATENCY) { 2806 mInputEventDeliverTimeNanos = System.nanoTime(); 2807 } 2808 2809 if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event); 2810 2811 if (mInputEventConsistencyVerifier != null) { 2812 mInputEventConsistencyVerifier.onTrackballEvent(event, 0); 2813 } 2814 2815 // If there is no view, then the event will not be handled. 2816 if (mView == null || !mAdded) { 2817 finishMotionEvent(event, sendDone, false); 2818 return; 2819 } 2820 2821 // Deliver the trackball event to the view. 2822 if (mView.dispatchTrackballEvent(event)) { 2823 // If we reach this, we delivered a trackball event to mView and 2824 // mView consumed it. Because we will not translate the trackball 2825 // event into a key event, touch mode will not exit, so we exit 2826 // touch mode here. 2827 ensureTouchMode(false); 2828 2829 finishMotionEvent(event, sendDone, true); 2830 mLastTrackballTime = Integer.MIN_VALUE; 2831 return; 2832 } 2833 2834 // Translate the trackball event into DPAD keys and try to deliver those. 2835 final TrackballAxis x = mTrackballAxisX; 2836 final TrackballAxis y = mTrackballAxisY; 2837 2838 long curTime = SystemClock.uptimeMillis(); 2839 if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) { 2840 // It has been too long since the last movement, 2841 // so restart at the beginning. 2842 x.reset(0); 2843 y.reset(0); 2844 mLastTrackballTime = curTime; 2845 } 2846 2847 final int action = event.getAction(); 2848 final int metaState = event.getMetaState(); 2849 switch (action) { 2850 case MotionEvent.ACTION_DOWN: 2851 x.reset(2); 2852 y.reset(2); 2853 deliverKeyEvent(new KeyEvent(curTime, curTime, 2854 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState, 2855 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 2856 InputDevice.SOURCE_KEYBOARD), false); 2857 break; 2858 case MotionEvent.ACTION_UP: 2859 x.reset(2); 2860 y.reset(2); 2861 deliverKeyEvent(new KeyEvent(curTime, curTime, 2862 KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState, 2863 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 2864 InputDevice.SOURCE_KEYBOARD), false); 2865 break; 2866 } 2867 2868 if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step=" 2869 + x.step + " dir=" + x.dir + " acc=" + x.acceleration 2870 + " move=" + event.getX() 2871 + " / Y=" + y.position + " step=" 2872 + y.step + " dir=" + y.dir + " acc=" + y.acceleration 2873 + " move=" + event.getY()); 2874 final float xOff = x.collect(event.getX(), event.getEventTime(), "X"); 2875 final float yOff = y.collect(event.getY(), event.getEventTime(), "Y"); 2876 2877 // Generate DPAD events based on the trackball movement. 2878 // We pick the axis that has moved the most as the direction of 2879 // the DPAD. When we generate DPAD events for one axis, then the 2880 // other axis is reset -- we don't want to perform DPAD jumps due 2881 // to slight movements in the trackball when making major movements 2882 // along the other axis. 2883 int keycode = 0; 2884 int movement = 0; 2885 float accel = 1; 2886 if (xOff > yOff) { 2887 movement = x.generate((2/event.getXPrecision())); 2888 if (movement != 0) { 2889 keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT 2890 : KeyEvent.KEYCODE_DPAD_LEFT; 2891 accel = x.acceleration; 2892 y.reset(2); 2893 } 2894 } else if (yOff > 0) { 2895 movement = y.generate((2/event.getYPrecision())); 2896 if (movement != 0) { 2897 keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN 2898 : KeyEvent.KEYCODE_DPAD_UP; 2899 accel = y.acceleration; 2900 x.reset(2); 2901 } 2902 } 2903 2904 if (keycode != 0) { 2905 if (movement < 0) movement = -movement; 2906 int accelMovement = (int)(movement * accel); 2907 if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement 2908 + " accelMovement=" + accelMovement 2909 + " accel=" + accel); 2910 if (accelMovement > movement) { 2911 if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: " 2912 + keycode); 2913 movement--; 2914 int repeatCount = accelMovement - movement; 2915 deliverKeyEvent(new KeyEvent(curTime, curTime, 2916 KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState, 2917 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 2918 InputDevice.SOURCE_KEYBOARD), false); 2919 } 2920 while (movement > 0) { 2921 if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: " 2922 + keycode); 2923 movement--; 2924 curTime = SystemClock.uptimeMillis(); 2925 deliverKeyEvent(new KeyEvent(curTime, curTime, 2926 KeyEvent.ACTION_DOWN, keycode, 0, metaState, 2927 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 2928 InputDevice.SOURCE_KEYBOARD), false); 2929 deliverKeyEvent(new KeyEvent(curTime, curTime, 2930 KeyEvent.ACTION_UP, keycode, 0, metaState, 2931 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 2932 InputDevice.SOURCE_KEYBOARD), false); 2933 } 2934 mLastTrackballTime = curTime; 2935 } 2936 2937 // Unfortunately we can't tell whether the application consumed the keys, so 2938 // we always consider the trackball event handled. 2939 finishMotionEvent(event, sendDone, true); 2940 } 2941 2942 private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) { 2943 if (ViewDebug.DEBUG_LATENCY) { 2944 mInputEventDeliverTimeNanos = System.nanoTime(); 2945 } 2946 2947 if (mInputEventConsistencyVerifier != null) { 2948 mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); 2949 } 2950 2951 final int source = event.getSource(); 2952 final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0; 2953 2954 // If there is no view, then the event will not be handled. 2955 if (mView == null || !mAdded) { 2956 if (isJoystick) { 2957 updateJoystickDirection(event, false); 2958 } 2959 finishMotionEvent(event, sendDone, false); 2960 return; 2961 } 2962 2963 // Deliver the event to the view. 2964 if (mView.dispatchGenericMotionEvent(event)) { 2965 if (isJoystick) { 2966 updateJoystickDirection(event, false); 2967 } 2968 finishMotionEvent(event, sendDone, true); 2969 return; 2970 } 2971 2972 if (isJoystick) { 2973 // Translate the joystick event into DPAD keys and try to deliver those. 2974 updateJoystickDirection(event, true); 2975 finishMotionEvent(event, sendDone, true); 2976 } else { 2977 finishMotionEvent(event, sendDone, false); 2978 } 2979 } 2980 2981 private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) { 2982 final long time = event.getEventTime(); 2983 final int metaState = event.getMetaState(); 2984 final int deviceId = event.getDeviceId(); 2985 final int source = event.getSource(); 2986 2987 int xDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_X)); 2988 if (xDirection == 0) { 2989 xDirection = joystickAxisValueToDirection(event.getX()); 2990 } 2991 2992 int yDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_Y)); 2993 if (yDirection == 0) { 2994 yDirection = joystickAxisValueToDirection(event.getY()); 2995 } 2996 2997 if (xDirection != mLastJoystickXDirection) { 2998 if (mLastJoystickXKeyCode != 0) { 2999 deliverKeyEvent(new KeyEvent(time, time, 3000 KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState, 3001 deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); 3002 mLastJoystickXKeyCode = 0; 3003 } 3004 3005 mLastJoystickXDirection = xDirection; 3006 3007 if (xDirection != 0 && synthesizeNewKeys) { 3008 mLastJoystickXKeyCode = xDirection > 0 3009 ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT; 3010 deliverKeyEvent(new KeyEvent(time, time, 3011 KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState, 3012 deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); 3013 } 3014 } 3015 3016 if (yDirection != mLastJoystickYDirection) { 3017 if (mLastJoystickYKeyCode != 0) { 3018 deliverKeyEvent(new KeyEvent(time, time, 3019 KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState, 3020 deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); 3021 mLastJoystickYKeyCode = 0; 3022 } 3023 3024 mLastJoystickYDirection = yDirection; 3025 3026 if (yDirection != 0 && synthesizeNewKeys) { 3027 mLastJoystickYKeyCode = yDirection > 0 3028 ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP; 3029 deliverKeyEvent(new KeyEvent(time, time, 3030 KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState, 3031 deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); 3032 } 3033 } 3034 } 3035 3036 private static int joystickAxisValueToDirection(float value) { 3037 if (value >= 0.5f) { 3038 return 1; 3039 } else if (value <= -0.5f) { 3040 return -1; 3041 } else { 3042 return 0; 3043 } 3044 } 3045 3046 /** 3047 * Returns true if the key is used for keyboard navigation. 3048 * @param keyEvent The key event. 3049 * @return True if the key is used for keyboard navigation. 3050 */ 3051 private static boolean isNavigationKey(KeyEvent keyEvent) { 3052 switch (keyEvent.getKeyCode()) { 3053 case KeyEvent.KEYCODE_DPAD_LEFT: 3054 case KeyEvent.KEYCODE_DPAD_RIGHT: 3055 case KeyEvent.KEYCODE_DPAD_UP: 3056 case KeyEvent.KEYCODE_DPAD_DOWN: 3057 case KeyEvent.KEYCODE_DPAD_CENTER: 3058 case KeyEvent.KEYCODE_PAGE_UP: 3059 case KeyEvent.KEYCODE_PAGE_DOWN: 3060 case KeyEvent.KEYCODE_MOVE_HOME: 3061 case KeyEvent.KEYCODE_MOVE_END: 3062 case KeyEvent.KEYCODE_TAB: 3063 case KeyEvent.KEYCODE_SPACE: 3064 case KeyEvent.KEYCODE_ENTER: 3065 return true; 3066 } 3067 return false; 3068 } 3069 3070 /** 3071 * Returns true if the key is used for typing. 3072 * @param keyEvent The key event. 3073 * @return True if the key is used for typing. 3074 */ 3075 private static boolean isTypingKey(KeyEvent keyEvent) { 3076 return keyEvent.getUnicodeChar() > 0; 3077 } 3078 3079 /** 3080 * See if the key event means we should leave touch mode (and leave touch mode if so). 3081 * @param event The key event. 3082 * @return Whether this key event should be consumed (meaning the act of 3083 * leaving touch mode alone is considered the event). 3084 */ 3085 private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) { 3086 // Only relevant in touch mode. 3087 if (!mAttachInfo.mInTouchMode) { 3088 return false; 3089 } 3090 3091 // Only consider leaving touch mode on DOWN or MULTIPLE actions, never on UP. 3092 final int action = event.getAction(); 3093 if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) { 3094 return false; 3095 } 3096 3097 // Don't leave touch mode if the IME told us not to. 3098 if ((event.getFlags() & KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) { 3099 return false; 3100 } 3101 3102 // If the key can be used for keyboard navigation then leave touch mode 3103 // and select a focused view if needed (in ensureTouchMode). 3104 // When a new focused view is selected, we consume the navigation key because 3105 // navigation doesn't make much sense unless a view already has focus so 3106 // the key's purpose is to set focus. 3107 if (isNavigationKey(event)) { 3108 return ensureTouchMode(false); 3109 } 3110 3111 // If the key can be used for typing then leave touch mode 3112 // and select a focused view if needed (in ensureTouchMode). 3113 // Always allow the view to process the typing key. 3114 if (isTypingKey(event)) { 3115 ensureTouchMode(false); 3116 return false; 3117 } 3118 3119 return false; 3120 } 3121 3122 int enqueuePendingEvent(Object event, boolean sendDone) { 3123 int seq = mPendingEventSeq+1; 3124 if (seq < 0) seq = 0; 3125 mPendingEventSeq = seq; 3126 mPendingEvents.put(seq, event); 3127 return sendDone ? seq : -seq; 3128 } 3129 3130 Object retrievePendingEvent(int seq) { 3131 if (seq < 0) seq = -seq; 3132 Object event = mPendingEvents.get(seq); 3133 if (event != null) { 3134 mPendingEvents.remove(seq); 3135 } 3136 return event; 3137 } 3138 3139 private void deliverKeyEvent(KeyEvent event, boolean sendDone) { 3140 if (ViewDebug.DEBUG_LATENCY) { 3141 mInputEventDeliverTimeNanos = System.nanoTime(); 3142 } 3143 3144 if (mInputEventConsistencyVerifier != null) { 3145 mInputEventConsistencyVerifier.onKeyEvent(event, 0); 3146 } 3147 3148 // If there is no view, then the event will not be handled. 3149 if (mView == null || !mAdded) { 3150 finishKeyEvent(event, sendDone, false); 3151 return; 3152 } 3153 3154 if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView); 3155 3156 // Perform predispatching before the IME. 3157 if (mView.dispatchKeyEventPreIme(event)) { 3158 finishKeyEvent(event, sendDone, true); 3159 return; 3160 } 3161 3162 // Dispatch to the IME before propagating down the view hierarchy. 3163 // The IME will eventually call back into handleFinishedEvent. 3164 if (mLastWasImTarget) { 3165 InputMethodManager imm = InputMethodManager.peekInstance(); 3166 if (imm != null) { 3167 int seq = enqueuePendingEvent(event, sendDone); 3168 if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq=" 3169 + seq + " event=" + event); 3170 imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback); 3171 return; 3172 } 3173 } 3174 3175 // Not dispatching to IME, continue with post IME actions. 3176 deliverKeyEventPostIme(event, sendDone); 3177 } 3178 3179 private void handleFinishedEvent(int seq, boolean handled) { 3180 final KeyEvent event = (KeyEvent)retrievePendingEvent(seq); 3181 if (DEBUG_IMF) Log.v(TAG, "IME finished event: seq=" + seq 3182 + " handled=" + handled + " event=" + event); 3183 if (event != null) { 3184 final boolean sendDone = seq >= 0; 3185 if (handled) { 3186 finishKeyEvent(event, sendDone, true); 3187 } else { 3188 deliverKeyEventPostIme(event, sendDone); 3189 } 3190 } 3191 } 3192 3193 private void deliverKeyEventPostIme(KeyEvent event, boolean sendDone) { 3194 if (ViewDebug.DEBUG_LATENCY) { 3195 mInputEventDeliverPostImeTimeNanos = System.nanoTime(); 3196 } 3197 3198 // If the view went away, then the event will not be handled. 3199 if (mView == null || !mAdded) { 3200 finishKeyEvent(event, sendDone, false); 3201 return; 3202 } 3203 3204 // If the key's purpose is to exit touch mode then we consume it and consider it handled. 3205 if (checkForLeavingTouchModeAndConsume(event)) { 3206 finishKeyEvent(event, sendDone, true); 3207 return; 3208 } 3209 3210 // Make sure the fallback event policy sees all keys that will be delivered to the 3211 // view hierarchy. 3212 mFallbackEventHandler.preDispatchKeyEvent(event); 3213 3214 // Deliver the key to the view hierarchy. 3215 if (mView.dispatchKeyEvent(event)) { 3216 finishKeyEvent(event, sendDone, true); 3217 return; 3218 } 3219 3220 // If the Control modifier is held, try to interpret the key as a shortcut. 3221 if (event.getAction() == KeyEvent.ACTION_UP 3222 && event.isCtrlPressed() 3223 && !KeyEvent.isModifierKey(event.getKeyCode())) { 3224 if (mView.dispatchKeyShortcutEvent(event)) { 3225 finishKeyEvent(event, sendDone, true); 3226 return; 3227 } 3228 } 3229 3230 // Apply the fallback event policy. 3231 if (mFallbackEventHandler.dispatchKeyEvent(event)) { 3232 finishKeyEvent(event, sendDone, true); 3233 return; 3234 } 3235 3236 // Handle automatic focus changes. 3237 if (event.getAction() == KeyEvent.ACTION_DOWN) { 3238 int direction = 0; 3239 switch (event.getKeyCode()) { 3240 case KeyEvent.KEYCODE_DPAD_LEFT: 3241 if (event.hasNoModifiers()) { 3242 direction = View.FOCUS_LEFT; 3243 } 3244 break; 3245 case KeyEvent.KEYCODE_DPAD_RIGHT: 3246 if (event.hasNoModifiers()) { 3247 direction = View.FOCUS_RIGHT; 3248 } 3249 break; 3250 case KeyEvent.KEYCODE_DPAD_UP: 3251 if (event.hasNoModifiers()) { 3252 direction = View.FOCUS_UP; 3253 } 3254 break; 3255 case KeyEvent.KEYCODE_DPAD_DOWN: 3256 if (event.hasNoModifiers()) { 3257 direction = View.FOCUS_DOWN; 3258 } 3259 break; 3260 case KeyEvent.KEYCODE_TAB: 3261 if (event.hasNoModifiers()) { 3262 direction = View.FOCUS_FORWARD; 3263 } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) { 3264 direction = View.FOCUS_BACKWARD; 3265 } 3266 break; 3267 } 3268 3269 if (direction != 0) { 3270 View focused = mView != null ? mView.findFocus() : null; 3271 if (focused != null) { 3272 View v = focused.focusSearch(direction); 3273 if (v != null && v != focused) { 3274 // do the math the get the interesting rect 3275 // of previous focused into the coord system of 3276 // newly focused view 3277 focused.getFocusedRect(mTempRect); 3278 if (mView instanceof ViewGroup) { 3279 ((ViewGroup) mView).offsetDescendantRectToMyCoords( 3280 focused, mTempRect); 3281 ((ViewGroup) mView).offsetRectIntoDescendantCoords( 3282 v, mTempRect); 3283 } 3284 if (v.requestFocus(direction, mTempRect)) { 3285 playSoundEffect( 3286 SoundEffectConstants.getContantForFocusDirection(direction)); 3287 finishKeyEvent(event, sendDone, true); 3288 return; 3289 } 3290 } 3291 3292 // Give the focused view a last chance to handle the dpad key. 3293 if (mView.dispatchUnhandledMove(focused, direction)) { 3294 finishKeyEvent(event, sendDone, true); 3295 return; 3296 } 3297 } 3298 } 3299 } 3300 3301 // Key was unhandled. 3302 finishKeyEvent(event, sendDone, false); 3303 } 3304 3305 private void finishKeyEvent(KeyEvent event, boolean sendDone, boolean handled) { 3306 if (sendDone) { 3307 finishInputEvent(event, handled); 3308 } 3309 } 3310 3311 /* drag/drop */ 3312 void setLocalDragState(Object obj) { 3313 mLocalDragState = obj; 3314 } 3315 3316 private void handleDragEvent(DragEvent event) { 3317 // From the root, only drag start/end/location are dispatched. entered/exited 3318 // are determined and dispatched by the viewgroup hierarchy, who then report 3319 // that back here for ultimate reporting back to the framework. 3320 if (mView != null && mAdded) { 3321 final int what = event.mAction; 3322 3323 if (what == DragEvent.ACTION_DRAG_EXITED) { 3324 // A direct EXITED event means that the window manager knows we've just crossed 3325 // a window boundary, so the current drag target within this one must have 3326 // just been exited. Send it the usual notifications and then we're done 3327 // for now. 3328 mView.dispatchDragEvent(event); 3329 } else { 3330 // Cache the drag description when the operation starts, then fill it in 3331 // on subsequent calls as a convenience 3332 if (what == DragEvent.ACTION_DRAG_STARTED) { 3333 mCurrentDragView = null; // Start the current-recipient tracking 3334 mDragDescription = event.mClipDescription; 3335 } else { 3336 event.mClipDescription = mDragDescription; 3337 } 3338 3339 // For events with a [screen] location, translate into window coordinates 3340 if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) { 3341 mDragPoint.set(event.mX, event.mY); 3342 if (mTranslator != null) { 3343 mTranslator.translatePointInScreenToAppWindow(mDragPoint); 3344 } 3345 3346 if (mCurScrollY != 0) { 3347 mDragPoint.offset(0, mCurScrollY); 3348 } 3349 3350 event.mX = mDragPoint.x; 3351 event.mY = mDragPoint.y; 3352 } 3353 3354 // Remember who the current drag target is pre-dispatch 3355 final View prevDragView = mCurrentDragView; 3356 3357 // Now dispatch the drag/drop event 3358 boolean result = mView.dispatchDragEvent(event); 3359 3360 // If we changed apparent drag target, tell the OS about it 3361 if (prevDragView != mCurrentDragView) { 3362 try { 3363 if (prevDragView != null) { 3364 sWindowSession.dragRecipientExited(mWindow); 3365 } 3366 if (mCurrentDragView != null) { 3367 sWindowSession.dragRecipientEntered(mWindow); 3368 } 3369 } catch (RemoteException e) { 3370 Slog.e(TAG, "Unable to note drag target change"); 3371 } 3372 } 3373 3374 // Report the drop result when we're done 3375 if (what == DragEvent.ACTION_DROP) { 3376 mDragDescription = null; 3377 try { 3378 Log.i(TAG, "Reporting drop result: " + result); 3379 sWindowSession.reportDropResult(mWindow, result); 3380 } catch (RemoteException e) { 3381 Log.e(TAG, "Unable to report drop result"); 3382 } 3383 } 3384 3385 // When the drag operation ends, release any local state object 3386 // that may have been in use 3387 if (what == DragEvent.ACTION_DRAG_ENDED) { 3388 setLocalDragState(null); 3389 } 3390 } 3391 } 3392 event.recycle(); 3393 } 3394 3395 public void handleDispatchSystemUiVisibilityChanged(int visibility) { 3396 if (mView == null) return; 3397 if (mAttachInfo != null) { 3398 mAttachInfo.mSystemUiVisibility = visibility; 3399 } 3400 mView.dispatchSystemUiVisibilityChanged(visibility); 3401 } 3402 3403 public void getLastTouchPoint(Point outLocation) { 3404 outLocation.x = (int) mLastTouchPoint.x; 3405 outLocation.y = (int) mLastTouchPoint.y; 3406 } 3407 3408 public void setDragFocus(View newDragTarget) { 3409 if (mCurrentDragView != newDragTarget) { 3410 mCurrentDragView = newDragTarget; 3411 } 3412 } 3413 3414 private AudioManager getAudioManager() { 3415 if (mView == null) { 3416 throw new IllegalStateException("getAudioManager called when there is no mView"); 3417 } 3418 if (mAudioManager == null) { 3419 mAudioManager = (AudioManager) mView.getContext().getSystemService(Context.AUDIO_SERVICE); 3420 } 3421 return mAudioManager; 3422 } 3423 3424 public AccessibilityInteractionController getAccessibilityInteractionController() { 3425 if (mView == null) { 3426 throw new IllegalStateException("getAccessibilityInteractionController" 3427 + " called when there is no mView"); 3428 } 3429 if (mAccessibilityInteractionContrtoller == null) { 3430 mAccessibilityInteractionContrtoller = new AccessibilityInteractionController(); 3431 } 3432 return mAccessibilityInteractionContrtoller; 3433 } 3434 3435 private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, 3436 boolean insetsPending) throws RemoteException { 3437 3438 float appScale = mAttachInfo.mApplicationScale; 3439 boolean restore = false; 3440 if (params != null && mTranslator != null) { 3441 restore = true; 3442 params.backup(); 3443 mTranslator.translateWindowLayout(params); 3444 } 3445 if (params != null) { 3446 if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params); 3447 } 3448 mPendingConfiguration.seq = 0; 3449 //Log.d(TAG, ">>>>>> CALLING relayout"); 3450 int relayoutResult = sWindowSession.relayout( 3451 mWindow, params, 3452 (int) (mView.getMeasuredWidth() * appScale + 0.5f), 3453 (int) (mView.getMeasuredHeight() * appScale + 0.5f), 3454 viewVisibility, insetsPending, mWinFrame, 3455 mPendingContentInsets, mPendingVisibleInsets, 3456 mPendingConfiguration, mSurface); 3457 //Log.d(TAG, "<<<<<< BACK FROM relayout"); 3458 if (restore) { 3459 params.restore(); 3460 } 3461 3462 if (mTranslator != null) { 3463 mTranslator.translateRectInScreenToAppWinFrame(mWinFrame); 3464 mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets); 3465 mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets); 3466 } 3467 return relayoutResult; 3468 } 3469 3470 /** 3471 * {@inheritDoc} 3472 */ 3473 public void playSoundEffect(int effectId) { 3474 checkThread(); 3475 3476 try { 3477 final AudioManager audioManager = getAudioManager(); 3478 3479 switch (effectId) { 3480 case SoundEffectConstants.CLICK: 3481 audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); 3482 return; 3483 case SoundEffectConstants.NAVIGATION_DOWN: 3484 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN); 3485 return; 3486 case SoundEffectConstants.NAVIGATION_LEFT: 3487 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT); 3488 return; 3489 case SoundEffectConstants.NAVIGATION_RIGHT: 3490 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT); 3491 return; 3492 case SoundEffectConstants.NAVIGATION_UP: 3493 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP); 3494 return; 3495 default: 3496 throw new IllegalArgumentException("unknown effect id " + effectId + 3497 " not defined in " + SoundEffectConstants.class.getCanonicalName()); 3498 } 3499 } catch (IllegalStateException e) { 3500 // Exception thrown by getAudioManager() when mView is null 3501 Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e); 3502 e.printStackTrace(); 3503 } 3504 } 3505 3506 /** 3507 * {@inheritDoc} 3508 */ 3509 public boolean performHapticFeedback(int effectId, boolean always) { 3510 try { 3511 return sWindowSession.performHapticFeedback(mWindow, effectId, always); 3512 } catch (RemoteException e) { 3513 return false; 3514 } 3515 } 3516 3517 /** 3518 * {@inheritDoc} 3519 */ 3520 public View focusSearch(View focused, int direction) { 3521 checkThread(); 3522 if (!(mView instanceof ViewGroup)) { 3523 return null; 3524 } 3525 return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction); 3526 } 3527 3528 public void debug() { 3529 mView.debug(); 3530 } 3531 3532 public void dumpGfxInfo(PrintWriter pw, int[] info) { 3533 if (mView != null) { 3534 getGfxInfo(mView, info); 3535 } else { 3536 info[0] = info[1] = 0; 3537 } 3538 } 3539 3540 private void getGfxInfo(View view, int[] info) { 3541 DisplayList displayList = view.mDisplayList; 3542 info[0]++; 3543 if (displayList != null) { 3544 info[1] += displayList.getSize(); 3545 } 3546 3547 if (view instanceof ViewGroup) { 3548 ViewGroup group = (ViewGroup) view; 3549 3550 int count = group.getChildCount(); 3551 for (int i = 0; i < count; i++) { 3552 getGfxInfo(group.getChildAt(i), info); 3553 } 3554 } 3555 } 3556 3557 public void die(boolean immediate) { 3558 if (immediate) { 3559 doDie(); 3560 } else { 3561 sendEmptyMessage(DIE); 3562 } 3563 } 3564 3565 void doDie() { 3566 checkThread(); 3567 if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface); 3568 synchronized (this) { 3569 if (mAdded && !mFirst) { 3570 destroyHardwareRenderer(); 3571 3572 int viewVisibility = mView.getVisibility(); 3573 boolean viewVisibilityChanged = mViewVisibility != viewVisibility; 3574 if (mWindowAttributesChanged || viewVisibilityChanged) { 3575 // If layout params have been changed, first give them 3576 // to the window manager to make sure it has the correct 3577 // animation info. 3578 try { 3579 if ((relayoutWindow(mWindowAttributes, viewVisibility, false) 3580 & WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { 3581 sWindowSession.finishDrawing(mWindow); 3582 } 3583 } catch (RemoteException e) { 3584 } 3585 } 3586 3587 mSurface.release(); 3588 } 3589 if (mAdded) { 3590 mAdded = false; 3591 dispatchDetachedFromWindow(); 3592 } 3593 } 3594 } 3595 3596 public void requestUpdateConfiguration(Configuration config) { 3597 Message msg = obtainMessage(UPDATE_CONFIGURATION, config); 3598 sendMessage(msg); 3599 } 3600 3601 private void destroyHardwareRenderer() { 3602 if (mAttachInfo.mHardwareRenderer != null) { 3603 mAttachInfo.mHardwareRenderer.destroy(true); 3604 mAttachInfo.mHardwareRenderer = null; 3605 mAttachInfo.mHardwareAccelerated = false; 3606 } 3607 } 3608 3609 public void dispatchFinishedEvent(int seq, boolean handled) { 3610 Message msg = obtainMessage(FINISHED_EVENT); 3611 msg.arg1 = seq; 3612 msg.arg2 = handled ? 1 : 0; 3613 sendMessage(msg); 3614 } 3615 3616 public void dispatchResized(int w, int h, Rect coveredInsets, 3617 Rect visibleInsets, boolean reportDraw, Configuration newConfig) { 3618 if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": w=" + w 3619 + " h=" + h + " coveredInsets=" + coveredInsets.toShortString() 3620 + " visibleInsets=" + visibleInsets.toShortString() 3621 + " reportDraw=" + reportDraw); 3622 Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED); 3623 if (mTranslator != null) { 3624 mTranslator.translateRectInScreenToAppWindow(coveredInsets); 3625 mTranslator.translateRectInScreenToAppWindow(visibleInsets); 3626 w *= mTranslator.applicationInvertedScale; 3627 h *= mTranslator.applicationInvertedScale; 3628 } 3629 msg.arg1 = w; 3630 msg.arg2 = h; 3631 ResizedInfo ri = new ResizedInfo(); 3632 ri.coveredInsets = new Rect(coveredInsets); 3633 ri.visibleInsets = new Rect(visibleInsets); 3634 ri.newConfig = newConfig; 3635 msg.obj = ri; 3636 sendMessage(msg); 3637 } 3638 3639 private long mInputEventReceiveTimeNanos; 3640 private long mInputEventDeliverTimeNanos; 3641 private long mInputEventDeliverPostImeTimeNanos; 3642 private InputQueue.FinishedCallback mFinishedCallback; 3643 3644 private final InputHandler mInputHandler = new InputHandler() { 3645 public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) { 3646 startInputEvent(finishedCallback); 3647 dispatchKey(event, true); 3648 } 3649 3650 public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) { 3651 startInputEvent(finishedCallback); 3652 dispatchMotion(event, true); 3653 } 3654 }; 3655 3656 public void dispatchKey(KeyEvent event) { 3657 dispatchKey(event, false); 3658 } 3659 3660 private void dispatchKey(KeyEvent event, boolean sendDone) { 3661 //noinspection ConstantConditions 3662 if (false && event.getAction() == KeyEvent.ACTION_DOWN) { 3663 if (event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) { 3664 if (DBG) Log.d("keydisp", "==================================================="); 3665 if (DBG) Log.d("keydisp", "Focused view Hierarchy is:"); 3666 3667 debug(); 3668 3669 if (DBG) Log.d("keydisp", "==================================================="); 3670 } 3671 } 3672 3673 Message msg = obtainMessage(DISPATCH_KEY); 3674 msg.obj = event; 3675 msg.arg1 = sendDone ? 1 : 0; 3676 3677 if (LOCAL_LOGV) Log.v( 3678 TAG, "sending key " + event + " to " + mView); 3679 3680 sendMessageAtTime(msg, event.getEventTime()); 3681 } 3682 3683 private void dispatchMotion(MotionEvent event, boolean sendDone) { 3684 int source = event.getSource(); 3685 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { 3686 dispatchPointer(event, sendDone); 3687 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 3688 dispatchTrackball(event, sendDone); 3689 } else { 3690 dispatchGenericMotion(event, sendDone); 3691 } 3692 } 3693 3694 private void dispatchPointer(MotionEvent event, boolean sendDone) { 3695 Message msg = obtainMessage(DISPATCH_POINTER); 3696 msg.obj = event; 3697 msg.arg1 = sendDone ? 1 : 0; 3698 sendMessageAtTime(msg, event.getEventTime()); 3699 } 3700 3701 private void dispatchTrackball(MotionEvent event, boolean sendDone) { 3702 Message msg = obtainMessage(DISPATCH_TRACKBALL); 3703 msg.obj = event; 3704 msg.arg1 = sendDone ? 1 : 0; 3705 sendMessageAtTime(msg, event.getEventTime()); 3706 } 3707 3708 private void dispatchGenericMotion(MotionEvent event, boolean sendDone) { 3709 Message msg = obtainMessage(DISPATCH_GENERIC_MOTION); 3710 msg.obj = event; 3711 msg.arg1 = sendDone ? 1 : 0; 3712 sendMessageAtTime(msg, event.getEventTime()); 3713 } 3714 3715 public void dispatchAppVisibility(boolean visible) { 3716 Message msg = obtainMessage(DISPATCH_APP_VISIBILITY); 3717 msg.arg1 = visible ? 1 : 0; 3718 sendMessage(msg); 3719 } 3720 3721 public void dispatchGetNewSurface() { 3722 Message msg = obtainMessage(DISPATCH_GET_NEW_SURFACE); 3723 sendMessage(msg); 3724 } 3725 3726 public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { 3727 Message msg = Message.obtain(); 3728 msg.what = WINDOW_FOCUS_CHANGED; 3729 msg.arg1 = hasFocus ? 1 : 0; 3730 msg.arg2 = inTouchMode ? 1 : 0; 3731 sendMessage(msg); 3732 } 3733 3734 public void dispatchCloseSystemDialogs(String reason) { 3735 Message msg = Message.obtain(); 3736 msg.what = CLOSE_SYSTEM_DIALOGS; 3737 msg.obj = reason; 3738 sendMessage(msg); 3739 } 3740 3741 public void dispatchDragEvent(DragEvent event) { 3742 final int what; 3743 if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) { 3744 what = DISPATCH_DRAG_LOCATION_EVENT; 3745 removeMessages(what); 3746 } else { 3747 what = DISPATCH_DRAG_EVENT; 3748 } 3749 Message msg = obtainMessage(what, event); 3750 sendMessage(msg); 3751 } 3752 3753 public void dispatchSystemUiVisibilityChanged(int visibility) { 3754 sendMessage(obtainMessage(DISPATCH_SYSTEM_UI_VISIBILITY, visibility, 0)); 3755 } 3756 3757 /** 3758 * The window is getting focus so if there is anything focused/selected 3759 * send an {@link AccessibilityEvent} to announce that. 3760 */ 3761 private void sendAccessibilityEvents() { 3762 if (!mAccessibilityManager.isEnabled()) { 3763 return; 3764 } 3765 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 3766 View focusedView = mView.findFocus(); 3767 if (focusedView != null && focusedView != mView) { 3768 focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 3769 } 3770 } 3771 3772 /** 3773 * Post a callback to send a 3774 * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. 3775 * This event is send at most once every 3776 * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}. 3777 */ 3778 private void postSendWindowContentChangedCallback() { 3779 if (mSendWindowContentChangedAccessibilityEvent == null) { 3780 mSendWindowContentChangedAccessibilityEvent = 3781 new SendWindowContentChangedAccessibilityEvent(); 3782 } 3783 if (!mSendWindowContentChangedAccessibilityEvent.mIsPending) { 3784 mSendWindowContentChangedAccessibilityEvent.mIsPending = true; 3785 postDelayed(mSendWindowContentChangedAccessibilityEvent, 3786 ViewConfiguration.getSendRecurringAccessibilityEventsInterval()); 3787 } 3788 } 3789 3790 /** 3791 * Remove a posted callback to send a 3792 * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. 3793 */ 3794 private void removeSendWindowContentChangedCallback() { 3795 if (mSendWindowContentChangedAccessibilityEvent != null) { 3796 removeCallbacks(mSendWindowContentChangedAccessibilityEvent); 3797 } 3798 } 3799 3800 public boolean showContextMenuForChild(View originalView) { 3801 return false; 3802 } 3803 3804 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) { 3805 return null; 3806 } 3807 3808 public void createContextMenu(ContextMenu menu) { 3809 } 3810 3811 public void childDrawableStateChanged(View child) { 3812 } 3813 3814 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { 3815 if (mView == null) { 3816 return false; 3817 } 3818 mAccessibilityManager.sendAccessibilityEvent(event); 3819 return true; 3820 } 3821 3822 void checkThread() { 3823 if (mThread != Thread.currentThread()) { 3824 throw new CalledFromWrongThreadException( 3825 "Only the original thread that created a view hierarchy can touch its views."); 3826 } 3827 } 3828 3829 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 3830 // ViewAncestor never intercepts touch event, so this can be a no-op 3831 } 3832 3833 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, 3834 boolean immediate) { 3835 return scrollToRectOrFocus(rectangle, immediate); 3836 } 3837 3838 class TakenSurfaceHolder extends BaseSurfaceHolder { 3839 @Override 3840 public boolean onAllowLockCanvas() { 3841 return mDrawingAllowed; 3842 } 3843 3844 @Override 3845 public void onRelayoutContainer() { 3846 // Not currently interesting -- from changing between fixed and layout size. 3847 } 3848 3849 public void setFormat(int format) { 3850 ((RootViewSurfaceTaker)mView).setSurfaceFormat(format); 3851 } 3852 3853 public void setType(int type) { 3854 ((RootViewSurfaceTaker)mView).setSurfaceType(type); 3855 } 3856 3857 @Override 3858 public void onUpdateSurface() { 3859 // We take care of format and type changes on our own. 3860 throw new IllegalStateException("Shouldn't be here"); 3861 } 3862 3863 public boolean isCreating() { 3864 return mIsCreating; 3865 } 3866 3867 @Override 3868 public void setFixedSize(int width, int height) { 3869 throw new UnsupportedOperationException( 3870 "Currently only support sizing from layout"); 3871 } 3872 3873 public void setKeepScreenOn(boolean screenOn) { 3874 ((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn); 3875 } 3876 } 3877 3878 static class InputMethodCallback extends IInputMethodCallback.Stub { 3879 private WeakReference<ViewRootImpl> mViewAncestor; 3880 3881 public InputMethodCallback(ViewRootImpl viewAncestor) { 3882 mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor); 3883 } 3884 3885 public void finishedEvent(int seq, boolean handled) { 3886 final ViewRootImpl viewAncestor = mViewAncestor.get(); 3887 if (viewAncestor != null) { 3888 viewAncestor.dispatchFinishedEvent(seq, handled); 3889 } 3890 } 3891 3892 public void sessionCreated(IInputMethodSession session) { 3893 // Stub -- not for use in the client. 3894 } 3895 } 3896 3897 static class W extends IWindow.Stub { 3898 private final WeakReference<ViewRootImpl> mViewAncestor; 3899 3900 W(ViewRootImpl viewAncestor) { 3901 mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor); 3902 } 3903 3904 public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets, 3905 boolean reportDraw, Configuration newConfig) { 3906 final ViewRootImpl viewAncestor = mViewAncestor.get(); 3907 if (viewAncestor != null) { 3908 viewAncestor.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, 3909 newConfig); 3910 } 3911 } 3912 3913 public void dispatchAppVisibility(boolean visible) { 3914 final ViewRootImpl viewAncestor = mViewAncestor.get(); 3915 if (viewAncestor != null) { 3916 viewAncestor.dispatchAppVisibility(visible); 3917 } 3918 } 3919 3920 public void dispatchGetNewSurface() { 3921 final ViewRootImpl viewAncestor = mViewAncestor.get(); 3922 if (viewAncestor != null) { 3923 viewAncestor.dispatchGetNewSurface(); 3924 } 3925 } 3926 3927 public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { 3928 final ViewRootImpl viewAncestor = mViewAncestor.get(); 3929 if (viewAncestor != null) { 3930 viewAncestor.windowFocusChanged(hasFocus, inTouchMode); 3931 } 3932 } 3933 3934 private static int checkCallingPermission(String permission) { 3935 try { 3936 return ActivityManagerNative.getDefault().checkPermission( 3937 permission, Binder.getCallingPid(), Binder.getCallingUid()); 3938 } catch (RemoteException e) { 3939 return PackageManager.PERMISSION_DENIED; 3940 } 3941 } 3942 3943 public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { 3944 final ViewRootImpl viewAncestor = mViewAncestor.get(); 3945 if (viewAncestor != null) { 3946 final View view = viewAncestor.mView; 3947 if (view != null) { 3948 if (checkCallingPermission(Manifest.permission.DUMP) != 3949 PackageManager.PERMISSION_GRANTED) { 3950 throw new SecurityException("Insufficient permissions to invoke" 3951 + " executeCommand() from pid=" + Binder.getCallingPid() 3952 + ", uid=" + Binder.getCallingUid()); 3953 } 3954 3955 OutputStream clientStream = null; 3956 try { 3957 clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out); 3958 ViewDebug.dispatchCommand(view, command, parameters, clientStream); 3959 } catch (IOException e) { 3960 e.printStackTrace(); 3961 } finally { 3962 if (clientStream != null) { 3963 try { 3964 clientStream.close(); 3965 } catch (IOException e) { 3966 e.printStackTrace(); 3967 } 3968 } 3969 } 3970 } 3971 } 3972 } 3973 3974 public void closeSystemDialogs(String reason) { 3975 final ViewRootImpl viewAncestor = mViewAncestor.get(); 3976 if (viewAncestor != null) { 3977 viewAncestor.dispatchCloseSystemDialogs(reason); 3978 } 3979 } 3980 3981 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 3982 boolean sync) { 3983 if (sync) { 3984 try { 3985 sWindowSession.wallpaperOffsetsComplete(asBinder()); 3986 } catch (RemoteException e) { 3987 } 3988 } 3989 } 3990 3991 public void dispatchWallpaperCommand(String action, int x, int y, 3992 int z, Bundle extras, boolean sync) { 3993 if (sync) { 3994 try { 3995 sWindowSession.wallpaperCommandComplete(asBinder(), null); 3996 } catch (RemoteException e) { 3997 } 3998 } 3999 } 4000 4001 /* Drag/drop */ 4002 public void dispatchDragEvent(DragEvent event) { 4003 final ViewRootImpl viewAncestor = mViewAncestor.get(); 4004 if (viewAncestor != null) { 4005 viewAncestor.dispatchDragEvent(event); 4006 } 4007 } 4008 4009 public void dispatchSystemUiVisibilityChanged(int visibility) { 4010 final ViewRootImpl viewAncestor = mViewAncestor.get(); 4011 if (viewAncestor != null) { 4012 viewAncestor.dispatchSystemUiVisibilityChanged(visibility); 4013 } 4014 } 4015 } 4016 4017 /** 4018 * Maintains state information for a single trackball axis, generating 4019 * discrete (DPAD) movements based on raw trackball motion. 4020 */ 4021 static final class TrackballAxis { 4022 /** 4023 * The maximum amount of acceleration we will apply. 4024 */ 4025 static final float MAX_ACCELERATION = 20; 4026 4027 /** 4028 * The maximum amount of time (in milliseconds) between events in order 4029 * for us to consider the user to be doing fast trackball movements, 4030 * and thus apply an acceleration. 4031 */ 4032 static final long FAST_MOVE_TIME = 150; 4033 4034 /** 4035 * Scaling factor to the time (in milliseconds) between events to how 4036 * much to multiple/divide the current acceleration. When movement 4037 * is < FAST_MOVE_TIME this multiplies the acceleration; when > 4038 * FAST_MOVE_TIME it divides it. 4039 */ 4040 static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40); 4041 4042 float position; 4043 float absPosition; 4044 float acceleration = 1; 4045 long lastMoveTime = 0; 4046 int step; 4047 int dir; 4048 int nonAccelMovement; 4049 4050 void reset(int _step) { 4051 position = 0; 4052 acceleration = 1; 4053 lastMoveTime = 0; 4054 step = _step; 4055 dir = 0; 4056 } 4057 4058 /** 4059 * Add trackball movement into the state. If the direction of movement 4060 * has been reversed, the state is reset before adding the 4061 * movement (so that you don't have to compensate for any previously 4062 * collected movement before see the result of the movement in the 4063 * new direction). 4064 * 4065 * @return Returns the absolute value of the amount of movement 4066 * collected so far. 4067 */ 4068 float collect(float off, long time, String axis) { 4069 long normTime; 4070 if (off > 0) { 4071 normTime = (long)(off * FAST_MOVE_TIME); 4072 if (dir < 0) { 4073 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!"); 4074 position = 0; 4075 step = 0; 4076 acceleration = 1; 4077 lastMoveTime = 0; 4078 } 4079 dir = 1; 4080 } else if (off < 0) { 4081 normTime = (long)((-off) * FAST_MOVE_TIME); 4082 if (dir > 0) { 4083 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!"); 4084 position = 0; 4085 step = 0; 4086 acceleration = 1; 4087 lastMoveTime = 0; 4088 } 4089 dir = -1; 4090 } else { 4091 normTime = 0; 4092 } 4093 4094 // The number of milliseconds between each movement that is 4095 // considered "normal" and will not result in any acceleration 4096 // or deceleration, scaled by the offset we have here. 4097 if (normTime > 0) { 4098 long delta = time - lastMoveTime; 4099 lastMoveTime = time; 4100 float acc = acceleration; 4101 if (delta < normTime) { 4102 // The user is scrolling rapidly, so increase acceleration. 4103 float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR; 4104 if (scale > 1) acc *= scale; 4105 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off=" 4106 + off + " normTime=" + normTime + " delta=" + delta 4107 + " scale=" + scale + " acc=" + acc); 4108 acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION; 4109 } else { 4110 // The user is scrolling slowly, so decrease acceleration. 4111 float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR; 4112 if (scale > 1) acc /= scale; 4113 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off=" 4114 + off + " normTime=" + normTime + " delta=" + delta 4115 + " scale=" + scale + " acc=" + acc); 4116 acceleration = acc > 1 ? acc : 1; 4117 } 4118 } 4119 position += off; 4120 return (absPosition = Math.abs(position)); 4121 } 4122 4123 /** 4124 * Generate the number of discrete movement events appropriate for 4125 * the currently collected trackball movement. 4126 * 4127 * @param precision The minimum movement required to generate the 4128 * first discrete movement. 4129 * 4130 * @return Returns the number of discrete movements, either positive 4131 * or negative, or 0 if there is not enough trackball movement yet 4132 * for a discrete movement. 4133 */ 4134 int generate(float precision) { 4135 int movement = 0; 4136 nonAccelMovement = 0; 4137 do { 4138 final int dir = position >= 0 ? 1 : -1; 4139 switch (step) { 4140 // If we are going to execute the first step, then we want 4141 // to do this as soon as possible instead of waiting for 4142 // a full movement, in order to make things look responsive. 4143 case 0: 4144 if (absPosition < precision) { 4145 return movement; 4146 } 4147 movement += dir; 4148 nonAccelMovement += dir; 4149 step = 1; 4150 break; 4151 // If we have generated the first movement, then we need 4152 // to wait for the second complete trackball motion before 4153 // generating the second discrete movement. 4154 case 1: 4155 if (absPosition < 2) { 4156 return movement; 4157 } 4158 movement += dir; 4159 nonAccelMovement += dir; 4160 position += dir > 0 ? -2 : 2; 4161 absPosition = Math.abs(position); 4162 step = 2; 4163 break; 4164 // After the first two, we generate discrete movements 4165 // consistently with the trackball, applying an acceleration 4166 // if the trackball is moving quickly. This is a simple 4167 // acceleration on top of what we already compute based 4168 // on how quickly the wheel is being turned, to apply 4169 // a longer increasing acceleration to continuous movement 4170 // in one direction. 4171 default: 4172 if (absPosition < 1) { 4173 return movement; 4174 } 4175 movement += dir; 4176 position += dir >= 0 ? -1 : 1; 4177 absPosition = Math.abs(position); 4178 float acc = acceleration; 4179 acc *= 1.1f; 4180 acceleration = acc < MAX_ACCELERATION ? acc : acceleration; 4181 break; 4182 } 4183 } while (true); 4184 } 4185 } 4186 4187 public static final class CalledFromWrongThreadException extends AndroidRuntimeException { 4188 public CalledFromWrongThreadException(String msg) { 4189 super(msg); 4190 } 4191 } 4192 4193 private SurfaceHolder mHolder = new SurfaceHolder() { 4194 // we only need a SurfaceHolder for opengl. it would be nice 4195 // to implement everything else though, especially the callback 4196 // support (opengl doesn't make use of it right now, but eventually 4197 // will). 4198 public Surface getSurface() { 4199 return mSurface; 4200 } 4201 4202 public boolean isCreating() { 4203 return false; 4204 } 4205 4206 public void addCallback(Callback callback) { 4207 } 4208 4209 public void removeCallback(Callback callback) { 4210 } 4211 4212 public void setFixedSize(int width, int height) { 4213 } 4214 4215 public void setSizeFromLayout() { 4216 } 4217 4218 public void setFormat(int format) { 4219 } 4220 4221 public void setType(int type) { 4222 } 4223 4224 public void setKeepScreenOn(boolean screenOn) { 4225 } 4226 4227 public Canvas lockCanvas() { 4228 return null; 4229 } 4230 4231 public Canvas lockCanvas(Rect dirty) { 4232 return null; 4233 } 4234 4235 public void unlockCanvasAndPost(Canvas canvas) { 4236 } 4237 public Rect getSurfaceFrame() { 4238 return null; 4239 } 4240 }; 4241 4242 static RunQueue getRunQueue() { 4243 RunQueue rq = sRunQueues.get(); 4244 if (rq != null) { 4245 return rq; 4246 } 4247 rq = new RunQueue(); 4248 sRunQueues.set(rq); 4249 return rq; 4250 } 4251 4252 /** 4253 * @hide 4254 */ 4255 static final class RunQueue { 4256 private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>(); 4257 4258 void post(Runnable action) { 4259 postDelayed(action, 0); 4260 } 4261 4262 void postDelayed(Runnable action, long delayMillis) { 4263 HandlerAction handlerAction = new HandlerAction(); 4264 handlerAction.action = action; 4265 handlerAction.delay = delayMillis; 4266 4267 synchronized (mActions) { 4268 mActions.add(handlerAction); 4269 } 4270 } 4271 4272 void removeCallbacks(Runnable action) { 4273 final HandlerAction handlerAction = new HandlerAction(); 4274 handlerAction.action = action; 4275 4276 synchronized (mActions) { 4277 final ArrayList<HandlerAction> actions = mActions; 4278 4279 while (actions.remove(handlerAction)) { 4280 // Keep going 4281 } 4282 } 4283 } 4284 4285 void executeActions(Handler handler) { 4286 synchronized (mActions) { 4287 final ArrayList<HandlerAction> actions = mActions; 4288 final int count = actions.size(); 4289 4290 for (int i = 0; i < count; i++) { 4291 final HandlerAction handlerAction = actions.get(i); 4292 handler.postDelayed(handlerAction.action, handlerAction.delay); 4293 } 4294 4295 actions.clear(); 4296 } 4297 } 4298 4299 private static class HandlerAction { 4300 Runnable action; 4301 long delay; 4302 4303 @Override 4304 public boolean equals(Object o) { 4305 if (this == o) return true; 4306 if (o == null || getClass() != o.getClass()) return false; 4307 4308 HandlerAction that = (HandlerAction) o; 4309 return !(action != null ? !action.equals(that.action) : that.action != null); 4310 4311 } 4312 4313 @Override 4314 public int hashCode() { 4315 int result = action != null ? action.hashCode() : 0; 4316 result = 31 * result + (int) (delay ^ (delay >>> 32)); 4317 return result; 4318 } 4319 } 4320 } 4321 4322 /** 4323 * Class for managing the accessibility interaction connection 4324 * based on the global accessibility state. 4325 */ 4326 final class AccessibilityInteractionConnectionManager 4327 implements AccessibilityStateChangeListener { 4328 public void onAccessibilityStateChanged(boolean enabled) { 4329 if (enabled) { 4330 ensureConnection(); 4331 } else { 4332 ensureNoConnection(); 4333 } 4334 } 4335 4336 public void ensureConnection() { 4337 final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID; 4338 if (!registered) { 4339 mAttachInfo.mAccessibilityWindowId = 4340 mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, 4341 new AccessibilityInteractionConnection(ViewRootImpl.this)); 4342 } 4343 } 4344 4345 public void ensureNoConnection() { 4346 final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID; 4347 if (registered) { 4348 mAttachInfo.mAccessibilityWindowId = View.NO_ID; 4349 mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow); 4350 } 4351 } 4352 } 4353 4354 /** 4355 * This class is an interface this ViewAncestor provides to the 4356 * AccessibilityManagerService to the latter can interact with 4357 * the view hierarchy in this ViewAncestor. 4358 */ 4359 final class AccessibilityInteractionConnection 4360 extends IAccessibilityInteractionConnection.Stub { 4361 private final WeakReference<ViewRootImpl> mViewAncestor; 4362 4363 AccessibilityInteractionConnection(ViewRootImpl viewAncestor) { 4364 mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor); 4365 } 4366 4367 public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId, 4368 int interactionId, IAccessibilityInteractionConnectionCallback callback) { 4369 if (mViewAncestor.get() != null) { 4370 getAccessibilityInteractionController() 4371 .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityId, 4372 interactionId, callback); 4373 } 4374 } 4375 4376 public void performAccessibilityAction(int accessibilityId, int action, 4377 int interactionId, IAccessibilityInteractionConnectionCallback callback) { 4378 if (mViewAncestor.get() != null) { 4379 getAccessibilityInteractionController() 4380 .performAccessibilityActionClientThread(accessibilityId, action, interactionId, 4381 callback); 4382 } 4383 } 4384 4385 public void findAccessibilityNodeInfoByViewId(int viewId, 4386 int interactionId, IAccessibilityInteractionConnectionCallback callback) { 4387 if (mViewAncestor.get() != null) { 4388 getAccessibilityInteractionController() 4389 .findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback); 4390 } 4391 } 4392 4393 public void findAccessibilityNodeInfosByViewText(String text, int accessibilityId, 4394 int interactionId, IAccessibilityInteractionConnectionCallback callback) { 4395 if (mViewAncestor.get() != null) { 4396 getAccessibilityInteractionController() 4397 .findAccessibilityNodeInfosByViewTextClientThread(text, accessibilityId, 4398 interactionId, callback); 4399 } 4400 } 4401 } 4402 4403 /** 4404 * Class for managing accessibility interactions initiated from the system 4405 * and targeting the view hierarchy. A *ClientThread method is to be 4406 * called from the interaction connection this ViewAncestor gives the 4407 * system to talk to it and a corresponding *UiThread method that is executed 4408 * on the UI thread. 4409 */ 4410 final class AccessibilityInteractionController { 4411 private static final int POOL_SIZE = 5; 4412 4413 private FindByAccessibilitytIdPredicate mFindByAccessibilityIdPredicate = 4414 new FindByAccessibilitytIdPredicate(); 4415 4416 private ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList = 4417 new ArrayList<AccessibilityNodeInfo>(); 4418 4419 // Reusable poolable arguments for interacting with the view hierarchy 4420 // to fit more arguments than Message and to avoid sharing objects between 4421 // two messages since several threads can send messages concurrently. 4422 private final Pool<SomeArgs> mPool = Pools.synchronizedPool(Pools.finitePool( 4423 new PoolableManager<SomeArgs>() { 4424 public SomeArgs newInstance() { 4425 return new SomeArgs(); 4426 } 4427 4428 public void onAcquired(SomeArgs info) { 4429 /* do nothing */ 4430 } 4431 4432 public void onReleased(SomeArgs info) { 4433 info.clear(); 4434 } 4435 }, POOL_SIZE) 4436 ); 4437 4438 public class SomeArgs implements Poolable<SomeArgs> { 4439 private SomeArgs mNext; 4440 private boolean mIsPooled; 4441 4442 public Object arg1; 4443 public Object arg2; 4444 public int argi1; 4445 public int argi2; 4446 public int argi3; 4447 4448 public SomeArgs getNextPoolable() { 4449 return mNext; 4450 } 4451 4452 public boolean isPooled() { 4453 return mIsPooled; 4454 } 4455 4456 public void setNextPoolable(SomeArgs args) { 4457 mNext = args; 4458 } 4459 4460 public void setPooled(boolean isPooled) { 4461 mIsPooled = isPooled; 4462 } 4463 4464 private void clear() { 4465 arg1 = null; 4466 arg2 = null; 4467 argi1 = 0; 4468 argi2 = 0; 4469 argi3 = 0; 4470 } 4471 } 4472 4473 public void findAccessibilityNodeInfoByAccessibilityIdClientThread(int accessibilityId, 4474 int interactionId, IAccessibilityInteractionConnectionCallback callback) { 4475 Message message = Message.obtain(); 4476 message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID; 4477 message.arg1 = accessibilityId; 4478 message.arg2 = interactionId; 4479 message.obj = callback; 4480 sendMessage(message); 4481 } 4482 4483 public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) { 4484 final int accessibilityId = message.arg1; 4485 final int interactionId = message.arg2; 4486 final IAccessibilityInteractionConnectionCallback callback = 4487 (IAccessibilityInteractionConnectionCallback) message.obj; 4488 4489 AccessibilityNodeInfo info = null; 4490 try { 4491 FindByAccessibilitytIdPredicate predicate = mFindByAccessibilityIdPredicate; 4492 predicate.init(accessibilityId); 4493 View root = ViewRootImpl.this.mView; 4494 View target = root.findViewByPredicate(predicate); 4495 if (target != null && target.isShown()) { 4496 info = target.createAccessibilityNodeInfo(); 4497 } 4498 } finally { 4499 try { 4500 callback.setFindAccessibilityNodeInfoResult(info, interactionId); 4501 } catch (RemoteException re) { 4502 /* ignore - the other side will time out */ 4503 } 4504 } 4505 } 4506 4507 public void findAccessibilityNodeInfoByViewIdClientThread(int viewId, int interactionId, 4508 IAccessibilityInteractionConnectionCallback callback) { 4509 Message message = Message.obtain(); 4510 message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID; 4511 message.arg1 = viewId; 4512 message.arg2 = interactionId; 4513 message.obj = callback; 4514 sendMessage(message); 4515 } 4516 4517 public void findAccessibilityNodeInfoByViewIdUiThread(Message message) { 4518 final int viewId = message.arg1; 4519 final int interactionId = message.arg2; 4520 final IAccessibilityInteractionConnectionCallback callback = 4521 (IAccessibilityInteractionConnectionCallback) message.obj; 4522 4523 AccessibilityNodeInfo info = null; 4524 try { 4525 View root = ViewRootImpl.this.mView; 4526 View target = root.findViewById(viewId); 4527 if (target != null && target.isShown()) { 4528 info = target.createAccessibilityNodeInfo(); 4529 } 4530 } finally { 4531 try { 4532 callback.setFindAccessibilityNodeInfoResult(info, interactionId); 4533 } catch (RemoteException re) { 4534 /* ignore - the other side will time out */ 4535 } 4536 } 4537 } 4538 4539 public void findAccessibilityNodeInfosByViewTextClientThread(String text, 4540 int accessibilityViewId, int interactionId, 4541 IAccessibilityInteractionConnectionCallback callback) { 4542 Message message = Message.obtain(); 4543 message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT; 4544 SomeArgs args = mPool.acquire(); 4545 args.arg1 = text; 4546 args.argi1 = accessibilityViewId; 4547 args.argi2 = interactionId; 4548 args.arg2 = callback; 4549 message.obj = args; 4550 sendMessage(message); 4551 } 4552 4553 public void findAccessibilityNodeInfosByViewTextUiThread(Message message) { 4554 SomeArgs args = (SomeArgs) message.obj; 4555 final String text = (String) args.arg1; 4556 final int accessibilityViewId = args.argi1; 4557 final int interactionId = args.argi2; 4558 final IAccessibilityInteractionConnectionCallback callback = 4559 (IAccessibilityInteractionConnectionCallback) args.arg2; 4560 mPool.release(args); 4561 4562 List<AccessibilityNodeInfo> infos = null; 4563 try { 4564 ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList; 4565 foundViews.clear(); 4566 4567 View root; 4568 if (accessibilityViewId != View.NO_ID) { 4569 root = findViewByAccessibilityId(accessibilityViewId); 4570 } else { 4571 root = ViewRootImpl.this.mView; 4572 } 4573 4574 if (root == null || !root.isShown()) { 4575 return; 4576 } 4577 4578 root.findViewsWithText(foundViews, text); 4579 if (foundViews.isEmpty()) { 4580 return; 4581 } 4582 4583 infos = mTempAccessibilityNodeInfoList; 4584 infos.clear(); 4585 4586 final int viewCount = foundViews.size(); 4587 for (int i = 0; i < viewCount; i++) { 4588 View foundView = foundViews.get(i); 4589 if (foundView.isShown()) { 4590 infos.add(foundView.createAccessibilityNodeInfo()); 4591 } 4592 } 4593 } finally { 4594 try { 4595 callback.setFindAccessibilityNodeInfosResult(infos, interactionId); 4596 } catch (RemoteException re) { 4597 /* ignore - the other side will time out */ 4598 } 4599 } 4600 } 4601 4602 public void performAccessibilityActionClientThread(int accessibilityId, int action, 4603 int interactionId, IAccessibilityInteractionConnectionCallback callback) { 4604 Message message = Message.obtain(); 4605 message.what = DO_PERFORM_ACCESSIBILITY_ACTION; 4606 SomeArgs args = mPool.acquire(); 4607 args.argi1 = accessibilityId; 4608 args.argi2 = action; 4609 args.argi3 = interactionId; 4610 args.arg1 = callback; 4611 message.obj = args; 4612 sendMessage(message); 4613 } 4614 4615 public void perfromAccessibilityActionUiThread(Message message) { 4616 SomeArgs args = (SomeArgs) message.obj; 4617 final int accessibilityId = args.argi1; 4618 final int action = args.argi2; 4619 final int interactionId = args.argi3; 4620 final IAccessibilityInteractionConnectionCallback callback = 4621 (IAccessibilityInteractionConnectionCallback) args.arg1; 4622 mPool.release(args); 4623 4624 boolean succeeded = false; 4625 try { 4626 switch (action) { 4627 case AccessibilityNodeInfo.ACTION_FOCUS: { 4628 succeeded = performActionFocus(accessibilityId); 4629 } break; 4630 case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: { 4631 succeeded = performActionClearFocus(accessibilityId); 4632 } break; 4633 case AccessibilityNodeInfo.ACTION_SELECT: { 4634 succeeded = performActionSelect(accessibilityId); 4635 } break; 4636 case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: { 4637 succeeded = performActionClearSelection(accessibilityId); 4638 } break; 4639 } 4640 } finally { 4641 try { 4642 callback.setPerformAccessibilityActionResult(succeeded, interactionId); 4643 } catch (RemoteException re) { 4644 /* ignore - the other side will time out */ 4645 } 4646 } 4647 } 4648 4649 private boolean performActionFocus(int accessibilityId) { 4650 View target = findViewByAccessibilityId(accessibilityId); 4651 if (target == null) { 4652 return false; 4653 } 4654 // Get out of touch mode since accessibility wants to move focus around. 4655 ensureTouchMode(false); 4656 return target.requestFocus(); 4657 } 4658 4659 private boolean performActionClearFocus(int accessibilityId) { 4660 View target = findViewByAccessibilityId(accessibilityId); 4661 if (target == null) { 4662 return false; 4663 } 4664 if (!target.isFocused()) { 4665 return false; 4666 } 4667 target.clearFocus(); 4668 return !target.isFocused(); 4669 } 4670 4671 private boolean performActionSelect(int accessibilityId) { 4672 View target = findViewByAccessibilityId(accessibilityId); 4673 if (target == null) { 4674 return false; 4675 } 4676 if (target.isSelected()) { 4677 return false; 4678 } 4679 target.setSelected(true); 4680 return target.isSelected(); 4681 } 4682 4683 private boolean performActionClearSelection(int accessibilityId) { 4684 View target = findViewByAccessibilityId(accessibilityId); 4685 if (target == null) { 4686 return false; 4687 } 4688 if (!target.isSelected()) { 4689 return false; 4690 } 4691 target.setSelected(false); 4692 return !target.isSelected(); 4693 } 4694 4695 private View findViewByAccessibilityId(int accessibilityId) { 4696 View root = ViewRootImpl.this.mView; 4697 if (root == null) { 4698 return null; 4699 } 4700 mFindByAccessibilityIdPredicate.init(accessibilityId); 4701 View foundView = root.findViewByPredicate(mFindByAccessibilityIdPredicate); 4702 return (foundView != null && foundView.isShown()) ? foundView : null; 4703 } 4704 4705 private final class FindByAccessibilitytIdPredicate implements Predicate<View> { 4706 public int mSerchedId; 4707 4708 public void init(int searchedId) { 4709 mSerchedId = searchedId; 4710 } 4711 4712 public boolean apply(View view) { 4713 return (view.getAccessibilityViewId() == mSerchedId); 4714 } 4715 } 4716 } 4717 4718 private class SendWindowContentChangedAccessibilityEvent implements Runnable { 4719 public volatile boolean mIsPending; 4720 4721 public void run() { 4722 if (mView != null) { 4723 // Check again for accessibility state since this is executed delayed. 4724 AccessibilityManager accessibilityManager = 4725 AccessibilityManager.getInstance(mView.mContext); 4726 if (accessibilityManager.isEnabled()) { 4727 // Send the event directly since we do not want to append the 4728 // source text because this is the text for the entire window 4729 // and we just want to notify that the content has changed. 4730 AccessibilityEvent event = AccessibilityEvent.obtain( 4731 AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 4732 mView.onInitializeAccessibilityEvent(event); 4733 accessibilityManager.sendAccessibilityEvent(event); 4734 } 4735 mIsPending = false; 4736 } 4737 } 4738 } 4739} 4740