From a83ce1dd2ad3a6b71e90ff4845afc1299fe17b9d Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 11 Mar 2015 15:16:13 -0700 Subject: More work on collecting assist data. Optimize parceling of AssistData (which is now renamed to AssistStructure) by pooling duplicated class name strings. Change text associated with a view node to a CharSequence, so styling information comes along. Include global text attributes -- size, colors, etc. Introduce a new AssistContent structure, which allows us to propagate information about the intent and data the activity is looking at. This further allows us to propagate permission grants, so the assistant can dig in to that data. The default implementation propagates the base intent of an activity, so if for example you bring up the assistant while doing a share the assistant itself has the same information and access that was given to the share activity (so it could for example share it in another way if it wanted to). Did some optimization of loading PersistableBundle from xml, to avoid duplicating hash maps and such. Changed how we dispatch ACTION_ASSIST to no longer include the more detailed AssistStructure (and new AssistContent) data when launching; now the example code that intercepts that needs to be sure to ask for assist data when it starts its session. This is more like it will finally be, and allows us to get to the UI more quickly. Change-Id: I88420a55761bf48d34ce3013e81bd96a0e087637 --- core/java/android/app/Activity.java | 28 +- core/java/android/app/ActivityManager.java | 6 + core/java/android/app/ActivityThread.java | 11 + core/java/android/app/AssistContent.aidl | 19 + core/java/android/app/AssistContent.java | 124 +++++ core/java/android/app/AssistData.java | 531 ------------------- core/java/android/app/AssistStructure.aidl | 19 + core/java/android/app/AssistStructure.java | 618 ++++++++++++++++++++++ core/java/android/app/ISearchManager.aidl | 2 +- core/java/android/app/SearchManager.java | 6 +- core/java/android/content/Intent.java | 17 + core/java/android/os/BaseBundle.java | 3 +- core/java/android/os/Bundle.java | 23 + core/java/android/os/PersistableBundle.java | 40 +- core/java/android/os/PooledStringReader.java | 50 ++ core/java/android/os/PooledStringWriter.java | 76 +++ core/java/android/view/PhoneWindow.java | 2 +- core/java/android/view/View.java | 8 +- core/java/android/view/ViewAssistData.java | 32 -- core/java/android/view/ViewAssistStructure.java | 35 ++ core/java/android/widget/Switch.java | 13 +- core/java/android/widget/TextView.java | 11 +- core/java/com/android/internal/util/XmlUtils.java | 58 +- 23 files changed, 1112 insertions(+), 620 deletions(-) create mode 100644 core/java/android/app/AssistContent.aidl create mode 100644 core/java/android/app/AssistContent.java delete mode 100644 core/java/android/app/AssistData.java create mode 100644 core/java/android/app/AssistStructure.aidl create mode 100644 core/java/android/app/AssistStructure.java create mode 100644 core/java/android/os/PooledStringReader.java create mode 100644 core/java/android/os/PooledStringWriter.java delete mode 100644 core/java/android/view/ViewAssistData.java create mode 100644 core/java/android/view/ViewAssistStructure.java (limited to 'core') diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 9f8befe..02f33b4 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1547,17 +1547,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. * *

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. + * + *

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.

