package android.app.assist; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.content.ComponentName; import android.graphics.Matrix; import android.graphics.Rect; import android.net.Uri; import android.os.BadParcelableException; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; import android.os.PooledStringReader; import android.os.PooledStringWriter; import android.os.RemoteException; import android.os.SystemClock; import android.service.autofill.FillRequest; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.view.View; import android.view.ViewRootImpl; import android.view.ViewStructure; import android.view.ViewStructure.HtmlInfo; import android.view.ViewStructure.HtmlInfo.Builder; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Assist data automatically created by the platform's implementation of assist and autofill. * *
The structure is used for assist purposes when created by * {@link android.app.Activity#onProvideAssistData}, {@link View#onProvideStructure(ViewStructure)}, * or {@link View#onProvideVirtualStructure(ViewStructure)}. * *
The structure is used for autofill purposes when created by * {@link View#onProvideAutofillStructure(ViewStructure, int)}, * or {@link View#onProvideAutofillVirtualStructure(ViewStructure, int)}. * *
For performance reasons, some properties of the assist data might be available just for assist
* or autofill purposes; in those case, the property availability will be document in its javadoc.
*/
public class AssistStructure implements Parcelable {
static final String TAG = "AssistStructure";
static final boolean DEBUG_PARCEL = false;
static final boolean DEBUG_PARCEL_CHILDREN = false;
static final boolean DEBUG_PARCEL_TREE = false;
static final int VALIDATE_WINDOW_TOKEN = 0x11111111;
static final int VALIDATE_VIEW_TOKEN = 0x22222222;
boolean mHaveData;
ComponentName mActivityComponent;
private boolean mIsHomeActivity;
private int mFlags;
final ArrayList It's only relevant when the {@link AssistStructure} is used for autofill purposes.
*
* @return autofill type as defined by {@link View#getAutofillType()},
* or {@link View#AUTOFILL_TYPE_NONE} if the structure was created for assist purposes.
*/
public @View.AutofillType int getAutofillType() {
return mAutofillType;
}
/**
* Describes the content of a view so that a autofill service can fill in the appropriate
* data.
*
* It's only relevant when the {@link AssistStructure} is used for autofill purposes,
* not for Assist - see {@link View#getAutofillHints()} for more info.
*
* @return The autofill hints for this view, or {@code null} if the structure was created
* for assist purposes.
*/
@Nullable public String[] getAutofillHints() {
return mAutofillHints;
}
/**
* Gets the the value of this view.
*
* It's only relevant when the {@link AssistStructure} is used for autofill purposes,
* not for assist purposes.
*
* @return the autofill value of this view, or {@code null} if the structure was created
* for assist purposes.
*/
@Nullable public AutofillValue getAutofillValue() {
return mAutofillValue;
}
/** @hide **/
public void setAutofillOverlay(AutofillOverlay overlay) {
mAutofillOverlay = overlay;
}
/**
* Gets the options that can be used to autofill this view.
*
* Typically used by nodes whose {@link View#getAutofillType()} is a list to indicate
* the meaning of each possible value in the list.
*
* It's relevant when the {@link AssistStructure} is used for autofill purposes, not
* for assist purposes.
*
* @return the options that can be used to autofill this view, or {@code null} if the
* structure was created for assist purposes.
*/
@Nullable public CharSequence[] getAutofillOptions() {
return mAutofillOptions;
}
/**
* Gets the {@link android.text.InputType} bits of this structure.
*
* @return bits as defined by {@link android.text.InputType}.
*/
public int getInputType() {
return mInputType;
}
/** @hide */
public boolean isSanitized() {
return mSanitized;
}
/**
* Updates the {@link AutofillValue} of this structure.
*
* Should be used just before sending the structure to the
* {@link android.service.autofill.AutofillService} for saving, since it will override the
* initial value.
*
* @hide
*/
public void updateAutofillValue(AutofillValue value) {
mAutofillValue = value;
if (value.isText()) {
if (mText == null) {
mText = new ViewNodeText();
}
mText.mText = value.getTextValue();
}
}
/**
* Returns the left edge of this view, in pixels, relative to the left edge of its parent.
*/
public int getLeft() {
return mX;
}
/**
* Returns the top edge of this view, in pixels, relative to the top edge of its parent.
*/
public int getTop() {
return mY;
}
/**
* Returns the current X scroll offset of this view, as per
* {@link android.view.View#getScrollX() View.getScrollX()}.
*/
public int getScrollX() {
return mScrollX;
}
/**
* Returns the current Y scroll offset of this view, as per
* {@link android.view.View#getScrollX() View.getScrollY()}.
*/
public int getScrollY() {
return mScrollY;
}
/**
* Returns the width of this view, in pixels.
*/
public int getWidth() {
return mWidth;
}
/**
* Returns the height of this view, in pixels.
*/
public int getHeight() {
return mHeight;
}
/**
* Returns the transformation that has been applied to this view, such as a translation
* or scaling. The returned Matrix object is owned by ViewNode; do not modify it.
* Returns null if there is no transformation applied to the view.
*
* It's only relevant when the {@link AssistStructure} is used for assist purposes,
* not for autofill purposes.
*/
public Matrix getTransformation() {
return mMatrix;
}
/**
* Returns the visual elevation of the view, used for shadowing and other visual
* characterstics, as set by {@link ViewStructure#setElevation
* ViewStructure.setElevation(float)}.
*
* It's only relevant when the {@link AssistStructure} is used for assist purposes,
* not for autofill purposes.
*/
public float getElevation() {
return mElevation;
}
/**
* Returns the alpha transformation of the view, used to reduce the overall opacity
* of the view's contents, as set by {@link ViewStructure#setAlpha
* ViewStructure.setAlpha(float)}.
*
* It's only relevant when the {@link AssistStructure} is used for assist purposes,
* not for autofill purposes.
*/
public float getAlpha() {
return mAlpha;
}
/**
* Returns the visibility mode of this view, as per
* {@link android.view.View#getVisibility() View.getVisibility()}.
*/
public int getVisibility() {
return mFlags&ViewNode.FLAGS_VISIBILITY_MASK;
}
/**
* Returns true if assist data has been blocked starting at this node in the hierarchy.
*/
public boolean isAssistBlocked() {
return (mFlags&ViewNode.FLAGS_ASSIST_BLOCKED) != 0;
}
/**
* Returns true if this node is in an enabled state.
*/
public boolean isEnabled() {
return (mFlags&ViewNode.FLAGS_DISABLED) == 0;
}
/**
* Returns true if this node is clickable by the user.
*/
public boolean isClickable() {
return (mFlags&ViewNode.FLAGS_CLICKABLE) != 0;
}
/**
* Returns true if this node can take input focus.
*/
public boolean isFocusable() {
return (mFlags&ViewNode.FLAGS_FOCUSABLE) != 0;
}
/**
* Returns true if this node currently had input focus at the time that the
* structure was collected.
*/
public boolean isFocused() {
return (mFlags&ViewNode.FLAGS_FOCUSED) != 0;
}
/**
* Returns true if this node currently had accessibility focus at the time that the
* structure was collected.
*/
public boolean isAccessibilityFocused() {
return (mFlags&ViewNode.FLAGS_ACCESSIBILITY_FOCUSED) != 0;
}
/**
* Returns true if this node represents something that is checkable by the user.
*/
public boolean isCheckable() {
return (mFlags&ViewNode.FLAGS_CHECKABLE) != 0;
}
/**
* Returns true if this node is currently in a checked state.
*/
public boolean isChecked() {
return (mFlags&ViewNode.FLAGS_CHECKED) != 0;
}
/**
* Returns true if this node has currently been selected by the user.
*/
public boolean isSelected() {
return (mFlags&ViewNode.FLAGS_SELECTED) != 0;
}
/**
* Returns true if this node has currently been activated by the user.
*/
public boolean isActivated() {
return (mFlags&ViewNode.FLAGS_ACTIVATED) != 0;
}
/**
* Returns true if this node is opaque.
*/
public boolean isOpaque() { return (mFlags&ViewNode.FLAGS_OPAQUE) != 0; }
/**
* Returns true if this node is something the user can perform a long click/press on.
*/
public boolean isLongClickable() {
return (mFlags&ViewNode.FLAGS_LONG_CLICKABLE) != 0;
}
/**
* Returns true if this node is something the user can perform a context click on.
*/
public boolean isContextClickable() {
return (mFlags&ViewNode.FLAGS_CONTEXT_CLICKABLE) != 0;
}
/**
* Returns the class name of the node's implementation, indicating its behavior.
* For example, a button will report "android.widget.Button" meaning it behaves
* like a {@link android.widget.Button}.
*/
public String getClassName() {
return mClassName;
}
/**
* Returns any content description associated with the node, which semantically describes
* its purpose for accessibility and other uses.
*/
public CharSequence getContentDescription() {
return mContentDescription;
}
/**
* Returns the domain of the HTML document represented by this view.
*
* Typically used when the view associated with the view is a container for an HTML
* document.
*
* WARNING: a {@link android.service.autofill.AutofillService} should only
* use this domain for autofill purposes when it trusts the app generating it (i.e., the app
* defined by {@link AssistStructure#getActivityComponent()}).
*
* @return domain-only part of the document. For example, if the full URL is
* {@code http://my.site/login?user=my_user}, it returns {@code my.site}.
*/
@Nullable public String getWebDomain() {
return mWebDomain;
}
/**
* Returns the HTML properties associated with this view.
*
* It's only relevant when the {@link AssistStructure} is used for autofill purposes,
* not for assist purposes.
*
* @return the HTML properties associated with this view, or {@code null} if the
* structure was created for assist purposes.
*/
@Nullable public HtmlInfo getHtmlInfo() {
return mHtmlInfo;
}
/**
* Returns the the list of locales associated with this view.
*/
@Nullable public LocaleList getLocaleList() {
return mLocaleList;
}
/**
* Returns any text associated with the node that is displayed to the user, or null
* if there is none.
*/
public CharSequence getText() {
return mText != null ? mText.mText : null;
}
/**
* If {@link #getText()} is non-null, this is where the current selection starts.
*
* It's only relevant when the {@link AssistStructure} is used for assist purposes,
* not for autofill purposes.
*/
public int getTextSelectionStart() {
return mText != null ? mText.mTextSelectionStart : -1;
}
/**
* If {@link #getText()} is non-null, this is where the current selection starts.
* If there is no selection, returns the same value as {@link #getTextSelectionStart()},
* indicating the cursor position.
*
* It's only relevant when the {@link AssistStructure} is used for assist purposes,
* not for autofill purposes.
*/
public int getTextSelectionEnd() {
return mText != null ? mText.mTextSelectionEnd : -1;
}
/**
* If {@link #getText()} is non-null, this is the main text color associated with it.
* If there is no text color, {@link #TEXT_COLOR_UNDEFINED} is returned.
* Note that the text may also contain style spans that modify the color of specific
* parts of the text.
*/
public int getTextColor() {
return mText != null ? mText.mTextColor : TEXT_COLOR_UNDEFINED;
}
/**
* If {@link #getText()} is non-null, this is the main text background color associated
* with it.
* If there is no text background color, {@link #TEXT_COLOR_UNDEFINED} is returned.
* Note that the text may also contain style spans that modify the color of specific
* parts of the text.
*
* It's only relevant when the {@link AssistStructure} is used for assist purposes,
* not for autofill purposes.
*/
public int getTextBackgroundColor() {
return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
}
/**
* If {@link #getText()} is non-null, this is the main text size (in pixels) associated
* with it.
* Note that the text may also contain style spans that modify the size of specific
* parts of the text.
*
* It's only relevant when the {@link AssistStructure} is used for assist purposes,
* not for autofill purposes.
*/
public float getTextSize() {
return mText != null ? mText.mTextSize : 0;
}
/**
* If {@link #getText()} is non-null, this is the main text style associated
* with it, containing a bit mask of {@link #TEXT_STYLE_BOLD},
* {@link #TEXT_STYLE_BOLD}, {@link #TEXT_STYLE_STRIKE_THRU}, and/or
* {@link #TEXT_STYLE_UNDERLINE}.
* Note that the text may also contain style spans that modify the style of specific
* parts of the text.
*
* It's only relevant when the {@link AssistStructure} is used for assist purposes,
* not for autofill purposes.
*/
public int getTextStyle() {
return mText != null ? mText.mTextStyle : 0;
}
/**
* Return per-line offsets into the text returned by {@link #getText()}. Each entry
* in the array is a formatted line of text, and the value it contains is the offset
* into the text string where that line starts. May return null if there is no line
* information.
*
* It's only relevant when the {@link AssistStructure} is used for assist purposes,
* not for autofill purposes.
*/
public int[] getTextLineCharOffsets() {
return mText != null ? mText.mLineCharOffsets : null;
}
/**
* Return per-line baselines into the text returned by {@link #getText()}. Each entry
* in the array is a formatted line of text, and the value it contains is the baseline
* where that text appears in the view. May return null if there is no line
* information.
*
* It's only relevant when the {@link AssistStructure} is used for assist purposes,
* not for autofill purposes.
*/
public int[] getTextLineBaselines() {
return mText != null ? mText.mLineBaselines : null;
}
/**
* Return additional hint text associated with the node; this is typically used with
* a node that takes user input, describing to the user what the input means.
*/
public String getHint() {
return mText != null ? mText.mHint : null;
}
/**
* Return a Bundle containing optional vendor-specific extension information.
*/
public Bundle getExtras() {
return mExtras;
}
/**
* Return the number of children this node has.
*/
public int getChildCount() {
return mChildren != null ? mChildren.length : 0;
}
/**
* Return a child of this node, given an index value from 0 to
* {@link #getChildCount()}-1.
*/
public ViewNode getChildAt(int index) {
return mChildren[index];
}
}
/**
* POJO used to override some autofill-related values when the node is parcelized.
*
* @hide
*/
static public class AutofillOverlay {
public boolean focused;
public AutofillValue value;
}
static class ViewNodeBuilder extends ViewStructure {
final AssistStructure mAssist;
final ViewNode mNode;
final boolean mAsync;
ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) {
mAssist = assist;
mNode = node;
mAsync = async;
}
@Override
public void setId(int id, String packageName, String typeName, String entryName) {
mNode.mId = id;
mNode.mIdPackage = packageName;
mNode.mIdType = typeName;
mNode.mIdEntry = entryName;
}
@Override
public void setDimens(int left, int top, int scrollX, int scrollY, int width, int height) {
mNode.mX = left;
mNode.mY = top;
mNode.mScrollX = scrollX;
mNode.mScrollY = scrollY;
mNode.mWidth = width;
mNode.mHeight = height;
}
@Override
public void setTransformation(Matrix matrix) {
if (matrix == null) {
mNode.mMatrix = null;
} else {
mNode.mMatrix = new Matrix(matrix);
}
}
@Override
public void setElevation(float elevation) {
mNode.mElevation = elevation;
}
@Override
public void setAlpha(float alpha) {
mNode.mAlpha = alpha;
}
@Override
public void setVisibility(int visibility) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_VISIBILITY_MASK) | visibility;
}
@Override
public void setAssistBlocked(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ASSIST_BLOCKED)
| (state ? ViewNode.FLAGS_ASSIST_BLOCKED : 0);
}
@Override
public void setEnabled(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_DISABLED)
| (state ? 0 : ViewNode.FLAGS_DISABLED);
}
@Override
public void setClickable(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CLICKABLE)
| (state ? ViewNode.FLAGS_CLICKABLE : 0);
}
@Override
public void setLongClickable(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_LONG_CLICKABLE)
| (state ? ViewNode.FLAGS_LONG_CLICKABLE : 0);
}
@Override
public void setContextClickable(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CONTEXT_CLICKABLE)
| (state ? ViewNode.FLAGS_CONTEXT_CLICKABLE : 0);
}
@Override
public void setFocusable(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSABLE)
| (state ? ViewNode.FLAGS_FOCUSABLE : 0);
}
@Override
public void setFocused(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSED)
| (state ? ViewNode.FLAGS_FOCUSED : 0);
}
@Override
public void setAccessibilityFocused(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACCESSIBILITY_FOCUSED)
| (state ? ViewNode.FLAGS_ACCESSIBILITY_FOCUSED : 0);
}
@Override
public void setCheckable(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKABLE)
| (state ? ViewNode.FLAGS_CHECKABLE : 0);
}
@Override
public void setChecked(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKED)
| (state ? ViewNode.FLAGS_CHECKED : 0);
}
@Override
public void setSelected(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_SELECTED)
| (state ? ViewNode.FLAGS_SELECTED : 0);
}
@Override
public void setActivated(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACTIVATED)
| (state ? ViewNode.FLAGS_ACTIVATED : 0);
}
@Override
public void setOpaque(boolean opaque) {
mNode.mFlags = (mNode.mFlags & ~ViewNode.FLAGS_OPAQUE)
| (opaque ? ViewNode.FLAGS_OPAQUE : 0);
}
@Override
public void setClassName(String className) {
mNode.mClassName = className;
}
@Override
public void setContentDescription(CharSequence contentDescription) {
mNode.mContentDescription = contentDescription;
}
private final ViewNodeText getNodeText() {
if (mNode.mText != null) {
return mNode.mText;
}
mNode.mText = new ViewNodeText();
return mNode.mText;
}
@Override
public void setText(CharSequence text) {
ViewNodeText t = getNodeText();
t.mText = TextUtils.trimNoCopySpans(text);
t.mTextSelectionStart = t.mTextSelectionEnd = -1;
}
@Override
public void setText(CharSequence text, int selectionStart, int selectionEnd) {
ViewNodeText t = getNodeText();
t.mText = TextUtils.trimNoCopySpans(text);
t.mTextSelectionStart = selectionStart;
t.mTextSelectionEnd = selectionEnd;
}
@Override
public void setTextStyle(float size, int fgColor, int bgColor, int style) {
ViewNodeText t = getNodeText();
t.mTextColor = fgColor;
t.mTextBackgroundColor = bgColor;
t.mTextSize = size;
t.mTextStyle = style;
}
@Override
public void setTextLines(int[] charOffsets, int[] baselines) {
ViewNodeText t = getNodeText();
t.mLineCharOffsets = charOffsets;
t.mLineBaselines = baselines;
}
@Override
public void setHint(CharSequence hint) {
getNodeText().mHint = hint != null ? hint.toString() : null;
}
@Override
public CharSequence getText() {
return mNode.mText != null ? mNode.mText.mText : null;
}
@Override
public int getTextSelectionStart() {
return mNode.mText != null ? mNode.mText.mTextSelectionStart : -1;
}
@Override
public int getTextSelectionEnd() {
return mNode.mText != null ? mNode.mText.mTextSelectionEnd : -1;
}
@Override
public CharSequence getHint() {
return mNode.mText != null ? mNode.mText.mHint : null;
}
@Override
public Bundle getExtras() {
if (mNode.mExtras != null) {
return mNode.mExtras;
}
mNode.mExtras = new Bundle();
return mNode.mExtras;
}
@Override
public boolean hasExtras() {
return mNode.mExtras != null;
}
@Override
public void setChildCount(int num) {
mNode.mChildren = new ViewNode[num];
}
@Override
public int addChildCount(int num) {
if (mNode.mChildren == null) {
setChildCount(num);
return 0;
}
final int start = mNode.mChildren.length;
ViewNode[] newArray = new ViewNode[start + num];
System.arraycopy(mNode.mChildren, 0, newArray, 0, start);
mNode.mChildren = newArray;
return start;
}
@Override
public int getChildCount() {
return mNode.mChildren != null ? mNode.mChildren.length : 0;
}
@Override
public ViewStructure newChild(int index) {
ViewNode node = new ViewNode();
mNode.mChildren[index] = node;
return new ViewNodeBuilder(mAssist, node, false);
}
@Override
public ViewStructure asyncNewChild(int index) {
synchronized (mAssist) {
ViewNode node = new ViewNode();
mNode.mChildren[index] = node;
ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true);
mAssist.mPendingAsyncChildren.add(builder);
return builder;
}
}
@Override
public void asyncCommit() {
synchronized (mAssist) {
if (!mAsync) {
throw new IllegalStateException("Child " + this
+ " was not created with ViewStructure.asyncNewChild");
}
if (!mAssist.mPendingAsyncChildren.remove(this)) {
throw new IllegalStateException("Child " + this + " already committed");
}
mAssist.notifyAll();
}
}
@Override
public Rect getTempRect() {
return mAssist.mTmpRect;
}
@Override
public void setAutofillId(@NonNull AutofillId id) {
mNode.mAutofillId = id;
}
@Override
public void setAutofillId(@NonNull AutofillId parentId, int virtualId) {
mNode.mAutofillId = new AutofillId(parentId, virtualId);
}
@Override
public AutofillId getAutofillId() {
return mNode.mAutofillId;
}
@Override
public void setAutofillType(@View.AutofillType int type) {
mNode.mAutofillType = type;
}
@Override
public void setAutofillHints(@Nullable String[] hints) {
mNode.mAutofillHints = hints;
}
@Override
public void setAutofillValue(AutofillValue value) {
mNode.mAutofillValue = value;
}
@Override
public void setAutofillOptions(CharSequence[] options) {
mNode.mAutofillOptions = options;
}
@Override
public void setInputType(int inputType) {
mNode.mInputType = inputType;
}
@Override
public void setDataIsSensitive(boolean sensitive) {
mNode.mSanitized = !sensitive;
}
@Override
public void setWebDomain(@Nullable String domain) {
if (domain == null) {
mNode.mWebDomain = null;
return;
}
mNode.mWebDomain = Uri.parse(domain).getHost();
}
@Override
public void setLocaleList(LocaleList localeList) {
mNode.mLocaleList = localeList;
}
@Override
public HtmlInfo.Builder newHtmlInfoBuilder(@NonNull String tagName) {
return new HtmlInfoNodeBuilder(tagName);
}
@Override
public void setHtmlInfo(@NonNull HtmlInfo htmlInfo) {
mNode.mHtmlInfo = htmlInfo;
}
}
private static final class HtmlInfoNode extends HtmlInfo implements Parcelable {
private final String mTag;
private final String[] mNames;
private final String[] mValues;
// Not parcelable
private ArrayList