package android.view;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import android.Manifest;
import android.animation.LayoutTransition;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ResourcesManager;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.content.pm.ActivityInfo;
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.Color;
import android.graphics.Matrix;
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.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.input.InputManager;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
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.os.Trace;
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.LongArray;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TypedValue;
import android.view.Surface.OutOfResourcesException;
import android.view.ThreadedRenderer.FrameDrawingCallback;
import android.view.View.AttachInfo;
import android.view.View.FocusDirection;
import android.view.View.MeasureSpec;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import android.view.accessibility.AccessibilityManager.HighTextContrastChangeListener;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.autofill.AutofillManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
import com.android.internal.policy.PhoneFallbackEventHandler;
import com.android.internal.util.Preconditions;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
import com.android.internal.view.SurfaceCallbackHelper;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
private static final String TAG = "ViewRootImpl";
private static final boolean DBG = false;
private static final boolean LOCAL_LOGV = false;
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 DEBUG_FPS = false;
private static final boolean DEBUG_INPUT_STAGES = false || LOCAL_LOGV;
private static final boolean DEBUG_KEEP_SCREEN_ON = false || LOCAL_LOGV;
private static final boolean MT_RENDERER_AVAILABLE = true;
private static final String PROPERTY_PROFILE_RENDERING = "viewroot.profile_rendering";
public static final String PROPERTY_EMULATOR_WIN_OUTSET_BOTTOM_PX =
"ro.emu.win_outset_bottom_px";
static final int MAX_TRACKBALL_DELAY = 250;
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
static boolean sFirstDrawComplete = false;
private FrameDrawingCallback mNextRtFrameCallback;
public interface ConfigChangedCallback {
void onConfigurationChanged(Configuration globalConfig);
}
private static final ArrayList<ConfigChangedCallback> sConfigCallbacks = new ArrayList<>();
public interface ActivityConfigCallback {
void onConfigurationChanged(Configuration overrideConfig, int newDisplayId);
}
private ActivityConfigCallback mActivityConfigCallback;
private boolean mForceNextConfigUpdate;
private static boolean sCompatibilityDone = false;
private static boolean sAlwaysAssignFocus;
@GuardedBy("mWindowCallbacks")
final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList<>();
final Context mContext;
final IWindowSession mWindowSession;
@NonNull Display mDisplay;
final DisplayManager mDisplayManager;
final String mBasePackageName;
final int[] mTmpLocation = new int[2];
final TypedValue mTmpValue = new TypedValue();
final Thread mThread;
final WindowLeaked mLocation;
public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
final W mWindow;
final int mTargetSdkVersion;
int mSeq;
View mView;
View mAccessibilityFocusedHost;
AccessibilityNodeInfo mAccessibilityFocusedVirtualView;
boolean mPointerCapture;
int mViewVisibility;
boolean mAppVisible = true;
private boolean mForceDecorViewVisibility = false;
private boolean mAppVisibilityChanged;
int mOrigWindowType = -1;
boolean mHadWindowFocus;
boolean mLostWindowFocus;
boolean mStopped = false;
boolean mIsAmbientMode = false;
boolean mPausedForTransition = 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;
public boolean mIsAnimating;
private boolean mUseMTRenderer;
private boolean mDragResizing;
private boolean mInvalidateRootRequested;
private int mResizeMode;
private int mCanvasOffsetX;
private int mCanvasOffsetY;
private boolean mActivityRelaunched;
CompatibilityInfo.Translator mTranslator;
final View.AttachInfo mAttachInfo;
InputChannel mInputChannel;
InputQueue.Callback mInputQueueCallback;
InputQueue mInputQueue;
FallbackEventHandler mFallbackEventHandler;
Choreographer mChoreographer;
final Rect mTempRect;
final Rect mVisRect;
@GuardedBy("this")
boolean mWindowFocusChanged;
@GuardedBy("this")
boolean mUpcomingWindowFocus;
@GuardedBy("this")
boolean mUpcomingInTouchMode;
public boolean mTraversalScheduled;
int mTraversalBarrier;
boolean mWillDrawSoon;
boolean mIsInTraversal;
boolean mApplyInsetsRequested;
boolean mLayoutRequested;
boolean mFirst;
boolean mReportNextDraw;
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
boolean mHasHadWindowFocus;
boolean mLastWasImTarget;
boolean mForceNextWindowRelayout;
CountDownLatch mWindowDrawCountDown;
boolean mIsDrawing;
int mLastSystemUiVisibility;
int mClientWindowLayoutFlags;
boolean mLastOverscanRequested;
private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
private QueuedInputEvent mQueuedInputEventPool;
private int mQueuedInputEventPoolSize;
QueuedInputEvent mPendingInputEventHead;
QueuedInputEvent mPendingInputEventTail;
int mPendingInputEventCount;
boolean mProcessInputEventsScheduled;
boolean mUnbufferedInputDispatch;
String mPendingInputEventQueueLengthCounterName = "pq";
InputStage mFirstInputStage;
InputStage mFirstPostImeInputStage;
InputStage mSyntheticInputStage;
private final UnhandledKeyManager mUnhandledKeyManager = new UnhandledKeyManager();
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
public final Surface mSurface = new Surface();
boolean mAdded;
boolean mAddedTouchMode;
final Rect mWinFrame;
final Rect mPendingOverscanInsets = new Rect();
final Rect mPendingVisibleInsets = new Rect();
final Rect mPendingStableInsets = new Rect();
final Rect mPendingContentInsets = new Rect();
final Rect mPendingOutsets = new Rect();
final Rect mPendingBackDropFrame = new Rect();
final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
boolean mPendingAlwaysConsumeNavBar;
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
final Rect mDispatchContentInsets = new Rect();
final Rect mDispatchStableInsets = new Rect();
DisplayCutout mDispatchDisplayCutout = DisplayCutout.NO_CUTOUT;
private WindowInsets mLastWindowInsets;
private final Configuration mLastConfigurationFromResources = new Configuration();
private final MergedConfiguration mLastReportedMergedConfiguration = new MergedConfiguration();
private final MergedConfiguration mPendingMergedConfiguration = new MergedConfiguration();
boolean mScrollMayChange;
@SoftInputModeFlags
int mSoftInputMode;
WeakReference<View> mLastScrolledFocus;
int mScrollY;
int mCurScrollY;
Scroller mScroller;
static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator();
private ArrayList<LayoutTransition> mPendingTransitions;
final ViewConfiguration mViewConfiguration;
ClipDescription mDragDescription;
View mCurrentDragView;
volatile Object mLocalDragState;
final PointF mDragPoint = new PointF();
final PointF mLastTouchPoint = new PointF();
int mLastTouchSource;
private boolean mProfileRendering;
private Choreographer.FrameCallback mRenderProfiler;
private boolean mRenderProfilingEnabled;
private long mFpsStartTime = -1;
private long mFpsPrevTime = -1;
private int mFpsNumFrames;
private int mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED;
private PointerIcon mCustomPointerIcon = null;
AudioManager mAudioManager;
final AccessibilityManager mAccessibilityManager;
AccessibilityInteractionController mAccessibilityInteractionController;
final AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager =
new AccessibilityInteractionConnectionManager();
final HighContrastTextManager mHighContrastTextManager;
SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent;
HashSet<View> mTempHashSet;
private final int mDensity;
private final int mNoncompatDensity;
private boolean mInLayout = false;
ArrayList<View> mLayoutRequesters = new ArrayList<View>();
boolean mHandlingLayoutInLayoutRequest = false;
private int mViewLayoutDirectionInitial;
private boolean mRemoved;
private boolean mNeedsRendererSetup;
protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
static final class SystemUiVisibilityInfo {
int seq;
int globalVisibility;
int localValue;
int localChanges;
}
private String mTag = TAG;
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
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);
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
mViewVisibility = View.GONE;
mTransparentRegion = new Region();
mPreviousTransparentRegion = new Region();
mFirst = true;
mAdded = false;
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager, mHandler);
mHighContrastTextManager = new HighContrastTextManager();
mAccessibilityManager.addHighTextContrastStateChangeListener(
mHighContrastTextManager, mHandler);
mViewConfiguration = ViewConfiguration.get(context);
mDensity = context.getResources().getDisplayMetrics().densityDpi;
mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
mFallbackEventHandler = new PhoneFallbackEventHandler(context);
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
if (!sCompatibilityDone) {
sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
sCompatibilityDone = true;
}
loadSystemProperties();
}
public static void addFirstDrawHandler(Runnable callback) {
synchronized (sFirstDrawHandlers) {
if (!sFirstDrawComplete) {
sFirstDrawHandlers.add(callback);
}
}
}
public static void addConfigCallback(ConfigChangedCallback callback) {
synchronized (sConfigCallbacks) {
sConfigCallbacks.add(callback);
}
}
public void setActivityConfigCallback(ActivityConfigCallback callback) {
mActivityConfigCallback = callback;
}
public void addWindowCallbacks(WindowCallbacks callback) {
synchronized (mWindowCallbacks) {
mWindowCallbacks.add(callback);
}
}
public void removeWindowCallbacks(WindowCallbacks callback) {
synchronized (mWindowCallbacks) {
mWindowCallbacks.remove(callback);
}
}
public void reportDrawFinish() {
if (mWindowDrawCountDown != null) {
mWindowDrawCountDown.countDown();
}
}
private boolean mProfile = false;
public void profile() {
mProfile = true;
}
static boolean isInTouchMode() {
IWindowSession windowSession = WindowManagerGlobal.peekWindowSession();
if (windowSession != null) {
try {
return windowSession.getInTouchMode();
} catch (RemoteException e) {
}
}
return false;
}
public void notifyChildRebuilt() {
if (mView instanceof RootViewSurfaceTaker) {
if (mSurfaceHolderCallback != null) {
mSurfaceHolder.removeCallback(mSurfaceHolderCallback);
}
mSurfaceHolderCallback =
((RootViewSurfaceTaker)mView).willYouTakeTheSurface();
if (mSurfaceHolderCallback != null) {
mSurfaceHolder = new TakenSurfaceHolder();
mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
mSurfaceHolder.addCallback(mSurfaceHolderCallback);
} else {
mSurfaceHolder = null;
}
mInputQueueCallback =
((RootViewSurfaceTaker)mView).willYouTakeTheInputQueue();
if (mInputQueueCallback != null) {
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
}
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
mAttachInfo.mDisplayState = mDisplay.getState();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
mFallbackEventHandler.setView(view);
mWindowAttributes.copyFrom(attrs);
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
attrs = mWindowAttributes;
setTag();
if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags
& WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0
&& (attrs.flags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) == 0) {
Slog.d(mTag, "setView: FLAG_KEEP_SCREEN_ON changed from true to false!");
}
mClientWindowLayoutFlags = attrs.flags;
setAccessibilityFocus(null, null);
if (view instanceof RootViewSurfaceTaker) {
mSurfaceHolderCallback =
((RootViewSurfaceTaker)view).willYouTakeTheSurface();
if (mSurfaceHolderCallback != null) {
mSurfaceHolder = new TakenSurfaceHolder();
mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
mSurfaceHolder.addCallback(mSurfaceHolderCallback);
}
}
if (!attrs.hasManualSurfaceInsets) {
attrs.setSurfaceInsets(view, false , true );
}
CompatibilityInfo compatibilityInfo =
mDisplay.getDisplayAdjustments().getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
if (mSurfaceHolder == null) {
enableHardwareAcceleration(attrs);
final boolean useMTRenderer = MT_RENDERER_AVAILABLE
&& mAttachInfo.mThreadedRenderer != null;
if (mUseMTRenderer != useMTRenderer) {
endDragResizing();
mUseMTRenderer = useMTRenderer;
}
}
boolean restore = false;
if (mTranslator != null) {
mSurface.setCompatibilityTranslator(mTranslator);
restore = true;
attrs.backup();
mTranslator.translateWindowLayout(attrs);
}
if (DEBUG_LAYOUT) Log.d(mTag, "WindowLayout in setView:" + attrs);
if (!compatibilityInfo.supportsScreen()) {
attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
mLastInCompatMode = true;
}
mSoftInputMode = attrs.softInputMode;
mWindowAttributesChanged = true;
mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
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;
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
}
mPendingOverscanInsets.set(0, 0, 0, 0);
mPendingContentInsets.set(mAttachInfo.mContentInsets);
mPendingStableInsets.set(mAttachInfo.mStableInsets);
mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout);
mPendingVisibleInsets.set(0, 0, 0, 0);
mAttachInfo.mAlwaysConsumeNavBar =
(res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
mAdded = false;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
switch (res) {
case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not for an application");
case WindowManagerGlobal.ADD_APP_EXITING:
throw new WindowManager.BadTokenException(
"Unable to add window -- app for token " + attrs.token
+ " is exiting");
case WindowManagerGlobal.ADD_DUPLICATE_ADD:
throw new WindowManager.BadTokenException(
"Unable to add window -- window " + mWindow
+ " has already been added");
case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
return;
case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- another window of type "
+ mWindowAttributes.type + " already exists");
case WindowManagerGlobal.ADD_PERMISSION_DENIED:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- permission denied for window type "
+ mWindowAttributes.type);
case WindowManagerGlobal.ADD_INVALID_DISPLAY:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified display can not be found");
case WindowManagerGlobal.ADD_INVALID_TYPE:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified window type "
+ mWindowAttributes.type + " is not valid");
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
if (view instanceof RootViewSurfaceTaker) {
mInputQueueCallback =
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
}
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
view.assignParent(this);
mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;
if (mAccessibilityManager.isEnabled()) {
mAccessibilityInteractionConnectionManager.ensureConnection();
}
if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
CharSequence counterSuffix = attrs.getTitle();
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
}
}
}
private void setTag() {
final String[] split = mWindowAttributes.getTitle().toString().split("\\.");
if (split.length > 0) {
mTag = TAG + "[" + split[split.length - 1] + "]";
}
}
private boolean isInLocalFocusMode() {
return (mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
}
public int getWindowFlags() {
return mWindowAttributes.flags;
}
public int getDisplayId() {
return mDisplay.getDisplayId();
}
public CharSequence getTitle() {
return mWindowAttributes.getTitle();
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
void destroyHardwareResources() {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.destroyHardwareResources(mView);
mAttachInfo.mThreadedRenderer.destroy();
}
}
public void detachFunctor(long functor) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.stopDrawing();
}
}
public static void invokeFunctor(long functor, boolean waitForCompletion) {
ThreadedRenderer.invokeFunctor(functor, waitForCompletion);
}
public void registerAnimatingRenderNode(RenderNode animator) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.registerAnimatingRenderNode(animator);
} else {
if (mAttachInfo.mPendingAnimatingRenderNodes == null) {
mAttachInfo.mPendingAnimatingRenderNodes = new ArrayList<RenderNode>();
}
mAttachInfo.mPendingAnimatingRenderNodes.add(animator);
}
}
public void registerVectorDrawableAnimator(
AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.registerVectorDrawableAnimator(animator);
}
}
public void registerRtFrameCallback(FrameDrawingCallback callback) {
mNextRtFrameCallback = callback;
}
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
mAttachInfo.mHardwareAccelerationRequested = false;
if (mTranslator != null) return;
final boolean hardwareAccelerated =
(attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
if (hardwareAccelerated) {
if (!ThreadedRenderer.isAvailable()) {
return;
}
final boolean fakeHwAccelerated = (attrs.privateFlags &
WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0;
final boolean forceHwAccelerated = (attrs.privateFlags &
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;
if (fakeHwAccelerated) {
mAttachInfo.mHardwareAccelerationRequested = true;
} else if (!ThreadedRenderer.sRendererDisabled
|| (ThreadedRenderer.sSystemRendererDisabled && forceHwAccelerated)) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.destroy();
}
final Rect insets = attrs.surfaceInsets;
final boolean hasSurfaceInsets = insets.left != 0 || insets.right != 0
|| insets.top != 0 || insets.bottom != 0;
final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets;
final boolean wideGamut =
mContext.getResources().getConfiguration().isScreenWideColorGamut()
&& attrs.getColorMode() == ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT;
mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
attrs.getTitle().toString());
mAttachInfo.mThreadedRenderer.setWideGamut(wideGamut);
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mHardwareAccelerated =
mAttachInfo.mHardwareAccelerationRequested = true;
}
}
}
}
public View getView() {
return mView;
}
final WindowLeaked getLocation() {
return mLocation;
}
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
synchronized (this) {
final int oldInsetLeft = mWindowAttributes.surfaceInsets.left;
final int oldInsetTop = mWindowAttributes.surfaceInsets.top;
final int oldInsetRight = mWindowAttributes.surfaceInsets.right;
final int oldInsetBottom = mWindowAttributes.surfaceInsets.bottom;
final int oldSoftInputMode = mWindowAttributes.softInputMode;
final boolean oldHasManualSurfaceInsets = mWindowAttributes.hasManualSurfaceInsets;
if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags
& WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0
&& (attrs.flags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) == 0) {
Slog.d(mTag, "setLayoutParams: FLAG_KEEP_SCREEN_ON from true to false!");
}
mClientWindowLayoutFlags = attrs.flags;
final int compatibleWindowFlag = mWindowAttributes.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
attrs.systemUiVisibility = mWindowAttributes.systemUiVisibility;
attrs.subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility;
mWindowAttributesChangesFlag = mWindowAttributes.copyFrom(attrs);
if ((mWindowAttributesChangesFlag
& WindowManager.LayoutParams.TRANSLUCENT_FLAGS_CHANGED) != 0) {
mAttachInfo.mRecomputeGlobalAttributes = true;
}
if ((mWindowAttributesChangesFlag
& WindowManager.LayoutParams.LAYOUT_CHANGED) != 0) {
mAttachInfo.mNeedsUpdateLightCenter = true;
}
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
mWindowAttributes.privateFlags |= compatibleWindowFlag;
if (mWindowAttributes.preservePreviousSurfaceInsets) {
mWindowAttributes.surfaceInsets.set(
oldInsetLeft, oldInsetTop, oldInsetRight, oldInsetBottom);
mWindowAttributes.hasManualSurfaceInsets = oldHasManualSurfaceInsets;
} else if (mWindowAttributes.surfaceInsets.left != oldInsetLeft
|| mWindowAttributes.surfaceInsets.top != oldInsetTop
|| mWindowAttributes.surfaceInsets.right != oldInsetRight
|| mWindowAttributes.surfaceInsets.bottom != oldInsetBottom) {
mNeedsRendererSetup = true;
}
applyKeepScreenOnFlag(mWindowAttributes);
if (newView) {
mSoftInputMode = attrs.softInputMode;
requestLayout();
}
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;
mAppVisibilityChanged = true;
scheduleTraversals();
if (!mAppVisible) {
WindowManagerGlobal.trimForeground();
}
}
}
void handleGetNewSurface() {
mNewSurfaceNeeded = true;
mFullRedrawNeeded = true;
scheduleTraversals();
}
private final DisplayListener mDisplayListener = new DisplayListener() {
@Override
public void onDisplayChanged(int displayId) {
if (mView != null && mDisplay.getDisplayId() == displayId) {
final int oldDisplayState = mAttachInfo.mDisplayState;
final int newDisplayState = mDisplay.getState();
if (oldDisplayState != newDisplayState) {
mAttachInfo.mDisplayState = newDisplayState;
pokeDrawLockIfNeeded();
if (oldDisplayState != Display.STATE_UNKNOWN) {
final int oldScreenState = toViewScreenState(oldDisplayState);
final int newScreenState = toViewScreenState(newDisplayState);
if (oldScreenState != newScreenState) {
mView.dispatchScreenStateChanged(newScreenState);
}
if (oldDisplayState == Display.STATE_OFF) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
}
}
}
}
@Override
public void onDisplayRemoved(int displayId) {
}
@Override
public void onDisplayAdded(int displayId) {
}
private int toViewScreenState(int displayState) {
return displayState == Display.STATE_OFF ?
View.SCREEN_STATE_OFF : View.SCREEN_STATE_ON;
}
};
public void onMovedToDisplay(int displayId, Configuration config) {
if (mDisplay.getDisplayId() == displayId) {
return;
}
mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(displayId,
mView.getResources());
mAttachInfo.mDisplayState = mDisplay.getState();
mView.dispatchMovedToDisplay(mDisplay, config);
}
void pokeDrawLockIfNeeded() {
final int displayState = mAttachInfo.mDisplayState;
if (mView != null && mAdded && mTraversalScheduled
&& (displayState == Display.STATE_DOZE
|| displayState == Display.STATE_DOZE_SUSPEND)) {
try {
mWindowSession.pokeDrawLock(mWindow);
} catch (RemoteException ex) {
}
}
}
@Override
public void requestFitSystemWindows() {
checkThread();
mApplyInsetsRequested = true;
scheduleTraversals();
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
@Override
public boolean isLayoutRequested() {
return mLayoutRequested;
}
@Override
public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {
if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
mIsAnimating = true;
}
invalidate();
}
void invalidate() {
mDirty.set(0, 0, mWidth, mHeight);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
void invalidateWorld(View view) {
view.invalidate();
if (view instanceof ViewGroup) {
ViewGroup parent = (ViewGroup) view;
for (int i = 0; i < parent.getChildCount(); i++) {
invalidateWorld(parent.getChildAt(i));
}
}
}
@Override
public void invalidateChild(View child, Rect dirty) {
invalidateChildInParent(null, dirty);
}
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
if (dirty == null) {
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
}
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);
}
}
invalidateRectOnScreen(dirty);
return null;
}
private void invalidateRectOnScreen(Rect dirty) {
final Rect localDirty = mDirty;
if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {
mAttachInfo.mSetIgnoreDirtyState = true;
mAttachInfo.mIgnoreDirtyState = true;
}
localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
final float appScale = mAttachInfo.mApplicationScale;
final boolean intersected = localDirty.intersect(0, 0,
(int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
if (!intersected) {
localDirty.setEmpty();
}
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
scheduleTraversals();
}
}
public void setIsAmbientMode(boolean ambient) {
mIsAmbientMode = ambient;
}
interface WindowStoppedCallback {
public void windowStopped(boolean stopped);
}
private final ArrayList<WindowStoppedCallback> mWindowStoppedCallbacks = new ArrayList<>();
void addWindowStoppedCallback(WindowStoppedCallback c) {
mWindowStoppedCallbacks.add(c);
}
void removeWindowStoppedCallback(WindowStoppedCallback c) {
mWindowStoppedCallbacks.remove(c);
}
void setWindowStopped(boolean stopped) {
if (mStopped != stopped) {
mStopped = stopped;
final ThreadedRenderer renderer = mAttachInfo.mThreadedRenderer;
if (renderer != null) {
if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
renderer.setStopped(mStopped);
}
if (!mStopped) {
scheduleTraversals();
} else {
if (renderer != null) {
renderer.destroyHardwareResources(mView);
}
}
for (int i = 0; i < mWindowStoppedCallbacks.size(); i++) {
mWindowStoppedCallbacks.get(i).windowStopped(stopped);
}
if (mStopped) {
mSurface.release();
}
}
}
public void setPausedForTransition(boolean paused) {
mPausedForTransition = paused;
}
@Override
public ViewParent getParent() {
return null;
}
@Override
public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
if (child != mView) {
throw new RuntimeException("child is not mine, honest!");
}
return r.intersect(0, 0, mWidth, mHeight);
}
@Override
public void bringChildToFront(View child) {
}
int getHostVisibility() {
return (mAppVisible || mForceDecorViewVisibility) ? mView.getVisibility() : View.GONE;
}
public void requestTransitionStart(LayoutTransition transition) {
if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) {
if (mPendingTransitions == null) {
mPendingTransitions = new ArrayList<LayoutTransition>();
}
mPendingTransitions.add(transition);
}
}
void notifyRendererOfFramePending() {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.notifyFramePending();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
private void applyKeepScreenOnFlag(WindowManager.LayoutParams params) {
if (mAttachInfo.mKeepScreenOn) {
params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
} else {
params.flags = (params.flags&~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
| (mClientWindowLayoutFlags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
private boolean collectViewAttributes() {
if (mAttachInfo.mRecomputeGlobalAttributes) {
mAttachInfo.mRecomputeGlobalAttributes = false;
boolean oldScreenOn = mAttachInfo.mKeepScreenOn;
mAttachInfo.mKeepScreenOn = false;
mAttachInfo.mSystemUiVisibility = 0;
mAttachInfo.mHasSystemUiListeners = false;
mView.dispatchCollectViewAttributes(mAttachInfo, 0);
mAttachInfo.mSystemUiVisibility &= ~mAttachInfo.mDisabledSystemUiVisibility;
WindowManager.LayoutParams params = mWindowAttributes;
mAttachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params);
if (mAttachInfo.mKeepScreenOn != oldScreenOn
|| mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
|| mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
applyKeepScreenOnFlag(params);
params.subtreeSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
params.hasSystemUiListeners = mAttachInfo.mHasSystemUiListeners;
mView.dispatchWindowSystemUiVisiblityChanged(mAttachInfo.mSystemUiVisibility);
return true;
}
}
return false;
}
private int getImpliedSystemUiVisibility(WindowManager.LayoutParams params) {
int vis = 0;
if ((params.flags & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) != 0) {
vis |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
}
if ((params.flags & WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) != 0) {
vis |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
}
return vis;
}
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
int childWidthMeasureSpec;
int childHeightMeasureSpec;
boolean windowSizeMayChange = false;
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(mTag,
"Measuring " + host + " in display " + desiredWindowWidth
+ "x" + desiredWindowHeight + "...");
boolean goodMeasure = false;
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
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(mTag, "Window " + mView + ": baseSize=" + baseSize
+ ", desiredWindowWidth=" + desiredWindowWidth);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight()
+ ") from width spec: " + MeasureSpec.toString(childWidthMeasureSpec)
+ " and height spec: " + MeasureSpec.toString(childHeightMeasureSpec));
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
baseSize = (baseSize+desiredWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
+ baseSize);
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
if (DEBUG_DIALOG) Log.v(mTag, "Good!");
goodMeasure = true;
}
}
}
}
if (!goodMeasure) {
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
}
}
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after measure");
host.debug();
}
return windowSizeMayChange;
}
void transformMatrixToGlobal(Matrix m) {
m.preTranslate(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
}
void transformMatrixToLocal(Matrix m) {
m.postTranslate(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop);
}
WindowInsets getWindowInsets(boolean forceConstruct) {
if (mLastWindowInsets == null || forceConstruct) {
mDispatchContentInsets.set(mAttachInfo.mContentInsets);
mDispatchStableInsets.set(mAttachInfo.mStableInsets);
mDispatchDisplayCutout = mAttachInfo.mDisplayCutout.get();
Rect contentInsets = mDispatchContentInsets;
Rect stableInsets = mDispatchStableInsets;
DisplayCutout displayCutout = mDispatchDisplayCutout;
if (!forceConstruct
&& (!mPendingContentInsets.equals(contentInsets) ||
!mPendingStableInsets.equals(stableInsets) ||
!mPendingDisplayCutout.get().equals(displayCutout))) {
contentInsets = mPendingContentInsets;
stableInsets = mPendingStableInsets;
displayCutout = mPendingDisplayCutout.get();
}
Rect outsets = mAttachInfo.mOutsets;
if (outsets.left > 0 || outsets.top > 0 || outsets.right > 0 || outsets.bottom > 0) {
contentInsets = new Rect(contentInsets.left + outsets.left,
contentInsets.top + outsets.top, contentInsets.right + outsets.right,
contentInsets.bottom + outsets.bottom);
}
contentInsets = ensureInsetsNonNegative(contentInsets, "content");
stableInsets = ensureInsetsNonNegative(stableInsets, "stable");
mLastWindowInsets = new WindowInsets(contentInsets,
null , stableInsets,
mContext.getResources().getConfiguration().isScreenRound(),
mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
}
return mLastWindowInsets;
}
private Rect ensureInsetsNonNegative(Rect insets, String kind) {
if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) {
return new Rect(Math.max(0, insets.left),
Math.max(0, insets.top),
Math.max(0, insets.right),
Math.max(0, insets.bottom));
}
return insets;
}
void dispatchApplyInsets(View host) {
WindowInsets insets = getWindowInsets(true );
final boolean dispatchCutout = (mWindowAttributes.layoutInDisplayCutoutMode
== LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS);
if (!dispatchCutout) {
insets = insets.consumeDisplayCutout();
}
host.dispatchApplyWindowInsets(insets);
}
private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) {
return lp.type == TYPE_STATUS_BAR_PANEL
|| lp.type == TYPE_INPUT_METHOD
|| lp.type == TYPE_VOLUME_OVERLAY;
}
private int dipToPx(int dip) {
final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
return (int) (displayMetrics.density * dip + 0.5f);
}
private void performTraversals() {
final View host = mView;
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals");
host.debug();
}
if (host == null || !mAdded)
return;
mIsInTraversal = true;
mWillDrawSoon = true;
boolean windowSizeMayChange = false;
boolean newSurface = false;
boolean surfaceChanged = false;
WindowManager.LayoutParams lp = mWindowAttributes;
int desiredWindowWidth;
int desiredWindowHeight;
final int viewVisibility = getHostVisibility();
final boolean viewVisibilityChanged = !mFirst
&& (mViewVisibility != viewVisibility || mNewSurfaceNeeded
|| mAppVisibilityChanged);
mAppVisibilityChanged = false;
final boolean viewUserVisibilityChanged = !mFirst &&
((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));
WindowManager.LayoutParams params = null;
if (mWindowAttributesChanged) {
mWindowAttributesChanged = false;
surfaceChanged = true;
params = lp;
}
CompatibilityInfo compatibilityInfo =
mDisplay.getDisplayAdjustments().getCompatibilityInfo();
if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
params = lp;
mFullRedrawNeeded = true;
mLayoutRequested = true;
if (mLastInCompatMode) {
params.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
mLastInCompatMode = false;
} else {
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
mLastInCompatMode = true;
}
}
mWindowAttributesChangesFlag = 0;
Rect frame = mWinFrame;
if (mFirst) {
mFullRedrawNeeded = true;
mLayoutRequested = true;
final Configuration config = mContext.getResources().getConfiguration();
if (shouldUseDisplaySize(lp)) {
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
desiredWindowWidth = mWinFrame.width();
desiredWindowHeight = mWinFrame.height();
}
mAttachInfo.mUse32BitDrawingCache = true;
mAttachInfo.mHasWindowFocus = false;
mAttachInfo.mWindowVisibility = viewVisibility;
mAttachInfo.mRecomputeGlobalAttributes = false;
mLastConfigurationFromResources.setTo(config);
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
host.setLayoutDirection(config.getLayoutDirection());
}
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
mFullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}
if (viewVisibilityChanged) {
mAttachInfo.mWindowVisibility = viewVisibility;
host.dispatchWindowVisibilityChanged(viewVisibility);
if (viewUserVisibilityChanged) {
host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
}
if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
endDragResizing();
destroyHardwareResources();
}
if (viewVisibility == View.GONE) {
mHasHadWindowFocus = false;
}
}
if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
host.clearAccessibilityFocus();
}
getRunQueue().executeActions(mAttachInfo.mHandler);
boolean insetsChanged = false;
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
if (layoutRequested) {
final Resources res = mView.getContext().getResources();
if (mFirst) {
mAttachInfo.mInTouchMode = !mAddedTouchMode;
ensureTouchModeLocally(mAddedTouchMode);
} else {
if (!mPendingOverscanInsets.equals(mAttachInfo.mOverscanInsets)) {
insetsChanged = true;
}
if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
insetsChanged = true;
}
if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
insetsChanged = true;
}
if (!mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout)) {
insetsChanged = true;
}
if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
if (!mPendingOutsets.equals(mAttachInfo.mOutsets)) {
insetsChanged = true;
}
if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) {
insetsChanged = true;
}
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowSizeMayChange = true;
if (shouldUseDisplaySize(lp)) {
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
Configuration config = res.getConfiguration();
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
}
}
}
windowSizeMayChange |= measureHierarchy(host, lp, res,
desiredWindowWidth, desiredWindowHeight);
}
if (collectViewAttributes()) {
params = lp;
}
if (mAttachInfo.mForceReportNewAttributes) {
mAttachInfo.mForceReportNewAttributes = false;
params = lp;
}
if (mFirst || mAttachInfo.mViewVisibilityChanged) {
mAttachInfo.mViewVisibilityChanged = false;
int resizeMode = mSoftInputMode &
WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
final int N = mAttachInfo.mScrollContainers.size();
for (int i=0; i<N; i++) {
if (mAttachInfo.mScrollContainers.get(i).isShown()) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
}
}
if (resizeMode == 0) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
}
if ((lp.softInputMode &
WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) {
lp.softInputMode = (lp.softInputMode &
~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) |
resizeMode;
params = lp;
}
}
}
if (params != null) {
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
if (!PixelFormat.formatHasAlpha(params.format)) {
params.format = PixelFormat.TRANSLUCENT;
}
}
mAttachInfo.mOverscanRequested = (params.flags
& WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0;
}
if (mApplyInsetsRequested) {
mApplyInsetsRequested = false;
mLastOverscanRequested = mAttachInfo.mOverscanRequested;
dispatchApplyInsets(host);
if (mLayoutRequested) {
windowSizeMayChange |= measureHierarchy(host, lp,
mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
}
}
if (layoutRequested) {
mLayoutRequested = false;
}
boolean windowShouldResize = layoutRequested && windowSizeMayChange
&& ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
|| (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.width() < desiredWindowWidth && frame.width() != mWidth)
|| (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.height() < desiredWindowHeight && frame.height() != mHeight));
windowShouldResize |= mDragResizing && mResizeMode == RESIZE_MODE_FREEFORM;
windowShouldResize |= mActivityRelaunched;
final boolean computesInternalInsets =
mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
|| mAttachInfo.mHasNonEmptyGivenInternalInsets;
boolean insetsPending = false;
int relayoutResult = 0;
boolean updatedConfiguration = false;
final int surfaceGenerationId = mSurface.getGenerationId();
final boolean isViewVisible = viewVisibility == View.VISIBLE;
final boolean windowRelayoutWasForced = mForceNextWindowRelayout;
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
mForceNextWindowRelayout = false;
if (isViewVisible) {
insetsPending = computesInternalInsets && (mFirst || viewVisibilityChanged);
}
if (mSurfaceHolder != null) {
mSurfaceHolder.mSurfaceLock.lock();
mDrawingAllowed = true;
}
boolean hwInitialized = false;
boolean contentInsetsChanged = false;
boolean hadSurface = mSurface.isValid();
try {
if (DEBUG_LAYOUT) {
Log.i(mTag, "host=w:" + host.getMeasuredWidth() + ", h:" +
host.getMeasuredHeight() + ", params=" + params);
}
if (mAttachInfo.mThreadedRenderer != null) {
if (mAttachInfo.mThreadedRenderer.pauseSurface(mSurface)) {
mDirty.set(0, 0, mWidth, mHeight);
}
mChoreographer.mFrameInfo.addFlags(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED);
}
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
+ " overscan=" + mPendingOverscanInsets.toShortString()
+ " content=" + mPendingContentInsets.toShortString()
+ " visible=" + mPendingVisibleInsets.toShortString()
+ " stable=" + mPendingStableInsets.toShortString()
+ " cutout=" + mPendingDisplayCutout.get().toString()
+ " outsets=" + mPendingOutsets.toShortString()
+ " surface=" + mSurface);
if (!mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) {
if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
+ mPendingMergedConfiguration.getMergedConfiguration());
performConfigurationChange(mPendingMergedConfiguration, !mFirst,
INVALID_DISPLAY );
updatedConfiguration = true;
}
final boolean overscanInsetsChanged = !mPendingOverscanInsets.equals(
mAttachInfo.mOverscanInsets);
contentInsetsChanged = !mPendingContentInsets.equals(
mAttachInfo.mContentInsets);
final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
mAttachInfo.mVisibleInsets);
final boolean stableInsetsChanged = !mPendingStableInsets.equals(
mAttachInfo.mStableInsets);
final boolean cutoutChanged = !mPendingDisplayCutout.equals(
mAttachInfo.mDisplayCutout);
final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
final boolean surfaceSizeChanged = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
surfaceChanged |= surfaceSizeChanged;
final boolean alwaysConsumeNavBarChanged =
mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar;
if (contentInsetsChanged) {
mAttachInfo.mContentInsets.set(mPendingContentInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Content insets changing to: "
+ mAttachInfo.mContentInsets);
}
if (overscanInsetsChanged) {
mAttachInfo.mOverscanInsets.set(mPendingOverscanInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Overscan insets changing to: "
+ mAttachInfo.mOverscanInsets);
contentInsetsChanged = true;
}
if (stableInsetsChanged) {
mAttachInfo.mStableInsets.set(mPendingStableInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Decor insets changing to: "
+ mAttachInfo.mStableInsets);
contentInsetsChanged = true;
}
if (cutoutChanged) {
mAttachInfo.mDisplayCutout.set(mPendingDisplayCutout);
if (DEBUG_LAYOUT) {
Log.v(mTag, "DisplayCutout changing to: " + mAttachInfo.mDisplayCutout);
}
contentInsetsChanged = true;
}
if (alwaysConsumeNavBarChanged) {
mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
contentInsetsChanged = true;
}
if (contentInsetsChanged || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
|| mLastOverscanRequested != mAttachInfo.mOverscanRequested
|| outsetsChanged) {
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
mLastOverscanRequested = mAttachInfo.mOverscanRequested;
mAttachInfo.mOutsets.set(mPendingOutsets);
mApplyInsetsRequested = false;
dispatchApplyInsets(host);
}
if (visibleInsetsChanged) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
if (!hadSurface) {
if (mSurface.isValid()) {
newSurface = true;
mFullRedrawNeeded = true;
mPreviousTransparentRegion.setEmpty();
if (mAttachInfo.mThreadedRenderer != null) {
try {
hwInitialized = mAttachInfo.mThreadedRenderer.initialize(
mSurface);
if (hwInitialized && (host.mPrivateFlags
& View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
mAttachInfo.mThreadedRenderer.allocateBuffers(mSurface);
}
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
}
}
}
} else if (!mSurface.isValid()) {
if (mLastScrolledFocus != null) {
mLastScrolledFocus.clear();
}
mScrollY = mCurScrollY = 0;
if (mView instanceof RootViewSurfaceTaker) {
((RootViewSurfaceTaker) mView).onRootViewScrollYChanged(mCurScrollY);
}
if (mScroller != null) {
mScroller.abortAnimation();
}
if (mAttachInfo.mThreadedRenderer != null &&
mAttachInfo.mThreadedRenderer.isEnabled()) {
mAttachInfo.mThreadedRenderer.destroy();
}
} else if ((surfaceGenerationId != mSurface.getGenerationId()
|| surfaceSizeChanged || windowRelayoutWasForced)
&& mSurfaceHolder == null
&& mAttachInfo.mThreadedRenderer != null) {
mFullRedrawNeeded = true;
try {
mAttachInfo.mThreadedRenderer.updateSurface(mSurface);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
}
}
final boolean freeformResizing = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM) != 0;
final boolean dockedResizing = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED) != 0;
final boolean dragResizing = freeformResizing || dockedResizing;
if (mDragResizing != dragResizing) {
if (dragResizing) {
mResizeMode = freeformResizing
? RESIZE_MODE_FREEFORM
: RESIZE_MODE_DOCKED_DIVIDER;
startDragResizing(mPendingBackDropFrame,
mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
mPendingStableInsets, mResizeMode);
} else {
endDragResizing();
}
}
if (!mUseMTRenderer) {
if (dragResizing) {
mCanvasOffsetX = mWinFrame.left;
mCanvasOffsetY = mWinFrame.top;
} else {
mCanvasOffsetX = mCanvasOffsetY = 0;
}
}
} catch (RemoteException e) {
}
if (DEBUG_ORIENTATION) Log.v(
TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface);
mAttachInfo.mWindowLeft = frame.left;
mAttachInfo.mWindowTop = frame.top;
if (mWidth != frame.width() || mHeight != frame.height()) {
mWidth = frame.width();
mHeight = frame.height();
}
if (mSurfaceHolder != null) {
if (mSurface.isValid()) {
mSurfaceHolder.mSurface = mSurface;
}
mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight);
mSurfaceHolder.mSurfaceLock.unlock();
if (mSurface.isValid()) {
if (!hadSurface) {
mSurfaceHolder.ungetCallbacks();
mIsCreating = true;
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceCreated(mSurfaceHolder);
}
}
surfaceChanged = true;
}
if (surfaceChanged || surfaceGenerationId != mSurface.getGenerationId()) {
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceChanged(mSurfaceHolder, lp.format,
mWidth, mHeight);
}
}
}
mIsCreating = false;
} else if (hadSurface) {
mSurfaceHolder.ungetCallbacks();
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceDestroyed(mSurfaceHolder);
}
}
mSurfaceHolder.mSurfaceLock.lock();
try {
mSurfaceHolder.mSurface = new Surface();
} finally {
mSurfaceHolder.mSurfaceLock.unlock();
}
}
}
final ThreadedRenderer threadedRenderer = mAttachInfo.mThreadedRenderer;
if (threadedRenderer != null && threadedRenderer.isEnabled()) {
if (hwInitialized
|| mWidth != threadedRenderer.getWidth()
|| mHeight != threadedRenderer.getHeight()
|| mNeedsRendererSetup) {
threadedRenderer.setup(mWidth, mHeight, mAttachInfo,
mWindowAttributes.surfaceInsets);
mNeedsRendererSetup = false;
}
}
if (!mStopped || mReportNextDraw) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
updatedConfiguration) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth="
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
+ " mHeight=" + mHeight
+ " measuredHeight=" + host.getMeasuredHeight()
+ " coveredInsetsChanged=" + contentInsetsChanged);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
if (lp.horizontalWeight > 0.0f) {
width += (int) ((mWidth - width) * lp.horizontalWeight);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (measureAgain) {
if (DEBUG_LAYOUT) Log.v(mTag,
"And hey let's measure once more: width=" + width
+ " height=" + height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
} else {
maybeHandleWindowMove(frame);
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
performLayout(lp, mWidth, mHeight);
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
host.getLocationInWindow(mTmpLocation);
mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + host.mRight - host.mLeft,
mTmpLocation[1] + host.mBottom - host.mTop);
host.gatherTransparentRegion(mTransparentRegion);
if (mTranslator != null) {
mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
}
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after setFrame");
host.debug();
}
}
if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
if (computesInternalInsets) {
final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets;
insets.reset();
mAttachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
mAttachInfo.mHasNonEmptyGivenInternalInsets = !insets.isEmpty();
if (insetsPending || !mLastGivenInsets.equals(insets)) {
mLastGivenInsets.set(insets);
final Rect contentInsets;
final Rect visibleInsets;
final Region touchableRegion;
if (mTranslator != null) {
contentInsets = mTranslator.getTranslatedContentInsets(insets.contentInsets);
visibleInsets = mTranslator.getTranslatedVisibleInsets(insets.visibleInsets);
touchableRegion = mTranslator.getTranslatedTouchableArea(insets.touchableRegion);
} else {
contentInsets = insets.contentInsets;
visibleInsets = insets.visibleInsets;
touchableRegion = insets.touchableRegion;
}
try {
mWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets, touchableRegion);
} catch (RemoteException e) {
}
}
}
if (mFirst) {
if (sAlwaysAssignFocus || !isInTouchMode()) {
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "First: mView.hasFocus()=" + mView.hasFocus());
}
if (mView != null) {
if (!mView.hasFocus()) {
mView.restoreDefaultFocus();
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "First: requested focused view=" + mView.findFocus());
}
} else {
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "First: existing focused view=" + mView.findFocus());
}
}
}
} else {
View focused = mView.findFocus();
if (focused instanceof ViewGroup
&& ((ViewGroup) focused).getDescendantFocusability()
== ViewGroup.FOCUS_AFTER_DESCENDANTS) {
focused.restoreDefaultFocus();
}
}
}
final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible;
final boolean regainedFocus = hasWindowFocus && mLostWindowFocus;
if (regainedFocus) {
mLostWindowFocus = false;
} else if (!hasWindowFocus && mHadWindowFocus) {
mLostWindowFocus = true;
}
if (changedVisibility || regainedFocus) {
boolean isToast = (mWindowAttributes == null) ? false
: (mWindowAttributes.type == WindowManager.LayoutParams.TYPE_TOAST);
if (!isToast) {
host.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
}
mFirst = false;
mWillDrawSoon = false;
mNewSurfaceNeeded = false;
mActivityRelaunched = false;
mViewVisibility = viewVisibility;
mHadWindowFocus = hasWindowFocus;
if (hasWindowFocus && !isInLocalFocusMode()) {
final boolean imTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
if (imTarget != mLastWasImTarget) {
mLastWasImTarget = imTarget;
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && imTarget) {
imm.onPreWindowFocus(mView, hasWindowFocus);
imm.onPostWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
}
}
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportNextDraw();
}
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
performDraw();
} else {
if (isViewVisible) {
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
mIsInTraversal = false;
}
private void maybeHandleWindowMove(Rect frame) {
final boolean windowMoved = mAttachInfo.mWindowLeft != frame.left
|| mAttachInfo.mWindowTop != frame.top;
if (windowMoved) {
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWinFrame(frame);
}
mAttachInfo.mWindowLeft = frame.left;
mAttachInfo.mWindowTop = frame.top;
}
if (windowMoved || mAttachInfo.mNeedsUpdateLightCenter) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.setLightCenter(mAttachInfo);
}
mAttachInfo.mNeedsUpdateLightCenter = false;
}
}
private void handleWindowFocusChanged() {
final boolean hasWindowFocus;
final boolean inTouchMode;
synchronized (this) {
if (!mWindowFocusChanged) {
return;
}
mWindowFocusChanged = false;
hasWindowFocus = mUpcomingWindowFocus;
inTouchMode = mUpcomingInTouchMode;
}
if (mAdded) {
profileRendering(hasWindowFocus);
if (hasWindowFocus) {
ensureTouchModeLocally(inTouchMode);
if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
mFullRedrawNeeded = true;
try {
final WindowManager.LayoutParams lp = mWindowAttributes;
final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
mAttachInfo.mThreadedRenderer.initializeIfNeeded(
mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
} catch (OutOfResourcesException e) {
Log.e(mTag, "OutOfResourcesException locking surface", e);
try {
if (!mWindowSession.outOfMemory(mWindow)) {
Slog.w(mTag, "No processes killed for memory;"
+ " killing self");
Process.killProcess(Process.myPid());
}
} catch (RemoteException ex) {
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(
MSG_WINDOW_FOCUS_CHANGED), 500);
return;
}
}
}
mAttachInfo.mHasWindowFocus = hasWindowFocus;
mLastWasImTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
imm.onPreWindowFocus(mView, hasWindowFocus);
}
if (mView != null) {
mAttachInfo.mKeyDispatchState.reset();
mView.dispatchWindowFocusChanged(hasWindowFocus);
mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
if (mAttachInfo.mTooltipHost != null) {
mAttachInfo.mTooltipHost.hideTooltip();
}
}
if (hasWindowFocus) {
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
imm.onPostWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
mWindowAttributes.softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
((WindowManager.LayoutParams) mView.getLayoutParams())
.softInputMode &=
~WindowManager.LayoutParams
.SOFT_INPUT_IS_FORWARD_NAVIGATION;
mHasHadWindowFocus = true;
fireAccessibilityFocusEventIfHasFocusedNode();
} else {
if (mPointerCapture) {
handlePointerCaptureChanged(false);
}
}
}
mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
}
private void fireAccessibilityFocusEventIfHasFocusedNode() {
if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
return;
}
final View focusedView = mView.findFocus();
if (focusedView == null) {
return;
}
final AccessibilityNodeProvider provider = focusedView.getAccessibilityNodeProvider();
if (provider == null) {
focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
} else {
final AccessibilityNodeInfo focusedNode = findFocusedVirtualNode(provider);
if (focusedNode != null) {
final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(
focusedNode.getSourceNodeId());
final AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_FOCUSED);
event.setSource(focusedView, virtualId);
event.setPackageName(focusedNode.getPackageName());
event.setChecked(focusedNode.isChecked());
event.setContentDescription(focusedNode.getContentDescription());
event.setPassword(focusedNode.isPassword());
event.getText().add(focusedNode.getText());
event.setEnabled(focusedNode.isEnabled());
focusedView.getParent().requestSendAccessibilityEvent(focusedView, event);
focusedNode.recycle();
}
}
}
private AccessibilityNodeInfo findFocusedVirtualNode(AccessibilityNodeProvider provider) {
AccessibilityNodeInfo focusedNode = provider.findFocus(
AccessibilityNodeInfo.FOCUS_INPUT);
if (focusedNode != null) {
return focusedNode;
}
if (!mContext.isAutofillCompatibilityEnabled()) {
return null;
}
AccessibilityNodeInfo current = provider.createAccessibilityNodeInfo(
AccessibilityNodeProvider.HOST_VIEW_ID);
if (current.isFocused()) {
return current;
}
final Queue<AccessibilityNodeInfo> fringe = new LinkedList<>();
fringe.offer(current);
while (!fringe.isEmpty()) {
current = fringe.poll();
final LongArray childNodeIds = current.getChildNodeIds();
if (childNodeIds== null || childNodeIds.size() <= 0) {
continue;
}
final int childCount = childNodeIds.size();
for (int i = 0; i < childCount; i++) {
final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(
childNodeIds.get(i));
final AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(virtualId);
if (child != null) {
if (child.isFocused()) {
return child;
}
fringe.offer(child);
}
}
current.recycle();
}
return null;
}
private void handleOutOfResourcesException(Surface.OutOfResourcesException e) {
Log.e(mTag, "OutOfResourcesException initializing HW surface", e);
try {
if (!mWindowSession.outOfMemory(mWindow) &&
Process.myUid() != Process.SYSTEM_UID) {
Slog.w(mTag, "No processes killed for memory; killing self");
Process.killProcess(Process.myPid());
}
} catch (RemoteException ex) {
}
mLayoutRequested = true;
}
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
boolean isInLayout() {
return mInLayout;
}
boolean requestLayoutDuringLayout(final View view) {
if (view.mParent == null || view.mAttachInfo == null) {
return true;
}
if (!mLayoutRequesters.contains(view)) {
mLayoutRequesters.add(view);
}
if (!mHandlingLayoutInLayoutRequest) {
return true;
} else {
return false;
}
}
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
mLayoutRequested = false;
mScrollMayChange = true;
mInLayout = true;
final View host = mView;
if (host == null) {
return;
}
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
Log.v(mTag, "Laying out " + host + " to (" +
host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
int numViewsRequestingLayout = mLayoutRequesters.size();
if (numViewsRequestingLayout > 0) {
ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
false);
if (validLayoutRequesters != null) {
mHandlingLayoutInLayoutRequest = true;
int numValidRequests = validLayoutRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = validLayoutRequesters.get(i);
Log.w("View", "requestLayout() improperly called by " + view +
" during layout: running second layout pass");
view.requestLayout();
}
measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
mInLayout = true;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mHandlingLayoutInLayoutRequest = false;
validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
if (validLayoutRequesters != null) {
final ArrayList<View> finalRequesters = validLayoutRequesters;
getRunQueue().post(new Runnable() {
@Override
public void run() {
int numValidRequests = finalRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = finalRequesters.get(i);
Log.w("View", "requestLayout() improperly called by " + view +
" during second layout pass: posting in next frame");
view.requestLayout();
}
}
});
}
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
private ArrayList<View> getValidLayoutRequesters(ArrayList<View> layoutRequesters,
boolean secondLayoutRequests) {
int numViewsRequestingLayout = layoutRequesters.size();
ArrayList<View> validLayoutRequesters = null;
for (int i = 0; i < numViewsRequestingLayout; ++i) {
View view = layoutRequesters.get(i);
if (view != null && view.mAttachInfo != null && view.mParent != null &&
(secondLayoutRequests || (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) ==
View.PFLAG_FORCE_LAYOUT)) {
boolean gone = false;
View parent = view;
while (parent != null) {
if ((parent.mViewFlags & View.VISIBILITY_MASK) == View.GONE) {
gone = true;
break;
}
if (parent.mParent instanceof View) {
parent = (View) parent.mParent;
} else {
parent = null;
}
}
if (!gone) {
if (validLayoutRequesters == null) {
validLayoutRequesters = new ArrayList<View>();
}
validLayoutRequesters.add(view);
}
}
}
if (!secondLayoutRequests) {
for (int i = 0; i < numViewsRequestingLayout; ++i) {
View view = layoutRequesters.get(i);
while (view != null &&
(view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) != 0) {
view.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
if (view.mParent instanceof View) {
view = (View) view.mParent;
} else {
view = null;
}
}
}
}
layoutRequesters.clear();
return validLayoutRequesters;
}
@Override
public void requestTransparentRegion(View child) {
checkThread();
if (mView == child) {
mView.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
mWindowAttributesChanged = true;
mWindowAttributesChangesFlag = 0;
requestLayout();
}
}
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
int mHardwareXOffset;
int mHardwareYOffset;
@Override
public void onPreDraw(DisplayListCanvas canvas) {
if (mCurScrollY != 0 && mHardwareYOffset != 0 && mAttachInfo.mThreadedRenderer.isOpaque()) {
canvas.drawColor(Color.BLACK);
}
canvas.translate(-mHardwareXOffset, -mHardwareYOffset);
}
@Override
public void onPostDraw(DisplayListCanvas canvas) {
drawAccessibilityFocusedDrawableIfNeeded(canvas);
if (mUseMTRenderer) {
for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
mWindowCallbacks.get(i).onPostDraw(canvas);
}
}
}
void outputDisplayList(View view) {
view.mRenderNode.output();
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.serializeDisplayListTree();
}
}
private void profileRendering(boolean enabled) {
if (mProfileRendering) {
mRenderProfilingEnabled = enabled;
if (mRenderProfiler != null) {
mChoreographer.removeFrameCallback(mRenderProfiler);
}
if (mRenderProfilingEnabled) {
if (mRenderProfiler == null) {
mRenderProfiler = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
mDirty.set(0, 0, mWidth, mHeight);
scheduleTraversals();
if (mRenderProfilingEnabled) {
mChoreographer.postFrameCallback(mRenderProfiler);
}
}
};
}
mChoreographer.postFrameCallback(mRenderProfiler);
} else {
mRenderProfiler = null;
}
}
}
private void trackFPS() {
long nowTime = System.currentTimeMillis();
if (mFpsStartTime < 0) {
mFpsStartTime = mFpsPrevTime = nowTime;
mFpsNumFrames = 0;
} else {
++mFpsNumFrames;
String thisHash = Integer.toHexString(System.identityHashCode(this));
long frameTime = nowTime - mFpsPrevTime;
long totalTime = nowTime - mFpsStartTime;
Log.v(mTag, "0x" + thisHash + "\tFrame time:\t" + frameTime);
mFpsPrevTime = nowTime;
if (totalTime > 1000) {
float fps = (float) mFpsNumFrames * 1000 / totalTime;
Log.v(mTag, "0x" + thisHash + "\tFPS:\t" + fps);
mFpsStartTime = nowTime;
mFpsNumFrames = 0;
}
}
}
int mDrawsNeededToReport = 0;
void drawPending() {
mDrawsNeededToReport++;
}
void pendingDrawFinished() {
if (mDrawsNeededToReport == 0) {
throw new RuntimeException("Unbalanced drawPending/pendingDrawFinished calls");
}
mDrawsNeededToReport--;
if (mDrawsNeededToReport == 0) {
reportDrawFinished();
}
}
private void postDrawFinished() {
mHandler.sendEmptyMessage(MSG_DRAW_FINISHED);
}
private void reportDrawFinished() {
try {
mDrawsNeededToReport = 0;
mWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
} else if (mView == null) {
return;
}
final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
boolean usingAsyncReport = false;
if (mReportNextDraw && mAttachInfo.mThreadedRenderer != null
&& mAttachInfo.mThreadedRenderer.isEnabled()) {
usingAsyncReport = true;
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
pendingDrawFinished();
});
}
try {
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
usingAsyncReport = false;
}
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
for (int i = 0; i < count; i++) {
mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
}
mAttachInfo.mPendingAnimatingRenderNodes.clear();
}
if (mReportNextDraw) {
mReportNextDraw = false;
if (mWindowDrawCountDown != null) {
try {
mWindowDrawCountDown.await();
} catch (InterruptedException e) {
Log.e(mTag, "Window redraw count down interrupted!");
}
mWindowDrawCountDown = null;
}
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.setStopped(mStopped);
}
if (LOCAL_LOGV) {
Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
if (mSurfaceHolder != null && mSurface.isValid()) {
SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
} else if (!usingAsyncReport) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.fence();
}
pendingDrawFinished();
}
}
}
private boolean draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
if (!surface.isValid()) {
return false;
}
if (DEBUG_FPS) {
trackFPS();
}
if (!sFirstDrawComplete) {
synchronized (sFirstDrawHandlers) {
sFirstDrawComplete = true;
final int count = sFirstDrawHandlers.size();
for (int i = 0; i< count; i++) {
mHandler.post(sFirstDrawHandlers.get(i));
}
}
}
scrollToRectOrFocus(null, false);
if (mAttachInfo.mViewScrollChanged) {
mAttachInfo.mViewScrollChanged = false;
mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
}
boolean animating = mScroller != null && mScroller.computeScrollOffset();
final int curScrollY;
if (animating) {
curScrollY = mScroller.getCurrY();
} else {
curScrollY = mScrollY;
}
if (mCurScrollY != curScrollY) {
mCurScrollY = curScrollY;
fullRedrawNeeded = true;
if (mView instanceof RootViewSurfaceTaker) {
((RootViewSurfaceTaker) mView).onRootViewScrollYChanged(mCurScrollY);
}
}
final float appScale = mAttachInfo.mApplicationScale;
final boolean scalingRequired = mAttachInfo.mScalingRequired;
final Rect dirty = mDirty;
if (mSurfaceHolder != null) {
dirty.setEmpty();
if (animating && mScroller != null) {
mScroller.abortAnimation();
}
return false;
}
if (fullRedrawNeeded) {
mAttachInfo.mIgnoreDirtyState = true;
dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
}
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
Log.v(mTag, "Draw " + mView + "/"
+ mWindowAttributes.getTitle()
+ ": dirty={" + dirty.left + "," + dirty.top
+ "," + dirty.right + "," + dirty.bottom + "} surface="
+ surface + " surface.isValid()=" + surface.isValid() + ", appScale:" +
appScale + ", width=" + mWidth + ", height=" + mHeight);
}
mAttachInfo.mTreeObserver.dispatchOnDraw();
int xOffset = -mCanvasOffsetX;
int yOffset = -mCanvasOffsetY + curScrollY;
final WindowManager.LayoutParams params = mWindowAttributes;
final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
if (surfaceInsets != null) {
xOffset -= surfaceInsets.left;
yOffset -= surfaceInsets.top;
dirty.offset(surfaceInsets.left, surfaceInsets.right);
}
boolean accessibilityFocusDirty = false;
final Drawable drawable = mAttachInfo.mAccessibilityFocusDrawable;
if (drawable != null) {
final Rect bounds = mAttachInfo.mTmpInvalRect;
final boolean hasFocus = getAccessibilityFocusedRect(bounds);
if (!hasFocus) {
bounds.setEmpty();
}
if (!bounds.equals(drawable.getBounds())) {
accessibilityFocusDirty = true;
}
}
mAttachInfo.mDrawingTime =
mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
boolean useAsyncReport = false;
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
mInvalidateRootRequested = false;
mIsAnimating = false;
if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
mHardwareYOffset = yOffset;
mHardwareXOffset = xOffset;
invalidateRoot = true;
}
if (invalidateRoot) {
mAttachInfo.mThreadedRenderer.invalidateRoot();
}
dirty.setEmpty();
final boolean updated = updateContentDrawBounds();
if (mReportNextDraw) {
mAttachInfo.mThreadedRenderer.setStopped(false);
}
if (updated) {
requestDrawWindow();
}
useAsyncReport = true;
final FrameDrawingCallback callback = mNextRtFrameCallback;
mNextRtFrameCallback = null;
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, callback);
} else {
if (mAttachInfo.mThreadedRenderer != null &&
!mAttachInfo.mThreadedRenderer.isEnabled() &&
mAttachInfo.mThreadedRenderer.isRequested() &&
mSurface.isValid()) {
try {
mAttachInfo.mThreadedRenderer.initializeIfNeeded(
mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
}
mFullRedrawNeeded = true;
scheduleTraversals();
return false;
}
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
return useAsyncReport;
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
final Canvas canvas;
int dirtyXOffset = xoff;
int dirtyYOffset = yoff;
if (surfaceInsets != null) {
dirtyXOffset += surfaceInsets.left;
dirtyYOffset += surfaceInsets.top;
}
try {
dirty.offset(-dirtyXOffset, -dirtyYOffset);
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
canvas = mSurface.lockCanvas(dirty);
if (left != dirty.left || top != dirty.top || right != dirty.right
|| bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
}
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not lock surface", e);
mLayoutRequested = true;
return false;
} finally {
dirty.offset(dirtyXOffset, dirtyYOffset);
}
try {
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
+ canvas.getWidth() + ", h=" + canvas.getHeight());
}
if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
dirty.setEmpty();
mIsAnimating = false;
mView.mPrivateFlags |= View.PFLAG_DRAWN;
if (DEBUG_DRAW) {
Context cxt = mView.getContext();
Log.i(mTag, "Drawing: package:" + cxt.getPackageName() +
", metrics=" + cxt.getResources().getDisplayMetrics() +
", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
}
try {
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
attachInfo.mIgnoreDirtyState = false;
}
}
} finally {
try {
surface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not unlock surface", e);
mLayoutRequested = true;
return false;
}
if (LOCAL_LOGV) {
Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
}
}
return true;
}
private void drawAccessibilityFocusedDrawableIfNeeded(Canvas canvas) {
final Rect bounds = mAttachInfo.mTmpInvalRect;
if (getAccessibilityFocusedRect(bounds)) {
final Drawable drawable = getAccessibilityFocusedDrawable();
if (drawable != null) {
drawable.setBounds(bounds);
drawable.draw(canvas);
}
} else if (mAttachInfo.mAccessibilityFocusDrawable != null) {
mAttachInfo.mAccessibilityFocusDrawable.setBounds(0, 0, 0, 0);
}
}
private boolean getAccessibilityFocusedRect(Rect bounds) {
final AccessibilityManager manager = AccessibilityManager.getInstance(mView.mContext);
if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) {
return false;
}
final View host = mAccessibilityFocusedHost;
if (host == null || host.mAttachInfo == null) {
return false;
}
final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
if (provider == null) {
host.getBoundsOnScreen(bounds, true);
} else if (mAccessibilityFocusedVirtualView != null) {
mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds);
} else {
return false;
}
final AttachInfo attachInfo = mAttachInfo;
bounds.offset(0, attachInfo.mViewRootImpl.mScrollY);
bounds.offset(-attachInfo.mWindowLeft, -attachInfo.mWindowTop);
if (!bounds.intersect(0, 0, attachInfo.mViewRootImpl.mWidth,
attachInfo.mViewRootImpl.mHeight)) {
bounds.setEmpty();
}
return !bounds.isEmpty();
}
private Drawable getAccessibilityFocusedDrawable() {
if (mAttachInfo.mAccessibilityFocusDrawable == null) {
final TypedValue value = new TypedValue();
final boolean resolved = mView.mContext.getTheme().resolveAttribute(
R.attr.accessibilityFocusedDrawable, value, true);
if (resolved) {
mAttachInfo.mAccessibilityFocusDrawable =
mView.mContext.getDrawable(value.resourceId);
}
}
return mAttachInfo.mAccessibilityFocusDrawable;
}
public void requestInvalidateRootRenderNode() {
mInvalidateRootRequested = true;
}
boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
final Rect ci = mAttachInfo.mContentInsets;
final Rect vi = mAttachInfo.mVisibleInsets;
int scrollY = 0;
boolean handled = false;
if (vi.left > ci.left || vi.top > ci.top
|| vi.right > ci.right || vi.bottom > ci.bottom) {
scrollY = mScrollY;
final View focus = mView.findFocus();
if (focus == null) {
return false;
}
View lastScrolledFocus = (mLastScrolledFocus != null) ? mLastScrolledFocus.get() : null;
if (focus != lastScrolledFocus) {
rectangle = null;
}
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Eval scroll: focus=" + focus
+ " rectangle=" + rectangle + " ci=" + ci
+ " vi=" + vi);
if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) {
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Keeping scroll y="
+ mScrollY + " vi=" + vi.toShortString());
} else {
mLastScrolledFocus = new WeakReference<View>(focus);
mScrollMayChange = false;
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Need to scroll?");
if (focus.getGlobalVisibleRect(mVisRect, null)) {
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "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(mTag, "Focus " + focus
+ ": focusRect=" + mTempRect.toShortString());
if (mView instanceof ViewGroup) {
((ViewGroup) mView).offsetDescendantRectToMyCoords(
focus, mTempRect);
}
if (DEBUG_INPUT_RESIZE) Log.v(mTag,
"Focus in window: focusRect="
+ mTempRect.toShortString()
+ " visRect=" + mVisRect.toShortString());
} else {
mTempRect.set(rectangle);
if (DEBUG_INPUT_RESIZE) Log.v(mTag,
"Request scroll to rect: "
+ mTempRect.toShortString()
+ " visRect=" + mVisRect.toShortString());
}
if (mTempRect.intersect(mVisRect)) {
if (DEBUG_INPUT_RESIZE) Log.v(mTag,
"Focus window visible rect: "
+ mTempRect.toShortString());
if (mTempRect.height() >
(mView.getHeight()-vi.top-vi.bottom)) {
if (DEBUG_INPUT_RESIZE) Log.v(mTag,
"Too tall; leaving scrollY=" + scrollY);
}
else if (mTempRect.top < vi.top) {
scrollY = mTempRect.top - vi.top;
if (DEBUG_INPUT_RESIZE) Log.v(mTag,
"Top covered; scrollY=" + scrollY);
} else if (mTempRect.bottom > (mView.getHeight()-vi.bottom)) {
scrollY = mTempRect.bottom - (mView.getHeight()-vi.bottom);
if (DEBUG_INPUT_RESIZE) Log.v(mTag,
"Bottom covered; scrollY=" + scrollY);
} else {
scrollY = 0;
}
handled = true;
}
}
}
}
if (scrollY != mScrollY) {
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Pan scroll changed: old="
+ mScrollY + " , new=" + scrollY);
if (!immediate) {
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 View getAccessibilityFocusedHost() {
return mAccessibilityFocusedHost;
}
public AccessibilityNodeInfo getAccessibilityFocusedVirtualView() {
return mAccessibilityFocusedVirtualView;
}
void setAccessibilityFocus(View view, AccessibilityNodeInfo node) {
if (mAccessibilityFocusedVirtualView != null) {
AccessibilityNodeInfo focusNode = mAccessibilityFocusedVirtualView;
View focusHost = mAccessibilityFocusedHost;
mAccessibilityFocusedHost = null;
mAccessibilityFocusedVirtualView = null;
focusHost.clearAccessibilityFocusNoCallbacks(
AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
AccessibilityNodeProvider provider = focusHost.getAccessibilityNodeProvider();
if (provider != null) {
focusNode.getBoundsInParent(mTempRect);
focusHost.invalidate(mTempRect);
final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
focusNode.getSourceNodeId());
provider.performAction(virtualNodeId,
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
}
focusNode.recycle();
}
if ((mAccessibilityFocusedHost != null) && (mAccessibilityFocusedHost != view)) {
mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks(
AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
}
mAccessibilityFocusedHost = view;
mAccessibilityFocusedVirtualView = node;
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.invalidateRoot();
}
}
boolean hasPointerCapture() {
return mPointerCapture;
}
void requestPointerCapture(boolean enabled) {
if (mPointerCapture == enabled) {
return;
}
InputManager.getInstance().requestPointerCapture(mAttachInfo.mWindowToken, enabled);
}
private void handlePointerCaptureChanged(boolean hasCapture) {
if (mPointerCapture == hasCapture) {
return;
}
mPointerCapture = hasCapture;
if (mView != null) {
mView.dispatchPointerCaptureChanged(hasCapture);
}
}
@Override
public void requestChildFocus(View child, View focused) {
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "Request child focus: focus now " + focused);
}
checkThread();
scheduleTraversals();
}
@Override
public void clearChildFocus(View child) {
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "Clearing child focus");
}
checkThread();
scheduleTraversals();
}
@Override
public ViewParent getParentForAccessibility() {
return null;
}
@Override
public void focusableViewAvailable(View v) {
checkThread();
if (mView != null) {
if (!mView.hasFocus()) {
if (sAlwaysAssignFocus || !mAttachInfo.mInTouchMode) {
v.requestFocus();
}
} else {
View focused = mView.findFocus();
if (focused instanceof ViewGroup) {
ViewGroup group = (ViewGroup) focused;
if (group.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS
&& isViewDescendantOf(v, focused)) {
v.requestFocus();
}
}
}
}
}
@Override
public void recomputeViewAttributes(View child) {
checkThread();
if (mView == child) {
mAttachInfo.mRecomputeGlobalAttributes = true;
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
}
void dispatchDetachedFromWindow() {
mFirstInputStage.onDetachedFromWindow();
if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
mView.dispatchDetachedFromWindow();
}
mAccessibilityInteractionConnectionManager.ensureNoConnection();
mAccessibilityManager.removeAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager);
mAccessibilityManager.removeHighTextContrastStateChangeListener(
mHighContrastTextManager);
removeSendWindowContentChangedCallback();
destroyHardwareRenderer();
setAccessibilityFocus(null, null);
mView.assignParent(null);
mView = null;
mAttachInfo.mRootView = null;
mSurface.release();
if (mInputQueueCallback != null && mInputQueue != null) {
mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
mInputQueue.dispose();
mInputQueueCallback = null;
mInputQueue = null;
}
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
mInputEventReceiver = null;
}
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
if (mInputChannel != null) {
mInputChannel.dispose();
mInputChannel = null;
}
mDisplayManager.unregisterDisplayListener(mDisplayListener);
unscheduleTraversals();
}
private void performConfigurationChange(MergedConfiguration mergedConfiguration, boolean force,
int newDisplayId) {
if (mergedConfiguration == null) {
throw new IllegalArgumentException("No merged config provided.");
}
Configuration globalConfig = mergedConfiguration.getGlobalConfiguration();
final Configuration overrideConfig = mergedConfiguration.getOverrideConfiguration();
if (DEBUG_CONFIGURATION) Log.v(mTag,
"Applying new config to window " + mWindowAttributes.getTitle()
+ ", globalConfig: " + globalConfig
+ ", overrideConfig: " + overrideConfig);
final CompatibilityInfo ci = mDisplay.getDisplayAdjustments().getCompatibilityInfo();
if (!ci.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) {
globalConfig = new Configuration(globalConfig);
ci.applyToConfiguration(mNoncompatDensity, globalConfig);
}
synchronized (sConfigCallbacks) {
for (int i=sConfigCallbacks.size()-1; i>=0; i--) {
sConfigCallbacks.get(i).onConfigurationChanged(globalConfig);
}
}
mLastReportedMergedConfiguration.setConfiguration(globalConfig, overrideConfig);
mForceNextConfigUpdate = force;
if (mActivityConfigCallback != null) {
mActivityConfigCallback.onConfigurationChanged(overrideConfig, newDisplayId);
} else {
updateConfiguration(newDisplayId);
}
mForceNextConfigUpdate = false;
}
public void updateConfiguration(int newDisplayId) {
if (mView == null) {
return;
}
final Resources localResources = mView.getResources();
final Configuration config = localResources.getConfiguration();
if (newDisplayId != INVALID_DISPLAY) {
onMovedToDisplay(newDisplayId, config);
}
if (mForceNextConfigUpdate || mLastConfigurationFromResources.diff(config) != 0) {
mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(
mDisplay.getDisplayId(), localResources);
final int lastLayoutDirection = mLastConfigurationFromResources.getLayoutDirection();
final int currentLayoutDirection = config.getLayoutDirection();
mLastConfigurationFromResources.setTo(config);
if (lastLayoutDirection != currentLayoutDirection
&& mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
mView.setLayoutDirection(currentLayoutDirection);
}
mView.dispatchConfigurationChanged(config);
mForceNextWindowRelayout = true;
requestLayout();
}
}
public 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));
}
}
}
private final static int MSG_INVALIDATE = 1;
private final static int MSG_INVALIDATE_RECT = 2;
private final static int MSG_DIE = 3;
private final static int MSG_RESIZED = 4;
private final static int MSG_RESIZED_REPORT = 5;
private final static int MSG_WINDOW_FOCUS_CHANGED = 6;
private final static int MSG_DISPATCH_INPUT_EVENT = 7;
private final static int MSG_DISPATCH_APP_VISIBILITY = 8;
private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9;
private final static int MSG_DISPATCH_KEY_FROM_IME = 11;
private final static int MSG_DISPATCH_KEY_FROM_AUTOFILL = 12;
private final static int MSG_CHECK_FOCUS = 13;
private final static int MSG_CLOSE_SYSTEM_DIALOGS = 14;
private final static int MSG_DISPATCH_DRAG_EVENT = 15;
private final static int MSG_DISPATCH_DRAG_LOCATION_EVENT = 16;
private final static int MSG_DISPATCH_SYSTEM_UI_VISIBILITY = 17;
private final static int MSG_UPDATE_CONFIGURATION = 18;
private final static int MSG_PROCESS_INPUT_EVENTS = 19;
private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 21;
private final static int MSG_INVALIDATE_WORLD = 22;
private final static int MSG_WINDOW_MOVED = 23;
private final static int MSG_SYNTHESIZE_INPUT_EVENT = 24;
private final static int MSG_DISPATCH_WINDOW_SHOWN = 25;
private final static int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26;
private final static int MSG_UPDATE_POINTER_ICON = 27;
private final static int MSG_POINTER_CAPTURE_CHANGED = 28;
private final static int MSG_DRAW_FINISHED = 29;
final class ViewRootHandler extends Handler {
@Override
public String getMessageName(Message message) {
switch (message.what) {
case MSG_INVALIDATE:
return "MSG_INVALIDATE";
case MSG_INVALIDATE_RECT:
return "MSG_INVALIDATE_RECT";
case MSG_DIE:
return "MSG_DIE";
case MSG_RESIZED:
return "MSG_RESIZED";
case MSG_RESIZED_REPORT:
return "MSG_RESIZED_REPORT";
case MSG_WINDOW_FOCUS_CHANGED:
return "MSG_WINDOW_FOCUS_CHANGED";
case MSG_DISPATCH_INPUT_EVENT:
return "MSG_DISPATCH_INPUT_EVENT";
case MSG_DISPATCH_APP_VISIBILITY:
return "MSG_DISPATCH_APP_VISIBILITY";
case MSG_DISPATCH_GET_NEW_SURFACE:
return "MSG_DISPATCH_GET_NEW_SURFACE";
case MSG_DISPATCH_KEY_FROM_IME:
return "MSG_DISPATCH_KEY_FROM_IME";
case MSG_DISPATCH_KEY_FROM_AUTOFILL:
return "MSG_DISPATCH_KEY_FROM_AUTOFILL";
case MSG_CHECK_FOCUS:
return "MSG_CHECK_FOCUS";
case MSG_CLOSE_SYSTEM_DIALOGS:
return "MSG_CLOSE_SYSTEM_DIALOGS";
case MSG_DISPATCH_DRAG_EVENT:
return "MSG_DISPATCH_DRAG_EVENT";
case MSG_DISPATCH_DRAG_LOCATION_EVENT:
return "MSG_DISPATCH_DRAG_LOCATION_EVENT";
case MSG_DISPATCH_SYSTEM_UI_VISIBILITY:
return "MSG_DISPATCH_SYSTEM_UI_VISIBILITY";
case MSG_UPDATE_CONFIGURATION:
return "MSG_UPDATE_CONFIGURATION";
case MSG_PROCESS_INPUT_EVENTS:
return "MSG_PROCESS_INPUT_EVENTS";
case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST:
return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST";
case MSG_WINDOW_MOVED:
return "MSG_WINDOW_MOVED";
case MSG_SYNTHESIZE_INPUT_EVENT:
return "MSG_SYNTHESIZE_INPUT_EVENT";
case MSG_DISPATCH_WINDOW_SHOWN:
return "MSG_DISPATCH_WINDOW_SHOWN";
case MSG_UPDATE_POINTER_ICON:
return "MSG_UPDATE_POINTER_ICON";
case MSG_POINTER_CAPTURE_CHANGED:
return "MSG_POINTER_CAPTURE_CHANGED";
case MSG_DRAW_FINISHED:
return "MSG_DRAW_FINISHED";
}
return super.getMessageName(message);
}
@Override
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
if (msg.what == MSG_REQUEST_KEYBOARD_SHORTCUTS && msg.obj == null) {
throw new NullPointerException(
"Attempted to call MSG_REQUEST_KEYBOARD_SHORTCUTS with null receiver:");
}
return super.sendMessageAtTime(msg, uptimeMillis);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
case MSG_INVALIDATE_RECT:
final View.AttachInfo.InvalidateInfo info =
(View.AttachInfo.InvalidateInfo) msg.obj;
info.target.invalidate(info.left, info.top, info.right, info.bottom);
info.recycle();
break;
case MSG_PROCESS_INPUT_EVENTS:
mProcessInputEventsScheduled = false;
doProcessInputEvents();
break;
case MSG_DISPATCH_APP_VISIBILITY:
handleAppVisibility(msg.arg1 != 0);
break;
case MSG_DISPATCH_GET_NEW_SURFACE:
handleGetNewSurface();
break;
case MSG_RESIZED: {
SomeArgs args = (SomeArgs) msg.obj;
if (mWinFrame.equals(args.arg1)
&& mPendingOverscanInsets.equals(args.arg5)
&& mPendingContentInsets.equals(args.arg2)
&& mPendingStableInsets.equals(args.arg6)
&& mPendingDisplayCutout.get().equals(args.arg9)
&& mPendingVisibleInsets.equals(args.arg3)
&& mPendingOutsets.equals(args.arg7)
&& mPendingBackDropFrame.equals(args.arg8)
&& args.arg4 == null
&& args.argi1 == 0
&& mDisplay.getDisplayId() == args.argi3) {
break;
}
}
case MSG_RESIZED_REPORT:
if (mAdded) {
SomeArgs args = (SomeArgs) msg.obj;
final int displayId = args.argi3;
MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4;
final boolean displayChanged = mDisplay.getDisplayId() != displayId;
if (!mLastReportedMergedConfiguration.equals(mergedConfiguration)) {
performConfigurationChange(mergedConfiguration, false ,
displayChanged
? displayId : INVALID_DISPLAY );
} else if (displayChanged) {
onMovedToDisplay(displayId, mLastConfigurationFromResources);
}
final boolean framesChanged = !mWinFrame.equals(args.arg1)
|| !mPendingOverscanInsets.equals(args.arg5)
|| !mPendingContentInsets.equals(args.arg2)
|| !mPendingStableInsets.equals(args.arg6)
|| !mPendingDisplayCutout.get().equals(args.arg9)
|| !mPendingVisibleInsets.equals(args.arg3)
|| !mPendingOutsets.equals(args.arg7);
mWinFrame.set((Rect) args.arg1);
mPendingOverscanInsets.set((Rect) args.arg5);
mPendingContentInsets.set((Rect) args.arg2);
mPendingStableInsets.set((Rect) args.arg6);
mPendingDisplayCutout.set((DisplayCutout) args.arg9);
mPendingVisibleInsets.set((Rect) args.arg3);
mPendingOutsets.set((Rect) args.arg7);
mPendingBackDropFrame.set((Rect) args.arg8);
mForceNextWindowRelayout = args.argi1 != 0;
mPendingAlwaysConsumeNavBar = args.argi2 != 0;
args.recycle();
if (msg.what == MSG_RESIZED_REPORT) {
reportNextDraw();
}
if (mView != null && framesChanged) {
forceLayout(mView);
}
requestLayout();
}
break;
case MSG_WINDOW_MOVED:
if (mAdded) {
final int w = mWinFrame.width();
final int h = mWinFrame.height();
final int l = msg.arg1;
final int t = msg.arg2;
mWinFrame.left = l;
mWinFrame.right = l + w;
mWinFrame.top = t;
mWinFrame.bottom = t + h;
mPendingBackDropFrame.set(mWinFrame);
maybeHandleWindowMove(mWinFrame);
}
break;
case MSG_WINDOW_FOCUS_CHANGED: {
handleWindowFocusChanged();
} break;
case MSG_DIE:
doDie();
break;
case MSG_DISPATCH_INPUT_EVENT: {
SomeArgs args = (SomeArgs) msg.obj;
InputEvent event = (InputEvent) args.arg1;
InputEventReceiver receiver = (InputEventReceiver) args.arg2;
enqueueInputEvent(event, receiver, 0, true);
args.recycle();
} break;
case MSG_SYNTHESIZE_INPUT_EVENT: {
InputEvent event = (InputEvent) msg.obj;
enqueueInputEvent(event, null, QueuedInputEvent.FLAG_UNHANDLED, true);
} break;
case MSG_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) {
event = KeyEvent.changeFlags(event,
event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
}
enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true);
} break;
case MSG_DISPATCH_KEY_FROM_AUTOFILL: {
if (LOCAL_LOGV) {
Log.v(TAG, "Dispatching key " + msg.obj + " from Autofill to " + mView);
}
KeyEvent event = (KeyEvent) msg.obj;
enqueueInputEvent(event, null, 0, true);
} break;
case MSG_CHECK_FOCUS: {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
imm.checkFocus();
}
} break;
case MSG_CLOSE_SYSTEM_DIALOGS: {
if (mView != null) {
mView.onCloseSystemDialogs((String) msg.obj);
}
} break;
case MSG_DISPATCH_DRAG_EVENT: {
}
case MSG_DISPATCH_DRAG_LOCATION_EVENT: {
DragEvent event = (DragEvent) msg.obj;
event.mLocalState = mLocalDragState;
handleDragEvent(event);
} break;
case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: {
handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj);
} break;
case MSG_UPDATE_CONFIGURATION: {
Configuration config = (Configuration) msg.obj;
if (config.isOtherSeqNewer(
mLastReportedMergedConfiguration.getMergedConfiguration())) {
config = mLastReportedMergedConfiguration.getGlobalConfiguration();
}
mPendingMergedConfiguration.setConfiguration(config,
mLastReportedMergedConfiguration.getOverrideConfiguration());
performConfigurationChange(mPendingMergedConfiguration, false ,
INVALID_DISPLAY );
} break;
case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: {
setAccessibilityFocus(null, null);
} break;
case MSG_INVALIDATE_WORLD: {
if (mView != null) {
invalidateWorld(mView);
}
} break;
case MSG_DISPATCH_WINDOW_SHOWN: {
handleDispatchWindowShown();
} break;
case MSG_REQUEST_KEYBOARD_SHORTCUTS: {
final IResultReceiver receiver = (IResultReceiver) msg.obj;
final int deviceId = msg.arg1;
handleRequestKeyboardShortcuts(receiver, deviceId);
} break;
case MSG_UPDATE_POINTER_ICON: {
MotionEvent event = (MotionEvent) msg.obj;
resetPointerIcon(event);
} break;
case MSG_POINTER_CAPTURE_CHANGED: {
final boolean hasCapture = msg.arg1 != 0;
handlePointerCaptureChanged(hasCapture);
} break;
case MSG_DRAW_FINISHED: {
pendingDrawFinished();
} break;
}
}
}
final ViewRootHandler mHandler = new ViewRootHandler();
boolean ensureTouchMode(boolean inTouchMode) {
if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current "
+ "touch mode is " + mAttachInfo.mInTouchMode);
if (mAttachInfo.mInTouchMode == inTouchMode) return false;
try {
mWindowSession.setInTouchMode(inTouchMode);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
return ensureTouchModeLocally(inTouchMode);
}
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 && mView.hasFocus()) {
final View focused = mView.findFocus();
if (focused != null && !focused.isFocusableInTouchMode()) {
final ViewGroup ancestorToTakeFocus = findAncestorToTakeFocusInTouchMode(focused);
if (ancestorToTakeFocus != null) {
return ancestorToTakeFocus.requestFocus();
} else {
focused.clearFocusInternal(null, true, false);
return true;
}
}
}
return false;
}
private static 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()) {
View focusedView = mView.findFocus();
if (!(focusedView instanceof ViewGroup)) {
return false;
} else if (((ViewGroup) focusedView).getDescendantFocusability() !=
ViewGroup.FOCUS_AFTER_DESCENDANTS) {
return false;
}
}
return mView.restoreDefaultFocus();
}
return false;
}
abstract class InputStage {
private final InputStage mNext;
protected static final int FORWARD = 0;
protected static final int FINISH_HANDLED = 1;
protected static final int FINISH_NOT_HANDLED = 2;
public InputStage(InputStage next) {
mNext = next;
}
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
apply(q, onProcess(q));
}
}
protected void finish(QueuedInputEvent q, boolean handled) {
q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
if (handled) {
q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
}
forward(q);
}
protected void forward(QueuedInputEvent q) {
onDeliverToNext(q);
}
protected void apply(QueuedInputEvent q, int result) {
if (result == FORWARD) {
forward(q);
} else if (result == FINISH_HANDLED) {
finish(q, true);
} else if (result == FINISH_NOT_HANDLED) {
finish(q, false);
} else {
throw new IllegalArgumentException("Invalid result: " + result);
}
}
protected int onProcess(QueuedInputEvent q) {
return FORWARD;
}
protected void onDeliverToNext(QueuedInputEvent q) {
if (DEBUG_INPUT_STAGES) {
Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
}
if (mNext != null) {
mNext.deliver(q);
} else {
finishInputEvent(q);
}
}
protected void onWindowFocusChanged(boolean hasWindowFocus) {
if (mNext != null) {
mNext.onWindowFocusChanged(hasWindowFocus);
}
}
protected void onDetachedFromWindow() {
if (mNext != null) {
mNext.onDetachedFromWindow();
}
}
protected boolean shouldDropInputEvent(QueuedInputEvent q) {
if (mView == null || !mAdded) {
Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent);
return true;
} else if ((!mAttachInfo.mHasWindowFocus
&& !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
&& !isAutofillUiShowing()) || mStopped
|| (mIsAmbientMode && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON))
|| (mPausedForTransition && !isBack(q.mEvent))) {
if (isTerminalInputEvent(q.mEvent)) {
q.mEvent.cancel();
Slog.w(mTag, "Cancelling event due to no window focus: " + q.mEvent);
return false;
}
Slog.w(mTag, "Dropping event due to no window focus: " + q.mEvent);
return true;
}
return false;
}
void dump(String prefix, PrintWriter writer) {
if (mNext != null) {
mNext.dump(prefix, writer);
}
}
private boolean isBack(InputEvent event) {
if (event instanceof KeyEvent) {
return ((KeyEvent) event).getKeyCode() == KeyEvent.KEYCODE_BACK;
} else {
return false;
}
}
}
abstract class AsyncInputStage extends InputStage {
private final String mTraceCounter;
private QueuedInputEvent mQueueHead;
private QueuedInputEvent mQueueTail;
private int mQueueLength;
protected static final int DEFER = 3;
public AsyncInputStage(InputStage next, String traceCounter) {
super(next);
mTraceCounter = traceCounter;
}
protected void defer(QueuedInputEvent q) {
q.mFlags |= QueuedInputEvent.FLAG_DEFERRED;
enqueue(q);
}
@Override
protected void forward(QueuedInputEvent q) {
q.mFlags &= ~QueuedInputEvent.FLAG_DEFERRED;
QueuedInputEvent curr = mQueueHead;
if (curr == null) {
super.forward(q);
return;
}
final int deviceId = q.mEvent.getDeviceId();
QueuedInputEvent prev = null;
boolean blocked = false;
while (curr != null && curr != q) {
if (!blocked && deviceId == curr.mEvent.getDeviceId()) {
blocked = true;
}
prev = curr;
curr = curr.mNext;
}
if (blocked) {
if (curr == null) {
enqueue(q);
}
return;
}
if (curr != null) {
curr = curr.mNext;
dequeue(q, prev);
}
super.forward(q);
while (curr != null) {
if (deviceId == curr.mEvent.getDeviceId()) {
if ((curr.mFlags & QueuedInputEvent.FLAG_DEFERRED) != 0) {
break;
}
QueuedInputEvent next = curr.mNext;
dequeue(curr, prev);
super.forward(curr);
curr = next;
} else {
prev = curr;
curr = curr.mNext;
}
}
}
@Override
protected void apply(QueuedInputEvent q, int result) {
if (result == DEFER) {
defer(q);
} else {
super.apply(q, result);
}
}
private void enqueue(QueuedInputEvent q) {
if (mQueueTail == null) {
mQueueHead = q;
mQueueTail = q;
} else {
mQueueTail.mNext = q;
mQueueTail = q;
}
mQueueLength += 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength);
}
private void dequeue(QueuedInputEvent q, QueuedInputEvent prev) {
if (prev == null) {
mQueueHead = q.mNext;
} else {
prev.mNext = q.mNext;
}
if (mQueueTail == q) {
mQueueTail = prev;
}
q.mNext = null;
mQueueLength -= 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength);
}
@Override
void dump(String prefix, PrintWriter writer) {
writer.print(prefix);
writer.print(getClass().getName());
writer.print(": mQueueLength=");
writer.println(mQueueLength);
super.dump(prefix, writer);
}
}
final class NativePreImeInputStage extends AsyncInputStage
implements InputQueue.FinishedInputEventCallback {
public NativePreImeInputStage(InputStage next, String traceCounter) {
super(next, traceCounter);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
mInputQueue.sendInputEvent(q.mEvent, q, true, this);
return DEFER;
}
return FORWARD;
}
@Override
public void onFinishedInputEvent(Object token, boolean handled) {
QueuedInputEvent q = (QueuedInputEvent)token;
if (handled) {
finish(q, true);
return;
}
forward(q);
}
}
final class ViewPreImeInputStage extends InputStage {
public ViewPreImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
}
return FORWARD;
}
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
if (mView.dispatchKeyEventPreIme(event)) {
return FINISH_HANDLED;
}
return FORWARD;
}
}
final class ImeInputStage extends AsyncInputStage
implements InputMethodManager.FinishedInputEventCallback {
public ImeInputStage(InputStage next, String traceCounter) {
super(next, traceCounter);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (mLastWasImTarget && !isInLocalFocusMode()) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
final InputEvent event = q.mEvent;
if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
int result = imm.dispatchInputEvent(event, q, this, mHandler);
if (result == InputMethodManager.DISPATCH_HANDLED) {
return FINISH_HANDLED;
} else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
return FORWARD;
} else {
return DEFER;
}
}
}
return FORWARD;
}
@Override
public void onFinishedInputEvent(Object token, boolean handled) {
QueuedInputEvent q = (QueuedInputEvent)token;
if (handled) {
finish(q, true);
return;
}
forward(q);
}
}
final class EarlyPostImeInputStage extends InputStage {
public EarlyPostImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
return processPointerEvent(q);
}
}
return FORWARD;
}
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
if (mAttachInfo.mTooltipHost != null) {
mAttachInfo.mTooltipHost.handleTooltipKey(event);
}
if (checkForLeavingTouchModeAndConsume(event)) {
return FINISH_HANDLED;
}
mFallbackEventHandler.preDispatchKeyEvent(event);
return FORWARD;
}
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
if (mTranslator != null) {
mTranslator.translateEventInScreenToAppWindow(event);
}
final int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
ensureTouchMode(event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN));
}
if (action == MotionEvent.ACTION_DOWN) {
AutofillManager afm = getAutofillManager();
if (afm != null) {
afm.requestHideFillUi();
}
}
if (action == MotionEvent.ACTION_DOWN && mAttachInfo.mTooltipHost != null) {
mAttachInfo.mTooltipHost.hideTooltip();
}
if (mCurScrollY != 0) {
event.offsetLocation(0, mCurScrollY);
}
if (event.isTouchEvent()) {
mLastTouchPoint.x = event.getRawX();
mLastTouchPoint.y = event.getRawY();
mLastTouchSource = event.getSource();
}
return FORWARD;
}
}
final class NativePostImeInputStage extends AsyncInputStage
implements InputQueue.FinishedInputEventCallback {
public NativePostImeInputStage(InputStage next, String traceCounter) {
super(next, traceCounter);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (mInputQueue != null) {
mInputQueue.sendInputEvent(q.mEvent, q, false, this);
return DEFER;
}
return FORWARD;
}
@Override
public void onFinishedInputEvent(Object token, boolean handled) {
QueuedInputEvent q = (QueuedInputEvent)token;
if (handled) {
finish(q, true);
return;
}
forward(q);
}
}
final class ViewPostImeInputStage extends InputStage {
public ViewPostImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
return processPointerEvent(q);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
return processTrackballEvent(q);
} else {
return processGenericMotionEvent(q);
}
}
}
@Override
protected void onDeliverToNext(QueuedInputEvent q) {
if (mUnbufferedInputDispatch
&& q.mEvent instanceof MotionEvent
&& ((MotionEvent)q.mEvent).isTouchEvent()
&& isTerminalInputEvent(q.mEvent)) {
mUnbufferedInputDispatch = false;
scheduleConsumeBatchedInput();
}
super.onDeliverToNext(q);
}
private boolean performFocusNavigation(KeyEvent event) {
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.findFocus();
if (focused != null) {
View v = focused.focusSearch(direction);
if (v != null && v != focused) {
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));
return true;
}
}
if (mView.dispatchUnhandledMove(focused, direction)) {
return true;
}
} else {
if (mView.restoreDefaultFocus()) {
return true;
}
}
}
return false;
}
private boolean performKeyboardGroupNavigation(int direction) {
final View focused = mView.findFocus();
if (focused == null && mView.restoreDefaultFocus()) {
return true;
}
View cluster = focused == null ? keyboardNavigationClusterSearch(null, direction)
: focused.keyboardNavigationClusterSearch(null, direction);
int realDirection = direction;
if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
realDirection = View.FOCUS_DOWN;
}
if (cluster != null && cluster.isRootNamespace()) {
if (cluster.restoreFocusNotInCluster()) {
playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
return true;
}
cluster = keyboardNavigationClusterSearch(null, direction);
}
if (cluster != null && cluster.restoreFocusInCluster(realDirection)) {
playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
return true;
}
return false;
}
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
if (mUnhandledKeyManager.preViewDispatch(event)) {
return FINISH_HANDLED;
}
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
}
if (shouldDropInputEvent(q)) {
return FINISH_NOT_HANDLED;
}
if (mUnhandledKeyManager.dispatch(mView, event)) {
return FINISH_HANDLED;
}
int groupNavigationDirection = 0;
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getKeyCode() == KeyEvent.KEYCODE_TAB) {
if (KeyEvent.metaStateHasModifiers(event.getMetaState(), KeyEvent.META_META_ON)) {
groupNavigationDirection = View.FOCUS_FORWARD;
} else if (KeyEvent.metaStateHasModifiers(event.getMetaState(),
KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON)) {
groupNavigationDirection = View.FOCUS_BACKWARD;
}
}
if (event.getAction() == KeyEvent.ACTION_DOWN
&& !KeyEvent.metaStateHasNoModifiers(event.getMetaState())
&& event.getRepeatCount() == 0
&& !KeyEvent.isModifierKey(event.getKeyCode())
&& groupNavigationDirection == 0) {
if (mView.dispatchKeyShortcutEvent(event)) {
return FINISH_HANDLED;
}
if (shouldDropInputEvent(q)) {
return FINISH_NOT_HANDLED;
}
}
if (mFallbackEventHandler.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
}
if (shouldDropInputEvent(q)) {
return FINISH_NOT_HANDLED;
}
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (groupNavigationDirection != 0) {
if (performKeyboardGroupNavigation(groupNavigationDirection)) {
return FINISH_HANDLED;
}
} else {
if (performFocusNavigation(event)) {
return FINISH_HANDLED;
}
}
}
return FORWARD;
}
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
boolean handled = mView.dispatchPointerEvent(event);
maybeUpdatePointerIcon(event);
maybeUpdateTooltip(event);
mAttachInfo.mHandlingPointerEvent = false;
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}
private void maybeUpdatePointerIcon(MotionEvent event) {
if (event.getPointerCount() == 1 && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER
|| event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED;
}
if (event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) {
if (!updatePointerIcon(event) &&
event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) {
mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED;
}
}
}
}
private int processTrackballEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
if (event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) {
if (!hasPointerCapture() || mView.dispatchCapturedPointerEvent(event)) {
return FINISH_HANDLED;
}
}
if (mView.dispatchTrackballEvent(event)) {
return FINISH_HANDLED;
}
return FORWARD;
}
private int processGenericMotionEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
if (mView.dispatchGenericMotionEvent(event)) {
return FINISH_HANDLED;
}
return FORWARD;
}
}
private void resetPointerIcon(MotionEvent event) {
mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED;
updatePointerIcon(event);
}
private boolean updatePointerIcon(MotionEvent event) {
final int pointerIndex = 0;
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
if (mView == null) {
Slog.d(mTag, "updatePointerIcon called after view was removed");
return false;
}
if (x < 0 || x >= mView.getWidth() || y < 0 || y >= mView.getHeight()) {
Slog.d(mTag, "updatePointerIcon called with position out of bounds");
return false;
}
final PointerIcon pointerIcon = mView.onResolvePointerIcon(event, pointerIndex);
final int pointerType = (pointerIcon != null) ?
pointerIcon.getType() : PointerIcon.TYPE_DEFAULT;
if (mPointerIconType != pointerType) {
mPointerIconType = pointerType;
mCustomPointerIcon = null;
if (mPointerIconType != PointerIcon.TYPE_CUSTOM) {
InputManager.getInstance().setPointerIconType(pointerType);
return true;
}
}
if (mPointerIconType == PointerIcon.TYPE_CUSTOM &&
!pointerIcon.equals(mCustomPointerIcon)) {
mCustomPointerIcon = pointerIcon;
InputManager.getInstance().setCustomPointerIcon(mCustomPointerIcon);
}
return true;
}
private void maybeUpdateTooltip(MotionEvent event) {
if (event.getPointerCount() != 1) {
return;
}
final int action = event.getActionMasked();
if (action != MotionEvent.ACTION_HOVER_ENTER
&& action != MotionEvent.ACTION_HOVER_MOVE
&& action != MotionEvent.ACTION_HOVER_EXIT) {
return;
}
AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
if (manager.isEnabled() && manager.isTouchExplorationEnabled()) {
return;
}
if (mView == null) {
Slog.d(mTag, "maybeUpdateTooltip called after view was removed");
return;
}
mView.dispatchTooltipHoverEvent(event);
}
final class SyntheticInputStage extends InputStage {
private final SyntheticTrackballHandler mTrackball = new SyntheticTrackballHandler();
private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler();
private final SyntheticTouchNavigationHandler mTouchNavigation =
new SyntheticTouchNavigationHandler();
private final SyntheticKeyboardHandler mKeyboard = new SyntheticKeyboardHandler();
public SyntheticInputStage() {
super(null);
}
@Override
protected int onProcess(QueuedInputEvent q) {
q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED;
if (q.mEvent instanceof MotionEvent) {
final MotionEvent event = (MotionEvent)q.mEvent;
final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
mTrackball.process(event);
return FINISH_HANDLED;
} else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
mJoystick.process(event);
return FINISH_HANDLED;
} else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
== InputDevice.SOURCE_TOUCH_NAVIGATION) {
mTouchNavigation.process(event);
return FINISH_HANDLED;
}
} else if ((q.mFlags & QueuedInputEvent.FLAG_UNHANDLED) != 0) {
mKeyboard.process((KeyEvent)q.mEvent);
return FINISH_HANDLED;
}
return FORWARD;
}
@Override
protected void onDeliverToNext(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) {
if (q.mEvent instanceof MotionEvent) {
final MotionEvent event = (MotionEvent)q.mEvent;
final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
mTrackball.cancel();
} else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
mJoystick.cancel();
} else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
== InputDevice.SOURCE_TOUCH_NAVIGATION) {
mTouchNavigation.cancel(event);
}
}
}
super.onDeliverToNext(q);
}
@Override
protected void onWindowFocusChanged(boolean hasWindowFocus) {
if (!hasWindowFocus) {
mJoystick.cancel();
}
}
@Override
protected void onDetachedFromWindow() {
mJoystick.cancel();
}
}
final class SyntheticTrackballHandler {
private final TrackballAxis mX = new TrackballAxis();
private final TrackballAxis mY = new TrackballAxis();
private long mLastTime;
public void process(MotionEvent event) {
long curTime = SystemClock.uptimeMillis();
if ((mLastTime + MAX_TRACKBALL_DELAY) < curTime) {
mX.reset(0);
mY.reset(0);
mLastTime = curTime;
}
final int action = event.getAction();
final int metaState = event.getMetaState();
switch (action) {
case MotionEvent.ACTION_DOWN:
mX.reset(2);
mY.reset(2);
enqueueInputEvent(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
InputDevice.SOURCE_KEYBOARD));
break;
case MotionEvent.ACTION_UP:
mX.reset(2);
mY.reset(2);
enqueueInputEvent(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
InputDevice.SOURCE_KEYBOARD));
break;
}
if (DEBUG_TRACKBALL) Log.v(mTag, "TB X=" + mX.position + " step="
+ mX.step + " dir=" + mX.dir + " acc=" + mX.acceleration
+ " move=" + event.getX()
+ " / Y=" + mY.position + " step="
+ mY.step + " dir=" + mY.dir + " acc=" + mY.acceleration
+ " move=" + event.getY());
final float xOff = mX.collect(event.getX(), event.getEventTime(), "X");
final float yOff = mY.collect(event.getY(), event.getEventTime(), "Y");
int keycode = 0;
int movement = 0;
float accel = 1;
if (xOff > yOff) {
movement = mX.generate();
if (movement != 0) {
keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
: KeyEvent.KEYCODE_DPAD_LEFT;
accel = mX.acceleration;
mY.reset(2);
}
} else if (yOff > 0) {
movement = mY.generate();
if (movement != 0) {
keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
: KeyEvent.KEYCODE_DPAD_UP;
accel = mY.acceleration;
mX.reset(2);
}
}
if (keycode != 0) {
if (movement < 0) movement = -movement;
int accelMovement = (int)(movement * accel);
if (DEBUG_TRACKBALL) Log.v(mTag, "Move: movement=" + movement
+ " accelMovement=" + accelMovement
+ " accel=" + accel);
if (accelMovement > movement) {
if (DEBUG_TRACKBALL) Log.v(mTag, "Delivering fake DPAD: "
+ keycode);
movement--;
int repeatCount = accelMovement - movement;
enqueueInputEvent(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
InputDevice.SOURCE_KEYBOARD));
}
while (movement > 0) {
if (DEBUG_TRACKBALL) Log.v(mTag, "Delivering fake DPAD: "
+ keycode);
movement--;
curTime = SystemClock.uptimeMillis();
enqueueInputEvent(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_DOWN, keycode, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
InputDevice.SOURCE_KEYBOARD));
enqueueInputEvent(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_UP, keycode, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
InputDevice.SOURCE_KEYBOARD));
}
mLastTime = curTime;
}
}
public void cancel() {
mLastTime = Integer.MIN_VALUE;
if (mView != null && mAdded) {
ensureTouchMode(false);
}
}
}
static final class TrackballAxis {
static final float MAX_ACCELERATION = 20;
static final long FAST_MOVE_TIME = 150;
static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
static final float FIRST_MOVEMENT_THRESHOLD = 0.5f;
static final float SECOND_CUMULATIVE_MOVEMENT_THRESHOLD = 2.0f;
static final float SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD = 1.0f;
float position;
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;
}
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;
}
if (normTime > 0) {
long delta = time - lastMoveTime;
lastMoveTime = time;
float acc = acceleration;
if (delta < normTime) {
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 {
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 Math.abs(position);
}
int generate() {
int movement = 0;
nonAccelMovement = 0;
do {
final int dir = position >= 0 ? 1 : -1;
switch (step) {
case 0:
if (Math.abs(position) < FIRST_MOVEMENT_THRESHOLD) {
return movement;
}
movement += dir;
nonAccelMovement += dir;
step = 1;
break;
case 1:
if (Math.abs(position) < SECOND_CUMULATIVE_MOVEMENT_THRESHOLD) {
return movement;
}
movement += dir;
nonAccelMovement += dir;
position -= SECOND_CUMULATIVE_MOVEMENT_THRESHOLD * dir;
step = 2;
break;
default:
if (Math.abs(position) < SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD) {
return movement;
}
movement += dir;
position -= dir * SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD;
float acc = acceleration;
acc *= 1.1f;
acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
break;
}
} while (true);
}
}
final class SyntheticJoystickHandler extends Handler {
private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 1;
private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 2;
private final JoystickAxesState mJoystickAxesState = new JoystickAxesState();
private final SparseArray<KeyEvent> mDeviceKeyEvents = new SparseArray<>();
public SyntheticJoystickHandler() {
super(true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: {
if (mAttachInfo.mHasWindowFocus) {
KeyEvent oldEvent = (KeyEvent) msg.obj;
KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent,
SystemClock.uptimeMillis(), oldEvent.getRepeatCount() + 1);
enqueueInputEvent(e);
Message m = obtainMessage(msg.what, e);
m.setAsynchronous(true);
sendMessageDelayed(m, ViewConfiguration.getKeyRepeatDelay());
}
} break;
}
}
public void process(MotionEvent event) {
switch(event.getActionMasked()) {
case MotionEvent.ACTION_CANCEL:
cancel();
break;
case MotionEvent.ACTION_MOVE:
update(event);
break;
default:
Log.w(mTag, "Unexpected action: " + event.getActionMasked());
}
}
private void cancel() {
removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
for (int i = 0; i < mDeviceKeyEvents.size(); i++) {
final KeyEvent keyEvent = mDeviceKeyEvents.valueAt(i);
if (keyEvent != null) {
enqueueInputEvent(KeyEvent.changeTimeRepeat(keyEvent,
SystemClock.uptimeMillis(), 0));
}
}
mDeviceKeyEvents.clear();
mJoystickAxesState.resetState();
}
private void update(MotionEvent event) {
final int historySize = event.getHistorySize();
for (int h = 0; h < historySize; h++) {
final long time = event.getHistoricalEventTime(h);
mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_X,
event.getHistoricalAxisValue(MotionEvent.AXIS_X, 0, h));
mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_Y,
event.getHistoricalAxisValue(MotionEvent.AXIS_Y, 0, h));
mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_X,
event.getHistoricalAxisValue(MotionEvent.AXIS_HAT_X, 0, h));
mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_Y,
event.getHistoricalAxisValue(MotionEvent.AXIS_HAT_Y, 0, h));
}
final long time = event.getEventTime();
mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_X,
event.getAxisValue(MotionEvent.AXIS_X));
mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_Y,
event.getAxisValue(MotionEvent.AXIS_Y));
mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_X,
event.getAxisValue(MotionEvent.AXIS_HAT_X));
mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_Y,
event.getAxisValue(MotionEvent.AXIS_HAT_Y));
}
final class JoystickAxesState {
private static final int STATE_UP_OR_LEFT = -1;
private static final int STATE_NEUTRAL = 0;
private static final int STATE_DOWN_OR_RIGHT = 1;
final int[] mAxisStatesHat = {STATE_NEUTRAL, STATE_NEUTRAL};
final int[] mAxisStatesStick = {STATE_NEUTRAL, STATE_NEUTRAL};
void resetState() {
mAxisStatesHat[0] = STATE_NEUTRAL;
mAxisStatesHat[1] = STATE_NEUTRAL;
mAxisStatesStick[0] = STATE_NEUTRAL;
mAxisStatesStick[1] = STATE_NEUTRAL;
}
void updateStateForAxis(MotionEvent event, long time, int axis, float value) {
final int axisStateIndex;
final int repeatMessage;
if (isXAxis(axis)) {
axisStateIndex = 0;
repeatMessage = MSG_ENQUEUE_X_AXIS_KEY_REPEAT;
} else if (isYAxis(axis)) {
axisStateIndex = 1;
repeatMessage = MSG_ENQUEUE_Y_AXIS_KEY_REPEAT;
} else {
Log.e(mTag, "Unexpected axis " + axis + " in updateStateForAxis!");
return;
}
final int newState = joystickAxisValueToState(value);
final int currentState;
if (axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Y) {
currentState = mAxisStatesStick[axisStateIndex];
} else {
currentState = mAxisStatesHat[axisStateIndex];
}
if (currentState == newState) {
return;
}
final int metaState = event.getMetaState();
final int deviceId = event.getDeviceId();
final int source = event.getSource();
if (currentState == STATE_DOWN_OR_RIGHT || currentState == STATE_UP_OR_LEFT) {
final int keyCode = joystickAxisAndStateToKeycode(axis, currentState);
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
enqueueInputEvent(new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode,
0, metaState, deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
mDeviceKeyEvents.put(deviceId, null);
}
removeMessages(repeatMessage);
}
if (newState == STATE_DOWN_OR_RIGHT || newState == STATE_UP_OR_LEFT) {
final int keyCode = joystickAxisAndStateToKeycode(axis, newState);
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
KeyEvent keyEvent = new KeyEvent(time, time, KeyEvent.ACTION_DOWN, keyCode,
0, metaState, deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
enqueueInputEvent(keyEvent);
Message m = obtainMessage(repeatMessage, keyEvent);
m.setAsynchronous(true);
sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
mDeviceKeyEvents.put(deviceId,
new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode,
0, metaState, deviceId, 0,
KeyEvent.FLAG_FALLBACK | KeyEvent.FLAG_CANCELED,
source));
}
}
if (axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Y) {
mAxisStatesStick[axisStateIndex] = newState;
} else {
mAxisStatesHat[axisStateIndex] = newState;
}
}
private boolean isXAxis(int axis) {
return axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_HAT_X;
}
private boolean isYAxis(int axis) {
return axis == MotionEvent.AXIS_Y || axis == MotionEvent.AXIS_HAT_Y;
}
private int joystickAxisAndStateToKeycode(int axis, int state) {
if (isXAxis(axis) && state == STATE_UP_OR_LEFT) {
return KeyEvent.KEYCODE_DPAD_LEFT;
}
if (isXAxis(axis) && state == STATE_DOWN_OR_RIGHT) {
return KeyEvent.KEYCODE_DPAD_RIGHT;
}
if (isYAxis(axis) && state == STATE_UP_OR_LEFT) {
return KeyEvent.KEYCODE_DPAD_UP;
}
if (isYAxis(axis) && state == STATE_DOWN_OR_RIGHT) {
return KeyEvent.KEYCODE_DPAD_DOWN;
}
Log.e(mTag, "Unknown axis " + axis + " or direction " + state);
return KeyEvent.KEYCODE_UNKNOWN;
}
private int joystickAxisValueToState(float value) {
if (value >= 0.5f) {
return STATE_DOWN_OR_RIGHT;
} else if (value <= -0.5f) {
return STATE_UP_OR_LEFT;
} else {
return STATE_NEUTRAL;
}
}
}
}
final class SyntheticTouchNavigationHandler extends Handler {
private static final String LOCAL_TAG = "SyntheticTouchNavigationHandler";
private static final boolean LOCAL_DEBUG = false;
private static final float DEFAULT_WIDTH_MILLIMETERS = 48;
private static final float DEFAULT_HEIGHT_MILLIMETERS = 48;
private static final int TICK_DISTANCE_MILLIMETERS = 12;
private static final float MIN_FLING_VELOCITY_TICKS_PER_SECOND = 6f;
private static final float MAX_FLING_VELOCITY_TICKS_PER_SECOND = 20f;
private static final float FLING_TICK_DECAY = 0.8f;
private int mCurrentDeviceId = -1;
private int mCurrentSource;
private boolean mCurrentDeviceSupported;
private float mConfigTickDistance;
private float mConfigMinFlingVelocity;
private float mConfigMaxFlingVelocity;
private VelocityTracker mVelocityTracker;
private int mActivePointerId = -1;
private float mStartX;
private float mStartY;
private float mLastX;
private float mLastY;
private float mAccumulatedX;
private float mAccumulatedY;
private boolean mConsumedMovement;
private long mPendingKeyDownTime;
private int mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN;
private int mPendingKeyRepeatCount;
private int mPendingKeyMetaState;
private boolean mFlinging;
private float mFlingVelocity;
public SyntheticTouchNavigationHandler() {
super(true);
}
public void process(MotionEvent event) {
final long time = event.getEventTime();
final int deviceId = event.getDeviceId();
final int source = event.getSource();
if (mCurrentDeviceId != deviceId || mCurrentSource != source) {
finishKeys(time);
finishTracking(time);
mCurrentDeviceId = deviceId;
mCurrentSource = source;
mCurrentDeviceSupported = false;
InputDevice device = event.getDevice();
if (device != null) {
InputDevice.MotionRange xRange = device.getMotionRange(MotionEvent.AXIS_X);
InputDevice.MotionRange yRange = device.getMotionRange(MotionEvent.AXIS_Y);
if (xRange != null && yRange != null) {
mCurrentDeviceSupported = true;
float xRes = xRange.getResolution();
if (xRes <= 0) {
xRes = xRange.getRange() / DEFAULT_WIDTH_MILLIMETERS;
}
float yRes = yRange.getResolution();
if (yRes <= 0) {
yRes = yRange.getRange() / DEFAULT_HEIGHT_MILLIMETERS;
}
float nominalRes = (xRes + yRes) * 0.5f;
mConfigTickDistance = TICK_DISTANCE_MILLIMETERS * nominalRes;
mConfigMinFlingVelocity =
MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
mConfigMaxFlingVelocity =
MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
if (LOCAL_DEBUG) {
Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId
+ " (" + Integer.toHexString(mCurrentSource) + "): "
+ ", mConfigTickDistance=" + mConfigTickDistance
+ ", mConfigMinFlingVelocity=" + mConfigMinFlingVelocity
+ ", mConfigMaxFlingVelocity=" + mConfigMaxFlingVelocity);
}
}
}
}
if (!mCurrentDeviceSupported) {
return;
}
final int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN: {
boolean caughtFling = mFlinging;
finishKeys(time);
finishTracking(time);
mActivePointerId = event.getPointerId(0);
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(event);
mStartX = event.getX();
mStartY = event.getY();
mLastX = mStartX;
mLastY = mStartY;
mAccumulatedX = 0;
mAccumulatedY = 0;
mConsumedMovement = caughtFling;
break;
}
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP: {
if (mActivePointerId < 0) {
break;
}
final int index = event.findPointerIndex(mActivePointerId);
if (index < 0) {
finishKeys(time);
finishTracking(time);
break;
}
mVelocityTracker.addMovement(event);
final float x = event.getX(index);
final float y = event.getY(index);
mAccumulatedX += x - mLastX;
mAccumulatedY += y - mLastY;
mLastX = x;
mLastY = y;
final int metaState = event.getMetaState();
consumeAccumulatedMovement(time, metaState);
if (action == MotionEvent.ACTION_UP) {
if (mConsumedMovement && mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
mVelocityTracker.computeCurrentVelocity(1000, mConfigMaxFlingVelocity);
final float vx = mVelocityTracker.getXVelocity(mActivePointerId);
final float vy = mVelocityTracker.getYVelocity(mActivePointerId);
if (!startFling(time, vx, vy)) {
finishKeys(time);
}
}
finishTracking(time);
}
break;
}
case MotionEvent.ACTION_CANCEL: {
finishKeys(time);
finishTracking(time);
break;
}
}
}
public void cancel(MotionEvent event) {
if (mCurrentDeviceId == event.getDeviceId()
&& mCurrentSource == event.getSource()) {
final long time = event.getEventTime();
finishKeys(time);
finishTracking(time);
}
}
private void finishKeys(long time) {
cancelFling();
sendKeyUp(time);
}
private void finishTracking(long time) {
if (mActivePointerId >= 0) {
mActivePointerId = -1;
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
private void consumeAccumulatedMovement(long time, int metaState) {
final float absX = Math.abs(mAccumulatedX);
final float absY = Math.abs(mAccumulatedY);
if (absX >= absY) {
if (absX >= mConfigTickDistance) {
mAccumulatedX = consumeAccumulatedMovement(time, metaState, mAccumulatedX,
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT);
mAccumulatedY = 0;
mConsumedMovement = true;
}
} else {
if (absY >= mConfigTickDistance) {
mAccumulatedY = consumeAccumulatedMovement(time, metaState, mAccumulatedY,
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN);
mAccumulatedX = 0;
mConsumedMovement = true;
}
}
}
private float consumeAccumulatedMovement(long time, int metaState,
float accumulator, int negativeKeyCode, int positiveKeyCode) {
while (accumulator <= -mConfigTickDistance) {
sendKeyDownOrRepeat(time, negativeKeyCode, metaState);
accumulator += mConfigTickDistance;
}
while (accumulator >= mConfigTickDistance) {
sendKeyDownOrRepeat(time, positiveKeyCode, metaState);
accumulator -= mConfigTickDistance;
}
return accumulator;
}
private void sendKeyDownOrRepeat(long time, int keyCode, int metaState) {
if (mPendingKeyCode != keyCode) {
sendKeyUp(time);
mPendingKeyDownTime = time;
mPendingKeyCode = keyCode;
mPendingKeyRepeatCount = 0;
} else {
mPendingKeyRepeatCount += 1;
}
mPendingKeyMetaState = metaState;
if (LOCAL_DEBUG) {
Log.d(LOCAL_TAG, "Sending key down: keyCode=" + mPendingKeyCode
+ ", repeatCount=" + mPendingKeyRepeatCount
+ ", metaState=" + Integer.toHexString(mPendingKeyMetaState));
}
enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time,
KeyEvent.ACTION_DOWN, mPendingKeyCode, mPendingKeyRepeatCount,
mPendingKeyMetaState, mCurrentDeviceId,
KeyEvent.FLAG_FALLBACK, mCurrentSource));
}
private void sendKeyUp(long time) {
if (mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
if (LOCAL_DEBUG) {
Log.d(LOCAL_TAG, "Sending key up: keyCode=" + mPendingKeyCode
+ ", metaState=" + Integer.toHexString(mPendingKeyMetaState));
}
enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time,
KeyEvent.ACTION_UP, mPendingKeyCode, 0, mPendingKeyMetaState,
mCurrentDeviceId, 0, KeyEvent.FLAG_FALLBACK,
mCurrentSource));
mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN;
}
}
private boolean startFling(long time, float vx, float vy) {
if (LOCAL_DEBUG) {
Log.d(LOCAL_TAG, "Considering fling: vx=" + vx + ", vy=" + vy
+ ", min=" + mConfigMinFlingVelocity);
}
switch (mPendingKeyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (-vx >= mConfigMinFlingVelocity
&& Math.abs(vy) < mConfigMinFlingVelocity) {
mFlingVelocity = -vx;
break;
}
return false;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (vx >= mConfigMinFlingVelocity
&& Math.abs(vy) < mConfigMinFlingVelocity) {
mFlingVelocity = vx;
break;
}
return false;
case KeyEvent.KEYCODE_DPAD_UP:
if (-vy >= mConfigMinFlingVelocity
&& Math.abs(vx) < mConfigMinFlingVelocity) {
mFlingVelocity = -vy;
break;
}
return false;
case KeyEvent.KEYCODE_DPAD_DOWN:
if (vy >= mConfigMinFlingVelocity
&& Math.abs(vx) < mConfigMinFlingVelocity) {
mFlingVelocity = vy;
break;
}
return false;
}
mFlinging = postFling(time);
return mFlinging;
}
private boolean postFling(long time) {
if (mFlingVelocity >= mConfigMinFlingVelocity) {
long delay = (long)(mConfigTickDistance / mFlingVelocity * 1000);
postAtTime(mFlingRunnable, time + delay);
if (LOCAL_DEBUG) {
Log.d(LOCAL_TAG, "Posted fling: velocity="
+ mFlingVelocity + ", delay=" + delay
+ ", keyCode=" + mPendingKeyCode);
}
return true;
}
return false;
}
private void cancelFling() {
if (mFlinging) {
removeCallbacks(mFlingRunnable);
mFlinging = false;
}
}
private final Runnable mFlingRunnable = new Runnable() {
@Override
public void run() {
final long time = SystemClock.uptimeMillis();
sendKeyDownOrRepeat(time, mPendingKeyCode, mPendingKeyMetaState);
mFlingVelocity *= FLING_TICK_DECAY;
if (!postFling(time)) {
mFlinging = false;
finishKeys(time);
}
}
};
}
final class SyntheticKeyboardHandler {
public void process(KeyEvent event) {
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
return;
}
final KeyCharacterMap kcm = event.getKeyCharacterMap();
final int keyCode = event.getKeyCode();
final int metaState = event.getMetaState();
KeyCharacterMap.FallbackAction fallbackAction =
kcm.getFallbackAction(keyCode, metaState);
if (fallbackAction != null) {
final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
KeyEvent fallbackEvent = KeyEvent.obtain(
event.getDownTime(), event.getEventTime(),
event.getAction(), fallbackAction.keyCode,
event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
flags, event.getSource(), null);
fallbackAction.recycle();
enqueueInputEvent(fallbackEvent);
}
}
}
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;
}
private static boolean isTypingKey(KeyEvent keyEvent) {
return keyEvent.getUnicodeChar() > 0;
}
private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) {
if (!mAttachInfo.mInTouchMode) {
return false;
}
final int action = event.getAction();
if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) {
return false;
}
if ((event.getFlags() & KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) {
return false;
}
if (isNavigationKey(event)) {
return ensureTouchMode(false);
}
if (isTypingKey(event)) {
ensureTouchMode(false);
return false;
}
return false;
}
void setLocalDragState(Object obj) {
mLocalDragState = obj;
}
private void handleDragEvent(DragEvent event) {
if (mView != null && mAdded) {
final int what = event.mAction;
if (what == DragEvent.ACTION_DRAG_STARTED) {
mCurrentDragView = null;
mDragDescription = event.mClipDescription;
} else {
if (what == DragEvent.ACTION_DRAG_ENDED) {
mDragDescription = null;
}
event.mClipDescription = mDragDescription;
}
if (what == DragEvent.ACTION_DRAG_EXITED) {
if (View.sCascadedDragDrop) {
mView.dispatchDragEnterExitInPreN(event);
}
setDragFocus(null, event);
} else {
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;
}
final View prevDragView = mCurrentDragView;
if (what == DragEvent.ACTION_DROP && event.mClipData != null) {
event.mClipData.prepareToEnterProcess();
}
boolean result = mView.dispatchDragEvent(event);
if (what == DragEvent.ACTION_DRAG_LOCATION && !event.mEventHandlerWasCalled) {
setDragFocus(null, event);
}
if (prevDragView != mCurrentDragView) {
try {
if (prevDragView != null) {
mWindowSession.dragRecipientExited(mWindow);
}
if (mCurrentDragView != null) {
mWindowSession.dragRecipientEntered(mWindow);
}
} catch (RemoteException e) {
Slog.e(mTag, "Unable to note drag target change");
}
}
if (what == DragEvent.ACTION_DROP) {
try {
Log.i(mTag, "Reporting drop result: " + result);
mWindowSession.reportDropResult(mWindow, result);
} catch (RemoteException e) {
Log.e(mTag, "Unable to report drop result");
}
}
if (what == DragEvent.ACTION_DRAG_ENDED) {
mCurrentDragView = null;
setLocalDragState(null);
mAttachInfo.mDragToken = null;
if (mAttachInfo.mDragSurface != null) {
mAttachInfo.mDragSurface.release();
mAttachInfo.mDragSurface = null;
}
}
}
}
event.recycle();
}
public void handleDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args) {
if (mSeq != args.seq) {
mSeq = args.seq;
mAttachInfo.mForceReportNewAttributes = true;
scheduleTraversals();
}
if (mView == null) return;
if (args.localChanges != 0) {
mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges);
}
int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS;
if (visibility != mAttachInfo.mGlobalSystemUiVisibility) {
mAttachInfo.mGlobalSystemUiVisibility = visibility;
mView.dispatchSystemUiVisibilityChanged(visibility);
}
}
public void onWindowTitleChanged() {
mAttachInfo.mForceReportNewAttributes = true;
}
public void handleDispatchWindowShown() {
mAttachInfo.mTreeObserver.dispatchOnWindowShown();
}
public void handleRequestKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
Bundle data = new Bundle();
ArrayList<KeyboardShortcutGroup> list = new ArrayList<>();
if (mView != null) {
mView.requestKeyboardShortcuts(list, deviceId);
}
data.putParcelableArrayList(WindowManager.PARCEL_KEY_SHORTCUTS_ARRAY, list);
try {
receiver.send(0, data);
} catch (RemoteException e) {
}
}
public void getLastTouchPoint(Point outLocation) {
outLocation.x = (int) mLastTouchPoint.x;
outLocation.y = (int) mLastTouchPoint.y;
}
public int getLastTouchSource() {
return mLastTouchSource;
}
public void setDragFocus(View newDragTarget, DragEvent event) {
if (mCurrentDragView != newDragTarget && !View.sCascadedDragDrop) {
final float tx = event.mX;
final float ty = event.mY;
final int action = event.mAction;
final ClipData td = event.mClipData;
event.mX = 0;
event.mY = 0;
event.mClipData = null;
if (mCurrentDragView != null) {
event.mAction = DragEvent.ACTION_DRAG_EXITED;
mCurrentDragView.callDragEventHandler(event);
}
if (newDragTarget != null) {
event.mAction = DragEvent.ACTION_DRAG_ENTERED;
newDragTarget.callDragEventHandler(event);
}
event.mAction = action;
event.mX = tx;
event.mY = ty;
event.mClipData = td;
}
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;
}
private @Nullable AutofillManager getAutofillManager() {
if (mView instanceof ViewGroup) {
ViewGroup decorView = (ViewGroup) mView;
if (decorView.getChildCount() > 0) {
return decorView.getChildAt(0).getContext()
.getSystemService(AutofillManager.class);
}
}
return null;
}
private boolean isAutofillUiShowing() {
AutofillManager afm = getAutofillManager();
if (afm == null) {
return false;
}
return afm.isAutofillUiShowing();
}
public AccessibilityInteractionController getAccessibilityInteractionController() {
if (mView == null) {
throw new IllegalStateException("getAccessibilityInteractionController"
+ " called when there is no mView");
}
if (mAccessibilityInteractionController == null) {
mAccessibilityInteractionController = new AccessibilityInteractionController(this);
}
return mAccessibilityInteractionController;
}
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(mTag, "WindowLayout in layoutWindow:" + params);
if (mOrigWindowType != params.type) {
if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Slog.w(mTag, "Window type can not be changed after "
+ "the window is added; ignoring change of " + mView);
params.type = mOrigWindowType;
}
}
}
long frameNumber = -1;
if (mSurface.isValid()) {
frameNumber = mSurface.getNextFrameNumber();
}
int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
mPendingMergedConfiguration, mSurface);
mPendingAlwaysConsumeNavBar =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
if (restore) {
params.restore();
}
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
}
return relayoutResult;
}
@Override
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) {
Log.e(mTag, "FATAL EXCEPTION when attempting to play sound effect: " + e);
e.printStackTrace();
}
}
@Override
public boolean performHapticFeedback(int effectId, boolean always) {
try {
return mWindowSession.performHapticFeedback(mWindow, effectId, always);
} catch (RemoteException e) {
return false;
}
}
@Override
public View focusSearch(View focused, int direction) {
checkThread();
if (!(mView instanceof ViewGroup)) {
return null;
}
return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
}
@Override
public View keyboardNavigationClusterSearch(View currentCluster,
@FocusDirection int direction) {
checkThread();
return FocusFinder.getInstance().findNextKeyboardNavigationCluster(
mView, currentCluster, direction);
}
public void debug() {
mView.debug();
}
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
String innerPrefix = prefix + " ";
writer.print(prefix); writer.println("ViewRoot:");
writer.print(innerPrefix); writer.print("mAdded="); writer.print(mAdded);
writer.print(" mRemoved="); writer.println(mRemoved);
writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled=");
writer.println(mConsumeBatchedInputScheduled);
writer.print(innerPrefix); writer.print("mConsumeBatchedInputImmediatelyScheduled=");
writer.println(mConsumeBatchedInputImmediatelyScheduled);
writer.print(innerPrefix); writer.print("mPendingInputEventCount=");
writer.println(mPendingInputEventCount);
writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled=");
writer.println(mProcessInputEventsScheduled);
writer.print(innerPrefix); writer.print("mTraversalScheduled=");
writer.print(mTraversalScheduled);
writer.print(innerPrefix); writer.print("mIsAmbientMode=");
writer.print(mIsAmbientMode);
if (mTraversalScheduled) {
writer.print(" (barrier="); writer.print(mTraversalBarrier); writer.println(")");
} else {
writer.println();
}
mFirstInputStage.dump(innerPrefix, writer);
mChoreographer.dump(prefix, writer);
writer.print(prefix); writer.println("View Hierarchy:");
dumpViewHierarchy(innerPrefix, writer, mView);
}
private void dumpViewHierarchy(String prefix, PrintWriter writer, View view) {
writer.print(prefix);
if (view == null) {
writer.println("null");
return;
}
writer.println(view.toString());
if (!(view instanceof ViewGroup)) {
return;
}
ViewGroup grp = (ViewGroup)view;
final int N = grp.getChildCount();
if (N <= 0) {
return;
}
prefix = prefix + " ";
for (int i=0; i<N; i++) {
dumpViewHierarchy(prefix, writer, grp.getChildAt(i));
}
}
public void dumpGfxInfo(int[] info) {
info[0] = info[1] = 0;
if (mView != null) {
getGfxInfo(mView, info);
}
}
private static void getGfxInfo(View view, int[] info) {
RenderNode renderNode = view.mRenderNode;
info[0]++;
if (renderNode != null) {
info[1] += renderNode.getDebugSize();
}
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
int count = group.getChildCount();
for (int i = 0; i < count; i++) {
getGfxInfo(group.getChildAt(i), info);
}
}
}
boolean die(boolean immediate) {
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
} else {
Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
void doDie() {
checkThread();
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
if (mAdded) {
dispatchDetachedFromWindow();
}
if (mAdded && !mFirst) {
destroyHardwareRenderer();
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
if (mWindowAttributesChanged || viewVisibilityChanged) {
try {
if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mWindowSession.finishDrawing(mWindow);
}
} catch (RemoteException e) {
}
}
mSurface.release();
}
}
mAdded = false;
}
WindowManagerGlobal.getInstance().doRemoveView(this);
}
public void requestUpdateConfiguration(Configuration config) {
Message msg = mHandler.obtainMessage(MSG_UPDATE_CONFIGURATION, config);
mHandler.sendMessage(msg);
}
public void loadSystemProperties() {
mHandler.post(new Runnable() {
@Override
public void run() {
mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false);
profileRendering(mAttachInfo.mHasWindowFocus);
if (mAttachInfo.mThreadedRenderer != null) {
if (mAttachInfo.mThreadedRenderer.loadSystemProperties()) {
invalidate();
}
}
boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false);
if (layout != mAttachInfo.mDebugLayout) {
mAttachInfo.mDebugLayout = layout;
if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) {
mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200);
}
}
}
});
}
private void destroyHardwareRenderer() {
ThreadedRenderer hardwareRenderer = mAttachInfo.mThreadedRenderer;
if (hardwareRenderer != null) {
if (mView != null) {
hardwareRenderer.destroyHardwareResources(mView);
}
hardwareRenderer.destroy();
hardwareRenderer.setRequested(false);
mAttachInfo.mThreadedRenderer = null;
mAttachInfo.mHardwareAccelerated = false;
}
}
private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
boolean alwaysConsumeNavBar, int displayId,
DisplayCutout.ParcelableWrapper displayCutout) {
if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
+ " contentInsets=" + contentInsets.toShortString()
+ " visibleInsets=" + visibleInsets.toShortString()
+ " reportDraw=" + reportDraw
+ " backDropFrame=" + backDropFrame);
if (mDragResizing && mUseMTRenderer) {
boolean fullscreen = frame.equals(backDropFrame);
synchronized (mWindowCallbacks) {
for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame, fullscreen,
visibleInsets, stableInsets);
}
}
}
Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(frame);
mTranslator.translateRectInScreenToAppWindow(overscanInsets);
mTranslator.translateRectInScreenToAppWindow(contentInsets);
mTranslator.translateRectInScreenToAppWindow(visibleInsets);
}
SomeArgs args = SomeArgs.obtain();
final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid());
args.arg1 = sameProcessCall ? new Rect(frame) : frame;
args.arg2 = sameProcessCall ? new Rect(contentInsets) : contentInsets;
args.arg3 = sameProcessCall ? new Rect(visibleInsets) : visibleInsets;
args.arg4 = sameProcessCall && mergedConfiguration != null
? new MergedConfiguration(mergedConfiguration) : mergedConfiguration;
args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets;
args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets;
args.arg7 = sameProcessCall ? new Rect(outsets) : outsets;
args.arg8 = sameProcessCall ? new Rect(backDropFrame) : backDropFrame;
args.arg9 = displayCutout.get();
args.argi1 = forceLayout ? 1 : 0;
args.argi2 = alwaysConsumeNavBar ? 1 : 0;
args.argi3 = displayId;
msg.obj = args;
mHandler.sendMessage(msg);
}
public void dispatchMoved(int newX, int newY) {
if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
if (mTranslator != null) {
PointF point = new PointF(newX, newY);
mTranslator.translatePointInScreenToAppWindow(point);
newX = (int) (point.x + 0.5);
newY = (int) (point.y + 0.5);
}
Message msg = mHandler.obtainMessage(MSG_WINDOW_MOVED, newX, newY);
mHandler.sendMessage(msg);
}
private static final class QueuedInputEvent {
public static final int FLAG_DELIVER_POST_IME = 1 << 0;
public static final int FLAG_DEFERRED = 1 << 1;
public static final int FLAG_FINISHED = 1 << 2;
public static final int FLAG_FINISHED_HANDLED = 1 << 3;
public static final int FLAG_RESYNTHESIZED = 1 << 4;
public static final int FLAG_UNHANDLED = 1 << 5;
public QueuedInputEvent mNext;
public InputEvent mEvent;
public InputEventReceiver mReceiver;
public int mFlags;
public boolean shouldSkipIme() {
if ((mFlags & FLAG_DELIVER_POST_IME) != 0) {
return true;
}
return mEvent instanceof MotionEvent
&& (mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
|| mEvent.isFromSource(InputDevice.SOURCE_ROTARY_ENCODER));
}
public boolean shouldSendToSynthesizer() {
if ((mFlags & FLAG_UNHANDLED) != 0) {
return true;
}
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("QueuedInputEvent{flags=");
boolean hasPrevious = false;
hasPrevious = flagToString("DELIVER_POST_IME", FLAG_DELIVER_POST_IME, hasPrevious, sb);
hasPrevious = flagToString("DEFERRED", FLAG_DEFERRED, hasPrevious, sb);
hasPrevious = flagToString("FINISHED", FLAG_FINISHED, hasPrevious, sb);
hasPrevious = flagToString("FINISHED_HANDLED", FLAG_FINISHED_HANDLED, hasPrevious, sb);
hasPrevious = flagToString("RESYNTHESIZED", FLAG_RESYNTHESIZED, hasPrevious, sb);
hasPrevious = flagToString("UNHANDLED", FLAG_UNHANDLED, hasPrevious, sb);
if (!hasPrevious) {
sb.append("0");
}
sb.append(", hasNextQueuedEvent=" + (mEvent != null ? "true" : "false"));
sb.append(", hasInputEventReceiver=" + (mReceiver != null ? "true" : "false"));
sb.append(", mEvent=" + mEvent + "}");
return sb.toString();
}
private boolean flagToString(String name, int flag,
boolean hasPrevious, StringBuilder sb) {
if ((mFlags & flag) != 0) {
if (hasPrevious) {
sb.append("|");
}
sb.append(name);
return true;
}
return hasPrevious;
}
}
private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
InputEventReceiver receiver, int flags) {
QueuedInputEvent q = mQueuedInputEventPool;
if (q != null) {
mQueuedInputEventPoolSize -= 1;
mQueuedInputEventPool = q.mNext;
q.mNext = null;
} else {
q = new QueuedInputEvent();
}
q.mEvent = event;
q.mReceiver = receiver;
q.mFlags = flags;
return q;
}
private void recycleQueuedInputEvent(QueuedInputEvent q) {
q.mEvent = null;
q.mReceiver = null;
if (mQueuedInputEventPoolSize < MAX_QUEUED_INPUT_EVENT_POOL_SIZE) {
mQueuedInputEventPoolSize += 1;
q.mNext = mQueuedInputEventPool;
mQueuedInputEventPool = q;
}
}
void enqueueInputEvent(InputEvent event) {
enqueueInputEvent(event, null, 0, false);
}
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
adjustInputEventForCompatibility(event);
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
private void scheduleProcessInputEvents() {
if (!mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = true;
Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
void doProcessInputEvents() {
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
long eventTime = q.mEvent.getEventTimeNano();
long oldestEventTime = eventTime;
if (q.mEvent instanceof MotionEvent) {
MotionEvent me = (MotionEvent)q.mEvent;
if (me.getHistorySize() > 0) {
oldestEventTime = me.getHistoricalEventTimeNano(0);
}
}
mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
deliverInputEvent(q);
}
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (q.mEvent instanceof KeyEvent) {
mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
}
if (stage != null) {
handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
private void finishInputEvent(QueuedInputEvent q) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
if (q.mReceiver != null) {
boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
q.mReceiver.finishInputEvent(q.mEvent, handled);
} else {
q.mEvent.recycleIfNeededAfterDispatch();
}
recycleQueuedInputEvent(q);
}
private void adjustInputEventForCompatibility(InputEvent e) {
if (mTargetSdkVersion < Build.VERSION_CODES.M && e instanceof MotionEvent) {
MotionEvent motion = (MotionEvent) e;
final int mask =
MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY;
final int buttonState = motion.getButtonState();
final int compatButtonState = (buttonState & mask) >> 4;
if (compatButtonState != 0) {
motion.setButtonState(buttonState | compatButtonState);
}
}
}
static boolean isTerminalInputEvent(InputEvent event) {
if (event instanceof KeyEvent) {
final KeyEvent keyEvent = (KeyEvent)event;
return keyEvent.getAction() == KeyEvent.ACTION_UP;
} else {
final MotionEvent motionEvent = (MotionEvent)event;
final int action = motionEvent.getAction();
return action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_CANCEL
|| action == MotionEvent.ACTION_HOVER_EXIT;
}
}
void scheduleConsumeBatchedInput() {
if (!mConsumeBatchedInputScheduled) {
mConsumeBatchedInputScheduled = true;
mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
mConsumedBatchedInputRunnable, null);
}
}
void unscheduleConsumeBatchedInput() {
if (mConsumeBatchedInputScheduled) {
mConsumeBatchedInputScheduled = false;
mChoreographer.removeCallbacks(Choreographer.CALLBACK_INPUT,
mConsumedBatchedInputRunnable, null);
}
}
void scheduleConsumeBatchedInputImmediately() {
if (!mConsumeBatchedInputImmediatelyScheduled) {
unscheduleConsumeBatchedInput();
mConsumeBatchedInputImmediatelyScheduled = true;
mHandler.post(mConsumeBatchedInputImmediatelyRunnable);
}
}
void doConsumeBatchedInput(long frameTimeNanos) {
if (mConsumeBatchedInputScheduled) {
mConsumeBatchedInputScheduled = false;
if (mInputEventReceiver != null) {
if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)
&& frameTimeNanos != -1) {
scheduleConsumeBatchedInput();
}
}
doProcessInputEvents();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event, int displayId) {
enqueueInputEvent(event, this, 0, true);
}
@Override
public void onBatchedInputEventPending() {
if (mUnbufferedInputDispatch) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
}
}
@Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
}
}
WindowInputEventReceiver mInputEventReceiver;
final class ConsumeBatchedInputRunnable implements Runnable {
@Override
public void run() {
doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());
}
}
final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable =
new ConsumeBatchedInputRunnable();
boolean mConsumeBatchedInputScheduled;
final class ConsumeBatchedInputImmediatelyRunnable implements Runnable {
@Override
public void run() {
doConsumeBatchedInput(-1);
}
}
final ConsumeBatchedInputImmediatelyRunnable mConsumeBatchedInputImmediatelyRunnable =
new ConsumeBatchedInputImmediatelyRunnable();
boolean mConsumeBatchedInputImmediatelyScheduled;
final class InvalidateOnAnimationRunnable implements Runnable {
private boolean mPosted;
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<AttachInfo.InvalidateInfo> mViewRects =
new ArrayList<AttachInfo.InvalidateInfo>();
private View[] mTempViews;
private AttachInfo.InvalidateInfo[] mTempViewRects;
public void addView(View view) {
synchronized (this) {
mViews.add(view);
postIfNeededLocked();
}
}
public void addViewRect(AttachInfo.InvalidateInfo info) {
synchronized (this) {
mViewRects.add(info);
postIfNeededLocked();
}
}
public void removeView(View view) {
synchronized (this) {
mViews.remove(view);
for (int i = mViewRects.size(); i-- > 0; ) {
AttachInfo.InvalidateInfo info = mViewRects.get(i);
if (info.target == view) {
mViewRects.remove(i);
info.recycle();
}
}
if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) {
mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, this, null);
mPosted = false;
}
}
}
@Override
public void run() {
final int viewCount;
final int viewRectCount;
synchronized (this) {
mPosted = false;
viewCount = mViews.size();
if (viewCount != 0) {
mTempViews = mViews.toArray(mTempViews != null
? mTempViews : new View[viewCount]);
mViews.clear();
}
viewRectCount = mViewRects.size();
if (viewRectCount != 0) {
mTempViewRects = mViewRects.toArray(mTempViewRects != null
? mTempViewRects : new AttachInfo.InvalidateInfo[viewRectCount]);
mViewRects.clear();
}
}
for (int i = 0; i < viewCount; i++) {
mTempViews[i].invalidate();
mTempViews[i] = null;
}
for (int i = 0; i < viewRectCount; i++) {
final View.AttachInfo.InvalidateInfo info = mTempViewRects[i];
info.target.invalidate(info.left, info.top, info.right, info.bottom);
info.recycle();
}
}
private void postIfNeededLocked() {
if (!mPosted) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
mPosted = true;
}
}
}
final InvalidateOnAnimationRunnable mInvalidateOnAnimationRunnable =
new InvalidateOnAnimationRunnable();
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
public void dispatchInvalidateRectDelayed(AttachInfo.InvalidateInfo info,
long delayMilliseconds) {
final Message msg = mHandler.obtainMessage(MSG_INVALIDATE_RECT, info);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
public void dispatchInvalidateOnAnimation(View view) {
mInvalidateOnAnimationRunnable.addView(view);
}
public void dispatchInvalidateRectOnAnimation(AttachInfo.InvalidateInfo info) {
mInvalidateOnAnimationRunnable.addViewRect(info);
}
public void cancelInvalidate(View view) {
mHandler.removeMessages(MSG_INVALIDATE, view);
mHandler.removeMessages(MSG_INVALIDATE_RECT, view);
mInvalidateOnAnimationRunnable.removeView(view);
}
public void dispatchInputEvent(InputEvent event) {
dispatchInputEvent(event, null);
}
public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = event;
args.arg2 = receiver;
Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
public void synthesizeInputEvent(InputEvent event) {
Message msg = mHandler.obtainMessage(MSG_SYNTHESIZE_INPUT_EVENT, event);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
public void dispatchKeyFromIme(KeyEvent event) {
Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_IME, event);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
public void dispatchKeyFromAutofill(KeyEvent event) {
Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_AUTOFILL, event);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
public void dispatchUnhandledInputEvent(InputEvent event) {
if (event instanceof MotionEvent) {
event = MotionEvent.obtain((MotionEvent) event);
}
synthesizeInputEvent(event);
}
public void dispatchAppVisibility(boolean visible) {
Message msg = mHandler.obtainMessage(MSG_DISPATCH_APP_VISIBILITY);
msg.arg1 = visible ? 1 : 0;
mHandler.sendMessage(msg);
}
public void dispatchGetNewSurface() {
Message msg = mHandler.obtainMessage(MSG_DISPATCH_GET_NEW_SURFACE);
mHandler.sendMessage(msg);
}
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
synchronized (this) {
mWindowFocusChanged = true;
mUpcomingWindowFocus = hasFocus;
mUpcomingInTouchMode = inTouchMode;
}
Message msg = Message.obtain();
msg.what = MSG_WINDOW_FOCUS_CHANGED;
mHandler.sendMessage(msg);
}
public void dispatchWindowShown() {
mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_SHOWN);
}
public void dispatchCloseSystemDialogs(String reason) {
Message msg = Message.obtain();
msg.what = MSG_CLOSE_SYSTEM_DIALOGS;
msg.obj = reason;
mHandler.sendMessage(msg);
}
public void dispatchDragEvent(DragEvent event) {
final int what;
if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
what = MSG_DISPATCH_DRAG_LOCATION_EVENT;
mHandler.removeMessages(what);
} else {
what = MSG_DISPATCH_DRAG_EVENT;
}
Message msg = mHandler.obtainMessage(what, event);
mHandler.sendMessage(msg);
}
public void updatePointerIcon(float x, float y) {
final int what = MSG_UPDATE_POINTER_ICON;
mHandler.removeMessages(what);
final long now = SystemClock.uptimeMillis();
final MotionEvent event = MotionEvent.obtain(
0, now, MotionEvent.ACTION_HOVER_MOVE, x, y, 0);
Message msg = mHandler.obtainMessage(what, event);
mHandler.sendMessage(msg);
}
public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
int localValue, int localChanges) {
SystemUiVisibilityInfo args = new SystemUiVisibilityInfo();
args.seq = seq;
args.globalVisibility = globalVisibility;
args.localValue = localValue;
args.localChanges = localChanges;
mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args));
}
public void dispatchCheckFocus() {
if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) {
mHandler.sendEmptyMessage(MSG_CHECK_FOCUS);
}
}
public void dispatchRequestKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
mHandler.obtainMessage(
MSG_REQUEST_KEYBOARD_SHORTCUTS, deviceId, 0, receiver).sendToTarget();
}
public void dispatchPointerCaptureChanged(boolean on) {
final int what = MSG_POINTER_CAPTURE_CHANGED;
mHandler.removeMessages(what);
Message msg = mHandler.obtainMessage(what);
msg.arg1 = on ? 1 : 0;
mHandler.sendMessage(msg);
}
private void postSendWindowContentChangedCallback(View source, int changeType) {
if (mSendWindowContentChangedAccessibilityEvent == null) {
mSendWindowContentChangedAccessibilityEvent =
new SendWindowContentChangedAccessibilityEvent();
}
mSendWindowContentChangedAccessibilityEvent.runOrPost(source, changeType);
}
private void removeSendWindowContentChangedCallback() {
if (mSendWindowContentChangedAccessibilityEvent != null) {
mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent);
}
}
@Override
public boolean showContextMenuForChild(View originalView) {
return false;
}
@Override
public boolean showContextMenuForChild(View originalView, float x, float y) {
return false;
}
@Override
public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
return null;
}
@Override
public ActionMode startActionModeForChild(
View originalView, ActionMode.Callback callback, int type) {
return null;
}
@Override
public void createContextMenu(ContextMenu menu) {
}
@Override
public void childDrawableStateChanged(View child) {
}
@Override
public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
if (mView == null || mStopped || mPausedForTransition) {
return false;
}
if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
&& mSendWindowContentChangedAccessibilityEvent != null
&& mSendWindowContentChangedAccessibilityEvent.mSource != null) {
mSendWindowContentChangedAccessibilityEvent.removeCallbacksAndRun();
}
final int eventType = event.getEventType();
switch (eventType) {
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
final long sourceNodeId = event.getSourceNodeId();
final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
sourceNodeId);
View source = mView.findViewByAccessibilityId(accessibilityViewId);
if (source != null) {
AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
if (provider != null) {
final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
sourceNodeId);
final AccessibilityNodeInfo node;
node = provider.createAccessibilityNodeInfo(virtualNodeId);
setAccessibilityFocus(source, node);
}
}
} break;
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
final long sourceNodeId = event.getSourceNodeId();
final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
sourceNodeId);
View source = mView.findViewByAccessibilityId(accessibilityViewId);
if (source != null) {
AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
if (provider != null) {
setAccessibilityFocus(null, null);
}
}
} break;
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
handleWindowContentChangedEvent(event);
} break;
}
mAccessibilityManager.sendAccessibilityEvent(event);
return true;
}
private void handleWindowContentChangedEvent(AccessibilityEvent event) {
final View focusedHost = mAccessibilityFocusedHost;
if (focusedHost == null || mAccessibilityFocusedVirtualView == null) {
return;
}
final AccessibilityNodeProvider provider = focusedHost.getAccessibilityNodeProvider();
if (provider == null) {
mAccessibilityFocusedHost = null;
mAccessibilityFocusedVirtualView = null;
focusedHost.clearAccessibilityFocusNoCallbacks(0);
return;
}
final int changes = event.getContentChangeTypes();
if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) == 0
&& changes != AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) {
return;
}
final long eventSourceNodeId = event.getSourceNodeId();
final int changedViewId = AccessibilityNodeInfo.getAccessibilityViewId(eventSourceNodeId);
boolean hostInSubtree = false;
View root = mAccessibilityFocusedHost;
while (root != null && !hostInSubtree) {
if (changedViewId == root.getAccessibilityViewId()) {
hostInSubtree = true;
} else {
final ViewParent parent = root.getParent();
if (parent instanceof View) {
root = (View) parent;
} else {
root = null;
}
}
}
if (!hostInSubtree) {
return;
}
final long focusedSourceNodeId = mAccessibilityFocusedVirtualView.getSourceNodeId();
int focusedChildId = AccessibilityNodeInfo.getVirtualDescendantId(focusedSourceNodeId);
final Rect oldBounds = mTempRect;
mAccessibilityFocusedVirtualView.getBoundsInScreen(oldBounds);
mAccessibilityFocusedVirtualView = provider.createAccessibilityNodeInfo(focusedChildId);
if (mAccessibilityFocusedVirtualView == null) {
mAccessibilityFocusedHost = null;
focusedHost.clearAccessibilityFocusNoCallbacks(0);
provider.performAction(focusedChildId,
AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(), null);
invalidateRectOnScreen(oldBounds);
} else {
final Rect newBounds = mAccessibilityFocusedVirtualView.getBoundsInScreen();
if (!oldBounds.equals(newBounds)) {
oldBounds.union(newBounds);
invalidateRectOnScreen(oldBounds);
}
}
}
@Override
public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
postSendWindowContentChangedCallback(Preconditions.checkNotNull(source), changeType);
}
@Override
public boolean canResolveLayoutDirection() {
return true;
}
@Override
public boolean isLayoutDirectionResolved() {
return true;
}
@Override
public int getLayoutDirection() {
return View.LAYOUT_DIRECTION_RESOLVED_DEFAULT;
}
@Override
public boolean canResolveTextDirection() {
return true;
}
@Override
public boolean isTextDirectionResolved() {
return true;
}
@Override
public int getTextDirection() {
return View.TEXT_DIRECTION_RESOLVED_DEFAULT;
}
@Override
public boolean canResolveTextAlignment() {
return true;
}
@Override
public boolean isTextAlignmentResolved() {
return true;
}
@Override
public int getTextAlignment() {
return View.TEXT_ALIGNMENT_RESOLVED_DEFAULT;
}
private View getCommonPredecessor(View first, View second) {
if (mTempHashSet == null) {
mTempHashSet = new HashSet<View>();
}
HashSet<View> seen = mTempHashSet;
seen.clear();
View firstCurrent = first;
while (firstCurrent != null) {
seen.add(firstCurrent);
ViewParent firstCurrentParent = firstCurrent.mParent;
if (firstCurrentParent instanceof View) {
firstCurrent = (View) firstCurrentParent;
} else {
firstCurrent = null;
}
}
View secondCurrent = second;
while (secondCurrent != null) {
if (seen.contains(secondCurrent)) {
seen.clear();
return secondCurrent;
}
ViewParent secondCurrentParent = secondCurrent.mParent;
if (secondCurrentParent instanceof View) {
secondCurrent = (View) secondCurrentParent;
} else {
secondCurrent = null;
}
}
seen.clear();
return null;
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
@Override
public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
if (rectangle == null) {
return scrollToRectOrFocus(null, immediate);
}
rectangle.offset(child.getLeft() - child.getScrollX(),
child.getTop() - child.getScrollY());
final boolean scrolled = scrollToRectOrFocus(rectangle, immediate);
mTempRect.set(rectangle);
mTempRect.offset(0, -mCurScrollY);
mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
try {
mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect);
} catch (RemoteException re) {
}
return scrolled;
}
@Override
public void childHasTransientStateChanged(View child, boolean hasTransientState) {
}
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
return false;
}
@Override
public void onStopNestedScroll(View target) {
}
@Override
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
}
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed) {
}
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
}
@Override
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
return false;
}
@Override
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
return false;
}
@Override
public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) {
return false;
}
private void reportNextDraw() {
if (mReportNextDraw == false) {
drawPending();
}
mReportNextDraw = true;
}
public void setReportNextDraw() {
reportNextDraw();
invalidate();
}
void changeCanvasOpacity(boolean opaque) {
Log.d(mTag, "changeCanvasOpacity: opaque=" + opaque);
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.setOpaque(opaque);
}
}
public boolean dispatchUnhandledKeyEvent(KeyEvent event) {
return mUnhandledKeyManager.dispatch(mView, event);
}
class TakenSurfaceHolder extends BaseSurfaceHolder {
@Override
public boolean onAllowLockCanvas() {
return mDrawingAllowed;
}
@Override
public void onRelayoutContainer() {
}
@Override
public void setFormat(int format) {
((RootViewSurfaceTaker)mView).setSurfaceFormat(format);
}
@Override
public void setType(int type) {
((RootViewSurfaceTaker)mView).setSurfaceType(type);
}
@Override
public void onUpdateSurface() {
throw new IllegalStateException("Shouldn't be here");
}
@Override
public boolean isCreating() {
return mIsCreating;
}
@Override
public void setFixedSize(int width, int height) {
throw new UnsupportedOperationException(
"Currently only support sizing from layout");
}
@Override
public void setKeepScreenOn(boolean screenOn) {
((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn);
}
}
static class W extends IWindow.Stub {
private final WeakReference<ViewRootImpl> mViewAncestor;
private final IWindowSession mWindowSession;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
mWindowSession = viewAncestor.mWindowSession;
}
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
boolean alwaysConsumeNavBar, int displayId,
DisplayCutout.ParcelableWrapper displayCutout) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
visibleInsets, stableInsets, outsets, reportDraw, mergedConfiguration,
backDropFrame, forceLayout, alwaysConsumeNavBar, displayId, displayCutout);
}
}
@Override
public void moved(int newX, int newY) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchMoved(newX, newY);
}
}
@Override
public void dispatchAppVisibility(boolean visible) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchAppVisibility(visible);
}
}
@Override
public void dispatchGetNewSurface() {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchGetNewSurface();
}
}
@Override
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 ActivityManager.getService().checkPermission(
permission, Binder.getCallingPid(), Binder.getCallingUid());
} catch (RemoteException e) {
return PackageManager.PERMISSION_DENIED;
}
}
@Override
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();
}
}
}
}
}
}
@Override
public void closeSystemDialogs(String reason) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchCloseSystemDialogs(reason);
}
}
@Override
public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
boolean sync) {
if (sync) {
try {
mWindowSession.wallpaperOffsetsComplete(asBinder());
} catch (RemoteException e) {
}
}
}
@Override
public void dispatchWallpaperCommand(String action, int x, int y,
int z, Bundle extras, boolean sync) {
if (sync) {
try {
mWindowSession.wallpaperCommandComplete(asBinder(), null);
} catch (RemoteException e) {
}
}
}
@Override
public void dispatchDragEvent(DragEvent event) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchDragEvent(event);
}
}
@Override
public void updatePointerIcon(float x, float y) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.updatePointerIcon(x, y);
}
}
@Override
public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
int localValue, int localChanges) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchSystemUiVisibilityChanged(seq, globalVisibility,
localValue, localChanges);
}
}
@Override
public void dispatchWindowShown() {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchWindowShown();
}
}
@Override
public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchRequestKeyboardShortcuts(receiver, deviceId);
}
}
@Override
public void dispatchPointerCaptureChanged(boolean hasCapture) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchPointerCaptureChanged(hasCapture);
}
}
}
public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
public CalledFromWrongThreadException(String msg) {
super(msg);
}
}
static HandlerActionQueue getRunQueue() {
HandlerActionQueue rq = sRunQueues.get();
if (rq != null) {
return rq;
}
rq = new HandlerActionQueue();
sRunQueues.set(rq);
return rq;
}
private void startDragResizing(Rect initialBounds, boolean fullscreen, Rect systemInsets,
Rect stableInsets, int resizeMode) {
if (!mDragResizing) {
mDragResizing = true;
if (mUseMTRenderer) {
for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
mWindowCallbacks.get(i).onWindowDragResizeStart(
initialBounds, fullscreen, systemInsets, stableInsets, resizeMode);
}
}
mFullRedrawNeeded = true;
}
}
private void endDragResizing() {
if (mDragResizing) {
mDragResizing = false;
if (mUseMTRenderer) {
for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
mWindowCallbacks.get(i).onWindowDragResizeEnd();
}
}
mFullRedrawNeeded = true;
}
}
private boolean updateContentDrawBounds() {
boolean updated = false;
if (mUseMTRenderer) {
for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
updated |=
mWindowCallbacks.get(i).onContentDrawn(mWindowAttributes.surfaceInsets.left,
mWindowAttributes.surfaceInsets.top, mWidth, mHeight);
}
}
return updated | (mDragResizing && mReportNextDraw);
}
private void requestDrawWindow() {
if (!mUseMTRenderer) {
return;
}
mWindowDrawCountDown = new CountDownLatch(mWindowCallbacks.size());
for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
mWindowCallbacks.get(i).onRequestDraw(mReportNextDraw);
}
}
public void reportActivityRelaunched() {
mActivityRelaunched = true;
}
final class AccessibilityInteractionConnectionManager
implements AccessibilityStateChangeListener {
@Override
public void onAccessibilityStateChanged(boolean enabled) {
if (enabled) {
ensureConnection();
if (mAttachInfo.mHasWindowFocus && (mView != null)) {
mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
View focusedView = mView.findFocus();
if (focusedView != null && focusedView != mView) {
focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
}
} else {
ensureNoConnection();
mHandler.obtainMessage(MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST).sendToTarget();
}
}
public void ensureConnection() {
final boolean registered = mAttachInfo.mAccessibilityWindowId
!= AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
if (!registered) {
mAttachInfo.mAccessibilityWindowId =
mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
mContext.getPackageName(),
new AccessibilityInteractionConnection(ViewRootImpl.this));
}
}
public void ensureNoConnection() {
final boolean registered = mAttachInfo.mAccessibilityWindowId
!= AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
if (registered) {
mAttachInfo.mAccessibilityWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow);
}
}
}
final class HighContrastTextManager implements HighTextContrastChangeListener {
HighContrastTextManager() {
ThreadedRenderer.setHighContrastText(mAccessibilityManager.isHighTextContrastEnabled());
}
@Override
public void onHighTextContrastStateChanged(boolean enabled) {
ThreadedRenderer.setHighContrastText(enabled);
destroyHardwareResources();
invalidate();
}
}
static final class AccessibilityInteractionConnection
extends IAccessibilityInteractionConnection.Stub {
private final WeakReference<ViewRootImpl> mViewRootImpl;
AccessibilityInteractionConnection(ViewRootImpl viewRootImpl) {
mViewRootImpl = new WeakReference<ViewRootImpl>(viewRootImpl);
}
@Override
public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId,
interactiveRegion, interactionId, callback, flags, interrogatingPid,
interrogatingTid, spec, args);
} else {
try {
callback.setFindAccessibilityNodeInfosResult(null, interactionId);
} catch (RemoteException re) {
}
}
}
@Override
public void performAccessibilityAction(long accessibilityNodeId, int action,
Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.performAccessibilityActionClientThread(accessibilityNodeId, action, arguments,
interactionId, callback, flags, interrogatingPid, interrogatingTid);
} else {
try {
callback.setPerformAccessibilityActionResult(false, interactionId);
} catch (RemoteException re) {
}
}
}
@Override
public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
String viewId, Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfosByViewIdClientThread(accessibilityNodeId,
viewId, interactiveRegion, interactionId, callback, flags,
interrogatingPid, interrogatingTid, spec);
} else {
try {
callback.setFindAccessibilityNodeInfoResult(null, interactionId);
} catch (RemoteException re) {
}
}
}
@Override
public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text,
interactiveRegion, interactionId, callback, flags, interrogatingPid,
interrogatingTid, spec);
} else {
try {
callback.setFindAccessibilityNodeInfosResult(null, interactionId);
} catch (RemoteException re) {
}
}
}
@Override
public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findFocusClientThread(accessibilityNodeId, focusType, interactiveRegion,
interactionId, callback, flags, interrogatingPid, interrogatingTid,
spec);
} else {
try {
callback.setFindAccessibilityNodeInfoResult(null, interactionId);
} catch (RemoteException re) {
}
}
}
@Override
public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.focusSearchClientThread(accessibilityNodeId, direction, interactiveRegion,
interactionId, callback, flags, interrogatingPid, interrogatingTid,
spec);
} else {
try {
callback.setFindAccessibilityNodeInfoResult(null, interactionId);
} catch (RemoteException re) {
}
}
}
}
private class SendWindowContentChangedAccessibilityEvent implements Runnable {
private int mChangeTypes = 0;
public View mSource;
public long mLastEventTimeMillis;
public StackTraceElement[] mOrigin;
@Override
public void run() {
View source = mSource;
mSource = null;
if (source == null) {
Log.e(TAG, "Accessibility content change has no source");
return;
}
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
mLastEventTimeMillis = SystemClock.uptimeMillis();
AccessibilityEvent event = AccessibilityEvent.obtain();
event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
event.setContentChangeTypes(mChangeTypes);
if (AccessibilityEvent.DEBUG_ORIGIN) event.originStackTrace = mOrigin;
source.sendAccessibilityEventUnchecked(event);
} else {
mLastEventTimeMillis = 0;
}
source.resetSubtreeAccessibilityStateChanged();
mChangeTypes = 0;
if (AccessibilityEvent.DEBUG_ORIGIN) mOrigin = null;
}
public void runOrPost(View source, int changeType) {
if (mHandler.getLooper() != Looper.myLooper()) {
CalledFromWrongThreadException e = new CalledFromWrongThreadException("Only the "
+ "original thread that created a view hierarchy can touch its views.");
Log.e(TAG, "Accessibility content change on non-UI thread. Future Android "
+ "versions will throw an exception.", e);
mHandler.removeCallbacks(this);
if (mSource != null) {
run();
}
}
if (mSource != null) {
View predecessor = getCommonPredecessor(mSource, source);
if (predecessor != null) {
predecessor = predecessor.getSelfOrParentImportantForA11y();
}
mSource = (predecessor != null) ? predecessor : source;
mChangeTypes |= changeType;
return;
}
mSource = source;
mChangeTypes = changeType;
if (AccessibilityEvent.DEBUG_ORIGIN) {
mOrigin = Thread.currentThread().getStackTrace();
}
final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis;
final long minEventIntevalMillis =
ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
if (timeSinceLastMillis >= minEventIntevalMillis) {
removeCallbacksAndRun();
} else {
mHandler.postDelayed(this, minEventIntevalMillis - timeSinceLastMillis);
}
}
public void removeCallbacksAndRun() {
mHandler.removeCallbacks(this);
run();
}
}
private static class UnhandledKeyManager {
private boolean mDispatched = true;
private final SparseArray<WeakReference<View>> mCapturedKeys = new SparseArray<>();
private WeakReference<View> mCurrentReceiver = null;
boolean dispatch(View root, KeyEvent event) {
if (mDispatched) {
return false;
}
View consumer;
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "UnhandledKeyEvent dispatch");
mDispatched = true;
consumer = root.dispatchUnhandledKeyEvent(event);
if (event.getAction() == KeyEvent.ACTION_DOWN) {
int keycode = event.getKeyCode();
if (consumer != null && !KeyEvent.isModifierKey(keycode)) {
mCapturedKeys.put(keycode, new WeakReference<>(consumer));
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return consumer != null;
}
void preDispatch(KeyEvent event) {
mCurrentReceiver = null;
if (event.getAction() == KeyEvent.ACTION_UP) {
int idx = mCapturedKeys.indexOfKey(event.getKeyCode());
if (idx >= 0) {
mCurrentReceiver = mCapturedKeys.valueAt(idx);
mCapturedKeys.removeAt(idx);
}
}
}
boolean preViewDispatch(KeyEvent event) {
mDispatched = false;
if (mCurrentReceiver == null) {
mCurrentReceiver = mCapturedKeys.get(event.getKeyCode());
}
if (mCurrentReceiver != null) {
View target = mCurrentReceiver.get();
if (event.getAction() == KeyEvent.ACTION_UP) {
mCurrentReceiver = null;
}
if (target != null && target.isAttachedToWindow()) {
target.onUnhandledKeyEvent(event);
}
return true;
}
return false;
}
}
}