summaryrefslogtreecommitdiff
path: root/android/view/ViewRootImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/view/ViewRootImpl.java')
-rw-r--r--android/view/ViewRootImpl.java414
1 files changed, 320 insertions, 94 deletions
diff --git a/android/view/ViewRootImpl.java b/android/view/ViewRootImpl.java
index 30f584c5..433c90b3 100644
--- a/android/view/ViewRootImpl.java
+++ b/android/view/ViewRootImpl.java
@@ -29,6 +29,7 @@ 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;
@@ -71,6 +72,7 @@ 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;
@@ -89,13 +91,12 @@ import android.view.accessibility.AccessibilityManager.HighTextContrastChangeLis
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
-import android.view.accessibility.AccessibilityViewHierarchyState;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
-import android.view.accessibility.ThrottlingAccessibilityEventSender;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
+import android.view.autofill.AutofillManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
@@ -115,6 +116,9 @@ 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;
/**
@@ -376,7 +380,7 @@ public final class ViewRootImpl implements ViewParent,
InputStage mFirstPostImeInputStage;
InputStage mSyntheticInputStage;
- private final KeyFallbackManager mKeyFallbackManager = new KeyFallbackManager();
+ private final UnhandledKeyManager mUnhandledKeyManager = new UnhandledKeyManager();
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
@@ -461,6 +465,10 @@ public final class ViewRootImpl implements ViewParent,
new AccessibilityInteractionConnectionManager();
final HighContrastTextManager mHighContrastTextManager;
+ SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent;
+
+ HashSet<View> mTempHashSet;
+
private final int mDensity;
private final int mNoncompatDensity;
@@ -475,8 +483,6 @@ public final class ViewRootImpl implements ViewParent,
private boolean mNeedsRendererSetup;
- protected AccessibilityViewHierarchyState mAccessibilityState;
-
/**
* Consistency verifier for debugging purposes.
*/
@@ -748,7 +754,7 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
- getHostVisibility(), mDisplay.getDisplayId(),
+ getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
} catch (RemoteException e) {
@@ -895,6 +901,26 @@ public final class ViewRootImpl implements ViewParent,
return mWindowAttributes.getTitle();
}
+ /**
+ * @return the width of the root view. Note that this will return {@code -1} until the first
+ * layout traversal, when the width is set.
+ *
+ * @hide
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * @return the height of the root view. Note that this will return {@code -1} until the first
+ * layout traversal, when the height is set.
+ *
+ * @hide
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+
void destroyHardwareResources() {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.destroyHardwareResources(mView);
@@ -1686,8 +1712,8 @@ public final class ViewRootImpl implements ViewParent,
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
- desiredWindowWidth = dipToPx(config.screenWidthDp);
- desiredWindowHeight = dipToPx(config.screenHeightDp);
+ desiredWindowWidth = mWinFrame.width();
+ desiredWindowHeight = mWinFrame.height();
}
// We used to use the following condition to choose 32 bits drawing caches:
@@ -1974,6 +2000,7 @@ public final class ViewRootImpl implements ViewParent,
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) {
@@ -2547,6 +2574,10 @@ public final class ViewRootImpl implements ViewParent,
~WindowManager.LayoutParams
.SOFT_INPUT_IS_FORWARD_NAVIGATION;
mHasHadWindowFocus = true;
+
+ // Refocusing a window that has a focused view should fire a
+ // focus event for the view since the global focused view changed.
+ fireAccessibilityFocusEventIfHasFocusedNode();
} else {
if (mPointerCapture) {
handlePointerCaptureChanged(false);
@@ -2556,6 +2587,86 @@ public final class ViewRootImpl implements ViewParent,
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());
+ // This is a best effort since clearing and setting the focus via the
+ // provider APIs could have side effects. We don't have a provider API
+ // similar to that on View to ask a given event to be fired.
+ 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;
+ }
+
+ // Unfortunately some provider implementations don't properly
+ // implement AccessibilityNodeProvider#findFocus
+ 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 {
@@ -3816,6 +3927,7 @@ public final class ViewRootImpl implements ViewParent,
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;
@@ -3857,6 +3969,8 @@ public final class ViewRootImpl implements ViewParent,
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:
@@ -4034,6 +4148,13 @@ public final class ViewRootImpl implements ViewParent,
}
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) {
@@ -4324,7 +4445,8 @@ public final class ViewRootImpl implements ViewParent,
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)) || mStopped
+ && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
+ && !isAutofillUiShowing()) || mStopped
|| (mIsAmbientMode && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON))
|| (mPausedForTransition && !isBack(q.mEvent))) {
// This is a focus event and the window doesn't currently have input focus or
@@ -4659,6 +4781,14 @@ public final class ViewRootImpl implements ViewParent,
ensureTouchMode(event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN));
}
+ if (action == MotionEvent.ACTION_DOWN) {
+ // Upon motion event within app window, close autofill ui.
+ AutofillManager afm = getAutofillManager();
+ if (afm != null) {
+ afm.requestHideFillUi();
+ }
+ }
+
if (action == MotionEvent.ACTION_DOWN && mAttachInfo.mTooltipHost != null) {
mAttachInfo.mTooltipHost.hideTooltip();
}
@@ -4845,10 +4975,10 @@ public final class ViewRootImpl implements ViewParent,
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
- mKeyFallbackManager.mDispatched = false;
+ mUnhandledKeyManager.mDispatched = false;
- if (mKeyFallbackManager.hasFocus()
- && mKeyFallbackManager.dispatchUnique(mView, event)) {
+ if (mUnhandledKeyManager.hasFocus()
+ && mUnhandledKeyManager.dispatchUnique(mView, event)) {
return FINISH_HANDLED;
}
@@ -4861,7 +4991,7 @@ public final class ViewRootImpl implements ViewParent,
return FINISH_NOT_HANDLED;
}
- if (mKeyFallbackManager.dispatchUnique(mView, event)) {
+ if (mUnhandledKeyManager.dispatchUnique(mView, event)) {
return FINISH_HANDLED;
}
@@ -6297,6 +6427,28 @@ public final class ViewRootImpl implements ViewParent,
return mAudioManager;
}
+ private @Nullable AutofillManager getAutofillManager() {
+ if (mView instanceof ViewGroup) {
+ ViewGroup decorView = (ViewGroup) mView;
+ if (decorView.getChildCount() > 0) {
+ // We cannot use decorView's Context for querying AutofillManager: DecorView's
+ // context is based on Application Context, it would allocate a different
+ // AutofillManager instance.
+ 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"
@@ -6318,18 +6470,24 @@ public final class ViewRootImpl implements ViewParent,
params.backup();
mTranslator.translateWindowLayout(params);
}
+
if (params != null) {
if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
- }
- if (params != null && mOrigWindowType != params.type) {
- // For compatibility with old apps, don't crash here.
- 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;
+ if (mOrigWindowType != params.type) {
+ // For compatibility with old apps, don't crash here.
+ 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;
+ }
+ }
+
+ if (mSurface.isValid()) {
+ params.frameNumber = mSurface.getNextFrameNumber();
}
}
+
int relayoutResult = mWindowSession.relayout(
mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
@@ -7153,6 +7311,12 @@ public final class ViewRootImpl implements ViewParent,
mHandler.sendMessage(msg);
}
+ public void dispatchKeyFromAutofill(KeyEvent event) {
+ Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_AUTOFILL, event);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ }
+
/**
* Reinject unhandled {@link InputEvent}s in order to synthesize fallbacks events.
*
@@ -7258,9 +7422,11 @@ public final class ViewRootImpl implements ViewParent,
* {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
*/
private void postSendWindowContentChangedCallback(View source, int changeType) {
- getAccessibilityState()
- .getSendWindowContentChangedAccessibilityEvent()
- .runOrPost(source, changeType);
+ if (mSendWindowContentChangedAccessibilityEvent == null) {
+ mSendWindowContentChangedAccessibilityEvent =
+ new SendWindowContentChangedAccessibilityEvent();
+ }
+ mSendWindowContentChangedAccessibilityEvent.runOrPost(source, changeType);
}
/**
@@ -7268,18 +7434,9 @@ public final class ViewRootImpl implements ViewParent,
* {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
*/
private void removeSendWindowContentChangedCallback() {
- if (mAccessibilityState != null
- && mAccessibilityState.isWindowContentChangedEventSenderInitialized()) {
- ThrottlingAccessibilityEventSender.cancelIfPending(
- mAccessibilityState.getSendWindowContentChangedAccessibilityEvent());
- }
- }
-
- AccessibilityViewHierarchyState getAccessibilityState() {
- if (mAccessibilityState == null) {
- mAccessibilityState = new AccessibilityViewHierarchyState();
+ if (mSendWindowContentChangedAccessibilityEvent != null) {
+ mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent);
}
- return mAccessibilityState;
}
@Override
@@ -7317,8 +7474,12 @@ public final class ViewRootImpl implements ViewParent,
return false;
}
- // Send any pending event to prevent reordering
- flushPendingAccessibilityEvents();
+ // Immediately flush pending content changed event (if any) to preserve event order
+ if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+ && mSendWindowContentChangedAccessibilityEvent != null
+ && mSendWindowContentChangedAccessibilityEvent.mSource != null) {
+ mSendWindowContentChangedAccessibilityEvent.removeCallbacksAndRun();
+ }
// Intercept accessibility focus events fired by virtual nodes to keep
// track of accessibility focus position in such nodes.
@@ -7362,19 +7523,6 @@ public final class ViewRootImpl implements ViewParent,
return true;
}
- /** @hide */
- public void flushPendingAccessibilityEvents() {
- if (mAccessibilityState != null) {
- if (mAccessibilityState.isScrollEventSenderInitialized()) {
- mAccessibilityState.getSendViewScrolledAccessibilityEvent().sendNowIfPending();
- }
- if (mAccessibilityState.isWindowContentChangedEventSenderInitialized()) {
- mAccessibilityState.getSendWindowContentChangedAccessibilityEvent()
- .sendNowIfPending();
- }
- }
- }
-
/**
* Updates the focused virtual view, when necessary, in response to a
* content changed event.
@@ -7509,6 +7657,39 @@ public final class ViewRootImpl implements ViewParent,
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(
@@ -7617,7 +7798,7 @@ public final class ViewRootImpl implements ViewParent,
* @return {@code true} if the event was handled, {@code false} otherwise.
*/
public boolean dispatchKeyFallbackEvent(KeyEvent event) {
- return mKeyFallbackManager.dispatch(mView, event);
+ return mUnhandledKeyManager.dispatch(mView, event);
}
class TakenSurfaceHolder extends BaseSurfaceHolder {
@@ -8119,18 +8300,91 @@ public final class ViewRootImpl implements ViewParent,
}
}
- private static class KeyFallbackManager {
+ private class SendWindowContentChangedAccessibilityEvent implements Runnable {
+ private int mChangeTypes = 0;
+
+ public View mSource;
+ public long mLastEventTimeMillis;
+
+ @Override
+ public void run() {
+ // Protect against re-entrant code and attempt to do the right thing in the case that
+ // we're multithreaded.
+ View source = mSource;
+ mSource = null;
+ if (source == null) {
+ Log.e(TAG, "Accessibility content change has no source");
+ return;
+ }
+ // The accessibility may be turned off while we were waiting so check again.
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ mLastEventTimeMillis = SystemClock.uptimeMillis();
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeTypes(mChangeTypes);
+ source.sendAccessibilityEventUnchecked(event);
+ } else {
+ mLastEventTimeMillis = 0;
+ }
+ // In any case reset to initial state.
+ source.resetSubtreeAccessibilityStateChanged();
+ mChangeTypes = 0;
+ }
+
+ 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.");
+ // TODO: Throw the exception
+ Log.e(TAG, "Accessibility content change on non-UI thread. Future Android "
+ + "versions will throw an exception.", e);
+ // Attempt to recover. This code does not eliminate the thread safety issue, but
+ // it should force any issues to happen near the above log.
+ mHandler.removeCallbacks(this);
+ if (mSource != null) {
+ // Dispatch whatever was pending. It's still possible that the runnable started
+ // just before we removed the callbacks, and bad things will happen, but at
+ // least they should happen very close to the logged error.
+ run();
+ }
+ }
+ if (mSource != null) {
+ // If there is no common predecessor, then mSource points to
+ // a removed view, hence in this case always prefer the source.
+ View predecessor = getCommonPredecessor(mSource, source);
+ mSource = (predecessor != null) ? predecessor : source;
+ mChangeTypes |= changeType;
+ return;
+ }
+ mSource = source;
+ mChangeTypes = changeType;
+ 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 {
- // This is used to ensure that key-fallback events are only dispatched once. We attempt
+ // This is used to ensure that unhandled events are only dispatched once. We attempt
// to dispatch more than once in order to achieve a certain order. Specifically, if we
- // are in an Activity or Dialog (and have a Window.Callback), the keyfallback events should
+ // are in an Activity or Dialog (and have a Window.Callback), the unhandled events should
// be dispatched after the view hierarchy, but before the Activity. However, if we aren't
- // in an activity, we still want key fallbacks to be dispatched.
+ // in an activity, we still want unhandled keys to be dispatched.
boolean mDispatched = false;
SparseBooleanArray mCapturedKeys = new SparseBooleanArray();
- WeakReference<View> mFallbackReceiver = null;
- int mVisitCount = 0;
+ WeakReference<View> mCurrentReceiver = null;
private void updateCaptureState(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
@@ -8147,56 +8401,28 @@ public final class ViewRootImpl implements ViewParent,
updateCaptureState(event);
- if (mFallbackReceiver != null) {
- View target = mFallbackReceiver.get();
+ if (mCurrentReceiver != null) {
+ View target = mCurrentReceiver.get();
if (mCapturedKeys.size() == 0) {
- mFallbackReceiver = null;
+ mCurrentReceiver = null;
}
if (target != null && target.isAttachedToWindow()) {
- return target.onKeyFallback(event);
+ target.onUnhandledKeyEvent(event);
}
// consume anyways so that we don't feed uncaptured key events to other views
return true;
}
- boolean result = dispatchInZOrder(root, event);
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- return result;
- }
-
- private boolean dispatchInZOrder(View view, KeyEvent evt) {
- if (view instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) view;
- ArrayList<View> orderedViews = vg.buildOrderedChildList();
- if (orderedViews != null) {
- try {
- for (int i = orderedViews.size() - 1; i >= 0; --i) {
- View v = orderedViews.get(i);
- if (dispatchInZOrder(v, evt)) {
- return true;
- }
- }
- } finally {
- orderedViews.clear();
- }
- } else {
- for (int i = vg.getChildCount() - 1; i >= 0; --i) {
- View v = vg.getChildAt(i);
- if (dispatchInZOrder(v, evt)) {
- return true;
- }
- }
- }
+ View consumer = root.dispatchUnhandledKeyEvent(event);
+ if (consumer != null) {
+ mCurrentReceiver = new WeakReference<>(consumer);
}
- if (view.onKeyFallback(evt)) {
- mFallbackReceiver = new WeakReference<>(view);
- return true;
- }
- return false;
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ return consumer != null;
}
boolean hasFocus() {
- return mFallbackReceiver != null;
+ return mCurrentReceiver != null;
}
boolean dispatchUnique(View root, KeyEvent event) {