diff options
author | Dianne Hackborn <hackbod@google.com> | 2015-03-13 16:41:27 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-03-13 16:41:29 +0000 |
commit | 484bc6e5a79af1b7b0a2eb682802006522b274fd (patch) | |
tree | 3f7aebb16288df0d7947c1283305345d8a711a9a /core | |
parent | 916b1e503d50b87bcf724e975a4bc3d32bf31431 (diff) | |
parent | a83ce1dd2ad3a6b71e90ff4845afc1299fe17b9d (diff) | |
download | frameworks_base-484bc6e5a79af1b7b0a2eb682802006522b274fd.zip frameworks_base-484bc6e5a79af1b7b0a2eb682802006522b274fd.tar.gz frameworks_base-484bc6e5a79af1b7b0a2eb682802006522b274fd.tar.bz2 |
Merge "More work on collecting assist data."
Diffstat (limited to 'core')
21 files changed, 593 insertions, 101 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index fdbf197..7fcbe35 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1548,17 +1548,35 @@ public class Activity extends ContextThemeWrapper * {@link Intent#ACTION_ASSIST} Intent with all of the context of the current * application. You can override this method to place into the bundle anything * you would like to appear in the {@link Intent#EXTRA_ASSIST_CONTEXT} part - * of the assist Intent. The default implementation automatically generates a - * {@link AssistData} from your activity and places it in to the Bundle; if you - * don't want your UI reported to the assistant, don't call this default - * implementation. + * of the assist Intent. * * <p>This function will be called after any global assist callbacks that had * been registered with {@link Application#registerOnProvideAssistDataListener * Application.registerOnProvideAssistDataListener}. */ public void onProvideAssistData(Bundle data) { - data.putParcelable(AssistData.ASSIST_KEY, new AssistData(this)); + } + + /** + * This is called when the user is requesting an assist, to provide references + * to content related to the current activity. Before being called, the + * {@code outContent} Intent is filled with the base Intent of the activity (the Intent + * returned by {@link #getIntent()}). The Intent's extras are stripped of any types + * that are not valid for {@link PersistableBundle} or non-framework Parcelables, and + * the flags {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION} and + * {@link Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION} are cleared from the Intent. + * + * <p>Custom implementation may adjust the content intent to better reflect the top-level + * context of the activity, and fill in its ClipData with additional content of + * interest that the user is currently viewing. For example, an image gallery application + * that has launched in to an activity allowing the user to swipe through pictures should + * modify the intent to reference the current image they are looking it; such an + * application when showing a list of pictures should add a ClipData that has + * references to all of the pictures currently visible on screen.</p> + * + * @param outContent The assist content to return. + */ + public void onProvideAssistContent(AssistContent outContent) { } /** diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index c525ef2..29b024ac 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -309,6 +309,12 @@ public class ActivityManager { /** @hide Process is being cached for later use and is empty. */ public static final int PROCESS_STATE_CACHED_EMPTY = 13; + /** @hide requestType for assist context: only basic information. */ + public static final int ASSIST_CONTEXT_BASIC = 0; + + /** @hide requestType for assist context: generate full AssistStructure. */ + public static final int ASSIST_CONTEXT_FULL = 1; + /** * Lock task mode is not active. */ diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 97793c5..7b8ec74 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2519,6 +2519,17 @@ public final class ActivityThread { if (r != null) { r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data); r.activity.onProvideAssistData(data); + if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) { + data.putParcelable(AssistStructure.ASSIST_KEY, new AssistStructure(r.activity)); + AssistContent content = new AssistContent(); + Intent intent = new Intent(r.activity.getIntent()); + intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)); + intent.removeUnsafeExtras(); + content.setIntent(intent); + r.activity.onProvideAssistContent(content); + data.putParcelable(AssistContent.ASSIST_KEY, content); + } } if (data.isEmpty()) { data = null; diff --git a/core/java/android/app/AssistContent.aidl b/core/java/android/app/AssistContent.aidl new file mode 100644 index 0000000..a6321bf --- /dev/null +++ b/core/java/android/app/AssistContent.aidl @@ -0,0 +1,19 @@ +/** + * 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; + +parcelable AssistContent; diff --git a/core/java/android/app/AssistContent.java b/core/java/android/app/AssistContent.java new file mode 100644 index 0000000..ace4af7 --- /dev/null +++ b/core/java/android/app/AssistContent.java @@ -0,0 +1,124 @@ +/* + * 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.ClipData; +import android.content.Intent; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Holds information about the content an application is viewing, to hand to an + * assistant at the user's request. This is filled in by + * {@link Activity#onProvideAssistContent Activity.onProvideAssistContent}. + */ +public class AssistContent implements Parcelable { + private Intent mIntent; + private ClipData mClipData; + + /** + * Key name this data structure is stored in the Bundle generated by + * {@link Activity#onProvideAssistData}. + */ + public static final String ASSIST_KEY = "android:assist_content"; + + /** + * Retrieve the framework-generated AssistContent that is stored within + * the Bundle filled in by {@link Activity#onProvideAssistContent}. + */ + public static AssistContent getAssistContent(Bundle assistBundle) { + return assistBundle.getParcelable(ASSIST_KEY); + } + + public AssistContent() { + } + + /** + * Sets the Intent associated with the content, describing the current top-level context of + * the activity. If this contains a reference to a piece of data related to the activity, + * be sure to set {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} so the accessibilty + * service can access it. + */ + public void setIntent(Intent intent) { + mIntent = intent; + } + + /** + * Return the current {@link #setIntent}, which you can modify in-place. + */ + public Intent getIntent() { + return mIntent; + } + + /** + * Optional additional content items that are involved with + * the current UI. Access to this content will be granted to the assistant as if you + * are sending it through an Intent with {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}. + */ + public void setClipData(ClipData clip) { + mClipData = clip; + } + + /** + * Return the current {@link #setClipData}, which you can modify in-place. + */ + public ClipData getClipData() { + return mClipData; + } + + AssistContent(Parcel in) { + if (in.readInt() != 0) { + mIntent = Intent.CREATOR.createFromParcel(in); + } + if (in.readInt() != 0) { + mClipData = ClipData.CREATOR.createFromParcel(in); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (mIntent != null) { + dest.writeInt(1); + mIntent.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + if (mClipData != null) { + dest.writeInt(1); + mClipData.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + } + + public static final Parcelable.Creator<AssistContent> CREATOR + = new Parcelable.Creator<AssistContent>() { + public AssistContent createFromParcel(Parcel in) { + return new AssistContent(in); + } + + public AssistContent[] newArray(int size) { + return new AssistContent[size]; + } + }; +} diff --git a/core/java/android/app/AssistStructure.aidl b/core/java/android/app/AssistStructure.aidl new file mode 100644 index 0000000..07fb2453 --- /dev/null +++ b/core/java/android/app/AssistStructure.aidl @@ -0,0 +1,19 @@ +/** + * 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; + +parcelable AssistStructure; diff --git a/core/java/android/app/AssistData.java b/core/java/android/app/AssistStructure.java index 7b5eb6d..25153fc 100644 --- a/core/java/android/app/AssistData.java +++ b/core/java/android/app/AssistStructure.java @@ -17,13 +17,19 @@ 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.ViewAssistData; +import android.view.ViewAssistStructure; import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; @@ -34,28 +40,32 @@ 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 #getAssistData(android.os.Bundle)}. + * data with {@link #getAssistStructure(android.os.Bundle)}. */ -final public class AssistData implements Parcelable { - static final String TAG = "AssistData"; +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"; + public static final String ASSIST_KEY = "android:assist_structure"; final ComponentName mActivityComponent; final ArrayList<ViewNodeImpl> mRootViews = new ArrayList<>(); - ViewAssistDataImpl mTmpViewAssistDataImpl = new ViewAssistDataImpl(); + ViewAssistStructureImpl mTmpViewAssistStructureImpl = new ViewAssistStructureImpl(); Bundle mTmpExtras = new Bundle(); - final static class ViewAssistDataImpl extends ViewAssistData { + 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 @@ -72,6 +82,33 @@ final public class AssistData implements Parcelable { } @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; } @@ -98,29 +135,45 @@ final public class AssistData implements Parcelable { } final static class ViewNodeTextImpl { - final String mText; + final CharSequence mText; final int mTextSelectionStart; final int mTextSelectionEnd; + int mTextColor; + int mTextBackgroundColor; + float mTextSize; + int mTextStyle; final String mHint; - ViewNodeTextImpl(ViewAssistDataImpl data) { - mText = data.mText != null ? data.mText.toString() : null; + 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 = in.readString(); + 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) { - out.writeString(mText); + 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); } } @@ -155,7 +208,7 @@ final public class AssistData implements Parcelable { final ViewNodeImpl[] mChildren; - ViewNodeImpl(AssistData assistData, View view, int left, int top, + ViewNodeImpl(AssistStructure assistStructure, View view, int left, int top, CharSequence contentDescription) { mX = left; mY = top; @@ -197,18 +250,18 @@ final public class AssistData implements Parcelable { mFlags = flags; mClassName = view.getAccessibilityClassName().toString(); mContentDescription = contentDescription != null ? contentDescription.toString() : null; - final ViewAssistDataImpl viewData = assistData.mTmpViewAssistDataImpl; - final Bundle extras = assistData.mTmpExtras; - view.onProvideAssistData(viewData, extras); + 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); - assistData.mTmpViewAssistDataImpl = new ViewAssistDataImpl(); + assistStructure.mTmpViewAssistStructureImpl = new ViewAssistStructureImpl(); } else { mText = null; } if (!extras.isEmpty()) { mExtras = extras; - assistData.mTmpExtras = new Bundle(); + assistStructure.mTmpExtras = new Bundle(); } else { mExtras = null; } @@ -218,7 +271,7 @@ final public class AssistData implements Parcelable { if (NCHILDREN > 0) { mChildren = new ViewNodeImpl[NCHILDREN]; for (int i=0; i<NCHILDREN; i++) { - mChildren[i] = new ViewNodeImpl(assistData, vg.getChildAt(i)); + mChildren[i] = new ViewNodeImpl(assistStructure, vg.getChildAt(i)); } } else { mChildren = null; @@ -228,11 +281,11 @@ final public class AssistData implements Parcelable { } } - ViewNodeImpl(AssistData assistData, View view) { - this(assistData, view, view.getLeft(), view.getTop(), view.getContentDescription()); + ViewNodeImpl(AssistStructure assistStructure, View view) { + this(assistStructure, view, view.getLeft(), view.getTop(), view.getContentDescription()); } - ViewNodeImpl(Parcel in) { + ViewNodeImpl(Parcel in, PooledStringReader preader) { mX = in.readInt(); mY = in.readInt(); mScrollX = in.readInt(); @@ -240,7 +293,7 @@ final public class AssistData implements Parcelable { mWidth = in.readInt(); mHeight = in.readInt(); mFlags = in.readInt(); - mClassName = in.readString(); + mClassName = preader.readString(); mContentDescription = in.readString(); if (in.readInt() != 0) { mText = new ViewNodeTextImpl(in); @@ -252,14 +305,14 @@ final public class AssistData implements Parcelable { if (NCHILDREN > 0) { mChildren = new ViewNodeImpl[NCHILDREN]; for (int i=0; i<NCHILDREN; i++) { - mChildren[i] = new ViewNodeImpl(in); + mChildren[i] = new ViewNodeImpl(in, preader); } } else { mChildren = null; } } - void writeToParcel(Parcel out) { + void writeToParcel(Parcel out, PooledStringWriter pwriter) { out.writeInt(mX); out.writeInt(mY); out.writeInt(mScrollX); @@ -267,7 +320,7 @@ final public class AssistData implements Parcelable { out.writeInt(mWidth); out.writeInt(mHeight); out.writeInt(mFlags); - out.writeString(mClassName); + pwriter.writeString(mClassName); out.writeString(mContentDescription); if (mText != null) { out.writeInt(1); @@ -280,7 +333,7 @@ final public class AssistData implements Parcelable { final int NCHILDREN = mChildren.length; out.writeInt(NCHILDREN); for (int i=0; i<NCHILDREN; i++) { - mChildren[i].writeToParcel(out); + mChildren[i].writeToParcel(out, pwriter); } } else { out.writeInt(0); @@ -292,6 +345,17 @@ final public class AssistData implements Parcelable { * 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() { @@ -373,7 +437,7 @@ final public class AssistData implements Parcelable { return mImpl.mContentDescription; } - public String getText() { + public CharSequence getText() { return mImpl.mText != null ? mImpl.mText.mText : null; } @@ -385,6 +449,22 @@ final public class AssistData implements Parcelable { 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; } @@ -402,7 +482,7 @@ final public class AssistData implements Parcelable { } } - AssistData(Activity activity) { + AssistStructure(Activity activity) { mActivityComponent = activity.getComponentName(); ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews( activity.getActivityToken()); @@ -417,11 +497,12 @@ final public class AssistData implements Parcelable { } } - AssistData(Parcel in) { + 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)); + mRootViews.add(new ViewNodeImpl(in, preader)); } //dump(); } @@ -450,10 +531,14 @@ final public class AssistData implements Parcelable { if (contentDescription != null) { Log.i(TAG, prefix + " Content description: " + contentDescription); } - String text = node.getText(); + 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) { @@ -476,10 +561,10 @@ final public class AssistData implements Parcelable { } /** - * Retrieve the framework-generated AssistData that is stored within + * Retrieve the framework-generated AssistStructure that is stored within * the Bundle filled in by {@link Activity#onProvideAssistData}. */ - public static AssistData getAssistData(Bundle assistBundle) { + public static AssistStructure getAssistStructure(Bundle assistBundle) { return assistBundle.getParcelable(ASSIST_KEY); } @@ -509,23 +594,25 @@ final public class AssistData implements Parcelable { 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); + mRootViews.get(i).writeToParcel(out, pwriter); } + pwriter.finish(); Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes"); } - public static final Parcelable.Creator<AssistData> CREATOR - = new Parcelable.Creator<AssistData>() { - public AssistData createFromParcel(Parcel in) { - return new AssistData(in); + public static final Parcelable.Creator<AssistStructure> CREATOR + = new Parcelable.Creator<AssistStructure>() { + public AssistStructure createFromParcel(Parcel in) { + return new AssistStructure(in); } - public AssistData[] newArray(int size) { - return new AssistData[size]; + public AssistStructure[] newArray(int size) { + return new AssistStructure[size]; } }; } diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl index 03e7ff4..6d27910 100644 --- a/core/java/android/app/ISearchManager.aidl +++ b/core/java/android/app/ISearchManager.aidl @@ -31,5 +31,5 @@ interface ISearchManager { ComponentName getGlobalSearchActivity(); ComponentName getWebSearchActivity(); ComponentName getAssistIntent(int userHandle); - boolean launchAssistAction(int requestType, String hint, int userHandle); + boolean launchAssistAction(String hint, int userHandle); } diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index d7c4467..fa27631 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -969,7 +969,7 @@ public class SearchManager intent.setComponent(comp); if (inclContext) { IActivityManager am = ActivityManagerNative.getDefault(); - Bundle extras = am.getAssistContextExtras(0); + Bundle extras = am.getAssistContextExtras(ActivityManager.ASSIST_CONTEXT_BASIC); if (extras != null) { intent.replaceExtras(extras); } @@ -985,12 +985,12 @@ public class SearchManager * Launch an assist action for the current top activity. * @hide */ - public boolean launchAssistAction(int requestType, String hint, int userHandle) { + public boolean launchAssistAction(String hint, int userHandle) { try { if (mService == null) { return false; } - return mService.launchAssistAction(requestType, hint, userHandle); + return mService.launchAssistAction(hint, userHandle); } catch (RemoteException re) { Log.e(TAG, "launchAssistAction() failed: " + re); return false; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 3222b2b..2ed8c44 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1239,6 +1239,13 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.extra.ASSIST_PACKAGE"; /** + * An optional field on {@link #ACTION_ASSIST} containing the uid of the current foreground + * application package at the time the assist was invoked. + */ + public static final String EXTRA_ASSIST_UID + = "android.intent.extra.ASSIST_UID"; + + /** * An optional field on {@link #ACTION_ASSIST} and containing additional contextual * information supplied by the current foreground app at the time of the assist request. * This is a {@link Bundle} of additional data. @@ -5386,6 +5393,16 @@ public class Intent implements Parcelable, Cloneable { } /** + * Filter extras to only basic types. + * @hide + */ + public void removeUnsafeExtras() { + if (mExtras != null) { + mExtras.filterValues(); + } + } + + /** * Retrieve any special flags associated with this intent. You will * normally just set them with {@link #setFlags} and let the system * take the appropriate action with them. diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index 7f8c95b..c373308 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -22,7 +22,6 @@ import android.util.Log; import java.io.Serializable; import java.util.ArrayList; -import java.util.Map; import java.util.Set; /** @@ -309,7 +308,7 @@ public class BaseBundle { * * @param map a Map */ - void putAll(Map map) { + void putAll(ArrayMap map) { unparcel(); mMap.putAll(map); } diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index c20024a..5e9b8c1 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -252,6 +252,29 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } /** + * Filter values in Bundle to only basic types. + * @hide + */ + public void filterValues() { + unparcel(); + if (mMap != null) { + for (int i = mMap.size() - 1; i >= 0; i--) { + Object value = mMap.valueAt(i); + if (PersistableBundle.isValidType(value)) { + continue; + } + if (value instanceof Bundle) { + ((Bundle)value).filterValues(); + } + if (value.getClass().getName().startsWith("android.")) { + continue; + } + mMap.removeAt(i); + } + } + } + + /** * Inserts a byte value into the mapping of this Bundle, replacing * any existing value for the given key. * diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java index a467c87..ea180b2 100644 --- a/core/java/android/os/PersistableBundle.java +++ b/core/java/android/os/PersistableBundle.java @@ -45,6 +45,16 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL; } + /** @hide */ + public static boolean isValidType(Object value) { + return (value instanceof Integer) || (value instanceof Long) || + (value instanceof Double) || (value instanceof String) || + (value instanceof int[]) || (value instanceof long[]) || + (value instanceof double[]) || (value instanceof String[]) || + (value instanceof PersistableBundle) || (value == null) || + (value instanceof Boolean) || (value instanceof boolean[]); + } + /** * Constructs a new, empty PersistableBundle. */ @@ -78,29 +88,22 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa * @param map a Map containing only those items that can be persisted. * @throws IllegalArgumentException if any element of #map cannot be persisted. */ - private PersistableBundle(Map<String, Object> map) { + private PersistableBundle(ArrayMap<String, Object> map) { super(); // First stuff everything in. putAll(map); // Now verify each item throwing an exception if there is a violation. - Set<String> keys = map.keySet(); - Iterator<String> iterator = keys.iterator(); - while (iterator.hasNext()) { - String key = iterator.next(); - Object value = map.get(key); - if (value instanceof Map) { + final int N = mMap.size(); + for (int i=0; i<N; i++) { + Object value = mMap.valueAt(i); + if (value instanceof ArrayMap) { // Fix up any Maps by replacing them with PersistableBundles. - putPersistableBundle(key, new PersistableBundle((Map<String, Object>) value)); - } else if (!(value instanceof Integer) && !(value instanceof Long) && - !(value instanceof Double) && !(value instanceof String) && - !(value instanceof int[]) && !(value instanceof long[]) && - !(value instanceof double[]) && !(value instanceof String[]) && - !(value instanceof PersistableBundle) && (value != null) && - !(value instanceof Boolean) && !(value instanceof boolean[])) { - throw new IllegalArgumentException("Bad value in PersistableBundle key=" + key + - " value=" + value); + mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value)); + } else if (!isValidType(value)) { + throw new IllegalArgumentException("Bad value in PersistableBundle key=" + + mMap.keyAt(i) + " value=" + value); } } } @@ -242,8 +245,9 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) { if (event == XmlPullParser.START_TAG) { - return new PersistableBundle((Map<String, Object>) - XmlUtils.readThisMapXml(in, startTag, tagName, new MyReadMapCallback())); + return new PersistableBundle((ArrayMap<String, Object>) + XmlUtils.readThisArrayMapXml(in, startTag, tagName, + new MyReadMapCallback())); } } return EMPTY; diff --git a/core/java/android/os/PooledStringReader.java b/core/java/android/os/PooledStringReader.java new file mode 100644 index 0000000..7795957 --- /dev/null +++ b/core/java/android/os/PooledStringReader.java @@ -0,0 +1,50 @@ +/* + * 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.os; + +/** + * Helper class for reading pooling strings from a Parcel. It must be used + * in conjunction with {@link android.os.PooledStringWriter}. This really needs + * to be pushed in to Parcel itself, but doing that is... complicated. + * @hide + */ +public class PooledStringReader { + private final Parcel mIn; + + /** + * The pool of strings we have collected so far. + */ + private final String[] mPool; + + public PooledStringReader(Parcel in) { + mIn = in; + final int size = in.readInt(); + mPool = new String[size]; + } + + public String readString() { + int idx = mIn.readInt(); + if (idx >= 0) { + return mPool[idx]; + } else { + idx = (-idx) - 1; + String str = mIn.readString(); + mPool[idx] = str; + return str; + } + } +} diff --git a/core/java/android/os/PooledStringWriter.java b/core/java/android/os/PooledStringWriter.java new file mode 100644 index 0000000..eac297d --- /dev/null +++ b/core/java/android/os/PooledStringWriter.java @@ -0,0 +1,76 @@ +/* + * 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.os; + +import java.util.HashMap; + +/** + * Helper class for writing pooled strings into a Parcel. It must be used + * in conjunction with {@link android.os.PooledStringReader}. This really needs + * to be pushed in to Parcel itself, but doing that is... complicated. + * @hide + */ +public class PooledStringWriter { + private final Parcel mOut; + + /** + * Book-keeping for writing pooled string objects, mapping strings we have + * written so far to their index in the pool. We deliberately use HashMap + * here since performance is critical, we expect to be doing lots of adds to + * it, and it is only a temporary object so its overall memory footprint is + * not a signifciant issue. + */ + private final HashMap<String, Integer> mPool; + + /** + * Book-keeping for writing pooling string objects, indicating where we + * started writing the pool, which is where we need to ultimately store + * how many strings are in the pool. + */ + private int mStart; + + /** + * Next available index in the pool. + */ + private int mNext; + + public PooledStringWriter(Parcel out) { + mOut = out; + mPool = new HashMap<>(); + mStart = out.dataPosition(); + out.writeInt(0); // reserve space for final pool size. + } + + public void writeString(String str) { + final Integer cur = mPool.get(str); + if (cur != null) { + mOut.writeInt(cur); + } else { + mPool.put(str, mNext); + mOut.writeInt(-(mNext+1)); + mOut.writeString(str); + mNext++; + } + } + + public void finish() { + final int pos = mOut.dataPosition(); + mOut.setDataPosition(mStart); + mOut.writeInt(mNext); + mOut.setDataPosition(pos); + } +} diff --git a/core/java/android/view/PhoneWindow.java b/core/java/android/view/PhoneWindow.java index b30d59a..8aef18a 100644 --- a/core/java/android/view/PhoneWindow.java +++ b/core/java/android/view/PhoneWindow.java @@ -4167,7 +4167,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) { // On TVs, if the app doesn't implement search, we want to launch assist. return ((SearchManager)getContext().getSystemService(Context.SEARCH_SERVICE)) - .launchAssistAction(0, null, UserHandle.myUserId()); + .launchAssistAction(null, UserHandle.myUserId()); } return result; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 57dc015..3927096 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5639,12 +5639,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Called when assist data is being retrieved from a view as part of + * Called when assist structure is being retrieved from a view as part of * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}. - * @param data - * @param extras + * @param structure Additional standard structured view structure to supply. + * @param extras Non-standard extensions. */ - public void onProvideAssistData(ViewAssistData data, Bundle extras) { + public void onProvideAssistStructure(ViewAssistStructure structure, Bundle extras) { } /** diff --git a/core/java/android/view/ViewAssistData.java b/core/java/android/view/ViewAssistStructure.java index 74436ea..5132bb9 100644 --- a/core/java/android/view/ViewAssistData.java +++ b/core/java/android/view/ViewAssistStructure.java @@ -16,13 +16,16 @@ package android.view; +import android.text.TextPaint; + /** - * Container for storing data generated by {@link View#onProvideAssistData - * View.onProvideAssistData}. + * Container for storing additional per-view data generated by {@link View#onProvideAssistStructure + * View.onProvideAssistStructure}. */ -public abstract class ViewAssistData { +public abstract class ViewAssistStructure { public abstract void setText(CharSequence text); public abstract void setText(CharSequence text, int selectionStart, int selectionEnd); + public abstract void setTextPaint(TextPaint paint); public abstract void setHint(CharSequence hint); public abstract CharSequence getText(); diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index ce1834e..bb290e7 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -46,7 +46,7 @@ import android.view.Gravity; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.VelocityTracker; -import android.view.ViewAssistData; +import android.view.ViewAssistStructure; import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -1363,18 +1363,19 @@ public class Switch extends CompoundButton { } @Override - public void onProvideAssistData(ViewAssistData data, Bundle extras) { - super.onProvideAssistData(data, extras); + public void onProvideAssistStructure(ViewAssistStructure structure, Bundle extras) { + super.onProvideAssistStructure(structure, extras); CharSequence switchText = isChecked() ? mTextOn : mTextOff; if (!TextUtils.isEmpty(switchText)) { - CharSequence oldText = data.getText(); + CharSequence oldText = structure.getText(); if (TextUtils.isEmpty(oldText)) { - data.setText(switchText); + structure.setText(switchText); } else { StringBuilder newText = new StringBuilder(); newText.append(oldText).append(' ').append(switchText); - data.setText(newText); + structure.setText(newText); } + structure.setTextPaint(mTextPaint); } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 628833d..447e9ac 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -115,7 +115,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.ViewAssistData; +import android.view.ViewAssistStructure; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup.LayoutParams; @@ -8580,13 +8580,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public void onProvideAssistData(ViewAssistData data, Bundle extras) { - super.onProvideAssistData(data, extras); + public void onProvideAssistStructure(ViewAssistStructure structure, Bundle extras) { + super.onProvideAssistStructure(structure, extras); final boolean isPassword = hasPasswordTransformationMethod(); if (!isPassword) { - data.setText(getText(), getSelectionStart(), getSelectionEnd()); + structure.setText(getText(), getSelectionStart(), getSelectionEnd()); + structure.setTextPaint(mTextPaint); } - data.setHint(getHint()); + structure.setHint(getHint()); } /** @hide */ diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java index 2bd607c..0350d61 100644 --- a/core/java/com/android/internal/util/XmlUtils.java +++ b/core/java/com/android/internal/util/XmlUtils.java @@ -20,6 +20,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Bitmap.CompressFormat; import android.net.Uri; +import android.util.ArrayMap; import android.util.Base64; import android.util.Xml; @@ -822,7 +823,36 @@ public class XmlUtils { int eventType = parser.getEventType(); do { if (eventType == parser.START_TAG) { - Object val = readThisValueXml(parser, name, callback); + Object val = readThisValueXml(parser, name, callback, false); + map.put(name[0], val); + } else if (eventType == parser.END_TAG) { + if (parser.getName().equals(endTag)) { + return map; + } + throw new XmlPullParserException( + "Expected " + endTag + " end tag at: " + parser.getName()); + } + eventType = parser.next(); + } while (eventType != parser.END_DOCUMENT); + + throw new XmlPullParserException( + "Document ended before " + endTag + " end tag"); + } + + /** + * Like {@link #readThisMapXml}, but returns an ArrayMap instead of HashMap. + * @hide + */ + public static final ArrayMap<String, ?> readThisArrayMapXml(XmlPullParser parser, String endTag, + String[] name, ReadMapCallback callback) + throws XmlPullParserException, java.io.IOException + { + ArrayMap<String, Object> map = new ArrayMap<>(); + + int eventType = parser.getEventType(); + do { + if (eventType == parser.START_TAG) { + Object val = readThisValueXml(parser, name, callback, true); map.put(name[0], val); } else if (eventType == parser.END_TAG) { if (parser.getName().equals(endTag)) { @@ -854,7 +884,7 @@ public class XmlUtils { */ public static final ArrayList readThisListXml(XmlPullParser parser, String endTag, String[] name) throws XmlPullParserException, java.io.IOException { - return readThisListXml(parser, endTag, name, null); + return readThisListXml(parser, endTag, name, null, false); } /** @@ -872,14 +902,14 @@ public class XmlUtils { * @see #readListXml */ private static final ArrayList readThisListXml(XmlPullParser parser, String endTag, - String[] name, ReadMapCallback callback) + String[] name, ReadMapCallback callback, boolean arrayMap) throws XmlPullParserException, java.io.IOException { ArrayList list = new ArrayList(); int eventType = parser.getEventType(); do { if (eventType == parser.START_TAG) { - Object val = readThisValueXml(parser, name, callback); + Object val = readThisValueXml(parser, name, callback, arrayMap); list.add(val); //System.out.println("Adding to list: " + val); } else if (eventType == parser.END_TAG) { @@ -915,7 +945,7 @@ public class XmlUtils { */ public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name) throws XmlPullParserException, java.io.IOException { - return readThisSetXml(parser, endTag, name, null); + return readThisSetXml(parser, endTag, name, null, false); } /** @@ -937,13 +967,14 @@ public class XmlUtils { * @hide */ private static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name, - ReadMapCallback callback) throws XmlPullParserException, java.io.IOException { + ReadMapCallback callback, boolean arrayMap) + throws XmlPullParserException, java.io.IOException { HashSet set = new HashSet(); int eventType = parser.getEventType(); do { if (eventType == parser.START_TAG) { - Object val = readThisValueXml(parser, name, callback); + Object val = readThisValueXml(parser, name, callback, arrayMap); set.add(val); //System.out.println("Adding to set: " + val); } else if (eventType == parser.END_TAG) { @@ -1292,7 +1323,7 @@ public class XmlUtils { int eventType = parser.getEventType(); do { if (eventType == parser.START_TAG) { - return readThisValueXml(parser, name, null); + return readThisValueXml(parser, name, null, false); } else if (eventType == parser.END_TAG) { throw new XmlPullParserException( "Unexpected end tag at: " + parser.getName()); @@ -1308,7 +1339,8 @@ public class XmlUtils { } private static final Object readThisValueXml(XmlPullParser parser, String[] name, - ReadMapCallback callback) throws XmlPullParserException, java.io.IOException { + ReadMapCallback callback, boolean arrayMap) + throws XmlPullParserException, java.io.IOException { final String valueName = parser.getAttributeValue(null, "name"); final String tagName = parser.getName(); @@ -1368,19 +1400,21 @@ public class XmlUtils { return res; } else if (tagName.equals("map")) { parser.next(); - res = readThisMapXml(parser, "map", name); + res = arrayMap + ? readThisArrayMapXml(parser, "map", name, callback) + : readThisMapXml(parser, "map", name, callback); name[0] = valueName; //System.out.println("Returning value for " + valueName + ": " + res); return res; } else if (tagName.equals("list")) { parser.next(); - res = readThisListXml(parser, "list", name); + res = readThisListXml(parser, "list", name, callback, arrayMap); name[0] = valueName; //System.out.println("Returning value for " + valueName + ": " + res); return res; } else if (tagName.equals("set")) { parser.next(); - res = readThisSetXml(parser, "set", name); + res = readThisSetXml(parser, "set", name, callback, arrayMap); name[0] = valueName; //System.out.println("Returning value for " + valueName + ": " + res); return res; |