diff options
Diffstat (limited to 'core/java/android/view/ViewRootImpl.java')
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index aceb276e9fc0..440df89f814f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -105,7 +105,11 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; +import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; +import android.view.contentcapture.ContentCaptureManager; +import android.view.contentcapture.ContentCaptureSession; +import android.view.contentcapture.MainContentCaptureSession; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; @@ -220,6 +224,21 @@ public final class ViewRootImpl implements ViewParent, */ static final int MAX_TRACKBALL_DELAY = 250; + /** + * Initial value for {@link #mContentCaptureEnabled}. + */ + private static final int CONTENT_CAPTURE_ENABLED_NOT_CHECKED = 0; + + /** + * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code true}. + */ + private static final int CONTENT_CAPTURE_ENABLED_TRUE = 1; + + /** + * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code false}. + */ + private static final int CONTENT_CAPTURE_ENABLED_FALSE = 2; + @UnsupportedAppUsage static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); @@ -410,6 +429,10 @@ public final class ViewRootImpl implements ViewParent, boolean mLayoutRequested; boolean mFirst; + @Nullable + int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED; + boolean mPerformContentCapture; + boolean mReportNextDraw; boolean mFullRedrawNeeded; boolean mNewSurfaceNeeded; @@ -607,6 +630,7 @@ public final class ViewRootImpl implements ViewParent, mTransparentRegion = new Region(); mPreviousTransparentRegion = new Region(); mFirst = true; // true for the first time the view is added + mPerformContentCapture = true; // also true for the first time the view is added mAdded = false; mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, context); @@ -2765,9 +2789,55 @@ public final class ViewRootImpl implements ViewParent, } } + if (mAttachInfo.mContentCaptureEvents != null) { + notifyContentCatpureEvents(); + } + mIsInTraversal = false; } + private void notifyContentCatpureEvents() { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents"); + try { + MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager + .getMainContentCaptureSession(); + for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) { + int sessionId = mAttachInfo.mContentCaptureEvents.keyAt(i); + mainSession.notifyViewTreeEvent(sessionId, /* started= */ true); + ArrayList<Object> events = mAttachInfo.mContentCaptureEvents + .valueAt(i); + for_each_event: for (int j = 0; j < events.size(); j++) { + Object event = events.get(j); + if (event instanceof AutofillId) { + mainSession.notifyViewDisappeared(sessionId, (AutofillId) event); + } else if (event instanceof View) { + View view = (View) event; + ContentCaptureSession session = view.getContentCaptureSession(); + if (session == null) { + Log.w(mTag, "no content capture session on view: " + view); + continue for_each_event; + } + int actualId = session.getId(); + if (actualId != sessionId) { + Log.w(mTag, "content capture session mismatch for view (" + view + + "): was " + sessionId + " before, it's " + actualId + " now"); + continue for_each_event; + } + ViewStructure structure = session.newViewStructure(view); + view.onProvideContentCaptureStructure(structure, /* flags= */ 0); + session.notifyViewAppeared(structure); + } else { + Log.w(mTag, "invalid content capture event: " + event); + } + } + mainSession.notifyViewTreeEvent(sessionId, /* started= */ false); + } + mAttachInfo.mContentCaptureEvents = null; + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + } + private void notifySurfaceDestroyed() { mSurfaceHolder.ungetCallbacks(); SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); @@ -2902,6 +2972,13 @@ public final class ViewRootImpl implements ViewParent, } } mFirstInputStage.onWindowFocusChanged(hasWindowFocus); + + // NOTE: there's no view visibility (appeared / disapparead) events when the windows focus + // is lost, so we don't need to to force a flush - there might be other events such as + // text changes, but these should be flushed independently. + if (hasWindowFocus) { + handleContentCaptureFlush(); + } } private void fireAccessibilityFocusEventIfHasFocusedNode() { @@ -3468,6 +3545,86 @@ public final class ViewRootImpl implements ViewParent, pendingDrawFinished(); } } + if (mPerformContentCapture) { + performContentCaptureInitialReport(); + } + } + + /** + * Checks (and caches) if content capture is enabled for this context. + */ + private boolean isContentCaptureEnabled() { + switch (mContentCaptureEnabled) { + case CONTENT_CAPTURE_ENABLED_TRUE: + return true; + case CONTENT_CAPTURE_ENABLED_FALSE: + return false; + case CONTENT_CAPTURE_ENABLED_NOT_CHECKED: + final boolean reallyEnabled = isContentCaptureReallyEnabled(); + mContentCaptureEnabled = reallyEnabled ? CONTENT_CAPTURE_ENABLED_TRUE + : CONTENT_CAPTURE_ENABLED_FALSE; + return reallyEnabled; + default: + Log.w(TAG, "isContentCaptureEnabled(): invalid state " + mContentCaptureEnabled); + return false; + } + + } + + /** + * Checks (without caching) if content capture is enabled for this context. + */ + private boolean isContentCaptureReallyEnabled() { + // First check if context supports it, so it saves a service lookup when it doesn't + if (mContext.getContentCaptureOptions() == null) return false; + + final ContentCaptureManager ccm = mAttachInfo.getContentCaptureManager(mContext); + // Then check if it's enabled in the contex itself. + if (ccm == null || !ccm.isContentCaptureEnabled()) return false; + + return true; + } + + private void performContentCaptureInitialReport() { + mPerformContentCapture = false; // One-time offer! + final View rootView = mView; + if (DEBUG_CONTENT_CAPTURE) { + Log.v(mTag, "performContentCaptureInitialReport() on " + rootView); + } + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for " + + getClass().getSimpleName()); + } + try { + if (!isContentCaptureEnabled()) return; + + // Content capture is a go! + rootView.dispatchInitialProvideContentCaptureStructure(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + } + + private void handleContentCaptureFlush() { + if (DEBUG_CONTENT_CAPTURE) { + Log.v(mTag, "handleContentCaptureFlush()"); + } + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for " + + getClass().getSimpleName()); + } + try { + if (!isContentCaptureEnabled()) return; + + final ContentCaptureManager ccm = mAttachInfo.mContentCaptureManager; + if (ccm == null) { + Log.w(TAG, "No ContentCapture on AttachInfo"); + return; + } + ccm.flush(ContentCaptureSession.FLUSH_REASON_VIEW_ROOT_ENTERED); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } } private boolean draw(boolean fullRedrawNeeded) { @@ -3834,6 +3991,13 @@ public final class ViewRootImpl implements ViewParent, } } + void updateLocationInParentDisplay(int x, int y) { + if (mAttachInfo != null + && !mAttachInfo.mLocationInParentDisplay.equals(x, y)) { + mAttachInfo.mLocationInParentDisplay.set(x, y); + } + } + /** * Set the root-level system gesture exclusion rects. These are added to those provided by * the root's view hierarchy. @@ -4338,6 +4502,7 @@ public final class ViewRootImpl implements ViewParent, private static final int MSG_INSETS_CHANGED = 30; private static final int MSG_INSETS_CONTROL_CHANGED = 31; private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32; + private static final int MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED = 33; final class ViewRootHandler extends Handler { @Override @@ -4399,6 +4564,8 @@ public final class ViewRootImpl implements ViewParent, return "MSG_INSETS_CONTROL_CHANGED"; case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: return "MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED"; + case MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED: + return "MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED"; } return super.getMessageName(message); } @@ -4634,6 +4801,9 @@ public final class ViewRootImpl implements ViewParent, case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: { systemGestureExclusionChanged(); } break; + case MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED: { + updateLocationInParentDisplay(msg.arg1, msg.arg2); + } break; } } } @@ -7840,6 +8010,17 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } + /** + * Dispatch the offset changed. + * + * @param offset the offset of this view in the parent window. + */ + public void dispatchLocationInParentDisplayChanged(Point offset) { + Message msg = + mHandler.obtainMessage(MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED, offset.x, offset.y); + mHandler.sendMessage(msg); + } + public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { synchronized (this) { mWindowFocusChanged = true; @@ -8367,6 +8548,14 @@ public final class ViewRootImpl implements ViewParent, } @Override + public void locationInParentDisplayChanged(Point offset) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchLocationInParentDisplayChanged(offset); + } + } + + @Override public void insetsChanged(InsetsState insetsState) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { |
