blob: a6f23666cc6d6a2e10b59bc4770a5e9e161d8dc9 [file] [log] [blame]
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001package android.app.assist;
2
3import android.app.Activity;
Dianne Hackborn16036f22015-06-22 14:05:51 -07004import android.content.ComponentName;
Dianne Hackborn70d8be72015-06-23 19:33:02 +00005import android.graphics.Matrix;
Dianne Hackborn16036f22015-06-22 14:05:51 -07006import android.graphics.Rect;
Dianne Hackborn3e8125b2015-07-22 17:02:10 -07007import android.os.BadParcelableException;
Dianne Hackborn16036f22015-06-22 14:05:51 -07008import android.os.Binder;
9import android.os.Bundle;
10import android.os.IBinder;
Dianne Hackborn69c6adc2015-06-02 10:52:59 -070011import android.os.Parcel;
12import android.os.Parcelable;
Dianne Hackborn16036f22015-06-22 14:05:51 -070013import android.os.PooledStringReader;
14import android.os.PooledStringWriter;
15import android.os.RemoteException;
16import android.os.SystemClock;
17import android.text.TextUtils;
18import android.util.Log;
19import android.view.View;
Dianne Hackborn8ecf16d2015-06-23 13:09:21 -070020import android.view.ViewStructure;
Dianne Hackborn16036f22015-06-22 14:05:51 -070021import android.view.ViewRootImpl;
22import android.view.WindowManager;
23import android.view.WindowManagerGlobal;
24
25import java.util.ArrayList;
Dianne Hackborn69c6adc2015-06-02 10:52:59 -070026
27/**
Dianne Hackborn16036f22015-06-22 14:05:51 -070028 * Assist data automatically created by the platform's implementation
29 * of {@link android.app.Activity#onProvideAssistData}.
Dianne Hackborn69c6adc2015-06-02 10:52:59 -070030 */
Dianne Hackborn16036f22015-06-22 14:05:51 -070031public class AssistStructure implements Parcelable {
32 static final String TAG = "AssistStructure";
Dianne Hackborn69c6adc2015-06-02 10:52:59 -070033
Dianne Hackborn782d4982015-07-08 17:36:37 -070034 static final boolean DEBUG_PARCEL = false;
Dianne Hackborn3e8125b2015-07-22 17:02:10 -070035 static final boolean DEBUG_PARCEL_CHILDREN = false;
Dianne Hackborn782d4982015-07-08 17:36:37 -070036 static final boolean DEBUG_PARCEL_TREE = false;
37
Dianne Hackborn3e8125b2015-07-22 17:02:10 -070038 static final int VALIDATE_WINDOW_TOKEN = 0x11111111;
39 static final int VALIDATE_VIEW_TOKEN = 0x22222222;
40
Dianne Hackborn16036f22015-06-22 14:05:51 -070041 boolean mHaveData;
42
43 ComponentName mActivityComponent;
44
45 final ArrayList<WindowNode> mWindowNodes = new ArrayList<>();
46
47 final ArrayList<ViewNodeBuilder> mPendingAsyncChildren = new ArrayList<>();
48
49 SendChannel mSendChannel;
50 IBinder mReceiveChannel;
51
52 Rect mTmpRect = new Rect();
53
54 static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1;
55 static final String DESCRIPTOR = "android.app.AssistStructure";
56
Dianne Hackborn782d4982015-07-08 17:36:37 -070057 final static class SendChannel extends Binder {
58 volatile AssistStructure mAssistStructure;
59
60 SendChannel(AssistStructure as) {
61 mAssistStructure = as;
62 }
63
Dianne Hackborn16036f22015-06-22 14:05:51 -070064 @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
65 throws RemoteException {
66 if (code == TRANSACTION_XFER) {
Dianne Hackborn782d4982015-07-08 17:36:37 -070067 AssistStructure as = mAssistStructure;
68 if (as == null) {
69 return true;
70 }
71
Dianne Hackborn16036f22015-06-22 14:05:51 -070072 data.enforceInterface(DESCRIPTOR);
Dianne Hackborn782d4982015-07-08 17:36:37 -070073 IBinder token = data.readStrongBinder();
74 if (DEBUG_PARCEL) Log.d(TAG, "Request for data on " + as
75 + " using token " + token);
76 if (token != null) {
77 if (DEBUG_PARCEL) Log.d(TAG, "Resuming partial write of " + token);
78 if (token instanceof ParcelTransferWriter) {
79 ParcelTransferWriter xfer = (ParcelTransferWriter)token;
80 xfer.writeToParcel(as, reply);
81 return true;
82 }
83 Log.w(TAG, "Caller supplied bad token type: " + token);
84 // Don't write anything; this is the end of the data.
85 return true;
86 }
87 //long start = SystemClock.uptimeMillis();
88 ParcelTransferWriter xfer = new ParcelTransferWriter(as, reply);
89 xfer.writeToParcel(as, reply);
90 //Log.i(TAG, "Time to parcel: " + (SystemClock.uptimeMillis()-start) + "ms");
Dianne Hackborn16036f22015-06-22 14:05:51 -070091 return true;
92 } else {
93 return super.onTransact(code, data, reply, flags);
94 }
95 }
96 }
97
Dianne Hackborn782d4982015-07-08 17:36:37 -070098 final static class ViewStackEntry {
99 ViewNode node;
100 int curChild;
101 int numChildren;
102 }
103
104 final static class ParcelTransferWriter extends Binder {
105 final boolean mWriteStructure;
106 int mCurWindow;
107 int mNumWindows;
108 final ArrayList<ViewStackEntry> mViewStack = new ArrayList<>();
109 ViewStackEntry mCurViewStackEntry;
110 int mCurViewStackPos;
111 int mNumWrittenWindows;
112 int mNumWrittenViews;
113 final float[] mTmpMatrix = new float[9];
114
115 ParcelTransferWriter(AssistStructure as, Parcel out) {
116 mWriteStructure = as.waitForReady();
117 ComponentName.writeToParcel(as.mActivityComponent, out);
118 mNumWindows = as.mWindowNodes.size();
119 if (mWriteStructure && mNumWindows > 0) {
120 out.writeInt(mNumWindows);
121 } else {
122 out.writeInt(0);
123 }
124 }
125
126 void writeToParcel(AssistStructure as, Parcel out) {
127 int start = out.dataPosition();
128 mNumWrittenWindows = 0;
129 mNumWrittenViews = 0;
130 boolean more = writeToParcelInner(as, out);
131 Log.i(TAG, "Flattened " + (more ? "partial" : "final") + " assist data: "
132 + (out.dataPosition() - start)
133 + " bytes, containing " + mNumWrittenWindows + " windows, "
134 + mNumWrittenViews + " views");
135 }
136
137 boolean writeToParcelInner(AssistStructure as, Parcel out) {
138 if (mNumWindows == 0) {
139 return false;
140 }
141 if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringWriter @ " + out.dataPosition());
142 PooledStringWriter pwriter = new PooledStringWriter(out);
143 while (writeNextEntryToParcel(as, out, pwriter)) {
Dianne Hackborn86cbc2b2015-07-28 18:07:13 -0700144 // If the parcel is above the IPC limit, then we are getting too
Dianne Hackborn782d4982015-07-08 17:36:37 -0700145 // large for a single IPC so stop here and let the caller come back when it
146 // is ready for more.
Dianne Hackborn86cbc2b2015-07-28 18:07:13 -0700147 if (out.dataSize() > IBinder.MAX_IPC_SIZE) {
Dianne Hackborn782d4982015-07-08 17:36:37 -0700148 if (DEBUG_PARCEL) Log.d(TAG, "Assist data size is " + out.dataSize()
149 + " @ pos " + out.dataPosition() + "; returning partial result");
150 out.writeInt(0);
151 out.writeStrongBinder(this);
152 if (DEBUG_PARCEL) Log.d(TAG, "Finishing PooledStringWriter @ "
153 + out.dataPosition() + ", size " + pwriter.getStringCount());
154 pwriter.finish();
155 return true;
156 }
157 }
158 if (DEBUG_PARCEL) Log.d(TAG, "Finishing PooledStringWriter @ "
159 + out.dataPosition() + ", size " + pwriter.getStringCount());
160 pwriter.finish();
161 mViewStack.clear();
162 return false;
163 }
164
165 void pushViewStackEntry(ViewNode node, int pos) {
166 ViewStackEntry entry;
167 if (pos >= mViewStack.size()) {
168 entry = new ViewStackEntry();
169 mViewStack.add(entry);
170 if (DEBUG_PARCEL_TREE) Log.d(TAG, "New stack entry at " + pos + ": " + entry);
171 } else {
172 entry = mViewStack.get(pos);
173 if (DEBUG_PARCEL_TREE) Log.d(TAG, "Existing stack entry at " + pos + ": " + entry);
174 }
175 entry.node = node;
176 entry.numChildren = node.getChildCount();
177 entry.curChild = 0;
178 mCurViewStackEntry = entry;
179 }
180
Dianne Hackborn3e8125b2015-07-22 17:02:10 -0700181 void writeView(ViewNode child, Parcel out, PooledStringWriter pwriter, int levelAdj) {
182 if (DEBUG_PARCEL) Log.d(TAG, "write view: at " + out.dataPosition()
183 + ", windows=" + mNumWrittenWindows
184 + ", views=" + mNumWrittenViews
185 + ", level=" + (mCurViewStackPos+levelAdj));
186 out.writeInt(VALIDATE_VIEW_TOKEN);
187 int flags = child.writeSelfToParcel(out, pwriter, mTmpMatrix);
188 mNumWrittenViews++;
189 // If the child has children, push it on the stack to write them next.
190 if ((flags&ViewNode.FLAGS_HAS_CHILDREN) != 0) {
191 if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG,
192 "Preparing to write " + child.mChildren.length
193 + " children: @ #" + mNumWrittenViews
194 + ", level " + (mCurViewStackPos+levelAdj));
195 out.writeInt(child.mChildren.length);
196 int pos = ++mCurViewStackPos;
197 pushViewStackEntry(child, pos);
198 }
199 }
200
Dianne Hackborn782d4982015-07-08 17:36:37 -0700201 boolean writeNextEntryToParcel(AssistStructure as, Parcel out, PooledStringWriter pwriter) {
202 // Write next view node if appropriate.
203 if (mCurViewStackEntry != null) {
204 if (mCurViewStackEntry.curChild < mCurViewStackEntry.numChildren) {
205 // Write the next child in the current view.
206 if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing child #"
207 + mCurViewStackEntry.curChild + " in " + mCurViewStackEntry.node);
208 ViewNode child = mCurViewStackEntry.node.mChildren[mCurViewStackEntry.curChild];
209 mCurViewStackEntry.curChild++;
Dianne Hackborn3e8125b2015-07-22 17:02:10 -0700210 writeView(child, out, pwriter, 1);
Dianne Hackborn782d4982015-07-08 17:36:37 -0700211 return true;
212 }
213
214 // We are done writing children of the current view; pop off the stack.
215 do {
216 int pos = --mCurViewStackPos;
217 if (DEBUG_PARCEL_TREE) Log.d(TAG, "Done with " + mCurViewStackEntry.node
218 + "; popping up to " + pos);
219 if (pos < 0) {
220 // Reached the last view; step to next window.
221 if (DEBUG_PARCEL_TREE) Log.d(TAG, "Done with view hierarchy!");
222 mCurViewStackEntry = null;
223 break;
224 }
225 mCurViewStackEntry = mViewStack.get(pos);
226 } while (mCurViewStackEntry.curChild >= mCurViewStackEntry.numChildren);
227 return true;
228 }
229
230 // Write the next window if appropriate.
231 int pos = mCurWindow;
232 if (pos < mNumWindows) {
233 WindowNode win = as.mWindowNodes.get(pos);
234 mCurWindow++;
235 if (DEBUG_PARCEL) Log.d(TAG, "write window #" + pos + ": at " + out.dataPosition()
236 + ", windows=" + mNumWrittenWindows
237 + ", views=" + mNumWrittenViews);
Dianne Hackborn3e8125b2015-07-22 17:02:10 -0700238 out.writeInt(VALIDATE_WINDOW_TOKEN);
Dianne Hackborn782d4982015-07-08 17:36:37 -0700239 win.writeSelfToParcel(out, pwriter, mTmpMatrix);
240 mNumWrittenWindows++;
241 ViewNode root = win.mRoot;
242 mCurViewStackPos = 0;
Dianne Hackborn3e8125b2015-07-22 17:02:10 -0700243 if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing initial root view " + root);
244 writeView(root, out, pwriter, 0);
Dianne Hackborn782d4982015-07-08 17:36:37 -0700245 return true;
246 }
247
248 return false;
249 }
250 }
251
252 final class ParcelTransferReader {
253 final float[] mTmpMatrix = new float[9];
254 PooledStringReader mStringReader;
255
256 int mNumReadWindows;
257 int mNumReadViews;
258
259 private final IBinder mChannel;
260 private IBinder mTransferToken;
261 private Parcel mCurParcel;
262
263 ParcelTransferReader(IBinder channel) {
264 mChannel = channel;
265 }
266
267 void go() {
268 fetchData();
269 mActivityComponent = ComponentName.readFromParcel(mCurParcel);
270 final int N = mCurParcel.readInt();
271 if (N > 0) {
272 if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringReader @ "
273 + mCurParcel.dataPosition());
274 mStringReader = new PooledStringReader(mCurParcel);
275 if (DEBUG_PARCEL) Log.d(TAG, "PooledStringReader size = "
276 + mStringReader.getStringCount());
277 for (int i=0; i<N; i++) {
278 mWindowNodes.add(new WindowNode(this));
279 }
280 }
281 if (DEBUG_PARCEL) Log.d(TAG, "Finished reading: at " + mCurParcel.dataPosition()
282 + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
283 + ", views=" + mNumReadViews);
284 }
285
Dianne Hackborn3e8125b2015-07-22 17:02:10 -0700286 Parcel readParcel(int validateToken, int level) {
Dianne Hackborn782d4982015-07-08 17:36:37 -0700287 if (DEBUG_PARCEL) Log.d(TAG, "readParcel: at " + mCurParcel.dataPosition()
288 + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
Dianne Hackborn3e8125b2015-07-22 17:02:10 -0700289 + ", views=" + mNumReadViews + ", level=" + level);
290 int token = mCurParcel.readInt();
291 if (token != 0) {
292 if (token != validateToken) {
293 throw new BadParcelableException("Got token " + Integer.toHexString(token)
294 + ", expected token " + Integer.toHexString(validateToken));
295 }
Dianne Hackborn782d4982015-07-08 17:36:37 -0700296 return mCurParcel;
297 }
298 // We have run out of partial data, need to read another batch.
299 mTransferToken = mCurParcel.readStrongBinder();
300 if (mTransferToken == null) {
301 throw new IllegalStateException(
302 "Reached end of partial data without transfer token");
303 }
304 if (DEBUG_PARCEL) Log.d(TAG, "Ran out of partial data at "
305 + mCurParcel.dataPosition() + ", token " + mTransferToken);
306 fetchData();
307 if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringReader @ "
308 + mCurParcel.dataPosition());
309 mStringReader = new PooledStringReader(mCurParcel);
310 if (DEBUG_PARCEL) Log.d(TAG, "PooledStringReader size = "
311 + mStringReader.getStringCount());
312 if (DEBUG_PARCEL) Log.d(TAG, "readParcel: at " + mCurParcel.dataPosition()
313 + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
314 + ", views=" + mNumReadViews);
315 mCurParcel.readInt();
316 return mCurParcel;
317 }
318
319 private void fetchData() {
320 Parcel data = Parcel.obtain();
321 data.writeInterfaceToken(DESCRIPTOR);
322 data.writeStrongBinder(mTransferToken);
323 if (DEBUG_PARCEL) Log.d(TAG, "Requesting data with token " + mTransferToken);
324 if (mCurParcel != null) {
325 mCurParcel.recycle();
326 }
327 mCurParcel = Parcel.obtain();
328 try {
329 mChannel.transact(TRANSACTION_XFER, data, mCurParcel, 0);
330 } catch (RemoteException e) {
331 Log.w(TAG, "Failure reading AssistStructure data", e);
332 throw new IllegalStateException("Failure reading AssistStructure data: " + e);
333 }
334 data.recycle();
335 mNumReadWindows = mNumReadViews = 0;
336 }
337 }
338
Dianne Hackborn16036f22015-06-22 14:05:51 -0700339 final static class ViewNodeText {
340 CharSequence mText;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700341 float mTextSize;
342 int mTextStyle;
Dianne Hackborn6f0fdc42015-07-07 14:29:36 -0700343 int mTextColor = ViewNode.TEXT_COLOR_UNDEFINED;
344 int mTextBackgroundColor = ViewNode.TEXT_COLOR_UNDEFINED;
345 int mTextSelectionStart;
346 int mTextSelectionEnd;
347 int[] mLineCharOffsets;
348 int[] mLineBaselines;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700349 String mHint;
350
351 ViewNodeText() {
352 }
353
Dianne Hackborn6f0fdc42015-07-07 14:29:36 -0700354 boolean isSimple() {
355 return mTextBackgroundColor == ViewNode.TEXT_COLOR_UNDEFINED
356 && mTextSelectionStart == 0 && mTextSelectionEnd == 0
357 && mLineCharOffsets == null && mLineBaselines == null && mHint == null;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700358 }
359
Dianne Hackborn6f0fdc42015-07-07 14:29:36 -0700360 ViewNodeText(Parcel in, boolean simple) {
361 mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
362 mTextSize = in.readFloat();
363 mTextStyle = in.readInt();
364 mTextColor = in.readInt();
365 if (!simple) {
366 mTextBackgroundColor = in.readInt();
367 mTextSelectionStart = in.readInt();
368 mTextSelectionEnd = in.readInt();
369 mLineCharOffsets = in.createIntArray();
370 mLineBaselines = in.createIntArray();
371 mHint = in.readString();
372 }
373 }
374
375 void writeToParcel(Parcel out, boolean simple) {
Dianne Hackborn16036f22015-06-22 14:05:51 -0700376 TextUtils.writeToParcel(mText, out, 0);
Dianne Hackborn16036f22015-06-22 14:05:51 -0700377 out.writeFloat(mTextSize);
378 out.writeInt(mTextStyle);
Dianne Hackborn6f0fdc42015-07-07 14:29:36 -0700379 out.writeInt(mTextColor);
380 if (!simple) {
381 out.writeInt(mTextBackgroundColor);
382 out.writeInt(mTextSelectionStart);
383 out.writeInt(mTextSelectionEnd);
384 out.writeIntArray(mLineCharOffsets);
385 out.writeIntArray(mLineBaselines);
386 out.writeString(mHint);
387 }
Dianne Hackborn16036f22015-06-22 14:05:51 -0700388 }
389 }
390
391 /**
392 * Describes a window in the assist data.
393 */
394 static public class WindowNode {
395 final int mX;
396 final int mY;
397 final int mWidth;
398 final int mHeight;
399 final CharSequence mTitle;
400 final int mDisplayId;
401 final ViewNode mRoot;
402
403 WindowNode(AssistStructure assist, ViewRootImpl root) {
404 View view = root.getView();
405 Rect rect = new Rect();
406 view.getBoundsOnScreen(rect);
407 mX = rect.left - view.getLeft();
408 mY = rect.top - view.getTop();
409 mWidth = rect.width();
410 mHeight = rect.height();
411 mTitle = root.getTitle();
412 mDisplayId = root.getDisplayId();
413 mRoot = new ViewNode();
414 ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false);
415 if ((root.getWindowFlags()& WindowManager.LayoutParams.FLAG_SECURE) != 0) {
416 // This is a secure window, so it doesn't want a screenshot, and that
417 // means we should also not copy out its view hierarchy.
418 view.onProvideStructure(builder);
419 builder.setAssistBlocked(true);
420 return;
421 }
422 view.dispatchProvideStructure(builder);
423 }
424
Dianne Hackborn782d4982015-07-08 17:36:37 -0700425 WindowNode(ParcelTransferReader reader) {
Dianne Hackborn3e8125b2015-07-22 17:02:10 -0700426 Parcel in = reader.readParcel(VALIDATE_WINDOW_TOKEN, 0);
Dianne Hackborn782d4982015-07-08 17:36:37 -0700427 reader.mNumReadWindows++;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700428 mX = in.readInt();
429 mY = in.readInt();
430 mWidth = in.readInt();
431 mHeight = in.readInt();
432 mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
433 mDisplayId = in.readInt();
Dianne Hackborn3e8125b2015-07-22 17:02:10 -0700434 mRoot = new ViewNode(reader, 0);
Dianne Hackborn16036f22015-06-22 14:05:51 -0700435 }
436
Dianne Hackborn782d4982015-07-08 17:36:37 -0700437 void writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
Dianne Hackborn16036f22015-06-22 14:05:51 -0700438 out.writeInt(mX);
439 out.writeInt(mY);
440 out.writeInt(mWidth);
441 out.writeInt(mHeight);
442 TextUtils.writeToParcel(mTitle, out, 0);
443 out.writeInt(mDisplayId);
Dianne Hackborn16036f22015-06-22 14:05:51 -0700444 }
445
446 /**
447 * Returns the left edge of the window, in pixels, relative to the left
448 * edge of the screen.
449 */
450 public int getLeft() {
451 return mX;
452 }
453
454 /**
455 * Returns the top edge of the window, in pixels, relative to the top
456 * edge of the screen.
457 */
458 public int getTop() {
459 return mY;
460 }
461
462 /**
463 * Returns the total width of the window in pixels.
464 */
465 public int getWidth() {
466 return mWidth;
467 }
468
469 /**
470 * Returns the total height of the window in pixels.
471 */
472 public int getHeight() {
473 return mHeight;
474 }
475
476 /**
477 * Returns the title associated with the window, if it has one.
478 */
479 public CharSequence getTitle() {
480 return mTitle;
481 }
482
483 /**
484 * Returns the ID of the display this window is on, for use with
485 * {@link android.hardware.display.DisplayManager#getDisplay DisplayManager.getDisplay()}.
486 */
487 public int getDisplayId() {
488 return mDisplayId;
489 }
490
491 /**
492 * Returns the {@link ViewNode} containing the root content of the window.
493 */
494 public ViewNode getRootViewNode() {
495 return mRoot;
496 }
497 }
498
499 /**
500 * Describes a single view in the assist data.
501 */
502 static public class ViewNode {
503 /**
504 * Magic value for text color that has not been defined, which is very unlikely
505 * to be confused with a real text color.
506 */
507 public static final int TEXT_COLOR_UNDEFINED = 1;
508
509 public static final int TEXT_STYLE_BOLD = 1<<0;
510 public static final int TEXT_STYLE_ITALIC = 1<<1;
511 public static final int TEXT_STYLE_UNDERLINE = 1<<2;
512 public static final int TEXT_STYLE_STRIKE_THRU = 1<<3;
513
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000514 int mId = View.NO_ID;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700515 String mIdPackage;
516 String mIdType;
517 String mIdEntry;
Felipe Leme29a5b0d2016-10-25 14:57:11 -0700518 int mAutoFillId = View.NO_ID;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700519 int mX;
520 int mY;
521 int mScrollX;
522 int mScrollY;
523 int mWidth;
524 int mHeight;
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000525 Matrix mMatrix;
526 float mElevation;
527 float mAlpha = 1.0f;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700528
529 static final int FLAGS_DISABLED = 0x00000001;
530 static final int FLAGS_VISIBILITY_MASK = View.VISIBLE|View.INVISIBLE|View.GONE;
531 static final int FLAGS_FOCUSABLE = 0x00000010;
532 static final int FLAGS_FOCUSED = 0x00000020;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700533 static final int FLAGS_SELECTED = 0x00000040;
534 static final int FLAGS_ASSIST_BLOCKED = 0x00000080;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700535 static final int FLAGS_CHECKABLE = 0x00000100;
536 static final int FLAGS_CHECKED = 0x00000200;
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000537 static final int FLAGS_CLICKABLE = 0x00000400;
538 static final int FLAGS_LONG_CLICKABLE = 0x00000800;
539 static final int FLAGS_ACCESSIBILITY_FOCUSED = 0x00001000;
540 static final int FLAGS_ACTIVATED = 0x00002000;
541 static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000;
542
Felipe Leme29a5b0d2016-10-25 14:57:11 -0700543 static final int FLAGS_HAS_AUTO_FILL_ID = 0x80000000;
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000544 static final int FLAGS_HAS_MATRIX = 0x40000000;
545 static final int FLAGS_HAS_ALPHA = 0x20000000;
546 static final int FLAGS_HAS_ELEVATION = 0x10000000;
547 static final int FLAGS_HAS_SCROLL = 0x08000000;
548 static final int FLAGS_HAS_LARGE_COORDS = 0x04000000;
549 static final int FLAGS_HAS_CONTENT_DESCRIPTION = 0x02000000;
550 static final int FLAGS_HAS_TEXT = 0x01000000;
Dianne Hackborn6f0fdc42015-07-07 14:29:36 -0700551 static final int FLAGS_HAS_COMPLEX_TEXT = 0x00800000;
552 static final int FLAGS_HAS_EXTRAS = 0x00400000;
553 static final int FLAGS_HAS_ID = 0x00200000;
554 static final int FLAGS_HAS_CHILDREN = 0x00100000;
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000555 static final int FLAGS_ALL_CONTROL = 0xfff00000;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700556
557 int mFlags;
558
559 String mClassName;
560 CharSequence mContentDescription;
561
562 ViewNodeText mText;
563 Bundle mExtras;
564
565 ViewNode[] mChildren;
566
567 ViewNode() {
568 }
569
Dianne Hackborn3e8125b2015-07-22 17:02:10 -0700570 ViewNode(ParcelTransferReader reader, int nestingLevel) {
571 final Parcel in = reader.readParcel(VALIDATE_VIEW_TOKEN, nestingLevel);
Dianne Hackborn782d4982015-07-08 17:36:37 -0700572 reader.mNumReadViews++;
573 final PooledStringReader preader = reader.mStringReader;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700574 mClassName = preader.readString();
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000575 mFlags = in.readInt();
576 final int flags = mFlags;
577 if ((flags&FLAGS_HAS_ID) != 0) {
578 mId = in.readInt();
579 if (mId != 0) {
580 mIdEntry = preader.readString();
581 if (mIdEntry != null) {
582 mIdType = preader.readString();
583 mIdPackage = preader.readString();
584 }
585 }
Dianne Hackborn16036f22015-06-22 14:05:51 -0700586 }
Felipe Leme29a5b0d2016-10-25 14:57:11 -0700587 if ((flags&FLAGS_HAS_AUTO_FILL_ID) != 0) {
588 mAutoFillId = in.readInt();
589 }
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000590 if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
591 mX = in.readInt();
592 mY = in.readInt();
593 mWidth = in.readInt();
594 mHeight = in.readInt();
595 } else {
596 int val = in.readInt();
597 mX = val&0x7fff;
598 mY = (val>>16)&0x7fff;
599 val = in.readInt();
600 mWidth = val&0x7fff;
601 mHeight = (val>>16)&0x7fff;
602 }
603 if ((flags&FLAGS_HAS_SCROLL) != 0) {
604 mScrollX = in.readInt();
605 mScrollY = in.readInt();
606 }
607 if ((flags&FLAGS_HAS_MATRIX) != 0) {
608 mMatrix = new Matrix();
Dianne Hackborn782d4982015-07-08 17:36:37 -0700609 in.readFloatArray(reader.mTmpMatrix);
610 mMatrix.setValues(reader.mTmpMatrix);
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000611 }
612 if ((flags&FLAGS_HAS_ELEVATION) != 0) {
613 mElevation = in.readFloat();
614 }
615 if ((flags&FLAGS_HAS_ALPHA) != 0) {
616 mAlpha = in.readFloat();
617 }
618 if ((flags&FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
619 mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
620 }
621 if ((flags&FLAGS_HAS_TEXT) != 0) {
Dianne Hackborn6f0fdc42015-07-07 14:29:36 -0700622 mText = new ViewNodeText(in, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0);
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000623 }
624 if ((flags&FLAGS_HAS_EXTRAS) != 0) {
625 mExtras = in.readBundle();
626 }
627 if ((flags&FLAGS_HAS_CHILDREN) != 0) {
628 final int NCHILDREN = in.readInt();
Dianne Hackborn3e8125b2015-07-22 17:02:10 -0700629 if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG,
630 "Preparing to read " + NCHILDREN
631 + " children: @ #" + reader.mNumReadViews
632 + ", level " + nestingLevel);
Dianne Hackborn16036f22015-06-22 14:05:51 -0700633 mChildren = new ViewNode[NCHILDREN];
634 for (int i=0; i<NCHILDREN; i++) {
Dianne Hackborn3e8125b2015-07-22 17:02:10 -0700635 mChildren[i] = new ViewNode(reader, nestingLevel + 1);
Dianne Hackborn16036f22015-06-22 14:05:51 -0700636 }
Dianne Hackborn16036f22015-06-22 14:05:51 -0700637 }
638 }
639
Dianne Hackborn782d4982015-07-08 17:36:37 -0700640 int writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000641 int flags = mFlags & ~FLAGS_ALL_CONTROL;
642 if (mId != View.NO_ID) {
643 flags |= FLAGS_HAS_ID;
644 }
Felipe Leme29a5b0d2016-10-25 14:57:11 -0700645 if (mAutoFillId != View.NO_ID) {
646 flags |= FLAGS_HAS_AUTO_FILL_ID;
647 }
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000648 if ((mX&~0x7fff) != 0 || (mY&~0x7fff) != 0
649 || (mWidth&~0x7fff) != 0 | (mHeight&~0x7fff) != 0) {
650 flags |= FLAGS_HAS_LARGE_COORDS;
651 }
652 if (mScrollX != 0 || mScrollY != 0) {
653 flags |= FLAGS_HAS_SCROLL;
654 }
655 if (mMatrix != null) {
656 flags |= FLAGS_HAS_MATRIX;
657 }
658 if (mElevation != 0) {
659 flags |= FLAGS_HAS_ELEVATION;
660 }
661 if (mAlpha != 1.0f) {
662 flags |= FLAGS_HAS_ALPHA;
663 }
664 if (mContentDescription != null) {
665 flags |= FLAGS_HAS_CONTENT_DESCRIPTION;
666 }
667 if (mText != null) {
668 flags |= FLAGS_HAS_TEXT;
Dianne Hackborn6f0fdc42015-07-07 14:29:36 -0700669 if (!mText.isSimple()) {
670 flags |= FLAGS_HAS_COMPLEX_TEXT;
671 }
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000672 }
673 if (mExtras != null) {
674 flags |= FLAGS_HAS_EXTRAS;
675 }
676 if (mChildren != null) {
677 flags |= FLAGS_HAS_CHILDREN;
678 }
679
680 pwriter.writeString(mClassName);
681 out.writeInt(flags);
682 if ((flags&FLAGS_HAS_ID) != 0) {
683 out.writeInt(mId);
684 if (mId != 0) {
685 pwriter.writeString(mIdEntry);
686 if (mIdEntry != null) {
687 pwriter.writeString(mIdType);
688 pwriter.writeString(mIdPackage);
689 }
Dianne Hackborn16036f22015-06-22 14:05:51 -0700690 }
691 }
Felipe Leme29a5b0d2016-10-25 14:57:11 -0700692 if ((flags&FLAGS_HAS_AUTO_FILL_ID) != 0) {
693 out.writeInt(mAutoFillId);
694 }
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000695 if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
696 out.writeInt(mX);
697 out.writeInt(mY);
698 out.writeInt(mWidth);
699 out.writeInt(mHeight);
Dianne Hackborn16036f22015-06-22 14:05:51 -0700700 } else {
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000701 out.writeInt((mY<<16) | mX);
702 out.writeInt((mHeight<<16) | mWidth);
Dianne Hackborn16036f22015-06-22 14:05:51 -0700703 }
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000704 if ((flags&FLAGS_HAS_SCROLL) != 0) {
705 out.writeInt(mScrollX);
706 out.writeInt(mScrollY);
707 }
708 if ((flags&FLAGS_HAS_MATRIX) != 0) {
709 mMatrix.getValues(tmpMatrix);
710 out.writeFloatArray(tmpMatrix);
711 }
712 if ((flags&FLAGS_HAS_ELEVATION) != 0) {
713 out.writeFloat(mElevation);
714 }
715 if ((flags&FLAGS_HAS_ALPHA) != 0) {
716 out.writeFloat(mAlpha);
717 }
718 if ((flags&FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
719 TextUtils.writeToParcel(mContentDescription, out, 0);
720 }
721 if ((flags&FLAGS_HAS_TEXT) != 0) {
Dianne Hackborn6f0fdc42015-07-07 14:29:36 -0700722 mText.writeToParcel(out, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0);
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000723 }
724 if ((flags&FLAGS_HAS_EXTRAS) != 0) {
725 out.writeBundle(mExtras);
726 }
Dianne Hackborn782d4982015-07-08 17:36:37 -0700727 return flags;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700728 }
729
730 /**
731 * Returns the ID associated with this view, as per {@link View#getId() View.getId()}.
732 */
733 public int getId() {
734 return mId;
735 }
736
737 /**
738 * If {@link #getId()} is a resource identifier, this is the package name of that
739 * identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId}
740 * for more information.
741 */
742 public String getIdPackage() {
743 return mIdPackage;
744 }
745
746 /**
747 * If {@link #getId()} is a resource identifier, this is the type name of that
748 * identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId}
749 * for more information.
750 */
751 public String getIdType() {
752 return mIdType;
753 }
754
755 /**
756 * If {@link #getId()} is a resource identifier, this is the entry name of that
757 * identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId}
758 * for more information.
759 */
760 public String getIdEntry() {
761 return mIdEntry;
762 }
763
764 /**
Felipe Leme29a5b0d2016-10-25 14:57:11 -0700765 * Returns the id that can be used to auto-fill the view.
766 *
767 * <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not
768 * for assist.
769 */
770 public int getAutoFillId() {
771 return mAutoFillId;
772 }
773
774 /**
Dianne Hackborn16036f22015-06-22 14:05:51 -0700775 * Returns the left edge of this view, in pixels, relative to the left edge of its parent.
776 */
777 public int getLeft() {
778 return mX;
779 }
780
781 /**
782 * Returns the top edge of this view, in pixels, relative to the top edge of its parent.
783 */
784 public int getTop() {
785 return mY;
786 }
787
788 /**
789 * Returns the current X scroll offset of this view, as per
790 * {@link android.view.View#getScrollX() View.getScrollX()}.
791 */
792 public int getScrollX() {
793 return mScrollX;
794 }
795
796 /**
797 * Returns the current Y scroll offset of this view, as per
798 * {@link android.view.View#getScrollX() View.getScrollY()}.
799 */
800 public int getScrollY() {
801 return mScrollY;
802 }
803
804 /**
805 * Returns the width of this view, in pixels.
806 */
807 public int getWidth() {
808 return mWidth;
809 }
810
811 /**
812 * Returns the height of this view, in pixels.
813 */
814 public int getHeight() {
815 return mHeight;
816 }
817
818 /**
Dianne Hackborn70d8be72015-06-23 19:33:02 +0000819 * Returns the transformation that has been applied to this view, such as a translation
820 * or scaling. The returned Matrix object is owned by ViewNode; do not modify it.
821 * Returns null if there is no transformation applied to the view.
822 */
823 public Matrix getTransformation() {
824 return mMatrix;
825 }
826
827 /**
828 * Returns the visual elevation of the view, used for shadowing and other visual
829 * characterstics, as set by {@link ViewStructure#setElevation
830 * ViewStructure.setElevation(float)}.
831 */
832 public float getElevation() {
833 return mElevation;
834 }
835
836 /**
837 * Returns the alpha transformation of the view, used to reduce the overall opacity
838 * of the view's contents, as set by {@link ViewStructure#setAlpha
839 * ViewStructure.setAlpha(float)}.
840 */
841 public float getAlpha() {
842 return mAlpha;
843 }
844
845 /**
Dianne Hackborn16036f22015-06-22 14:05:51 -0700846 * Returns the visibility mode of this view, as per
847 * {@link android.view.View#getVisibility() View.getVisibility()}.
848 */
849 public int getVisibility() {
850 return mFlags&ViewNode.FLAGS_VISIBILITY_MASK;
851 }
852
853 /**
854 * Returns true if assist data has been blocked starting at this node in the hierarchy.
855 */
856 public boolean isAssistBlocked() {
Dianne Hackbornfcbfeaf2015-07-15 11:25:55 -0700857 return (mFlags&ViewNode.FLAGS_ASSIST_BLOCKED) != 0;
Dianne Hackborn16036f22015-06-22 14:05:51 -0700858 }
859
860 /**
861 * Returns true if this node is in an enabled state.
862 */
863 public boolean isEnabled() {
864 return (mFlags&ViewNode.FLAGS_DISABLED) == 0;
865 }
866
867 /**
868 * Returns true if this node is clickable by the user.
869 */
870 public boolean isClickable() {
871 return (mFlags&ViewNode.FLAGS_CLICKABLE) != 0;
872 }
873
874 /**
875 * Returns true if this node can take input focus.
876 */
877 public boolean isFocusable() {
878 return (mFlags&ViewNode.FLAGS_FOCUSABLE) != 0;
879 }
880
881 /**
882 * Returns true if this node currently had input focus at the time that the
883 * structure was collected.
884 */
885 public boolean isFocused() {
886 return (mFlags&ViewNode.FLAGS_FOCUSED) != 0;
887 }
888
889 /**
890 * Returns true if this node currently had accessibility focus at the time that the
891 * structure was collected.
892 */
893 public boolean isAccessibilityFocused() {
894 return (mFlags&ViewNode.FLAGS_ACCESSIBILITY_FOCUSED) != 0;
895 }
896
897 /**
898 * Returns true if this node represents something that is checkable by the user.
899 */
900 public boolean isCheckable() {
901 return (mFlags&ViewNode.FLAGS_CHECKABLE) != 0;
902 }
903
904 /**
905 * Returns true if this node is currently in a checked state.
906 */
907 public boolean isChecked() {
908 return (mFlags&ViewNode.FLAGS_CHECKED) != 0;
909 }
910
911 /**
912 * Returns true if this node has currently been selected by the user.
913 */
914 public boolean isSelected() {
915 return (mFlags&ViewNode.FLAGS_SELECTED) != 0;
916 }
917
918 /**
919 * Returns true if this node has currently been activated by the user.
920 */
921 public boolean isActivated() {
922 return (mFlags&ViewNode.FLAGS_ACTIVATED) != 0;
923 }
924
925 /**
926 * Returns true if this node is something the user can perform a long click/press on.
927 */
928 public boolean isLongClickable() {
929 return (mFlags&ViewNode.FLAGS_LONG_CLICKABLE) != 0;
930 }
931
932 /**
933 * Returns true if this node is something the user can perform a context click on.
934 */
935 public boolean isContextClickable() {
936 return (mFlags&ViewNode.FLAGS_CONTEXT_CLICKABLE) != 0;
937 }
938
939 /**
940 * Returns the class name of the node's implementation, indicating its behavior.
941 * For example, a button will report "android.widget.Button" meaning it behaves
942 * like a {@link android.widget.Button}.
943 */
944 public String getClassName() {
945 return mClassName;
946 }
947
948 /**
949 * Returns any content description associated with the node, which semantically describes
950 * its purpose for accessibility and other uses.
951 */
952 public CharSequence getContentDescription() {
953 return mContentDescription;
954 }
955
956 /**
957 * Returns any text associated with the node that is displayed to the user, or null
958 * if there is none.
959 */
960 public CharSequence getText() {
961 return mText != null ? mText.mText : null;
962 }
963
964 /**
965 * If {@link #getText()} is non-null, this is where the current selection starts.
966 */
967 public int getTextSelectionStart() {
968 return mText != null ? mText.mTextSelectionStart : -1;
969 }
970
971 /**
972 * If {@link #getText()} is non-null, this is where the current selection starts.
973 * If there is no selection, returns the same value as {@link #getTextSelectionStart()},
974 * indicating the cursor position.
975 */
976 public int getTextSelectionEnd() {
977 return mText != null ? mText.mTextSelectionEnd : -1;
978 }
979
980 /**
981 * If {@link #getText()} is non-null, this is the main text color associated with it.
982 * If there is no text color, {@link #TEXT_COLOR_UNDEFINED} is returned.
983 * Note that the text may also contain style spans that modify the color of specific
984 * parts of the text.
985 */
986 public int getTextColor() {
987 return mText != null ? mText.mTextColor : TEXT_COLOR_UNDEFINED;
988 }
989
990 /**
991 * If {@link #getText()} is non-null, this is the main text background color associated
992 * with it.
993 * If there is no text background color, {@link #TEXT_COLOR_UNDEFINED} is returned.
994 * Note that the text may also contain style spans that modify the color of specific
995 * parts of the text.
996 */
997 public int getTextBackgroundColor() {
998 return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
999 }
1000
1001 /**
1002 * If {@link #getText()} is non-null, this is the main text size (in pixels) associated
1003 * with it.
1004 * Note that the text may also contain style spans that modify the size of specific
1005 * parts of the text.
1006 */
1007 public float getTextSize() {
1008 return mText != null ? mText.mTextSize : 0;
1009 }
1010
1011 /**
1012 * If {@link #getText()} is non-null, this is the main text style associated
1013 * with it, containing a bit mask of {@link #TEXT_STYLE_BOLD},
1014 * {@link #TEXT_STYLE_BOLD}, {@link #TEXT_STYLE_STRIKE_THRU}, and/or
1015 * {@link #TEXT_STYLE_UNDERLINE}.
1016 * Note that the text may also contain style spans that modify the style of specific
1017 * parts of the text.
1018 */
1019 public int getTextStyle() {
1020 return mText != null ? mText.mTextStyle : 0;
1021 }
1022
1023 /**
Dianne Hackborn6f0fdc42015-07-07 14:29:36 -07001024 * Return per-line offsets into the text returned by {@link #getText()}. Each entry
1025 * in the array is a formatted line of text, and the value it contains is the offset
1026 * into the text string where that line starts. May return null if there is no line
1027 * information.
1028 */
1029 public int[] getTextLineCharOffsets() {
1030 return mText != null ? mText.mLineCharOffsets : null;
1031 }
1032
1033 /**
1034 * Return per-line baselines into the text returned by {@link #getText()}. Each entry
1035 * in the array is a formatted line of text, and the value it contains is the baseline
1036 * where that text appears in the view. May return null if there is no line
1037 * information.
1038 */
1039 public int[] getTextLineBaselines() {
1040 return mText != null ? mText.mLineBaselines : null;
1041 }
1042
1043 /**
Dianne Hackborn16036f22015-06-22 14:05:51 -07001044 * Return additional hint text associated with the node; this is typically used with
1045 * a node that takes user input, describing to the user what the input means.
1046 */
1047 public String getHint() {
1048 return mText != null ? mText.mHint : null;
1049 }
1050
1051 /**
1052 * Return a Bundle containing optional vendor-specific extension information.
1053 */
1054 public Bundle getExtras() {
1055 return mExtras;
1056 }
1057
1058 /**
1059 * Return the number of children this node has.
1060 */
1061 public int getChildCount() {
1062 return mChildren != null ? mChildren.length : 0;
1063 }
1064
1065 /**
1066 * Return a child of this node, given an index value from 0 to
1067 * {@link #getChildCount()}-1.
1068 */
1069 public ViewNode getChildAt(int index) {
1070 return mChildren[index];
1071 }
1072 }
1073
Dianne Hackborn8ecf16d2015-06-23 13:09:21 -07001074 static class ViewNodeBuilder extends ViewStructure {
Dianne Hackborn16036f22015-06-22 14:05:51 -07001075 final AssistStructure mAssist;
1076 final ViewNode mNode;
1077 final boolean mAsync;
1078
1079 ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) {
1080 mAssist = assist;
1081 mNode = node;
1082 mAsync = async;
1083 }
1084
1085 @Override
1086 public void setId(int id, String packageName, String typeName, String entryName) {
1087 mNode.mId = id;
1088 mNode.mIdPackage = packageName;
1089 mNode.mIdType = typeName;
1090 mNode.mIdEntry = entryName;
1091 }
1092
1093 @Override
1094 public void setDimens(int left, int top, int scrollX, int scrollY, int width, int height) {
1095 mNode.mX = left;
1096 mNode.mY = top;
1097 mNode.mScrollX = scrollX;
1098 mNode.mScrollY = scrollY;
1099 mNode.mWidth = width;
1100 mNode.mHeight = height;
1101 }
1102
1103 @Override
Dianne Hackborn70d8be72015-06-23 19:33:02 +00001104 public void setTransformation(Matrix matrix) {
1105 if (matrix == null) {
1106 mNode.mMatrix = null;
1107 } else {
1108 mNode.mMatrix = new Matrix(matrix);
1109 }
1110 }
1111
1112 @Override
1113 public void setElevation(float elevation) {
1114 mNode.mElevation = elevation;
1115 }
1116
1117 @Override
1118 public void setAlpha(float alpha) {
1119 mNode.mAlpha = alpha;
1120 }
1121
1122 @Override
Dianne Hackborn16036f22015-06-22 14:05:51 -07001123 public void setVisibility(int visibility) {
1124 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_VISIBILITY_MASK) | visibility;
1125 }
1126
1127 @Override
1128 public void setAssistBlocked(boolean state) {
1129 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ASSIST_BLOCKED)
Dianne Hackbornafb308d2015-07-31 13:10:55 -07001130 | (state ? ViewNode.FLAGS_ASSIST_BLOCKED : 0);
Dianne Hackborn16036f22015-06-22 14:05:51 -07001131 }
1132
1133 @Override
1134 public void setEnabled(boolean state) {
1135 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_DISABLED)
1136 | (state ? 0 : ViewNode.FLAGS_DISABLED);
1137 }
1138
1139 @Override
1140 public void setClickable(boolean state) {
1141 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CLICKABLE)
1142 | (state ? ViewNode.FLAGS_CLICKABLE : 0);
1143 }
1144
1145 @Override
1146 public void setLongClickable(boolean state) {
1147 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_LONG_CLICKABLE)
1148 | (state ? ViewNode.FLAGS_LONG_CLICKABLE : 0);
1149 }
1150
1151 @Override
1152 public void setContextClickable(boolean state) {
1153 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CONTEXT_CLICKABLE)
1154 | (state ? ViewNode.FLAGS_CONTEXT_CLICKABLE : 0);
1155 }
1156
1157 @Override
1158 public void setFocusable(boolean state) {
1159 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSABLE)
1160 | (state ? ViewNode.FLAGS_FOCUSABLE : 0);
1161 }
1162
1163 @Override
1164 public void setFocused(boolean state) {
1165 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSED)
1166 | (state ? ViewNode.FLAGS_FOCUSED : 0);
1167 }
1168
1169 @Override
1170 public void setAccessibilityFocused(boolean state) {
1171 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACCESSIBILITY_FOCUSED)
1172 | (state ? ViewNode.FLAGS_ACCESSIBILITY_FOCUSED : 0);
1173 }
1174
1175 @Override
1176 public void setCheckable(boolean state) {
1177 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKABLE)
1178 | (state ? ViewNode.FLAGS_CHECKABLE : 0);
1179 }
1180
1181 @Override
1182 public void setChecked(boolean state) {
1183 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKED)
1184 | (state ? ViewNode.FLAGS_CHECKED : 0);
1185 }
1186
1187 @Override
1188 public void setSelected(boolean state) {
1189 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_SELECTED)
1190 | (state ? ViewNode.FLAGS_SELECTED : 0);
1191 }
1192
1193 @Override
1194 public void setActivated(boolean state) {
1195 mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACTIVATED)
1196 | (state ? ViewNode.FLAGS_ACTIVATED : 0);
1197 }
1198
1199 @Override
1200 public void setClassName(String className) {
1201 mNode.mClassName = className;
1202 }
1203
1204 @Override
1205 public void setContentDescription(CharSequence contentDescription) {
1206 mNode.mContentDescription = contentDescription;
1207 }
1208
1209 private final ViewNodeText getNodeText() {
1210 if (mNode.mText != null) {
1211 return mNode.mText;
1212 }
1213 mNode.mText = new ViewNodeText();
1214 return mNode.mText;
1215 }
1216
1217 @Override
1218 public void setText(CharSequence text) {
1219 ViewNodeText t = getNodeText();
1220 t.mText = text;
1221 t.mTextSelectionStart = t.mTextSelectionEnd = -1;
1222 }
1223
1224 @Override
1225 public void setText(CharSequence text, int selectionStart, int selectionEnd) {
1226 ViewNodeText t = getNodeText();
1227 t.mText = text;
1228 t.mTextSelectionStart = selectionStart;
1229 t.mTextSelectionEnd = selectionEnd;
1230 }
1231
1232 @Override
1233 public void setTextStyle(float size, int fgColor, int bgColor, int style) {
1234 ViewNodeText t = getNodeText();
1235 t.mTextColor = fgColor;
1236 t.mTextBackgroundColor = bgColor;
1237 t.mTextSize = size;
1238 t.mTextStyle = style;
1239 }
1240
1241 @Override
Dianne Hackborn6f0fdc42015-07-07 14:29:36 -07001242 public void setTextLines(int[] charOffsets, int[] baselines) {
1243 ViewNodeText t = getNodeText();
1244 t.mLineCharOffsets = charOffsets;
1245 t.mLineBaselines = baselines;
1246 }
1247
1248 @Override
Dianne Hackborn16036f22015-06-22 14:05:51 -07001249 public void setHint(CharSequence hint) {
1250 getNodeText().mHint = hint != null ? hint.toString() : null;
1251 }
1252
1253 @Override
1254 public CharSequence getText() {
1255 return mNode.mText != null ? mNode.mText.mText : null;
1256 }
1257
1258 @Override
1259 public int getTextSelectionStart() {
1260 return mNode.mText != null ? mNode.mText.mTextSelectionStart : -1;
1261 }
1262
1263 @Override
1264 public int getTextSelectionEnd() {
1265 return mNode.mText != null ? mNode.mText.mTextSelectionEnd : -1;
1266 }
1267
1268 @Override
1269 public CharSequence getHint() {
1270 return mNode.mText != null ? mNode.mText.mHint : null;
1271 }
1272
1273 @Override
1274 public Bundle getExtras() {
1275 if (mNode.mExtras != null) {
1276 return mNode.mExtras;
1277 }
1278 mNode.mExtras = new Bundle();
1279 return mNode.mExtras;
1280 }
1281
1282 @Override
1283 public boolean hasExtras() {
1284 return mNode.mExtras != null;
1285 }
1286
1287 @Override
1288 public void setChildCount(int num) {
1289 mNode.mChildren = new ViewNode[num];
1290 }
1291
1292 @Override
1293 public int addChildCount(int num) {
1294 if (mNode.mChildren == null) {
1295 setChildCount(num);
1296 return 0;
1297 }
1298 final int start = mNode.mChildren.length;
1299 ViewNode[] newArray = new ViewNode[start + num];
1300 System.arraycopy(mNode.mChildren, 0, newArray, 0, start);
1301 mNode.mChildren = newArray;
1302 return start;
1303 }
1304
1305 @Override
1306 public int getChildCount() {
1307 return mNode.mChildren != null ? mNode.mChildren.length : 0;
1308 }
1309
1310 @Override
Dianne Hackborn8ecf16d2015-06-23 13:09:21 -07001311 public ViewStructure newChild(int index) {
Dianne Hackborn16036f22015-06-22 14:05:51 -07001312 ViewNode node = new ViewNode();
1313 mNode.mChildren[index] = node;
1314 return new ViewNodeBuilder(mAssist, node, false);
1315 }
1316
1317 @Override
Dianne Hackborn8ecf16d2015-06-23 13:09:21 -07001318 public ViewStructure asyncNewChild(int index) {
Dianne Hackborn16036f22015-06-22 14:05:51 -07001319 synchronized (mAssist) {
1320 ViewNode node = new ViewNode();
1321 mNode.mChildren[index] = node;
1322 ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true);
1323 mAssist.mPendingAsyncChildren.add(builder);
1324 return builder;
1325 }
1326 }
1327
1328 @Override
1329 public void asyncCommit() {
1330 synchronized (mAssist) {
1331 if (!mAsync) {
1332 throw new IllegalStateException("Child " + this
Dianne Hackborn8ecf16d2015-06-23 13:09:21 -07001333 + " was not created with ViewStructure.asyncNewChild");
Dianne Hackborn16036f22015-06-22 14:05:51 -07001334 }
1335 if (!mAssist.mPendingAsyncChildren.remove(this)) {
1336 throw new IllegalStateException("Child " + this + " already committed");
1337 }
1338 mAssist.notifyAll();
1339 }
1340 }
1341
1342 @Override
1343 public Rect getTempRect() {
1344 return mAssist.mTmpRect;
1345 }
Felipe Leme29a5b0d2016-10-25 14:57:11 -07001346
1347 @Override
1348 public void setAutoFillId(int autoFillId) {
1349 mNode.mAutoFillId = autoFillId;
1350 }
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001351 }
1352
1353 /** @hide */
1354 public AssistStructure(Activity activity) {
Dianne Hackborn16036f22015-06-22 14:05:51 -07001355 mHaveData = true;
1356 mActivityComponent = activity.getComponentName();
1357 ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
1358 activity.getActivityToken());
1359 for (int i=0; i<views.size(); i++) {
1360 ViewRootImpl root = views.get(i);
1361 mWindowNodes.add(new WindowNode(this, root));
1362 }
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001363 }
1364
Dianne Hackborn16036f22015-06-22 14:05:51 -07001365 public AssistStructure() {
1366 mHaveData = true;
1367 mActivityComponent = null;
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001368 }
1369
Dianne Hackborn16036f22015-06-22 14:05:51 -07001370 /** @hide */
1371 public AssistStructure(Parcel in) {
1372 mReceiveChannel = in.readStrongBinder();
1373 }
1374
1375 /** @hide */
1376 public void dump() {
1377 Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString());
1378 final int N = getWindowNodeCount();
1379 for (int i=0; i<N; i++) {
1380 WindowNode node = getWindowNodeAt(i);
1381 Log.i(TAG, "Window #" + i + " [" + node.getLeft() + "," + node.getTop()
1382 + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getTitle());
1383 dump(" ", node.getRootViewNode());
1384 }
1385 }
1386
1387 void dump(String prefix, ViewNode node) {
1388 Log.i(TAG, prefix + "View [" + node.getLeft() + "," + node.getTop()
1389 + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getClassName());
1390 int id = node.getId();
1391 if (id != 0) {
1392 StringBuilder sb = new StringBuilder();
1393 sb.append(prefix); sb.append(" ID: #"); sb.append(Integer.toHexString(id));
1394 String entry = node.getIdEntry();
1395 if (entry != null) {
1396 String type = node.getIdType();
1397 String pkg = node.getIdPackage();
1398 sb.append(" "); sb.append(pkg); sb.append(":"); sb.append(type);
1399 sb.append("/"); sb.append(entry);
1400 }
1401 Log.i(TAG, sb.toString());
1402 }
1403 int scrollX = node.getScrollX();
1404 int scrollY = node.getScrollY();
1405 if (scrollX != 0 || scrollY != 0) {
1406 Log.i(TAG, prefix + " Scroll: " + scrollX + "," + scrollY);
1407 }
Dianne Hackborn70d8be72015-06-23 19:33:02 +00001408 Matrix matrix = node.getTransformation();
1409 if (matrix != null) {
1410 Log.i(TAG, prefix + " Transformation: " + matrix);
1411 }
1412 float elevation = node.getElevation();
1413 if (elevation != 0) {
1414 Log.i(TAG, prefix + " Elevation: " + elevation);
1415 }
1416 float alpha = node.getAlpha();
1417 if (alpha != 0) {
1418 Log.i(TAG, prefix + " Alpha: " + elevation);
1419 }
Dianne Hackborn16036f22015-06-22 14:05:51 -07001420 CharSequence contentDescription = node.getContentDescription();
1421 if (contentDescription != null) {
1422 Log.i(TAG, prefix + " Content description: " + contentDescription);
1423 }
1424 CharSequence text = node.getText();
1425 if (text != null) {
1426 Log.i(TAG, prefix + " Text (sel " + node.getTextSelectionStart() + "-"
1427 + node.getTextSelectionEnd() + "): " + text);
1428 Log.i(TAG, prefix + " Text size: " + node.getTextSize() + " , style: #"
1429 + node.getTextStyle());
1430 Log.i(TAG, prefix + " Text color fg: #" + Integer.toHexString(node.getTextColor())
1431 + ", bg: #" + Integer.toHexString(node.getTextBackgroundColor()));
1432 }
1433 String hint = node.getHint();
1434 if (hint != null) {
1435 Log.i(TAG, prefix + " Hint: " + hint);
1436 }
1437 Bundle extras = node.getExtras();
1438 if (extras != null) {
1439 Log.i(TAG, prefix + " Extras: " + extras);
1440 }
Dianne Hackbornafb308d2015-07-31 13:10:55 -07001441 if (node.isAssistBlocked()) {
1442 Log.i(TAG, prefix + " BLOCKED");
1443 }
Dianne Hackborn16036f22015-06-22 14:05:51 -07001444 final int NCHILDREN = node.getChildCount();
1445 if (NCHILDREN > 0) {
1446 Log.i(TAG, prefix + " Children:");
1447 String cprefix = prefix + " ";
1448 for (int i=0; i<NCHILDREN; i++) {
1449 ViewNode cnode = node.getChildAt(i);
1450 dump(cprefix, cnode);
1451 }
1452 }
1453 }
1454
1455 /**
1456 * Return the activity this AssistStructure came from.
1457 */
1458 public ComponentName getActivityComponent() {
1459 ensureData();
1460 return mActivityComponent;
1461 }
1462
1463 /**
1464 * Return the number of window contents that have been collected in this assist data.
1465 */
1466 public int getWindowNodeCount() {
1467 ensureData();
1468 return mWindowNodes.size();
1469 }
1470
1471 /**
1472 * Return one of the windows in the assist data.
1473 * @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1.
1474 */
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001475 public WindowNode getWindowNodeAt(int index) {
Dianne Hackborn16036f22015-06-22 14:05:51 -07001476 ensureData();
1477 return mWindowNodes.get(index);
1478 }
1479
1480 /** @hide */
1481 public void ensureData() {
1482 if (mHaveData) {
1483 return;
1484 }
1485 mHaveData = true;
Dianne Hackborn782d4982015-07-08 17:36:37 -07001486 ParcelTransferReader reader = new ParcelTransferReader(mReceiveChannel);
1487 reader.go();
Dianne Hackborn16036f22015-06-22 14:05:51 -07001488 }
1489
Dianne Hackborn782d4982015-07-08 17:36:37 -07001490 boolean waitForReady() {
Dianne Hackborn16036f22015-06-22 14:05:51 -07001491 boolean skipStructure = false;
1492 synchronized (this) {
1493 long endTime = SystemClock.uptimeMillis() + 5000;
1494 long now;
1495 while (mPendingAsyncChildren.size() > 0 && (now=SystemClock.uptimeMillis()) < endTime) {
1496 try {
1497 wait(endTime-now);
1498 } catch (InterruptedException e) {
1499 }
1500 }
1501 if (mPendingAsyncChildren.size() > 0) {
1502 // We waited too long, assume none of the assist structure is valid.
Dianne Hackborn70d8be72015-06-23 19:33:02 +00001503 Log.w(TAG, "Skipping assist structure, waiting too long for async children (have "
1504 + mPendingAsyncChildren.size() + " remaining");
Dianne Hackborn16036f22015-06-22 14:05:51 -07001505 skipStructure = true;
1506 }
1507 }
Dianne Hackborn782d4982015-07-08 17:36:37 -07001508 return !skipStructure;
Dianne Hackborn16036f22015-06-22 14:05:51 -07001509 }
1510
Dianne Hackborn782d4982015-07-08 17:36:37 -07001511 /** @hide */
1512 public void clearSendChannel() {
1513 if (mSendChannel != null) {
1514 mSendChannel.mAssistStructure = null;
Dianne Hackborn16036f22015-06-22 14:05:51 -07001515 }
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001516 }
1517
1518 public int describeContents() {
1519 return 0;
1520 }
1521
1522 public void writeToParcel(Parcel out, int flags) {
1523 if (mHaveData) {
1524 // This object holds its data. We want to write a send channel that the
1525 // other side can use to retrieve that data.
1526 if (mSendChannel == null) {
Dianne Hackborn782d4982015-07-08 17:36:37 -07001527 mSendChannel = new SendChannel(this);
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001528 }
1529 out.writeStrongBinder(mSendChannel);
1530 } else {
1531 // This object doesn't hold its data, so just propagate along its receive channel.
1532 out.writeStrongBinder(mReceiveChannel);
1533 }
1534 }
1535
1536 public static final Parcelable.Creator<AssistStructure> CREATOR
1537 = new Parcelable.Creator<AssistStructure>() {
1538 public AssistStructure createFromParcel(Parcel in) {
1539 return new AssistStructure(in);
1540 }
1541
1542 public AssistStructure[] newArray(int size) {
1543 return new AssistStructure[size];
1544 }
1545 };
1546}