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
18package com.android.mail.browse;
19
20
21import android.app.DialogFragment;
22import android.app.Fragment;
23import android.app.FragmentManager;
24import android.app.FragmentTransaction;
25import android.content.ActivityNotFoundException;
26import android.content.ContentValues;
27import android.content.Context;
28import android.content.Intent;
29import android.net.Uri;
30import android.os.Handler;
31import android.os.Parcelable;
32
33import com.android.mail.providers.Attachment;
34import com.android.mail.providers.Message;
35import com.android.mail.providers.UIProvider;
36import com.android.mail.providers.UIProvider.AttachmentColumns;
37import com.android.mail.providers.UIProvider.AttachmentContentValueKeys;
38import com.android.mail.providers.UIProvider.AttachmentDestination;
39import com.android.mail.providers.UIProvider.AttachmentState;
40import com.android.mail.utils.LogTag;
41import com.android.mail.utils.LogUtils;
42import com.android.mail.utils.Utils;
43
44import java.util.ArrayList;
45
46public 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