| /** |
| * Copyright (c) 2011, Google Inc. |
| * |
| * 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 com.android.mail.providers; |
| |
| import com.google.common.collect.Lists; |
| |
| import android.content.Intent; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.text.TextUtils; |
| |
| import com.android.mail.providers.UIProvider.AttachmentColumns; |
| import com.android.mail.providers.UIProvider.AttachmentDestination; |
| import com.android.mail.providers.UIProvider.AttachmentState; |
| import com.android.mail.utils.LogUtils; |
| |
| import org.json.JSONArray; |
| import org.json.JSONException; |
| import org.json.JSONObject; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| public class Attachment implements Parcelable { |
| public static final int SERVER_ATTACHMENT = 0; |
| /** Extras are "<path>". */ |
| public static final int LOCAL_FILE = 1; |
| |
| public static final String LOG_TAG = new LogUtils().getLogTag(); |
| |
| /** |
| * Attachment file name. See {@link AttachmentColumns#NAME}. |
| */ |
| public String name; |
| |
| /** |
| * Attachment size in bytes. See {@link AttachmentColumns#SIZE}. |
| */ |
| public int size; |
| |
| /** |
| * The provider-generated URI for this Attachment. Must be globally unique. For local |
| * attachments generated by the Compose UI prior to send/save, this field will be null. |
| * |
| *@see AttachmentColumns#URI |
| */ |
| public Uri uri; |
| |
| /** |
| * MIME type of the file. |
| * |
| * @see AttachmentColumns#CONTENT_TYPE |
| */ |
| public String contentType; |
| |
| /** |
| * @see AttachmentColumns#STATE |
| */ |
| public int state; |
| |
| /** |
| * @see AttachmentColumns#DESTINATION |
| */ |
| public int destination; |
| |
| /** |
| * @see AttachmentColumns#DOWNLOADED_SIZE |
| */ |
| public int downloadedSize; |
| |
| /** |
| * @see AttachmentColumns#CONTENT_URI |
| */ |
| public Uri contentUri; |
| |
| /** |
| * Might be null. |
| * |
| * @see AttachmentColumns#THUMBNAIL_URI |
| */ |
| public Uri thumbnailUri; |
| |
| /** |
| * Might be null. |
| * |
| * @see AttachmentColumns#PREVIEW_INTENT |
| */ |
| public Intent previewIntent; |
| |
| /** |
| * Part id of the attachment. |
| */ |
| @Deprecated |
| // partId is provider implementation specific. providers must uniquely identify Attachments |
| // by uri. |
| public String partId; |
| |
| @Deprecated |
| // this is a local file iff the provider 'uri' field is null. |
| public int origin; |
| |
| /** |
| * Attachment origin info. |
| * TODO: do we want this? Or location? |
| */ |
| @Deprecated |
| public String originExtras; |
| |
| public Attachment(Parcel in) { |
| name = in.readString(); |
| size = in.readInt(); |
| uri = in.readParcelable(null); |
| contentType = in.readString(); |
| state = in.readInt(); |
| destination = in.readInt(); |
| downloadedSize = in.readInt(); |
| contentUri = in.readParcelable(null); |
| thumbnailUri = in.readParcelable(null); |
| previewIntent = in.readParcelable(null); |
| partId = in.readString(); |
| origin = in.readInt(); |
| originExtras = in.readString(); |
| } |
| |
| public Attachment() { |
| } |
| |
| public Attachment(Cursor cursor) { |
| if (cursor == null) { |
| return; |
| } |
| |
| name = cursor.getString(UIProvider.ATTACHMENT_NAME_COLUMN); |
| size = cursor.getInt(UIProvider.ATTACHMENT_SIZE_COLUMN); |
| uri = Uri.parse(cursor.getString(UIProvider.ATTACHMENT_URI_COLUMN)); |
| contentType = cursor.getString(UIProvider.ATTACHMENT_CONTENT_TYPE_COLUMN); |
| state = cursor.getInt(UIProvider.ATTACHMENT_STATE_COLUMN); |
| destination = cursor.getInt(UIProvider.ATTACHMENT_DESTINATION_COLUMN); |
| downloadedSize = cursor.getInt(UIProvider.ATTACHMENT_DOWNLOADED_SIZE_COLUMN); |
| contentUri = parseOptionalUri(cursor.getString(UIProvider.ATTACHMENT_CONTENT_URI_COLUMN)); |
| thumbnailUri = parseOptionalUri( |
| cursor.getString(UIProvider.ATTACHMENT_THUMBNAIL_URI_COLUMN)); |
| previewIntent = getOptionalIntentFromBlob( |
| cursor.getBlob(UIProvider.ATTACHMENT_PREVIEW_INTENT_COLUMN)); |
| |
| // TODO: ensure that local files attached to a draft have sane values, like SAVED/EXTERNAL |
| // and that contentUri is populated |
| } |
| |
| @Deprecated |
| public Attachment(String attachmentString) { |
| String[] attachmentValues = TextUtils.split(attachmentString, "\\|"); |
| if (attachmentValues != null) { |
| partId = attachmentValues[0]; |
| name = attachmentValues[1]; |
| contentType = attachmentValues[2]; |
| try { |
| size = Integer.parseInt(attachmentValues[3]); |
| } catch (NumberFormatException e) { |
| size = 0; |
| } |
| contentType = attachmentValues[4]; |
| origin = Integer.parseInt(attachmentValues[5]); |
| contentUri = parseOptionalUri(attachmentValues[6]); |
| originExtras = attachmentValues[7]; |
| } |
| } |
| |
| @Deprecated |
| public String toJoinedString() { |
| // FIXME: contentType is read/written twice |
| return TextUtils.join("|", Lists.newArrayList(partId == null ? "" : partId, |
| name == null ? "" : name.replaceAll("[|\n]", ""), contentType, size, contentType, |
| contentUri != null ? LOCAL_FILE : SERVER_ATTACHMENT, contentUri, |
| originExtras == null ? "" : originExtras, "")); |
| } |
| |
| private static Intent getOptionalIntentFromBlob(byte[] blob) { |
| if (blob == null) { |
| return null; |
| } |
| final Parcel intentParcel = Parcel.obtain(); |
| intentParcel.unmarshall(blob, 0, blob.length); |
| final Intent intent = new Intent(); |
| intent.readFromParcel(intentParcel); |
| return intent; |
| } |
| |
| private static Uri parseOptionalUri(String uriString) { |
| return uriString == null ? null : Uri.parse(uriString); |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeString(name); |
| dest.writeInt(size); |
| dest.writeParcelable(uri, flags); |
| dest.writeString(contentType); |
| dest.writeInt(state); |
| dest.writeInt(destination); |
| dest.writeInt(downloadedSize); |
| dest.writeParcelable(contentUri, flags); |
| dest.writeParcelable(thumbnailUri, flags); |
| dest.writeParcelable(previewIntent, flags); |
| dest.writeString(partId); |
| dest.writeInt(origin); |
| dest.writeString(originExtras); |
| } |
| |
| public static final Creator<Attachment> CREATOR = new Creator<Attachment>() { |
| @Override |
| public Attachment createFromParcel(Parcel source) { |
| return new Attachment(source); |
| } |
| |
| @Override |
| public Attachment[] newArray(int size) { |
| return new Attachment[size]; |
| } |
| }; |
| |
| public boolean isImage() { |
| return !TextUtils.isEmpty(contentType) ? contentType.startsWith("image/") : false; |
| } |
| |
| public boolean isDownloading() { |
| return state == AttachmentState.DOWNLOADING; |
| } |
| |
| public boolean isPresentLocally() { |
| return state == AttachmentState.SAVED || origin == LOCAL_FILE; |
| } |
| |
| public boolean isSavedToExternal() { |
| return state == AttachmentState.SAVED && destination == AttachmentDestination.EXTERNAL; |
| } |
| |
| public boolean canSave() { |
| return origin != LOCAL_FILE && state != AttachmentState.DOWNLOADING && |
| !isSavedToExternal(); |
| } |
| |
| /** |
| * If this attachment is an image, returns a Uri pointing to the image that can be used as a |
| * thumbnail. If the provider supports dedicated thumbnails, it will be relatively small, but |
| * if not, the image may be arbitrarily large. Client code must handle this efficiently. For |
| * non-image attachments, this method will return null. This method may also return null if the |
| * attachment is not yet downloaded. |
| */ |
| public Uri getImageUri() { |
| if (!isImage()) { |
| return null; |
| } |
| |
| return (thumbnailUri != null) ? thumbnailUri : contentUri; |
| } |
| |
| // Methods to support JSON [de-]serialization of Attachment data |
| // TODO: add support for origin/originExtras (and possibly partId?) or fold those fields into |
| // other fields so Compose View can use JSON objects |
| |
| public JSONObject toJSON() throws JSONException { |
| return toJSON(name, size, uri, contentUri, contentType); |
| } |
| |
| public static JSONObject toJSON(String name, int size, Uri uri, Uri contentUri, |
| String contentType) throws JSONException { |
| final JSONObject result = new JSONObject(); |
| |
| result.putOpt(AttachmentColumns.NAME, name); |
| result.putOpt(AttachmentColumns.SIZE, size); |
| if (uri != null) { |
| result.putOpt(AttachmentColumns.URI, uri.toString()); |
| } |
| if (contentUri != null) { |
| result.putOpt(AttachmentColumns.CONTENT_URI, contentUri.toString()); |
| } |
| result.putOpt(AttachmentColumns.CONTENT_TYPE, contentType); |
| |
| return result; |
| } |
| |
| public Attachment(JSONObject srcJson) { |
| name = srcJson.optString(AttachmentColumns.NAME, null); |
| size = srcJson.optInt(AttachmentColumns.SIZE); |
| uri = parseOptionalUri(srcJson, AttachmentColumns.URI); |
| contentUri = parseOptionalUri(srcJson, AttachmentColumns.CONTENT_URI); |
| contentType = srcJson.optString(AttachmentColumns.CONTENT_TYPE, null); |
| } |
| |
| private static Uri parseOptionalUri(JSONObject srcJson, String key) { |
| final String uriStr = srcJson.optString(key, null); |
| return uriStr == null ? null : Uri.parse(uriStr); |
| } |
| |
| public static String toJSONArray(Collection<Attachment> attachments) { |
| final JSONArray result = new JSONArray(); |
| try { |
| for (Attachment attachment : attachments) { |
| result.put(attachment.toJSON()); |
| } |
| } catch (JSONException e) { |
| throw new IllegalArgumentException(e); |
| } |
| return result.toString(); |
| } |
| |
| public static List<Attachment> fromJSONArray(String jsonArrayStr) { |
| final List<Attachment> results = Lists.newArrayList(); |
| try { |
| final JSONArray arr = new JSONArray(jsonArrayStr); |
| |
| for (int i = 0; i < arr.length(); i++) { |
| results.add(new Attachment(arr.getJSONObject(i))); |
| } |
| |
| } catch (JSONException e) { |
| throw new IllegalArgumentException(e); |
| } |
| return results; |
| } |
| |
| } |