+ * + * @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 CREATOR + = new Parcelable.Creator() { + 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/AssistData.java b/core/java/android/app/AssistData.java deleted file mode 100644 index 7b5eb6d..0000000 --- a/core/java/android/app/AssistData.java +++ /dev/null @@ -1,531 +0,0 @@ -/* - * 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.Rect; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; -import android.view.View; -import android.view.ViewAssistData; -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 #getAssistData(android.os.Bundle)}. - */ -final public class AssistData implements Parcelable { - static final String TAG = "AssistData"; - - /** - * Key name this data structure is stored in the Bundle generated by - * {@link Activity#onProvideAssistData}. - */ - public static final String ASSIST_KEY = "android:assist"; - - final ComponentName mActivityComponent; - - final ArrayList mRootViews = new ArrayList<>(); - - ViewAssistDataImpl mTmpViewAssistDataImpl = new ViewAssistDataImpl(); - Bundle mTmpExtras = new Bundle(); - - final static class ViewAssistDataImpl extends ViewAssistData { - CharSequence mText; - int mTextSelectionStart = -1; - int mTextSelectionEnd = -1; - 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 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 String mText; - final int mTextSelectionStart; - final int mTextSelectionEnd; - final String mHint; - - ViewNodeTextImpl(ViewAssistDataImpl data) { - mText = data.mText != null ? data.mText.toString() : null; - mTextSelectionStart = data.mTextSelectionStart; - mTextSelectionEnd = data.mTextSelectionEnd; - mHint = data.mHint != null ? data.mHint.toString() : null; - } - - ViewNodeTextImpl(Parcel in) { - mText = in.readString(); - mTextSelectionStart = in.readInt(); - mTextSelectionEnd = in.readInt(); - mHint = in.readString(); - } - - void writeToParcel(Parcel out) { - out.writeString(mText); - out.writeInt(mTextSelectionStart); - out.writeInt(mTextSelectionEnd); - 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(AssistData assistData, 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 ViewAssistDataImpl viewData = assistData.mTmpViewAssistDataImpl; - final Bundle extras = assistData.mTmpExtras; - view.onProvideAssistData(viewData, extras); - if (viewData.mText != null || viewData.mHint != null) { - mText = new ViewNodeTextImpl(viewData); - assistData.mTmpViewAssistDataImpl = new ViewAssistDataImpl(); - } else { - mText = null; - } - if (!extras.isEmpty()) { - mExtras = extras; - assistData.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 0) { - mChildren = new ViewNodeImpl[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 + " "; - ViewNode cnode = new ViewNode(); - for (int i=0; i CREATOR - = new Parcelable.Creator() { - public AssistData createFromParcel(Parcel in) { - return new AssistData(in); - } - - public AssistData[] newArray(int size) { - return new AssistData[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/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 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 0) { + mChildren = new ViewNodeImpl[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 + " "; + ViewNode cnode = new ViewNode(); + 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]; + } + }; +} 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 map) { + private PersistableBundle(ArrayMap map) { super(); // First stuff everything in. putAll(map); // Now verify each item throwing an exception if there is a violation. - Set keys = map.keySet(); - Iterator 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) 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) 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) - XmlUtils.readThisMapXml(in, startTag, tagName, new MyReadMapCallback())); + return new PersistableBundle((ArrayMap) + 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 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 d4b14f6..cf5affc 100644 --- a/core/java/android/view/PhoneWindow.java +++ b/core/java/android/view/PhoneWindow.java @@ -4158,7 +4158,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 4f2d4a6..69eb7b4 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5620,12 +5620,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/ViewAssistData.java deleted file mode 100644 index 74436ea..0000000 --- a/core/java/android/view/ViewAssistData.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.view; - -/** - * Container for storing data generated by {@link View#onProvideAssistData - * View.onProvideAssistData}. - */ -public abstract class ViewAssistData { - public abstract void setText(CharSequence text); - public abstract void setText(CharSequence text, int selectionStart, int selectionEnd); - public abstract void setHint(CharSequence hint); - - public abstract CharSequence getText(); - public abstract int getTextSelectionStart(); - public abstract int getTextSelectionEnd(); - public abstract CharSequence getHint(); -} diff --git a/core/java/android/view/ViewAssistStructure.java b/core/java/android/view/ViewAssistStructure.java new file mode 100644 index 0000000..5132bb9 --- /dev/null +++ b/core/java/android/view/ViewAssistStructure.java @@ -0,0 +1,35 @@ +/* + * 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.view; + +import android.text.TextPaint; + +/** + * Container for storing additional per-view data generated by {@link View#onProvideAssistStructure + * View.onProvideAssistStructure}. + */ +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(); + public abstract int getTextSelectionStart(); + public abstract int getTextSelectionEnd(); + public abstract CharSequence getHint(); +} 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 readThisArrayMapXml(XmlPullParser parser, String endTag, + String[] name, ReadMapCallback callback) + throws XmlPullParserException, java.io.IOException + { + ArrayMap 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; -- cgit v1.1