DO NOT MERGE - Re-add ContentCapture support from standard SDK toolkit.
am: 911cb1fa76
Change-Id: I15c8da47beb6b4dd64b627aebb0288fcb359c3e5
This commit is contained in:
@@ -3171,10 +3171,13 @@ package android.view {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
|
@UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
|
||||||
|
method @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, to="auto"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_YES, to="yes"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_NO, to="no"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, to="yesExcludeDescendants"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, to="noExcludeDescendants")}) public int getImportantForContentCapture();
|
||||||
method public android.view.View getTooltipView();
|
method public android.view.View getTooltipView();
|
||||||
method public boolean isAutofilled();
|
method public boolean isAutofilled();
|
||||||
method public static boolean isDefaultFocusHighlightEnabled();
|
method public static boolean isDefaultFocusHighlightEnabled();
|
||||||
method public boolean isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
|
method public boolean isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
|
||||||
|
method public final boolean isImportantForContentCapture();
|
||||||
|
method public void onProvideContentCaptureStructure(@NonNull android.view.ViewStructure, int);
|
||||||
method protected void resetResolvedDrawables();
|
method protected void resetResolvedDrawables();
|
||||||
method public void resetResolvedLayoutDirection();
|
method public void resetResolvedLayoutDirection();
|
||||||
method public void resetResolvedPadding();
|
method public void resetResolvedPadding();
|
||||||
@@ -3185,7 +3188,13 @@ package android.view {
|
|||||||
method public boolean restoreFocusNotInCluster();
|
method public boolean restoreFocusNotInCluster();
|
||||||
method public void setAutofilled(boolean);
|
method public void setAutofilled(boolean);
|
||||||
method public final void setFocusedInCluster();
|
method public final void setFocusedInCluster();
|
||||||
|
method public void setImportantForContentCapture(int);
|
||||||
method public void setIsRootNamespace(boolean);
|
method public void setIsRootNamespace(boolean);
|
||||||
|
field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0; // 0x0
|
||||||
|
field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 2; // 0x2
|
||||||
|
field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
|
||||||
|
field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 1; // 0x1
|
||||||
|
field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ViewConfiguration {
|
public class ViewConfiguration {
|
||||||
|
|||||||
@@ -1377,6 +1377,74 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
*/
|
*/
|
||||||
public static final int AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x1;
|
public static final int AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x1;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@IntDef(prefix = { "IMPORTANT_FOR_CONTENT_CAPTURE_" }, value = {
|
||||||
|
IMPORTANT_FOR_CONTENT_CAPTURE_AUTO,
|
||||||
|
IMPORTANT_FOR_CONTENT_CAPTURE_YES,
|
||||||
|
IMPORTANT_FOR_CONTENT_CAPTURE_NO,
|
||||||
|
IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
|
||||||
|
IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface ContentCaptureImportance {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically determine whether a view is important for content capture.
|
||||||
|
*
|
||||||
|
* @see #isImportantForContentCapture()
|
||||||
|
* @see #setImportantForContentCapture(int)
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@TestApi
|
||||||
|
public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0x0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The view is important for content capture, and its children (if any) will be traversed.
|
||||||
|
*
|
||||||
|
* @see #isImportantForContentCapture()
|
||||||
|
* @see #setImportantForContentCapture(int)
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@TestApi
|
||||||
|
public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 0x1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The view is not important for content capture, but its children (if any) will be traversed.
|
||||||
|
*
|
||||||
|
* @see #isImportantForContentCapture()
|
||||||
|
* @see #setImportantForContentCapture(int)
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@TestApi
|
||||||
|
public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 0x2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The view is important for content capture, but its children (if any) will not be traversed.
|
||||||
|
*
|
||||||
|
* @see #isImportantForContentCapture()
|
||||||
|
* @see #setImportantForContentCapture(int)
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@TestApi
|
||||||
|
public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 0x4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The view is not important for content capture, and its children (if any) will not be
|
||||||
|
* traversed.
|
||||||
|
*
|
||||||
|
* @see #isImportantForContentCapture()
|
||||||
|
* @see #setImportantForContentCapture(int)
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@TestApi
|
||||||
|
public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 0x8;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This view is enabled. Interpretation varies by subclass.
|
* This view is enabled. Interpretation varies by subclass.
|
||||||
* Use with ENABLED_MASK when calling setFlags.
|
* Use with ENABLED_MASK when calling setFlags.
|
||||||
@@ -3349,6 +3417,55 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
|
|
||||||
/* End of masks for mPrivateFlags3 */
|
/* End of masks for mPrivateFlags3 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Masks for mPrivateFlags4, as generated by dumpFlags():
|
||||||
|
*
|
||||||
|
* |-------|-------|-------|-------|
|
||||||
|
* 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
|
||||||
|
* 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED
|
||||||
|
* 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED
|
||||||
|
* 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
|
||||||
|
* 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE
|
||||||
|
* 11 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK
|
||||||
|
* |-------|-------|-------|-------|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mask for obtaining the bits which specify how to determine
|
||||||
|
* whether a view is important for autofill.
|
||||||
|
*
|
||||||
|
* <p>NOTE: the important for content capture values were the first flags added and are set in
|
||||||
|
* the rightmost position, so we don't need to shift them
|
||||||
|
*/
|
||||||
|
private static final int PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK =
|
||||||
|
IMPORTANT_FOR_CONTENT_CAPTURE_AUTO | IMPORTANT_FOR_CONTENT_CAPTURE_YES
|
||||||
|
| IMPORTANT_FOR_CONTENT_CAPTURE_NO
|
||||||
|
| IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS
|
||||||
|
| IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Variables used to control when the IntelligenceManager.notifyNodeAdded()/removed() methods
|
||||||
|
* should be called.
|
||||||
|
*
|
||||||
|
* The idea is to call notifyAppeared() after the view is layout and visible, then call
|
||||||
|
* notifyDisappeared() when it's gone (without known when it was removed from the parent).
|
||||||
|
*/
|
||||||
|
private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10;
|
||||||
|
private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flags used to cache the value returned by isImportantForContentCapture while the view
|
||||||
|
* hierarchy is being traversed.
|
||||||
|
*/
|
||||||
|
private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED = 0x40;
|
||||||
|
private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE = 0x80;
|
||||||
|
|
||||||
|
private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK =
|
||||||
|
PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
|
||||||
|
| PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
|
||||||
|
|
||||||
|
/* End of masks for mPrivateFlags4 */
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
protected static final int VIEW_STRUCTURE_FOR_ASSIST = 0;
|
protected static final int VIEW_STRUCTURE_FOR_ASSIST = 0;
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@@ -3972,6 +4089,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 129147060)
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 129147060)
|
||||||
int mPrivateFlags3;
|
int mPrivateFlags3;
|
||||||
|
|
||||||
|
private int mPrivateFlags4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This view's request for the visibility of the status bar.
|
* This view's request for the visibility of the status bar.
|
||||||
* @hide
|
* @hide
|
||||||
@@ -8427,6 +8546,65 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
onProvideStructure(structure, VIEW_STRUCTURE_FOR_AUTOFILL, flags);
|
onProvideStructure(structure, VIEW_STRUCTURE_FOR_AUTOFILL, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates a {@link ViewStructure} for content capture.
|
||||||
|
*
|
||||||
|
* <p>This method is called after a view is that is eligible for content capture
|
||||||
|
* (for example, if it {@link #isImportantForAutofill()}, an intelligence service is enabled for
|
||||||
|
* the user, and the activity rendering the view is enabled for content capture) is laid out and
|
||||||
|
* is visible.
|
||||||
|
*
|
||||||
|
* <p>The populated structure is then passed to the service through
|
||||||
|
* {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}.
|
||||||
|
*
|
||||||
|
* <p><b>Note: </b>views that manage a virtual structure under this view must populate just
|
||||||
|
* the node representing this view and return right away, then asynchronously report (not
|
||||||
|
* necessarily in the UI thread) when the children nodes appear, disappear or have their text
|
||||||
|
* changed by calling
|
||||||
|
* {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)},
|
||||||
|
* {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and
|
||||||
|
* {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence)}
|
||||||
|
* respectively. The structure for the a child must be created using
|
||||||
|
* {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the
|
||||||
|
* {@code autofillId} for a child can be obtained either through
|
||||||
|
* {@code childStructure.getAutofillId()} or
|
||||||
|
* {@link ContentCaptureSession#newAutofillId(AutofillId, long)}.
|
||||||
|
*
|
||||||
|
* <p>When the virtual view hierarchy represents a web page, you should also:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Call {@link ContentCaptureManager#getContentCaptureConditions()} to infer content
|
||||||
|
* capture events should be generate for that URL.
|
||||||
|
* <li>Create a new {@link ContentCaptureSession} child for every HTML element that
|
||||||
|
* renders a new URL (like an {@code IFRAME}) and use that session to notify events from
|
||||||
|
* that subtree.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link ViewStructure#setChildCount(int)}
|
||||||
|
* <li>{@link ViewStructure#addChildCount(int)}
|
||||||
|
* <li>{@link ViewStructure#getChildCount()}
|
||||||
|
* <li>{@link ViewStructure#newChild(int)}
|
||||||
|
* <li>{@link ViewStructure#asyncNewChild(int)}
|
||||||
|
* <li>{@link ViewStructure#asyncCommit()}
|
||||||
|
* <li>{@link ViewStructure#setWebDomain(String)}
|
||||||
|
* <li>{@link ViewStructure#newHtmlInfoBuilder(String)}
|
||||||
|
* <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}
|
||||||
|
* <li>{@link ViewStructure#setDataIsSensitive(boolean)}
|
||||||
|
* <li>{@link ViewStructure#setAlpha(float)}
|
||||||
|
* <li>{@link ViewStructure#setElevation(float)}
|
||||||
|
* <li>{@link ViewStructure#setTransformation(Matrix)}
|
||||||
|
*
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@TestApi
|
||||||
|
public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
|
||||||
|
onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
protected void onProvideStructure(@NonNull ViewStructure structure,
|
protected void onProvideStructure(@NonNull ViewStructure structure,
|
||||||
@ViewStructureType int viewFor, int flags) {
|
@ViewStructureType int viewFor, int flags) {
|
||||||
@@ -9064,6 +9242,274 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the mode for determining whether this view is important for content capture.
|
||||||
|
*
|
||||||
|
* <p>See {@link #setImportantForContentCapture(int)} and
|
||||||
|
* {@link #isImportantForContentCapture()} for more info about this mode.
|
||||||
|
*
|
||||||
|
* @return {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO} by default, or value passed to
|
||||||
|
* {@link #setImportantForContentCapture(int)}.
|
||||||
|
*
|
||||||
|
* @attr ref android.R.styleable#View_importantForContentCapture
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@ViewDebug.ExportedProperty(mapping = {
|
||||||
|
@ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, to = "auto"),
|
||||||
|
@ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES, to = "yes"),
|
||||||
|
@ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO, to = "no"),
|
||||||
|
@ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
|
||||||
|
to = "yesExcludeDescendants"),
|
||||||
|
@ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS,
|
||||||
|
to = "noExcludeDescendants")})
|
||||||
|
// @InspectableProperty(enumMapping = {
|
||||||
|
// @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, name = "auto"),
|
||||||
|
// @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES, name = "yes"),
|
||||||
|
// @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO, name = "no"),
|
||||||
|
// @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
|
||||||
|
// name = "yesExcludeDescendants"),
|
||||||
|
// @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS,
|
||||||
|
// name = "noExcludeDescendants"),
|
||||||
|
// })
|
||||||
|
@TestApi
|
||||||
|
public @ContentCaptureImportance int getImportantForContentCapture() {
|
||||||
|
// NOTE: the important for content capture values were the first flags added and are set in
|
||||||
|
// the rightmost position, so we don't need to shift them
|
||||||
|
return mPrivateFlags4 & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the mode for determining whether this view is considered important for content capture.
|
||||||
|
*
|
||||||
|
* <p>The platform determines the importance for autofill automatically but you
|
||||||
|
* can use this method to customize the behavior. Typically, a view that provides text should
|
||||||
|
* be marked as {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}.
|
||||||
|
*
|
||||||
|
* @param mode {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO},
|
||||||
|
* {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}, {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO},
|
||||||
|
* {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS},
|
||||||
|
* or {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS}.
|
||||||
|
*
|
||||||
|
* @attr ref android.R.styleable#View_importantForContentCapture
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@TestApi
|
||||||
|
public void setImportantForContentCapture(@ContentCaptureImportance int mode) {
|
||||||
|
// Reset first
|
||||||
|
mPrivateFlags4 &= ~PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK;
|
||||||
|
// Then set again
|
||||||
|
// NOTE: the important for content capture values were the first flags added and are set in
|
||||||
|
// the rightmost position, so we don't need to shift them
|
||||||
|
mPrivateFlags4 |= (mode & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hints the Android System whether this view is considered important for content capture, based
|
||||||
|
* on the value explicitly set by {@link #setImportantForContentCapture(int)} and heuristics
|
||||||
|
* when it's {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}.
|
||||||
|
*
|
||||||
|
* <p>See {@link ContentCaptureManager} for more info about content capture.
|
||||||
|
*
|
||||||
|
* @return whether the view is considered important for content capture.
|
||||||
|
*
|
||||||
|
* @see #setImportantForContentCapture(int)
|
||||||
|
* @see #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO
|
||||||
|
* @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES
|
||||||
|
* @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO
|
||||||
|
* @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS
|
||||||
|
* @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@TestApi
|
||||||
|
public final boolean isImportantForContentCapture() {
|
||||||
|
boolean isImportant;
|
||||||
|
if ((mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED) != 0) {
|
||||||
|
isImportant = (mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE) != 0;
|
||||||
|
return isImportant;
|
||||||
|
}
|
||||||
|
|
||||||
|
isImportant = calculateIsImportantForContentCapture();
|
||||||
|
|
||||||
|
mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
|
||||||
|
if (isImportant) {
|
||||||
|
mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
|
||||||
|
}
|
||||||
|
mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED;
|
||||||
|
return isImportant;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates whether the flag is important for content capture so it can be used by
|
||||||
|
* {@link #isImportantForContentCapture()} while the tree is traversed.
|
||||||
|
*/
|
||||||
|
private boolean calculateIsImportantForContentCapture() {
|
||||||
|
// Check parent mode to ensure we're important
|
||||||
|
ViewParent parent = mParent;
|
||||||
|
while (parent instanceof View) {
|
||||||
|
final int parentImportance = ((View) parent).getImportantForContentCapture();
|
||||||
|
if (parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
|
||||||
|
|| parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS) {
|
||||||
|
if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
|
||||||
|
Log.v(CONTENT_CAPTURE_LOG_TAG, "View (" + this + ") is not important for "
|
||||||
|
+ "content capture because parent " + parent + "'s importance is "
|
||||||
|
+ parentImportance);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
parent = parent.getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
final int importance = getImportantForContentCapture();
|
||||||
|
|
||||||
|
// First, check the explicit states.
|
||||||
|
if (importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS
|
||||||
|
|| importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
|
||||||
|
|| importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO) {
|
||||||
|
if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
|
||||||
|
Log.v(CONTENT_CAPTURE_LOG_TAG, "View (" + this + ") is not important for content "
|
||||||
|
+ "capture because its importance is " + importance);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then use some heuristics to handle AUTO.
|
||||||
|
if (importance != IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
|
||||||
|
Log.w(CONTENT_CAPTURE_LOG_TAG, "invalid content capture importance (" + importance
|
||||||
|
+ " on view " + this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// View group is important if at least one children also is
|
||||||
|
if (this instanceof ViewGroup) {
|
||||||
|
final ViewGroup group = (ViewGroup) this;
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
final View child = group.getChildAt(i);
|
||||||
|
if (child.isImportantForContentCapture()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the app developer explicitly set hints or autofill hintsfor it, it's important.
|
||||||
|
if (getAutofillHints() != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, assume it's not important...
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper used to notify the {@link ContentCaptureManager} when the view is removed or
|
||||||
|
* added, based on whether it's laid out and visible, and without knowing if the parent removed
|
||||||
|
* it from the view hierarchy.
|
||||||
|
*
|
||||||
|
* <p>This method is called from many places (visibility changed, view laid out, view attached
|
||||||
|
* or detached to/from window, etc...) and hence must contain the logic to call the manager, as
|
||||||
|
* described below:
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>It should only be called when content capture is enabled for the view.
|
||||||
|
* <li>It must call viewAppeared() before viewDisappeared()
|
||||||
|
* <li>viewAppearead() can only be called when the view is visible and laidout
|
||||||
|
* <li>It should not call the same event twice.
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
|
||||||
|
AttachInfo ai = mAttachInfo;
|
||||||
|
// Skip it while the view is being laided out for the first time
|
||||||
|
if (ai != null && !ai.mReadyForContentCaptureUpdates) return;
|
||||||
|
|
||||||
|
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
|
||||||
|
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
|
||||||
|
"notifyContentCapture(" + appeared + ") for " + getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(appeared);
|
||||||
|
} finally {
|
||||||
|
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(boolean appeared) {
|
||||||
|
AttachInfo ai = mAttachInfo;
|
||||||
|
|
||||||
|
// First check if context has client, so it saves a service lookup when it doesn't
|
||||||
|
if (mContext.getContentCaptureOptions() == null) return;
|
||||||
|
|
||||||
|
// Then check if it's enabled in the context...
|
||||||
|
final ContentCaptureManager ccm = ai != null ? ai.getContentCaptureManager(mContext)
|
||||||
|
: mContext.getSystemService(ContentCaptureManager.class);
|
||||||
|
if (ccm == null || !ccm.isContentCaptureEnabled()) return;
|
||||||
|
|
||||||
|
// ... and finally at the view level
|
||||||
|
// NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled()
|
||||||
|
if (!isImportantForContentCapture()) return;
|
||||||
|
|
||||||
|
ContentCaptureSession session = getContentCaptureSession();
|
||||||
|
if (session == null) return;
|
||||||
|
|
||||||
|
if (appeared) {
|
||||||
|
if (!isLaidOut() || getVisibility() != VISIBLE
|
||||||
|
|| (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) {
|
||||||
|
if (DEBUG_CONTENT_CAPTURE) {
|
||||||
|
Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid="
|
||||||
|
+ isLaidOut() + ", visibleToUser=" + isVisibleToUser()
|
||||||
|
+ ", visible=" + (getVisibility() == VISIBLE)
|
||||||
|
+ ": alreadyNotifiedAppeared=" + ((mPrivateFlags4
|
||||||
|
& PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0)
|
||||||
|
+ ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4
|
||||||
|
& PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setNotifiedContentCaptureAppeared();
|
||||||
|
|
||||||
|
if (ai != null) {
|
||||||
|
ai.delayNotifyContentCaptureEvent(session, this, appeared);
|
||||||
|
} else {
|
||||||
|
if (DEBUG_CONTENT_CAPTURE) {
|
||||||
|
Log.w(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on appeared for " + this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0
|
||||||
|
|| (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) {
|
||||||
|
if (DEBUG_CONTENT_CAPTURE) {
|
||||||
|
Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this + ": laid="
|
||||||
|
+ isLaidOut() + ", visibleToUser=" + isVisibleToUser()
|
||||||
|
+ ", visible=" + (getVisibility() == VISIBLE)
|
||||||
|
+ ": alreadyNotifiedAppeared=" + ((mPrivateFlags4
|
||||||
|
& PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0)
|
||||||
|
+ ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4
|
||||||
|
& PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
|
||||||
|
mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
|
||||||
|
|
||||||
|
if (ai != null) {
|
||||||
|
ai.delayNotifyContentCaptureEvent(session, this, appeared);
|
||||||
|
} else {
|
||||||
|
if (DEBUG_CONTENT_CAPTURE) {
|
||||||
|
Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on disappeared for " + this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setNotifiedContentCaptureAppeared() {
|
||||||
|
mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
|
||||||
|
mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the (optional) {@link ContentCaptureSession} associated with this view.
|
* Sets the (optional) {@link ContentCaptureSession} associated with this view.
|
||||||
*
|
*
|
||||||
@@ -9316,6 +9762,68 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches the initial content capture events for a view structure.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void dispatchInitialProvideContentCaptureStructure() {
|
||||||
|
AttachInfo ai = mAttachInfo;
|
||||||
|
if (ai == null) {
|
||||||
|
Log.w(CONTENT_CAPTURE_LOG_TAG,
|
||||||
|
"dispatchProvideContentCaptureStructure(): no AttachInfo for " + this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ContentCaptureManager ccm = ai.mContentCaptureManager;
|
||||||
|
if (ccm == null) {
|
||||||
|
Log.w(CONTENT_CAPTURE_LOG_TAG, "dispatchProvideContentCaptureStructure(): "
|
||||||
|
+ "no ContentCaptureManager for " + this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We must set it before checkign if the view itself is important, because it might
|
||||||
|
// initially not be (for example, if it's empty), although that might change later (for
|
||||||
|
// example, if important views are added)
|
||||||
|
ai.mReadyForContentCaptureUpdates = true;
|
||||||
|
|
||||||
|
if (!isImportantForContentCapture()) {
|
||||||
|
if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) {
|
||||||
|
Log.d(CONTENT_CAPTURE_LOG_TAG,
|
||||||
|
"dispatchProvideContentCaptureStructure(): decorView is not important");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ai.mContentCaptureManager = ccm;
|
||||||
|
|
||||||
|
ContentCaptureSession session = getContentCaptureSession();
|
||||||
|
if (session == null) {
|
||||||
|
if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) {
|
||||||
|
Log.d(CONTENT_CAPTURE_LOG_TAG,
|
||||||
|
"dispatchProvideContentCaptureStructure(): no session for " + this);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
session.internalNotifyViewTreeEvent(/* started= */ true);
|
||||||
|
try {
|
||||||
|
dispatchProvideContentCaptureStructure();
|
||||||
|
} finally {
|
||||||
|
session.internalNotifyViewTreeEvent(/* started= */ false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
void dispatchProvideContentCaptureStructure() {
|
||||||
|
ContentCaptureSession session = getContentCaptureSession();
|
||||||
|
if (session != null) {
|
||||||
|
ViewStructure structure = session.newViewStructure(this);
|
||||||
|
onProvideContentCaptureStructure(structure, /* flags= */ 0);
|
||||||
|
setNotifiedContentCaptureAppeared();
|
||||||
|
session.notifyViewAppeared(structure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
|
* @see #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
|
||||||
*
|
*
|
||||||
@@ -13266,6 +13774,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
public void dispatchStartTemporaryDetach() {
|
public void dispatchStartTemporaryDetach() {
|
||||||
mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH;
|
mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH;
|
||||||
notifyEnterOrExitForAutoFillIfNeeded(false);
|
notifyEnterOrExitForAutoFillIfNeeded(false);
|
||||||
|
notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
|
||||||
onStartTemporaryDetach();
|
onStartTemporaryDetach();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13292,6 +13801,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
notifyFocusChangeToInputMethodManager(true /* hasFocus */);
|
notifyFocusChangeToInputMethodManager(true /* hasFocus */);
|
||||||
}
|
}
|
||||||
notifyEnterOrExitForAutoFillIfNeeded(true);
|
notifyEnterOrExitForAutoFillIfNeeded(true);
|
||||||
|
notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13883,6 +14393,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
: AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
|
: AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17578,6 +18090,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset content capture caches
|
// Reset content capture caches
|
||||||
|
mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
|
||||||
mCachedContentCaptureSession = null;
|
mCachedContentCaptureSession = null;
|
||||||
|
|
||||||
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|
||||||
@@ -19587,6 +20100,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
needGlobalAttributesUpdate(false);
|
needGlobalAttributesUpdate(false);
|
||||||
|
|
||||||
notifyEnterOrExitForAutoFillIfNeeded(true);
|
notifyEnterOrExitForAutoFillIfNeeded(true);
|
||||||
|
notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
|
||||||
@@ -19636,6 +20150,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
notifyEnterOrExitForAutoFillIfNeeded(false);
|
notifyEnterOrExitForAutoFillIfNeeded(false);
|
||||||
|
notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21948,6 +22463,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
|
mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
|
||||||
notifyEnterOrExitForAutoFillIfNeeded(true);
|
notifyEnterOrExitForAutoFillIfNeeded(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasParentWantsFocus() {
|
private boolean hasParentWantsFocus() {
|
||||||
@@ -28158,6 +28675,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
*/
|
*/
|
||||||
View mTooltipHost;
|
View mTooltipHost;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The initial structure has been reported so the view is ready to report updates.
|
||||||
|
*/
|
||||||
|
boolean mReadyForContentCaptureUpdates;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map(keyed by session) of content capture events that need to be notified after the view
|
||||||
|
* hierarchy is traversed: value is either the view itself for appearead events, or its
|
||||||
|
* autofill id for disappeared.
|
||||||
|
*/
|
||||||
|
SparseArray<ArrayList<Object>> mContentCaptureEvents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached reference to the {@link ContentCaptureManager}.
|
||||||
|
*/
|
||||||
|
ContentCaptureManager mContentCaptureManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new set of attachment information with the specified
|
* Creates a new set of attachment information with the specified
|
||||||
* events handler and thread.
|
* events handler and thread.
|
||||||
@@ -28176,6 +28710,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
mRootCallbacks = effectPlayer;
|
mRootCallbacks = effectPlayer;
|
||||||
mTreeObserver = new ViewTreeObserver(context);
|
mTreeObserver = new ViewTreeObserver(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void delayNotifyContentCaptureEvent(@NonNull ContentCaptureSession session,
|
||||||
|
@NonNull View view, boolean appeared) {
|
||||||
|
if (mContentCaptureEvents == null) {
|
||||||
|
// Most of the time there will be just one session, so intial capacity is 1
|
||||||
|
mContentCaptureEvents = new SparseArray<>(1);
|
||||||
|
}
|
||||||
|
int sessionId = session.getId();
|
||||||
|
// TODO: life would be much easier if we provided a MultiMap implementation somwhere...
|
||||||
|
ArrayList<Object> events = mContentCaptureEvents.get(sessionId);
|
||||||
|
if (events == null) {
|
||||||
|
events = new ArrayList<>();
|
||||||
|
mContentCaptureEvents.put(sessionId, events);
|
||||||
|
}
|
||||||
|
events.add(appeared ? view : view.getAutofillId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
|
||||||
|
if (mContentCaptureManager != null) {
|
||||||
|
return mContentCaptureManager;
|
||||||
|
}
|
||||||
|
mContentCaptureManager = context.getSystemService(ContentCaptureManager.class);
|
||||||
|
return mContentCaptureManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3606,7 +3606,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ChildListForAutofill children = getChildrenForAutofill(flags);
|
final ChildListForAutoFillOrContentCapture children = getChildrenForAutofill(flags);
|
||||||
final int childrenCount = children.size();
|
final int childrenCount = children.size();
|
||||||
structure.setChildCount(childrenCount);
|
structure.setChildCount(childrenCount);
|
||||||
for (int i = 0; i < childrenCount; i++) {
|
for (int i = 0; i < childrenCount; i++) {
|
||||||
@@ -3617,14 +3617,30 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
|||||||
children.recycle();
|
children.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@Override
|
||||||
|
public void dispatchProvideContentCaptureStructure() {
|
||||||
|
super.dispatchProvideContentCaptureStructure();
|
||||||
|
|
||||||
|
if (!isLaidOut()) return;
|
||||||
|
|
||||||
|
final ChildListForAutoFillOrContentCapture children = getChildrenForContentCapture();
|
||||||
|
final int childrenCount = children.size();
|
||||||
|
for (int i = 0; i < childrenCount; i++) {
|
||||||
|
final View child = children.get(i);
|
||||||
|
child.dispatchProvideContentCaptureStructure();
|
||||||
|
}
|
||||||
|
children.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the children for autofill. Children for autofill are the first
|
* Gets the children for autofill. Children for autofill are the first
|
||||||
* level descendants that are important for autofill. The returned
|
* level descendants that are important for autofill. The returned
|
||||||
* child list object is pooled and the caller must recycle it once done.
|
* child list object is pooled and the caller must recycle it once done.
|
||||||
* @hide */
|
* @hide */
|
||||||
private @NonNull ChildListForAutofill getChildrenForAutofill(
|
private @NonNull ChildListForAutoFillOrContentCapture getChildrenForAutofill(
|
||||||
@AutofillFlags int flags) {
|
@AutofillFlags int flags) {
|
||||||
final ChildListForAutofill children = ChildListForAutofill
|
final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture
|
||||||
.obtain();
|
.obtain();
|
||||||
populateChildrenForAutofill(children, flags);
|
populateChildrenForAutofill(children, flags);
|
||||||
return children;
|
return children;
|
||||||
@@ -3652,6 +3668,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @NonNull ChildListForAutoFillOrContentCapture getChildrenForContentCapture() {
|
||||||
|
final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture
|
||||||
|
.obtain();
|
||||||
|
populateChildrenForContentCapture(children);
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
private void populateChildrenForContentCapture(ArrayList<View> list) {
|
||||||
|
final int childrenCount = mChildrenCount;
|
||||||
|
if (childrenCount <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final ArrayList<View> preorderedList = buildOrderedChildList();
|
||||||
|
final boolean customOrder = preorderedList == null
|
||||||
|
&& isChildrenDrawingOrderEnabled();
|
||||||
|
for (int i = 0; i < childrenCount; i++) {
|
||||||
|
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
|
||||||
|
final View child = (preorderedList == null)
|
||||||
|
? mChildren[childIndex] : preorderedList.get(childIndex);
|
||||||
|
if (child.isImportantForContentCapture()) {
|
||||||
|
list.add(child);
|
||||||
|
} else if (child instanceof ViewGroup) {
|
||||||
|
((ViewGroup) child).populateChildrenForContentCapture(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children,
|
private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children,
|
||||||
int childIndex) {
|
int childIndex) {
|
||||||
final View child;
|
final View child;
|
||||||
@@ -8634,16 +8678,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
|||||||
/**
|
/**
|
||||||
* Pooled class that to hold the children for autifill.
|
* Pooled class that to hold the children for autifill.
|
||||||
*/
|
*/
|
||||||
private static class ChildListForAutofill extends ArrayList<View> {
|
private static class ChildListForAutoFillOrContentCapture extends ArrayList<View> {
|
||||||
private static final int MAX_POOL_SIZE = 32;
|
private static final int MAX_POOL_SIZE = 32;
|
||||||
|
|
||||||
private static final Pools.SimplePool<ChildListForAutofill> sPool =
|
private static final Pools.SimplePool<ChildListForAutoFillOrContentCapture> sPool =
|
||||||
new Pools.SimplePool<>(MAX_POOL_SIZE);
|
new Pools.SimplePool<>(MAX_POOL_SIZE);
|
||||||
|
|
||||||
public static ChildListForAutofill obtain() {
|
public static ChildListForAutoFillOrContentCapture obtain() {
|
||||||
ChildListForAutofill list = sPool.acquire();
|
ChildListForAutoFillOrContentCapture list = sPool.acquire();
|
||||||
if (list == null) {
|
if (list == null) {
|
||||||
list = new ChildListForAutofill();
|
list = new ChildListForAutoFillOrContentCapture();
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,11 @@ import android.view.accessibility.IAccessibilityInteractionConnection;
|
|||||||
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
|
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
|
||||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||||
import android.view.animation.Interpolator;
|
import android.view.animation.Interpolator;
|
||||||
|
import android.view.autofill.AutofillId;
|
||||||
import android.view.autofill.AutofillManager;
|
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.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.Scroller;
|
import android.widget.Scroller;
|
||||||
|
|
||||||
@@ -220,6 +224,21 @@ public final class ViewRootImpl implements ViewParent,
|
|||||||
*/
|
*/
|
||||||
static final int MAX_TRACKBALL_DELAY = 250;
|
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
|
@UnsupportedAppUsage
|
||||||
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
|
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
|
||||||
|
|
||||||
@@ -410,6 +429,10 @@ public final class ViewRootImpl implements ViewParent,
|
|||||||
boolean mLayoutRequested;
|
boolean mLayoutRequested;
|
||||||
boolean mFirst;
|
boolean mFirst;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED;
|
||||||
|
boolean mPerformContentCapture;
|
||||||
|
|
||||||
boolean mReportNextDraw;
|
boolean mReportNextDraw;
|
||||||
boolean mFullRedrawNeeded;
|
boolean mFullRedrawNeeded;
|
||||||
boolean mNewSurfaceNeeded;
|
boolean mNewSurfaceNeeded;
|
||||||
@@ -607,6 +630,7 @@ public final class ViewRootImpl implements ViewParent,
|
|||||||
mTransparentRegion = new Region();
|
mTransparentRegion = new Region();
|
||||||
mPreviousTransparentRegion = new Region();
|
mPreviousTransparentRegion = new Region();
|
||||||
mFirst = true; // true for the first time the view is added
|
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;
|
mAdded = false;
|
||||||
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
|
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
|
||||||
context);
|
context);
|
||||||
@@ -2756,9 +2780,55 @@ public final class ViewRootImpl implements ViewParent,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mAttachInfo.mContentCaptureEvents != null) {
|
||||||
|
notifyContentCatpureEvents();
|
||||||
|
}
|
||||||
|
|
||||||
mIsInTraversal = false;
|
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() {
|
private void notifySurfaceDestroyed() {
|
||||||
mSurfaceHolder.ungetCallbacks();
|
mSurfaceHolder.ungetCallbacks();
|
||||||
SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks();
|
SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks();
|
||||||
@@ -2893,6 +2963,13 @@ public final class ViewRootImpl implements ViewParent,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
|
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() {
|
private void fireAccessibilityFocusEventIfHasFocusedNode() {
|
||||||
@@ -3459,6 +3536,86 @@ public final class ViewRootImpl implements ViewParent,
|
|||||||
pendingDrawFinished();
|
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) {
|
private boolean draw(boolean fullRedrawNeeded) {
|
||||||
|
|||||||
@@ -413,6 +413,9 @@ public class WebView extends AbsoluteLayout
|
|||||||
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
|
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
|
||||||
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
|
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
|
||||||
}
|
}
|
||||||
|
if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
|
||||||
|
setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES);
|
||||||
|
}
|
||||||
|
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
throw new IllegalArgumentException("Invalid context argument");
|
throw new IllegalArgumentException("Invalid context argument");
|
||||||
@@ -2795,6 +2798,12 @@ public class WebView extends AbsoluteLayout
|
|||||||
mProvider.getViewDelegate().onProvideAutofillVirtualStructure(structure, flags);
|
mProvider.getViewDelegate().onProvideAutofillVirtualStructure(structure, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@Override
|
||||||
|
public void onProvideContentCaptureStructure(ViewStructure structure, int flags) {
|
||||||
|
mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void autofill(SparseArray<AutofillValue>values) {
|
public void autofill(SparseArray<AutofillValue>values) {
|
||||||
mProvider.getViewDelegate().autofill(values);
|
mProvider.getViewDelegate().autofill(values);
|
||||||
|
|||||||
@@ -1318,7 +1318,8 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
|
|||||||
@ViewStructureType int viewFor, int flags) {
|
@ViewStructureType int viewFor, int flags) {
|
||||||
super.onProvideStructure(structure, viewFor, flags);
|
super.onProvideStructure(structure, viewFor, flags);
|
||||||
|
|
||||||
if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
|
if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
|
||||||
|
|| viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
|
||||||
final Adapter adapter = getAdapter();
|
final Adapter adapter = getAdapter();
|
||||||
if (adapter == null) return;
|
if (adapter == null) return;
|
||||||
|
|
||||||
|
|||||||
@@ -162,6 +162,8 @@ import android.view.accessibility.AccessibilityNodeInfo;
|
|||||||
import android.view.animation.AnimationUtils;
|
import android.view.animation.AnimationUtils;
|
||||||
import android.view.autofill.AutofillManager;
|
import android.view.autofill.AutofillManager;
|
||||||
import android.view.autofill.AutofillValue;
|
import android.view.autofill.AutofillValue;
|
||||||
|
import android.view.contentcapture.ContentCaptureManager;
|
||||||
|
import android.view.contentcapture.ContentCaptureSession;
|
||||||
import android.view.inputmethod.BaseInputConnection;
|
import android.view.inputmethod.BaseInputConnection;
|
||||||
import android.view.inputmethod.CompletionInfo;
|
import android.view.inputmethod.CompletionInfo;
|
||||||
import android.view.inputmethod.CorrectionInfo;
|
import android.view.inputmethod.CorrectionInfo;
|
||||||
@@ -977,6 +979,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
|
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
|
||||||
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
|
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
|
||||||
}
|
}
|
||||||
|
if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
|
||||||
|
setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES);
|
||||||
|
}
|
||||||
|
|
||||||
setTextInternal("");
|
setTextInternal("");
|
||||||
|
|
||||||
@@ -10550,7 +10555,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify managers (such as {@link AutofillManager}) that are interested in text changes.
|
* Notify managers (such as {@link AutofillManager} and {@link ContentCaptureManager}) that are
|
||||||
|
* interested on text changes.
|
||||||
*/
|
*/
|
||||||
private void notifyListeningManagersAfterTextChanged() {
|
private void notifyListeningManagersAfterTextChanged() {
|
||||||
|
|
||||||
@@ -10566,6 +10572,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
afm.notifyValueChanged(TextView.this);
|
afm.notifyValueChanged(TextView.this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(b/121045053): should use a flag / boolean to keep status of SHOWN / HIDDEN instead
|
||||||
|
// of using isLaidout(), so it's not called in cases where it's laid out but a
|
||||||
|
// notifyAppeared was not sent.
|
||||||
|
|
||||||
|
// ContentCapture
|
||||||
|
if (isLaidOut() && isImportantForContentCapture() && isTextEditable()) {
|
||||||
|
final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
|
||||||
|
if (cm != null && cm.isContentCaptureEnabled()) {
|
||||||
|
final ContentCaptureSession session = getContentCaptureSession();
|
||||||
|
if (session != null) {
|
||||||
|
// TODO(b/111276913): pass flags when edited by user / add CTS test
|
||||||
|
session.notifyViewTextChanged(getAutofillId(), getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAutofillable() {
|
private boolean isAutofillable() {
|
||||||
@@ -11409,7 +11431,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
|
|
||||||
final boolean isPassword = hasPasswordTransformationMethod()
|
final boolean isPassword = hasPasswordTransformationMethod()
|
||||||
|| isPasswordInputType(getInputType());
|
|| isPasswordInputType(getInputType());
|
||||||
if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
|
if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
|
||||||
|
|| viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
|
||||||
if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
|
if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
|
||||||
structure.setDataIsSensitive(!mTextSetFromXmlOrResourceId);
|
structure.setDataIsSensitive(!mTextSetFromXmlOrResourceId);
|
||||||
}
|
}
|
||||||
@@ -11425,8 +11448,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
|
if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
|
||||||
|
|| viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
|
||||||
if (mLayout == null) {
|
if (mLayout == null) {
|
||||||
|
if (viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
|
||||||
|
Log.w(LOG_TAG, "onProvideContentCaptureStructure(): calling assumeLayout()");
|
||||||
|
}
|
||||||
assumeLayout();
|
assumeLayout();
|
||||||
}
|
}
|
||||||
Layout layout = mLayout;
|
Layout layout = mLayout;
|
||||||
@@ -11514,7 +11541,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewFor == VIEW_STRUCTURE_FOR_ASSIST) {
|
if (viewFor == VIEW_STRUCTURE_FOR_ASSIST
|
||||||
|
|| viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
|
||||||
// Extract style information that applies to the TextView as a whole.
|
// Extract style information that applies to the TextView as a whole.
|
||||||
int style = 0;
|
int style = 0;
|
||||||
int typefaceStyle = getTypefaceStyle();
|
int typefaceStyle = getTypefaceStyle();
|
||||||
@@ -11542,7 +11570,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
structure.setTextStyle(getTextSize(), getCurrentTextColor(),
|
structure.setTextStyle(getTextSize(), getCurrentTextColor(),
|
||||||
AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style);
|
AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style);
|
||||||
}
|
}
|
||||||
if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
|
if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
|
||||||
|
|| viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
|
||||||
structure.setMinTextEms(getMinEms());
|
structure.setMinTextEms(getMinEms());
|
||||||
structure.setMaxTextEms(getMaxEms());
|
structure.setMaxTextEms(getMaxEms());
|
||||||
int maxLength = -1;
|
int maxLength = -1;
|
||||||
|
|||||||
Reference in New Issue
Block a user