1 /* 2 * Copyright (C) 2012 Google Inc. 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.mail.browse; 19 20 21 import android.app.DialogFragment; 22 import android.app.Fragment; 23 import android.app.FragmentManager; 24 import android.app.FragmentTransaction; 25 import android.content.ActivityNotFoundException; 26 import android.content.ContentValues; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.net.Uri; 30 import android.os.Handler; 31 import android.os.Parcelable; 32 33 import com.android.mail.providers.Attachment; 34 import com.android.mail.providers.Message; 35 import com.android.mail.providers.UIProvider; 36 import com.android.mail.providers.UIProvider.AttachmentColumns; 37 import com.android.mail.providers.UIProvider.AttachmentContentValueKeys; 38 import com.android.mail.providers.UIProvider.AttachmentDestination; 39 import com.android.mail.providers.UIProvider.AttachmentState; 40 import com.android.mail.utils.LogTag; 41 import com.android.mail.utils.LogUtils; 42 import com.android.mail.utils.Utils; 43 44 import java.util.ArrayList; 45 46 public class AttachmentActionHandler { 47 private static final String PROGRESS_FRAGMENT_TAG = "attachment-progress"; 48 49 private String mAccount; 50 private Message mMessage; 51 private Attachment mAttachment; 52 53 private final AttachmentCommandHandler mCommandHandler; 54 private final AttachmentViewInterface mView; 55 private final Context mContext; 56 private final Handler mHandler; 57 private FragmentManager mFragmentManager; 58 private boolean mViewOnFinish; 59 60 private static final String LOG_TAG = LogTag.getLogTag(); 61 62 private static OptionHandler sOptionHandler = new OptionHandler(); 63 64 public AttachmentActionHandler(Context context, AttachmentViewInterface view) { 65 mCommandHandler = new AttachmentCommandHandler(context); 66 mView = view; 67 mContext = context; 68 mHandler = new Handler(); 69 mViewOnFinish = true; 70 } 71 72 public void initialize(FragmentManager fragmentManager) { 73 mFragmentManager = fragmentManager; 74 } 75 76 public void setAccount(String account) { 77 mAccount = account; 78 } 79 80 public void setMessage(Message message) { 81 mMessage = message; 82 } 83 84 public void setAttachment(Attachment attachment) { 85 mAttachment = attachment; 86 } 87 88 public void setViewOnFinish(boolean viewOnFinish) { 89 mViewOnFinish = viewOnFinish; 90 } 91 92 public void showAttachment(int destination) { 93 if (mView == null) { 94 return; 95 } 96 97 // If the caller requested that this attachments be saved to the external storage, we should 98 // verify that the it was saved there. 99 if (mAttachment.isPresentLocally() && 100 (destination == AttachmentDestination.CACHE || 101 mAttachment.destination == destination)) { 102 mView.viewAttachment(); 103 } else { 104 showDownloadingDialog(); 105 startDownloadingAttachment(destination); 106 } 107 } 108 109 /** 110 * Start downloading the full size attachment set with 111 * {@link #setAttachment(Attachment)} immediately. 112 */ 113 public void startDownloadingAttachment(int destination) { 114 startDownloadingAttachment(destination, UIProvider.AttachmentRendition.BEST, 0, false); 115 } 116 117 public void startDownloadingAttachment( 118 int destination, int rendition, int additionalPriority, boolean delayDownload) { 119 startDownloadingAttachment( 120 mAttachment, destination, rendition, additionalPriority, delayDownload); 121 } 122 123 private void startDownloadingAttachment( 124 Attachment attachment, int destination, int rendition, int additionalPriority, 125 boolean delayDownload) { 126 final ContentValues params = new ContentValues(5); 127 params.put(AttachmentColumns.STATE, AttachmentState.DOWNLOADING); 128 params.put(AttachmentColumns.DESTINATION, destination); 129 params.put(AttachmentContentValueKeys.RENDITION, rendition); 130 params.put(AttachmentContentValueKeys.ADDITIONAL_PRIORITY, additionalPriority); 131 params.put(AttachmentContentValueKeys.DELAY_DOWNLOAD, delayDownload); 132 133 mCommandHandler.sendCommand(attachment.uri, params); 134 } 135 136 public void cancelAttachment() { 137 final ContentValues params = new ContentValues(1); 138 params.put(AttachmentColumns.STATE, AttachmentState.NOT_SAVED); 139 140 mCommandHandler.sendCommand(mAttachment.uri, params); 141 } 142 143 public void startRedownloadingAttachment(Attachment attachment) { 144 final ContentValues params = new ContentValues(2); 145 params.put(AttachmentColumns.STATE, AttachmentState.REDOWNLOADING); 146 params.put(AttachmentColumns.DESTINATION, attachment.destination); 147 148 mCommandHandler.sendCommand(attachment.uri, params); 149 } 150 151 /** 152 * Displays a loading dialog to be used for downloading attachments. 153 * Must be called on the UI thread. 154 */ 155 public void showDownloadingDialog() { 156 final FragmentTransaction ft = mFragmentManager.beginTransaction(); 157 final Fragment prev = mFragmentManager.findFragmentByTag(PROGRESS_FRAGMENT_TAG); 158 if (prev != null) { 159 ft.remove(prev); 160 } 161 ft.addToBackStack(null); 162 163 // Create and show the dialog. 164 final DialogFragment newFragment = AttachmentProgressDialogFragment.newInstance( 165 mAttachment); 166 newFragment.show(ft, PROGRESS_FRAGMENT_TAG); 167 } 168 169 /** 170 * Update progress-related views. Will also trigger a view intent if a progress dialog was 171 * previously brought up (by tapping 'View') and the download has now finished. 172 */ 173 public void updateStatus(boolean loaderResult) { 174 if (mView == null) { 175 return; 176 } 177 178 final boolean showProgress = mAttachment.shouldShowProgress(); 179 180 final AttachmentProgressDialogFragment dialog = (AttachmentProgressDialogFragment) 181 mFragmentManager.findFragmentByTag(PROGRESS_FRAGMENT_TAG); 182 if (dialog != null && dialog.isShowingDialogForAttachment(mAttachment)) { 183 dialog.setProgress(mAttachment.downloadedSize); 184 185 // We don't want the progress bar to switch back to indeterminate mode after 186 // have been in determinate progress mode. 187 final boolean indeterminate = !showProgress && dialog.isIndeterminate(); 188 dialog.setIndeterminate(indeterminate); 189 190 if (loaderResult && mAttachment.isDownloadFinishedOrFailed()) { 191 mHandler.post(new Runnable() { 192 @Override 193 public void run() { 194 dialog.dismiss(); 195 } 196 }); 197 } 198 199 if (mAttachment.state == AttachmentState.SAVED && mViewOnFinish) { 200 mView.viewAttachment(); 201 } 202 } else { 203 mView.updateProgress(showProgress); 204 } 205 206 // Call on update status for the view so that it can do some specific things. 207 mView.onUpdateStatus(); 208 } 209 210 public boolean isProgressDialogVisible() { 211 final Fragment dialog = mFragmentManager.findFragmentByTag(PROGRESS_FRAGMENT_TAG); 212 return dialog != null && dialog.isVisible(); 213 } 214 215 public void shareAttachment() { 216 if (mAttachment.contentUri == null) { 217 return; 218 } 219 220 Intent intent = new Intent(Intent.ACTION_SEND); 221 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION 222 | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 223 224 final Uri uri = Utils.normalizeUri(mAttachment.contentUri); 225 intent.putExtra(Intent.EXTRA_STREAM, uri); 226 intent.setType(Utils.normalizeMimeType(mAttachment.getContentType())); 227 228 try { 229 mContext.startActivity(intent); 230 } catch (ActivityNotFoundException e) { 231 // couldn't find activity for SEND intent 232 LogUtils.e(LOG_TAG, "Couldn't find Activity for intent", e); 233 } 234 } 235 236 public void shareAttachments(ArrayList<Parcelable> uris) { 237 Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE); 238 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION 239 | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 240 241 intent.setType("image/*"); 242 intent.putParcelableArrayListExtra( 243 Intent.EXTRA_STREAM, uris); 244 245 try { 246 mContext.startActivity(intent); 247 } catch (ActivityNotFoundException e) { 248 // couldn't find activity for SEND_MULTIPLE intent 249 LogUtils.e(LOG_TAG, "Couldn't find Activity for intent", e); 250 } 251 } 252 253 public static void setOptionHandler(OptionHandler handler) { 254 sOptionHandler = handler; 255 } 256 257 public boolean shouldShowExtraOption1(final String accountType, final String mimeType) { 258 return (sOptionHandler != null) && sOptionHandler.shouldShowExtraOption1( 259 accountType, mimeType); 260 } 261 262 public void handleOption1() { 263 if (sOptionHandler == null) { 264 return; 265 } 266 sOptionHandler.handleOption1(mContext, mAccount, mMessage, mAttachment, mFragmentManager); 267 } 268 269 /** 270 * A default, no-op option class. Override this and set it globally with 271 * {@link AttachmentActionHandler#setOptionHandler(OptionHandler)}.<br> 272 * <br> 273 * Subclasses of this type will live pretty much forever, so really, really try to avoid 274 * keeping any state as member variables in them. 275 */ 276 public static class OptionHandler { 277 278 public boolean shouldShowExtraOption1(String accountType, String mimeType) { 279 return false; 280 } 281 282 public void handleOption1(Context context, String account, Message message, 283 Attachment attachment, FragmentManager fm) { 284 // no-op 285 } 286 } 287 } 288