1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.messaging.datamodel.data; 17 18 import android.net.Uri; 19 import android.os.Parcel; 20 import android.os.Parcelable; 21 import android.support.annotation.NonNull; 22 23 import com.android.messaging.util.Assert; 24 import com.android.messaging.util.ContentType; 25 import com.android.messaging.util.LogUtil; 26 import com.android.messaging.util.SafeAsyncTask; 27 import com.android.messaging.util.UriUtil; 28 29 /** 30 * Represents a "pending" message part that acts as a placeholder for the actual attachment being 31 * loaded. It handles the task to load and persist the attachment from a Uri to local scratch 32 * folder. This item is not persisted to the database. 33 */ 34 public class PendingAttachmentData extends MessagePartData { 35 /** The pending state. This is the initial state where we haven't started loading yet */ 36 public static final int STATE_PENDING = 0; 37 38 /** The state for when we are currently loading the attachment to the scratch space */ 39 public static final int STATE_LOADING = 1; 40 41 /** The attachment has been successfully loaded and no longer pending */ 42 public static final int STATE_LOADED = 2; 43 44 /** The attachment failed to load */ 45 public static final int STATE_FAILED = 3; 46 47 private static final int LOAD_MEDIA_TIME_LIMIT_MILLIS = 60 * 1000; // 60s 48 49 /** The current state of the pending attachment. Refer to the STATE_* states above */ 50 private int mCurrentState; 51 52 /** 53 * Create a new instance of PendingAttachmentData with an output Uri. 54 * @param sourceUri the source Uri of the attachment. The Uri maybe temporary or remote, 55 * so we need to persist it to local storage. 56 */ 57 protected PendingAttachmentData(final String caption, final String contentType, 58 @NonNull final Uri sourceUri, final int width, final int height, 59 final boolean onlySingleAttachment) { 60 super(caption, contentType, sourceUri, width, height, onlySingleAttachment); 61 mCurrentState = STATE_PENDING; 62 } 63 64 /** 65 * Creates a pending attachment data that is able to load from the given source uri and 66 * persist the media resource locally in the scratch folder. 67 */ 68 public static PendingAttachmentData createPendingAttachmentData(final String contentType, 69 final Uri sourceUri) { 70 return createPendingAttachmentData(null, contentType, sourceUri, UNSPECIFIED_SIZE, 71 UNSPECIFIED_SIZE); 72 } 73 74 public static PendingAttachmentData createPendingAttachmentData(final String caption, 75 final String contentType, final Uri sourceUri, final int width, final int height) { 76 Assert.isTrue(ContentType.isMediaType(contentType)); 77 return new PendingAttachmentData(caption, contentType, sourceUri, width, height, 78 false /*onlySingleAttachment*/); 79 } 80 81 public static PendingAttachmentData createPendingAttachmentData(final String caption, 82 final String contentType, final Uri sourceUri, final int width, final int height, 83 final boolean onlySingleAttachment) { 84 Assert.isTrue(ContentType.isMediaType(contentType)); 85 return new PendingAttachmentData(caption, contentType, sourceUri, width, height, 86 onlySingleAttachment); 87 } 88 89 public int getCurrentState() { 90 return mCurrentState; 91 } 92 93 public void loadAttachmentForDraft(final DraftMessageData draftMessageData, 94 final String bindingId) { 95 if (mCurrentState != STATE_PENDING) { 96 return; 97 } 98 mCurrentState = STATE_LOADING; 99 100 // Kick off a SafeAsyncTask to load the content of the media and persist it locally. 101 // Note: we need to persist the media locally even if it's not remote, because we 102 // want to be able to resend the media in case the message failed to send. 103 new SafeAsyncTask<Void, Void, MessagePartData>(LOAD_MEDIA_TIME_LIMIT_MILLIS, 104 true /* cancelExecutionOnTimeout */) { 105 @Override 106 protected MessagePartData doInBackgroundTimed(final Void... params) { 107 final Uri contentUri = getContentUri(); 108 final Uri persistedUri = UriUtil.persistContentToScratchSpace(contentUri); 109 if (persistedUri != null) { 110 return MessagePartData.createMediaMessagePart( 111 getText(), 112 getContentType(), 113 persistedUri, 114 getWidth(), 115 getHeight()); 116 } 117 return null; 118 } 119 120 @Override 121 protected void onCancelled() { 122 LogUtil.w(LogUtil.BUGLE_TAG, "Timeout while retrieving media"); 123 mCurrentState = STATE_FAILED; 124 if (draftMessageData.isBound(bindingId)) { 125 draftMessageData.removePendingAttachment(PendingAttachmentData.this); 126 } 127 } 128 129 @Override 130 protected void onPostExecute(final MessagePartData attachment) { 131 if (attachment != null) { 132 mCurrentState = STATE_LOADED; 133 if (draftMessageData.isBound(bindingId)) { 134 draftMessageData.updatePendingAttachment(attachment, 135 PendingAttachmentData.this); 136 } else { 137 // The draft message data is no longer bound, drop the loaded attachment. 138 attachment.destroyAsync(); 139 } 140 } else { 141 // Media load failed. We already logged in doInBackground() so don't need to 142 // do that again. 143 mCurrentState = STATE_FAILED; 144 if (draftMessageData.isBound(bindingId)) { 145 draftMessageData.onPendingAttachmentLoadFailed(PendingAttachmentData.this); 146 draftMessageData.removePendingAttachment(PendingAttachmentData.this); 147 } 148 } 149 } 150 }.executeOnThreadPool(); 151 } 152 153 protected PendingAttachmentData(final Parcel in) { 154 super(in); 155 mCurrentState = in.readInt(); 156 } 157 158 @Override 159 public void writeToParcel(final Parcel out, final int flags) { 160 super.writeToParcel(out, flags); 161 out.writeInt(mCurrentState); 162 } 163 164 public static final Parcelable.Creator<PendingAttachmentData> CREATOR 165 = new Parcelable.Creator<PendingAttachmentData>() { 166 @Override 167 public PendingAttachmentData createFromParcel(final Parcel in) { 168 return new PendingAttachmentData(in); 169 } 170 171 @Override 172 public PendingAttachmentData[] newArray(final int size) { 173 return new PendingAttachmentData[size]; 174 } 175 }; 176 } 177