diff options
Diffstat (limited to 'core/java/android/app/AssistStructure.java')
-rw-r--r-- | core/java/android/app/AssistStructure.java | 618 |
1 files changed, 618 insertions, 0 deletions
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java new file mode 100644 index 0000000..25153fc --- /dev/null +++ b/core/java/android/app/AssistStructure.java @@ -0,0 +1,618 @@ +/* + * 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.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PooledStringReader; +import android.os.PooledStringWriter; +import android.text.TextPaint; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.view.ViewAssistStructure; +import android.view.ViewGroup; +import android.view.ViewRootImpl; +import android.view.WindowManagerGlobal; +import android.widget.Checkable; + +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"; + + final ComponentName mActivityComponent; + + final ArrayList<ViewNodeImpl> mRootViews = new ArrayList<>(); + + ViewAssistStructureImpl mTmpViewAssistStructureImpl = new ViewAssistStructureImpl(); + Bundle mTmpExtras = new Bundle(); + + final static class ViewAssistStructureImpl extends ViewAssistStructure { + CharSequence mText; + int mTextSelectionStart = -1; + int mTextSelectionEnd = -1; + int mTextColor = ViewNode.TEXT_COLOR_UNDEFINED; + int mTextBackgroundColor = ViewNode.TEXT_COLOR_UNDEFINED; + float mTextSize = 0; + int mTextStyle = 0; + CharSequence mHint; + + @Override + public void setText(CharSequence text) { + mText = text; + mTextSelectionStart = mTextSelectionEnd = -1; + } + + @Override + public void setText(CharSequence text, int selectionStart, int selectionEnd) { + mText = text; + mTextSelectionStart = selectionStart; + mTextSelectionEnd = selectionEnd; + } + + @Override + public void setTextPaint(TextPaint paint) { + mTextColor = paint.getColor(); + mTextBackgroundColor = paint.bgColor; + mTextSize = paint.getTextSize(); + mTextStyle = 0; + Typeface tf = paint.getTypeface(); + if (tf != null) { + if (tf.isBold()) { + mTextStyle |= ViewNode.TEXT_STYLE_BOLD; + } + if (tf.isItalic()) { + mTextStyle |= ViewNode.TEXT_STYLE_ITALIC; + } + } + int pflags = paint.getFlags(); + if ((pflags& Paint.FAKE_BOLD_TEXT_FLAG) != 0) { + mTextStyle |= ViewNode.TEXT_STYLE_BOLD; + } + if ((pflags& Paint.UNDERLINE_TEXT_FLAG) != 0) { + mTextStyle |= ViewNode.TEXT_STYLE_UNDERLINE; + } + if ((pflags& Paint.STRIKE_THRU_TEXT_FLAG) != 0) { + mTextStyle |= ViewNode.TEXT_STYLE_STRIKE_THRU; + } + } + + @Override + public void setHint(CharSequence hint) { + mHint = hint; + } + + @Override + public CharSequence getText() { + return mText; + } + + @Override + public int getTextSelectionStart() { + return mTextSelectionStart; + } + + @Override + public int getTextSelectionEnd() { + return mTextSelectionEnd; + } + + @Override + public CharSequence getHint() { + return mHint; + } + } + + final static class ViewNodeTextImpl { + final CharSequence mText; + final int mTextSelectionStart; + final int mTextSelectionEnd; + int mTextColor; + int mTextBackgroundColor; + float mTextSize; + int mTextStyle; + final String mHint; + + ViewNodeTextImpl(ViewAssistStructureImpl data) { + mText = data.mText; + mTextSelectionStart = data.mTextSelectionStart; + mTextSelectionEnd = data.mTextSelectionEnd; + mTextColor = data.mTextColor; + mTextBackgroundColor = data.mTextBackgroundColor; + mTextSize = data.mTextSize; + mTextStyle = data.mTextStyle; + mHint = data.mHint != null ? data.mHint.toString() : null; + } + + ViewNodeTextImpl(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); + } + } + + final static class ViewNodeImpl { + final int mX; + final int mY; + final int mScrollX; + final int mScrollY; + final int mWidth; + final 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_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; + + final int mFlags; + + final String mClassName; + final String mContentDescription; + + final ViewNodeTextImpl mText; + final Bundle mExtras; + + final ViewNodeImpl[] mChildren; + + ViewNodeImpl(AssistStructure assistStructure, View view, int left, int top, + CharSequence contentDescription) { + mX = left; + mY = top; + mScrollX = view.getScrollX(); + mScrollY = view.getScrollY(); + mWidth = view.getWidth(); + mHeight = view.getHeight(); + int flags = view.getVisibility(); + if (!view.isEnabled()) { + flags |= FLAGS_DISABLED; + } + if (!view.isClickable()) { + flags |= FLAGS_CLICKABLE; + } + if (!view.isFocusable()) { + flags |= FLAGS_FOCUSABLE; + } + if (!view.isFocused()) { + flags |= FLAGS_FOCUSED; + } + if (!view.isAccessibilityFocused()) { + flags |= FLAGS_ACCESSIBILITY_FOCUSED; + } + if (!view.isSelected()) { + flags |= FLAGS_SELECTED; + } + if (!view.isActivated()) { + flags |= FLAGS_ACTIVATED; + } + if (!view.isLongClickable()) { + flags |= FLAGS_LONG_CLICKABLE; + } + if (view instanceof Checkable) { + flags |= FLAGS_CHECKABLE; + if (((Checkable)view).isChecked()) { + flags |= FLAGS_CHECKED; + } + } + mFlags = flags; + mClassName = view.getAccessibilityClassName().toString(); + mContentDescription = contentDescription != null ? contentDescription.toString() : null; + final ViewAssistStructureImpl viewData = assistStructure.mTmpViewAssistStructureImpl; + final Bundle extras = assistStructure.mTmpExtras; + view.onProvideAssistStructure(viewData, extras); + if (viewData.mText != null || viewData.mHint != null) { + mText = new ViewNodeTextImpl(viewData); + assistStructure.mTmpViewAssistStructureImpl = new ViewAssistStructureImpl(); + } else { + mText = null; + } + if (!extras.isEmpty()) { + mExtras = extras; + assistStructure.mTmpExtras = new Bundle(); + } else { + mExtras = null; + } + if (view instanceof ViewGroup) { + ViewGroup vg = (ViewGroup)view; + final int NCHILDREN = vg.getChildCount(); + if (NCHILDREN > 0) { + mChildren = new ViewNodeImpl[NCHILDREN]; + for (int i=0; i<NCHILDREN; i++) { + mChildren[i] = new ViewNodeImpl(assistStructure, vg.getChildAt(i)); + } + } else { + mChildren = null; + } + } else { + mChildren = null; + } + } + + ViewNodeImpl(AssistStructure assistStructure, View view) { + this(assistStructure, view, view.getLeft(), view.getTop(), view.getContentDescription()); + } + + ViewNodeImpl(Parcel in, PooledStringReader preader) { + 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 = in.readString(); + if (in.readInt() != 0) { + mText = new ViewNodeTextImpl(in); + } else { + mText = null; + } + mExtras = in.readBundle(); + final int NCHILDREN = in.readInt(); + if (NCHILDREN > 0) { + mChildren = new ViewNodeImpl[NCHILDREN]; + for (int i=0; i<NCHILDREN; i++) { + mChildren[i] = new ViewNodeImpl(in, preader); + } + } else { + mChildren = null; + } + } + + void writeToParcel(Parcel out, PooledStringWriter pwriter) { + out.writeInt(mX); + out.writeInt(mY); + out.writeInt(mScrollX); + out.writeInt(mScrollY); + out.writeInt(mWidth); + out.writeInt(mHeight); + out.writeInt(mFlags); + pwriter.writeString(mClassName); + out.writeString(mContentDescription); + 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); + } + } + } + + /** + * Provides access to information about 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; + + ViewNodeImpl mImpl; + + public ViewNode() { + } + + public int getLeft() { + return mImpl.mX; + } + + public int getTop() { + return mImpl.mY; + } + + public int getScrollX() { + return mImpl.mScrollX; + } + + public int getScrollY() { + return mImpl.mScrollY; + } + + public int getWidth() { + return mImpl.mWidth; + } + + public int getHeight() { + return mImpl.mHeight; + } + + public int getVisibility() { + return mImpl.mFlags&ViewNodeImpl.FLAGS_VISIBILITY_MASK; + } + + public boolean isEnabled() { + return (mImpl.mFlags&ViewNodeImpl.FLAGS_DISABLED) == 0; + } + + public boolean isClickable() { + return (mImpl.mFlags&ViewNodeImpl.FLAGS_CLICKABLE) != 0; + } + + public boolean isFocusable() { + return (mImpl.mFlags&ViewNodeImpl.FLAGS_FOCUSABLE) != 0; + } + + public boolean isFocused() { + return (mImpl.mFlags&ViewNodeImpl.FLAGS_FOCUSED) != 0; + } + + public boolean isAccessibilityFocused() { + return (mImpl.mFlags&ViewNodeImpl.FLAGS_ACCESSIBILITY_FOCUSED) != 0; + } + + public boolean isCheckable() { + return (mImpl.mFlags&ViewNodeImpl.FLAGS_CHECKABLE) != 0; + } + + public boolean isChecked() { + return (mImpl.mFlags&ViewNodeImpl.FLAGS_CHECKED) != 0; + } + + public boolean isSelected() { + return (mImpl.mFlags&ViewNodeImpl.FLAGS_SELECTED) != 0; + } + + public boolean isActivated() { + return (mImpl.mFlags&ViewNodeImpl.FLAGS_ACTIVATED) != 0; + } + + public boolean isLongClickable() { + return (mImpl.mFlags&ViewNodeImpl.FLAGS_LONG_CLICKABLE) != 0; + } + + public String getClassName() { + return mImpl.mClassName; + } + + public String getContentDescription() { + return mImpl.mContentDescription; + } + + public CharSequence getText() { + return mImpl.mText != null ? mImpl.mText.mText : null; + } + + public int getTextSelectionStart() { + return mImpl.mText != null ? mImpl.mText.mTextSelectionStart : -1; + } + + public int getTextSelectionEnd() { + return mImpl.mText != null ? mImpl.mText.mTextSelectionEnd : -1; + } + + public int getTextColor() { + return mImpl.mText != null ? mImpl.mText.mTextColor : TEXT_COLOR_UNDEFINED; + } + + public int getTextBackgroundColor() { + return mImpl.mText != null ? mImpl.mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED; + } + + public float getTextSize() { + return mImpl.mText != null ? mImpl.mText.mTextSize : 0; + } + + public int getTextStyle() { + return mImpl.mText != null ? mImpl.mText.mTextStyle : 0; + } + + public String getHint() { + return mImpl.mText != null ? mImpl.mText.mHint : null; + } + + public Bundle getExtras() { + return mImpl.mExtras; + } + + public int getChildCount() { + return mImpl.mChildren != null ? mImpl.mChildren.length : 0; + } + + public void getChildAt(int index, ViewNode outNode) { + outNode.mImpl = mImpl.mChildren[index]; + } + } + + AssistStructure(Activity activity) { + mActivityComponent = activity.getComponentName(); + ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews( + activity.getActivityToken()); + for (int i=0; i<views.size(); i++) { + ViewRootImpl root = views.get(i); + View view = root.getView(); + Rect rect = new Rect(); + view.getBoundsOnScreen(rect); + CharSequence title = root.getTitle(); + mRootViews.add(new ViewNodeImpl(this, view, rect.left, rect.top, + title != null ? title : view.getContentDescription())); + } + } + + AssistStructure(Parcel in) { + PooledStringReader preader = new PooledStringReader(in); + mActivityComponent = ComponentName.readFromParcel(in); + final int N = in.readInt(); + for (int i=0; i<N; i++) { + mRootViews.add(new ViewNodeImpl(in, preader)); + } + //dump(); + } + + /** @hide */ + public void dump() { + Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString()); + ViewNode node = new ViewNode(); + final int N = getWindowCount(); + for (int i=0; i<N; i++) { + Log.i(TAG, "Window #" + i + ":"); + getWindowAt(i, node); + dump(" ", node); + } + } + + void dump(String prefix, ViewNode node) { + Log.i(TAG, prefix + "View [" + node.getLeft() + "," + node.getTop() + + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getClassName()); + int scrollX = node.getScrollX(); + int scrollY = node.getScrollY(); + if (scrollX != 0 || scrollY != 0) { + Log.i(TAG, prefix + " Scroll: " + scrollX + "," + scrollY); + } + String 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 + " "; + ViewNode cnode = new ViewNode(); + for (int i=0; i<NCHILDREN; i++) { + node.getChildAt(i, cnode); + 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() { + return mActivityComponent; + } + + /** + * Return the number of window contents that have been collected in this assist data. + */ + public int getWindowCount() { + return mRootViews.size(); + } + + /** + * Return the root view for one of the windows in the assist data. + * @param index Which window to retrieve, may be 0 to {@link #getWindowCount()}-1. + * @param outNode Node in which to place the window's root view. + */ + public void getWindowAt(int index, ViewNode outNode) { + outNode.mImpl = mRootViews.get(index); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + int start = out.dataPosition(); + PooledStringWriter pwriter = new PooledStringWriter(out); + ComponentName.writeToParcel(mActivityComponent, out); + final int N = mRootViews.size(); + out.writeInt(N); + for (int i=0; i<N; i++) { + mRootViews.get(i).writeToParcel(out, pwriter); + } + pwriter.finish(); + Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes"); + } + + 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]; + } + }; +} |