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