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