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 
AttachmentActionHandler(Context context, AttachmentViewInterface view)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 
initialize(FragmentManager fragmentManager)72     public void initialize(FragmentManager fragmentManager) {
73         mFragmentManager = fragmentManager;
74     }
75 
setAccount(String account)76     public void setAccount(String account) {
77         mAccount = account;
78     }
79 
setMessage(Message message)80     public void setMessage(Message message) {
81         mMessage = message;
82     }
83 
setAttachment(Attachment attachment)84     public void setAttachment(Attachment attachment) {
85         mAttachment = attachment;
86     }
87 
setViewOnFinish(boolean viewOnFinish)88     public void setViewOnFinish(boolean viewOnFinish) {
89         mViewOnFinish = viewOnFinish;
90     }
91 
showAttachment(int destination)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      */
startDownloadingAttachment(int destination)113     public void startDownloadingAttachment(int destination) {
114         startDownloadingAttachment(destination, UIProvider.AttachmentRendition.BEST, 0, false);
115     }
116 
startDownloadingAttachment( int destination, int rendition, int additionalPriority, boolean delayDownload)117     public void startDownloadingAttachment(
118             int destination, int rendition, int additionalPriority, boolean delayDownload) {
119         startDownloadingAttachment(
120                 mAttachment, destination, rendition, additionalPriority, delayDownload);
121     }
122 
startDownloadingAttachment( Attachment attachment, int destination, int rendition, int additionalPriority, boolean delayDownload)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 
cancelAttachment()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 
startRedownloadingAttachment(Attachment attachment)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      */
showDownloadingDialog()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      */
updateStatus(boolean loaderResult)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 
isProgressDialogVisible()210     public boolean isProgressDialogVisible() {
211         final Fragment dialog = mFragmentManager.findFragmentByTag(PROGRESS_FRAGMENT_TAG);
212         return dialog != null && dialog.isVisible();
213     }
214 
shareAttachment()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 
shareAttachments(ArrayList<Parcelable> uris)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 
setOptionHandler(OptionHandler handler)253     public static void setOptionHandler(OptionHandler handler) {
254         sOptionHandler = handler;
255     }
256 
shouldShowExtraOption1(final String accountType, final String mimeType)257     public boolean shouldShowExtraOption1(final String accountType, final String mimeType) {
258         return (sOptionHandler != null) && sOptionHandler.shouldShowExtraOption1(
259                 accountType, mimeType);
260     }
261 
handleOption1()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 
shouldShowExtraOption1(String accountType, String mimeType)278         public boolean shouldShowExtraOption1(String accountType, String mimeType) {
279             return false;
280         }
281 
handleOption1(Context context, String account, Message message, Attachment attachment, FragmentManager fm)282         public void handleOption1(Context context, String account, Message message,
283                 Attachment attachment, FragmentManager fm) {
284             // no-op
285         }
286     }
287 }
288