From 6dd005b48138708762bfade0081d031a2a4a3822 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 18 Jul 2011 13:22:50 -0700 Subject: I. Can. Not. Stand. ViewAncestor. It was done so we would have the name "ViewRoot" available for a public API. However, the name "ViewAncestor" just makes no sense. So instead, change it to ViewRootImpl. Change-Id: If9599ca67896f339f6fefa7d1dde121201171d97 --- core/java/android/app/ActivityThread.java | 6 +- core/java/android/app/WallpaperManager.java | 8 +- .../service/wallpaper/WallpaperService.java | 4 +- core/java/android/view/SurfaceView.java | 2 +- core/java/android/view/View.java | 22 +- core/java/android/view/ViewAncestor.java | 4670 -------------------- core/java/android/view/ViewDebug.java | 8 +- core/java/android/view/ViewGroup.java | 18 +- core/java/android/view/ViewRootImpl.java | 4670 ++++++++++++++++++++ core/java/android/view/WindowManagerImpl.java | 28 +- .../view/inputmethod/BaseInputConnection.java | 4 +- .../view/inputmethod/InputMethodManager.java | 8 +- core/java/android/webkit/BrowserFrame.java | 4 +- core/java/android/widget/TextView.java | 6 +- .../java/android/widget/ZoomButtonsController.java | 10 +- 15 files changed, 4734 insertions(+), 4734 deletions(-) delete mode 100644 core/java/android/view/ViewAncestor.java create mode 100644 core/java/android/view/ViewRootImpl.java (limited to 'core/java/android') diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 86161da..f6cd866 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -70,7 +70,7 @@ import android.view.HardwareRenderer; import android.view.View; import android.view.ViewDebug; import android.view.ViewManager; -import android.view.ViewAncestor; +import android.view.ViewRootImpl; import android.view.Window; import android.view.WindowManager; import android.view.WindowManagerImpl; @@ -4072,7 +4072,7 @@ public final class ActivityThread { sThreadLocal.set(this); mSystemThread = system; if (!system) { - ViewAncestor.addFirstDrawHandler(new Runnable() { + ViewRootImpl.addFirstDrawHandler(new Runnable() { public void run() { ensureJitEnabled(); } @@ -4102,7 +4102,7 @@ public final class ActivityThread { } } - ViewAncestor.addConfigCallback(new ComponentCallbacks() { + ViewRootImpl.addConfigCallback(new ComponentCallbacks() { public void onConfigurationChanged(Configuration newConfig) { synchronized (mPackages) { // We need to apply this change to the resources diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 7fd5a7d..8472b31 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -40,7 +40,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.DisplayMetrics; import android.util.Log; -import android.view.ViewAncestor; +import android.view.ViewRootImpl; import java.io.FileOutputStream; import java.io.IOException; @@ -592,7 +592,7 @@ public class WallpaperManager { public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) { try { //Log.v(TAG, "Sending new wallpaper offsets from app..."); - ViewAncestor.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( + ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep); //Log.v(TAG, "...app returning after sending offsets!"); } catch (RemoteException e) { @@ -630,7 +630,7 @@ public class WallpaperManager { int x, int y, int z, Bundle extras) { try { //Log.v(TAG, "Sending new wallpaper offsets from app..."); - ViewAncestor.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand( + ViewRootImpl.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand( windowToken, action, x, y, z, extras, false); //Log.v(TAG, "...app returning after sending offsets!"); } catch (RemoteException e) { @@ -650,7 +650,7 @@ public class WallpaperManager { */ public void clearWallpaperOffsets(IBinder windowToken) { try { - ViewAncestor.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( + ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( windowToken, -1, -1, -1, -1); } catch (RemoteException e) { // Ignore. diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 8fc8b9d..c51ba2a 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -51,7 +51,7 @@ import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; -import android.view.ViewAncestor; +import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.WindowManagerPolicy; @@ -650,7 +650,7 @@ public abstract class WallpaperService extends Service { mWindowToken = wrapper.mWindowToken; mSurfaceHolder.setSizeFromLayout(); mInitializing = true; - mSession = ViewAncestor.getWindowSession(getMainLooper()); + mSession = ViewRootImpl.getWindowSession(getMainLooper()); mWindow.setSession(mSession); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 764899f..cbdb38e 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -426,7 +426,7 @@ public class SurfaceView extends View { if (!mHaveFrame) { return; } - ViewAncestor viewRoot = (ViewAncestor) getRootView().getParent(); + ViewRootImpl viewRoot = (ViewRootImpl) getRootView().getParent(); if (viewRoot != null) { mTranslator = viewRoot.mTranslator; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4385c2f..4e9c0b7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5043,9 +5043,9 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } /** Gets the ViewAncestor, or null if not attached. */ - /*package*/ ViewAncestor getViewAncestor() { + /*package*/ ViewRootImpl getViewRootImpl() { View root = getRootView(); - return root != null ? (ViewAncestor)root.getParent() : null; + return root != null ? (ViewRootImpl)root.getParent() : null; } /** @@ -5061,7 +5061,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit public final boolean requestFocusFromTouch() { // Leave touch mode if we need to if (isInTouchMode()) { - ViewAncestor viewRoot = getViewAncestor(); + ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null) { viewRoot.ensureTouchMode(false); } @@ -5653,7 +5653,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit if (mAttachInfo != null) { return mAttachInfo.mInTouchMode; } else { - return ViewAncestor.isInTouchMode(); + return ViewRootImpl.isInTouchMode(); } } @@ -8254,7 +8254,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit handler = attachInfo.mHandler; } else { // Assume that post will succeed later - ViewAncestor.getRunQueue().post(action); + ViewRootImpl.getRunQueue().post(action); return true; } @@ -8284,7 +8284,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit handler = attachInfo.mHandler; } else { // Assume that post will succeed later - ViewAncestor.getRunQueue().postDelayed(action, delayMillis); + ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); return true; } @@ -8308,7 +8308,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit handler = attachInfo.mHandler; } else { // Assume that post will succeed later - ViewAncestor.getRunQueue().removeCallbacks(action); + ViewRootImpl.getRunQueue().removeCallbacks(action); return true; } @@ -11580,9 +11580,9 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit viewParent = view.mParent; } - if (viewParent instanceof ViewAncestor) { + if (viewParent instanceof ViewRootImpl) { // *cough* - final ViewAncestor vr = (ViewAncestor)viewParent; + final ViewRootImpl vr = (ViewRootImpl)viewParent; location[1] -= vr.mCurScrollY; } } @@ -12709,7 +12709,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit surface.unlockCanvasAndPost(canvas); } - final ViewAncestor root = getViewAncestor(); + final ViewRootImpl root = getViewRootImpl(); // Cache the local state object for delivery with DragEvents root.setLocalDragState(myLocalState); @@ -13916,7 +13916,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit Canvas mCanvas; /** - * A Handler supplied by a view's {@link android.view.ViewAncestor}. This + * A Handler supplied by a view's {@link android.view.ViewRootImpl}. This * handler can be used to pump events in the UI events queue. */ final Handler mHandler; diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java deleted file mode 100644 index ac73611..0000000 --- a/core/java/android/view/ViewAncestor.java +++ /dev/null @@ -1,4670 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.view; - -import android.Manifest; -import android.animation.LayoutTransition; -import android.app.ActivityManagerNative; -import android.content.ClipDescription; -import android.content.ComponentCallbacks; -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.res.CompatibilityInfo; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.Point; -import android.graphics.PointF; -import android.graphics.PorterDuff; -import android.graphics.Rect; -import android.graphics.Region; -import android.media.AudioManager; -import android.os.Binder; -import android.os.Bundle; -import android.os.Debug; -import android.os.Handler; -import android.os.LatencyTimer; -import android.os.Looper; -import android.os.Message; -import android.os.ParcelFileDescriptor; -import android.os.Process; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.util.AndroidRuntimeException; -import android.util.DisplayMetrics; -import android.util.EventLog; -import android.util.Log; -import android.util.Pool; -import android.util.Poolable; -import android.util.PoolableManager; -import android.util.Pools; -import android.util.Slog; -import android.util.SparseArray; -import android.util.TypedValue; -import android.view.View.MeasureSpec; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.accessibility.IAccessibilityInteractionConnection; -import android.view.accessibility.IAccessibilityInteractionConnectionCallback; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.Interpolator; -import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodManager; -import android.widget.Scroller; - -import com.android.internal.policy.PolicyManager; -import com.android.internal.util.Predicate; -import com.android.internal.view.BaseSurfaceHolder; -import com.android.internal.view.IInputMethodCallback; -import com.android.internal.view.IInputMethodSession; -import com.android.internal.view.RootViewSurfaceTaker; - -import java.io.IOException; -import java.io.OutputStream; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; - -/** - * The top of a view hierarchy, implementing the needed protocol between View - * and the WindowManager. This is for the most part an internal implementation - * detail of {@link WindowManagerImpl}. - * - * {@hide} - */ -@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"}) -public final class ViewAncestor extends Handler implements ViewParent, - View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { - private static final String TAG = "ViewAncestor"; - private static final boolean DBG = false; - private static final boolean LOCAL_LOGV = false; - /** @noinspection PointlessBooleanExpression*/ - private static final boolean DEBUG_DRAW = false || LOCAL_LOGV; - private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV; - private static final boolean DEBUG_DIALOG = false || LOCAL_LOGV; - private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV; - private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV; - private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV; - private static final boolean DEBUG_IMF = false || LOCAL_LOGV; - private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV; - private static final boolean WATCH_POINTER = false; - - /** - * Set this system property to true to force the view hierarchy to render - * at 60 Hz. This can be used to measure the potential framerate. - */ - private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering"; - - private static final boolean MEASURE_LATENCY = false; - private static LatencyTimer lt; - - /** - * Maximum time we allow the user to roll the trackball enough to generate - * a key event, before resetting the counters. - */ - static final int MAX_TRACKBALL_DELAY = 250; - - static IWindowSession sWindowSession; - - static final Object mStaticInit = new Object(); - static boolean mInitialized = false; - - static final ThreadLocal sRunQueues = new ThreadLocal(); - - static final ArrayList sFirstDrawHandlers = new ArrayList(); - static boolean sFirstDrawComplete = false; - - static final ArrayList sConfigCallbacks - = new ArrayList(); - - long mLastTrackballTime = 0; - final TrackballAxis mTrackballAxisX = new TrackballAxis(); - final TrackballAxis mTrackballAxisY = new TrackballAxis(); - - int mLastJoystickXDirection; - int mLastJoystickYDirection; - int mLastJoystickXKeyCode; - int mLastJoystickYKeyCode; - - final int[] mTmpLocation = new int[2]; - - final TypedValue mTmpValue = new TypedValue(); - - final InputMethodCallback mInputMethodCallback; - final SparseArray mPendingEvents = new SparseArray(); - int mPendingEventSeq = 0; - - final Thread mThread; - - final WindowLeaked mLocation; - - final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams(); - - final W mWindow; - - View mView; - View mFocusedView; - View mRealFocusedView; // this is not set to null in touch mode - int mViewVisibility; - boolean mAppVisible = true; - - // Set to true if the owner of this window is in the stopped state, - // so the window should no longer be active. - boolean mStopped = false; - - boolean mLastInCompatMode = false; - - SurfaceHolder.Callback2 mSurfaceHolderCallback; - BaseSurfaceHolder mSurfaceHolder; - boolean mIsCreating; - boolean mDrawingAllowed; - - final Region mTransparentRegion; - final Region mPreviousTransparentRegion; - - int mWidth; - int mHeight; - Rect mDirty; - final Rect mCurrentDirty = new Rect(); - final Rect mPreviousDirty = new Rect(); - boolean mIsAnimating; - - CompatibilityInfo.Translator mTranslator; - - final View.AttachInfo mAttachInfo; - InputChannel mInputChannel; - InputQueue.Callback mInputQueueCallback; - InputQueue mInputQueue; - FallbackEventHandler mFallbackEventHandler; - - final Rect mTempRect; // used in the transaction to not thrash the heap. - final Rect mVisRect; // used to retrieve visible rect of focused view. - - boolean mTraversalScheduled; - long mLastTraversalFinishedTimeNanos; - long mLastDrawDurationNanos; - boolean mWillDrawSoon; - boolean mLayoutRequested; - boolean mFirst; - boolean mReportNextDraw; - boolean mFullRedrawNeeded; - boolean mNewSurfaceNeeded; - boolean mHasHadWindowFocus; - boolean mLastWasImTarget; - - boolean mWindowAttributesChanged = false; - - // These can be accessed by any thread, must be protected with a lock. - // Surface can never be reassigned or cleared (use Surface.clear()). - private final Surface mSurface = new Surface(); - - boolean mAdded; - boolean mAddedTouchMode; - - CompatibilityInfoHolder mCompatibilityInfo; - - /*package*/ int mAddNesting; - - // These are accessed by multiple threads. - final Rect mWinFrame; // frame given by window manager. - - final Rect mPendingVisibleInsets = new Rect(); - final Rect mPendingContentInsets = new Rect(); - final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets - = new ViewTreeObserver.InternalInsetsInfo(); - - final Configuration mLastConfiguration = new Configuration(); - final Configuration mPendingConfiguration = new Configuration(); - - class ResizedInfo { - Rect coveredInsets; - Rect visibleInsets; - Configuration newConfig; - } - - boolean mScrollMayChange; - int mSoftInputMode; - View mLastScrolledFocus; - int mScrollY; - int mCurScrollY; - Scroller mScroller; - HardwareLayer mResizeBuffer; - long mResizeBufferStartTime; - int mResizeBufferDuration; - static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator(); - private ArrayList mPendingTransitions; - - final ViewConfiguration mViewConfiguration; - - /* Drag/drop */ - ClipDescription mDragDescription; - View mCurrentDragView; - volatile Object mLocalDragState; - final PointF mDragPoint = new PointF(); - final PointF mLastTouchPoint = new PointF(); - - private boolean mProfileRendering; - private Thread mRenderProfiler; - private volatile boolean mRenderProfilingEnabled; - - /** - * see {@link #playSoundEffect(int)} - */ - AudioManager mAudioManager; - - final AccessibilityManager mAccessibilityManager; - - AccessibilityInteractionController mAccessibilityInteractionContrtoller; - - AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager; - - SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent; - - private final int mDensity; - - /** - * Consistency verifier for debugging purposes. - */ - protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier = - InputEventConsistencyVerifier.isInstrumentationEnabled() ? - new InputEventConsistencyVerifier(this, 0) : null; - - public static IWindowSession getWindowSession(Looper mainLooper) { - synchronized (mStaticInit) { - if (!mInitialized) { - try { - InputMethodManager imm = InputMethodManager.getInstance(mainLooper); - sWindowSession = Display.getWindowManager().openSession( - imm.getClient(), imm.getInputContext()); - mInitialized = true; - } catch (RemoteException e) { - } - } - return sWindowSession; - } - } - - public ViewAncestor(Context context) { - super(); - - if (MEASURE_LATENCY) { - if (lt == null) { - lt = new LatencyTimer(100, 1000); - } - } - - // Initialize the statics when this class is first instantiated. This is - // done here instead of in the static block because Zygote does not - // allow the spawning of threads. - getWindowSession(context.getMainLooper()); - - mThread = Thread.currentThread(); - mLocation = new WindowLeaked(null); - mLocation.fillInStackTrace(); - mWidth = -1; - mHeight = -1; - mDirty = new Rect(); - mTempRect = new Rect(); - mVisRect = new Rect(); - mWinFrame = new Rect(); - mWindow = new W(this); - mInputMethodCallback = new InputMethodCallback(this); - mViewVisibility = View.GONE; - mTransparentRegion = new Region(); - mPreviousTransparentRegion = new Region(); - mFirst = true; // true for the first time the view is added - mAdded = false; - mAccessibilityManager = AccessibilityManager.getInstance(context); - mAccessibilityInteractionConnectionManager = - new AccessibilityInteractionConnectionManager(); - mAccessibilityManager.addAccessibilityStateChangeListener( - mAccessibilityInteractionConnectionManager); - mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this); - mViewConfiguration = ViewConfiguration.get(context); - mDensity = context.getResources().getDisplayMetrics().densityDpi; - mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context); - mProfileRendering = Boolean.parseBoolean( - SystemProperties.get(PROPERTY_PROFILE_RENDERING, "false")); - } - - public static void addFirstDrawHandler(Runnable callback) { - synchronized (sFirstDrawHandlers) { - if (!sFirstDrawComplete) { - sFirstDrawHandlers.add(callback); - } - } - } - - public static void addConfigCallback(ComponentCallbacks callback) { - synchronized (sConfigCallbacks) { - sConfigCallbacks.add(callback); - } - } - - // FIXME for perf testing only - private boolean mProfile = false; - - /** - * Call this to profile the next traversal call. - * FIXME for perf testing only. Remove eventually - */ - public void profile() { - mProfile = true; - } - - /** - * Indicates whether we are in touch mode. Calling this method triggers an IPC - * call and should be avoided whenever possible. - * - * @return True, if the device is in touch mode, false otherwise. - * - * @hide - */ - static boolean isInTouchMode() { - if (mInitialized) { - try { - return sWindowSession.getInTouchMode(); - } catch (RemoteException e) { - } - } - return false; - } - - /** - * We have one child - */ - public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { - synchronized (this) { - if (mView == null) { - mView = view; - mFallbackEventHandler.setView(view); - mWindowAttributes.copyFrom(attrs); - attrs = mWindowAttributes; - - if (view instanceof RootViewSurfaceTaker) { - mSurfaceHolderCallback = - ((RootViewSurfaceTaker)view).willYouTakeTheSurface(); - if (mSurfaceHolderCallback != null) { - mSurfaceHolder = new TakenSurfaceHolder(); - mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); - } - } - - // If the application owns the surface, don't enable hardware acceleration - if (mSurfaceHolder == null) { - enableHardwareAcceleration(attrs); - } - - CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get(); - mTranslator = compatibilityInfo.getTranslator(); - - if (mTranslator != null) { - mSurface.setCompatibilityTranslator(mTranslator); - } - - boolean restore = false; - if (mTranslator != null) { - restore = true; - attrs.backup(); - mTranslator.translateWindowLayout(attrs); - } - if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs); - - if (!compatibilityInfo.supportsScreen()) { - attrs.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; - mLastInCompatMode = true; - } - - mSoftInputMode = attrs.softInputMode; - mWindowAttributesChanged = true; - mAttachInfo.mRootView = view; - mAttachInfo.mScalingRequired = mTranslator != null; - mAttachInfo.mApplicationScale = - mTranslator == null ? 1.0f : mTranslator.applicationScale; - if (panelParentView != null) { - mAttachInfo.mPanelParentWindowToken - = panelParentView.getApplicationWindowToken(); - } - mAdded = true; - int res; /* = WindowManagerImpl.ADD_OKAY; */ - - // Schedule the first layout -before- adding to the window - // manager, to make sure we do the relayout before receiving - // any other events from the system. - requestLayout(); - mInputChannel = new InputChannel(); - try { - res = sWindowSession.add(mWindow, mWindowAttributes, - getHostVisibility(), mAttachInfo.mContentInsets, - mInputChannel); - } catch (RemoteException e) { - mAdded = false; - mView = null; - mAttachInfo.mRootView = null; - mInputChannel = null; - mFallbackEventHandler.setView(null); - unscheduleTraversals(); - throw new RuntimeException("Adding window failed", e); - } finally { - if (restore) { - attrs.restore(); - } - } - - if (mTranslator != null) { - mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets); - } - mPendingContentInsets.set(mAttachInfo.mContentInsets); - mPendingVisibleInsets.set(0, 0, 0, 0); - if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow); - if (res < WindowManagerImpl.ADD_OKAY) { - mView = null; - mAttachInfo.mRootView = null; - mAdded = false; - mFallbackEventHandler.setView(null); - unscheduleTraversals(); - switch (res) { - case WindowManagerImpl.ADD_BAD_APP_TOKEN: - case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN: - throw new WindowManagerImpl.BadTokenException( - "Unable to add window -- token " + attrs.token - + " is not valid; is your activity running?"); - case WindowManagerImpl.ADD_NOT_APP_TOKEN: - throw new WindowManagerImpl.BadTokenException( - "Unable to add window -- token " + attrs.token - + " is not for an application"); - case WindowManagerImpl.ADD_APP_EXITING: - throw new WindowManagerImpl.BadTokenException( - "Unable to add window -- app for token " + attrs.token - + " is exiting"); - case WindowManagerImpl.ADD_DUPLICATE_ADD: - throw new WindowManagerImpl.BadTokenException( - "Unable to add window -- window " + mWindow - + " has already been added"); - case WindowManagerImpl.ADD_STARTING_NOT_NEEDED: - // Silently ignore -- we would have just removed it - // right away, anyway. - return; - case WindowManagerImpl.ADD_MULTIPLE_SINGLETON: - throw new WindowManagerImpl.BadTokenException( - "Unable to add window " + mWindow + - " -- another window of this type already exists"); - case WindowManagerImpl.ADD_PERMISSION_DENIED: - throw new WindowManagerImpl.BadTokenException( - "Unable to add window " + mWindow + - " -- permission denied for this window type"); - } - throw new RuntimeException( - "Unable to add window -- unknown error code " + res); - } - - if (view instanceof RootViewSurfaceTaker) { - mInputQueueCallback = - ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); - } - if (mInputQueueCallback != null) { - mInputQueue = new InputQueue(mInputChannel); - mInputQueueCallback.onInputQueueCreated(mInputQueue); - } else { - InputQueue.registerInputChannel(mInputChannel, mInputHandler, - Looper.myQueue()); - } - - view.assignParent(this); - mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0; - mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0; - - if (mAccessibilityManager.isEnabled()) { - mAccessibilityInteractionConnectionManager.ensureConnection(); - } - } - } - } - - private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { - mAttachInfo.mHardwareAccelerated = false; - mAttachInfo.mHardwareAccelerationRequested = false; - - // Try to enable hardware acceleration if requested - final boolean hardwareAccelerated = - (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; - - if (hardwareAccelerated) { - if (!HardwareRenderer.isAvailable()) { - mAttachInfo.mHardwareAccelerationRequested = true; - return; - } - - // Only enable hardware acceleration if we are not in the system process - // The window manager creates ViewAncestors to display animated preview windows - // of launching apps and we don't want those to be hardware accelerated - - final boolean systemHwAccelerated = - (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED_SYSTEM) != 0; - - if (!HardwareRenderer.sRendererDisabled || systemHwAccelerated) { - // Don't enable hardware acceleration when we're not on the main thread - if (!systemHwAccelerated && Looper.getMainLooper() != Looper.myLooper()) { - Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware " - + "acceleration outside of the main thread, aborting"); - return; - } - - final boolean translucent = attrs.format != PixelFormat.OPAQUE; - if (mAttachInfo.mHardwareRenderer != null) { - mAttachInfo.mHardwareRenderer.destroy(true); - } - mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent); - mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested - = mAttachInfo.mHardwareRenderer != null; - } - } - } - - public View getView() { - return mView; - } - - final WindowLeaked getLocation() { - return mLocation; - } - - void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) { - synchronized (this) { - int oldSoftInputMode = mWindowAttributes.softInputMode; - // preserve compatible window flag if exists. - int compatibleWindowFlag = - mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; - mWindowAttributes.copyFrom(attrs); - mWindowAttributes.flags |= compatibleWindowFlag; - - if (newView) { - mSoftInputMode = attrs.softInputMode; - requestLayout(); - } - // Don't lose the mode we last auto-computed. - if ((attrs.softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { - mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode - & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - | (oldSoftInputMode - & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST); - } - mWindowAttributesChanged = true; - scheduleTraversals(); - } - } - - void handleAppVisibility(boolean visible) { - if (mAppVisible != visible) { - mAppVisible = visible; - scheduleTraversals(); - } - } - - void handleGetNewSurface() { - mNewSurfaceNeeded = true; - mFullRedrawNeeded = true; - scheduleTraversals(); - } - - /** - * {@inheritDoc} - */ - public void requestLayout() { - checkThread(); - mLayoutRequested = true; - scheduleTraversals(); - } - - /** - * {@inheritDoc} - */ - public boolean isLayoutRequested() { - return mLayoutRequested; - } - - public void invalidateChild(View child, Rect dirty) { - checkThread(); - if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty); - if (dirty == null) { - // Fast invalidation for GL-enabled applications; GL must redraw everything - invalidate(); - return; - } - if (mCurScrollY != 0 || mTranslator != null) { - mTempRect.set(dirty); - dirty = mTempRect; - if (mCurScrollY != 0) { - dirty.offset(0, -mCurScrollY); - } - if (mTranslator != null) { - mTranslator.translateRectInAppWindowToScreen(dirty); - } - if (mAttachInfo.mScalingRequired) { - dirty.inset(-1, -1); - } - } - if (!mDirty.isEmpty() && !mDirty.contains(dirty)) { - mAttachInfo.mSetIgnoreDirtyState = true; - mAttachInfo.mIgnoreDirtyState = true; - } - mDirty.union(dirty); - if (!mWillDrawSoon) { - scheduleTraversals(); - } - } - - void invalidate() { - mDirty.set(0, 0, mWidth, mHeight); - scheduleTraversals(); - } - - void setStopped(boolean stopped) { - if (mStopped != stopped) { - mStopped = stopped; - if (!stopped) { - scheduleTraversals(); - } - } - } - - public ViewParent getParent() { - return null; - } - - public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { - invalidateChild(null, dirty); - return null; - } - - public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { - if (child != mView) { - throw new RuntimeException("child is not mine, honest!"); - } - // Note: don't apply scroll offset, because we want to know its - // visibility in the virtual canvas being given to the view hierarchy. - return r.intersect(0, 0, mWidth, mHeight); - } - - public void bringChildToFront(View child) { - } - - public void scheduleTraversals() { - if (!mTraversalScheduled) { - mTraversalScheduled = true; - - //noinspection ConstantConditions - if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) { - final long now = System.nanoTime(); - Log.d(TAG, "Latency: Scheduled traversal, it has been " - + ((now - mLastTraversalFinishedTimeNanos) * 0.000001f) - + "ms since the last traversal finished."); - } - - sendEmptyMessage(DO_TRAVERSAL); - } - } - - public void unscheduleTraversals() { - if (mTraversalScheduled) { - mTraversalScheduled = false; - removeMessages(DO_TRAVERSAL); - } - } - - int getHostVisibility() { - return mAppVisible ? mView.getVisibility() : View.GONE; - } - - void disposeResizeBuffer() { - if (mResizeBuffer != null) { - mResizeBuffer.destroy(); - mResizeBuffer = null; - } - } - - /** - * Add LayoutTransition to the list of transitions to be started in the next traversal. - * This list will be cleared after the transitions on the list are start()'ed. These - * transitionsa re added by LayoutTransition itself when it sets up animations. The setup - * happens during the layout phase of traversal, which we want to complete before any of the - * animations are started (because those animations may side-effect properties that layout - * depends upon, like the bounding rectangles of the affected views). So we add the transition - * to the list and it is started just prior to starting the drawing phase of traversal. - * - * @param transition The LayoutTransition to be started on the next traversal. - * - * @hide - */ - public void requestTransitionStart(LayoutTransition transition) { - if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) { - if (mPendingTransitions == null) { - mPendingTransitions = new ArrayList(); - } - mPendingTransitions.add(transition); - } - } - - private void performTraversals() { - // cache mView since it is used so much below... - final View host = mView; - - if (DBG) { - System.out.println("======================================"); - System.out.println("performTraversals"); - host.debug(); - } - - if (host == null || !mAdded) - return; - - mTraversalScheduled = false; - mWillDrawSoon = true; - boolean windowSizeMayChange = false; - boolean fullRedrawNeeded = mFullRedrawNeeded; - boolean newSurface = false; - boolean surfaceChanged = false; - WindowManager.LayoutParams lp = mWindowAttributes; - - int desiredWindowWidth; - int desiredWindowHeight; - int childWidthMeasureSpec; - int childHeightMeasureSpec; - - final View.AttachInfo attachInfo = mAttachInfo; - - final int viewVisibility = getHostVisibility(); - boolean viewVisibilityChanged = mViewVisibility != viewVisibility - || mNewSurfaceNeeded; - - WindowManager.LayoutParams params = null; - if (mWindowAttributesChanged) { - mWindowAttributesChanged = false; - surfaceChanged = true; - params = lp; - } - CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get(); - if (compatibilityInfo.supportsScreen() == mLastInCompatMode) { - params = lp; - fullRedrawNeeded = true; - mLayoutRequested = true; - if (mLastInCompatMode) { - params.flags &= ~WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; - mLastInCompatMode = false; - } else { - params.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; - mLastInCompatMode = true; - } - } - Rect frame = mWinFrame; - if (mFirst) { - fullRedrawNeeded = true; - mLayoutRequested = true; - - if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) { - // NOTE -- system code, won't try to do compat mode. - Display disp = WindowManagerImpl.getDefault().getDefaultDisplay(); - desiredWindowWidth = disp.getRealWidth(); - desiredWindowHeight = disp.getRealHeight(); - } else { - DisplayMetrics packageMetrics = - mView.getContext().getResources().getDisplayMetrics(); - desiredWindowWidth = packageMetrics.widthPixels; - desiredWindowHeight = packageMetrics.heightPixels; - } - - // For the very first time, tell the view hierarchy that it - // is attached to the window. Note that at this point the surface - // object is not initialized to its backing store, but soon it - // will be (assuming the window is visible). - attachInfo.mSurface = mSurface; - // We used to use the following condition to choose 32 bits drawing caches: - // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888 - // However, windows are now always 32 bits by default, so choose 32 bits - attachInfo.mUse32BitDrawingCache = true; - attachInfo.mHasWindowFocus = false; - attachInfo.mWindowVisibility = viewVisibility; - attachInfo.mRecomputeGlobalAttributes = false; - attachInfo.mKeepScreenOn = false; - attachInfo.mSystemUiVisibility = 0; - viewVisibilityChanged = false; - mLastConfiguration.setTo(host.getResources().getConfiguration()); - host.dispatchAttachedToWindow(attachInfo, 0); - //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn); - - } else { - desiredWindowWidth = frame.width(); - desiredWindowHeight = frame.height(); - if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) { - if (DEBUG_ORIENTATION) Log.v(TAG, - "View " + host + " resized to: " + frame); - fullRedrawNeeded = true; - mLayoutRequested = true; - windowSizeMayChange = true; - } - } - - if (viewVisibilityChanged) { - attachInfo.mWindowVisibility = viewVisibility; - host.dispatchWindowVisibilityChanged(viewVisibility); - if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { - if (mAttachInfo.mHardwareRenderer != null) { - mAttachInfo.mHardwareRenderer.destroy(false); - } - } - if (viewVisibility == View.GONE) { - // After making a window gone, we will count it as being - // shown for the first time the next time it gets focus. - mHasHadWindowFocus = false; - } - } - - boolean insetsChanged = false; - - if (mLayoutRequested && !mStopped) { - // Execute enqueued actions on every layout in case a view that was detached - // enqueued an action after being detached - getRunQueue().executeActions(attachInfo.mHandler); - - final Resources res = mView.getContext().getResources(); - - if (mFirst) { - host.fitSystemWindows(mAttachInfo.mContentInsets); - // make sure touch mode code executes by setting cached value - // to opposite of the added touch mode. - mAttachInfo.mInTouchMode = !mAddedTouchMode; - ensureTouchModeLocally(mAddedTouchMode); - } else { - if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) { - if (mWidth > 0 && mHeight > 0 && - mSurface != null && mSurface.isValid() && - !mAttachInfo.mTurnOffWindowResizeAnim && - mAttachInfo.mHardwareRenderer != null && - mAttachInfo.mHardwareRenderer.isEnabled() && - mAttachInfo.mHardwareRenderer.validate() && - lp != null && !PixelFormat.formatHasAlpha(lp.format)) { - - disposeResizeBuffer(); - - boolean completed = false; - HardwareCanvas canvas = null; - try { - if (mResizeBuffer == null) { - mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer( - mWidth, mHeight, false); - } else if (mResizeBuffer.getWidth() != mWidth || - mResizeBuffer.getHeight() != mHeight) { - mResizeBuffer.resize(mWidth, mHeight); - } - canvas = mResizeBuffer.start(mAttachInfo.mHardwareCanvas); - canvas.setViewport(mWidth, mHeight); - canvas.onPreDraw(null); - final int restoreCount = canvas.save(); - - canvas.drawColor(0xff000000, PorterDuff.Mode.SRC); - - int yoff; - final boolean scrolling = mScroller != null - && mScroller.computeScrollOffset(); - if (scrolling) { - yoff = mScroller.getCurrY(); - mScroller.abortAnimation(); - } else { - yoff = mScrollY; - } - - canvas.translate(0, -yoff); - if (mTranslator != null) { - mTranslator.translateCanvas(canvas); - } - - mView.draw(canvas); - - mResizeBufferStartTime = SystemClock.uptimeMillis(); - mResizeBufferDuration = mView.getResources().getInteger( - com.android.internal.R.integer.config_mediumAnimTime); - completed = true; - - canvas.restoreToCount(restoreCount); - } catch (OutOfMemoryError e) { - Log.w(TAG, "Not enough memory for content change anim buffer", e); - } finally { - if (canvas != null) { - canvas.onPostDraw(); - } - if (mResizeBuffer != null) { - mResizeBuffer.end(mAttachInfo.mHardwareCanvas); - if (!completed) { - mResizeBuffer.destroy(); - mResizeBuffer = null; - } - } - } - } - mAttachInfo.mContentInsets.set(mPendingContentInsets); - host.fitSystemWindows(mAttachInfo.mContentInsets); - insetsChanged = true; - if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: " - + mAttachInfo.mContentInsets); - } - if (!mAttachInfo.mVisibleInsets.equals(mPendingVisibleInsets)) { - mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets); - if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: " - + mAttachInfo.mVisibleInsets); - } - if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT - || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { - windowSizeMayChange = true; - - if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) { - // NOTE -- system code, won't try to do compat mode. - Display disp = WindowManagerImpl.getDefault().getDefaultDisplay(); - desiredWindowWidth = disp.getRealWidth(); - desiredWindowHeight = disp.getRealHeight(); - } else { - DisplayMetrics packageMetrics = res.getDisplayMetrics(); - desiredWindowWidth = packageMetrics.widthPixels; - desiredWindowHeight = packageMetrics.heightPixels; - } - } - } - - // Ask host how big it wants to be - if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG, - "Measuring " + host + " in display " + desiredWindowWidth - + "x" + desiredWindowHeight + "..."); - - boolean goodMeasure = false; - if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) { - // On large screens, we don't want to allow dialogs to just - // stretch to fill the entire width of the screen to display - // one line of text. First try doing the layout at a smaller - // size to see if it will fit. - final DisplayMetrics packageMetrics = res.getDisplayMetrics(); - res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true); - int baseSize = 0; - if (mTmpValue.type == TypedValue.TYPE_DIMENSION) { - baseSize = (int)mTmpValue.getDimension(packageMetrics); - } - if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize); - if (baseSize != 0 && desiredWindowWidth > baseSize) { - childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); - childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); - host.measure(childWidthMeasureSpec, childHeightMeasureSpec); - if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" - + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); - if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { - goodMeasure = true; - } else { - // Didn't fit in that size... try expanding a bit. - baseSize = (baseSize+desiredWindowWidth)/2; - if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize=" - + baseSize); - childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); - host.measure(childWidthMeasureSpec, childHeightMeasureSpec); - if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" - + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); - if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { - if (DEBUG_DIALOG) Log.v(TAG, "Good!"); - goodMeasure = true; - } - } - } - } - - if (!goodMeasure) { - childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); - childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); - host.measure(childWidthMeasureSpec, childHeightMeasureSpec); - if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) { - windowSizeMayChange = true; - } - } - - if (DBG) { - System.out.println("======================================"); - System.out.println("performTraversals -- after measure"); - host.debug(); - } - } - - if (attachInfo.mRecomputeGlobalAttributes && host.mAttachInfo != null) { - //Log.i(TAG, "Computing view hierarchy attributes!"); - attachInfo.mRecomputeGlobalAttributes = false; - boolean oldScreenOn = attachInfo.mKeepScreenOn; - int oldVis = attachInfo.mSystemUiVisibility; - attachInfo.mKeepScreenOn = false; - attachInfo.mSystemUiVisibility = 0; - attachInfo.mHasSystemUiListeners = false; - host.dispatchCollectViewAttributes(0); - if (attachInfo.mKeepScreenOn != oldScreenOn - || attachInfo.mSystemUiVisibility != oldVis - || attachInfo.mHasSystemUiListeners) { - params = lp; - } - } - - if (mFirst || attachInfo.mViewVisibilityChanged) { - attachInfo.mViewVisibilityChanged = false; - int resizeMode = mSoftInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; - // If we are in auto resize mode, then we need to determine - // what mode to use now. - if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { - final int N = attachInfo.mScrollContainers.size(); - for (int i=0; i ci.left || vi.top > ci.top - || vi.right > ci.right || vi.bottom > ci.bottom) { - // We'll assume that we aren't going to change the scroll - // offset, since we want to avoid that unless it is actually - // going to make the focus visible... otherwise we scroll - // all over the place. - scrollY = mScrollY; - // We can be called for two different situations: during a draw, - // to update the scroll position if the focus has changed (in which - // case 'rectangle' is null), or in response to a - // requestChildRectangleOnScreen() call (in which case 'rectangle' - // is non-null and we just want to scroll to whatever that - // rectangle is). - View focus = mRealFocusedView; - - // When in touch mode, focus points to the previously focused view, - // which may have been removed from the view hierarchy. The following - // line checks whether the view is still in our hierarchy. - if (focus == null || focus.mAttachInfo != mAttachInfo) { - mRealFocusedView = null; - return false; - } - - if (focus != mLastScrolledFocus) { - // If the focus has changed, then ignore any requests to scroll - // to a rectangle; first we want to make sure the entire focus - // view is visible. - rectangle = null; - } - if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus - + " rectangle=" + rectangle + " ci=" + ci - + " vi=" + vi); - if (focus == mLastScrolledFocus && !mScrollMayChange - && rectangle == null) { - // Optimization: if the focus hasn't changed since last - // time, and no layout has happened, then just leave things - // as they are. - if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Keeping scroll y=" - + mScrollY + " vi=" + vi.toShortString()); - } else if (focus != null) { - // We need to determine if the currently focused view is - // within the visible part of the window and, if not, apply - // a pan so it can be seen. - mLastScrolledFocus = focus; - mScrollMayChange = false; - if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?"); - // Try to find the rectangle from the focus view. - if (focus.getGlobalVisibleRect(mVisRect, null)) { - if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Root w=" - + mView.getWidth() + " h=" + mView.getHeight() - + " ci=" + ci.toShortString() - + " vi=" + vi.toShortString()); - if (rectangle == null) { - focus.getFocusedRect(mTempRect); - if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Focus " + focus - + ": focusRect=" + mTempRect.toShortString()); - if (mView instanceof ViewGroup) { - ((ViewGroup) mView).offsetDescendantRectToMyCoords( - focus, mTempRect); - } - if (DEBUG_INPUT_RESIZE) Log.v(TAG, - "Focus in window: focusRect=" - + mTempRect.toShortString() - + " visRect=" + mVisRect.toShortString()); - } else { - mTempRect.set(rectangle); - if (DEBUG_INPUT_RESIZE) Log.v(TAG, - "Request scroll to rect: " - + mTempRect.toShortString() - + " visRect=" + mVisRect.toShortString()); - } - if (mTempRect.intersect(mVisRect)) { - if (DEBUG_INPUT_RESIZE) Log.v(TAG, - "Focus window visible rect: " - + mTempRect.toShortString()); - if (mTempRect.height() > - (mView.getHeight()-vi.top-vi.bottom)) { - // If the focus simply is not going to fit, then - // best is probably just to leave things as-is. - if (DEBUG_INPUT_RESIZE) Log.v(TAG, - "Too tall; leaving scrollY=" + scrollY); - } else if ((mTempRect.top-scrollY) < vi.top) { - scrollY -= vi.top - (mTempRect.top-scrollY); - if (DEBUG_INPUT_RESIZE) Log.v(TAG, - "Top covered; scrollY=" + scrollY); - } else if ((mTempRect.bottom-scrollY) - > (mView.getHeight()-vi.bottom)) { - scrollY += (mTempRect.bottom-scrollY) - - (mView.getHeight()-vi.bottom); - if (DEBUG_INPUT_RESIZE) Log.v(TAG, - "Bottom covered; scrollY=" + scrollY); - } - handled = true; - } - } - } - } - - if (scrollY != mScrollY) { - if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old=" - + mScrollY + " , new=" + scrollY); - if (!immediate && mResizeBuffer == null) { - if (mScroller == null) { - mScroller = new Scroller(mView.getContext()); - } - mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY); - } else if (mScroller != null) { - mScroller.abortAnimation(); - } - mScrollY = scrollY; - } - - return handled; - } - - public void requestChildFocus(View child, View focused) { - checkThread(); - if (mFocusedView != focused) { - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused); - scheduleTraversals(); - } - mFocusedView = mRealFocusedView = focused; - if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now " - + mFocusedView); - } - - public void clearChildFocus(View child) { - checkThread(); - - View oldFocus = mFocusedView; - - if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus"); - mFocusedView = mRealFocusedView = null; - if (mView != null && !mView.hasFocus()) { - // If a view gets the focus, the listener will be invoked from requestChildFocus() - if (!mView.requestFocus(View.FOCUS_FORWARD)) { - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); - } - } else if (oldFocus != null) { - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); - } - } - - - public void focusableViewAvailable(View v) { - checkThread(); - - if (mView != null) { - if (!mView.hasFocus()) { - v.requestFocus(); - } else { - // the one case where will transfer focus away from the current one - // is if the current view is a view group that prefers to give focus - // to its children first AND the view is a descendant of it. - mFocusedView = mView.findFocus(); - boolean descendantsHaveDibsOnFocus = - (mFocusedView instanceof ViewGroup) && - (((ViewGroup) mFocusedView).getDescendantFocusability() == - ViewGroup.FOCUS_AFTER_DESCENDANTS); - if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) { - // If a view gets the focus, the listener will be invoked from requestChildFocus() - v.requestFocus(); - } - } - } - } - - public void recomputeViewAttributes(View child) { - checkThread(); - if (mView == child) { - mAttachInfo.mRecomputeGlobalAttributes = true; - if (!mWillDrawSoon) { - scheduleTraversals(); - } - } - } - - void dispatchDetachedFromWindow() { - if (mView != null && mView.mAttachInfo != null) { - mView.dispatchDetachedFromWindow(); - } - - mAccessibilityInteractionConnectionManager.ensureNoConnection(); - mAccessibilityManager.removeAccessibilityStateChangeListener( - mAccessibilityInteractionConnectionManager); - removeSendWindowContentChangedCallback(); - - mView = null; - mAttachInfo.mRootView = null; - mAttachInfo.mSurface = null; - - destroyHardwareRenderer(); - - mSurface.release(); - - if (mInputChannel != null) { - if (mInputQueueCallback != null) { - mInputQueueCallback.onInputQueueDestroyed(mInputQueue); - mInputQueueCallback = null; - } else { - InputQueue.unregisterInputChannel(mInputChannel); - } - } - try { - sWindowSession.remove(mWindow); - } catch (RemoteException e) { - } - - // Dispose the input channel after removing the window so the Window Manager - // doesn't interpret the input channel being closed as an abnormal termination. - if (mInputChannel != null) { - mInputChannel.dispose(); - mInputChannel = null; - } - } - - void updateConfiguration(Configuration config, boolean force) { - if (DEBUG_CONFIGURATION) Log.v(TAG, - "Applying new config to window " - + mWindowAttributes.getTitle() - + ": " + config); - - CompatibilityInfo ci = mCompatibilityInfo.getIfNeeded(); - if (ci != null) { - config = new Configuration(config); - ci.applyToConfiguration(config); - } - - synchronized (sConfigCallbacks) { - for (int i=sConfigCallbacks.size()-1; i>=0; i--) { - sConfigCallbacks.get(i).onConfigurationChanged(config); - } - } - if (mView != null) { - // At this point the resources have been updated to - // have the most recent config, whatever that is. Use - // the on in them which may be newer. - config = mView.getResources().getConfiguration(); - if (force || mLastConfiguration.diff(config) != 0) { - mLastConfiguration.setTo(config); - mView.dispatchConfigurationChanged(config); - } - } - } - - /** - * Return true if child is an ancestor of parent, (or equal to the parent). - */ - private static boolean isViewDescendantOf(View child, View parent) { - if (child == parent) { - return true; - } - - final ViewParent theParent = child.getParent(); - return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); - } - - private static void forceLayout(View view) { - view.forceLayout(); - if (view instanceof ViewGroup) { - ViewGroup group = (ViewGroup) view; - final int count = group.getChildCount(); - for (int i = 0; i < count; i++) { - forceLayout(group.getChildAt(i)); - } - } - } - - public final static int DO_TRAVERSAL = 1000; - public final static int DIE = 1001; - public final static int RESIZED = 1002; - public final static int RESIZED_REPORT = 1003; - public final static int WINDOW_FOCUS_CHANGED = 1004; - public final static int DISPATCH_KEY = 1005; - public final static int DISPATCH_POINTER = 1006; - public final static int DISPATCH_TRACKBALL = 1007; - public final static int DISPATCH_APP_VISIBILITY = 1008; - public final static int DISPATCH_GET_NEW_SURFACE = 1009; - public final static int FINISHED_EVENT = 1010; - public final static int DISPATCH_KEY_FROM_IME = 1011; - public final static int FINISH_INPUT_CONNECTION = 1012; - public final static int CHECK_FOCUS = 1013; - public final static int CLOSE_SYSTEM_DIALOGS = 1014; - public final static int DISPATCH_DRAG_EVENT = 1015; - public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016; - public final static int DISPATCH_SYSTEM_UI_VISIBILITY = 1017; - public final static int DISPATCH_GENERIC_MOTION = 1018; - public final static int UPDATE_CONFIGURATION = 1019; - public final static int DO_PERFORM_ACCESSIBILITY_ACTION = 1020; - public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1021; - public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022; - public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1023; - - @Override - public String getMessageName(Message message) { - switch (message.what) { - case DO_TRAVERSAL: - return "DO_TRAVERSAL"; - case DIE: - return "DIE"; - case RESIZED: - return "RESIZED"; - case RESIZED_REPORT: - return "RESIZED_REPORT"; - case WINDOW_FOCUS_CHANGED: - return "WINDOW_FOCUS_CHANGED"; - case DISPATCH_KEY: - return "DISPATCH_KEY"; - case DISPATCH_POINTER: - return "DISPATCH_POINTER"; - case DISPATCH_TRACKBALL: - return "DISPATCH_TRACKBALL"; - case DISPATCH_APP_VISIBILITY: - return "DISPATCH_APP_VISIBILITY"; - case DISPATCH_GET_NEW_SURFACE: - return "DISPATCH_GET_NEW_SURFACE"; - case FINISHED_EVENT: - return "FINISHED_EVENT"; - case DISPATCH_KEY_FROM_IME: - return "DISPATCH_KEY_FROM_IME"; - case FINISH_INPUT_CONNECTION: - return "FINISH_INPUT_CONNECTION"; - case CHECK_FOCUS: - return "CHECK_FOCUS"; - case CLOSE_SYSTEM_DIALOGS: - return "CLOSE_SYSTEM_DIALOGS"; - case DISPATCH_DRAG_EVENT: - return "DISPATCH_DRAG_EVENT"; - case DISPATCH_DRAG_LOCATION_EVENT: - return "DISPATCH_DRAG_LOCATION_EVENT"; - case DISPATCH_SYSTEM_UI_VISIBILITY: - return "DISPATCH_SYSTEM_UI_VISIBILITY"; - case DISPATCH_GENERIC_MOTION: - return "DISPATCH_GENERIC_MOTION"; - case UPDATE_CONFIGURATION: - return "UPDATE_CONFIGURATION"; - case DO_PERFORM_ACCESSIBILITY_ACTION: - return "DO_PERFORM_ACCESSIBILITY_ACTION"; - case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: - return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID"; - case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: - return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID"; - case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: - return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT"; - - } - return super.getMessageName(message); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case View.AttachInfo.INVALIDATE_MSG: - ((View) msg.obj).invalidate(); - break; - case View.AttachInfo.INVALIDATE_RECT_MSG: - final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj; - info.target.invalidate(info.left, info.top, info.right, info.bottom); - info.release(); - break; - case DO_TRAVERSAL: - if (mProfile) { - Debug.startMethodTracing("ViewAncestor"); - } - - final long traversalStartTime; - if (ViewDebug.DEBUG_LATENCY) { - traversalStartTime = System.nanoTime(); - mLastDrawDurationNanos = 0; - } - - performTraversals(); - - if (ViewDebug.DEBUG_LATENCY) { - long now = System.nanoTime(); - Log.d(TAG, "Latency: Spent " - + ((now - traversalStartTime) * 0.000001f) - + "ms in performTraversals(), with " - + (mLastDrawDurationNanos * 0.000001f) - + "ms of that time in draw()"); - mLastTraversalFinishedTimeNanos = now; - } - - if (mProfile) { - Debug.stopMethodTracing(); - mProfile = false; - } - break; - case FINISHED_EVENT: - handleFinishedEvent(msg.arg1, msg.arg2 != 0); - break; - case DISPATCH_KEY: - deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0); - break; - case DISPATCH_POINTER: - deliverPointerEvent((MotionEvent) msg.obj, msg.arg1 != 0); - break; - case DISPATCH_TRACKBALL: - deliverTrackballEvent((MotionEvent) msg.obj, msg.arg1 != 0); - break; - case DISPATCH_GENERIC_MOTION: - deliverGenericMotionEvent((MotionEvent) msg.obj, msg.arg1 != 0); - break; - case DISPATCH_APP_VISIBILITY: - handleAppVisibility(msg.arg1 != 0); - break; - case DISPATCH_GET_NEW_SURFACE: - handleGetNewSurface(); - break; - case RESIZED: - ResizedInfo ri = (ResizedInfo)msg.obj; - - if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2 - && mPendingContentInsets.equals(ri.coveredInsets) - && mPendingVisibleInsets.equals(ri.visibleInsets) - && ((ResizedInfo)msg.obj).newConfig == null) { - break; - } - // fall through... - case RESIZED_REPORT: - if (mAdded) { - Configuration config = ((ResizedInfo)msg.obj).newConfig; - if (config != null) { - updateConfiguration(config, false); - } - mWinFrame.left = 0; - mWinFrame.right = msg.arg1; - mWinFrame.top = 0; - mWinFrame.bottom = msg.arg2; - mPendingContentInsets.set(((ResizedInfo)msg.obj).coveredInsets); - mPendingVisibleInsets.set(((ResizedInfo)msg.obj).visibleInsets); - if (msg.what == RESIZED_REPORT) { - mReportNextDraw = true; - } - - if (mView != null) { - forceLayout(mView); - } - requestLayout(); - } - break; - case WINDOW_FOCUS_CHANGED: { - if (mAdded) { - boolean hasWindowFocus = msg.arg1 != 0; - mAttachInfo.mHasWindowFocus = hasWindowFocus; - - profileRendering(hasWindowFocus); - - if (hasWindowFocus) { - boolean inTouchMode = msg.arg2 != 0; - ensureTouchModeLocally(inTouchMode); - - if (mAttachInfo.mHardwareRenderer != null && - mSurface != null && mSurface.isValid()) { - mFullRedrawNeeded = true; - try { - mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight, - mAttachInfo, mHolder); - } catch (Surface.OutOfResourcesException e) { - Log.e(TAG, "OutOfResourcesException locking surface", e); - try { - if (!sWindowSession.outOfMemory(mWindow)) { - Slog.w(TAG, "No processes killed for memory; killing self"); - Process.killProcess(Process.myPid()); - } - } catch (RemoteException ex) { - } - // Retry in a bit. - sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500); - return; - } - } - } - - mLastWasImTarget = WindowManager.LayoutParams - .mayUseInputMethod(mWindowAttributes.flags); - - InputMethodManager imm = InputMethodManager.peekInstance(); - if (mView != null) { - if (hasWindowFocus && imm != null && mLastWasImTarget) { - imm.startGettingWindowFocus(mView); - } - mAttachInfo.mKeyDispatchState.reset(); - mView.dispatchWindowFocusChanged(hasWindowFocus); - } - - // Note: must be done after the focus change callbacks, - // so all of the view state is set up correctly. - if (hasWindowFocus) { - if (imm != null && mLastWasImTarget) { - imm.onWindowFocus(mView, mView.findFocus(), - mWindowAttributes.softInputMode, - !mHasHadWindowFocus, mWindowAttributes.flags); - } - // Clear the forward bit. We can just do this directly, since - // the window manager doesn't care about it. - mWindowAttributes.softInputMode &= - ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; - ((WindowManager.LayoutParams)mView.getLayoutParams()) - .softInputMode &= - ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; - mHasHadWindowFocus = true; - } - - if (hasWindowFocus && mView != null) { - sendAccessibilityEvents(); - } - } - } break; - case DIE: - doDie(); - break; - case DISPATCH_KEY_FROM_IME: { - if (LOCAL_LOGV) Log.v( - TAG, "Dispatching key " - + msg.obj + " from IME to " + mView); - KeyEvent event = (KeyEvent)msg.obj; - if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) { - // The IME is trying to say this event is from the - // system! Bad bad bad! - //noinspection UnusedAssignment - event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM); - } - deliverKeyEventPostIme((KeyEvent)msg.obj, false); - } break; - case FINISH_INPUT_CONNECTION: { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - imm.reportFinishInputConnection((InputConnection)msg.obj); - } - } break; - case CHECK_FOCUS: { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - imm.checkFocus(); - } - } break; - case CLOSE_SYSTEM_DIALOGS: { - if (mView != null) { - mView.onCloseSystemDialogs((String)msg.obj); - } - } break; - case DISPATCH_DRAG_EVENT: - case DISPATCH_DRAG_LOCATION_EVENT: { - DragEvent event = (DragEvent)msg.obj; - event.mLocalState = mLocalDragState; // only present when this app called startDrag() - handleDragEvent(event); - } break; - case DISPATCH_SYSTEM_UI_VISIBILITY: { - handleDispatchSystemUiVisibilityChanged(msg.arg1); - } break; - case UPDATE_CONFIGURATION: { - Configuration config = (Configuration)msg.obj; - if (config.isOtherSeqNewer(mLastConfiguration)) { - config = mLastConfiguration; - } - updateConfiguration(config, false); - } break; - case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: { - if (mView != null) { - getAccessibilityInteractionController() - .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg); - } - } break; - case DO_PERFORM_ACCESSIBILITY_ACTION: { - if (mView != null) { - getAccessibilityInteractionController() - .perfromAccessibilityActionUiThread(msg); - } - } break; - case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: { - if (mView != null) { - getAccessibilityInteractionController() - .findAccessibilityNodeInfoByViewIdUiThread(msg); - } - } break; - case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: { - if (mView != null) { - getAccessibilityInteractionController() - .findAccessibilityNodeInfosByViewTextUiThread(msg); - } - } break; - } - } - - private void startInputEvent(InputQueue.FinishedCallback finishedCallback) { - if (mFinishedCallback != null) { - Slog.w(TAG, "Received a new input event from the input queue but there is " - + "already an unfinished input event in progress."); - } - - if (ViewDebug.DEBUG_LATENCY) { - mInputEventReceiveTimeNanos = System.nanoTime(); - mInputEventDeliverTimeNanos = 0; - mInputEventDeliverPostImeTimeNanos = 0; - } - - mFinishedCallback = finishedCallback; - } - - private void finishInputEvent(InputEvent event, boolean handled) { - if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished"); - - if (mFinishedCallback == null) { - Slog.w(TAG, "Attempted to tell the input queue that the current input event " - + "is finished but there is no input event actually in progress."); - return; - } - - if (ViewDebug.DEBUG_LATENCY) { - final long now = System.nanoTime(); - final long eventTime = event.getEventTimeNano(); - final StringBuilder msg = new StringBuilder(); - msg.append("Latency: Spent "); - msg.append((now - mInputEventReceiveTimeNanos) * 0.000001f); - msg.append("ms processing "); - if (event instanceof KeyEvent) { - final KeyEvent keyEvent = (KeyEvent)event; - msg.append("key event, action="); - msg.append(KeyEvent.actionToString(keyEvent.getAction())); - } else { - final MotionEvent motionEvent = (MotionEvent)event; - msg.append("motion event, action="); - msg.append(MotionEvent.actionToString(motionEvent.getAction())); - msg.append(", historySize="); - msg.append(motionEvent.getHistorySize()); - } - msg.append(", handled="); - msg.append(handled); - msg.append(", received at +"); - msg.append((mInputEventReceiveTimeNanos - eventTime) * 0.000001f); - if (mInputEventDeliverTimeNanos != 0) { - msg.append("ms, delivered at +"); - msg.append((mInputEventDeliverTimeNanos - eventTime) * 0.000001f); - } - if (mInputEventDeliverPostImeTimeNanos != 0) { - msg.append("ms, delivered post IME at +"); - msg.append((mInputEventDeliverPostImeTimeNanos - eventTime) * 0.000001f); - } - msg.append("ms, finished at +"); - msg.append((now - eventTime) * 0.000001f); - msg.append("ms."); - Log.d(TAG, msg.toString()); - } - - mFinishedCallback.finished(handled); - mFinishedCallback = null; - } - - /** - * Something in the current window tells us we need to change the touch mode. For - * example, we are not in touch mode, and the user touches the screen. - * - * If the touch mode has changed, tell the window manager, and handle it locally. - * - * @param inTouchMode Whether we want to be in touch mode. - * @return True if the touch mode changed and focus changed was changed as a result - */ - boolean ensureTouchMode(boolean inTouchMode) { - if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current " - + "touch mode is " + mAttachInfo.mInTouchMode); - if (mAttachInfo.mInTouchMode == inTouchMode) return false; - - // tell the window manager - try { - sWindowSession.setInTouchMode(inTouchMode); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - - // handle the change - return ensureTouchModeLocally(inTouchMode); - } - - /** - * Ensure that the touch mode for this window is set, and if it is changing, - * take the appropriate action. - * @param inTouchMode Whether we want to be in touch mode. - * @return True if the touch mode changed and focus changed was changed as a result - */ - private boolean ensureTouchModeLocally(boolean inTouchMode) { - if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current " - + "touch mode is " + mAttachInfo.mInTouchMode); - - if (mAttachInfo.mInTouchMode == inTouchMode) return false; - - mAttachInfo.mInTouchMode = inTouchMode; - mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode); - - return (inTouchMode) ? enterTouchMode() : leaveTouchMode(); - } - - private boolean enterTouchMode() { - if (mView != null) { - if (mView.hasFocus()) { - // note: not relying on mFocusedView here because this could - // be when the window is first being added, and mFocused isn't - // set yet. - final View focused = mView.findFocus(); - if (focused != null && !focused.isFocusableInTouchMode()) { - - final ViewGroup ancestorToTakeFocus = - findAncestorToTakeFocusInTouchMode(focused); - if (ancestorToTakeFocus != null) { - // there is an ancestor that wants focus after its descendants that - // is focusable in touch mode.. give it focus - return ancestorToTakeFocus.requestFocus(); - } else { - // nothing appropriate to have focus in touch mode, clear it out - mView.unFocus(); - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null); - mFocusedView = null; - return true; - } - } - } - } - return false; - } - - - /** - * Find an ancestor of focused that wants focus after its descendants and is - * focusable in touch mode. - * @param focused The currently focused view. - * @return An appropriate view, or null if no such view exists. - */ - private ViewGroup findAncestorToTakeFocusInTouchMode(View focused) { - ViewParent parent = focused.getParent(); - while (parent instanceof ViewGroup) { - final ViewGroup vgParent = (ViewGroup) parent; - if (vgParent.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS - && vgParent.isFocusableInTouchMode()) { - return vgParent; - } - if (vgParent.isRootNamespace()) { - return null; - } else { - parent = vgParent.getParent(); - } - } - return null; - } - - private boolean leaveTouchMode() { - if (mView != null) { - if (mView.hasFocus()) { - // i learned the hard way to not trust mFocusedView :) - mFocusedView = mView.findFocus(); - if (!(mFocusedView instanceof ViewGroup)) { - // some view has focus, let it keep it - return false; - } else if (((ViewGroup)mFocusedView).getDescendantFocusability() != - ViewGroup.FOCUS_AFTER_DESCENDANTS) { - // some view group has focus, and doesn't prefer its children - // over itself for focus, so let them keep it. - return false; - } - } - - // find the best view to give focus to in this brave new non-touch-mode - // world - final View focused = focusSearch(null, View.FOCUS_DOWN); - if (focused != null) { - return focused.requestFocus(View.FOCUS_DOWN); - } - } - return false; - } - - private void deliverPointerEvent(MotionEvent event, boolean sendDone) { - if (ViewDebug.DEBUG_LATENCY) { - mInputEventDeliverTimeNanos = System.nanoTime(); - } - - final boolean isTouchEvent = event.isTouchEvent(); - if (mInputEventConsistencyVerifier != null) { - if (isTouchEvent) { - mInputEventConsistencyVerifier.onTouchEvent(event, 0); - } else { - mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); - } - } - - // If there is no view, then the event will not be handled. - if (mView == null || !mAdded) { - finishMotionEvent(event, sendDone, false); - return; - } - - // Translate the pointer event for compatibility, if needed. - if (mTranslator != null) { - mTranslator.translateEventInScreenToAppWindow(event); - } - - // Enter touch mode on down or scroll. - final int action = event.getAction(); - if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { - ensureTouchMode(true); - } - - // Offset the scroll position. - if (mCurScrollY != 0) { - event.offsetLocation(0, mCurScrollY); - } - if (MEASURE_LATENCY) { - lt.sample("A Dispatching PointerEvents", System.nanoTime() - event.getEventTimeNano()); - } - - // Remember the touch position for possible drag-initiation. - if (isTouchEvent) { - mLastTouchPoint.x = event.getRawX(); - mLastTouchPoint.y = event.getRawY(); - } - - // Dispatch touch to view hierarchy. - boolean handled = mView.dispatchPointerEvent(event); - if (MEASURE_LATENCY) { - lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano()); - } - if (handled) { - finishMotionEvent(event, sendDone, true); - return; - } - - // Pointer event was unhandled. - finishMotionEvent(event, sendDone, false); - } - - private void finishMotionEvent(MotionEvent event, boolean sendDone, boolean handled) { - event.recycle(); - if (sendDone) { - finishInputEvent(event, handled); - } - //noinspection ConstantConditions - if (LOCAL_LOGV || WATCH_POINTER) { - if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { - Log.i(TAG, "Done dispatching!"); - } - } - } - - private void deliverTrackballEvent(MotionEvent event, boolean sendDone) { - if (ViewDebug.DEBUG_LATENCY) { - mInputEventDeliverTimeNanos = System.nanoTime(); - } - - if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event); - - if (mInputEventConsistencyVerifier != null) { - mInputEventConsistencyVerifier.onTrackballEvent(event, 0); - } - - // If there is no view, then the event will not be handled. - if (mView == null || !mAdded) { - finishMotionEvent(event, sendDone, false); - return; - } - - // Deliver the trackball event to the view. - if (mView.dispatchTrackballEvent(event)) { - // If we reach this, we delivered a trackball event to mView and - // mView consumed it. Because we will not translate the trackball - // event into a key event, touch mode will not exit, so we exit - // touch mode here. - ensureTouchMode(false); - - finishMotionEvent(event, sendDone, true); - mLastTrackballTime = Integer.MIN_VALUE; - return; - } - - // Translate the trackball event into DPAD keys and try to deliver those. - final TrackballAxis x = mTrackballAxisX; - final TrackballAxis y = mTrackballAxisY; - - long curTime = SystemClock.uptimeMillis(); - if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) { - // It has been too long since the last movement, - // so restart at the beginning. - x.reset(0); - y.reset(0); - mLastTrackballTime = curTime; - } - - final int action = event.getAction(); - final int metaState = event.getMetaState(); - switch (action) { - case MotionEvent.ACTION_DOWN: - x.reset(2); - y.reset(2); - deliverKeyEvent(new KeyEvent(curTime, curTime, - KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState, - KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, - InputDevice.SOURCE_KEYBOARD), false); - break; - case MotionEvent.ACTION_UP: - x.reset(2); - y.reset(2); - deliverKeyEvent(new KeyEvent(curTime, curTime, - KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState, - KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, - InputDevice.SOURCE_KEYBOARD), false); - break; - } - - if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step=" - + x.step + " dir=" + x.dir + " acc=" + x.acceleration - + " move=" + event.getX() - + " / Y=" + y.position + " step=" - + y.step + " dir=" + y.dir + " acc=" + y.acceleration - + " move=" + event.getY()); - final float xOff = x.collect(event.getX(), event.getEventTime(), "X"); - final float yOff = y.collect(event.getY(), event.getEventTime(), "Y"); - - // Generate DPAD events based on the trackball movement. - // We pick the axis that has moved the most as the direction of - // the DPAD. When we generate DPAD events for one axis, then the - // other axis is reset -- we don't want to perform DPAD jumps due - // to slight movements in the trackball when making major movements - // along the other axis. - int keycode = 0; - int movement = 0; - float accel = 1; - if (xOff > yOff) { - movement = x.generate((2/event.getXPrecision())); - if (movement != 0) { - keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT - : KeyEvent.KEYCODE_DPAD_LEFT; - accel = x.acceleration; - y.reset(2); - } - } else if (yOff > 0) { - movement = y.generate((2/event.getYPrecision())); - if (movement != 0) { - keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN - : KeyEvent.KEYCODE_DPAD_UP; - accel = y.acceleration; - x.reset(2); - } - } - - if (keycode != 0) { - if (movement < 0) movement = -movement; - int accelMovement = (int)(movement * accel); - if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement - + " accelMovement=" + accelMovement - + " accel=" + accel); - if (accelMovement > movement) { - if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: " - + keycode); - movement--; - int repeatCount = accelMovement - movement; - deliverKeyEvent(new KeyEvent(curTime, curTime, - KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState, - KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, - InputDevice.SOURCE_KEYBOARD), false); - } - while (movement > 0) { - if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: " - + keycode); - movement--; - curTime = SystemClock.uptimeMillis(); - deliverKeyEvent(new KeyEvent(curTime, curTime, - KeyEvent.ACTION_DOWN, keycode, 0, metaState, - KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, - InputDevice.SOURCE_KEYBOARD), false); - deliverKeyEvent(new KeyEvent(curTime, curTime, - KeyEvent.ACTION_UP, keycode, 0, metaState, - KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, - InputDevice.SOURCE_KEYBOARD), false); - } - mLastTrackballTime = curTime; - } - - // Unfortunately we can't tell whether the application consumed the keys, so - // we always consider the trackball event handled. - finishMotionEvent(event, sendDone, true); - } - - private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) { - if (ViewDebug.DEBUG_LATENCY) { - mInputEventDeliverTimeNanos = System.nanoTime(); - } - - if (mInputEventConsistencyVerifier != null) { - mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); - } - - final int source = event.getSource(); - final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0; - - // If there is no view, then the event will not be handled. - if (mView == null || !mAdded) { - if (isJoystick) { - updateJoystickDirection(event, false); - } - finishMotionEvent(event, sendDone, false); - return; - } - - // Deliver the event to the view. - if (mView.dispatchGenericMotionEvent(event)) { - if (isJoystick) { - updateJoystickDirection(event, false); - } - finishMotionEvent(event, sendDone, true); - return; - } - - if (isJoystick) { - // Translate the joystick event into DPAD keys and try to deliver those. - updateJoystickDirection(event, true); - finishMotionEvent(event, sendDone, true); - } else { - finishMotionEvent(event, sendDone, false); - } - } - - private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) { - final long time = event.getEventTime(); - final int metaState = event.getMetaState(); - final int deviceId = event.getDeviceId(); - final int source = event.getSource(); - - int xDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_X)); - if (xDirection == 0) { - xDirection = joystickAxisValueToDirection(event.getX()); - } - - int yDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_Y)); - if (yDirection == 0) { - yDirection = joystickAxisValueToDirection(event.getY()); - } - - if (xDirection != mLastJoystickXDirection) { - if (mLastJoystickXKeyCode != 0) { - deliverKeyEvent(new KeyEvent(time, time, - KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState, - deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); - mLastJoystickXKeyCode = 0; - } - - mLastJoystickXDirection = xDirection; - - if (xDirection != 0 && synthesizeNewKeys) { - mLastJoystickXKeyCode = xDirection > 0 - ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT; - deliverKeyEvent(new KeyEvent(time, time, - KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState, - deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); - } - } - - if (yDirection != mLastJoystickYDirection) { - if (mLastJoystickYKeyCode != 0) { - deliverKeyEvent(new KeyEvent(time, time, - KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState, - deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); - mLastJoystickYKeyCode = 0; - } - - mLastJoystickYDirection = yDirection; - - if (yDirection != 0 && synthesizeNewKeys) { - mLastJoystickYKeyCode = yDirection > 0 - ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP; - deliverKeyEvent(new KeyEvent(time, time, - KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState, - deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); - } - } - } - - private static int joystickAxisValueToDirection(float value) { - if (value >= 0.5f) { - return 1; - } else if (value <= -0.5f) { - return -1; - } else { - return 0; - } - } - - /** - * Returns true if the key is used for keyboard navigation. - * @param keyEvent The key event. - * @return True if the key is used for keyboard navigation. - */ - private static boolean isNavigationKey(KeyEvent keyEvent) { - switch (keyEvent.getKeyCode()) { - case KeyEvent.KEYCODE_DPAD_LEFT: - case KeyEvent.KEYCODE_DPAD_RIGHT: - case KeyEvent.KEYCODE_DPAD_UP: - case KeyEvent.KEYCODE_DPAD_DOWN: - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_PAGE_UP: - case KeyEvent.KEYCODE_PAGE_DOWN: - case KeyEvent.KEYCODE_MOVE_HOME: - case KeyEvent.KEYCODE_MOVE_END: - case KeyEvent.KEYCODE_TAB: - case KeyEvent.KEYCODE_SPACE: - case KeyEvent.KEYCODE_ENTER: - return true; - } - return false; - } - - /** - * Returns true if the key is used for typing. - * @param keyEvent The key event. - * @return True if the key is used for typing. - */ - private static boolean isTypingKey(KeyEvent keyEvent) { - return keyEvent.getUnicodeChar() > 0; - } - - /** - * See if the key event means we should leave touch mode (and leave touch mode if so). - * @param event The key event. - * @return Whether this key event should be consumed (meaning the act of - * leaving touch mode alone is considered the event). - */ - private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) { - // Only relevant in touch mode. - if (!mAttachInfo.mInTouchMode) { - return false; - } - - // Only consider leaving touch mode on DOWN or MULTIPLE actions, never on UP. - final int action = event.getAction(); - if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) { - return false; - } - - // Don't leave touch mode if the IME told us not to. - if ((event.getFlags() & KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) { - return false; - } - - // If the key can be used for keyboard navigation then leave touch mode - // and select a focused view if needed (in ensureTouchMode). - // When a new focused view is selected, we consume the navigation key because - // navigation doesn't make much sense unless a view already has focus so - // the key's purpose is to set focus. - if (isNavigationKey(event)) { - return ensureTouchMode(false); - } - - // If the key can be used for typing then leave touch mode - // and select a focused view if needed (in ensureTouchMode). - // Always allow the view to process the typing key. - if (isTypingKey(event)) { - ensureTouchMode(false); - return false; - } - - return false; - } - - int enqueuePendingEvent(Object event, boolean sendDone) { - int seq = mPendingEventSeq+1; - if (seq < 0) seq = 0; - mPendingEventSeq = seq; - mPendingEvents.put(seq, event); - return sendDone ? seq : -seq; - } - - Object retrievePendingEvent(int seq) { - if (seq < 0) seq = -seq; - Object event = mPendingEvents.get(seq); - if (event != null) { - mPendingEvents.remove(seq); - } - return event; - } - - private void deliverKeyEvent(KeyEvent event, boolean sendDone) { - if (ViewDebug.DEBUG_LATENCY) { - mInputEventDeliverTimeNanos = System.nanoTime(); - } - - if (mInputEventConsistencyVerifier != null) { - mInputEventConsistencyVerifier.onKeyEvent(event, 0); - } - - // If there is no view, then the event will not be handled. - if (mView == null || !mAdded) { - finishKeyEvent(event, sendDone, false); - return; - } - - if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView); - - // Perform predispatching before the IME. - if (mView.dispatchKeyEventPreIme(event)) { - finishKeyEvent(event, sendDone, true); - return; - } - - // Dispatch to the IME before propagating down the view hierarchy. - // The IME will eventually call back into handleFinishedEvent. - if (mLastWasImTarget) { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - int seq = enqueuePendingEvent(event, sendDone); - if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq=" - + seq + " event=" + event); - imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback); - return; - } - } - - // Not dispatching to IME, continue with post IME actions. - deliverKeyEventPostIme(event, sendDone); - } - - private void handleFinishedEvent(int seq, boolean handled) { - final KeyEvent event = (KeyEvent)retrievePendingEvent(seq); - if (DEBUG_IMF) Log.v(TAG, "IME finished event: seq=" + seq - + " handled=" + handled + " event=" + event); - if (event != null) { - final boolean sendDone = seq >= 0; - if (handled) { - finishKeyEvent(event, sendDone, true); - } else { - deliverKeyEventPostIme(event, sendDone); - } - } - } - - private void deliverKeyEventPostIme(KeyEvent event, boolean sendDone) { - if (ViewDebug.DEBUG_LATENCY) { - mInputEventDeliverPostImeTimeNanos = System.nanoTime(); - } - - // If the view went away, then the event will not be handled. - if (mView == null || !mAdded) { - finishKeyEvent(event, sendDone, false); - return; - } - - // If the key's purpose is to exit touch mode then we consume it and consider it handled. - if (checkForLeavingTouchModeAndConsume(event)) { - finishKeyEvent(event, sendDone, true); - return; - } - - // Make sure the fallback event policy sees all keys that will be delivered to the - // view hierarchy. - mFallbackEventHandler.preDispatchKeyEvent(event); - - // Deliver the key to the view hierarchy. - if (mView.dispatchKeyEvent(event)) { - finishKeyEvent(event, sendDone, true); - return; - } - - // If the Control modifier is held, try to interpret the key as a shortcut. - if (event.getAction() == KeyEvent.ACTION_UP - && event.isCtrlPressed() - && !KeyEvent.isModifierKey(event.getKeyCode())) { - if (mView.dispatchKeyShortcutEvent(event)) { - finishKeyEvent(event, sendDone, true); - return; - } - } - - // Apply the fallback event policy. - if (mFallbackEventHandler.dispatchKeyEvent(event)) { - finishKeyEvent(event, sendDone, true); - return; - } - - // Handle automatic focus changes. - if (event.getAction() == KeyEvent.ACTION_DOWN) { - int direction = 0; - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_DPAD_LEFT: - if (event.hasNoModifiers()) { - direction = View.FOCUS_LEFT; - } - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (event.hasNoModifiers()) { - direction = View.FOCUS_RIGHT; - } - break; - case KeyEvent.KEYCODE_DPAD_UP: - if (event.hasNoModifiers()) { - direction = View.FOCUS_UP; - } - break; - case KeyEvent.KEYCODE_DPAD_DOWN: - if (event.hasNoModifiers()) { - direction = View.FOCUS_DOWN; - } - break; - case KeyEvent.KEYCODE_TAB: - if (event.hasNoModifiers()) { - direction = View.FOCUS_FORWARD; - } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) { - direction = View.FOCUS_BACKWARD; - } - break; - } - - if (direction != 0) { - View focused = mView != null ? mView.findFocus() : null; - if (focused != null) { - View v = focused.focusSearch(direction); - if (v != null && v != focused) { - // do the math the get the interesting rect - // of previous focused into the coord system of - // newly focused view - focused.getFocusedRect(mTempRect); - if (mView instanceof ViewGroup) { - ((ViewGroup) mView).offsetDescendantRectToMyCoords( - focused, mTempRect); - ((ViewGroup) mView).offsetRectIntoDescendantCoords( - v, mTempRect); - } - if (v.requestFocus(direction, mTempRect)) { - playSoundEffect( - SoundEffectConstants.getContantForFocusDirection(direction)); - finishKeyEvent(event, sendDone, true); - return; - } - } - - // Give the focused view a last chance to handle the dpad key. - if (mView.dispatchUnhandledMove(focused, direction)) { - finishKeyEvent(event, sendDone, true); - return; - } - } - } - } - - // Key was unhandled. - finishKeyEvent(event, sendDone, false); - } - - private void finishKeyEvent(KeyEvent event, boolean sendDone, boolean handled) { - if (sendDone) { - finishInputEvent(event, handled); - } - } - - /* drag/drop */ - void setLocalDragState(Object obj) { - mLocalDragState = obj; - } - - private void handleDragEvent(DragEvent event) { - // From the root, only drag start/end/location are dispatched. entered/exited - // are determined and dispatched by the viewgroup hierarchy, who then report - // that back here for ultimate reporting back to the framework. - if (mView != null && mAdded) { - final int what = event.mAction; - - if (what == DragEvent.ACTION_DRAG_EXITED) { - // A direct EXITED event means that the window manager knows we've just crossed - // a window boundary, so the current drag target within this one must have - // just been exited. Send it the usual notifications and then we're done - // for now. - mView.dispatchDragEvent(event); - } else { - // Cache the drag description when the operation starts, then fill it in - // on subsequent calls as a convenience - if (what == DragEvent.ACTION_DRAG_STARTED) { - mCurrentDragView = null; // Start the current-recipient tracking - mDragDescription = event.mClipDescription; - } else { - event.mClipDescription = mDragDescription; - } - - // For events with a [screen] location, translate into window coordinates - if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) { - mDragPoint.set(event.mX, event.mY); - if (mTranslator != null) { - mTranslator.translatePointInScreenToAppWindow(mDragPoint); - } - - if (mCurScrollY != 0) { - mDragPoint.offset(0, mCurScrollY); - } - - event.mX = mDragPoint.x; - event.mY = mDragPoint.y; - } - - // Remember who the current drag target is pre-dispatch - final View prevDragView = mCurrentDragView; - - // Now dispatch the drag/drop event - boolean result = mView.dispatchDragEvent(event); - - // If we changed apparent drag target, tell the OS about it - if (prevDragView != mCurrentDragView) { - try { - if (prevDragView != null) { - sWindowSession.dragRecipientExited(mWindow); - } - if (mCurrentDragView != null) { - sWindowSession.dragRecipientEntered(mWindow); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to note drag target change"); - } - } - - // Report the drop result when we're done - if (what == DragEvent.ACTION_DROP) { - mDragDescription = null; - try { - Log.i(TAG, "Reporting drop result: " + result); - sWindowSession.reportDropResult(mWindow, result); - } catch (RemoteException e) { - Log.e(TAG, "Unable to report drop result"); - } - } - - // When the drag operation ends, release any local state object - // that may have been in use - if (what == DragEvent.ACTION_DRAG_ENDED) { - setLocalDragState(null); - } - } - } - event.recycle(); - } - - public void handleDispatchSystemUiVisibilityChanged(int visibility) { - if (mView == null) return; - if (mAttachInfo != null) { - mAttachInfo.mSystemUiVisibility = visibility; - } - mView.dispatchSystemUiVisibilityChanged(visibility); - } - - public void getLastTouchPoint(Point outLocation) { - outLocation.x = (int) mLastTouchPoint.x; - outLocation.y = (int) mLastTouchPoint.y; - } - - public void setDragFocus(View newDragTarget) { - if (mCurrentDragView != newDragTarget) { - mCurrentDragView = newDragTarget; - } - } - - private AudioManager getAudioManager() { - if (mView == null) { - throw new IllegalStateException("getAudioManager called when there is no mView"); - } - if (mAudioManager == null) { - mAudioManager = (AudioManager) mView.getContext().getSystemService(Context.AUDIO_SERVICE); - } - return mAudioManager; - } - - public AccessibilityInteractionController getAccessibilityInteractionController() { - if (mView == null) { - throw new IllegalStateException("getAccessibilityInteractionController" - + " called when there is no mView"); - } - if (mAccessibilityInteractionContrtoller == null) { - mAccessibilityInteractionContrtoller = new AccessibilityInteractionController(); - } - return mAccessibilityInteractionContrtoller; - } - - private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, - boolean insetsPending) throws RemoteException { - - float appScale = mAttachInfo.mApplicationScale; - boolean restore = false; - if (params != null && mTranslator != null) { - restore = true; - params.backup(); - mTranslator.translateWindowLayout(params); - } - if (params != null) { - if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params); - } - mPendingConfiguration.seq = 0; - //Log.d(TAG, ">>>>>> CALLING relayout"); - int relayoutResult = sWindowSession.relayout( - mWindow, params, - (int) (mView.getMeasuredWidth() * appScale + 0.5f), - (int) (mView.getMeasuredHeight() * appScale + 0.5f), - viewVisibility, insetsPending, mWinFrame, - mPendingContentInsets, mPendingVisibleInsets, - mPendingConfiguration, mSurface); - //Log.d(TAG, "<<<<<< BACK FROM relayout"); - if (restore) { - params.restore(); - } - - if (mTranslator != null) { - mTranslator.translateRectInScreenToAppWinFrame(mWinFrame); - mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets); - mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets); - } - return relayoutResult; - } - - /** - * {@inheritDoc} - */ - public void playSoundEffect(int effectId) { - checkThread(); - - try { - final AudioManager audioManager = getAudioManager(); - - switch (effectId) { - case SoundEffectConstants.CLICK: - audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); - return; - case SoundEffectConstants.NAVIGATION_DOWN: - audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN); - return; - case SoundEffectConstants.NAVIGATION_LEFT: - audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT); - return; - case SoundEffectConstants.NAVIGATION_RIGHT: - audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT); - return; - case SoundEffectConstants.NAVIGATION_UP: - audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP); - return; - default: - throw new IllegalArgumentException("unknown effect id " + effectId + - " not defined in " + SoundEffectConstants.class.getCanonicalName()); - } - } catch (IllegalStateException e) { - // Exception thrown by getAudioManager() when mView is null - Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e); - e.printStackTrace(); - } - } - - /** - * {@inheritDoc} - */ - public boolean performHapticFeedback(int effectId, boolean always) { - try { - return sWindowSession.performHapticFeedback(mWindow, effectId, always); - } catch (RemoteException e) { - return false; - } - } - - /** - * {@inheritDoc} - */ - public View focusSearch(View focused, int direction) { - checkThread(); - if (!(mView instanceof ViewGroup)) { - return null; - } - return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction); - } - - public void debug() { - mView.debug(); - } - - public void die(boolean immediate) { - if (immediate) { - doDie(); - } else { - sendEmptyMessage(DIE); - } - } - - void doDie() { - checkThread(); - if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface); - synchronized (this) { - if (mAdded && !mFirst) { - destroyHardwareRenderer(); - - int viewVisibility = mView.getVisibility(); - boolean viewVisibilityChanged = mViewVisibility != viewVisibility; - if (mWindowAttributesChanged || viewVisibilityChanged) { - // If layout params have been changed, first give them - // to the window manager to make sure it has the correct - // animation info. - try { - if ((relayoutWindow(mWindowAttributes, viewVisibility, false) - & WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { - sWindowSession.finishDrawing(mWindow); - } - } catch (RemoteException e) { - } - } - - mSurface.release(); - } - if (mAdded) { - mAdded = false; - dispatchDetachedFromWindow(); - } - } - } - - public void requestUpdateConfiguration(Configuration config) { - Message msg = obtainMessage(UPDATE_CONFIGURATION, config); - sendMessage(msg); - } - - private void destroyHardwareRenderer() { - if (mAttachInfo.mHardwareRenderer != null) { - mAttachInfo.mHardwareRenderer.destroy(true); - mAttachInfo.mHardwareRenderer = null; - mAttachInfo.mHardwareAccelerated = false; - } - } - - public void dispatchFinishedEvent(int seq, boolean handled) { - Message msg = obtainMessage(FINISHED_EVENT); - msg.arg1 = seq; - msg.arg2 = handled ? 1 : 0; - sendMessage(msg); - } - - public void dispatchResized(int w, int h, Rect coveredInsets, - Rect visibleInsets, boolean reportDraw, Configuration newConfig) { - if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": w=" + w - + " h=" + h + " coveredInsets=" + coveredInsets.toShortString() - + " visibleInsets=" + visibleInsets.toShortString() - + " reportDraw=" + reportDraw); - Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED); - if (mTranslator != null) { - mTranslator.translateRectInScreenToAppWindow(coveredInsets); - mTranslator.translateRectInScreenToAppWindow(visibleInsets); - w *= mTranslator.applicationInvertedScale; - h *= mTranslator.applicationInvertedScale; - } - msg.arg1 = w; - msg.arg2 = h; - ResizedInfo ri = new ResizedInfo(); - ri.coveredInsets = new Rect(coveredInsets); - ri.visibleInsets = new Rect(visibleInsets); - ri.newConfig = newConfig; - msg.obj = ri; - sendMessage(msg); - } - - private long mInputEventReceiveTimeNanos; - private long mInputEventDeliverTimeNanos; - private long mInputEventDeliverPostImeTimeNanos; - private InputQueue.FinishedCallback mFinishedCallback; - - private final InputHandler mInputHandler = new InputHandler() { - public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) { - startInputEvent(finishedCallback); - dispatchKey(event, true); - } - - public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) { - startInputEvent(finishedCallback); - dispatchMotion(event, true); - } - }; - - public void dispatchKey(KeyEvent event) { - dispatchKey(event, false); - } - - private void dispatchKey(KeyEvent event, boolean sendDone) { - //noinspection ConstantConditions - if (false && event.getAction() == KeyEvent.ACTION_DOWN) { - if (event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) { - if (DBG) Log.d("keydisp", "==================================================="); - if (DBG) Log.d("keydisp", "Focused view Hierarchy is:"); - - debug(); - - if (DBG) Log.d("keydisp", "==================================================="); - } - } - - Message msg = obtainMessage(DISPATCH_KEY); - msg.obj = event; - msg.arg1 = sendDone ? 1 : 0; - - if (LOCAL_LOGV) Log.v( - TAG, "sending key " + event + " to " + mView); - - sendMessageAtTime(msg, event.getEventTime()); - } - - private void dispatchMotion(MotionEvent event, boolean sendDone) { - int source = event.getSource(); - if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { - dispatchPointer(event, sendDone); - } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { - dispatchTrackball(event, sendDone); - } else { - dispatchGenericMotion(event, sendDone); - } - } - - private void dispatchPointer(MotionEvent event, boolean sendDone) { - Message msg = obtainMessage(DISPATCH_POINTER); - msg.obj = event; - msg.arg1 = sendDone ? 1 : 0; - sendMessageAtTime(msg, event.getEventTime()); - } - - private void dispatchTrackball(MotionEvent event, boolean sendDone) { - Message msg = obtainMessage(DISPATCH_TRACKBALL); - msg.obj = event; - msg.arg1 = sendDone ? 1 : 0; - sendMessageAtTime(msg, event.getEventTime()); - } - - private void dispatchGenericMotion(MotionEvent event, boolean sendDone) { - Message msg = obtainMessage(DISPATCH_GENERIC_MOTION); - msg.obj = event; - msg.arg1 = sendDone ? 1 : 0; - sendMessageAtTime(msg, event.getEventTime()); - } - - public void dispatchAppVisibility(boolean visible) { - Message msg = obtainMessage(DISPATCH_APP_VISIBILITY); - msg.arg1 = visible ? 1 : 0; - sendMessage(msg); - } - - public void dispatchGetNewSurface() { - Message msg = obtainMessage(DISPATCH_GET_NEW_SURFACE); - sendMessage(msg); - } - - public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { - Message msg = Message.obtain(); - msg.what = WINDOW_FOCUS_CHANGED; - msg.arg1 = hasFocus ? 1 : 0; - msg.arg2 = inTouchMode ? 1 : 0; - sendMessage(msg); - } - - public void dispatchCloseSystemDialogs(String reason) { - Message msg = Message.obtain(); - msg.what = CLOSE_SYSTEM_DIALOGS; - msg.obj = reason; - sendMessage(msg); - } - - public void dispatchDragEvent(DragEvent event) { - final int what; - if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) { - what = DISPATCH_DRAG_LOCATION_EVENT; - removeMessages(what); - } else { - what = DISPATCH_DRAG_EVENT; - } - Message msg = obtainMessage(what, event); - sendMessage(msg); - } - - public void dispatchSystemUiVisibilityChanged(int visibility) { - sendMessage(obtainMessage(DISPATCH_SYSTEM_UI_VISIBILITY, visibility, 0)); - } - - /** - * The window is getting focus so if there is anything focused/selected - * send an {@link AccessibilityEvent} to announce that. - */ - private void sendAccessibilityEvents() { - if (!mAccessibilityManager.isEnabled()) { - return; - } - mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - View focusedView = mView.findFocus(); - if (focusedView != null && focusedView != mView) { - focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); - } - } - - /** - * Post a callback to send a - * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. - * This event is send at most once every - * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}. - */ - private void postSendWindowContentChangedCallback() { - if (mSendWindowContentChangedAccessibilityEvent == null) { - mSendWindowContentChangedAccessibilityEvent = - new SendWindowContentChangedAccessibilityEvent(); - } - if (!mSendWindowContentChangedAccessibilityEvent.mIsPending) { - mSendWindowContentChangedAccessibilityEvent.mIsPending = true; - postDelayed(mSendWindowContentChangedAccessibilityEvent, - ViewConfiguration.getSendRecurringAccessibilityEventsInterval()); - } - } - - /** - * Remove a posted callback to send a - * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. - */ - private void removeSendWindowContentChangedCallback() { - if (mSendWindowContentChangedAccessibilityEvent != null) { - removeCallbacks(mSendWindowContentChangedAccessibilityEvent); - } - } - - public boolean showContextMenuForChild(View originalView) { - return false; - } - - public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) { - return null; - } - - public void createContextMenu(ContextMenu menu) { - } - - public void childDrawableStateChanged(View child) { - } - - public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { - if (mView == null) { - return false; - } - mAccessibilityManager.sendAccessibilityEvent(event); - return true; - } - - void checkThread() { - if (mThread != Thread.currentThread()) { - throw new CalledFromWrongThreadException( - "Only the original thread that created a view hierarchy can touch its views."); - } - } - - public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { - // ViewAncestor never intercepts touch event, so this can be a no-op - } - - public boolean requestChildRectangleOnScreen(View child, Rect rectangle, - boolean immediate) { - return scrollToRectOrFocus(rectangle, immediate); - } - - class TakenSurfaceHolder extends BaseSurfaceHolder { - @Override - public boolean onAllowLockCanvas() { - return mDrawingAllowed; - } - - @Override - public void onRelayoutContainer() { - // Not currently interesting -- from changing between fixed and layout size. - } - - public void setFormat(int format) { - ((RootViewSurfaceTaker)mView).setSurfaceFormat(format); - } - - public void setType(int type) { - ((RootViewSurfaceTaker)mView).setSurfaceType(type); - } - - @Override - public void onUpdateSurface() { - // We take care of format and type changes on our own. - throw new IllegalStateException("Shouldn't be here"); - } - - public boolean isCreating() { - return mIsCreating; - } - - @Override - public void setFixedSize(int width, int height) { - throw new UnsupportedOperationException( - "Currently only support sizing from layout"); - } - - public void setKeepScreenOn(boolean screenOn) { - ((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn); - } - } - - static class InputMethodCallback extends IInputMethodCallback.Stub { - private WeakReference mViewAncestor; - - public InputMethodCallback(ViewAncestor viewAncestor) { - mViewAncestor = new WeakReference(viewAncestor); - } - - public void finishedEvent(int seq, boolean handled) { - final ViewAncestor viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - viewAncestor.dispatchFinishedEvent(seq, handled); - } - } - - public void sessionCreated(IInputMethodSession session) { - // Stub -- not for use in the client. - } - } - - static class W extends IWindow.Stub { - private final WeakReference mViewAncestor; - - W(ViewAncestor viewAncestor) { - mViewAncestor = new WeakReference(viewAncestor); - } - - public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets, - boolean reportDraw, Configuration newConfig) { - final ViewAncestor viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - viewAncestor.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, - newConfig); - } - } - - public void dispatchAppVisibility(boolean visible) { - final ViewAncestor viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - viewAncestor.dispatchAppVisibility(visible); - } - } - - public void dispatchGetNewSurface() { - final ViewAncestor viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - viewAncestor.dispatchGetNewSurface(); - } - } - - public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { - final ViewAncestor viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - viewAncestor.windowFocusChanged(hasFocus, inTouchMode); - } - } - - private static int checkCallingPermission(String permission) { - try { - return ActivityManagerNative.getDefault().checkPermission( - permission, Binder.getCallingPid(), Binder.getCallingUid()); - } catch (RemoteException e) { - return PackageManager.PERMISSION_DENIED; - } - } - - public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { - final ViewAncestor viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - final View view = viewAncestor.mView; - if (view != null) { - if (checkCallingPermission(Manifest.permission.DUMP) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Insufficient permissions to invoke" - + " executeCommand() from pid=" + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - } - - OutputStream clientStream = null; - try { - clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out); - ViewDebug.dispatchCommand(view, command, parameters, clientStream); - } catch (IOException e) { - e.printStackTrace(); - } finally { - if (clientStream != null) { - try { - clientStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - } - } - - public void closeSystemDialogs(String reason) { - final ViewAncestor viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - viewAncestor.dispatchCloseSystemDialogs(reason); - } - } - - public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, - boolean sync) { - if (sync) { - try { - sWindowSession.wallpaperOffsetsComplete(asBinder()); - } catch (RemoteException e) { - } - } - } - - public void dispatchWallpaperCommand(String action, int x, int y, - int z, Bundle extras, boolean sync) { - if (sync) { - try { - sWindowSession.wallpaperCommandComplete(asBinder(), null); - } catch (RemoteException e) { - } - } - } - - /* Drag/drop */ - public void dispatchDragEvent(DragEvent event) { - final ViewAncestor viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - viewAncestor.dispatchDragEvent(event); - } - } - - public void dispatchSystemUiVisibilityChanged(int visibility) { - final ViewAncestor viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - viewAncestor.dispatchSystemUiVisibilityChanged(visibility); - } - } - } - - /** - * Maintains state information for a single trackball axis, generating - * discrete (DPAD) movements based on raw trackball motion. - */ - static final class TrackballAxis { - /** - * The maximum amount of acceleration we will apply. - */ - static final float MAX_ACCELERATION = 20; - - /** - * The maximum amount of time (in milliseconds) between events in order - * for us to consider the user to be doing fast trackball movements, - * and thus apply an acceleration. - */ - static final long FAST_MOVE_TIME = 150; - - /** - * Scaling factor to the time (in milliseconds) between events to how - * much to multiple/divide the current acceleration. When movement - * is < FAST_MOVE_TIME this multiplies the acceleration; when > - * FAST_MOVE_TIME it divides it. - */ - static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40); - - float position; - float absPosition; - float acceleration = 1; - long lastMoveTime = 0; - int step; - int dir; - int nonAccelMovement; - - void reset(int _step) { - position = 0; - acceleration = 1; - lastMoveTime = 0; - step = _step; - dir = 0; - } - - /** - * Add trackball movement into the state. If the direction of movement - * has been reversed, the state is reset before adding the - * movement (so that you don't have to compensate for any previously - * collected movement before see the result of the movement in the - * new direction). - * - * @return Returns the absolute value of the amount of movement - * collected so far. - */ - float collect(float off, long time, String axis) { - long normTime; - if (off > 0) { - normTime = (long)(off * FAST_MOVE_TIME); - if (dir < 0) { - if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!"); - position = 0; - step = 0; - acceleration = 1; - lastMoveTime = 0; - } - dir = 1; - } else if (off < 0) { - normTime = (long)((-off) * FAST_MOVE_TIME); - if (dir > 0) { - if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!"); - position = 0; - step = 0; - acceleration = 1; - lastMoveTime = 0; - } - dir = -1; - } else { - normTime = 0; - } - - // The number of milliseconds between each movement that is - // considered "normal" and will not result in any acceleration - // or deceleration, scaled by the offset we have here. - if (normTime > 0) { - long delta = time - lastMoveTime; - lastMoveTime = time; - float acc = acceleration; - if (delta < normTime) { - // The user is scrolling rapidly, so increase acceleration. - float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR; - if (scale > 1) acc *= scale; - if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off=" - + off + " normTime=" + normTime + " delta=" + delta - + " scale=" + scale + " acc=" + acc); - acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION; - } else { - // The user is scrolling slowly, so decrease acceleration. - float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR; - if (scale > 1) acc /= scale; - if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off=" - + off + " normTime=" + normTime + " delta=" + delta - + " scale=" + scale + " acc=" + acc); - acceleration = acc > 1 ? acc : 1; - } - } - position += off; - return (absPosition = Math.abs(position)); - } - - /** - * Generate the number of discrete movement events appropriate for - * the currently collected trackball movement. - * - * @param precision The minimum movement required to generate the - * first discrete movement. - * - * @return Returns the number of discrete movements, either positive - * or negative, or 0 if there is not enough trackball movement yet - * for a discrete movement. - */ - int generate(float precision) { - int movement = 0; - nonAccelMovement = 0; - do { - final int dir = position >= 0 ? 1 : -1; - switch (step) { - // If we are going to execute the first step, then we want - // to do this as soon as possible instead of waiting for - // a full movement, in order to make things look responsive. - case 0: - if (absPosition < precision) { - return movement; - } - movement += dir; - nonAccelMovement += dir; - step = 1; - break; - // If we have generated the first movement, then we need - // to wait for the second complete trackball motion before - // generating the second discrete movement. - case 1: - if (absPosition < 2) { - return movement; - } - movement += dir; - nonAccelMovement += dir; - position += dir > 0 ? -2 : 2; - absPosition = Math.abs(position); - step = 2; - break; - // After the first two, we generate discrete movements - // consistently with the trackball, applying an acceleration - // if the trackball is moving quickly. This is a simple - // acceleration on top of what we already compute based - // on how quickly the wheel is being turned, to apply - // a longer increasing acceleration to continuous movement - // in one direction. - default: - if (absPosition < 1) { - return movement; - } - movement += dir; - position += dir >= 0 ? -1 : 1; - absPosition = Math.abs(position); - float acc = acceleration; - acc *= 1.1f; - acceleration = acc < MAX_ACCELERATION ? acc : acceleration; - break; - } - } while (true); - } - } - - public static final class CalledFromWrongThreadException extends AndroidRuntimeException { - public CalledFromWrongThreadException(String msg) { - super(msg); - } - } - - private SurfaceHolder mHolder = new SurfaceHolder() { - // we only need a SurfaceHolder for opengl. it would be nice - // to implement everything else though, especially the callback - // support (opengl doesn't make use of it right now, but eventually - // will). - public Surface getSurface() { - return mSurface; - } - - public boolean isCreating() { - return false; - } - - public void addCallback(Callback callback) { - } - - public void removeCallback(Callback callback) { - } - - public void setFixedSize(int width, int height) { - } - - public void setSizeFromLayout() { - } - - public void setFormat(int format) { - } - - public void setType(int type) { - } - - public void setKeepScreenOn(boolean screenOn) { - } - - public Canvas lockCanvas() { - return null; - } - - public Canvas lockCanvas(Rect dirty) { - return null; - } - - public void unlockCanvasAndPost(Canvas canvas) { - } - public Rect getSurfaceFrame() { - return null; - } - }; - - static RunQueue getRunQueue() { - RunQueue rq = sRunQueues.get(); - if (rq != null) { - return rq; - } - rq = new RunQueue(); - sRunQueues.set(rq); - return rq; - } - - /** - * @hide - */ - static final class RunQueue { - private final ArrayList mActions = new ArrayList(); - - void post(Runnable action) { - postDelayed(action, 0); - } - - void postDelayed(Runnable action, long delayMillis) { - HandlerAction handlerAction = new HandlerAction(); - handlerAction.action = action; - handlerAction.delay = delayMillis; - - synchronized (mActions) { - mActions.add(handlerAction); - } - } - - void removeCallbacks(Runnable action) { - final HandlerAction handlerAction = new HandlerAction(); - handlerAction.action = action; - - synchronized (mActions) { - final ArrayList actions = mActions; - - while (actions.remove(handlerAction)) { - // Keep going - } - } - } - - void executeActions(Handler handler) { - synchronized (mActions) { - final ArrayList actions = mActions; - final int count = actions.size(); - - for (int i = 0; i < count; i++) { - final HandlerAction handlerAction = actions.get(i); - handler.postDelayed(handlerAction.action, handlerAction.delay); - } - - actions.clear(); - } - } - - private static class HandlerAction { - Runnable action; - long delay; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - HandlerAction that = (HandlerAction) o; - return !(action != null ? !action.equals(that.action) : that.action != null); - - } - - @Override - public int hashCode() { - int result = action != null ? action.hashCode() : 0; - result = 31 * result + (int) (delay ^ (delay >>> 32)); - return result; - } - } - } - - /** - * Class for managing the accessibility interaction connection - * based on the global accessibility state. - */ - final class AccessibilityInteractionConnectionManager - implements AccessibilityStateChangeListener { - public void onAccessibilityStateChanged(boolean enabled) { - if (enabled) { - ensureConnection(); - } else { - ensureNoConnection(); - } - } - - public void ensureConnection() { - final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID; - if (!registered) { - mAttachInfo.mAccessibilityWindowId = - mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, - new AccessibilityInteractionConnection(ViewAncestor.this)); - } - } - - public void ensureNoConnection() { - final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID; - if (registered) { - mAttachInfo.mAccessibilityWindowId = View.NO_ID; - mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow); - } - } - } - - /** - * This class is an interface this ViewAncestor provides to the - * AccessibilityManagerService to the latter can interact with - * the view hierarchy in this ViewAncestor. - */ - final class AccessibilityInteractionConnection - extends IAccessibilityInteractionConnection.Stub { - private final WeakReference mViewAncestor; - - AccessibilityInteractionConnection(ViewAncestor viewAncestor) { - mViewAncestor = new WeakReference(viewAncestor); - } - - public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId, - int interactionId, IAccessibilityInteractionConnectionCallback callback) { - if (mViewAncestor.get() != null) { - getAccessibilityInteractionController() - .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityId, - interactionId, callback); - } - } - - public void performAccessibilityAction(int accessibilityId, int action, - int interactionId, IAccessibilityInteractionConnectionCallback callback) { - if (mViewAncestor.get() != null) { - getAccessibilityInteractionController() - .performAccessibilityActionClientThread(accessibilityId, action, interactionId, - callback); - } - } - - public void findAccessibilityNodeInfoByViewId(int viewId, - int interactionId, IAccessibilityInteractionConnectionCallback callback) { - if (mViewAncestor.get() != null) { - getAccessibilityInteractionController() - .findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback); - } - } - - public void findAccessibilityNodeInfosByViewText(String text, int accessibilityId, - int interactionId, IAccessibilityInteractionConnectionCallback callback) { - if (mViewAncestor.get() != null) { - getAccessibilityInteractionController() - .findAccessibilityNodeInfosByViewTextClientThread(text, accessibilityId, - interactionId, callback); - } - } - } - - /** - * Class for managing accessibility interactions initiated from the system - * and targeting the view hierarchy. A *ClientThread method is to be - * called from the interaction connection this ViewAncestor gives the - * system to talk to it and a corresponding *UiThread method that is executed - * on the UI thread. - */ - final class AccessibilityInteractionController { - private static final int POOL_SIZE = 5; - - private FindByAccessibilitytIdPredicate mFindByAccessibilityIdPredicate = - new FindByAccessibilitytIdPredicate(); - - private ArrayList mTempAccessibilityNodeInfoList = - new ArrayList(); - - // Reusable poolable arguments for interacting with the view hierarchy - // to fit more arguments than Message and to avoid sharing objects between - // two messages since several threads can send messages concurrently. - private final Pool mPool = Pools.synchronizedPool(Pools.finitePool( - new PoolableManager() { - public SomeArgs newInstance() { - return new SomeArgs(); - } - - public void onAcquired(SomeArgs info) { - /* do nothing */ - } - - public void onReleased(SomeArgs info) { - info.clear(); - } - }, POOL_SIZE) - ); - - public class SomeArgs implements Poolable { - private SomeArgs mNext; - private boolean mIsPooled; - - public Object arg1; - public Object arg2; - public int argi1; - public int argi2; - public int argi3; - - public SomeArgs getNextPoolable() { - return mNext; - } - - public boolean isPooled() { - return mIsPooled; - } - - public void setNextPoolable(SomeArgs args) { - mNext = args; - } - - public void setPooled(boolean isPooled) { - mIsPooled = isPooled; - } - - private void clear() { - arg1 = null; - arg2 = null; - argi1 = 0; - argi2 = 0; - argi3 = 0; - } - } - - public void findAccessibilityNodeInfoByAccessibilityIdClientThread(int accessibilityId, - int interactionId, IAccessibilityInteractionConnectionCallback callback) { - Message message = Message.obtain(); - message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID; - message.arg1 = accessibilityId; - message.arg2 = interactionId; - message.obj = callback; - sendMessage(message); - } - - public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) { - final int accessibilityId = message.arg1; - final int interactionId = message.arg2; - final IAccessibilityInteractionConnectionCallback callback = - (IAccessibilityInteractionConnectionCallback) message.obj; - - AccessibilityNodeInfo info = null; - try { - FindByAccessibilitytIdPredicate predicate = mFindByAccessibilityIdPredicate; - predicate.init(accessibilityId); - View root = ViewAncestor.this.mView; - View target = root.findViewByPredicate(predicate); - if (target != null && target.isShown()) { - info = target.createAccessibilityNodeInfo(); - } - } finally { - try { - callback.setFindAccessibilityNodeInfoResult(info, interactionId); - } catch (RemoteException re) { - /* ignore - the other side will time out */ - } - } - } - - public void findAccessibilityNodeInfoByViewIdClientThread(int viewId, int interactionId, - IAccessibilityInteractionConnectionCallback callback) { - Message message = Message.obtain(); - message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID; - message.arg1 = viewId; - message.arg2 = interactionId; - message.obj = callback; - sendMessage(message); - } - - public void findAccessibilityNodeInfoByViewIdUiThread(Message message) { - final int viewId = message.arg1; - final int interactionId = message.arg2; - final IAccessibilityInteractionConnectionCallback callback = - (IAccessibilityInteractionConnectionCallback) message.obj; - - AccessibilityNodeInfo info = null; - try { - View root = ViewAncestor.this.mView; - View target = root.findViewById(viewId); - if (target != null && target.isShown()) { - info = target.createAccessibilityNodeInfo(); - } - } finally { - try { - callback.setFindAccessibilityNodeInfoResult(info, interactionId); - } catch (RemoteException re) { - /* ignore - the other side will time out */ - } - } - } - - public void findAccessibilityNodeInfosByViewTextClientThread(String text, - int accessibilityViewId, int interactionId, - IAccessibilityInteractionConnectionCallback callback) { - Message message = Message.obtain(); - message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT; - SomeArgs args = mPool.acquire(); - args.arg1 = text; - args.argi1 = accessibilityViewId; - args.argi2 = interactionId; - args.arg2 = callback; - message.obj = args; - sendMessage(message); - } - - public void findAccessibilityNodeInfosByViewTextUiThread(Message message) { - SomeArgs args = (SomeArgs) message.obj; - final String text = (String) args.arg1; - final int accessibilityViewId = args.argi1; - final int interactionId = args.argi2; - final IAccessibilityInteractionConnectionCallback callback = - (IAccessibilityInteractionConnectionCallback) args.arg2; - mPool.release(args); - - List infos = null; - try { - ArrayList foundViews = mAttachInfo.mFocusablesTempList; - foundViews.clear(); - - View root; - if (accessibilityViewId != View.NO_ID) { - root = findViewByAccessibilityId(accessibilityViewId); - } else { - root = ViewAncestor.this.mView; - } - - if (root == null || !root.isShown()) { - return; - } - - root.findViewsWithText(foundViews, text); - if (foundViews.isEmpty()) { - return; - } - - infos = mTempAccessibilityNodeInfoList; - infos.clear(); - - final int viewCount = foundViews.size(); - for (int i = 0; i < viewCount; i++) { - View foundView = foundViews.get(i); - if (foundView.isShown()) { - infos.add(foundView.createAccessibilityNodeInfo()); - } - } - } finally { - try { - callback.setFindAccessibilityNodeInfosResult(infos, interactionId); - } catch (RemoteException re) { - /* ignore - the other side will time out */ - } - } - } - - public void performAccessibilityActionClientThread(int accessibilityId, int action, - int interactionId, IAccessibilityInteractionConnectionCallback callback) { - Message message = Message.obtain(); - message.what = DO_PERFORM_ACCESSIBILITY_ACTION; - SomeArgs args = mPool.acquire(); - args.argi1 = accessibilityId; - args.argi2 = action; - args.argi3 = interactionId; - args.arg1 = callback; - message.obj = args; - sendMessage(message); - } - - public void perfromAccessibilityActionUiThread(Message message) { - SomeArgs args = (SomeArgs) message.obj; - final int accessibilityId = args.argi1; - final int action = args.argi2; - final int interactionId = args.argi3; - final IAccessibilityInteractionConnectionCallback callback = - (IAccessibilityInteractionConnectionCallback) args.arg1; - mPool.release(args); - - boolean succeeded = false; - try { - switch (action) { - case AccessibilityNodeInfo.ACTION_FOCUS: { - succeeded = performActionFocus(accessibilityId); - } break; - case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: { - succeeded = performActionClearFocus(accessibilityId); - } break; - case AccessibilityNodeInfo.ACTION_SELECT: { - succeeded = performActionSelect(accessibilityId); - } break; - case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: { - succeeded = performActionClearSelection(accessibilityId); - } break; - } - } finally { - try { - callback.setPerformAccessibilityActionResult(succeeded, interactionId); - } catch (RemoteException re) { - /* ignore - the other side will time out */ - } - } - } - - private boolean performActionFocus(int accessibilityId) { - View target = findViewByAccessibilityId(accessibilityId); - if (target == null) { - return false; - } - // Get out of touch mode since accessibility wants to move focus around. - ensureTouchMode(false); - return target.requestFocus(); - } - - private boolean performActionClearFocus(int accessibilityId) { - View target = findViewByAccessibilityId(accessibilityId); - if (target == null) { - return false; - } - if (!target.isFocused()) { - return false; - } - target.clearFocus(); - return !target.isFocused(); - } - - private boolean performActionSelect(int accessibilityId) { - View target = findViewByAccessibilityId(accessibilityId); - if (target == null) { - return false; - } - if (target.isSelected()) { - return false; - } - target.setSelected(true); - return target.isSelected(); - } - - private boolean performActionClearSelection(int accessibilityId) { - View target = findViewByAccessibilityId(accessibilityId); - if (target == null) { - return false; - } - if (!target.isSelected()) { - return false; - } - target.setSelected(false); - return !target.isSelected(); - } - - private View findViewByAccessibilityId(int accessibilityId) { - View root = ViewAncestor.this.mView; - if (root == null) { - return null; - } - mFindByAccessibilityIdPredicate.init(accessibilityId); - View foundView = root.findViewByPredicate(mFindByAccessibilityIdPredicate); - return (foundView != null && foundView.isShown()) ? foundView : null; - } - - private final class FindByAccessibilitytIdPredicate implements Predicate { - public int mSerchedId; - - public void init(int searchedId) { - mSerchedId = searchedId; - } - - public boolean apply(View view) { - return (view.getAccessibilityViewId() == mSerchedId); - } - } - } - - private class SendWindowContentChangedAccessibilityEvent implements Runnable { - public volatile boolean mIsPending; - - public void run() { - if (mView != null) { - // Check again for accessibility state since this is executed delayed. - AccessibilityManager accessibilityManager = - AccessibilityManager.getInstance(mView.mContext); - if (accessibilityManager.isEnabled()) { - // Send the event directly since we do not want to append the - // source text because this is the text for the entire window - // and we just want to notify that the content has changed. - AccessibilityEvent event = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); - mView.onInitializeAccessibilityEvent(event); - accessibilityManager.sendAccessibilityEvent(event); - } - mIsPending = false; - } - } - } -} diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index f7f5a21..b85159b 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -366,7 +366,7 @@ public class ViewDebug { } private static BufferedWriter sHierarchyTraces; - private static ViewAncestor sHierarhcyRoot; + private static ViewRootImpl sHierarhcyRoot; private static String sHierarchyTracePrefix; /** @@ -415,7 +415,7 @@ public class ViewDebug { * @hide */ public static long getViewAncestorInstanceCount() { - return Debug.countInstancesOfClass(ViewAncestor.class); + return Debug.countInstancesOfClass(ViewRootImpl.class); } /** @@ -748,7 +748,7 @@ public class ViewDebug { return; } - sHierarhcyRoot = (ViewAncestor) view.getRootView().getParent(); + sHierarhcyRoot = (ViewRootImpl) view.getRootView().getParent(); } /** @@ -1100,7 +1100,7 @@ public class ViewDebug { private static void outputDisplayList(View root, String parameter) throws IOException { final View view = findView(root, parameter); - view.getViewAncestor().outputDisplayList(view); + view.getViewRootImpl().outputDisplayList(view); } private static void capture(View root, final OutputStream clientStream, String parameter) diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index cb3e9c6..5f7673a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -954,7 +954,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final float tx = event.mX; final float ty = event.mY; - ViewAncestor root = getViewAncestor(); + ViewRootImpl root = getViewRootImpl(); // Dispatch down the view hierarchy switch (event.mAction) { @@ -3839,13 +3839,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (drawAnimation) { if (view != null) { view.mPrivateFlags |= DRAW_ANIMATION; - } else if (parent instanceof ViewAncestor) { - ((ViewAncestor) parent).mIsAnimating = true; + } else if (parent instanceof ViewRootImpl) { + ((ViewRootImpl) parent).mIsAnimating = true; } } - if (parent instanceof ViewAncestor) { - ((ViewAncestor) parent).invalidate(); + if (parent instanceof ViewRootImpl) { + ((ViewRootImpl) parent).invalidate(); parent = null; } else if (view != null) { if ((view.mPrivateFlags & DRAWN) == DRAWN || @@ -3902,8 +3902,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (drawAnimation) { if (view != null) { view.mPrivateFlags |= DRAW_ANIMATION; - } else if (parent instanceof ViewAncestor) { - ((ViewAncestor) parent).mIsAnimating = true; + } else if (parent instanceof ViewRootImpl) { + ((ViewRootImpl) parent).mIsAnimating = true; } } @@ -4426,7 +4426,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // If this group is dirty, check that the parent is dirty as well if ((mPrivateFlags & DIRTY_MASK) != 0) { final ViewParent parent = getParent(); - if (parent != null && !(parent instanceof ViewAncestor)) { + if (parent != null && !(parent instanceof ViewRootImpl)) { if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) { result = false; android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, @@ -4995,7 +4995,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @hide */ public void requestTransitionStart(LayoutTransition transition) { - ViewAncestor viewAncestor = getViewAncestor(); + ViewRootImpl viewAncestor = getViewRootImpl(); viewAncestor.requestTransitionStart(transition); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java new file mode 100644 index 0000000..470493d --- /dev/null +++ b/core/java/android/view/ViewRootImpl.java @@ -0,0 +1,4670 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.Manifest; +import android.animation.LayoutTransition; +import android.app.ActivityManagerNative; +import android.content.ClipDescription; +import android.content.ComponentCallbacks; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.Region; +import android.media.AudioManager; +import android.os.Binder; +import android.os.Bundle; +import android.os.Debug; +import android.os.Handler; +import android.os.LatencyTimer; +import android.os.Looper; +import android.os.Message; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.util.AndroidRuntimeException; +import android.util.DisplayMetrics; +import android.util.EventLog; +import android.util.Log; +import android.util.Pool; +import android.util.Poolable; +import android.util.PoolableManager; +import android.util.Pools; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.View.MeasureSpec; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.IAccessibilityInteractionConnection; +import android.view.accessibility.IAccessibilityInteractionConnectionCallback; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; +import android.widget.Scroller; + +import com.android.internal.policy.PolicyManager; +import com.android.internal.util.Predicate; +import com.android.internal.view.BaseSurfaceHolder; +import com.android.internal.view.IInputMethodCallback; +import com.android.internal.view.IInputMethodSession; +import com.android.internal.view.RootViewSurfaceTaker; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +/** + * The top of a view hierarchy, implementing the needed protocol between View + * and the WindowManager. This is for the most part an internal implementation + * detail of {@link WindowManagerImpl}. + * + * {@hide} + */ +@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"}) +public final class ViewRootImpl extends Handler implements ViewParent, + View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { + private static final String TAG = "ViewAncestor"; + private static final boolean DBG = false; + private static final boolean LOCAL_LOGV = false; + /** @noinspection PointlessBooleanExpression*/ + private static final boolean DEBUG_DRAW = false || LOCAL_LOGV; + private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV; + private static final boolean DEBUG_DIALOG = false || LOCAL_LOGV; + private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV; + private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV; + private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV; + private static final boolean DEBUG_IMF = false || LOCAL_LOGV; + private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV; + private static final boolean WATCH_POINTER = false; + + /** + * Set this system property to true to force the view hierarchy to render + * at 60 Hz. This can be used to measure the potential framerate. + */ + private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering"; + + private static final boolean MEASURE_LATENCY = false; + private static LatencyTimer lt; + + /** + * Maximum time we allow the user to roll the trackball enough to generate + * a key event, before resetting the counters. + */ + static final int MAX_TRACKBALL_DELAY = 250; + + static IWindowSession sWindowSession; + + static final Object mStaticInit = new Object(); + static boolean mInitialized = false; + + static final ThreadLocal sRunQueues = new ThreadLocal(); + + static final ArrayList sFirstDrawHandlers = new ArrayList(); + static boolean sFirstDrawComplete = false; + + static final ArrayList sConfigCallbacks + = new ArrayList(); + + long mLastTrackballTime = 0; + final TrackballAxis mTrackballAxisX = new TrackballAxis(); + final TrackballAxis mTrackballAxisY = new TrackballAxis(); + + int mLastJoystickXDirection; + int mLastJoystickYDirection; + int mLastJoystickXKeyCode; + int mLastJoystickYKeyCode; + + final int[] mTmpLocation = new int[2]; + + final TypedValue mTmpValue = new TypedValue(); + + final InputMethodCallback mInputMethodCallback; + final SparseArray mPendingEvents = new SparseArray(); + int mPendingEventSeq = 0; + + final Thread mThread; + + final WindowLeaked mLocation; + + final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams(); + + final W mWindow; + + View mView; + View mFocusedView; + View mRealFocusedView; // this is not set to null in touch mode + int mViewVisibility; + boolean mAppVisible = true; + + // Set to true if the owner of this window is in the stopped state, + // so the window should no longer be active. + boolean mStopped = false; + + boolean mLastInCompatMode = false; + + SurfaceHolder.Callback2 mSurfaceHolderCallback; + BaseSurfaceHolder mSurfaceHolder; + boolean mIsCreating; + boolean mDrawingAllowed; + + final Region mTransparentRegion; + final Region mPreviousTransparentRegion; + + int mWidth; + int mHeight; + Rect mDirty; + final Rect mCurrentDirty = new Rect(); + final Rect mPreviousDirty = new Rect(); + boolean mIsAnimating; + + CompatibilityInfo.Translator mTranslator; + + final View.AttachInfo mAttachInfo; + InputChannel mInputChannel; + InputQueue.Callback mInputQueueCallback; + InputQueue mInputQueue; + FallbackEventHandler mFallbackEventHandler; + + final Rect mTempRect; // used in the transaction to not thrash the heap. + final Rect mVisRect; // used to retrieve visible rect of focused view. + + boolean mTraversalScheduled; + long mLastTraversalFinishedTimeNanos; + long mLastDrawDurationNanos; + boolean mWillDrawSoon; + boolean mLayoutRequested; + boolean mFirst; + boolean mReportNextDraw; + boolean mFullRedrawNeeded; + boolean mNewSurfaceNeeded; + boolean mHasHadWindowFocus; + boolean mLastWasImTarget; + + boolean mWindowAttributesChanged = false; + + // These can be accessed by any thread, must be protected with a lock. + // Surface can never be reassigned or cleared (use Surface.clear()). + private final Surface mSurface = new Surface(); + + boolean mAdded; + boolean mAddedTouchMode; + + CompatibilityInfoHolder mCompatibilityInfo; + + /*package*/ int mAddNesting; + + // These are accessed by multiple threads. + final Rect mWinFrame; // frame given by window manager. + + final Rect mPendingVisibleInsets = new Rect(); + final Rect mPendingContentInsets = new Rect(); + final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets + = new ViewTreeObserver.InternalInsetsInfo(); + + final Configuration mLastConfiguration = new Configuration(); + final Configuration mPendingConfiguration = new Configuration(); + + class ResizedInfo { + Rect coveredInsets; + Rect visibleInsets; + Configuration newConfig; + } + + boolean mScrollMayChange; + int mSoftInputMode; + View mLastScrolledFocus; + int mScrollY; + int mCurScrollY; + Scroller mScroller; + HardwareLayer mResizeBuffer; + long mResizeBufferStartTime; + int mResizeBufferDuration; + static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator(); + private ArrayList mPendingTransitions; + + final ViewConfiguration mViewConfiguration; + + /* Drag/drop */ + ClipDescription mDragDescription; + View mCurrentDragView; + volatile Object mLocalDragState; + final PointF mDragPoint = new PointF(); + final PointF mLastTouchPoint = new PointF(); + + private boolean mProfileRendering; + private Thread mRenderProfiler; + private volatile boolean mRenderProfilingEnabled; + + /** + * see {@link #playSoundEffect(int)} + */ + AudioManager mAudioManager; + + final AccessibilityManager mAccessibilityManager; + + AccessibilityInteractionController mAccessibilityInteractionContrtoller; + + AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager; + + SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent; + + private final int mDensity; + + /** + * Consistency verifier for debugging purposes. + */ + protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier = + InputEventConsistencyVerifier.isInstrumentationEnabled() ? + new InputEventConsistencyVerifier(this, 0) : null; + + public static IWindowSession getWindowSession(Looper mainLooper) { + synchronized (mStaticInit) { + if (!mInitialized) { + try { + InputMethodManager imm = InputMethodManager.getInstance(mainLooper); + sWindowSession = Display.getWindowManager().openSession( + imm.getClient(), imm.getInputContext()); + mInitialized = true; + } catch (RemoteException e) { + } + } + return sWindowSession; + } + } + + public ViewRootImpl(Context context) { + super(); + + if (MEASURE_LATENCY) { + if (lt == null) { + lt = new LatencyTimer(100, 1000); + } + } + + // Initialize the statics when this class is first instantiated. This is + // done here instead of in the static block because Zygote does not + // allow the spawning of threads. + getWindowSession(context.getMainLooper()); + + mThread = Thread.currentThread(); + mLocation = new WindowLeaked(null); + mLocation.fillInStackTrace(); + mWidth = -1; + mHeight = -1; + mDirty = new Rect(); + mTempRect = new Rect(); + mVisRect = new Rect(); + mWinFrame = new Rect(); + mWindow = new W(this); + mInputMethodCallback = new InputMethodCallback(this); + mViewVisibility = View.GONE; + mTransparentRegion = new Region(); + mPreviousTransparentRegion = new Region(); + mFirst = true; // true for the first time the view is added + mAdded = false; + mAccessibilityManager = AccessibilityManager.getInstance(context); + mAccessibilityInteractionConnectionManager = + new AccessibilityInteractionConnectionManager(); + mAccessibilityManager.addAccessibilityStateChangeListener( + mAccessibilityInteractionConnectionManager); + mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this); + mViewConfiguration = ViewConfiguration.get(context); + mDensity = context.getResources().getDisplayMetrics().densityDpi; + mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context); + mProfileRendering = Boolean.parseBoolean( + SystemProperties.get(PROPERTY_PROFILE_RENDERING, "false")); + } + + public static void addFirstDrawHandler(Runnable callback) { + synchronized (sFirstDrawHandlers) { + if (!sFirstDrawComplete) { + sFirstDrawHandlers.add(callback); + } + } + } + + public static void addConfigCallback(ComponentCallbacks callback) { + synchronized (sConfigCallbacks) { + sConfigCallbacks.add(callback); + } + } + + // FIXME for perf testing only + private boolean mProfile = false; + + /** + * Call this to profile the next traversal call. + * FIXME for perf testing only. Remove eventually + */ + public void profile() { + mProfile = true; + } + + /** + * Indicates whether we are in touch mode. Calling this method triggers an IPC + * call and should be avoided whenever possible. + * + * @return True, if the device is in touch mode, false otherwise. + * + * @hide + */ + static boolean isInTouchMode() { + if (mInitialized) { + try { + return sWindowSession.getInTouchMode(); + } catch (RemoteException e) { + } + } + return false; + } + + /** + * We have one child + */ + public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { + synchronized (this) { + if (mView == null) { + mView = view; + mFallbackEventHandler.setView(view); + mWindowAttributes.copyFrom(attrs); + attrs = mWindowAttributes; + + if (view instanceof RootViewSurfaceTaker) { + mSurfaceHolderCallback = + ((RootViewSurfaceTaker)view).willYouTakeTheSurface(); + if (mSurfaceHolderCallback != null) { + mSurfaceHolder = new TakenSurfaceHolder(); + mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); + } + } + + // If the application owns the surface, don't enable hardware acceleration + if (mSurfaceHolder == null) { + enableHardwareAcceleration(attrs); + } + + CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get(); + mTranslator = compatibilityInfo.getTranslator(); + + if (mTranslator != null) { + mSurface.setCompatibilityTranslator(mTranslator); + } + + boolean restore = false; + if (mTranslator != null) { + restore = true; + attrs.backup(); + mTranslator.translateWindowLayout(attrs); + } + if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs); + + if (!compatibilityInfo.supportsScreen()) { + attrs.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; + mLastInCompatMode = true; + } + + mSoftInputMode = attrs.softInputMode; + mWindowAttributesChanged = true; + mAttachInfo.mRootView = view; + mAttachInfo.mScalingRequired = mTranslator != null; + mAttachInfo.mApplicationScale = + mTranslator == null ? 1.0f : mTranslator.applicationScale; + if (panelParentView != null) { + mAttachInfo.mPanelParentWindowToken + = panelParentView.getApplicationWindowToken(); + } + mAdded = true; + int res; /* = WindowManagerImpl.ADD_OKAY; */ + + // Schedule the first layout -before- adding to the window + // manager, to make sure we do the relayout before receiving + // any other events from the system. + requestLayout(); + mInputChannel = new InputChannel(); + try { + res = sWindowSession.add(mWindow, mWindowAttributes, + getHostVisibility(), mAttachInfo.mContentInsets, + mInputChannel); + } catch (RemoteException e) { + mAdded = false; + mView = null; + mAttachInfo.mRootView = null; + mInputChannel = null; + mFallbackEventHandler.setView(null); + unscheduleTraversals(); + throw new RuntimeException("Adding window failed", e); + } finally { + if (restore) { + attrs.restore(); + } + } + + if (mTranslator != null) { + mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets); + } + mPendingContentInsets.set(mAttachInfo.mContentInsets); + mPendingVisibleInsets.set(0, 0, 0, 0); + if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow); + if (res < WindowManagerImpl.ADD_OKAY) { + mView = null; + mAttachInfo.mRootView = null; + mAdded = false; + mFallbackEventHandler.setView(null); + unscheduleTraversals(); + switch (res) { + case WindowManagerImpl.ADD_BAD_APP_TOKEN: + case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN: + throw new WindowManagerImpl.BadTokenException( + "Unable to add window -- token " + attrs.token + + " is not valid; is your activity running?"); + case WindowManagerImpl.ADD_NOT_APP_TOKEN: + throw new WindowManagerImpl.BadTokenException( + "Unable to add window -- token " + attrs.token + + " is not for an application"); + case WindowManagerImpl.ADD_APP_EXITING: + throw new WindowManagerImpl.BadTokenException( + "Unable to add window -- app for token " + attrs.token + + " is exiting"); + case WindowManagerImpl.ADD_DUPLICATE_ADD: + throw new WindowManagerImpl.BadTokenException( + "Unable to add window -- window " + mWindow + + " has already been added"); + case WindowManagerImpl.ADD_STARTING_NOT_NEEDED: + // Silently ignore -- we would have just removed it + // right away, anyway. + return; + case WindowManagerImpl.ADD_MULTIPLE_SINGLETON: + throw new WindowManagerImpl.BadTokenException( + "Unable to add window " + mWindow + + " -- another window of this type already exists"); + case WindowManagerImpl.ADD_PERMISSION_DENIED: + throw new WindowManagerImpl.BadTokenException( + "Unable to add window " + mWindow + + " -- permission denied for this window type"); + } + throw new RuntimeException( + "Unable to add window -- unknown error code " + res); + } + + if (view instanceof RootViewSurfaceTaker) { + mInputQueueCallback = + ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); + } + if (mInputQueueCallback != null) { + mInputQueue = new InputQueue(mInputChannel); + mInputQueueCallback.onInputQueueCreated(mInputQueue); + } else { + InputQueue.registerInputChannel(mInputChannel, mInputHandler, + Looper.myQueue()); + } + + view.assignParent(this); + mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0; + mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0; + + if (mAccessibilityManager.isEnabled()) { + mAccessibilityInteractionConnectionManager.ensureConnection(); + } + } + } + } + + private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { + mAttachInfo.mHardwareAccelerated = false; + mAttachInfo.mHardwareAccelerationRequested = false; + + // Try to enable hardware acceleration if requested + final boolean hardwareAccelerated = + (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; + + if (hardwareAccelerated) { + if (!HardwareRenderer.isAvailable()) { + mAttachInfo.mHardwareAccelerationRequested = true; + return; + } + + // Only enable hardware acceleration if we are not in the system process + // The window manager creates ViewAncestors to display animated preview windows + // of launching apps and we don't want those to be hardware accelerated + + final boolean systemHwAccelerated = + (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED_SYSTEM) != 0; + + if (!HardwareRenderer.sRendererDisabled || systemHwAccelerated) { + // Don't enable hardware acceleration when we're not on the main thread + if (!systemHwAccelerated && Looper.getMainLooper() != Looper.myLooper()) { + Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware " + + "acceleration outside of the main thread, aborting"); + return; + } + + final boolean translucent = attrs.format != PixelFormat.OPAQUE; + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.destroy(true); + } + mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent); + mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested + = mAttachInfo.mHardwareRenderer != null; + } + } + } + + public View getView() { + return mView; + } + + final WindowLeaked getLocation() { + return mLocation; + } + + void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) { + synchronized (this) { + int oldSoftInputMode = mWindowAttributes.softInputMode; + // preserve compatible window flag if exists. + int compatibleWindowFlag = + mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; + mWindowAttributes.copyFrom(attrs); + mWindowAttributes.flags |= compatibleWindowFlag; + + if (newView) { + mSoftInputMode = attrs.softInputMode; + requestLayout(); + } + // Don't lose the mode we last auto-computed. + if ((attrs.softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { + mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode + & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + | (oldSoftInputMode + & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST); + } + mWindowAttributesChanged = true; + scheduleTraversals(); + } + } + + void handleAppVisibility(boolean visible) { + if (mAppVisible != visible) { + mAppVisible = visible; + scheduleTraversals(); + } + } + + void handleGetNewSurface() { + mNewSurfaceNeeded = true; + mFullRedrawNeeded = true; + scheduleTraversals(); + } + + /** + * {@inheritDoc} + */ + public void requestLayout() { + checkThread(); + mLayoutRequested = true; + scheduleTraversals(); + } + + /** + * {@inheritDoc} + */ + public boolean isLayoutRequested() { + return mLayoutRequested; + } + + public void invalidateChild(View child, Rect dirty) { + checkThread(); + if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty); + if (dirty == null) { + // Fast invalidation for GL-enabled applications; GL must redraw everything + invalidate(); + return; + } + if (mCurScrollY != 0 || mTranslator != null) { + mTempRect.set(dirty); + dirty = mTempRect; + if (mCurScrollY != 0) { + dirty.offset(0, -mCurScrollY); + } + if (mTranslator != null) { + mTranslator.translateRectInAppWindowToScreen(dirty); + } + if (mAttachInfo.mScalingRequired) { + dirty.inset(-1, -1); + } + } + if (!mDirty.isEmpty() && !mDirty.contains(dirty)) { + mAttachInfo.mSetIgnoreDirtyState = true; + mAttachInfo.mIgnoreDirtyState = true; + } + mDirty.union(dirty); + if (!mWillDrawSoon) { + scheduleTraversals(); + } + } + + void invalidate() { + mDirty.set(0, 0, mWidth, mHeight); + scheduleTraversals(); + } + + void setStopped(boolean stopped) { + if (mStopped != stopped) { + mStopped = stopped; + if (!stopped) { + scheduleTraversals(); + } + } + } + + public ViewParent getParent() { + return null; + } + + public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { + invalidateChild(null, dirty); + return null; + } + + public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { + if (child != mView) { + throw new RuntimeException("child is not mine, honest!"); + } + // Note: don't apply scroll offset, because we want to know its + // visibility in the virtual canvas being given to the view hierarchy. + return r.intersect(0, 0, mWidth, mHeight); + } + + public void bringChildToFront(View child) { + } + + public void scheduleTraversals() { + if (!mTraversalScheduled) { + mTraversalScheduled = true; + + //noinspection ConstantConditions + if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) { + final long now = System.nanoTime(); + Log.d(TAG, "Latency: Scheduled traversal, it has been " + + ((now - mLastTraversalFinishedTimeNanos) * 0.000001f) + + "ms since the last traversal finished."); + } + + sendEmptyMessage(DO_TRAVERSAL); + } + } + + public void unscheduleTraversals() { + if (mTraversalScheduled) { + mTraversalScheduled = false; + removeMessages(DO_TRAVERSAL); + } + } + + int getHostVisibility() { + return mAppVisible ? mView.getVisibility() : View.GONE; + } + + void disposeResizeBuffer() { + if (mResizeBuffer != null) { + mResizeBuffer.destroy(); + mResizeBuffer = null; + } + } + + /** + * Add LayoutTransition to the list of transitions to be started in the next traversal. + * This list will be cleared after the transitions on the list are start()'ed. These + * transitionsa re added by LayoutTransition itself when it sets up animations. The setup + * happens during the layout phase of traversal, which we want to complete before any of the + * animations are started (because those animations may side-effect properties that layout + * depends upon, like the bounding rectangles of the affected views). So we add the transition + * to the list and it is started just prior to starting the drawing phase of traversal. + * + * @param transition The LayoutTransition to be started on the next traversal. + * + * @hide + */ + public void requestTransitionStart(LayoutTransition transition) { + if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) { + if (mPendingTransitions == null) { + mPendingTransitions = new ArrayList(); + } + mPendingTransitions.add(transition); + } + } + + private void performTraversals() { + // cache mView since it is used so much below... + final View host = mView; + + if (DBG) { + System.out.println("======================================"); + System.out.println("performTraversals"); + host.debug(); + } + + if (host == null || !mAdded) + return; + + mTraversalScheduled = false; + mWillDrawSoon = true; + boolean windowSizeMayChange = false; + boolean fullRedrawNeeded = mFullRedrawNeeded; + boolean newSurface = false; + boolean surfaceChanged = false; + WindowManager.LayoutParams lp = mWindowAttributes; + + int desiredWindowWidth; + int desiredWindowHeight; + int childWidthMeasureSpec; + int childHeightMeasureSpec; + + final View.AttachInfo attachInfo = mAttachInfo; + + final int viewVisibility = getHostVisibility(); + boolean viewVisibilityChanged = mViewVisibility != viewVisibility + || mNewSurfaceNeeded; + + WindowManager.LayoutParams params = null; + if (mWindowAttributesChanged) { + mWindowAttributesChanged = false; + surfaceChanged = true; + params = lp; + } + CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get(); + if (compatibilityInfo.supportsScreen() == mLastInCompatMode) { + params = lp; + fullRedrawNeeded = true; + mLayoutRequested = true; + if (mLastInCompatMode) { + params.flags &= ~WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; + mLastInCompatMode = false; + } else { + params.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; + mLastInCompatMode = true; + } + } + Rect frame = mWinFrame; + if (mFirst) { + fullRedrawNeeded = true; + mLayoutRequested = true; + + if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) { + // NOTE -- system code, won't try to do compat mode. + Display disp = WindowManagerImpl.getDefault().getDefaultDisplay(); + desiredWindowWidth = disp.getRealWidth(); + desiredWindowHeight = disp.getRealHeight(); + } else { + DisplayMetrics packageMetrics = + mView.getContext().getResources().getDisplayMetrics(); + desiredWindowWidth = packageMetrics.widthPixels; + desiredWindowHeight = packageMetrics.heightPixels; + } + + // For the very first time, tell the view hierarchy that it + // is attached to the window. Note that at this point the surface + // object is not initialized to its backing store, but soon it + // will be (assuming the window is visible). + attachInfo.mSurface = mSurface; + // We used to use the following condition to choose 32 bits drawing caches: + // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888 + // However, windows are now always 32 bits by default, so choose 32 bits + attachInfo.mUse32BitDrawingCache = true; + attachInfo.mHasWindowFocus = false; + attachInfo.mWindowVisibility = viewVisibility; + attachInfo.mRecomputeGlobalAttributes = false; + attachInfo.mKeepScreenOn = false; + attachInfo.mSystemUiVisibility = 0; + viewVisibilityChanged = false; + mLastConfiguration.setTo(host.getResources().getConfiguration()); + host.dispatchAttachedToWindow(attachInfo, 0); + //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn); + + } else { + desiredWindowWidth = frame.width(); + desiredWindowHeight = frame.height(); + if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) { + if (DEBUG_ORIENTATION) Log.v(TAG, + "View " + host + " resized to: " + frame); + fullRedrawNeeded = true; + mLayoutRequested = true; + windowSizeMayChange = true; + } + } + + if (viewVisibilityChanged) { + attachInfo.mWindowVisibility = viewVisibility; + host.dispatchWindowVisibilityChanged(viewVisibility); + if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.destroy(false); + } + } + if (viewVisibility == View.GONE) { + // After making a window gone, we will count it as being + // shown for the first time the next time it gets focus. + mHasHadWindowFocus = false; + } + } + + boolean insetsChanged = false; + + if (mLayoutRequested && !mStopped) { + // Execute enqueued actions on every layout in case a view that was detached + // enqueued an action after being detached + getRunQueue().executeActions(attachInfo.mHandler); + + final Resources res = mView.getContext().getResources(); + + if (mFirst) { + host.fitSystemWindows(mAttachInfo.mContentInsets); + // make sure touch mode code executes by setting cached value + // to opposite of the added touch mode. + mAttachInfo.mInTouchMode = !mAddedTouchMode; + ensureTouchModeLocally(mAddedTouchMode); + } else { + if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) { + if (mWidth > 0 && mHeight > 0 && + mSurface != null && mSurface.isValid() && + !mAttachInfo.mTurnOffWindowResizeAnim && + mAttachInfo.mHardwareRenderer != null && + mAttachInfo.mHardwareRenderer.isEnabled() && + mAttachInfo.mHardwareRenderer.validate() && + lp != null && !PixelFormat.formatHasAlpha(lp.format)) { + + disposeResizeBuffer(); + + boolean completed = false; + HardwareCanvas canvas = null; + try { + if (mResizeBuffer == null) { + mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer( + mWidth, mHeight, false); + } else if (mResizeBuffer.getWidth() != mWidth || + mResizeBuffer.getHeight() != mHeight) { + mResizeBuffer.resize(mWidth, mHeight); + } + canvas = mResizeBuffer.start(mAttachInfo.mHardwareCanvas); + canvas.setViewport(mWidth, mHeight); + canvas.onPreDraw(null); + final int restoreCount = canvas.save(); + + canvas.drawColor(0xff000000, PorterDuff.Mode.SRC); + + int yoff; + final boolean scrolling = mScroller != null + && mScroller.computeScrollOffset(); + if (scrolling) { + yoff = mScroller.getCurrY(); + mScroller.abortAnimation(); + } else { + yoff = mScrollY; + } + + canvas.translate(0, -yoff); + if (mTranslator != null) { + mTranslator.translateCanvas(canvas); + } + + mView.draw(canvas); + + mResizeBufferStartTime = SystemClock.uptimeMillis(); + mResizeBufferDuration = mView.getResources().getInteger( + com.android.internal.R.integer.config_mediumAnimTime); + completed = true; + + canvas.restoreToCount(restoreCount); + } catch (OutOfMemoryError e) { + Log.w(TAG, "Not enough memory for content change anim buffer", e); + } finally { + if (canvas != null) { + canvas.onPostDraw(); + } + if (mResizeBuffer != null) { + mResizeBuffer.end(mAttachInfo.mHardwareCanvas); + if (!completed) { + mResizeBuffer.destroy(); + mResizeBuffer = null; + } + } + } + } + mAttachInfo.mContentInsets.set(mPendingContentInsets); + host.fitSystemWindows(mAttachInfo.mContentInsets); + insetsChanged = true; + if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: " + + mAttachInfo.mContentInsets); + } + if (!mAttachInfo.mVisibleInsets.equals(mPendingVisibleInsets)) { + mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets); + if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: " + + mAttachInfo.mVisibleInsets); + } + if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT + || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { + windowSizeMayChange = true; + + if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) { + // NOTE -- system code, won't try to do compat mode. + Display disp = WindowManagerImpl.getDefault().getDefaultDisplay(); + desiredWindowWidth = disp.getRealWidth(); + desiredWindowHeight = disp.getRealHeight(); + } else { + DisplayMetrics packageMetrics = res.getDisplayMetrics(); + desiredWindowWidth = packageMetrics.widthPixels; + desiredWindowHeight = packageMetrics.heightPixels; + } + } + } + + // Ask host how big it wants to be + if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG, + "Measuring " + host + " in display " + desiredWindowWidth + + "x" + desiredWindowHeight + "..."); + + boolean goodMeasure = false; + if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) { + // On large screens, we don't want to allow dialogs to just + // stretch to fill the entire width of the screen to display + // one line of text. First try doing the layout at a smaller + // size to see if it will fit. + final DisplayMetrics packageMetrics = res.getDisplayMetrics(); + res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true); + int baseSize = 0; + if (mTmpValue.type == TypedValue.TYPE_DIMENSION) { + baseSize = (int)mTmpValue.getDimension(packageMetrics); + } + if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize); + if (baseSize != 0 && desiredWindowWidth > baseSize) { + childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); + childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); + host.measure(childWidthMeasureSpec, childHeightMeasureSpec); + if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" + + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); + if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { + goodMeasure = true; + } else { + // Didn't fit in that size... try expanding a bit. + baseSize = (baseSize+desiredWindowWidth)/2; + if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize=" + + baseSize); + childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); + host.measure(childWidthMeasureSpec, childHeightMeasureSpec); + if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" + + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); + if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { + if (DEBUG_DIALOG) Log.v(TAG, "Good!"); + goodMeasure = true; + } + } + } + } + + if (!goodMeasure) { + childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); + childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); + host.measure(childWidthMeasureSpec, childHeightMeasureSpec); + if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) { + windowSizeMayChange = true; + } + } + + if (DBG) { + System.out.println("======================================"); + System.out.println("performTraversals -- after measure"); + host.debug(); + } + } + + if (attachInfo.mRecomputeGlobalAttributes && host.mAttachInfo != null) { + //Log.i(TAG, "Computing view hierarchy attributes!"); + attachInfo.mRecomputeGlobalAttributes = false; + boolean oldScreenOn = attachInfo.mKeepScreenOn; + int oldVis = attachInfo.mSystemUiVisibility; + attachInfo.mKeepScreenOn = false; + attachInfo.mSystemUiVisibility = 0; + attachInfo.mHasSystemUiListeners = false; + host.dispatchCollectViewAttributes(0); + if (attachInfo.mKeepScreenOn != oldScreenOn + || attachInfo.mSystemUiVisibility != oldVis + || attachInfo.mHasSystemUiListeners) { + params = lp; + } + } + + if (mFirst || attachInfo.mViewVisibilityChanged) { + attachInfo.mViewVisibilityChanged = false; + int resizeMode = mSoftInputMode & + WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; + // If we are in auto resize mode, then we need to determine + // what mode to use now. + if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { + final int N = attachInfo.mScrollContainers.size(); + for (int i=0; i ci.left || vi.top > ci.top + || vi.right > ci.right || vi.bottom > ci.bottom) { + // We'll assume that we aren't going to change the scroll + // offset, since we want to avoid that unless it is actually + // going to make the focus visible... otherwise we scroll + // all over the place. + scrollY = mScrollY; + // We can be called for two different situations: during a draw, + // to update the scroll position if the focus has changed (in which + // case 'rectangle' is null), or in response to a + // requestChildRectangleOnScreen() call (in which case 'rectangle' + // is non-null and we just want to scroll to whatever that + // rectangle is). + View focus = mRealFocusedView; + + // When in touch mode, focus points to the previously focused view, + // which may have been removed from the view hierarchy. The following + // line checks whether the view is still in our hierarchy. + if (focus == null || focus.mAttachInfo != mAttachInfo) { + mRealFocusedView = null; + return false; + } + + if (focus != mLastScrolledFocus) { + // If the focus has changed, then ignore any requests to scroll + // to a rectangle; first we want to make sure the entire focus + // view is visible. + rectangle = null; + } + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus + + " rectangle=" + rectangle + " ci=" + ci + + " vi=" + vi); + if (focus == mLastScrolledFocus && !mScrollMayChange + && rectangle == null) { + // Optimization: if the focus hasn't changed since last + // time, and no layout has happened, then just leave things + // as they are. + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Keeping scroll y=" + + mScrollY + " vi=" + vi.toShortString()); + } else if (focus != null) { + // We need to determine if the currently focused view is + // within the visible part of the window and, if not, apply + // a pan so it can be seen. + mLastScrolledFocus = focus; + mScrollMayChange = false; + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?"); + // Try to find the rectangle from the focus view. + if (focus.getGlobalVisibleRect(mVisRect, null)) { + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Root w=" + + mView.getWidth() + " h=" + mView.getHeight() + + " ci=" + ci.toShortString() + + " vi=" + vi.toShortString()); + if (rectangle == null) { + focus.getFocusedRect(mTempRect); + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Focus " + focus + + ": focusRect=" + mTempRect.toShortString()); + if (mView instanceof ViewGroup) { + ((ViewGroup) mView).offsetDescendantRectToMyCoords( + focus, mTempRect); + } + if (DEBUG_INPUT_RESIZE) Log.v(TAG, + "Focus in window: focusRect=" + + mTempRect.toShortString() + + " visRect=" + mVisRect.toShortString()); + } else { + mTempRect.set(rectangle); + if (DEBUG_INPUT_RESIZE) Log.v(TAG, + "Request scroll to rect: " + + mTempRect.toShortString() + + " visRect=" + mVisRect.toShortString()); + } + if (mTempRect.intersect(mVisRect)) { + if (DEBUG_INPUT_RESIZE) Log.v(TAG, + "Focus window visible rect: " + + mTempRect.toShortString()); + if (mTempRect.height() > + (mView.getHeight()-vi.top-vi.bottom)) { + // If the focus simply is not going to fit, then + // best is probably just to leave things as-is. + if (DEBUG_INPUT_RESIZE) Log.v(TAG, + "Too tall; leaving scrollY=" + scrollY); + } else if ((mTempRect.top-scrollY) < vi.top) { + scrollY -= vi.top - (mTempRect.top-scrollY); + if (DEBUG_INPUT_RESIZE) Log.v(TAG, + "Top covered; scrollY=" + scrollY); + } else if ((mTempRect.bottom-scrollY) + > (mView.getHeight()-vi.bottom)) { + scrollY += (mTempRect.bottom-scrollY) + - (mView.getHeight()-vi.bottom); + if (DEBUG_INPUT_RESIZE) Log.v(TAG, + "Bottom covered; scrollY=" + scrollY); + } + handled = true; + } + } + } + } + + if (scrollY != mScrollY) { + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old=" + + mScrollY + " , new=" + scrollY); + if (!immediate && mResizeBuffer == null) { + if (mScroller == null) { + mScroller = new Scroller(mView.getContext()); + } + mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY); + } else if (mScroller != null) { + mScroller.abortAnimation(); + } + mScrollY = scrollY; + } + + return handled; + } + + public void requestChildFocus(View child, View focused) { + checkThread(); + if (mFocusedView != focused) { + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused); + scheduleTraversals(); + } + mFocusedView = mRealFocusedView = focused; + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now " + + mFocusedView); + } + + public void clearChildFocus(View child) { + checkThread(); + + View oldFocus = mFocusedView; + + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus"); + mFocusedView = mRealFocusedView = null; + if (mView != null && !mView.hasFocus()) { + // If a view gets the focus, the listener will be invoked from requestChildFocus() + if (!mView.requestFocus(View.FOCUS_FORWARD)) { + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); + } + } else if (oldFocus != null) { + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); + } + } + + + public void focusableViewAvailable(View v) { + checkThread(); + + if (mView != null) { + if (!mView.hasFocus()) { + v.requestFocus(); + } else { + // the one case where will transfer focus away from the current one + // is if the current view is a view group that prefers to give focus + // to its children first AND the view is a descendant of it. + mFocusedView = mView.findFocus(); + boolean descendantsHaveDibsOnFocus = + (mFocusedView instanceof ViewGroup) && + (((ViewGroup) mFocusedView).getDescendantFocusability() == + ViewGroup.FOCUS_AFTER_DESCENDANTS); + if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) { + // If a view gets the focus, the listener will be invoked from requestChildFocus() + v.requestFocus(); + } + } + } + } + + public void recomputeViewAttributes(View child) { + checkThread(); + if (mView == child) { + mAttachInfo.mRecomputeGlobalAttributes = true; + if (!mWillDrawSoon) { + scheduleTraversals(); + } + } + } + + void dispatchDetachedFromWindow() { + if (mView != null && mView.mAttachInfo != null) { + mView.dispatchDetachedFromWindow(); + } + + mAccessibilityInteractionConnectionManager.ensureNoConnection(); + mAccessibilityManager.removeAccessibilityStateChangeListener( + mAccessibilityInteractionConnectionManager); + removeSendWindowContentChangedCallback(); + + mView = null; + mAttachInfo.mRootView = null; + mAttachInfo.mSurface = null; + + destroyHardwareRenderer(); + + mSurface.release(); + + if (mInputChannel != null) { + if (mInputQueueCallback != null) { + mInputQueueCallback.onInputQueueDestroyed(mInputQueue); + mInputQueueCallback = null; + } else { + InputQueue.unregisterInputChannel(mInputChannel); + } + } + try { + sWindowSession.remove(mWindow); + } catch (RemoteException e) { + } + + // Dispose the input channel after removing the window so the Window Manager + // doesn't interpret the input channel being closed as an abnormal termination. + if (mInputChannel != null) { + mInputChannel.dispose(); + mInputChannel = null; + } + } + + void updateConfiguration(Configuration config, boolean force) { + if (DEBUG_CONFIGURATION) Log.v(TAG, + "Applying new config to window " + + mWindowAttributes.getTitle() + + ": " + config); + + CompatibilityInfo ci = mCompatibilityInfo.getIfNeeded(); + if (ci != null) { + config = new Configuration(config); + ci.applyToConfiguration(config); + } + + synchronized (sConfigCallbacks) { + for (int i=sConfigCallbacks.size()-1; i>=0; i--) { + sConfigCallbacks.get(i).onConfigurationChanged(config); + } + } + if (mView != null) { + // At this point the resources have been updated to + // have the most recent config, whatever that is. Use + // the on in them which may be newer. + config = mView.getResources().getConfiguration(); + if (force || mLastConfiguration.diff(config) != 0) { + mLastConfiguration.setTo(config); + mView.dispatchConfigurationChanged(config); + } + } + } + + /** + * Return true if child is an ancestor of parent, (or equal to the parent). + */ + private static boolean isViewDescendantOf(View child, View parent) { + if (child == parent) { + return true; + } + + final ViewParent theParent = child.getParent(); + return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); + } + + private static void forceLayout(View view) { + view.forceLayout(); + if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup) view; + final int count = group.getChildCount(); + for (int i = 0; i < count; i++) { + forceLayout(group.getChildAt(i)); + } + } + } + + public final static int DO_TRAVERSAL = 1000; + public final static int DIE = 1001; + public final static int RESIZED = 1002; + public final static int RESIZED_REPORT = 1003; + public final static int WINDOW_FOCUS_CHANGED = 1004; + public final static int DISPATCH_KEY = 1005; + public final static int DISPATCH_POINTER = 1006; + public final static int DISPATCH_TRACKBALL = 1007; + public final static int DISPATCH_APP_VISIBILITY = 1008; + public final static int DISPATCH_GET_NEW_SURFACE = 1009; + public final static int FINISHED_EVENT = 1010; + public final static int DISPATCH_KEY_FROM_IME = 1011; + public final static int FINISH_INPUT_CONNECTION = 1012; + public final static int CHECK_FOCUS = 1013; + public final static int CLOSE_SYSTEM_DIALOGS = 1014; + public final static int DISPATCH_DRAG_EVENT = 1015; + public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016; + public final static int DISPATCH_SYSTEM_UI_VISIBILITY = 1017; + public final static int DISPATCH_GENERIC_MOTION = 1018; + public final static int UPDATE_CONFIGURATION = 1019; + public final static int DO_PERFORM_ACCESSIBILITY_ACTION = 1020; + public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1021; + public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022; + public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1023; + + @Override + public String getMessageName(Message message) { + switch (message.what) { + case DO_TRAVERSAL: + return "DO_TRAVERSAL"; + case DIE: + return "DIE"; + case RESIZED: + return "RESIZED"; + case RESIZED_REPORT: + return "RESIZED_REPORT"; + case WINDOW_FOCUS_CHANGED: + return "WINDOW_FOCUS_CHANGED"; + case DISPATCH_KEY: + return "DISPATCH_KEY"; + case DISPATCH_POINTER: + return "DISPATCH_POINTER"; + case DISPATCH_TRACKBALL: + return "DISPATCH_TRACKBALL"; + case DISPATCH_APP_VISIBILITY: + return "DISPATCH_APP_VISIBILITY"; + case DISPATCH_GET_NEW_SURFACE: + return "DISPATCH_GET_NEW_SURFACE"; + case FINISHED_EVENT: + return "FINISHED_EVENT"; + case DISPATCH_KEY_FROM_IME: + return "DISPATCH_KEY_FROM_IME"; + case FINISH_INPUT_CONNECTION: + return "FINISH_INPUT_CONNECTION"; + case CHECK_FOCUS: + return "CHECK_FOCUS"; + case CLOSE_SYSTEM_DIALOGS: + return "CLOSE_SYSTEM_DIALOGS"; + case DISPATCH_DRAG_EVENT: + return "DISPATCH_DRAG_EVENT"; + case DISPATCH_DRAG_LOCATION_EVENT: + return "DISPATCH_DRAG_LOCATION_EVENT"; + case DISPATCH_SYSTEM_UI_VISIBILITY: + return "DISPATCH_SYSTEM_UI_VISIBILITY"; + case DISPATCH_GENERIC_MOTION: + return "DISPATCH_GENERIC_MOTION"; + case UPDATE_CONFIGURATION: + return "UPDATE_CONFIGURATION"; + case DO_PERFORM_ACCESSIBILITY_ACTION: + return "DO_PERFORM_ACCESSIBILITY_ACTION"; + case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: + return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID"; + case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: + return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID"; + case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: + return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT"; + + } + return super.getMessageName(message); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case View.AttachInfo.INVALIDATE_MSG: + ((View) msg.obj).invalidate(); + break; + case View.AttachInfo.INVALIDATE_RECT_MSG: + final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj; + info.target.invalidate(info.left, info.top, info.right, info.bottom); + info.release(); + break; + case DO_TRAVERSAL: + if (mProfile) { + Debug.startMethodTracing("ViewAncestor"); + } + + final long traversalStartTime; + if (ViewDebug.DEBUG_LATENCY) { + traversalStartTime = System.nanoTime(); + mLastDrawDurationNanos = 0; + } + + performTraversals(); + + if (ViewDebug.DEBUG_LATENCY) { + long now = System.nanoTime(); + Log.d(TAG, "Latency: Spent " + + ((now - traversalStartTime) * 0.000001f) + + "ms in performTraversals(), with " + + (mLastDrawDurationNanos * 0.000001f) + + "ms of that time in draw()"); + mLastTraversalFinishedTimeNanos = now; + } + + if (mProfile) { + Debug.stopMethodTracing(); + mProfile = false; + } + break; + case FINISHED_EVENT: + handleFinishedEvent(msg.arg1, msg.arg2 != 0); + break; + case DISPATCH_KEY: + deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0); + break; + case DISPATCH_POINTER: + deliverPointerEvent((MotionEvent) msg.obj, msg.arg1 != 0); + break; + case DISPATCH_TRACKBALL: + deliverTrackballEvent((MotionEvent) msg.obj, msg.arg1 != 0); + break; + case DISPATCH_GENERIC_MOTION: + deliverGenericMotionEvent((MotionEvent) msg.obj, msg.arg1 != 0); + break; + case DISPATCH_APP_VISIBILITY: + handleAppVisibility(msg.arg1 != 0); + break; + case DISPATCH_GET_NEW_SURFACE: + handleGetNewSurface(); + break; + case RESIZED: + ResizedInfo ri = (ResizedInfo)msg.obj; + + if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2 + && mPendingContentInsets.equals(ri.coveredInsets) + && mPendingVisibleInsets.equals(ri.visibleInsets) + && ((ResizedInfo)msg.obj).newConfig == null) { + break; + } + // fall through... + case RESIZED_REPORT: + if (mAdded) { + Configuration config = ((ResizedInfo)msg.obj).newConfig; + if (config != null) { + updateConfiguration(config, false); + } + mWinFrame.left = 0; + mWinFrame.right = msg.arg1; + mWinFrame.top = 0; + mWinFrame.bottom = msg.arg2; + mPendingContentInsets.set(((ResizedInfo)msg.obj).coveredInsets); + mPendingVisibleInsets.set(((ResizedInfo)msg.obj).visibleInsets); + if (msg.what == RESIZED_REPORT) { + mReportNextDraw = true; + } + + if (mView != null) { + forceLayout(mView); + } + requestLayout(); + } + break; + case WINDOW_FOCUS_CHANGED: { + if (mAdded) { + boolean hasWindowFocus = msg.arg1 != 0; + mAttachInfo.mHasWindowFocus = hasWindowFocus; + + profileRendering(hasWindowFocus); + + if (hasWindowFocus) { + boolean inTouchMode = msg.arg2 != 0; + ensureTouchModeLocally(inTouchMode); + + if (mAttachInfo.mHardwareRenderer != null && + mSurface != null && mSurface.isValid()) { + mFullRedrawNeeded = true; + try { + mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight, + mAttachInfo, mHolder); + } catch (Surface.OutOfResourcesException e) { + Log.e(TAG, "OutOfResourcesException locking surface", e); + try { + if (!sWindowSession.outOfMemory(mWindow)) { + Slog.w(TAG, "No processes killed for memory; killing self"); + Process.killProcess(Process.myPid()); + } + } catch (RemoteException ex) { + } + // Retry in a bit. + sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500); + return; + } + } + } + + mLastWasImTarget = WindowManager.LayoutParams + .mayUseInputMethod(mWindowAttributes.flags); + + InputMethodManager imm = InputMethodManager.peekInstance(); + if (mView != null) { + if (hasWindowFocus && imm != null && mLastWasImTarget) { + imm.startGettingWindowFocus(mView); + } + mAttachInfo.mKeyDispatchState.reset(); + mView.dispatchWindowFocusChanged(hasWindowFocus); + } + + // Note: must be done after the focus change callbacks, + // so all of the view state is set up correctly. + if (hasWindowFocus) { + if (imm != null && mLastWasImTarget) { + imm.onWindowFocus(mView, mView.findFocus(), + mWindowAttributes.softInputMode, + !mHasHadWindowFocus, mWindowAttributes.flags); + } + // Clear the forward bit. We can just do this directly, since + // the window manager doesn't care about it. + mWindowAttributes.softInputMode &= + ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; + ((WindowManager.LayoutParams)mView.getLayoutParams()) + .softInputMode &= + ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; + mHasHadWindowFocus = true; + } + + if (hasWindowFocus && mView != null) { + sendAccessibilityEvents(); + } + } + } break; + case DIE: + doDie(); + break; + case DISPATCH_KEY_FROM_IME: { + if (LOCAL_LOGV) Log.v( + TAG, "Dispatching key " + + msg.obj + " from IME to " + mView); + KeyEvent event = (KeyEvent)msg.obj; + if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) { + // The IME is trying to say this event is from the + // system! Bad bad bad! + //noinspection UnusedAssignment + event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM); + } + deliverKeyEventPostIme((KeyEvent)msg.obj, false); + } break; + case FINISH_INPUT_CONNECTION: { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + imm.reportFinishInputConnection((InputConnection)msg.obj); + } + } break; + case CHECK_FOCUS: { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + imm.checkFocus(); + } + } break; + case CLOSE_SYSTEM_DIALOGS: { + if (mView != null) { + mView.onCloseSystemDialogs((String)msg.obj); + } + } break; + case DISPATCH_DRAG_EVENT: + case DISPATCH_DRAG_LOCATION_EVENT: { + DragEvent event = (DragEvent)msg.obj; + event.mLocalState = mLocalDragState; // only present when this app called startDrag() + handleDragEvent(event); + } break; + case DISPATCH_SYSTEM_UI_VISIBILITY: { + handleDispatchSystemUiVisibilityChanged(msg.arg1); + } break; + case UPDATE_CONFIGURATION: { + Configuration config = (Configuration)msg.obj; + if (config.isOtherSeqNewer(mLastConfiguration)) { + config = mLastConfiguration; + } + updateConfiguration(config, false); + } break; + case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: { + if (mView != null) { + getAccessibilityInteractionController() + .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg); + } + } break; + case DO_PERFORM_ACCESSIBILITY_ACTION: { + if (mView != null) { + getAccessibilityInteractionController() + .perfromAccessibilityActionUiThread(msg); + } + } break; + case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: { + if (mView != null) { + getAccessibilityInteractionController() + .findAccessibilityNodeInfoByViewIdUiThread(msg); + } + } break; + case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: { + if (mView != null) { + getAccessibilityInteractionController() + .findAccessibilityNodeInfosByViewTextUiThread(msg); + } + } break; + } + } + + private void startInputEvent(InputQueue.FinishedCallback finishedCallback) { + if (mFinishedCallback != null) { + Slog.w(TAG, "Received a new input event from the input queue but there is " + + "already an unfinished input event in progress."); + } + + if (ViewDebug.DEBUG_LATENCY) { + mInputEventReceiveTimeNanos = System.nanoTime(); + mInputEventDeliverTimeNanos = 0; + mInputEventDeliverPostImeTimeNanos = 0; + } + + mFinishedCallback = finishedCallback; + } + + private void finishInputEvent(InputEvent event, boolean handled) { + if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished"); + + if (mFinishedCallback == null) { + Slog.w(TAG, "Attempted to tell the input queue that the current input event " + + "is finished but there is no input event actually in progress."); + return; + } + + if (ViewDebug.DEBUG_LATENCY) { + final long now = System.nanoTime(); + final long eventTime = event.getEventTimeNano(); + final StringBuilder msg = new StringBuilder(); + msg.append("Latency: Spent "); + msg.append((now - mInputEventReceiveTimeNanos) * 0.000001f); + msg.append("ms processing "); + if (event instanceof KeyEvent) { + final KeyEvent keyEvent = (KeyEvent)event; + msg.append("key event, action="); + msg.append(KeyEvent.actionToString(keyEvent.getAction())); + } else { + final MotionEvent motionEvent = (MotionEvent)event; + msg.append("motion event, action="); + msg.append(MotionEvent.actionToString(motionEvent.getAction())); + msg.append(", historySize="); + msg.append(motionEvent.getHistorySize()); + } + msg.append(", handled="); + msg.append(handled); + msg.append(", received at +"); + msg.append((mInputEventReceiveTimeNanos - eventTime) * 0.000001f); + if (mInputEventDeliverTimeNanos != 0) { + msg.append("ms, delivered at +"); + msg.append((mInputEventDeliverTimeNanos - eventTime) * 0.000001f); + } + if (mInputEventDeliverPostImeTimeNanos != 0) { + msg.append("ms, delivered post IME at +"); + msg.append((mInputEventDeliverPostImeTimeNanos - eventTime) * 0.000001f); + } + msg.append("ms, finished at +"); + msg.append((now - eventTime) * 0.000001f); + msg.append("ms."); + Log.d(TAG, msg.toString()); + } + + mFinishedCallback.finished(handled); + mFinishedCallback = null; + } + + /** + * Something in the current window tells us we need to change the touch mode. For + * example, we are not in touch mode, and the user touches the screen. + * + * If the touch mode has changed, tell the window manager, and handle it locally. + * + * @param inTouchMode Whether we want to be in touch mode. + * @return True if the touch mode changed and focus changed was changed as a result + */ + boolean ensureTouchMode(boolean inTouchMode) { + if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current " + + "touch mode is " + mAttachInfo.mInTouchMode); + if (mAttachInfo.mInTouchMode == inTouchMode) return false; + + // tell the window manager + try { + sWindowSession.setInTouchMode(inTouchMode); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + // handle the change + return ensureTouchModeLocally(inTouchMode); + } + + /** + * Ensure that the touch mode for this window is set, and if it is changing, + * take the appropriate action. + * @param inTouchMode Whether we want to be in touch mode. + * @return True if the touch mode changed and focus changed was changed as a result + */ + private boolean ensureTouchModeLocally(boolean inTouchMode) { + if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current " + + "touch mode is " + mAttachInfo.mInTouchMode); + + if (mAttachInfo.mInTouchMode == inTouchMode) return false; + + mAttachInfo.mInTouchMode = inTouchMode; + mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode); + + return (inTouchMode) ? enterTouchMode() : leaveTouchMode(); + } + + private boolean enterTouchMode() { + if (mView != null) { + if (mView.hasFocus()) { + // note: not relying on mFocusedView here because this could + // be when the window is first being added, and mFocused isn't + // set yet. + final View focused = mView.findFocus(); + if (focused != null && !focused.isFocusableInTouchMode()) { + + final ViewGroup ancestorToTakeFocus = + findAncestorToTakeFocusInTouchMode(focused); + if (ancestorToTakeFocus != null) { + // there is an ancestor that wants focus after its descendants that + // is focusable in touch mode.. give it focus + return ancestorToTakeFocus.requestFocus(); + } else { + // nothing appropriate to have focus in touch mode, clear it out + mView.unFocus(); + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null); + mFocusedView = null; + return true; + } + } + } + } + return false; + } + + + /** + * Find an ancestor of focused that wants focus after its descendants and is + * focusable in touch mode. + * @param focused The currently focused view. + * @return An appropriate view, or null if no such view exists. + */ + private ViewGroup findAncestorToTakeFocusInTouchMode(View focused) { + ViewParent parent = focused.getParent(); + while (parent instanceof ViewGroup) { + final ViewGroup vgParent = (ViewGroup) parent; + if (vgParent.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS + && vgParent.isFocusableInTouchMode()) { + return vgParent; + } + if (vgParent.isRootNamespace()) { + return null; + } else { + parent = vgParent.getParent(); + } + } + return null; + } + + private boolean leaveTouchMode() { + if (mView != null) { + if (mView.hasFocus()) { + // i learned the hard way to not trust mFocusedView :) + mFocusedView = mView.findFocus(); + if (!(mFocusedView instanceof ViewGroup)) { + // some view has focus, let it keep it + return false; + } else if (((ViewGroup)mFocusedView).getDescendantFocusability() != + ViewGroup.FOCUS_AFTER_DESCENDANTS) { + // some view group has focus, and doesn't prefer its children + // over itself for focus, so let them keep it. + return false; + } + } + + // find the best view to give focus to in this brave new non-touch-mode + // world + final View focused = focusSearch(null, View.FOCUS_DOWN); + if (focused != null) { + return focused.requestFocus(View.FOCUS_DOWN); + } + } + return false; + } + + private void deliverPointerEvent(MotionEvent event, boolean sendDone) { + if (ViewDebug.DEBUG_LATENCY) { + mInputEventDeliverTimeNanos = System.nanoTime(); + } + + final boolean isTouchEvent = event.isTouchEvent(); + if (mInputEventConsistencyVerifier != null) { + if (isTouchEvent) { + mInputEventConsistencyVerifier.onTouchEvent(event, 0); + } else { + mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); + } + } + + // If there is no view, then the event will not be handled. + if (mView == null || !mAdded) { + finishMotionEvent(event, sendDone, false); + return; + } + + // Translate the pointer event for compatibility, if needed. + if (mTranslator != null) { + mTranslator.translateEventInScreenToAppWindow(event); + } + + // Enter touch mode on down or scroll. + final int action = event.getAction(); + if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { + ensureTouchMode(true); + } + + // Offset the scroll position. + if (mCurScrollY != 0) { + event.offsetLocation(0, mCurScrollY); + } + if (MEASURE_LATENCY) { + lt.sample("A Dispatching PointerEvents", System.nanoTime() - event.getEventTimeNano()); + } + + // Remember the touch position for possible drag-initiation. + if (isTouchEvent) { + mLastTouchPoint.x = event.getRawX(); + mLastTouchPoint.y = event.getRawY(); + } + + // Dispatch touch to view hierarchy. + boolean handled = mView.dispatchPointerEvent(event); + if (MEASURE_LATENCY) { + lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano()); + } + if (handled) { + finishMotionEvent(event, sendDone, true); + return; + } + + // Pointer event was unhandled. + finishMotionEvent(event, sendDone, false); + } + + private void finishMotionEvent(MotionEvent event, boolean sendDone, boolean handled) { + event.recycle(); + if (sendDone) { + finishInputEvent(event, handled); + } + //noinspection ConstantConditions + if (LOCAL_LOGV || WATCH_POINTER) { + if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { + Log.i(TAG, "Done dispatching!"); + } + } + } + + private void deliverTrackballEvent(MotionEvent event, boolean sendDone) { + if (ViewDebug.DEBUG_LATENCY) { + mInputEventDeliverTimeNanos = System.nanoTime(); + } + + if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event); + + if (mInputEventConsistencyVerifier != null) { + mInputEventConsistencyVerifier.onTrackballEvent(event, 0); + } + + // If there is no view, then the event will not be handled. + if (mView == null || !mAdded) { + finishMotionEvent(event, sendDone, false); + return; + } + + // Deliver the trackball event to the view. + if (mView.dispatchTrackballEvent(event)) { + // If we reach this, we delivered a trackball event to mView and + // mView consumed it. Because we will not translate the trackball + // event into a key event, touch mode will not exit, so we exit + // touch mode here. + ensureTouchMode(false); + + finishMotionEvent(event, sendDone, true); + mLastTrackballTime = Integer.MIN_VALUE; + return; + } + + // Translate the trackball event into DPAD keys and try to deliver those. + final TrackballAxis x = mTrackballAxisX; + final TrackballAxis y = mTrackballAxisY; + + long curTime = SystemClock.uptimeMillis(); + if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) { + // It has been too long since the last movement, + // so restart at the beginning. + x.reset(0); + y.reset(0); + mLastTrackballTime = curTime; + } + + final int action = event.getAction(); + final int metaState = event.getMetaState(); + switch (action) { + case MotionEvent.ACTION_DOWN: + x.reset(2); + y.reset(2); + deliverKeyEvent(new KeyEvent(curTime, curTime, + KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, + InputDevice.SOURCE_KEYBOARD), false); + break; + case MotionEvent.ACTION_UP: + x.reset(2); + y.reset(2); + deliverKeyEvent(new KeyEvent(curTime, curTime, + KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, + InputDevice.SOURCE_KEYBOARD), false); + break; + } + + if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step=" + + x.step + " dir=" + x.dir + " acc=" + x.acceleration + + " move=" + event.getX() + + " / Y=" + y.position + " step=" + + y.step + " dir=" + y.dir + " acc=" + y.acceleration + + " move=" + event.getY()); + final float xOff = x.collect(event.getX(), event.getEventTime(), "X"); + final float yOff = y.collect(event.getY(), event.getEventTime(), "Y"); + + // Generate DPAD events based on the trackball movement. + // We pick the axis that has moved the most as the direction of + // the DPAD. When we generate DPAD events for one axis, then the + // other axis is reset -- we don't want to perform DPAD jumps due + // to slight movements in the trackball when making major movements + // along the other axis. + int keycode = 0; + int movement = 0; + float accel = 1; + if (xOff > yOff) { + movement = x.generate((2/event.getXPrecision())); + if (movement != 0) { + keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT + : KeyEvent.KEYCODE_DPAD_LEFT; + accel = x.acceleration; + y.reset(2); + } + } else if (yOff > 0) { + movement = y.generate((2/event.getYPrecision())); + if (movement != 0) { + keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN + : KeyEvent.KEYCODE_DPAD_UP; + accel = y.acceleration; + x.reset(2); + } + } + + if (keycode != 0) { + if (movement < 0) movement = -movement; + int accelMovement = (int)(movement * accel); + if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement + + " accelMovement=" + accelMovement + + " accel=" + accel); + if (accelMovement > movement) { + if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: " + + keycode); + movement--; + int repeatCount = accelMovement - movement; + deliverKeyEvent(new KeyEvent(curTime, curTime, + KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, + InputDevice.SOURCE_KEYBOARD), false); + } + while (movement > 0) { + if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: " + + keycode); + movement--; + curTime = SystemClock.uptimeMillis(); + deliverKeyEvent(new KeyEvent(curTime, curTime, + KeyEvent.ACTION_DOWN, keycode, 0, metaState, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, + InputDevice.SOURCE_KEYBOARD), false); + deliverKeyEvent(new KeyEvent(curTime, curTime, + KeyEvent.ACTION_UP, keycode, 0, metaState, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, + InputDevice.SOURCE_KEYBOARD), false); + } + mLastTrackballTime = curTime; + } + + // Unfortunately we can't tell whether the application consumed the keys, so + // we always consider the trackball event handled. + finishMotionEvent(event, sendDone, true); + } + + private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) { + if (ViewDebug.DEBUG_LATENCY) { + mInputEventDeliverTimeNanos = System.nanoTime(); + } + + if (mInputEventConsistencyVerifier != null) { + mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); + } + + final int source = event.getSource(); + final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0; + + // If there is no view, then the event will not be handled. + if (mView == null || !mAdded) { + if (isJoystick) { + updateJoystickDirection(event, false); + } + finishMotionEvent(event, sendDone, false); + return; + } + + // Deliver the event to the view. + if (mView.dispatchGenericMotionEvent(event)) { + if (isJoystick) { + updateJoystickDirection(event, false); + } + finishMotionEvent(event, sendDone, true); + return; + } + + if (isJoystick) { + // Translate the joystick event into DPAD keys and try to deliver those. + updateJoystickDirection(event, true); + finishMotionEvent(event, sendDone, true); + } else { + finishMotionEvent(event, sendDone, false); + } + } + + private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) { + final long time = event.getEventTime(); + final int metaState = event.getMetaState(); + final int deviceId = event.getDeviceId(); + final int source = event.getSource(); + + int xDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_X)); + if (xDirection == 0) { + xDirection = joystickAxisValueToDirection(event.getX()); + } + + int yDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_Y)); + if (yDirection == 0) { + yDirection = joystickAxisValueToDirection(event.getY()); + } + + if (xDirection != mLastJoystickXDirection) { + if (mLastJoystickXKeyCode != 0) { + deliverKeyEvent(new KeyEvent(time, time, + KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState, + deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); + mLastJoystickXKeyCode = 0; + } + + mLastJoystickXDirection = xDirection; + + if (xDirection != 0 && synthesizeNewKeys) { + mLastJoystickXKeyCode = xDirection > 0 + ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT; + deliverKeyEvent(new KeyEvent(time, time, + KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState, + deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); + } + } + + if (yDirection != mLastJoystickYDirection) { + if (mLastJoystickYKeyCode != 0) { + deliverKeyEvent(new KeyEvent(time, time, + KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState, + deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); + mLastJoystickYKeyCode = 0; + } + + mLastJoystickYDirection = yDirection; + + if (yDirection != 0 && synthesizeNewKeys) { + mLastJoystickYKeyCode = yDirection > 0 + ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP; + deliverKeyEvent(new KeyEvent(time, time, + KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState, + deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); + } + } + } + + private static int joystickAxisValueToDirection(float value) { + if (value >= 0.5f) { + return 1; + } else if (value <= -0.5f) { + return -1; + } else { + return 0; + } + } + + /** + * Returns true if the key is used for keyboard navigation. + * @param keyEvent The key event. + * @return True if the key is used for keyboard navigation. + */ + private static boolean isNavigationKey(KeyEvent keyEvent) { + switch (keyEvent.getKeyCode()) { + case KeyEvent.KEYCODE_DPAD_LEFT: + case KeyEvent.KEYCODE_DPAD_RIGHT: + case KeyEvent.KEYCODE_DPAD_UP: + case KeyEvent.KEYCODE_DPAD_DOWN: + case KeyEvent.KEYCODE_DPAD_CENTER: + case KeyEvent.KEYCODE_PAGE_UP: + case KeyEvent.KEYCODE_PAGE_DOWN: + case KeyEvent.KEYCODE_MOVE_HOME: + case KeyEvent.KEYCODE_MOVE_END: + case KeyEvent.KEYCODE_TAB: + case KeyEvent.KEYCODE_SPACE: + case KeyEvent.KEYCODE_ENTER: + return true; + } + return false; + } + + /** + * Returns true if the key is used for typing. + * @param keyEvent The key event. + * @return True if the key is used for typing. + */ + private static boolean isTypingKey(KeyEvent keyEvent) { + return keyEvent.getUnicodeChar() > 0; + } + + /** + * See if the key event means we should leave touch mode (and leave touch mode if so). + * @param event The key event. + * @return Whether this key event should be consumed (meaning the act of + * leaving touch mode alone is considered the event). + */ + private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) { + // Only relevant in touch mode. + if (!mAttachInfo.mInTouchMode) { + return false; + } + + // Only consider leaving touch mode on DOWN or MULTIPLE actions, never on UP. + final int action = event.getAction(); + if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) { + return false; + } + + // Don't leave touch mode if the IME told us not to. + if ((event.getFlags() & KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) { + return false; + } + + // If the key can be used for keyboard navigation then leave touch mode + // and select a focused view if needed (in ensureTouchMode). + // When a new focused view is selected, we consume the navigation key because + // navigation doesn't make much sense unless a view already has focus so + // the key's purpose is to set focus. + if (isNavigationKey(event)) { + return ensureTouchMode(false); + } + + // If the key can be used for typing then leave touch mode + // and select a focused view if needed (in ensureTouchMode). + // Always allow the view to process the typing key. + if (isTypingKey(event)) { + ensureTouchMode(false); + return false; + } + + return false; + } + + int enqueuePendingEvent(Object event, boolean sendDone) { + int seq = mPendingEventSeq+1; + if (seq < 0) seq = 0; + mPendingEventSeq = seq; + mPendingEvents.put(seq, event); + return sendDone ? seq : -seq; + } + + Object retrievePendingEvent(int seq) { + if (seq < 0) seq = -seq; + Object event = mPendingEvents.get(seq); + if (event != null) { + mPendingEvents.remove(seq); + } + return event; + } + + private void deliverKeyEvent(KeyEvent event, boolean sendDone) { + if (ViewDebug.DEBUG_LATENCY) { + mInputEventDeliverTimeNanos = System.nanoTime(); + } + + if (mInputEventConsistencyVerifier != null) { + mInputEventConsistencyVerifier.onKeyEvent(event, 0); + } + + // If there is no view, then the event will not be handled. + if (mView == null || !mAdded) { + finishKeyEvent(event, sendDone, false); + return; + } + + if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView); + + // Perform predispatching before the IME. + if (mView.dispatchKeyEventPreIme(event)) { + finishKeyEvent(event, sendDone, true); + return; + } + + // Dispatch to the IME before propagating down the view hierarchy. + // The IME will eventually call back into handleFinishedEvent. + if (mLastWasImTarget) { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + int seq = enqueuePendingEvent(event, sendDone); + if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq=" + + seq + " event=" + event); + imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback); + return; + } + } + + // Not dispatching to IME, continue with post IME actions. + deliverKeyEventPostIme(event, sendDone); + } + + private void handleFinishedEvent(int seq, boolean handled) { + final KeyEvent event = (KeyEvent)retrievePendingEvent(seq); + if (DEBUG_IMF) Log.v(TAG, "IME finished event: seq=" + seq + + " handled=" + handled + " event=" + event); + if (event != null) { + final boolean sendDone = seq >= 0; + if (handled) { + finishKeyEvent(event, sendDone, true); + } else { + deliverKeyEventPostIme(event, sendDone); + } + } + } + + private void deliverKeyEventPostIme(KeyEvent event, boolean sendDone) { + if (ViewDebug.DEBUG_LATENCY) { + mInputEventDeliverPostImeTimeNanos = System.nanoTime(); + } + + // If the view went away, then the event will not be handled. + if (mView == null || !mAdded) { + finishKeyEvent(event, sendDone, false); + return; + } + + // If the key's purpose is to exit touch mode then we consume it and consider it handled. + if (checkForLeavingTouchModeAndConsume(event)) { + finishKeyEvent(event, sendDone, true); + return; + } + + // Make sure the fallback event policy sees all keys that will be delivered to the + // view hierarchy. + mFallbackEventHandler.preDispatchKeyEvent(event); + + // Deliver the key to the view hierarchy. + if (mView.dispatchKeyEvent(event)) { + finishKeyEvent(event, sendDone, true); + return; + } + + // If the Control modifier is held, try to interpret the key as a shortcut. + if (event.getAction() == KeyEvent.ACTION_UP + && event.isCtrlPressed() + && !KeyEvent.isModifierKey(event.getKeyCode())) { + if (mView.dispatchKeyShortcutEvent(event)) { + finishKeyEvent(event, sendDone, true); + return; + } + } + + // Apply the fallback event policy. + if (mFallbackEventHandler.dispatchKeyEvent(event)) { + finishKeyEvent(event, sendDone, true); + return; + } + + // Handle automatic focus changes. + if (event.getAction() == KeyEvent.ACTION_DOWN) { + int direction = 0; + switch (event.getKeyCode()) { + case KeyEvent.KEYCODE_DPAD_LEFT: + if (event.hasNoModifiers()) { + direction = View.FOCUS_LEFT; + } + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + if (event.hasNoModifiers()) { + direction = View.FOCUS_RIGHT; + } + break; + case KeyEvent.KEYCODE_DPAD_UP: + if (event.hasNoModifiers()) { + direction = View.FOCUS_UP; + } + break; + case KeyEvent.KEYCODE_DPAD_DOWN: + if (event.hasNoModifiers()) { + direction = View.FOCUS_DOWN; + } + break; + case KeyEvent.KEYCODE_TAB: + if (event.hasNoModifiers()) { + direction = View.FOCUS_FORWARD; + } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) { + direction = View.FOCUS_BACKWARD; + } + break; + } + + if (direction != 0) { + View focused = mView != null ? mView.findFocus() : null; + if (focused != null) { + View v = focused.focusSearch(direction); + if (v != null && v != focused) { + // do the math the get the interesting rect + // of previous focused into the coord system of + // newly focused view + focused.getFocusedRect(mTempRect); + if (mView instanceof ViewGroup) { + ((ViewGroup) mView).offsetDescendantRectToMyCoords( + focused, mTempRect); + ((ViewGroup) mView).offsetRectIntoDescendantCoords( + v, mTempRect); + } + if (v.requestFocus(direction, mTempRect)) { + playSoundEffect( + SoundEffectConstants.getContantForFocusDirection(direction)); + finishKeyEvent(event, sendDone, true); + return; + } + } + + // Give the focused view a last chance to handle the dpad key. + if (mView.dispatchUnhandledMove(focused, direction)) { + finishKeyEvent(event, sendDone, true); + return; + } + } + } + } + + // Key was unhandled. + finishKeyEvent(event, sendDone, false); + } + + private void finishKeyEvent(KeyEvent event, boolean sendDone, boolean handled) { + if (sendDone) { + finishInputEvent(event, handled); + } + } + + /* drag/drop */ + void setLocalDragState(Object obj) { + mLocalDragState = obj; + } + + private void handleDragEvent(DragEvent event) { + // From the root, only drag start/end/location are dispatched. entered/exited + // are determined and dispatched by the viewgroup hierarchy, who then report + // that back here for ultimate reporting back to the framework. + if (mView != null && mAdded) { + final int what = event.mAction; + + if (what == DragEvent.ACTION_DRAG_EXITED) { + // A direct EXITED event means that the window manager knows we've just crossed + // a window boundary, so the current drag target within this one must have + // just been exited. Send it the usual notifications and then we're done + // for now. + mView.dispatchDragEvent(event); + } else { + // Cache the drag description when the operation starts, then fill it in + // on subsequent calls as a convenience + if (what == DragEvent.ACTION_DRAG_STARTED) { + mCurrentDragView = null; // Start the current-recipient tracking + mDragDescription = event.mClipDescription; + } else { + event.mClipDescription = mDragDescription; + } + + // For events with a [screen] location, translate into window coordinates + if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) { + mDragPoint.set(event.mX, event.mY); + if (mTranslator != null) { + mTranslator.translatePointInScreenToAppWindow(mDragPoint); + } + + if (mCurScrollY != 0) { + mDragPoint.offset(0, mCurScrollY); + } + + event.mX = mDragPoint.x; + event.mY = mDragPoint.y; + } + + // Remember who the current drag target is pre-dispatch + final View prevDragView = mCurrentDragView; + + // Now dispatch the drag/drop event + boolean result = mView.dispatchDragEvent(event); + + // If we changed apparent drag target, tell the OS about it + if (prevDragView != mCurrentDragView) { + try { + if (prevDragView != null) { + sWindowSession.dragRecipientExited(mWindow); + } + if (mCurrentDragView != null) { + sWindowSession.dragRecipientEntered(mWindow); + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to note drag target change"); + } + } + + // Report the drop result when we're done + if (what == DragEvent.ACTION_DROP) { + mDragDescription = null; + try { + Log.i(TAG, "Reporting drop result: " + result); + sWindowSession.reportDropResult(mWindow, result); + } catch (RemoteException e) { + Log.e(TAG, "Unable to report drop result"); + } + } + + // When the drag operation ends, release any local state object + // that may have been in use + if (what == DragEvent.ACTION_DRAG_ENDED) { + setLocalDragState(null); + } + } + } + event.recycle(); + } + + public void handleDispatchSystemUiVisibilityChanged(int visibility) { + if (mView == null) return; + if (mAttachInfo != null) { + mAttachInfo.mSystemUiVisibility = visibility; + } + mView.dispatchSystemUiVisibilityChanged(visibility); + } + + public void getLastTouchPoint(Point outLocation) { + outLocation.x = (int) mLastTouchPoint.x; + outLocation.y = (int) mLastTouchPoint.y; + } + + public void setDragFocus(View newDragTarget) { + if (mCurrentDragView != newDragTarget) { + mCurrentDragView = newDragTarget; + } + } + + private AudioManager getAudioManager() { + if (mView == null) { + throw new IllegalStateException("getAudioManager called when there is no mView"); + } + if (mAudioManager == null) { + mAudioManager = (AudioManager) mView.getContext().getSystemService(Context.AUDIO_SERVICE); + } + return mAudioManager; + } + + public AccessibilityInteractionController getAccessibilityInteractionController() { + if (mView == null) { + throw new IllegalStateException("getAccessibilityInteractionController" + + " called when there is no mView"); + } + if (mAccessibilityInteractionContrtoller == null) { + mAccessibilityInteractionContrtoller = new AccessibilityInteractionController(); + } + return mAccessibilityInteractionContrtoller; + } + + private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, + boolean insetsPending) throws RemoteException { + + float appScale = mAttachInfo.mApplicationScale; + boolean restore = false; + if (params != null && mTranslator != null) { + restore = true; + params.backup(); + mTranslator.translateWindowLayout(params); + } + if (params != null) { + if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params); + } + mPendingConfiguration.seq = 0; + //Log.d(TAG, ">>>>>> CALLING relayout"); + int relayoutResult = sWindowSession.relayout( + mWindow, params, + (int) (mView.getMeasuredWidth() * appScale + 0.5f), + (int) (mView.getMeasuredHeight() * appScale + 0.5f), + viewVisibility, insetsPending, mWinFrame, + mPendingContentInsets, mPendingVisibleInsets, + mPendingConfiguration, mSurface); + //Log.d(TAG, "<<<<<< BACK FROM relayout"); + if (restore) { + params.restore(); + } + + if (mTranslator != null) { + mTranslator.translateRectInScreenToAppWinFrame(mWinFrame); + mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets); + mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets); + } + return relayoutResult; + } + + /** + * {@inheritDoc} + */ + public void playSoundEffect(int effectId) { + checkThread(); + + try { + final AudioManager audioManager = getAudioManager(); + + switch (effectId) { + case SoundEffectConstants.CLICK: + audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); + return; + case SoundEffectConstants.NAVIGATION_DOWN: + audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN); + return; + case SoundEffectConstants.NAVIGATION_LEFT: + audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT); + return; + case SoundEffectConstants.NAVIGATION_RIGHT: + audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT); + return; + case SoundEffectConstants.NAVIGATION_UP: + audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP); + return; + default: + throw new IllegalArgumentException("unknown effect id " + effectId + + " not defined in " + SoundEffectConstants.class.getCanonicalName()); + } + } catch (IllegalStateException e) { + // Exception thrown by getAudioManager() when mView is null + Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e); + e.printStackTrace(); + } + } + + /** + * {@inheritDoc} + */ + public boolean performHapticFeedback(int effectId, boolean always) { + try { + return sWindowSession.performHapticFeedback(mWindow, effectId, always); + } catch (RemoteException e) { + return false; + } + } + + /** + * {@inheritDoc} + */ + public View focusSearch(View focused, int direction) { + checkThread(); + if (!(mView instanceof ViewGroup)) { + return null; + } + return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction); + } + + public void debug() { + mView.debug(); + } + + public void die(boolean immediate) { + if (immediate) { + doDie(); + } else { + sendEmptyMessage(DIE); + } + } + + void doDie() { + checkThread(); + if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface); + synchronized (this) { + if (mAdded && !mFirst) { + destroyHardwareRenderer(); + + int viewVisibility = mView.getVisibility(); + boolean viewVisibilityChanged = mViewVisibility != viewVisibility; + if (mWindowAttributesChanged || viewVisibilityChanged) { + // If layout params have been changed, first give them + // to the window manager to make sure it has the correct + // animation info. + try { + if ((relayoutWindow(mWindowAttributes, viewVisibility, false) + & WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { + sWindowSession.finishDrawing(mWindow); + } + } catch (RemoteException e) { + } + } + + mSurface.release(); + } + if (mAdded) { + mAdded = false; + dispatchDetachedFromWindow(); + } + } + } + + public void requestUpdateConfiguration(Configuration config) { + Message msg = obtainMessage(UPDATE_CONFIGURATION, config); + sendMessage(msg); + } + + private void destroyHardwareRenderer() { + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.destroy(true); + mAttachInfo.mHardwareRenderer = null; + mAttachInfo.mHardwareAccelerated = false; + } + } + + public void dispatchFinishedEvent(int seq, boolean handled) { + Message msg = obtainMessage(FINISHED_EVENT); + msg.arg1 = seq; + msg.arg2 = handled ? 1 : 0; + sendMessage(msg); + } + + public void dispatchResized(int w, int h, Rect coveredInsets, + Rect visibleInsets, boolean reportDraw, Configuration newConfig) { + if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": w=" + w + + " h=" + h + " coveredInsets=" + coveredInsets.toShortString() + + " visibleInsets=" + visibleInsets.toShortString() + + " reportDraw=" + reportDraw); + Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED); + if (mTranslator != null) { + mTranslator.translateRectInScreenToAppWindow(coveredInsets); + mTranslator.translateRectInScreenToAppWindow(visibleInsets); + w *= mTranslator.applicationInvertedScale; + h *= mTranslator.applicationInvertedScale; + } + msg.arg1 = w; + msg.arg2 = h; + ResizedInfo ri = new ResizedInfo(); + ri.coveredInsets = new Rect(coveredInsets); + ri.visibleInsets = new Rect(visibleInsets); + ri.newConfig = newConfig; + msg.obj = ri; + sendMessage(msg); + } + + private long mInputEventReceiveTimeNanos; + private long mInputEventDeliverTimeNanos; + private long mInputEventDeliverPostImeTimeNanos; + private InputQueue.FinishedCallback mFinishedCallback; + + private final InputHandler mInputHandler = new InputHandler() { + public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) { + startInputEvent(finishedCallback); + dispatchKey(event, true); + } + + public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) { + startInputEvent(finishedCallback); + dispatchMotion(event, true); + } + }; + + public void dispatchKey(KeyEvent event) { + dispatchKey(event, false); + } + + private void dispatchKey(KeyEvent event, boolean sendDone) { + //noinspection ConstantConditions + if (false && event.getAction() == KeyEvent.ACTION_DOWN) { + if (event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) { + if (DBG) Log.d("keydisp", "==================================================="); + if (DBG) Log.d("keydisp", "Focused view Hierarchy is:"); + + debug(); + + if (DBG) Log.d("keydisp", "==================================================="); + } + } + + Message msg = obtainMessage(DISPATCH_KEY); + msg.obj = event; + msg.arg1 = sendDone ? 1 : 0; + + if (LOCAL_LOGV) Log.v( + TAG, "sending key " + event + " to " + mView); + + sendMessageAtTime(msg, event.getEventTime()); + } + + private void dispatchMotion(MotionEvent event, boolean sendDone) { + int source = event.getSource(); + if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { + dispatchPointer(event, sendDone); + } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { + dispatchTrackball(event, sendDone); + } else { + dispatchGenericMotion(event, sendDone); + } + } + + private void dispatchPointer(MotionEvent event, boolean sendDone) { + Message msg = obtainMessage(DISPATCH_POINTER); + msg.obj = event; + msg.arg1 = sendDone ? 1 : 0; + sendMessageAtTime(msg, event.getEventTime()); + } + + private void dispatchTrackball(MotionEvent event, boolean sendDone) { + Message msg = obtainMessage(DISPATCH_TRACKBALL); + msg.obj = event; + msg.arg1 = sendDone ? 1 : 0; + sendMessageAtTime(msg, event.getEventTime()); + } + + private void dispatchGenericMotion(MotionEvent event, boolean sendDone) { + Message msg = obtainMessage(DISPATCH_GENERIC_MOTION); + msg.obj = event; + msg.arg1 = sendDone ? 1 : 0; + sendMessageAtTime(msg, event.getEventTime()); + } + + public void dispatchAppVisibility(boolean visible) { + Message msg = obtainMessage(DISPATCH_APP_VISIBILITY); + msg.arg1 = visible ? 1 : 0; + sendMessage(msg); + } + + public void dispatchGetNewSurface() { + Message msg = obtainMessage(DISPATCH_GET_NEW_SURFACE); + sendMessage(msg); + } + + public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { + Message msg = Message.obtain(); + msg.what = WINDOW_FOCUS_CHANGED; + msg.arg1 = hasFocus ? 1 : 0; + msg.arg2 = inTouchMode ? 1 : 0; + sendMessage(msg); + } + + public void dispatchCloseSystemDialogs(String reason) { + Message msg = Message.obtain(); + msg.what = CLOSE_SYSTEM_DIALOGS; + msg.obj = reason; + sendMessage(msg); + } + + public void dispatchDragEvent(DragEvent event) { + final int what; + if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) { + what = DISPATCH_DRAG_LOCATION_EVENT; + removeMessages(what); + } else { + what = DISPATCH_DRAG_EVENT; + } + Message msg = obtainMessage(what, event); + sendMessage(msg); + } + + public void dispatchSystemUiVisibilityChanged(int visibility) { + sendMessage(obtainMessage(DISPATCH_SYSTEM_UI_VISIBILITY, visibility, 0)); + } + + /** + * The window is getting focus so if there is anything focused/selected + * send an {@link AccessibilityEvent} to announce that. + */ + private void sendAccessibilityEvents() { + if (!mAccessibilityManager.isEnabled()) { + return; + } + mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + View focusedView = mView.findFocus(); + if (focusedView != null && focusedView != mView) { + focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + } + } + + /** + * Post a callback to send a + * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. + * This event is send at most once every + * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}. + */ + private void postSendWindowContentChangedCallback() { + if (mSendWindowContentChangedAccessibilityEvent == null) { + mSendWindowContentChangedAccessibilityEvent = + new SendWindowContentChangedAccessibilityEvent(); + } + if (!mSendWindowContentChangedAccessibilityEvent.mIsPending) { + mSendWindowContentChangedAccessibilityEvent.mIsPending = true; + postDelayed(mSendWindowContentChangedAccessibilityEvent, + ViewConfiguration.getSendRecurringAccessibilityEventsInterval()); + } + } + + /** + * Remove a posted callback to send a + * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. + */ + private void removeSendWindowContentChangedCallback() { + if (mSendWindowContentChangedAccessibilityEvent != null) { + removeCallbacks(mSendWindowContentChangedAccessibilityEvent); + } + } + + public boolean showContextMenuForChild(View originalView) { + return false; + } + + public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) { + return null; + } + + public void createContextMenu(ContextMenu menu) { + } + + public void childDrawableStateChanged(View child) { + } + + public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { + if (mView == null) { + return false; + } + mAccessibilityManager.sendAccessibilityEvent(event); + return true; + } + + void checkThread() { + if (mThread != Thread.currentThread()) { + throw new CalledFromWrongThreadException( + "Only the original thread that created a view hierarchy can touch its views."); + } + } + + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + // ViewAncestor never intercepts touch event, so this can be a no-op + } + + public boolean requestChildRectangleOnScreen(View child, Rect rectangle, + boolean immediate) { + return scrollToRectOrFocus(rectangle, immediate); + } + + class TakenSurfaceHolder extends BaseSurfaceHolder { + @Override + public boolean onAllowLockCanvas() { + return mDrawingAllowed; + } + + @Override + public void onRelayoutContainer() { + // Not currently interesting -- from changing between fixed and layout size. + } + + public void setFormat(int format) { + ((RootViewSurfaceTaker)mView).setSurfaceFormat(format); + } + + public void setType(int type) { + ((RootViewSurfaceTaker)mView).setSurfaceType(type); + } + + @Override + public void onUpdateSurface() { + // We take care of format and type changes on our own. + throw new IllegalStateException("Shouldn't be here"); + } + + public boolean isCreating() { + return mIsCreating; + } + + @Override + public void setFixedSize(int width, int height) { + throw new UnsupportedOperationException( + "Currently only support sizing from layout"); + } + + public void setKeepScreenOn(boolean screenOn) { + ((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn); + } + } + + static class InputMethodCallback extends IInputMethodCallback.Stub { + private WeakReference mViewAncestor; + + public InputMethodCallback(ViewRootImpl viewAncestor) { + mViewAncestor = new WeakReference(viewAncestor); + } + + public void finishedEvent(int seq, boolean handled) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchFinishedEvent(seq, handled); + } + } + + public void sessionCreated(IInputMethodSession session) { + // Stub -- not for use in the client. + } + } + + static class W extends IWindow.Stub { + private final WeakReference mViewAncestor; + + W(ViewRootImpl viewAncestor) { + mViewAncestor = new WeakReference(viewAncestor); + } + + public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets, + boolean reportDraw, Configuration newConfig) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, + newConfig); + } + } + + public void dispatchAppVisibility(boolean visible) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchAppVisibility(visible); + } + } + + public void dispatchGetNewSurface() { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchGetNewSurface(); + } + } + + public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.windowFocusChanged(hasFocus, inTouchMode); + } + } + + private static int checkCallingPermission(String permission) { + try { + return ActivityManagerNative.getDefault().checkPermission( + permission, Binder.getCallingPid(), Binder.getCallingUid()); + } catch (RemoteException e) { + return PackageManager.PERMISSION_DENIED; + } + } + + public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + final View view = viewAncestor.mView; + if (view != null) { + if (checkCallingPermission(Manifest.permission.DUMP) != + PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Insufficient permissions to invoke" + + " executeCommand() from pid=" + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + } + + OutputStream clientStream = null; + try { + clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out); + ViewDebug.dispatchCommand(view, command, parameters, clientStream); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (clientStream != null) { + try { + clientStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + } + } + + public void closeSystemDialogs(String reason) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchCloseSystemDialogs(reason); + } + } + + public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, + boolean sync) { + if (sync) { + try { + sWindowSession.wallpaperOffsetsComplete(asBinder()); + } catch (RemoteException e) { + } + } + } + + public void dispatchWallpaperCommand(String action, int x, int y, + int z, Bundle extras, boolean sync) { + if (sync) { + try { + sWindowSession.wallpaperCommandComplete(asBinder(), null); + } catch (RemoteException e) { + } + } + } + + /* Drag/drop */ + public void dispatchDragEvent(DragEvent event) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchDragEvent(event); + } + } + + public void dispatchSystemUiVisibilityChanged(int visibility) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchSystemUiVisibilityChanged(visibility); + } + } + } + + /** + * Maintains state information for a single trackball axis, generating + * discrete (DPAD) movements based on raw trackball motion. + */ + static final class TrackballAxis { + /** + * The maximum amount of acceleration we will apply. + */ + static final float MAX_ACCELERATION = 20; + + /** + * The maximum amount of time (in milliseconds) between events in order + * for us to consider the user to be doing fast trackball movements, + * and thus apply an acceleration. + */ + static final long FAST_MOVE_TIME = 150; + + /** + * Scaling factor to the time (in milliseconds) between events to how + * much to multiple/divide the current acceleration. When movement + * is < FAST_MOVE_TIME this multiplies the acceleration; when > + * FAST_MOVE_TIME it divides it. + */ + static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40); + + float position; + float absPosition; + float acceleration = 1; + long lastMoveTime = 0; + int step; + int dir; + int nonAccelMovement; + + void reset(int _step) { + position = 0; + acceleration = 1; + lastMoveTime = 0; + step = _step; + dir = 0; + } + + /** + * Add trackball movement into the state. If the direction of movement + * has been reversed, the state is reset before adding the + * movement (so that you don't have to compensate for any previously + * collected movement before see the result of the movement in the + * new direction). + * + * @return Returns the absolute value of the amount of movement + * collected so far. + */ + float collect(float off, long time, String axis) { + long normTime; + if (off > 0) { + normTime = (long)(off * FAST_MOVE_TIME); + if (dir < 0) { + if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!"); + position = 0; + step = 0; + acceleration = 1; + lastMoveTime = 0; + } + dir = 1; + } else if (off < 0) { + normTime = (long)((-off) * FAST_MOVE_TIME); + if (dir > 0) { + if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!"); + position = 0; + step = 0; + acceleration = 1; + lastMoveTime = 0; + } + dir = -1; + } else { + normTime = 0; + } + + // The number of milliseconds between each movement that is + // considered "normal" and will not result in any acceleration + // or deceleration, scaled by the offset we have here. + if (normTime > 0) { + long delta = time - lastMoveTime; + lastMoveTime = time; + float acc = acceleration; + if (delta < normTime) { + // The user is scrolling rapidly, so increase acceleration. + float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR; + if (scale > 1) acc *= scale; + if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off=" + + off + " normTime=" + normTime + " delta=" + delta + + " scale=" + scale + " acc=" + acc); + acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION; + } else { + // The user is scrolling slowly, so decrease acceleration. + float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR; + if (scale > 1) acc /= scale; + if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off=" + + off + " normTime=" + normTime + " delta=" + delta + + " scale=" + scale + " acc=" + acc); + acceleration = acc > 1 ? acc : 1; + } + } + position += off; + return (absPosition = Math.abs(position)); + } + + /** + * Generate the number of discrete movement events appropriate for + * the currently collected trackball movement. + * + * @param precision The minimum movement required to generate the + * first discrete movement. + * + * @return Returns the number of discrete movements, either positive + * or negative, or 0 if there is not enough trackball movement yet + * for a discrete movement. + */ + int generate(float precision) { + int movement = 0; + nonAccelMovement = 0; + do { + final int dir = position >= 0 ? 1 : -1; + switch (step) { + // If we are going to execute the first step, then we want + // to do this as soon as possible instead of waiting for + // a full movement, in order to make things look responsive. + case 0: + if (absPosition < precision) { + return movement; + } + movement += dir; + nonAccelMovement += dir; + step = 1; + break; + // If we have generated the first movement, then we need + // to wait for the second complete trackball motion before + // generating the second discrete movement. + case 1: + if (absPosition < 2) { + return movement; + } + movement += dir; + nonAccelMovement += dir; + position += dir > 0 ? -2 : 2; + absPosition = Math.abs(position); + step = 2; + break; + // After the first two, we generate discrete movements + // consistently with the trackball, applying an acceleration + // if the trackball is moving quickly. This is a simple + // acceleration on top of what we already compute based + // on how quickly the wheel is being turned, to apply + // a longer increasing acceleration to continuous movement + // in one direction. + default: + if (absPosition < 1) { + return movement; + } + movement += dir; + position += dir >= 0 ? -1 : 1; + absPosition = Math.abs(position); + float acc = acceleration; + acc *= 1.1f; + acceleration = acc < MAX_ACCELERATION ? acc : acceleration; + break; + } + } while (true); + } + } + + public static final class CalledFromWrongThreadException extends AndroidRuntimeException { + public CalledFromWrongThreadException(String msg) { + super(msg); + } + } + + private SurfaceHolder mHolder = new SurfaceHolder() { + // we only need a SurfaceHolder for opengl. it would be nice + // to implement everything else though, especially the callback + // support (opengl doesn't make use of it right now, but eventually + // will). + public Surface getSurface() { + return mSurface; + } + + public boolean isCreating() { + return false; + } + + public void addCallback(Callback callback) { + } + + public void removeCallback(Callback callback) { + } + + public void setFixedSize(int width, int height) { + } + + public void setSizeFromLayout() { + } + + public void setFormat(int format) { + } + + public void setType(int type) { + } + + public void setKeepScreenOn(boolean screenOn) { + } + + public Canvas lockCanvas() { + return null; + } + + public Canvas lockCanvas(Rect dirty) { + return null; + } + + public void unlockCanvasAndPost(Canvas canvas) { + } + public Rect getSurfaceFrame() { + return null; + } + }; + + static RunQueue getRunQueue() { + RunQueue rq = sRunQueues.get(); + if (rq != null) { + return rq; + } + rq = new RunQueue(); + sRunQueues.set(rq); + return rq; + } + + /** + * @hide + */ + static final class RunQueue { + private final ArrayList mActions = new ArrayList(); + + void post(Runnable action) { + postDelayed(action, 0); + } + + void postDelayed(Runnable action, long delayMillis) { + HandlerAction handlerAction = new HandlerAction(); + handlerAction.action = action; + handlerAction.delay = delayMillis; + + synchronized (mActions) { + mActions.add(handlerAction); + } + } + + void removeCallbacks(Runnable action) { + final HandlerAction handlerAction = new HandlerAction(); + handlerAction.action = action; + + synchronized (mActions) { + final ArrayList actions = mActions; + + while (actions.remove(handlerAction)) { + // Keep going + } + } + } + + void executeActions(Handler handler) { + synchronized (mActions) { + final ArrayList actions = mActions; + final int count = actions.size(); + + for (int i = 0; i < count; i++) { + final HandlerAction handlerAction = actions.get(i); + handler.postDelayed(handlerAction.action, handlerAction.delay); + } + + actions.clear(); + } + } + + private static class HandlerAction { + Runnable action; + long delay; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + HandlerAction that = (HandlerAction) o; + return !(action != null ? !action.equals(that.action) : that.action != null); + + } + + @Override + public int hashCode() { + int result = action != null ? action.hashCode() : 0; + result = 31 * result + (int) (delay ^ (delay >>> 32)); + return result; + } + } + } + + /** + * Class for managing the accessibility interaction connection + * based on the global accessibility state. + */ + final class AccessibilityInteractionConnectionManager + implements AccessibilityStateChangeListener { + public void onAccessibilityStateChanged(boolean enabled) { + if (enabled) { + ensureConnection(); + } else { + ensureNoConnection(); + } + } + + public void ensureConnection() { + final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID; + if (!registered) { + mAttachInfo.mAccessibilityWindowId = + mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, + new AccessibilityInteractionConnection(ViewRootImpl.this)); + } + } + + public void ensureNoConnection() { + final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID; + if (registered) { + mAttachInfo.mAccessibilityWindowId = View.NO_ID; + mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow); + } + } + } + + /** + * This class is an interface this ViewAncestor provides to the + * AccessibilityManagerService to the latter can interact with + * the view hierarchy in this ViewAncestor. + */ + final class AccessibilityInteractionConnection + extends IAccessibilityInteractionConnection.Stub { + private final WeakReference mViewAncestor; + + AccessibilityInteractionConnection(ViewRootImpl viewAncestor) { + mViewAncestor = new WeakReference(viewAncestor); + } + + public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId, + int interactionId, IAccessibilityInteractionConnectionCallback callback) { + if (mViewAncestor.get() != null) { + getAccessibilityInteractionController() + .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityId, + interactionId, callback); + } + } + + public void performAccessibilityAction(int accessibilityId, int action, + int interactionId, IAccessibilityInteractionConnectionCallback callback) { + if (mViewAncestor.get() != null) { + getAccessibilityInteractionController() + .performAccessibilityActionClientThread(accessibilityId, action, interactionId, + callback); + } + } + + public void findAccessibilityNodeInfoByViewId(int viewId, + int interactionId, IAccessibilityInteractionConnectionCallback callback) { + if (mViewAncestor.get() != null) { + getAccessibilityInteractionController() + .findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback); + } + } + + public void findAccessibilityNodeInfosByViewText(String text, int accessibilityId, + int interactionId, IAccessibilityInteractionConnectionCallback callback) { + if (mViewAncestor.get() != null) { + getAccessibilityInteractionController() + .findAccessibilityNodeInfosByViewTextClientThread(text, accessibilityId, + interactionId, callback); + } + } + } + + /** + * Class for managing accessibility interactions initiated from the system + * and targeting the view hierarchy. A *ClientThread method is to be + * called from the interaction connection this ViewAncestor gives the + * system to talk to it and a corresponding *UiThread method that is executed + * on the UI thread. + */ + final class AccessibilityInteractionController { + private static final int POOL_SIZE = 5; + + private FindByAccessibilitytIdPredicate mFindByAccessibilityIdPredicate = + new FindByAccessibilitytIdPredicate(); + + private ArrayList mTempAccessibilityNodeInfoList = + new ArrayList(); + + // Reusable poolable arguments for interacting with the view hierarchy + // to fit more arguments than Message and to avoid sharing objects between + // two messages since several threads can send messages concurrently. + private final Pool mPool = Pools.synchronizedPool(Pools.finitePool( + new PoolableManager() { + public SomeArgs newInstance() { + return new SomeArgs(); + } + + public void onAcquired(SomeArgs info) { + /* do nothing */ + } + + public void onReleased(SomeArgs info) { + info.clear(); + } + }, POOL_SIZE) + ); + + public class SomeArgs implements Poolable { + private SomeArgs mNext; + private boolean mIsPooled; + + public Object arg1; + public Object arg2; + public int argi1; + public int argi2; + public int argi3; + + public SomeArgs getNextPoolable() { + return mNext; + } + + public boolean isPooled() { + return mIsPooled; + } + + public void setNextPoolable(SomeArgs args) { + mNext = args; + } + + public void setPooled(boolean isPooled) { + mIsPooled = isPooled; + } + + private void clear() { + arg1 = null; + arg2 = null; + argi1 = 0; + argi2 = 0; + argi3 = 0; + } + } + + public void findAccessibilityNodeInfoByAccessibilityIdClientThread(int accessibilityId, + int interactionId, IAccessibilityInteractionConnectionCallback callback) { + Message message = Message.obtain(); + message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID; + message.arg1 = accessibilityId; + message.arg2 = interactionId; + message.obj = callback; + sendMessage(message); + } + + public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) { + final int accessibilityId = message.arg1; + final int interactionId = message.arg2; + final IAccessibilityInteractionConnectionCallback callback = + (IAccessibilityInteractionConnectionCallback) message.obj; + + AccessibilityNodeInfo info = null; + try { + FindByAccessibilitytIdPredicate predicate = mFindByAccessibilityIdPredicate; + predicate.init(accessibilityId); + View root = ViewRootImpl.this.mView; + View target = root.findViewByPredicate(predicate); + if (target != null && target.isShown()) { + info = target.createAccessibilityNodeInfo(); + } + } finally { + try { + callback.setFindAccessibilityNodeInfoResult(info, interactionId); + } catch (RemoteException re) { + /* ignore - the other side will time out */ + } + } + } + + public void findAccessibilityNodeInfoByViewIdClientThread(int viewId, int interactionId, + IAccessibilityInteractionConnectionCallback callback) { + Message message = Message.obtain(); + message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID; + message.arg1 = viewId; + message.arg2 = interactionId; + message.obj = callback; + sendMessage(message); + } + + public void findAccessibilityNodeInfoByViewIdUiThread(Message message) { + final int viewId = message.arg1; + final int interactionId = message.arg2; + final IAccessibilityInteractionConnectionCallback callback = + (IAccessibilityInteractionConnectionCallback) message.obj; + + AccessibilityNodeInfo info = null; + try { + View root = ViewRootImpl.this.mView; + View target = root.findViewById(viewId); + if (target != null && target.isShown()) { + info = target.createAccessibilityNodeInfo(); + } + } finally { + try { + callback.setFindAccessibilityNodeInfoResult(info, interactionId); + } catch (RemoteException re) { + /* ignore - the other side will time out */ + } + } + } + + public void findAccessibilityNodeInfosByViewTextClientThread(String text, + int accessibilityViewId, int interactionId, + IAccessibilityInteractionConnectionCallback callback) { + Message message = Message.obtain(); + message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT; + SomeArgs args = mPool.acquire(); + args.arg1 = text; + args.argi1 = accessibilityViewId; + args.argi2 = interactionId; + args.arg2 = callback; + message.obj = args; + sendMessage(message); + } + + public void findAccessibilityNodeInfosByViewTextUiThread(Message message) { + SomeArgs args = (SomeArgs) message.obj; + final String text = (String) args.arg1; + final int accessibilityViewId = args.argi1; + final int interactionId = args.argi2; + final IAccessibilityInteractionConnectionCallback callback = + (IAccessibilityInteractionConnectionCallback) args.arg2; + mPool.release(args); + + List infos = null; + try { + ArrayList foundViews = mAttachInfo.mFocusablesTempList; + foundViews.clear(); + + View root; + if (accessibilityViewId != View.NO_ID) { + root = findViewByAccessibilityId(accessibilityViewId); + } else { + root = ViewRootImpl.this.mView; + } + + if (root == null || !root.isShown()) { + return; + } + + root.findViewsWithText(foundViews, text); + if (foundViews.isEmpty()) { + return; + } + + infos = mTempAccessibilityNodeInfoList; + infos.clear(); + + final int viewCount = foundViews.size(); + for (int i = 0; i < viewCount; i++) { + View foundView = foundViews.get(i); + if (foundView.isShown()) { + infos.add(foundView.createAccessibilityNodeInfo()); + } + } + } finally { + try { + callback.setFindAccessibilityNodeInfosResult(infos, interactionId); + } catch (RemoteException re) { + /* ignore - the other side will time out */ + } + } + } + + public void performAccessibilityActionClientThread(int accessibilityId, int action, + int interactionId, IAccessibilityInteractionConnectionCallback callback) { + Message message = Message.obtain(); + message.what = DO_PERFORM_ACCESSIBILITY_ACTION; + SomeArgs args = mPool.acquire(); + args.argi1 = accessibilityId; + args.argi2 = action; + args.argi3 = interactionId; + args.arg1 = callback; + message.obj = args; + sendMessage(message); + } + + public void perfromAccessibilityActionUiThread(Message message) { + SomeArgs args = (SomeArgs) message.obj; + final int accessibilityId = args.argi1; + final int action = args.argi2; + final int interactionId = args.argi3; + final IAccessibilityInteractionConnectionCallback callback = + (IAccessibilityInteractionConnectionCallback) args.arg1; + mPool.release(args); + + boolean succeeded = false; + try { + switch (action) { + case AccessibilityNodeInfo.ACTION_FOCUS: { + succeeded = performActionFocus(accessibilityId); + } break; + case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: { + succeeded = performActionClearFocus(accessibilityId); + } break; + case AccessibilityNodeInfo.ACTION_SELECT: { + succeeded = performActionSelect(accessibilityId); + } break; + case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: { + succeeded = performActionClearSelection(accessibilityId); + } break; + } + } finally { + try { + callback.setPerformAccessibilityActionResult(succeeded, interactionId); + } catch (RemoteException re) { + /* ignore - the other side will time out */ + } + } + } + + private boolean performActionFocus(int accessibilityId) { + View target = findViewByAccessibilityId(accessibilityId); + if (target == null) { + return false; + } + // Get out of touch mode since accessibility wants to move focus around. + ensureTouchMode(false); + return target.requestFocus(); + } + + private boolean performActionClearFocus(int accessibilityId) { + View target = findViewByAccessibilityId(accessibilityId); + if (target == null) { + return false; + } + if (!target.isFocused()) { + return false; + } + target.clearFocus(); + return !target.isFocused(); + } + + private boolean performActionSelect(int accessibilityId) { + View target = findViewByAccessibilityId(accessibilityId); + if (target == null) { + return false; + } + if (target.isSelected()) { + return false; + } + target.setSelected(true); + return target.isSelected(); + } + + private boolean performActionClearSelection(int accessibilityId) { + View target = findViewByAccessibilityId(accessibilityId); + if (target == null) { + return false; + } + if (!target.isSelected()) { + return false; + } + target.setSelected(false); + return !target.isSelected(); + } + + private View findViewByAccessibilityId(int accessibilityId) { + View root = ViewRootImpl.this.mView; + if (root == null) { + return null; + } + mFindByAccessibilityIdPredicate.init(accessibilityId); + View foundView = root.findViewByPredicate(mFindByAccessibilityIdPredicate); + return (foundView != null && foundView.isShown()) ? foundView : null; + } + + private final class FindByAccessibilitytIdPredicate implements Predicate { + public int mSerchedId; + + public void init(int searchedId) { + mSerchedId = searchedId; + } + + public boolean apply(View view) { + return (view.getAccessibilityViewId() == mSerchedId); + } + } + } + + private class SendWindowContentChangedAccessibilityEvent implements Runnable { + public volatile boolean mIsPending; + + public void run() { + if (mView != null) { + // Check again for accessibility state since this is executed delayed. + AccessibilityManager accessibilityManager = + AccessibilityManager.getInstance(mView.mContext); + if (accessibilityManager.isEnabled()) { + // Send the event directly since we do not want to append the + // source text because this is the text for the entire window + // and we just want to notify that the content has changed. + AccessibilityEvent event = AccessibilityEvent.obtain( + AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); + mView.onInitializeAccessibilityEvent(event); + accessibilityManager.sendAccessibilityEvent(event); + } + mIsPending = false; + } + } + } +} diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 4e9531b..a451bb5 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -78,7 +78,7 @@ public class WindowManagerImpl implements WindowManager { public static final int ADD_PERMISSION_DENIED = -8; private View[] mViews; - private ViewAncestor[] mRoots; + private ViewRootImpl[] mRoots; private WindowManager.LayoutParams[] mParams; private final static Object sLock = new Object(); @@ -202,7 +202,7 @@ public class WindowManagerImpl implements WindowManager { final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; - ViewAncestor root; + ViewRootImpl root; View panelParentView = null; synchronized (this) { @@ -239,7 +239,7 @@ public class WindowManagerImpl implements WindowManager { } } - root = new ViewAncestor(view.getContext()); + root = new ViewRootImpl(view.getContext()); root.mAddNesting = 1; if (cih == null) { root.mCompatibilityInfo = new CompatibilityInfoHolder(); @@ -252,7 +252,7 @@ public class WindowManagerImpl implements WindowManager { if (mViews == null) { index = 1; mViews = new View[1]; - mRoots = new ViewAncestor[1]; + mRoots = new ViewRootImpl[1]; mParams = new WindowManager.LayoutParams[1]; } else { index = mViews.length + 1; @@ -260,7 +260,7 @@ public class WindowManagerImpl implements WindowManager { mViews = new View[index]; System.arraycopy(old, 0, mViews, 0, index-1); old = mRoots; - mRoots = new ViewAncestor[index]; + mRoots = new ViewRootImpl[index]; System.arraycopy(old, 0, mRoots, 0, index-1); old = mParams; mParams = new WindowManager.LayoutParams[index]; @@ -288,7 +288,7 @@ public class WindowManagerImpl implements WindowManager { synchronized (this) { int index = findViewLocked(view, true); - ViewAncestor root = mRoots[index]; + ViewRootImpl root = mRoots[index]; mParams[index] = wparams; root.setLayoutParams(wparams, false); } @@ -310,7 +310,7 @@ public class WindowManagerImpl implements WindowManager { public void removeViewImmediate(View view) { synchronized (this) { int index = findViewLocked(view, true); - ViewAncestor root = mRoots[index]; + ViewRootImpl root = mRoots[index]; View curView = root.getView(); root.mAddNesting = 0; @@ -326,7 +326,7 @@ public class WindowManagerImpl implements WindowManager { } View removeViewLocked(int index) { - ViewAncestor root = mRoots[index]; + ViewRootImpl root = mRoots[index]; View view = root.getView(); // Don't really remove until we have matched all calls to add(). @@ -354,7 +354,7 @@ public class WindowManagerImpl implements WindowManager { removeItem(tmpViews, mViews, index); mViews = tmpViews; - ViewAncestor[] tmpRoots = new ViewAncestor[count-1]; + ViewRootImpl[] tmpRoots = new ViewRootImpl[count-1]; removeItem(tmpRoots, mRoots, index); mRoots = tmpRoots; @@ -381,7 +381,7 @@ public class WindowManagerImpl implements WindowManager { //Log.i("foo", "@ " + i + " token " + mParams[i].token // + " view " + mRoots[i].getView()); if (token == null || mParams[i].token == token) { - ViewAncestor root = mRoots[i]; + ViewRootImpl root = mRoots[i]; root.mAddNesting = 1; //Log.i("foo", "Force closing " + root); @@ -417,7 +417,7 @@ public class WindowManagerImpl implements WindowManager { int count = mViews.length; for (int i=0; i