diff --git a/CleanSpec.mk b/CleanSpec.mk index a9f4d195faa1c..e961a5a06cb36 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -77,6 +77,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/libhwui.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/libhwui.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libhwui.so) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/storage/*) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/content/IClipboard.P) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST diff --git a/api/current.xml b/api/current.xml index b8871bb17b77d..ad4fb2204def9 100644 --- a/api/current.xml +++ b/api/current.xml @@ -39293,6 +39293,436 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + *

A ClipData is a sub-class of {@link ClipDescription}, which describes + * important meta-data about the clip. In particular, its {@link #getMimeType(int)} + * must return correct MIME type(s) describing the data in the clip. For help + * in correctly constructing a clip with the correct MIME type, use + * {@link #newPlainText(CharSequence, Bitmap, CharSequence)}, + * {@link #newUri(ContentResolver, CharSequence, Bitmap, Uri)}, and + * {@link #newIntent(CharSequence, Bitmap, Intent)}. + * *

Each Item instance can be one of three main classes of data: a simple * CharSequence of text, a single Intent object, or a Uri. See {@link Item} * for more details. @@ -53,11 +61,16 @@ import java.util.ArrayList; * *

If all you want is the textual representation of the clipped data, you * can use the convenience method {@link Item#coerceToText Item.coerceToText}. + * In this case there is generally no need to worry about the MIME types + * reported by {@link #getMimeType(int)}, since any clip item an always be + * converted to a string. * *

More complicated exchanges will be done through URIs, in particular * "content:" URIs. A content URI allows the recipient of a ClippedData item * to interact closely with the ContentProvider holding the data in order to - * negotiate the transfer of that data. + * negotiate the transfer of that data. The clip must also be filled in with + * the available MIME types; {@link #newUri(ContentResolver, CharSequence, Bitmap, Uri)} + * will take care of correctly doing this. * *

For example, here is the paste function of a simple NotePad application. * When retrieving the data from the clipboard, it can do either two things: @@ -120,9 +133,12 @@ import java.util.ArrayList; * into an editor), then {@link Item#coerceToText(Context)} will ask the content * provider for the clip URI as text and successfully paste the entire note. */ -public class ClippedData implements Parcelable { - CharSequence mLabel; - Bitmap mIcon; +public class ClipData extends ClipDescription { + static final String[] MIMETYPES_TEXT_PLAIN = new String[] { MIMETYPE_TEXT_PLAIN }; + static final String[] MIMETYPES_TEXT_URILIST = new String[] { MIMETYPE_TEXT_URILIST }; + static final String[] MIMETYPES_TEXT_INTENT = new String[] { MIMETYPE_TEXT_INTENT }; + + final Bitmap mIcon; final ArrayList mItems = new ArrayList(); @@ -146,28 +162,34 @@ public class ClippedData implements Parcelable { * */ public static class Item { - CharSequence mText; - Intent mIntent; - Uri mUri; + final CharSequence mText; + final Intent mIntent; + final Uri mUri; /** * Create an Item consisting of a single block of (possibly styled) text. */ public Item(CharSequence text) { mText = text; + mIntent = null; + mUri = null; } /** * Create an Item consisting of an arbitrary Intent. */ public Item(Intent intent) { + mText = null; mIntent = intent; + mUri = null; } /** * Create an Item consisting of an arbitrary URI. */ public Item(Uri uri) { + mText = null; + mIntent = null; mUri = uri; } @@ -292,19 +314,103 @@ public class ClippedData implements Parcelable { * Create a new clip. * * @param label Label to show to the user describing this clip. + * @param mimeTypes An array of MIME types this data is available as. * @param icon Bitmap providing the user with an iconing representation of * the clip. * @param item The contents of the first item in the clip. */ - public ClippedData(CharSequence label, Bitmap icon, Item item) { + public ClipData(CharSequence label, String[] mimeTypes, Bitmap icon, Item item) { + super(label, mimeTypes); if (item == null) { throw new NullPointerException("item is null"); } - mLabel = label; mIcon = icon; mItems.add(item); } + /** + * Create a new ClipData holding data of the type {@link #MIMETYPE_TEXT_PLAIN}. + * + * @param label User-visible label for the clip data. + * @param icon Iconic representation of the clip data. + * @param text The actual text in the clip. + * @return Returns a new ClipData containing the specified data. + */ + static public ClipData newPlainText(CharSequence label, Bitmap icon, CharSequence text) { + Item item = new Item(text); + return new ClipData(label, MIMETYPES_TEXT_PLAIN, icon, item); + } + + /** + * Create a new ClipData holding an Intent with MIME type {@link #MIMETYPE_TEXT_INTENT}. + * + * @param label User-visible label for the clip data. + * @param icon Iconic representation of the clip data. + * @param intent The actual Intent in the clip. + * @return Returns a new ClipData containing the specified data. + */ + static public ClipData newIntent(CharSequence label, Bitmap icon, Intent intent) { + Item item = new Item(intent); + return new ClipData(label, MIMETYPES_TEXT_INTENT, icon, item); + } + + /** + * Create a new ClipData holding a URI. If the URI is a content: URI, + * this will query the content provider for the MIME type of its data and + * use that as the MIME type. Otherwise, it will use the MIME type + * {@link #MIMETYPE_TEXT_URILIST}. + * + * @param resolver ContentResolver used to get information about the URI. + * @param label User-visible label for the clip data. + * @param icon Iconic representation of the clip data. + * @param uri The URI in the clip. + * @return Returns a new ClipData containing the specified data. + */ + static public ClipData newUri(ContentResolver resolver, CharSequence label, + Bitmap icon, Uri uri) { + Item item = new Item(uri); + String[] mimeTypes = null; + if ("content".equals(uri.getScheme())) { + String realType = resolver.getType(uri); + mimeTypes = resolver.getStreamTypes(uri, "*/*"); + if (mimeTypes == null) { + if (realType != null) { + mimeTypes = new String[] { realType, MIMETYPE_TEXT_URILIST }; + } + } else { + String[] tmp = new String[mimeTypes.length + (realType != null ? 2 : 1)]; + int i = 0; + if (realType != null) { + tmp[0] = realType; + i++; + } + System.arraycopy(mimeTypes, 0, tmp, i, mimeTypes.length); + tmp[i + mimeTypes.length] = MIMETYPE_TEXT_URILIST; + mimeTypes = tmp; + } + } + if (mimeTypes == null) { + mimeTypes = MIMETYPES_TEXT_URILIST; + } + return new ClipData(label, mimeTypes, icon, item); + } + + /** + * Create a new ClipData holding an URI with MIME type {@link #MIMETYPE_TEXT_URILIST}. + * Unlike {@link #newUri(ContentResolver, CharSequence, Bitmap, Uri)}, nothing + * is inferred about the URI -- if it is a content: URI holding a bitmap, + * the reported type will still be uri-list. Use this with care! + * + * @param label User-visible label for the clip data. + * @param icon Iconic representation of the clip data. + * @param uri The URI in the clip. + * @return Returns a new ClipData containing the specified data. + */ + static public ClipData newRawUri(CharSequence label, Bitmap icon, Uri uri) { + Item item = new Item(uri); + return new ClipData(label, MIMETYPES_TEXT_URILIST, icon, item); + } + public void addItem(Item item) { if (item == null) { throw new NullPointerException("item is null"); @@ -312,10 +418,6 @@ public class ClippedData implements Parcelable { mItems.add(item); } - public CharSequence getLabel() { - return mLabel; - } - public Bitmap getIcon() { return mIcon; } @@ -335,7 +437,7 @@ public class ClippedData implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - TextUtils.writeToParcel(mLabel, dest, flags); + super.writeToParcel(dest, flags); if (mIcon != null) { dest.writeInt(1); mIcon.writeToParcel(dest, flags); @@ -362,10 +464,12 @@ public class ClippedData implements Parcelable { } } - ClippedData(Parcel in) { - mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + ClipData(Parcel in) { + super(in); if (in.readInt() != 0) { mIcon = Bitmap.CREATOR.createFromParcel(in); + } else { + mIcon = null; } final int N = in.readInt(); for (int i=0; i CREATOR = - new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { - public ClippedData createFromParcel(Parcel source) { - return new ClippedData(source); + public ClipData createFromParcel(Parcel source) { + return new ClipData(source); } - public ClippedData[] newArray(int size) { - return new ClippedData[size]; + public ClipData[] newArray(int size) { + return new ClipData[size]; } }; } diff --git a/core/java/android/content/ClipDescription.aidl b/core/java/android/content/ClipDescription.aidl new file mode 100644 index 0000000000000..391fd5a086d69 --- /dev/null +++ b/core/java/android/content/ClipDescription.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2010, 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.content; + +parcelable ClipDescription; diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java new file mode 100644 index 0000000000000..3229f0f9a2114 --- /dev/null +++ b/core/java/android/content/ClipDescription.java @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2010, 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.content; + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import java.util.ArrayList; + +/** + * Meta-data describing the contents of a {@link #ClipData}. Provides enough + * information to know if you can handle the ClipData, but not the data + * itself. + */ +public class ClipDescription implements Parcelable { + /** + * The MIME type for a clip holding plain text. + */ + public static final String MIMETYPE_TEXT_PLAIN = "text/plain"; + + /** + * The MIME type for a clip holding one or more URIs. This should be + * used for URIs that are meaningful to a user (such as an http: URI). + * It should not be used for a content: URI that references some + * other piece of data; in that case the MIME type should be the type + * of the referenced data. + */ + public static final String MIMETYPE_TEXT_URILIST = "text/uri-list"; + + /** + * The MIME type for a clip holding an Intent. + */ + public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent"; + + final CharSequence mLabel; + final String[] mMimeTypes; + + /** + * Create a new clip. + * + * @param label Label to show to the user describing this clip. + * @param mimeTypes An array of MIME types this data is available as. + */ + public ClipDescription(CharSequence label, String[] mimeTypes) { + if (mimeTypes == null) { + throw new NullPointerException("mimeTypes is null"); + } + mLabel = label; + mMimeTypes = mimeTypes; + } + + /** + * Create a copy of a ClipDescription. + */ + public ClipDescription(ClipDescription o) { + mLabel = o.mLabel; + mMimeTypes = o.mMimeTypes; + } + + /** + * Helper to compare two MIME types, where one may be a pattern. + * @param concreteType A fully-specified MIME type. + * @param desiredType A desired MIME type that may be a pattern such as *\/*. + * @return Returns true if the two MIME types match. + */ + public static boolean compareMimeTypes(String concreteType, String desiredType) { + final int typeLength = desiredType.length(); + if (typeLength == 3 && desiredType.equals("*/*")) { + return true; + } + + final int slashpos = desiredType.indexOf('/'); + if (slashpos > 0) { + if (typeLength == slashpos+2 && desiredType.charAt(slashpos+1) == '*') { + if (desiredType.regionMatches(0, concreteType, 0, slashpos+1)) { + return true; + } + } else if (desiredType.equals(concreteType)) { + return true; + } + } + + return false; + } + + /** + * Return the label for this clip. + */ + public CharSequence getLabel() { + return mLabel; + } + + /** + * Check whether the clip description contains the given MIME type. + * + * @param mimeType The desired MIME type. May be a pattern. + * @return Returns true if one of the MIME types in the clip description + * matches the desired MIME type, else false. + */ + public boolean hasMimeType(String mimeType) { + for (int i=0; i array = null; + for (int i=0; i(); + } + array.add(mMimeTypes[i]); + } + } + if (array == null) { + return null; + } + String[] rawArray = new String[array.size()]; + array.toArray(rawArray); + return rawArray; + } + + /** + * Return the number of MIME types the clip is available in. + */ + public int getMimeTypeCount() { + return mMimeTypes.length; + } + + /** + * Return one of the possible clip MIME types. + */ + public String getMimeType(int index) { + return mMimeTypes[index]; + } + + /** @hide */ + public void validate() { + if (mMimeTypes == null) { + throw new NullPointerException("null mime types"); + } + if (mMimeTypes.length <= 0) { + throw new IllegalArgumentException("must have at least 1 mime type"); + } + for (int i=0; i CREATOR = + new Parcelable.Creator() { + + public ClipDescription createFromParcel(Parcel source) { + return new ClipDescription(source); + } + + public ClipDescription[] newArray(int size) { + return new ClipDescription[size]; + } + }; +} diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java index d685cf37dde52..0ea06482dbe4a 100644 --- a/core/java/android/content/ClipboardManager.java +++ b/core/java/android/content/ClipboardManager.java @@ -37,7 +37,7 @@ import java.util.ArrayList; *

* The ClipboardManager API itself is very simple: it consists of methods * to atomically get and set the current primary clipboard data. That data - * is expressed as a {@link ClippedData} object, which defines the protocol + * is expressed as a {@link ClipData} object, which defines the protocol * for data exchange between applications. * * @see android.content.Context#getSystemService @@ -96,7 +96,7 @@ public class ClipboardManager extends android.text.ClipboardManager { * * @param clip The clipped data item to set. */ - public void setPrimaryClip(ClippedData clip) { + public void setPrimaryClip(ClipData clip) { try { getService().setPrimaryClip(clip); } catch (RemoteException e) { @@ -106,7 +106,7 @@ public class ClipboardManager extends android.text.ClipboardManager { /** * Returns the current primary clip on the clipboard. */ - public ClippedData getPrimaryClip() { + public ClipData getPrimaryClip() { try { return getService().getPrimaryClip(); } catch (RemoteException e) { @@ -114,6 +114,18 @@ public class ClipboardManager extends android.text.ClipboardManager { } } + /** + * Returns a description of the current primary clip on the clipboard + * but not a copy of its data. + */ + public ClipDescription getPrimaryClipDescription() { + try { + return getService().getPrimaryClipDescription(); + } catch (RemoteException e) { + return null; + } + } + /** * Returns true if there is currently a primary clip on the clipboard. */ @@ -156,7 +168,7 @@ public class ClipboardManager extends android.text.ClipboardManager { * the primary clip and tries to coerce it to a string. */ public CharSequence getText() { - ClippedData clip = getPrimaryClip(); + ClipData clip = getPrimaryClip(); if (clip != null && clip.getItemCount() > 0) { return clip.getItem(0).coerceToText(mContext); } @@ -164,12 +176,12 @@ public class ClipboardManager extends android.text.ClipboardManager { } /** - * @deprecated Use {@link #setPrimaryClip(ClippedData)} instead. This + * @deprecated Use {@link #setPrimaryClip(ClipData)} instead. This * creates a ClippedItem holding the given text and sets it as the * primary clip. It has no label or icon. */ public void setText(CharSequence text) { - setPrimaryClip(new ClippedData(null, null, new ClippedData.Item(text))); + setPrimaryClip(ClipData.newPlainText(null, null, text)); } /** diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 88e0123970834..d3c1b4e567e95 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -766,32 +766,6 @@ public abstract class ContentProvider implements ComponentCallbacks { return ParcelFileDescriptor.open(new File(path), modeBits); } - /** - * Helper to compare two MIME types, where one may be a pattern. - * @param concreteType A fully-specified MIME type. - * @param desiredType A desired MIME type that may be a pattern such as *\/*. - * @return Returns true if the two MIME types match. - */ - public static boolean compareMimeTypes(String concreteType, String desiredType) { - final int typeLength = desiredType.length(); - if (typeLength == 3 && desiredType.equals("*/*")) { - return true; - } - - final int slashpos = desiredType.indexOf('/'); - if (slashpos > 0) { - if (typeLength == slashpos+2 && desiredType.charAt(slashpos+1) == '*') { - if (desiredType.regionMatches(0, concreteType, 0, slashpos+1)) { - return true; - } - } else if (desiredType.equals(concreteType)) { - return true; - } - } - - return false; - } - /** * Called by a client to determine the types of data streams that this * content provider supports for the given URI. The default implementation @@ -809,7 +783,7 @@ public abstract class ContentProvider implements ComponentCallbacks { * * @see #getType(Uri) * @see #openTypedAssetFile(Uri, String, Bundle) - * @see #compareMimeTypes(String, String) + * @see ClipDescription#compareMimeTypes(String, String) */ public String[] getStreamTypes(Uri uri, String mimeTypeFilter) { return null; @@ -825,7 +799,7 @@ public abstract class ContentProvider implements ComponentCallbacks { * result of {@link #getType(Uri)} and, if the match, simple calls * {@link #openAssetFile(Uri, String)}. * - *

See {@link ClippedData} for examples of the use and implementation + *

See {@link ClipData} for examples of the use and implementation * of this method. * * @param uri The data in the content provider being queried. @@ -848,7 +822,7 @@ public abstract class ContentProvider implements ComponentCallbacks { * * @see #getStreamTypes(Uri, String) * @see #openAssetFile(Uri, String) - * @see #compareMimeTypes(String, String) + * @see ClipDescription#compareMimeTypes(String, String) */ public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts) throws FileNotFoundException { @@ -857,7 +831,7 @@ public abstract class ContentProvider implements ComponentCallbacks { return openAssetFile(uri, "r"); } String baseType = getType(uri); - if (baseType != null && compareMimeTypes(baseType, mimeTypeFilter)) { + if (baseType != null && ClipDescription.compareMimeTypes(baseType, mimeTypeFilter)) { // Use old untyped open call if this provider has a type for this // URI and it matches the request. return openAssetFile(uri, "r"); diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl index b4534a94b6765..3e1fe55f8b95d 100644 --- a/core/java/android/content/IClipboard.aidl +++ b/core/java/android/content/IClipboard.aidl @@ -16,7 +16,8 @@ package android.content; -import android.content.ClippedData; +import android.content.ClipData; +import android.content.ClipDescription; import android.content.IOnPrimaryClipChangedListener; /** @@ -25,8 +26,9 @@ import android.content.IOnPrimaryClipChangedListener; * {@hide} */ interface IClipboard { - void setPrimaryClip(in ClippedData clip); - ClippedData getPrimaryClip(); + void setPrimaryClip(in ClipData clip); + ClipData getPrimaryClip(); + ClipDescription getPrimaryClipDescription(); boolean hasPrimaryClip(); void addPrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener); void removePrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 6db5b9c2ff1b4..961c7821894c4 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -22,7 +22,7 @@ import com.android.internal.widget.EditableInputConnection; import org.xmlpull.v1.XmlPullParserException; import android.content.ClipboardManager; -import android.content.ClippedData; +import android.content.ClipData; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.ColorStateList; @@ -7330,17 +7330,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener URLSpan[] urls = ((Spanned) mText).getSpans(min, max, URLSpan.class); if (urls.length >= 1) { - ClippedData clip = null; + ClipData clip = null; for (int i=0; i mPrimaryClipListeners = new RemoteCallbackList(); @@ -36,7 +37,7 @@ public class ClipboardService extends IClipboard.Stub { */ public ClipboardService(Context context) { } - public void setPrimaryClip(ClippedData clip) { + public void setPrimaryClip(ClipData clip) { synchronized (this) { if (clip != null && clip.getItemCount() <= 0) { throw new IllegalArgumentException("No items"); @@ -56,12 +57,18 @@ public class ClipboardService extends IClipboard.Stub { } } - public ClippedData getPrimaryClip() { + public ClipData getPrimaryClip() { synchronized (this) { return mPrimaryClip; } } + public ClipDescription getPrimaryClipDescription() { + synchronized (this) { + return new ClipDescription(mPrimaryClip); + } + } + public boolean hasPrimaryClip() { synchronized (this) { return mPrimaryClip != null;