| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.app; |
| |
| import android.content.ComponentName; |
| import android.graphics.Paint; |
| import android.graphics.Rect; |
| import android.graphics.Typeface; |
| import android.os.Binder; |
| import android.os.Bundle; |
| import android.os.IBinder; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.os.PooledStringReader; |
| import android.os.PooledStringWriter; |
| import android.os.RemoteException; |
| import android.os.SystemClock; |
| import android.text.TextPaint; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.view.View; |
| import android.view.ViewAssistStructure; |
| import android.view.ViewRootImpl; |
| import android.view.WindowManagerGlobal; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * Assist data automatically created by the platform's implementation |
| * of {@link Activity#onProvideAssistData}. Retrieve it from the assist |
| * data with {@link #getAssistStructure(android.os.Bundle)}. |
| */ |
| final public class AssistStructure implements Parcelable { |
| static final String TAG = "AssistStructure"; |
| |
| /** |
| * Key name this data structure is stored in the Bundle generated by |
| * {@link Activity#onProvideAssistData}. |
| */ |
| public static final String ASSIST_KEY = "android:assist_structure"; |
| |
| boolean mHaveData; |
| |
| ComponentName mActivityComponent; |
| |
| final ArrayList<WindowNode> mWindowNodes = new ArrayList<>(); |
| |
| final ArrayList<ViewNodeBuilder> mPendingAsyncChildren = new ArrayList<>(); |
| |
| SendChannel mSendChannel; |
| IBinder mReceiveChannel; |
| |
| Rect mTmpRect = new Rect(); |
| |
| static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1; |
| static final String DESCRIPTOR = "android.app.AssistStructure"; |
| |
| final class SendChannel extends Binder { |
| @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) |
| throws RemoteException { |
| if (code == TRANSACTION_XFER) { |
| data.enforceInterface(DESCRIPTOR); |
| writeContentToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); |
| return true; |
| } else { |
| return super.onTransact(code, data, reply, flags); |
| } |
| } |
| } |
| |
| final static class ViewNodeText { |
| CharSequence mText; |
| int mTextSelectionStart; |
| int mTextSelectionEnd; |
| int mTextColor; |
| int mTextBackgroundColor; |
| float mTextSize; |
| int mTextStyle; |
| String mHint; |
| |
| ViewNodeText() { |
| } |
| |
| ViewNodeText(Parcel in) { |
| mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); |
| mTextSelectionStart = in.readInt(); |
| mTextSelectionEnd = in.readInt(); |
| mTextColor = in.readInt(); |
| mTextBackgroundColor = in.readInt(); |
| mTextSize = in.readFloat(); |
| mTextStyle = in.readInt(); |
| mHint = in.readString(); |
| } |
| |
| void writeToParcel(Parcel out) { |
| TextUtils.writeToParcel(mText, out, 0); |
| out.writeInt(mTextSelectionStart); |
| out.writeInt(mTextSelectionEnd); |
| out.writeInt(mTextColor); |
| out.writeInt(mTextBackgroundColor); |
| out.writeFloat(mTextSize); |
| out.writeInt(mTextStyle); |
| 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 ViewNode mRoot; |
| |
| WindowNode(AssistStructure assist, ViewRootImpl root) { |
| 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(); |
| mRoot = new ViewNode(); |
| ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false); |
| view.dispatchProvideAssistStructure(builder); |
| } |
| |
| WindowNode(Parcel in, PooledStringReader preader) { |
| mX = in.readInt(); |
| mY = in.readInt(); |
| mWidth = in.readInt(); |
| mHeight = in.readInt(); |
| mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); |
| mRoot = new ViewNode(in, preader); |
| } |
| |
| void writeToParcel(Parcel out, PooledStringWriter pwriter) { |
| out.writeInt(mX); |
| out.writeInt(mY); |
| out.writeInt(mWidth); |
| out.writeInt(mHeight); |
| TextUtils.writeToParcel(mTitle, out, 0); |
| mRoot.writeToParcel(out, pwriter); |
| } |
| |
| public int getLeft() { |
| return mX; |
| } |
| |
| public int getTop() { |
| return mY; |
| } |
| |
| public int getWidth() { |
| return mWidth; |
| } |
| |
| public int getHeight() { |
| return mHeight; |
| } |
| |
| public CharSequence getTitle() { |
| return mTitle; |
| } |
| |
| 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; |
| String mIdPackage; |
| String mIdType; |
| String mIdEntry; |
| int mX; |
| int mY; |
| int mScrollX; |
| int mScrollY; |
| int mWidth; |
| int mHeight; |
| |
| 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_ACCESSIBILITY_FOCUSED = 0x04000000; |
| static final int FLAGS_SELECTED = 0x00000040; |
| static final int FLAGS_ASSIST_BLOCKED = 0x00000080; |
| static final int FLAGS_ACTIVATED = 0x40000000; |
| static final int FLAGS_CHECKABLE = 0x00000100; |
| static final int FLAGS_CHECKED = 0x00000200; |
| static final int FLAGS_CLICKABLE = 0x00004000; |
| static final int FLAGS_LONG_CLICKABLE = 0x00200000; |
| |
| int mFlags; |
| |
| String mClassName; |
| CharSequence mContentDescription; |
| |
| ViewNodeText mText; |
| Bundle mExtras; |
| |
| ViewNode[] mChildren; |
| |
| ViewNode() { |
| } |
| |
| ViewNode(Parcel in, PooledStringReader preader) { |
| mId = in.readInt(); |
| if (mId != 0) { |
| mIdEntry = preader.readString(); |
| if (mIdEntry != null) { |
| mIdType = preader.readString(); |
| mIdPackage = preader.readString(); |
| } else { |
| mIdPackage = mIdType = null; |
| } |
| } else { |
| mIdPackage = mIdType = mIdEntry = null; |
| } |
| mX = in.readInt(); |
| mY = in.readInt(); |
| mScrollX = in.readInt(); |
| mScrollY = in.readInt(); |
| mWidth = in.readInt(); |
| mHeight = in.readInt(); |
| mFlags = in.readInt(); |
| mClassName = preader.readString(); |
| mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); |
| if (in.readInt() != 0) { |
| mText = new ViewNodeText(in); |
| } else { |
| mText = null; |
| } |
| mExtras = in.readBundle(); |
| final int NCHILDREN = in.readInt(); |
| if (NCHILDREN > 0) { |
| mChildren = new ViewNode[NCHILDREN]; |
| for (int i=0; i<NCHILDREN; i++) { |
| mChildren[i] = new ViewNode(in, preader); |
| } |
| } else { |
| mChildren = null; |
| } |
| } |
| |
| void writeToParcel(Parcel out, PooledStringWriter pwriter) { |
| out.writeInt(mId); |
| if (mId != 0) { |
| pwriter.writeString(mIdEntry); |
| if (mIdEntry != null) { |
| pwriter.writeString(mIdType); |
| pwriter.writeString(mIdPackage); |
| } |
| } |
| out.writeInt(mX); |
| out.writeInt(mY); |
| out.writeInt(mScrollX); |
| out.writeInt(mScrollY); |
| out.writeInt(mWidth); |
| out.writeInt(mHeight); |
| out.writeInt(mFlags); |
| pwriter.writeString(mClassName); |
| TextUtils.writeToParcel(mContentDescription, out, 0); |
| if (mText != null) { |
| out.writeInt(1); |
| mText.writeToParcel(out); |
| } else { |
| out.writeInt(0); |
| } |
| out.writeBundle(mExtras); |
| if (mChildren != null) { |
| final int NCHILDREN = mChildren.length; |
| out.writeInt(NCHILDREN); |
| for (int i=0; i<NCHILDREN; i++) { |
| mChildren[i].writeToParcel(out, pwriter); |
| } |
| } else { |
| out.writeInt(0); |
| } |
| } |
| |
| public int getId() { |
| return mId; |
| } |
| |
| public String getIdPackage() { |
| return mIdPackage; |
| } |
| |
| public String getIdType() { |
| return mIdType; |
| } |
| |
| public String getIdEntry() { |
| return mIdEntry; |
| } |
| |
| public int getLeft() { |
| return mX; |
| } |
| |
| public int getTop() { |
| return mY; |
| } |
| |
| public int getScrollX() { |
| return mScrollX; |
| } |
| |
| public int getScrollY() { |
| return mScrollY; |
| } |
| |
| public int getWidth() { |
| return mWidth; |
| } |
| |
| public int getHeight() { |
| return mHeight; |
| } |
| |
| public int getVisibility() { |
| return mFlags&ViewNode.FLAGS_VISIBILITY_MASK; |
| } |
| |
| public boolean isAssistBlocked() { |
| return (mFlags&ViewNode.FLAGS_ASSIST_BLOCKED) == 0; |
| } |
| |
| public boolean isEnabled() { |
| return (mFlags&ViewNode.FLAGS_DISABLED) == 0; |
| } |
| |
| public boolean isClickable() { |
| return (mFlags&ViewNode.FLAGS_CLICKABLE) != 0; |
| } |
| |
| public boolean isFocusable() { |
| return (mFlags&ViewNode.FLAGS_FOCUSABLE) != 0; |
| } |
| |
| public boolean isFocused() { |
| return (mFlags&ViewNode.FLAGS_FOCUSED) != 0; |
| } |
| |
| public boolean isAccessibilityFocused() { |
| return (mFlags&ViewNode.FLAGS_ACCESSIBILITY_FOCUSED) != 0; |
| } |
| |
| public boolean isCheckable() { |
| return (mFlags&ViewNode.FLAGS_CHECKABLE) != 0; |
| } |
| |
| public boolean isChecked() { |
| return (mFlags&ViewNode.FLAGS_CHECKED) != 0; |
| } |
| |
| public boolean isSelected() { |
| return (mFlags&ViewNode.FLAGS_SELECTED) != 0; |
| } |
| |
| public boolean isActivated() { |
| return (mFlags&ViewNode.FLAGS_ACTIVATED) != 0; |
| } |
| |
| public boolean isLongClickable() { |
| return (mFlags&ViewNode.FLAGS_LONG_CLICKABLE) != 0; |
| } |
| |
| public String getClassName() { |
| return mClassName; |
| } |
| |
| public CharSequence getContentDescription() { |
| return mContentDescription; |
| } |
| |
| public CharSequence getText() { |
| return mText != null ? mText.mText : null; |
| } |
| |
| public int getTextSelectionStart() { |
| return mText != null ? mText.mTextSelectionStart : -1; |
| } |
| |
| public int getTextSelectionEnd() { |
| return mText != null ? mText.mTextSelectionEnd : -1; |
| } |
| |
| public int getTextColor() { |
| return mText != null ? mText.mTextColor : TEXT_COLOR_UNDEFINED; |
| } |
| |
| public int getTextBackgroundColor() { |
| return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED; |
| } |
| |
| public float getTextSize() { |
| return mText != null ? mText.mTextSize : 0; |
| } |
| |
| public int getTextStyle() { |
| return mText != null ? mText.mTextStyle : 0; |
| } |
| |
| public String getHint() { |
| return mText != null ? mText.mHint : null; |
| } |
| |
| public Bundle getExtras() { |
| return mExtras; |
| } |
| |
| public int getChildCount() { |
| return mChildren != null ? mChildren.length : 0; |
| } |
| |
| public ViewNode getChildAt(int index) { |
| return mChildren[index]; |
| } |
| } |
| |
| static class ViewNodeBuilder extends ViewAssistStructure { |
| final AssistStructure mAssist; |
| final ViewNode mNode; |
| final boolean mAsync; |
| |
| ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) { |
| mAssist = assist; |
| mNode = node; |
| mAsync = async; |
| } |
| |
| @Override |
| public void setId(int id, String packageName, String typeName, String entryName) { |
| mNode.mId = id; |
| mNode.mIdPackage = packageName; |
| mNode.mIdType = typeName; |
| mNode.mIdEntry = entryName; |
| } |
| |
| @Override |
| public void setDimens(int left, int top, int scrollX, int scrollY, int width, int height) { |
| mNode.mX = left; |
| mNode.mY = top; |
| mNode.mScrollX = scrollX; |
| mNode.mScrollY = scrollY; |
| mNode.mWidth = width; |
| mNode.mHeight = height; |
| } |
| |
| @Override |
| public void setVisibility(int visibility) { |
| mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_VISIBILITY_MASK) | visibility; |
| } |
| |
| @Override |
| public void setAssistBlocked(boolean state) { |
| mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ASSIST_BLOCKED) |
| | (state ? 0 : ViewNode.FLAGS_ASSIST_BLOCKED); |
| } |
| |
| @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 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 setClassName(String className) { |
| mNode.mClassName = className; |
| } |
| |
| @Override |
| public void setContentDescription(CharSequence contentDescription) { |
| mNode.mContentDescription = contentDescription; |
| } |
| |
| private final ViewNodeText getNodeText() { |
| if (mNode.mText != null) { |
| return mNode.mText; |
| } |
| mNode.mText = new ViewNodeText(); |
| return mNode.mText; |
| } |
| |
| @Override |
| public void setText(CharSequence text) { |
| ViewNodeText t = getNodeText(); |
| t.mText = text; |
| t.mTextSelectionStart = t.mTextSelectionEnd = -1; |
| } |
| |
| @Override |
| public void setText(CharSequence text, int selectionStart, int selectionEnd) { |
| ViewNodeText t = getNodeText(); |
| t.mText = text; |
| t.mTextSelectionStart = selectionStart; |
| t.mTextSelectionEnd = selectionEnd; |
| } |
| |
| @Override |
| public void setTextPaint(TextPaint paint) { |
| ViewNodeText t = getNodeText(); |
| t.mTextColor = paint.getColor(); |
| t.mTextBackgroundColor = paint.bgColor; |
| t.mTextSize = paint.getTextSize(); |
| t.mTextStyle = 0; |
| Typeface tf = paint.getTypeface(); |
| if (tf != null) { |
| if (tf.isBold()) { |
| t.mTextStyle |= ViewNode.TEXT_STYLE_BOLD; |
| } |
| if (tf.isItalic()) { |
| t.mTextStyle |= ViewNode.TEXT_STYLE_ITALIC; |
| } |
| } |
| int pflags = paint.getFlags(); |
| if ((pflags& Paint.FAKE_BOLD_TEXT_FLAG) != 0) { |
| t.mTextStyle |= ViewNode.TEXT_STYLE_BOLD; |
| } |
| if ((pflags& Paint.UNDERLINE_TEXT_FLAG) != 0) { |
| t.mTextStyle |= ViewNode.TEXT_STYLE_UNDERLINE; |
| } |
| if ((pflags& Paint.STRIKE_THRU_TEXT_FLAG) != 0) { |
| t.mTextStyle |= ViewNode.TEXT_STYLE_STRIKE_THRU; |
| } |
| } |
| |
| @Override |
| public void setHint(CharSequence hint) { |
| getNodeText().mHint = hint != null ? hint.toString() : null; |
| } |
| |
| @Override |
| public CharSequence getText() { |
| return mNode.mText != null ? mNode.mText.mText : null; |
| } |
| |
| @Override |
| public int getTextSelectionStart() { |
| return mNode.mText != null ? mNode.mText.mTextSelectionStart : -1; |
| } |
| |
| @Override |
| public int getTextSelectionEnd() { |
| return mNode.mText != null ? mNode.mText.mTextSelectionEnd : -1; |
| } |
| |
| @Override |
| public CharSequence getHint() { |
| return mNode.mText != null ? mNode.mText.mHint : null; |
| } |
| |
| @Override |
| public Bundle editExtras() { |
| if (mNode.mExtras != null) { |
| return mNode.mExtras; |
| } |
| mNode.mExtras = new Bundle(); |
| return mNode.mExtras; |
| } |
| |
| @Override |
| public void clearExtras() { |
| mNode.mExtras = null; |
| } |
| |
| @Override |
| public void setChildCount(int num) { |
| mNode.mChildren = new ViewNode[num]; |
| } |
| |
| @Override |
| public int getChildCount() { |
| return mNode.mChildren != null ? mNode.mChildren.length : 0; |
| } |
| |
| @Override |
| public ViewAssistStructure newChild(int index) { |
| ViewNode node = new ViewNode(); |
| mNode.mChildren[index] = node; |
| return new ViewNodeBuilder(mAssist, node, false); |
| } |
| |
| @Override |
| public ViewAssistStructure asyncNewChild(int index) { |
| synchronized (mAssist) { |
| ViewNode node = new ViewNode(); |
| mNode.mChildren[index] = node; |
| ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true); |
| mAssist.mPendingAsyncChildren.add(builder); |
| return builder; |
| } |
| } |
| |
| @Override |
| public void asyncCommit() { |
| synchronized (mAssist) { |
| if (!mAsync) { |
| throw new IllegalStateException("Child " + this |
| + " was not created with ViewAssistStructure.asyncNewChild"); |
| } |
| if (!mAssist.mPendingAsyncChildren.remove(this)) { |
| throw new IllegalStateException("Child " + this + " already committed"); |
| } |
| mAssist.notifyAll(); |
| } |
| } |
| |
| @Override |
| public Rect getTempRect() { |
| return mAssist.mTmpRect; |
| } |
| } |
| |
| AssistStructure(Activity activity) { |
| mHaveData = true; |
| mActivityComponent = activity.getComponentName(); |
| ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews( |
| activity.getActivityToken()); |
| for (int i=0; i<views.size(); i++) { |
| ViewRootImpl root = views.get(i); |
| mWindowNodes.add(new WindowNode(this, root)); |
| } |
| } |
| |
| AssistStructure(Parcel in) { |
| mReceiveChannel = in.readStrongBinder(); |
| } |
| |
| /** @hide */ |
| public void dump() { |
| Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString()); |
| 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()); |
| } |
| } |
| |
| void dump(String prefix, ViewNode node) { |
| 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); |
| } |
| CharSequence contentDescription = node.getContentDescription(); |
| if (contentDescription != null) { |
| Log.i(TAG, prefix + " Content description: " + contentDescription); |
| } |
| CharSequence text = node.getText(); |
| if (text != null) { |
| Log.i(TAG, prefix + " Text (sel " + node.getTextSelectionStart() + "-" |
| + node.getTextSelectionEnd() + "): " + text); |
| 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())); |
| } |
| String hint = node.getHint(); |
| if (hint != null) { |
| Log.i(TAG, prefix + " Hint: " + hint); |
| } |
| Bundle extras = node.getExtras(); |
| if (extras != null) { |
| Log.i(TAG, prefix + " Extras: " + extras); |
| } |
| 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); |
| } |
| } |
| } |
| |
| /** |
| * Retrieve the framework-generated AssistStructure that is stored within |
| * the Bundle filled in by {@link Activity#onProvideAssistData}. |
| */ |
| public static AssistStructure getAssistStructure(Bundle assistBundle) { |
| return assistBundle.getParcelable(ASSIST_KEY); |
| } |
| |
| public ComponentName getActivityComponent() { |
| ensureData(); |
| return mActivityComponent; |
| } |
| |
| /** |
| * 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); |
| } |
| |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** @hide */ |
| public void ensureData() { |
| if (mHaveData) { |
| return; |
| } |
| mHaveData = true; |
| Parcel data = Parcel.obtain(); |
| Parcel reply = Parcel.obtain(); |
| data.writeInterfaceToken(DESCRIPTOR); |
| try { |
| mReceiveChannel.transact(TRANSACTION_XFER, data, reply, 0); |
| } catch (RemoteException e) { |
| Log.w(TAG, "Failure reading AssistStructure data", e); |
| return; |
| } |
| readContentFromParcel(reply); |
| data.recycle(); |
| reply.recycle(); |
| } |
| |
| void writeContentToParcel(Parcel out, int flags) { |
| // First make sure all content has been created. |
| 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. |
| skipStructure = true; |
| } |
| } |
| int start = out.dataPosition(); |
| PooledStringWriter pwriter = new PooledStringWriter(out); |
| ComponentName.writeToParcel(mActivityComponent, out); |
| final int N = skipStructure ? 0 : mWindowNodes.size(); |
| out.writeInt(N); |
| for (int i=0; i<N; i++) { |
| mWindowNodes.get(i).writeToParcel(out, pwriter); |
| } |
| pwriter.finish(); |
| Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes"); |
| } |
| |
| void readContentFromParcel(Parcel in) { |
| PooledStringReader preader = new PooledStringReader(in); |
| mActivityComponent = ComponentName.readFromParcel(in); |
| final int N = in.readInt(); |
| for (int i=0; i<N; i++) { |
| mWindowNodes.add(new WindowNode(in, preader)); |
| } |
| //dump(); |
| } |
| |
| public void writeToParcel(Parcel out, int flags) { |
| 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(); |
| } |
| out.writeStrongBinder(mSendChannel); |
| } else { |
| // This object doesn't hold its data, so just propagate along its receive channel. |
| out.writeStrongBinder(mReceiveChannel); |
| } |
| } |
| |
| public static final Parcelable.Creator<AssistStructure> CREATOR |
| = new Parcelable.Creator<AssistStructure>() { |
| public AssistStructure createFromParcel(Parcel in) { |
| return new AssistStructure(in); |
| } |
| |
| public AssistStructure[] newArray(int size) { |
| return new AssistStructure[size]; |
| } |
| }; |
| } |