2860 lines
105 KiB
Java
2860 lines
105 KiB
Java
![]() |
package android.app.assist;
|
||
|
|
||
|
import static android.credentials.Constants.FAILURE_CREDMAN_SELECTOR;
|
||
|
import static android.credentials.Constants.SUCCESS_CREDMAN_SELECTOR;
|
||
|
import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION;
|
||
|
|
||
|
import android.annotation.FlaggedApi;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.annotation.SuppressLint;
|
||
|
import android.annotation.SystemApi;
|
||
|
import android.app.Activity;
|
||
|
import android.content.ComponentName;
|
||
|
import android.content.Context;
|
||
|
import android.credentials.CredentialOption;
|
||
|
import android.credentials.GetCredentialException;
|
||
|
import android.credentials.GetCredentialRequest;
|
||
|
import android.credentials.GetCredentialResponse;
|
||
|
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.Handler;
|
||
|
import android.os.IBinder;
|
||
|
import android.os.LocaleList;
|
||
|
import android.os.Looper;
|
||
|
import android.os.OutcomeReceiver;
|
||
|
import android.os.Parcel;
|
||
|
import android.os.Parcelable;
|
||
|
import android.os.PooledStringReader;
|
||
|
import android.os.PooledStringWriter;
|
||
|
import android.os.RemoteException;
|
||
|
import android.os.ResultReceiver;
|
||
|
import android.os.SystemClock;
|
||
|
import android.service.autofill.FillRequest;
|
||
|
import android.service.credentials.CredentialProviderService;
|
||
|
import android.text.InputType;
|
||
|
import android.text.Spanned;
|
||
|
import android.text.TextUtils;
|
||
|
import android.util.ArrayMap;
|
||
|
import android.util.Log;
|
||
|
import android.util.Pair;
|
||
|
import android.util.Slog;
|
||
|
import android.view.View;
|
||
|
import android.view.View.AutofillImportance;
|
||
|
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;
|
||
|
import java.util.Objects;
|
||
|
|
||
|
/**
|
||
|
* <p>This API automatically creates assist data from the platform's
|
||
|
* implementation of assist and autofill.
|
||
|
*
|
||
|
* <p>The structure is used for assist purposes when created by
|
||
|
* {@link android.app.Activity#onProvideAssistData}, {@link View#onProvideStructure(ViewStructure)},
|
||
|
* or {@link View#onProvideVirtualStructure(ViewStructure)}.
|
||
|
*
|
||
|
* <p>The structure is also used for autofill purposes when created by
|
||
|
* {@link View#onProvideAutofillStructure(ViewStructure, int)},
|
||
|
* or {@link View#onProvideAutofillVirtualStructure(ViewStructure, int)}.
|
||
|
*
|
||
|
* <p>For performance reasons, some properties of the assist data might only be available for
|
||
|
* assist or autofill purposes. In those cases, a property's availability will be documented
|
||
|
* in its javadoc.
|
||
|
*
|
||
|
* <p>To learn about using Autofill in your app, read the
|
||
|
* <a href="/guide/topics/text/autofill">Autofill Framework</a> guides.
|
||
|
*/
|
||
|
public class AssistStructure implements Parcelable {
|
||
|
private static final String TAG = "AssistStructure";
|
||
|
|
||
|
private static final boolean DEBUG_PARCEL = false;
|
||
|
private static final boolean DEBUG_PARCEL_CHILDREN = false;
|
||
|
private static final boolean DEBUG_PARCEL_TREE = false;
|
||
|
|
||
|
private static final int VALIDATE_WINDOW_TOKEN = 0x11111111;
|
||
|
private static final int VALIDATE_VIEW_TOKEN = 0x22222222;
|
||
|
|
||
|
private boolean mHaveData;
|
||
|
|
||
|
// The task id and component of the activity which this assist structure is for
|
||
|
private int mTaskId;
|
||
|
private ComponentName mActivityComponent;
|
||
|
private boolean mIsHomeActivity;
|
||
|
private int mFlags;
|
||
|
private int mAutofillFlags;
|
||
|
|
||
|
private final ArrayList<WindowNode> mWindowNodes = new ArrayList<>();
|
||
|
|
||
|
private final ArrayList<ViewNodeBuilder> mPendingAsyncChildren = new ArrayList<>();
|
||
|
|
||
|
private SendChannel mSendChannel;
|
||
|
private IBinder mReceiveChannel;
|
||
|
|
||
|
private Rect mTmpRect = new Rect();
|
||
|
|
||
|
private boolean mSanitizeOnWrite = false;
|
||
|
private long mAcquisitionStartTime;
|
||
|
private long mAcquisitionEndTime;
|
||
|
|
||
|
private static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1;
|
||
|
private static final String DESCRIPTOR = "android.app.AssistStructure";
|
||
|
|
||
|
/** @hide */
|
||
|
public void setAcquisitionStartTime(long acquisitionStartTime) {
|
||
|
mAcquisitionStartTime = acquisitionStartTime;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public void setAcquisitionEndTime(long acquisitionEndTime) {
|
||
|
mAcquisitionEndTime = acquisitionEndTime;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
* Set the home activity flag.
|
||
|
*/
|
||
|
public void setHomeActivity(boolean isHomeActivity) {
|
||
|
mIsHomeActivity = isHomeActivity;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the time when the activity started generating assist data to build the
|
||
|
* AssistStructure. The time is as specified by {@link SystemClock#uptimeMillis()}.
|
||
|
*
|
||
|
* @see #getAcquisitionEndTime()
|
||
|
* @return Returns the acquisition start time of the assist data, in milliseconds.
|
||
|
*/
|
||
|
public long getAcquisitionStartTime() {
|
||
|
ensureData();
|
||
|
return mAcquisitionStartTime;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the time when the activity finished generating assist data to build the
|
||
|
* AssistStructure. The time is as specified by {@link SystemClock#uptimeMillis()}.
|
||
|
*
|
||
|
* @see #getAcquisitionStartTime()
|
||
|
* @return Returns the acquisition end time of the assist data, in milliseconds.
|
||
|
*/
|
||
|
public long getAcquisitionEndTime() {
|
||
|
ensureData();
|
||
|
return mAcquisitionEndTime;
|
||
|
}
|
||
|
|
||
|
final static class SendChannel extends Binder {
|
||
|
volatile AssistStructure mAssistStructure;
|
||
|
|
||
|
SendChannel(AssistStructure as) {
|
||
|
mAssistStructure = as;
|
||
|
}
|
||
|
|
||
|
@Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
|
||
|
throws RemoteException {
|
||
|
if (code == TRANSACTION_XFER) {
|
||
|
AssistStructure as = mAssistStructure;
|
||
|
if (as == null) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
data.enforceInterface(DESCRIPTOR);
|
||
|
IBinder token = data.readStrongBinder();
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "Request for data on " + as
|
||
|
+ " using token " + token);
|
||
|
if (token != null) {
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "Resuming partial write of " + token);
|
||
|
if (token instanceof ParcelTransferWriter) {
|
||
|
ParcelTransferWriter xfer = (ParcelTransferWriter)token;
|
||
|
xfer.writeToParcel(as, reply);
|
||
|
return true;
|
||
|
}
|
||
|
Log.w(TAG, "Caller supplied bad token type: " + token);
|
||
|
// Don't write anything; this is the end of the data.
|
||
|
return true;
|
||
|
}
|
||
|
//long start = SystemClock.uptimeMillis();
|
||
|
ParcelTransferWriter xfer = new ParcelTransferWriter(as, reply);
|
||
|
xfer.writeToParcel(as, reply);
|
||
|
//Log.i(TAG, "Time to parcel: " + (SystemClock.uptimeMillis()-start) + "ms");
|
||
|
return true;
|
||
|
} else {
|
||
|
return super.onTransact(code, data, reply, flags);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
final static class ViewStackEntry {
|
||
|
ViewNode node;
|
||
|
int curChild;
|
||
|
int numChildren;
|
||
|
}
|
||
|
|
||
|
final static class ParcelTransferWriter extends Binder {
|
||
|
final boolean mWriteStructure;
|
||
|
int mCurWindow;
|
||
|
int mNumWindows;
|
||
|
final ArrayList<ViewStackEntry> mViewStack = new ArrayList<>();
|
||
|
ViewStackEntry mCurViewStackEntry;
|
||
|
int mCurViewStackPos;
|
||
|
int mNumWrittenWindows;
|
||
|
int mNumWrittenViews;
|
||
|
final float[] mTmpMatrix = new float[9];
|
||
|
final boolean mSanitizeOnWrite;
|
||
|
|
||
|
ParcelTransferWriter(AssistStructure as, Parcel out) {
|
||
|
mSanitizeOnWrite = as.mSanitizeOnWrite;
|
||
|
mWriteStructure = as.waitForReady();
|
||
|
out.writeInt(as.mFlags);
|
||
|
out.writeInt(as.mAutofillFlags);
|
||
|
out.writeLong(as.mAcquisitionStartTime);
|
||
|
out.writeLong(as.mAcquisitionEndTime);
|
||
|
mNumWindows = as.mWindowNodes.size();
|
||
|
if (mWriteStructure && mNumWindows > 0) {
|
||
|
out.writeInt(mNumWindows);
|
||
|
} else {
|
||
|
out.writeInt(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void writeToParcel(AssistStructure as, Parcel out) {
|
||
|
int start = out.dataPosition();
|
||
|
mNumWrittenWindows = 0;
|
||
|
mNumWrittenViews = 0;
|
||
|
boolean more = writeToParcelInner(as, out);
|
||
|
Log.i(TAG, "Flattened " + (more ? "partial" : "final") + " assist data: "
|
||
|
+ (out.dataPosition() - start)
|
||
|
+ " bytes, containing " + mNumWrittenWindows + " windows, "
|
||
|
+ mNumWrittenViews + " views");
|
||
|
}
|
||
|
|
||
|
boolean writeToParcelInner(AssistStructure as, Parcel out) {
|
||
|
if (mNumWindows == 0) {
|
||
|
return false;
|
||
|
}
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringWriter @ " + out.dataPosition());
|
||
|
PooledStringWriter pwriter = new PooledStringWriter(out);
|
||
|
while (writeNextEntryToParcel(as, out, pwriter)) {
|
||
|
// If the parcel is above the IPC limit, then we are getting too
|
||
|
// large for a single IPC so stop here and let the caller come back when it
|
||
|
// is ready for more.
|
||
|
if (out.dataSize() > IBinder.MAX_IPC_SIZE) {
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "Assist data size is " + out.dataSize()
|
||
|
+ " @ pos " + out.dataPosition() + "; returning partial result");
|
||
|
out.writeInt(0);
|
||
|
out.writeStrongBinder(this);
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "Finishing PooledStringWriter @ "
|
||
|
+ out.dataPosition() + ", size " + pwriter.getStringCount());
|
||
|
pwriter.finish();
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "Finishing PooledStringWriter @ "
|
||
|
+ out.dataPosition() + ", size " + pwriter.getStringCount());
|
||
|
pwriter.finish();
|
||
|
mViewStack.clear();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void pushViewStackEntry(ViewNode node, int pos) {
|
||
|
ViewStackEntry entry;
|
||
|
if (pos >= mViewStack.size()) {
|
||
|
entry = new ViewStackEntry();
|
||
|
mViewStack.add(entry);
|
||
|
if (DEBUG_PARCEL_TREE) Log.d(TAG, "New stack entry at " + pos + ": " + entry);
|
||
|
} else {
|
||
|
entry = mViewStack.get(pos);
|
||
|
if (DEBUG_PARCEL_TREE) Log.d(TAG, "Existing stack entry at " + pos + ": " + entry);
|
||
|
}
|
||
|
entry.node = node;
|
||
|
entry.numChildren = node.getChildCount();
|
||
|
entry.curChild = 0;
|
||
|
mCurViewStackEntry = entry;
|
||
|
}
|
||
|
|
||
|
void writeView(ViewNode child, Parcel out, PooledStringWriter pwriter, int levelAdj) {
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "write view: at " + out.dataPosition()
|
||
|
+ ", windows=" + mNumWrittenWindows
|
||
|
+ ", views=" + mNumWrittenViews
|
||
|
+ ", level=" + (mCurViewStackPos+levelAdj));
|
||
|
out.writeInt(VALIDATE_VIEW_TOKEN);
|
||
|
int flags = child.writeSelfToParcel(out, pwriter, mSanitizeOnWrite,
|
||
|
mTmpMatrix, /*willWriteChildren=*/true);
|
||
|
mNumWrittenViews++;
|
||
|
// If the child has children, push it on the stack to write them next.
|
||
|
if ((flags&ViewNode.FLAGS_HAS_CHILDREN) != 0) {
|
||
|
if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG,
|
||
|
"Preparing to write " + child.mChildren.length
|
||
|
+ " children: @ #" + mNumWrittenViews
|
||
|
+ ", level " + (mCurViewStackPos+levelAdj));
|
||
|
out.writeInt(child.mChildren.length);
|
||
|
int pos = ++mCurViewStackPos;
|
||
|
pushViewStackEntry(child, pos);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
boolean writeNextEntryToParcel(AssistStructure as, Parcel out, PooledStringWriter pwriter) {
|
||
|
// Write next view node if appropriate.
|
||
|
if (mCurViewStackEntry != null) {
|
||
|
if (mCurViewStackEntry.curChild < mCurViewStackEntry.numChildren) {
|
||
|
// Write the next child in the current view.
|
||
|
if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing child #"
|
||
|
+ mCurViewStackEntry.curChild + " in " + mCurViewStackEntry.node);
|
||
|
ViewNode child = mCurViewStackEntry.node.mChildren[mCurViewStackEntry.curChild];
|
||
|
mCurViewStackEntry.curChild++;
|
||
|
writeView(child, out, pwriter, 1);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// We are done writing children of the current view; pop off the stack.
|
||
|
do {
|
||
|
int pos = --mCurViewStackPos;
|
||
|
if (DEBUG_PARCEL_TREE) Log.d(TAG, "Done with " + mCurViewStackEntry.node
|
||
|
+ "; popping up to " + pos);
|
||
|
if (pos < 0) {
|
||
|
// Reached the last view; step to next window.
|
||
|
if (DEBUG_PARCEL_TREE) Log.d(TAG, "Done with view hierarchy!");
|
||
|
mCurViewStackEntry = null;
|
||
|
break;
|
||
|
}
|
||
|
mCurViewStackEntry = mViewStack.get(pos);
|
||
|
} while (mCurViewStackEntry.curChild >= mCurViewStackEntry.numChildren);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Write the next window if appropriate.
|
||
|
int pos = mCurWindow;
|
||
|
if (pos < mNumWindows) {
|
||
|
WindowNode win = as.mWindowNodes.get(pos);
|
||
|
mCurWindow++;
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "write window #" + pos + ": at " + out.dataPosition()
|
||
|
+ ", windows=" + mNumWrittenWindows
|
||
|
+ ", views=" + mNumWrittenViews);
|
||
|
out.writeInt(VALIDATE_WINDOW_TOKEN);
|
||
|
win.writeSelfToParcel(out, pwriter, mTmpMatrix);
|
||
|
mNumWrittenWindows++;
|
||
|
ViewNode root = win.mRoot;
|
||
|
mCurViewStackPos = 0;
|
||
|
if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing initial root view " + root);
|
||
|
writeView(root, out, pwriter, 0);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
final class ParcelTransferReader {
|
||
|
final float[] mTmpMatrix = new float[9];
|
||
|
PooledStringReader mStringReader;
|
||
|
|
||
|
int mNumReadWindows;
|
||
|
int mNumReadViews;
|
||
|
|
||
|
private final IBinder mChannel;
|
||
|
private IBinder mTransferToken;
|
||
|
private Parcel mCurParcel;
|
||
|
|
||
|
ParcelTransferReader(IBinder channel) {
|
||
|
mChannel = channel;
|
||
|
}
|
||
|
|
||
|
void go() {
|
||
|
fetchData();
|
||
|
mFlags = mCurParcel.readInt();
|
||
|
mAutofillFlags = mCurParcel.readInt();
|
||
|
mAcquisitionStartTime = mCurParcel.readLong();
|
||
|
mAcquisitionEndTime = mCurParcel.readLong();
|
||
|
final int N = mCurParcel.readInt();
|
||
|
if (N > 0) {
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringReader @ "
|
||
|
+ mCurParcel.dataPosition());
|
||
|
mStringReader = new PooledStringReader(mCurParcel);
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "PooledStringReader size = "
|
||
|
+ mStringReader.getStringCount());
|
||
|
for (int i=0; i<N; i++) {
|
||
|
mWindowNodes.add(new WindowNode(this));
|
||
|
}
|
||
|
}
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "Finished reading: at " + mCurParcel.dataPosition()
|
||
|
+ ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
|
||
|
+ ", views=" + mNumReadViews);
|
||
|
mCurParcel.recycle();
|
||
|
mCurParcel = null; // Parcel cannot be used after recycled.
|
||
|
}
|
||
|
|
||
|
Parcel readParcel(int validateToken, int level) {
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "readParcel: at " + mCurParcel.dataPosition()
|
||
|
+ ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
|
||
|
+ ", views=" + mNumReadViews + ", level=" + level);
|
||
|
int token = mCurParcel.readInt();
|
||
|
if (token != 0) {
|
||
|
if (token != validateToken) {
|
||
|
throw new BadParcelableException("Got token " + Integer.toHexString(token)
|
||
|
+ ", expected token " + Integer.toHexString(validateToken));
|
||
|
}
|
||
|
return mCurParcel;
|
||
|
}
|
||
|
// We have run out of partial data, need to read another batch.
|
||
|
mTransferToken = mCurParcel.readStrongBinder();
|
||
|
if (mTransferToken == null) {
|
||
|
throw new IllegalStateException(
|
||
|
"Reached end of partial data without transfer token");
|
||
|
}
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "Ran out of partial data at "
|
||
|
+ mCurParcel.dataPosition() + ", token " + mTransferToken);
|
||
|
fetchData();
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringReader @ "
|
||
|
+ mCurParcel.dataPosition());
|
||
|
mStringReader = new PooledStringReader(mCurParcel);
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "PooledStringReader size = "
|
||
|
+ mStringReader.getStringCount());
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "readParcel: at " + mCurParcel.dataPosition()
|
||
|
+ ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
|
||
|
+ ", views=" + mNumReadViews);
|
||
|
mCurParcel.readInt();
|
||
|
return mCurParcel;
|
||
|
}
|
||
|
|
||
|
private void fetchData() {
|
||
|
Parcel data = Parcel.obtain();
|
||
|
try {
|
||
|
data.writeInterfaceToken(DESCRIPTOR);
|
||
|
data.writeStrongBinder(mTransferToken);
|
||
|
if (DEBUG_PARCEL) Log.d(TAG, "Requesting data with token " + mTransferToken);
|
||
|
if (mCurParcel != null) {
|
||
|
mCurParcel.recycle();
|
||
|
}
|
||
|
mCurParcel = Parcel.obtain();
|
||
|
try {
|
||
|
mChannel.transact(TRANSACTION_XFER, data, mCurParcel, 0);
|
||
|
} catch (RemoteException e) {
|
||
|
Log.w(TAG, "Failure reading AssistStructure data", e);
|
||
|
throw new IllegalStateException("Failure reading AssistStructure data: " + e);
|
||
|
}
|
||
|
} finally {
|
||
|
data.recycle();
|
||
|
}
|
||
|
mNumReadWindows = mNumReadViews = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
final static class ViewNodeText {
|
||
|
CharSequence mText;
|
||
|
float mTextSize;
|
||
|
int mTextStyle;
|
||
|
int mTextColor = ViewNode.TEXT_COLOR_UNDEFINED;
|
||
|
int mTextBackgroundColor = ViewNode.TEXT_COLOR_UNDEFINED;
|
||
|
int mTextSelectionStart;
|
||
|
int mTextSelectionEnd;
|
||
|
int[] mLineCharOffsets;
|
||
|
int[] mLineBaselines;
|
||
|
String mHint;
|
||
|
|
||
|
ViewNodeText() {
|
||
|
}
|
||
|
|
||
|
boolean isSimple() {
|
||
|
return mTextBackgroundColor == ViewNode.TEXT_COLOR_UNDEFINED
|
||
|
&& mTextSelectionStart == 0 && mTextSelectionEnd == 0
|
||
|
&& mLineCharOffsets == null && mLineBaselines == null && mHint == null;
|
||
|
}
|
||
|
|
||
|
ViewNodeText(Parcel in, boolean simple) {
|
||
|
mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
|
||
|
mTextSize = in.readFloat();
|
||
|
mTextStyle = in.readInt();
|
||
|
mTextColor = in.readInt();
|
||
|
if (!simple) {
|
||
|
mTextBackgroundColor = in.readInt();
|
||
|
mTextSelectionStart = in.readInt();
|
||
|
mTextSelectionEnd = in.readInt();
|
||
|
mLineCharOffsets = in.createIntArray();
|
||
|
mLineBaselines = in.createIntArray();
|
||
|
mHint = in.readString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void writeToParcel(Parcel out, boolean simple, boolean writeSensitive) {
|
||
|
TextUtils.writeToParcel(writeSensitive ? mText : "", out, 0);
|
||
|
out.writeFloat(mTextSize);
|
||
|
out.writeInt(mTextStyle);
|
||
|
out.writeInt(mTextColor);
|
||
|
if (!simple) {
|
||
|
out.writeInt(mTextBackgroundColor);
|
||
|
out.writeInt(mTextSelectionStart);
|
||
|
out.writeInt(mTextSelectionEnd);
|
||
|
out.writeIntArray(mLineCharOffsets);
|
||
|
out.writeIntArray(mLineBaselines);
|
||
|
out.writeString(mHint);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Describes a window in the assist data.
|
||
|
*/
|
||
|
static public class WindowNode {
|
||
|
final int mX;
|
||
|
final int mY;
|
||
|
final int mWidth;
|
||
|
final int mHeight;
|
||
|
final CharSequence mTitle;
|
||
|
final int mDisplayId;
|
||
|
final ViewNode mRoot;
|
||
|
|
||
|
WindowNode(AssistStructure assist, ViewRootImpl root, boolean forAutoFill, int flags) {
|
||
|
View view = root.getView();
|
||
|
Rect rect = new Rect();
|
||
|
view.getBoundsOnScreen(rect);
|
||
|
mX = rect.left - view.getLeft();
|
||
|
mY = rect.top - view.getTop();
|
||
|
mWidth = rect.width();
|
||
|
mHeight = rect.height();
|
||
|
mTitle = root.getTitle();
|
||
|
mDisplayId = root.getDisplayId();
|
||
|
mRoot = new ViewNode();
|
||
|
|
||
|
ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false);
|
||
|
if ((root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
|
||
|
if (forAutoFill) {
|
||
|
final int viewFlags = resolveViewAutofillFlags(view.getContext(), flags);
|
||
|
view.onProvideAutofillStructure(builder, viewFlags);
|
||
|
} else {
|
||
|
// This is a secure window, so it doesn't want a screenshot, and that
|
||
|
// means we should also not copy out its view hierarchy for Assist
|
||
|
view.onProvideStructure(builder);
|
||
|
builder.setAssistBlocked(true);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
if (forAutoFill) {
|
||
|
final int viewFlags = resolveViewAutofillFlags(view.getContext(), flags);
|
||
|
view.dispatchProvideAutofillStructure(builder, viewFlags);
|
||
|
} else {
|
||
|
view.dispatchProvideStructure(builder);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
WindowNode(ParcelTransferReader reader) {
|
||
|
Parcel in = reader.readParcel(VALIDATE_WINDOW_TOKEN, 0);
|
||
|
reader.mNumReadWindows++;
|
||
|
mX = in.readInt();
|
||
|
mY = in.readInt();
|
||
|
mWidth = in.readInt();
|
||
|
mHeight = in.readInt();
|
||
|
mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
|
||
|
mDisplayId = in.readInt();
|
||
|
mRoot = new ViewNode(reader, 0);
|
||
|
}
|
||
|
|
||
|
int resolveViewAutofillFlags(Context context, int fillRequestFlags) {
|
||
|
return (fillRequestFlags & FillRequest.FLAG_MANUAL_REQUEST) != 0
|
||
|
|| context.isAutofillCompatibilityEnabled()
|
||
|
|| (fillRequestFlags & FillRequest.FLAG_PCC_DETECTION) != 0
|
||
|
? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
|
||
|
}
|
||
|
|
||
|
void writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
|
||
|
out.writeInt(mX);
|
||
|
out.writeInt(mY);
|
||
|
out.writeInt(mWidth);
|
||
|
out.writeInt(mHeight);
|
||
|
TextUtils.writeToParcel(mTitle, out, 0);
|
||
|
out.writeInt(mDisplayId);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the left edge of the window, in pixels, relative to the left
|
||
|
* edge of the screen.
|
||
|
*/
|
||
|
public int getLeft() {
|
||
|
return mX;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the top edge of the window, in pixels, relative to the top
|
||
|
* edge of the screen.
|
||
|
*/
|
||
|
public int getTop() {
|
||
|
return mY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the total width of the window in pixels.
|
||
|
*/
|
||
|
public int getWidth() {
|
||
|
return mWidth;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the total height of the window in pixels.
|
||
|
*/
|
||
|
public int getHeight() {
|
||
|
return mHeight;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the title associated with the window, if it has one.
|
||
|
*/
|
||
|
public CharSequence getTitle() {
|
||
|
return mTitle;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the ID of the display this window is on, for use with
|
||
|
* {@link android.hardware.display.DisplayManager#getDisplay DisplayManager.getDisplay()}.
|
||
|
*/
|
||
|
public int getDisplayId() {
|
||
|
return mDisplayId;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the {@link ViewNode} containing the root content of the window.
|
||
|
*/
|
||
|
public ViewNode getRootViewNode() {
|
||
|
return mRoot;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Describes a single view in the assist data.
|
||
|
*/
|
||
|
static public class ViewNode {
|
||
|
/**
|
||
|
* Magic value for text color that has not been defined, which is very unlikely
|
||
|
* to be confused with a real text color.
|
||
|
*/
|
||
|
public static final int TEXT_COLOR_UNDEFINED = 1;
|
||
|
|
||
|
public static final int TEXT_STYLE_BOLD = 1<<0;
|
||
|
public static final int TEXT_STYLE_ITALIC = 1<<1;
|
||
|
public static final int TEXT_STYLE_UNDERLINE = 1<<2;
|
||
|
public static final int TEXT_STYLE_STRIKE_THRU = 1<<3;
|
||
|
|
||
|
int mId = View.NO_ID;
|
||
|
String mIdPackage;
|
||
|
String mIdType;
|
||
|
String mIdEntry;
|
||
|
|
||
|
AutofillId mAutofillId;
|
||
|
@View.AutofillType int mAutofillType = View.AUTOFILL_TYPE_NONE;
|
||
|
@Nullable String[] mAutofillHints;
|
||
|
|
||
|
@Nullable GetCredentialRequest mGetCredentialRequest;
|
||
|
|
||
|
@Nullable OutcomeReceiver<GetCredentialResponse, GetCredentialException>
|
||
|
mGetCredentialCallback;
|
||
|
|
||
|
@Nullable ResultReceiver mGetCredentialResultReceiver;
|
||
|
|
||
|
|
||
|
AutofillValue mAutofillValue;
|
||
|
CharSequence[] mAutofillOptions;
|
||
|
boolean mSanitized;
|
||
|
HtmlInfo mHtmlInfo;
|
||
|
int mMinEms = -1;
|
||
|
int mMaxEms = -1;
|
||
|
int mMaxLength = -1;
|
||
|
@Nullable String mTextIdEntry;
|
||
|
@Nullable String mHintIdEntry;
|
||
|
@AutofillImportance int mImportantForAutofill;
|
||
|
|
||
|
// POJO used to override some autofill-related values when the node is parcelized.
|
||
|
// Not written to parcel.
|
||
|
AutofillOverlay mAutofillOverlay;
|
||
|
boolean mIsCredential;
|
||
|
|
||
|
int mX;
|
||
|
int mY;
|
||
|
int mScrollX;
|
||
|
int mScrollY;
|
||
|
int mWidth;
|
||
|
int mHeight;
|
||
|
Matrix mMatrix;
|
||
|
float mElevation;
|
||
|
float mAlpha = 1.0f;
|
||
|
|
||
|
// TODO: The FLAGS_* below have filled all bits, will need to be refactored.
|
||
|
static final int FLAGS_DISABLED = 0x00000001;
|
||
|
static final int FLAGS_VISIBILITY_MASK = View.VISIBLE|View.INVISIBLE|View.GONE;
|
||
|
static final int FLAGS_FOCUSABLE = 0x00000010;
|
||
|
static final int FLAGS_FOCUSED = 0x00000020;
|
||
|
static final int FLAGS_SELECTED = 0x00000040;
|
||
|
static final int FLAGS_ASSIST_BLOCKED = 0x00000080;
|
||
|
static final int FLAGS_CHECKABLE = 0x00000100;
|
||
|
static final int FLAGS_CHECKED = 0x00000200;
|
||
|
static final int FLAGS_CLICKABLE = 0x00000400;
|
||
|
static final int FLAGS_LONG_CLICKABLE = 0x00000800;
|
||
|
static final int FLAGS_ACCESSIBILITY_FOCUSED = 0x00001000;
|
||
|
static final int FLAGS_ACTIVATED = 0x00002000;
|
||
|
static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000;
|
||
|
static final int FLAGS_OPAQUE = 0x00008000;
|
||
|
|
||
|
// --IMPORTANT-- must update this flag if any below flags extend to further bits.
|
||
|
// This flag is used to clear all FLAGS_HAS_* values in mFlags prior to parceling.
|
||
|
static final int FLAGS_ALL_CONTROL = 0xffff0000;
|
||
|
|
||
|
static final int FLAGS_HAS_MIME_TYPES = 0x80000000;
|
||
|
static final int FLAGS_HAS_MATRIX = 0x40000000;
|
||
|
static final int FLAGS_HAS_ALPHA = 0x20000000;
|
||
|
static final int FLAGS_HAS_ELEVATION = 0x10000000;
|
||
|
static final int FLAGS_HAS_SCROLL = 0x08000000;
|
||
|
static final int FLAGS_HAS_LARGE_COORDS = 0x04000000;
|
||
|
static final int FLAGS_HAS_CONTENT_DESCRIPTION = 0x02000000;
|
||
|
static final int FLAGS_HAS_TEXT = 0x01000000;
|
||
|
static final int FLAGS_HAS_COMPLEX_TEXT = 0x00800000;
|
||
|
static final int FLAGS_HAS_EXTRAS = 0x00400000;
|
||
|
static final int FLAGS_HAS_ID = 0x00200000;
|
||
|
static final int FLAGS_HAS_CHILDREN = 0x00100000;
|
||
|
static final int FLAGS_HAS_URL_DOMAIN = 0x00080000;
|
||
|
static final int FLAGS_HAS_INPUT_TYPE = 0x00040000;
|
||
|
static final int FLAGS_HAS_URL_SCHEME = 0x00020000;
|
||
|
static final int FLAGS_HAS_LOCALE_LIST = 0x00010000;
|
||
|
// --IMPORTANT END--
|
||
|
|
||
|
static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID = 0x0001;
|
||
|
static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID = 0x0002;
|
||
|
static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE = 0x0004;
|
||
|
static final int AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE = 0x0008;
|
||
|
static final int AUTOFILL_FLAGS_HAS_AUTOFILL_HINTS = 0x0010;
|
||
|
static final int AUTOFILL_FLAGS_HAS_AUTOFILL_OPTIONS = 0x0020;
|
||
|
static final int AUTOFILL_FLAGS_HAS_HTML_INFO = 0x0040;
|
||
|
static final int AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY = 0x0080;
|
||
|
static final int AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS = 0x0100;
|
||
|
static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS = 0x0200;
|
||
|
static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH = 0x0400;
|
||
|
static final int AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID = 0x0800;
|
||
|
static final int AUTOFILL_FLAGS_HAS_HINT_ID_ENTRY = 0x1000;
|
||
|
|
||
|
int mFlags;
|
||
|
int mAutofillFlags;
|
||
|
|
||
|
String mClassName;
|
||
|
CharSequence mContentDescription;
|
||
|
|
||
|
ViewNodeText mText;
|
||
|
int mInputType;
|
||
|
String mWebScheme;
|
||
|
String mWebDomain;
|
||
|
Bundle mExtras;
|
||
|
LocaleList mLocaleList;
|
||
|
String[] mReceiveContentMimeTypes;
|
||
|
|
||
|
ViewNode[] mChildren;
|
||
|
|
||
|
// TODO(b/111276913): temporarily made public / @hide until we decide what will be used by
|
||
|
// COntent Capture.
|
||
|
/** @hide */
|
||
|
@SystemApi
|
||
|
public ViewNode() {
|
||
|
}
|
||
|
|
||
|
ViewNode(@NonNull Parcel in) {
|
||
|
initializeFromParcelWithoutChildren(in, /*preader=*/null, /*tmpMatrix=*/null);
|
||
|
}
|
||
|
|
||
|
ViewNode(ParcelTransferReader reader, int nestingLevel) {
|
||
|
final Parcel in = reader.readParcel(VALIDATE_VIEW_TOKEN, nestingLevel);
|
||
|
reader.mNumReadViews++;
|
||
|
initializeFromParcelWithoutChildren(in, Objects.requireNonNull(reader.mStringReader),
|
||
|
Objects.requireNonNull(reader.mTmpMatrix));
|
||
|
if ((mFlags & FLAGS_HAS_CHILDREN) != 0) {
|
||
|
final int numChildren = in.readInt();
|
||
|
if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) {
|
||
|
Log.d(TAG,
|
||
|
"Preparing to read " + numChildren
|
||
|
+ " children: @ #" + reader.mNumReadViews
|
||
|
+ ", level " + nestingLevel);
|
||
|
}
|
||
|
mChildren = new ViewNode[numChildren];
|
||
|
for (int i = 0; i < numChildren; i++) {
|
||
|
mChildren[i] = new ViewNode(reader, nestingLevel + 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static void writeString(@NonNull Parcel out, @Nullable PooledStringWriter pwriter,
|
||
|
@Nullable String str) {
|
||
|
if (pwriter != null) {
|
||
|
pwriter.writeString(str);
|
||
|
} else {
|
||
|
out.writeString(str);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
private static String readString(@NonNull Parcel in, @Nullable PooledStringReader preader) {
|
||
|
if (preader != null) {
|
||
|
return preader.readString();
|
||
|
}
|
||
|
return in.readString();
|
||
|
}
|
||
|
|
||
|
// This does not read the child nodes.
|
||
|
void initializeFromParcelWithoutChildren(Parcel in, @Nullable PooledStringReader preader,
|
||
|
@Nullable float[] tmpMatrix) {
|
||
|
mClassName = readString(in, preader);
|
||
|
mFlags = in.readInt();
|
||
|
final int flags = mFlags;
|
||
|
mAutofillFlags = in.readInt();
|
||
|
final int autofillFlags = mAutofillFlags;
|
||
|
if ((flags&FLAGS_HAS_ID) != 0) {
|
||
|
mId = in.readInt();
|
||
|
if (mId != View.NO_ID) {
|
||
|
mIdEntry = readString(in, preader);
|
||
|
if (mIdEntry != null) {
|
||
|
mIdType = readString(in, preader);
|
||
|
mIdPackage = readString(in, preader);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (autofillFlags != 0) {
|
||
|
mSanitized = in.readInt() == 1;
|
||
|
mIsCredential = in.readInt() == 1;
|
||
|
mImportantForAutofill = in.readInt();
|
||
|
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID) != 0) {
|
||
|
int autofillViewId = in.readInt();
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID) != 0) {
|
||
|
mAutofillId = new AutofillId(autofillViewId, in.readInt());
|
||
|
} else {
|
||
|
mAutofillId = new AutofillId(autofillViewId);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID) != 0) {
|
||
|
mAutofillId.setSessionId(in.readInt());
|
||
|
}
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) {
|
||
|
mAutofillType = in.readInt();
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_HINTS) != 0) {
|
||
|
mAutofillHints = in.readStringArray();
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE) != 0) {
|
||
|
mAutofillValue = in.readParcelable(null, android.view.autofill.AutofillValue.class);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_OPTIONS) != 0) {
|
||
|
mAutofillOptions = in.readCharSequenceArray();
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_HTML_INFO) != 0) {
|
||
|
mHtmlInfo = in.readParcelable(null, android.view.ViewStructure.HtmlInfo.class);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS) != 0) {
|
||
|
mMinEms = in.readInt();
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS) != 0) {
|
||
|
mMaxEms = in.readInt();
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH) != 0) {
|
||
|
mMaxLength = in.readInt();
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY) != 0) {
|
||
|
mTextIdEntry = readString(in, preader);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_HINT_ID_ENTRY) != 0) {
|
||
|
mHintIdEntry = readString(in, preader);
|
||
|
}
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
|
||
|
mX = in.readInt();
|
||
|
mY = in.readInt();
|
||
|
mWidth = in.readInt();
|
||
|
mHeight = in.readInt();
|
||
|
} else {
|
||
|
int val = in.readInt();
|
||
|
mX = val&0x7fff;
|
||
|
mY = (val>>16)&0x7fff;
|
||
|
val = in.readInt();
|
||
|
mWidth = val&0x7fff;
|
||
|
mHeight = (val>>16)&0x7fff;
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_SCROLL) != 0) {
|
||
|
mScrollX = in.readInt();
|
||
|
mScrollY = in.readInt();
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_MATRIX) != 0) {
|
||
|
mMatrix = new Matrix();
|
||
|
if (tmpMatrix == null) {
|
||
|
tmpMatrix = new float[9];
|
||
|
}
|
||
|
in.readFloatArray(tmpMatrix);
|
||
|
mMatrix.setValues(tmpMatrix);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_ELEVATION) != 0) {
|
||
|
mElevation = in.readFloat();
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_ALPHA) != 0) {
|
||
|
mAlpha = in.readFloat();
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
|
||
|
mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_TEXT) != 0) {
|
||
|
mText = new ViewNodeText(in, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_INPUT_TYPE) != 0) {
|
||
|
mInputType = in.readInt();
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_URL_SCHEME) != 0) {
|
||
|
mWebScheme = in.readString();
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_URL_DOMAIN) != 0) {
|
||
|
mWebDomain = in.readString();
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
|
||
|
mLocaleList = in.readParcelable(null, android.os.LocaleList.class);
|
||
|
}
|
||
|
if ((flags & FLAGS_HAS_MIME_TYPES) != 0) {
|
||
|
mReceiveContentMimeTypes = in.readStringArray();
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_EXTRAS) != 0) {
|
||
|
mExtras = in.readBundle();
|
||
|
}
|
||
|
mGetCredentialRequest = in.readTypedObject(GetCredentialRequest.CREATOR);
|
||
|
mGetCredentialResultReceiver = in.readTypedObject(ResultReceiver.CREATOR);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This does not write the child nodes.
|
||
|
*
|
||
|
* @param willWriteChildren whether child nodes will be written to the parcel or not after
|
||
|
* calling this method.
|
||
|
*/
|
||
|
int writeSelfToParcel(@NonNull Parcel out, @Nullable PooledStringWriter pwriter,
|
||
|
boolean sanitizeOnWrite, @Nullable float[] tmpMatrix, boolean willWriteChildren) {
|
||
|
// Guard used to skip non-sanitized data when writing for autofill.
|
||
|
boolean writeSensitive = true;
|
||
|
|
||
|
int flags = mFlags & ~FLAGS_ALL_CONTROL;
|
||
|
int autofillFlags = 0;
|
||
|
|
||
|
if (mId != View.NO_ID) {
|
||
|
flags |= FLAGS_HAS_ID;
|
||
|
}
|
||
|
if ((mX&~0x7fff) != 0 || (mY&~0x7fff) != 0
|
||
|
|| (mWidth&~0x7fff) != 0 | (mHeight&~0x7fff) != 0) {
|
||
|
flags |= FLAGS_HAS_LARGE_COORDS;
|
||
|
}
|
||
|
if (mScrollX != 0 || mScrollY != 0) {
|
||
|
flags |= FLAGS_HAS_SCROLL;
|
||
|
}
|
||
|
if (mMatrix != null) {
|
||
|
flags |= FLAGS_HAS_MATRIX;
|
||
|
}
|
||
|
if (mElevation != 0) {
|
||
|
flags |= FLAGS_HAS_ELEVATION;
|
||
|
}
|
||
|
if (mAlpha != 1.0f) {
|
||
|
flags |= FLAGS_HAS_ALPHA;
|
||
|
}
|
||
|
if (mContentDescription != null) {
|
||
|
flags |= FLAGS_HAS_CONTENT_DESCRIPTION;
|
||
|
}
|
||
|
if (mText != null) {
|
||
|
flags |= FLAGS_HAS_TEXT;
|
||
|
if (!mText.isSimple()) {
|
||
|
flags |= FLAGS_HAS_COMPLEX_TEXT;
|
||
|
}
|
||
|
}
|
||
|
if (mInputType != 0) {
|
||
|
flags |= FLAGS_HAS_INPUT_TYPE;
|
||
|
}
|
||
|
if (mWebScheme != null) {
|
||
|
flags |= FLAGS_HAS_URL_SCHEME;
|
||
|
}
|
||
|
if (mWebDomain != null) {
|
||
|
flags |= FLAGS_HAS_URL_DOMAIN;
|
||
|
}
|
||
|
if (mLocaleList != null) {
|
||
|
flags |= FLAGS_HAS_LOCALE_LIST;
|
||
|
}
|
||
|
if (mReceiveContentMimeTypes != null) {
|
||
|
flags |= FLAGS_HAS_MIME_TYPES;
|
||
|
}
|
||
|
if (mExtras != null) {
|
||
|
flags |= FLAGS_HAS_EXTRAS;
|
||
|
}
|
||
|
if (mChildren != null && willWriteChildren) {
|
||
|
flags |= FLAGS_HAS_CHILDREN;
|
||
|
}
|
||
|
if (mAutofillId != null) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID;
|
||
|
if (mAutofillId.isVirtualInt()) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID;
|
||
|
}
|
||
|
if (mAutofillId.hasSession()) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID;
|
||
|
}
|
||
|
}
|
||
|
if (mAutofillValue != null) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE;
|
||
|
}
|
||
|
if (mAutofillType != View.AUTOFILL_TYPE_NONE) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE;
|
||
|
}
|
||
|
if (mAutofillHints != null) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_HINTS;
|
||
|
}
|
||
|
if (mAutofillOptions != null) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_OPTIONS;
|
||
|
}
|
||
|
if (mHtmlInfo instanceof Parcelable) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_HTML_INFO;
|
||
|
}
|
||
|
if (mMinEms > -1) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS;
|
||
|
}
|
||
|
if (mMaxEms > -1) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS;
|
||
|
}
|
||
|
if (mMaxLength > -1) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH;
|
||
|
}
|
||
|
if (mTextIdEntry != null) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY;
|
||
|
}
|
||
|
if (mHintIdEntry != null) {
|
||
|
autofillFlags |= AUTOFILL_FLAGS_HAS_HINT_ID_ENTRY;
|
||
|
}
|
||
|
|
||
|
writeString(out, pwriter, mClassName);
|
||
|
|
||
|
int writtenFlags = flags;
|
||
|
if (autofillFlags != 0 && (mSanitized || !sanitizeOnWrite)) {
|
||
|
// Remove 'checked' from sanitized autofill request.
|
||
|
writtenFlags = flags & ~FLAGS_CHECKED;
|
||
|
}
|
||
|
if (mAutofillOverlay != null) {
|
||
|
if (mAutofillOverlay.focused) {
|
||
|
writtenFlags |= ViewNode.FLAGS_FOCUSED;
|
||
|
} else {
|
||
|
writtenFlags &= ~ViewNode.FLAGS_FOCUSED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
out.writeInt(writtenFlags);
|
||
|
out.writeInt(autofillFlags);
|
||
|
if ((flags&FLAGS_HAS_ID) != 0) {
|
||
|
out.writeInt(mId);
|
||
|
if (mId != View.NO_ID) {
|
||
|
writeString(out, pwriter, mIdEntry);
|
||
|
if (mIdEntry != null) {
|
||
|
writeString(out, pwriter, mIdType);
|
||
|
writeString(out, pwriter, mIdPackage);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (autofillFlags != 0) {
|
||
|
out.writeInt(mSanitized ? 1 : 0);
|
||
|
out.writeInt(mIsCredential ? 1 : 0);
|
||
|
out.writeInt(mImportantForAutofill);
|
||
|
writeSensitive = mSanitized || !sanitizeOnWrite;
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID) != 0) {
|
||
|
out.writeInt(mAutofillId.getViewId());
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID) != 0) {
|
||
|
out.writeInt(mAutofillId.getVirtualChildIntId());
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID) != 0) {
|
||
|
out.writeInt(mAutofillId.getSessionId());
|
||
|
}
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) {
|
||
|
out.writeInt(mAutofillType);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_HINTS) != 0) {
|
||
|
out.writeStringArray(mAutofillHints);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE) != 0) {
|
||
|
final AutofillValue sanitizedValue;
|
||
|
if (writeSensitive) {
|
||
|
sanitizedValue = mAutofillValue;
|
||
|
} else if (mAutofillOverlay != null && mAutofillOverlay.value != null) {
|
||
|
sanitizedValue = mAutofillOverlay.value;
|
||
|
} else {
|
||
|
sanitizedValue = null;
|
||
|
}
|
||
|
out.writeParcelable(sanitizedValue, 0);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_OPTIONS) != 0) {
|
||
|
out.writeCharSequenceArray(mAutofillOptions);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_HTML_INFO) != 0) {
|
||
|
out.writeParcelable((Parcelable) mHtmlInfo, 0);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS) != 0) {
|
||
|
out.writeInt(mMinEms);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS) != 0) {
|
||
|
out.writeInt(mMaxEms);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH) != 0) {
|
||
|
out.writeInt(mMaxLength);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY) != 0) {
|
||
|
writeString(out, pwriter, mTextIdEntry);
|
||
|
}
|
||
|
if ((autofillFlags & AUTOFILL_FLAGS_HAS_HINT_ID_ENTRY) != 0) {
|
||
|
writeString(out, pwriter, mHintIdEntry);
|
||
|
}
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
|
||
|
out.writeInt(mX);
|
||
|
out.writeInt(mY);
|
||
|
out.writeInt(mWidth);
|
||
|
out.writeInt(mHeight);
|
||
|
} else {
|
||
|
out.writeInt((mY<<16) | mX);
|
||
|
out.writeInt((mHeight<<16) | mWidth);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_SCROLL) != 0) {
|
||
|
out.writeInt(mScrollX);
|
||
|
out.writeInt(mScrollY);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_MATRIX) != 0) {
|
||
|
if (tmpMatrix == null) {
|
||
|
tmpMatrix = new float[9];
|
||
|
}
|
||
|
mMatrix.getValues(tmpMatrix);
|
||
|
out.writeFloatArray(tmpMatrix);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_ELEVATION) != 0) {
|
||
|
out.writeFloat(mElevation);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_ALPHA) != 0) {
|
||
|
out.writeFloat(mAlpha);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
|
||
|
TextUtils.writeToParcel(mContentDescription, out, 0);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_TEXT) != 0) {
|
||
|
mText.writeToParcel(out, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0, writeSensitive);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_INPUT_TYPE) != 0) {
|
||
|
out.writeInt(mInputType);
|
||
|
}
|
||
|
if ((flags & FLAGS_HAS_URL_SCHEME) != 0) {
|
||
|
out.writeString(mWebScheme);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_URL_DOMAIN) != 0) {
|
||
|
out.writeString(mWebDomain);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
|
||
|
out.writeParcelable(mLocaleList, 0);
|
||
|
}
|
||
|
if ((flags & FLAGS_HAS_MIME_TYPES) != 0) {
|
||
|
out.writeStringArray(mReceiveContentMimeTypes);
|
||
|
}
|
||
|
if ((flags&FLAGS_HAS_EXTRAS) != 0) {
|
||
|
out.writeBundle(mExtras);
|
||
|
}
|
||
|
out.writeTypedObject(mGetCredentialRequest, flags);
|
||
|
out.writeTypedObject(mGetCredentialResultReceiver, flags);
|
||
|
return flags;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the ID associated with this view, as per {@link View#getId() View.getId()}.
|
||
|
*/
|
||
|
public int getId() {
|
||
|
return mId;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If {@link #getId()} is a resource identifier, this is the package name of that
|
||
|
* identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId}
|
||
|
* for more information.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public String getIdPackage() {
|
||
|
return mIdPackage;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If {@link #getId()} is a resource identifier, this is the type name of that
|
||
|
* identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId}
|
||
|
* for more information.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public String getIdType() {
|
||
|
return mIdType;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If {@link #getId()} is a resource identifier, this is the entry name of that
|
||
|
* identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId}
|
||
|
* for more information.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public String getIdEntry() {
|
||
|
return mIdEntry;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the id that can be used to autofill the view contents.
|
||
|
*
|
||
|
* <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes.
|
||
|
*
|
||
|
* @return id that can be used to autofill the view contents, or {@code null} if the
|
||
|
* structure was created for assist purposes.
|
||
|
*/
|
||
|
@Nullable public AutofillId getAutofillId() {
|
||
|
return mAutofillId;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the type of value that can be used to autofill the view contents.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <p>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 value of this view.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <p>Typically used by nodes whose {@link View#getAutofillType()} is a list to indicate
|
||
|
* the meaning of each possible value in the list.
|
||
|
*
|
||
|
* <p>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;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return whether the node is a credential.
|
||
|
*
|
||
|
* <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
|
||
|
* not for assist purposes.
|
||
|
* TODO(b/303677885): add TestApi
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
public boolean isCredential() {
|
||
|
return mIsCredential;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the request associated with this node
|
||
|
* @return
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
|
||
|
@Nullable
|
||
|
public GetCredentialRequest getPendingCredentialRequest() {
|
||
|
return mGetCredentialRequest;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
@FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
|
||
|
@Nullable
|
||
|
public ResultReceiver getPendingCredentialCallback() {
|
||
|
return mGetCredentialResultReceiver;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <p>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)}.
|
||
|
*
|
||
|
* <p>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)}.
|
||
|
*
|
||
|
* <p>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}.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public String getClassName() {
|
||
|
return mClassName;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns any content description associated with the node, which semantically describes
|
||
|
* its purpose for accessibility and other uses.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public CharSequence getContentDescription() {
|
||
|
return mContentDescription;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the domain of the HTML document represented by this view.
|
||
|
*
|
||
|
* <p>Typically used when the view associated with the view is a container for an HTML
|
||
|
* document.
|
||
|
*
|
||
|
* <p><b>Warning:</b> an autofill service cannot trust the value reported by this method
|
||
|
* without verifing its authenticity—see the "Web security" section of
|
||
|
* {@link android.service.autofill.AutofillService} for more details.
|
||
|
*
|
||
|
* @return domain-only part of the document. For example, if the full URL is
|
||
|
* {@code https://example.com/login?user=my_user}, it returns {@code example.com}.
|
||
|
*/
|
||
|
@Nullable public String getWebDomain() {
|
||
|
return mWebDomain;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public void setWebDomain(@Nullable String domain) {
|
||
|
if (domain == null) return;
|
||
|
|
||
|
Uri uri = Uri.parse(domain);
|
||
|
if (uri == null) {
|
||
|
// Cannot log domain because it could contain PII;
|
||
|
Log.w(TAG, "Failed to parse web domain");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
mWebScheme = uri.getScheme();
|
||
|
if (mWebScheme == null) {
|
||
|
uri = Uri.parse("http://" + domain);
|
||
|
}
|
||
|
|
||
|
mWebDomain = uri.getHost();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the scheme of the HTML document represented by this view.
|
||
|
*
|
||
|
* <p>Typically used when the view associated with the view is a container for an HTML
|
||
|
* document.
|
||
|
*
|
||
|
* @return scheme-only part of the document. For example, if the full URL is
|
||
|
* {@code https://example.com/login?user=my_user}, it returns {@code https}.
|
||
|
*/
|
||
|
@Nullable public String getWebScheme() {
|
||
|
return mWebScheme;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the HTML properties associated with this view.
|
||
|
*
|
||
|
* <p>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 list of locales associated with this view.
|
||
|
*/
|
||
|
@Nullable public LocaleList getLocaleList() {
|
||
|
return mLocaleList;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the MIME types accepted by {@link View#performReceiveContent} for this view. See
|
||
|
* {@link View#getReceiveContentMimeTypes()} for details.
|
||
|
*/
|
||
|
@Nullable
|
||
|
@SuppressLint("NullableCollection")
|
||
|
public String[] getReceiveContentMimeTypes() {
|
||
|
return mReceiveContentMimeTypes;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns any text associated with the node that is displayed to the user, or null
|
||
|
* if there is none.
|
||
|
*
|
||
|
* <p> The text will be stripped of any spans that could potentially contain reference to
|
||
|
* the activity context, to avoid memory leak. If the text contained a span, a plain
|
||
|
* string version of the text will be returned.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public CharSequence getText() {
|
||
|
return mText != null ? mText.mText : null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If {@link #getText()} is non-null, this is where the current selection starts.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
|
||
|
* not for autofill purposes.
|
||
|
*/
|
||
|
@Nullable
|
||
|
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.
|
||
|
*
|
||
|
* <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
|
||
|
* not for autofill purposes.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public int[] getTextLineBaselines() {
|
||
|
return mText != null ? mText.mLineBaselines : null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the identifier used to set the text associated with this view.
|
||
|
*
|
||
|
* <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
|
||
|
* not for assist purposes.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public String getTextIdEntry() {
|
||
|
return mTextIdEntry;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public String getHint() {
|
||
|
return mText != null ? mText.mHint : null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the identifier used to set the hint associated with this view.
|
||
|
*
|
||
|
* <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
|
||
|
* not for assist purposes.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public String getHintIdEntry() {
|
||
|
return mHintIdEntry;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return a Bundle containing optional vendor-specific extension information.
|
||
|
*/
|
||
|
@Nullable
|
||
|
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];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the minimum width in ems of the text associated with this node, or {@code -1}
|
||
|
* if not supported by the node.
|
||
|
*
|
||
|
* <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
|
||
|
* not for assist purposes.
|
||
|
*/
|
||
|
public int getMinTextEms() {
|
||
|
return mMinEms;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the maximum width in ems of the text associated with this node, or {@code -1}
|
||
|
* if not supported by the node.
|
||
|
*
|
||
|
* <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
|
||
|
* not for assist purposes.
|
||
|
*/
|
||
|
public int getMaxTextEms() {
|
||
|
return mMaxEms;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the maximum length of the text associated with this node, or {@code -1} if not
|
||
|
* supported by the node or not set. System may set a default value if the text length is
|
||
|
* not set.
|
||
|
*
|
||
|
* <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
|
||
|
* not for assist purposes.
|
||
|
*/
|
||
|
public int getMaxTextLength() {
|
||
|
return mMaxLength;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the {@link View#setImportantForAutofill(int) importantForAutofill mode} of
|
||
|
* the view associated with this node.
|
||
|
*
|
||
|
* <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes.
|
||
|
*/
|
||
|
public @AutofillImportance int getImportantForAutofill() {
|
||
|
return mImportantForAutofill;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A parcelable wrapper class around {@link ViewNode}.
|
||
|
*
|
||
|
* <p>This class, when parceled and unparceled, does not carry the child nodes.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
public static final class ViewNodeParcelable implements Parcelable {
|
||
|
|
||
|
@NonNull
|
||
|
private final ViewNode mViewNode;
|
||
|
|
||
|
public ViewNodeParcelable(@NonNull ViewNode viewNode) {
|
||
|
mViewNode = viewNode;
|
||
|
}
|
||
|
|
||
|
public ViewNodeParcelable(@NonNull Parcel in) {
|
||
|
mViewNode = new ViewNode(in);
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public ViewNode getViewNode() {
|
||
|
return mViewNode;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int describeContents() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void writeToParcel(@NonNull Parcel parcel, int flags) {
|
||
|
mViewNode.writeSelfToParcel(parcel, /*pwriter=*/null, /*sanitizeOnWrite=*/false,
|
||
|
/*tmpMatrix*/null, /*willWriteChildren=*/ false);
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static final Parcelable.Creator<ViewNodeParcelable> CREATOR =
|
||
|
new Parcelable.Creator<ViewNodeParcelable>() {
|
||
|
@Override
|
||
|
public ViewNodeParcelable createFromParcel(@NonNull Parcel in) {
|
||
|
return new ViewNodeParcelable(in);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public ViewNodeParcelable[] newArray(int size) {
|
||
|
return new ViewNodeParcelable[size];
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* POJO used to override some autofill-related values when the node is parcelized.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
static public class AutofillOverlay {
|
||
|
public boolean focused;
|
||
|
public AutofillValue value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public static class ViewNodeBuilder extends ViewStructure {
|
||
|
final AssistStructure mAssist;
|
||
|
final ViewNode mNode;
|
||
|
final boolean mAsync;
|
||
|
private Handler mHandler;
|
||
|
|
||
|
/**
|
||
|
* Used to instantiate a builder for a stand-alone {@link ViewNode} which is not associated
|
||
|
* to a properly created {@link AssistStructure}.
|
||
|
*/
|
||
|
public ViewNodeBuilder() {
|
||
|
mAssist = new AssistStructure();
|
||
|
mNode = new ViewNode();
|
||
|
mAsync = false;
|
||
|
}
|
||
|
|
||
|
ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) {
|
||
|
mAssist = assist;
|
||
|
mNode = node;
|
||
|
mAsync = async;
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public ViewNode getViewNode() {
|
||
|
return mNode;
|
||
|
}
|
||
|
|
||
|
@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 & ViewNode.FLAGS_VISIBILITY_MASK);
|
||
|
}
|
||
|
|
||
|
@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();
|
||
|
// Strip spans from the text to avoid memory leak
|
||
|
t.mText = TextUtils.trimToParcelableSize(stripAllSpansFromText(text));
|
||
|
t.mTextSelectionStart = t.mTextSelectionEnd = -1;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setText(CharSequence text, int selectionStart, int selectionEnd) {
|
||
|
ViewNodeText t = getNodeText();
|
||
|
// Strip spans from the text to avoid memory leak
|
||
|
t.mText = stripAllSpansFromText(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 setTextIdEntry(@NonNull String entryName) {
|
||
|
mNode.mTextIdEntry = Objects.requireNonNull(entryName);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setHint(CharSequence hint) {
|
||
|
getNodeText().mHint = hint != null ? hint.toString() : null;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setHintIdEntry(@NonNull String entryName) {
|
||
|
mNode.mHintIdEntry = Objects.requireNonNull(entryName);
|
||
|
}
|
||
|
|
||
|
@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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
@Override
|
||
|
public GetCredentialRequest getPendingCredentialRequest() {
|
||
|
return mNode.mGetCredentialRequest;
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
@Override
|
||
|
public OutcomeReceiver<
|
||
|
GetCredentialResponse, GetCredentialException> getPendingCredentialCallback() {
|
||
|
return mNode.mGetCredentialCallback;
|
||
|
}
|
||
|
|
||
|
@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 setImportantForAutofill(@AutofillImportance int mode) {
|
||
|
mNode.mImportantForAutofill = mode;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setIsCredential(boolean isCredential) {
|
||
|
mNode.mIsCredential = isCredential;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setPendingCredentialRequest(@NonNull GetCredentialRequest request,
|
||
|
@NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
|
||
|
mNode.mGetCredentialRequest = request;
|
||
|
mNode.mGetCredentialCallback = callback;
|
||
|
for (CredentialOption option : request.getCredentialOptions()) {
|
||
|
ArrayList<AutofillId> ids = option.getCandidateQueryData()
|
||
|
.getParcelableArrayList(
|
||
|
CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
|
||
|
ids = ids != null ? ids : new ArrayList<>();
|
||
|
if (!ids.contains(getAutofillId())) {
|
||
|
ids.add(getAutofillId());
|
||
|
}
|
||
|
option.getCandidateQueryData()
|
||
|
.putParcelableArrayList(CredentialProviderService.EXTRA_AUTOFILL_ID, ids);
|
||
|
}
|
||
|
setUpResultReceiver(callback);
|
||
|
}
|
||
|
|
||
|
private void setUpResultReceiver(
|
||
|
OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
|
||
|
|
||
|
if (mHandler == null) {
|
||
|
mHandler = new Handler(Looper.getMainLooper(), null, true);
|
||
|
}
|
||
|
final ResultReceiver resultReceiver = new ResultReceiver(mHandler) {
|
||
|
@Override
|
||
|
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||
|
if (resultCode == SUCCESS_CREDMAN_SELECTOR) {
|
||
|
Slog.d(TAG, "onReceiveResult from Credential Manager");
|
||
|
GetCredentialResponse getCredentialResponse =
|
||
|
resultData.getParcelable(
|
||
|
CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
|
||
|
GetCredentialResponse.class);
|
||
|
|
||
|
callback.onResult(getCredentialResponse);
|
||
|
} else if (resultCode == FAILURE_CREDMAN_SELECTOR) {
|
||
|
String[] exception = resultData.getStringArray(
|
||
|
CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION);
|
||
|
if (exception != null && exception.length >= 2) {
|
||
|
Slog.w(TAG, "Credman bottom sheet from pinned "
|
||
|
+ "entry failed with: + " + exception[0] + " , "
|
||
|
+ exception[1]);
|
||
|
callback.onError(new GetCredentialException(
|
||
|
exception[0], exception[1]));
|
||
|
}
|
||
|
} else {
|
||
|
Slog.d(TAG, "Unknown resultCode from credential "
|
||
|
+ "manager bottom sheet: " + resultCode);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
ResultReceiver ipcFriendlyResultReceiver =
|
||
|
toIpcFriendlyResultReceiver(resultReceiver);
|
||
|
mNode.mGetCredentialResultReceiver = ipcFriendlyResultReceiver;
|
||
|
}
|
||
|
|
||
|
private ResultReceiver toIpcFriendlyResultReceiver(ResultReceiver resultReceiver) {
|
||
|
final Parcel parcel = Parcel.obtain();
|
||
|
resultReceiver.writeToParcel(parcel, 0);
|
||
|
parcel.setDataPosition(0);
|
||
|
|
||
|
final ResultReceiver ipcFriendly = ResultReceiver.CREATOR.createFromParcel(parcel);
|
||
|
parcel.recycle();
|
||
|
|
||
|
return ipcFriendly;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setReceiveContentMimeTypes(@Nullable String[] mimeTypes) {
|
||
|
mNode.mReceiveContentMimeTypes = mimeTypes;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setInputType(int inputType) {
|
||
|
mNode.mInputType = inputType;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setMinTextEms(int minEms) {
|
||
|
mNode.mMinEms = minEms;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setMaxTextEms(int maxEms) {
|
||
|
mNode.mMaxEms = maxEms;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setMaxTextLength(int maxLength) {
|
||
|
mNode.mMaxLength = maxLength;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setDataIsSensitive(boolean sensitive) {
|
||
|
mNode.mSanitized = !sensitive;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setWebDomain(@Nullable String domain) {
|
||
|
mNode.setWebDomain(domain);
|
||
|
}
|
||
|
|
||
|
@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 CharSequence stripAllSpansFromText(CharSequence text) {
|
||
|
if (text instanceof Spanned) {
|
||
|
return text.toString();
|
||
|
}
|
||
|
return text;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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<Pair<String, String>> mAttributes;
|
||
|
|
||
|
private HtmlInfoNode(HtmlInfoNodeBuilder builder) {
|
||
|
mTag = builder.mTag;
|
||
|
if (builder.mNames == null) {
|
||
|
mNames = null;
|
||
|
mValues = null;
|
||
|
} else {
|
||
|
mNames = new String[builder.mNames.size()];
|
||
|
mValues = new String[builder.mValues.size()];
|
||
|
builder.mNames.toArray(mNames);
|
||
|
builder.mValues.toArray(mValues);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String getTag() {
|
||
|
return mTag;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public List<Pair<String, String>> getAttributes() {
|
||
|
if (mAttributes == null && mNames != null) {
|
||
|
mAttributes = new ArrayList<>(mNames.length);
|
||
|
for (int i = 0; i < mNames.length; i++) {
|
||
|
final Pair<String, String> pair = new Pair<>(mNames[i], mValues[i]);
|
||
|
mAttributes.add(i, pair);
|
||
|
}
|
||
|
}
|
||
|
return mAttributes;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int describeContents() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void writeToParcel(Parcel parcel, int flags) {
|
||
|
parcel.writeString(mTag);
|
||
|
parcel.writeStringArray(mNames);
|
||
|
parcel.writeStringArray(mValues);
|
||
|
}
|
||
|
|
||
|
@SuppressWarnings("hiding")
|
||
|
public static final @android.annotation.NonNull Creator<HtmlInfoNode> CREATOR = new Creator<HtmlInfoNode>() {
|
||
|
@Override
|
||
|
public HtmlInfoNode createFromParcel(Parcel parcel) {
|
||
|
// Always go through the builder to ensure the data ingested by
|
||
|
// the system obeys the contract of the builder to avoid attacks
|
||
|
// using specially crafted parcels.
|
||
|
final String tag = parcel.readString();
|
||
|
final HtmlInfoNodeBuilder builder = new HtmlInfoNodeBuilder(tag);
|
||
|
final String[] names = parcel.readStringArray();
|
||
|
final String[] values = parcel.readStringArray();
|
||
|
if (names != null && values != null) {
|
||
|
if (names.length != values.length) {
|
||
|
Log.w(TAG, "HtmlInfo attributes mismatch: names=" + names.length
|
||
|
+ ", values=" + values.length);
|
||
|
} else {
|
||
|
for (int i = 0; i < names.length; i++) {
|
||
|
builder.addAttribute(names[i], values[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return builder.build();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public HtmlInfoNode[] newArray(int size) {
|
||
|
return new HtmlInfoNode[size];
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private static final class HtmlInfoNodeBuilder extends HtmlInfo.Builder {
|
||
|
private final String mTag;
|
||
|
private ArrayList<String> mNames;
|
||
|
private ArrayList<String> mValues;
|
||
|
|
||
|
HtmlInfoNodeBuilder(String tag) {
|
||
|
mTag = tag;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public Builder addAttribute(String name, String value) {
|
||
|
if (mNames == null) {
|
||
|
mNames = new ArrayList<>();
|
||
|
mValues = new ArrayList<>();
|
||
|
}
|
||
|
mNames.add(name);
|
||
|
mValues.add(value);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public HtmlInfoNode build() {
|
||
|
return new HtmlInfoNode(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public AssistStructure(Activity activity, boolean forAutoFill, int flags) {
|
||
|
mHaveData = true;
|
||
|
mFlags = flags;
|
||
|
ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
|
||
|
activity.getActivityToken());
|
||
|
for (int i=0; i<views.size(); i++) {
|
||
|
ViewRootImpl root = views.get(i);
|
||
|
if (root.getView() == null) {
|
||
|
Log.w(TAG, "Skipping window with dettached view: " + root.getTitle());
|
||
|
continue;
|
||
|
}
|
||
|
mWindowNodes.add(new WindowNode(this, root, forAutoFill, flags));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public AssistStructure() {
|
||
|
mHaveData = true;
|
||
|
mFlags = 0;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public AssistStructure(Parcel in) {
|
||
|
mTaskId = in.readInt();
|
||
|
mActivityComponent = ComponentName.readFromParcel(in);
|
||
|
mIsHomeActivity = in.readInt() == 1;
|
||
|
mReceiveChannel = in.readStrongBinder();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Helper method used to sanitize the structure before it's written to a parcel.
|
||
|
*
|
||
|
* <p>Used just on autofill.
|
||
|
* @hide
|
||
|
*/
|
||
|
public void sanitizeForParceling(boolean sanitize) {
|
||
|
mSanitizeOnWrite = sanitize;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public void dump(boolean showSensitive) {
|
||
|
if (mActivityComponent == null) {
|
||
|
Log.i(TAG, "dump(): calling ensureData() first");
|
||
|
ensureData();
|
||
|
}
|
||
|
Log.i(TAG, "Task id: " + mTaskId);
|
||
|
Log.i(TAG, "Activity: " + (mActivityComponent != null
|
||
|
? mActivityComponent.flattenToShortString()
|
||
|
: null));
|
||
|
Log.i(TAG, "Sanitize on write: " + mSanitizeOnWrite);
|
||
|
Log.i(TAG, "Flags: " + mFlags);
|
||
|
final int N = getWindowNodeCount();
|
||
|
for (int i=0; i<N; i++) {
|
||
|
WindowNode node = getWindowNodeAt(i);
|
||
|
Log.i(TAG, "Window #" + i + " [" + node.getLeft() + "," + node.getTop()
|
||
|
+ " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getTitle());
|
||
|
dump(" ", node.getRootViewNode(), showSensitive);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void dump(String prefix, ViewNode node, boolean showSensitive) {
|
||
|
Log.i(TAG, prefix + "View [" + node.getLeft() + "," + node.getTop()
|
||
|
+ " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getClassName());
|
||
|
int id = node.getId();
|
||
|
if (id != 0) {
|
||
|
StringBuilder sb = new StringBuilder();
|
||
|
sb.append(prefix); sb.append(" ID: #"); sb.append(Integer.toHexString(id));
|
||
|
String entry = node.getIdEntry();
|
||
|
if (entry != null) {
|
||
|
String type = node.getIdType();
|
||
|
String pkg = node.getIdPackage();
|
||
|
sb.append(" "); sb.append(pkg); sb.append(":"); sb.append(type);
|
||
|
sb.append("/"); sb.append(entry);
|
||
|
}
|
||
|
Log.i(TAG, sb.toString());
|
||
|
}
|
||
|
int scrollX = node.getScrollX();
|
||
|
int scrollY = node.getScrollY();
|
||
|
if (scrollX != 0 || scrollY != 0) {
|
||
|
Log.i(TAG, prefix + " Scroll: " + scrollX + "," + scrollY);
|
||
|
}
|
||
|
Matrix matrix = node.getTransformation();
|
||
|
if (matrix != null) {
|
||
|
Log.i(TAG, prefix + " Transformation: " + matrix);
|
||
|
}
|
||
|
float elevation = node.getElevation();
|
||
|
if (elevation != 0) {
|
||
|
Log.i(TAG, prefix + " Elevation: " + elevation);
|
||
|
}
|
||
|
float alpha = node.getAlpha();
|
||
|
if (alpha != 0) {
|
||
|
Log.i(TAG, prefix + " Alpha: " + elevation);
|
||
|
}
|
||
|
CharSequence contentDescription = node.getContentDescription();
|
||
|
if (contentDescription != null) {
|
||
|
Log.i(TAG, prefix + " Content description: " + contentDescription);
|
||
|
}
|
||
|
CharSequence text = node.getText();
|
||
|
if (text != null) {
|
||
|
final String safeText = node.isSanitized() || showSensitive ? text.toString()
|
||
|
: "REDACTED[" + text.length() + " chars]";
|
||
|
Log.i(TAG, prefix + " Text (sel " + node.getTextSelectionStart() + "-"
|
||
|
+ node.getTextSelectionEnd() + "): " + safeText);
|
||
|
Log.i(TAG, prefix + " Text size: " + node.getTextSize() + " , style: #"
|
||
|
+ node.getTextStyle());
|
||
|
Log.i(TAG, prefix + " Text color fg: #" + Integer.toHexString(node.getTextColor())
|
||
|
+ ", bg: #" + Integer.toHexString(node.getTextBackgroundColor()));
|
||
|
Log.i(TAG, prefix + " Input type: " + getInputTypeString(node.getInputType()));
|
||
|
Log.i(TAG, prefix + " Resource id: " + node.getTextIdEntry());
|
||
|
}
|
||
|
String webDomain = node.getWebDomain();
|
||
|
if (webDomain != null) {
|
||
|
Log.i(TAG, prefix + " Web domain: " + webDomain);
|
||
|
}
|
||
|
HtmlInfo htmlInfo = node.getHtmlInfo();
|
||
|
if (htmlInfo != null) {
|
||
|
Log.i(TAG, prefix + " HtmlInfo: tag=" + htmlInfo.getTag()
|
||
|
+ ", attr="+ htmlInfo.getAttributes());
|
||
|
}
|
||
|
|
||
|
LocaleList localeList = node.getLocaleList();
|
||
|
if (localeList != null) {
|
||
|
Log.i(TAG, prefix + " LocaleList: " + localeList);
|
||
|
}
|
||
|
String[] mimeTypes = node.getReceiveContentMimeTypes();
|
||
|
if (mimeTypes != null) {
|
||
|
Log.i(TAG, prefix + " MIME types: " + Arrays.toString(mimeTypes));
|
||
|
}
|
||
|
String hint = node.getHint();
|
||
|
if (hint != null) {
|
||
|
Log.i(TAG, prefix + " Hint: " + hint);
|
||
|
Log.i(TAG, prefix + " Resource id: " + node.getHintIdEntry());
|
||
|
}
|
||
|
Bundle extras = node.getExtras();
|
||
|
if (extras != null) {
|
||
|
Log.i(TAG, prefix + " Extras: " + extras);
|
||
|
}
|
||
|
if (node.isAssistBlocked()) {
|
||
|
Log.i(TAG, prefix + " BLOCKED");
|
||
|
}
|
||
|
AutofillId autofillId = node.getAutofillId();
|
||
|
if (autofillId == null) {
|
||
|
Log.i(TAG, prefix + " No autofill ID");
|
||
|
} else {
|
||
|
Log.i(TAG, prefix + " Autofill info: id= " + autofillId
|
||
|
+ ", type=" + node.getAutofillType()
|
||
|
+ ", options=" + Arrays.toString(node.getAutofillOptions())
|
||
|
+ ", hints=" + Arrays.toString(node.getAutofillHints())
|
||
|
+ ", value=" + node.getAutofillValue()
|
||
|
+ ", sanitized=" + node.isSanitized()
|
||
|
+ ", important=" + node.getImportantForAutofill()
|
||
|
+ ", visibility=" + node.getVisibility()
|
||
|
+ ", isCredential=" + node.isCredential()
|
||
|
);
|
||
|
}
|
||
|
GetCredentialRequest getCredentialRequest = node.getPendingCredentialRequest();
|
||
|
Log.i(TAG, prefix + " Credential Manager info:"
|
||
|
+ " hasCredentialManagerRequest=" + (getCredentialRequest != null)
|
||
|
+ (getCredentialRequest != null
|
||
|
? ", sizeOfOptions=" + getCredentialRequest.getCredentialOptions().size()
|
||
|
: "")
|
||
|
);
|
||
|
|
||
|
final int NCHILDREN = node.getChildCount();
|
||
|
if (NCHILDREN > 0) {
|
||
|
Log.i(TAG, prefix + " Children:");
|
||
|
String cprefix = prefix + " ";
|
||
|
for (int i=0; i<NCHILDREN; i++) {
|
||
|
ViewNode cnode = node.getChildAt(i);
|
||
|
dump(cprefix, cnode, showSensitive);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the task id is associated with the activity from which this AssistStructure was
|
||
|
* generated.
|
||
|
* @hide
|
||
|
*/
|
||
|
public void setTaskId(int taskId) {
|
||
|
mTaskId = taskId;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return The task id for the associated activity.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
public int getTaskId() {
|
||
|
return mTaskId;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the activity that is associated with this AssistStructure.
|
||
|
* @hide
|
||
|
*/
|
||
|
public void setActivityComponent(ComponentName componentName) {
|
||
|
mActivityComponent = componentName;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the activity this AssistStructure came from.
|
||
|
*/
|
||
|
public ComponentName getActivityComponent() {
|
||
|
return mActivityComponent;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public int getFlags() {
|
||
|
return mFlags;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns whether the activity associated with this AssistStructure was the home activity
|
||
|
* (Launcher) at the time the assist data was acquired.
|
||
|
* @return Whether the activity was the home activity.
|
||
|
* @see android.content.Intent#CATEGORY_HOME
|
||
|
*/
|
||
|
public boolean isHomeActivity() {
|
||
|
return mIsHomeActivity;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the number of window contents that have been collected in this assist data.
|
||
|
*/
|
||
|
public int getWindowNodeCount() {
|
||
|
ensureData();
|
||
|
return mWindowNodes.size();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return one of the windows in the assist data.
|
||
|
* @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1.
|
||
|
*/
|
||
|
public WindowNode getWindowNodeAt(int index) {
|
||
|
ensureData();
|
||
|
return mWindowNodes.get(index);
|
||
|
}
|
||
|
|
||
|
// TODO(b/35708678): temporary method that disable one-way warning flag on binder.
|
||
|
/** @hide */
|
||
|
public void ensureDataForAutofill() {
|
||
|
if (mHaveData) {
|
||
|
return;
|
||
|
}
|
||
|
mHaveData = true;
|
||
|
Binder.allowBlocking(mReceiveChannel);
|
||
|
try {
|
||
|
ParcelTransferReader reader = new ParcelTransferReader(mReceiveChannel);
|
||
|
reader.go();
|
||
|
} finally {
|
||
|
Binder.defaultBlocking(mReceiveChannel);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public void ensureData() {
|
||
|
if (mHaveData) {
|
||
|
return;
|
||
|
}
|
||
|
mHaveData = true;
|
||
|
ParcelTransferReader reader = new ParcelTransferReader(mReceiveChannel);
|
||
|
reader.go();
|
||
|
}
|
||
|
|
||
|
boolean waitForReady() {
|
||
|
boolean skipStructure = false;
|
||
|
synchronized (this) {
|
||
|
long endTime = SystemClock.uptimeMillis() + 5000;
|
||
|
long now;
|
||
|
while (mPendingAsyncChildren.size() > 0 && (now=SystemClock.uptimeMillis()) < endTime) {
|
||
|
try {
|
||
|
wait(endTime-now);
|
||
|
} catch (InterruptedException e) {
|
||
|
}
|
||
|
}
|
||
|
if (mPendingAsyncChildren.size() > 0) {
|
||
|
// We waited too long, assume none of the assist structure is valid.
|
||
|
Log.w(TAG, "Skipping assist structure, waiting too long for async children (have "
|
||
|
+ mPendingAsyncChildren.size() + " remaining");
|
||
|
skipStructure = true;
|
||
|
}
|
||
|
}
|
||
|
return !skipStructure;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public void clearSendChannel() {
|
||
|
if (mSendChannel != null) {
|
||
|
mSendChannel.mAssistStructure = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int describeContents() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void writeToParcel(Parcel out, int flags) {
|
||
|
out.writeInt(mTaskId);
|
||
|
ComponentName.writeToParcel(mActivityComponent, out);
|
||
|
out.writeInt(mIsHomeActivity ? 1 : 0);
|
||
|
if (mHaveData) {
|
||
|
// This object holds its data. We want to write a send channel that the
|
||
|
// other side can use to retrieve that data.
|
||
|
if (mSendChannel == null) {
|
||
|
mSendChannel = new SendChannel(this);
|
||
|
}
|
||
|
out.writeStrongBinder(mSendChannel);
|
||
|
} else {
|
||
|
// This object doesn't hold its data, so just propagate along its receive channel.
|
||
|
out.writeStrongBinder(mReceiveChannel);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static final @android.annotation.NonNull Parcelable.Creator<AssistStructure> CREATOR
|
||
|
= new Parcelable.Creator<AssistStructure>() {
|
||
|
@Override
|
||
|
public AssistStructure createFromParcel(Parcel in) {
|
||
|
return new AssistStructure(in);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public AssistStructure[] newArray(int size) {
|
||
|
return new AssistStructure[size];
|
||
|
}
|
||
|
};
|
||
|
|
||
|
private static final ArrayMap<Integer, String> INPUT_TYPE_VARIATIONS = new ArrayMap<>();
|
||
|
static {
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, "EmailSubject");
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS, "PostalAddress");
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_PERSON_NAME, "PersonName");
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_PASSWORD, "Password");
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, "VisiblePassword");
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_URI, "URI");
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS, "WebEmailAddress");
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD, "WebPassword");
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE, "LongMessage");
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE, "ShortMessage");
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_FLAG_MULTI_LINE, "MultiLine");
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE, "ImeMultiLine");
|
||
|
INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_FILTER, "Filter");
|
||
|
}
|
||
|
|
||
|
private static String getInputTypeString(int inputType) {
|
||
|
StringBuilder sb = new StringBuilder();
|
||
|
sb.append(inputType);
|
||
|
sb.append("(class=").append(inputType & InputType.TYPE_MASK_CLASS).append(')');
|
||
|
for (int variation : INPUT_TYPE_VARIATIONS.keySet()) {
|
||
|
if ((variation & inputType) == variation) {
|
||
|
sb.append('|').append(INPUT_TYPE_VARIATIONS.get(variation));
|
||
|
}
|
||
|
}
|
||
|
return sb.toString();
|
||
|
}
|
||
|
}
|