/* * 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.content.res.Resources; 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 mWindowNodes = 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); } } /** * 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(assist, view); } 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; final int mId; final String mIdPackage; final String mIdType; final String mIdEntry; 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 CharSequence mContentDescription; final ViewNodeTextImpl mText; final Bundle mExtras; final ViewNode[] mChildren; ViewNode(AssistStructure assistStructure, View view) { mId = view.getId(); if (mId > 0 && (mId&0xff000000) != 0 && (mId&0x00ff0000) != 0 && (mId&0x0000ffff) != 0) { String pkg, type, entry; try { Resources res = view.getResources(); entry = res.getResourceEntryName(mId); type = res.getResourceTypeName(mId); pkg = res.getResourcePackageName(mId); } catch (Resources.NotFoundException e) { entry = type = pkg = null; } mIdPackage = pkg; mIdType = type; mIdEntry = entry; } else { mIdPackage = mIdType = mIdEntry = null; } mX = view.getLeft(); mY = view.getTop(); 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 = view.getContentDescription(); 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 ViewNode[NCHILDREN]; for (int i=0; i 0) { mChildren = new ViewNode[NCHILDREN]; for (int i=0; i views = WindowManagerGlobal.getInstance().getRootViews( activity.getActivityToken()); for (int i=0; i 0) { Log.i(TAG, prefix + " Children:"); String cprefix = prefix + " "; for (int i=0; i CREATOR = new Parcelable.Creator() { public AssistStructure createFromParcel(Parcel in) { return new AssistStructure(in); } public AssistStructure[] newArray(int size) { return new AssistStructure[size]; } }; }