1 /*
2  * Copyright (C) 2014 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.photo;
19 
20 import android.app.FragmentManager;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.database.Cursor;
24 import android.os.Bundle;
25 import android.os.Parcelable;
26 import android.support.v4.print.PrintHelper;
27 import android.view.Menu;
28 import android.view.MenuInflater;
29 import android.view.MenuItem;
30 import android.view.View;
31 import android.widget.ImageView;
32 import android.widget.TextView;
33 
34 import com.android.ex.photo.ActionBarInterface;
35 import com.android.ex.photo.PhotoViewController;
36 import com.android.ex.photo.fragments.PhotoViewFragment;
37 import com.android.ex.photo.views.ProgressBarWrapper;
38 import com.android.mail.R;
39 import com.android.mail.analytics.Analytics;
40 import com.android.mail.browse.AttachmentActionHandler;
41 import com.android.mail.print.PrintUtils;
42 import com.android.mail.providers.Attachment;
43 import com.android.mail.providers.Message;
44 import com.android.mail.providers.UIProvider;
45 import com.android.mail.utils.AttachmentUtils;
46 import com.android.mail.utils.LogTag;
47 import com.android.mail.utils.LogUtils;
48 import com.android.mail.utils.Utils;
49 import com.google.common.collect.Lists;
50 
51 import java.io.FileNotFoundException;
52 import java.util.ArrayList;
53 import java.util.List;
54 
55 /**
56  * Derives from {@link PhotoViewController} to customize behavior
57  * for UnifiedEmail's implementation of the photoviewer.
58  * All of the work is actually performed here.
59  */
60 public class MailPhotoViewController extends PhotoViewController {
61 
62     public interface ActivityInterface extends PhotoViewController.ActivityInterface {
getFragmentManager()63         FragmentManager getFragmentManager();
getMenuInflater()64         MenuInflater getMenuInflater();
65     }
66 
67     private final ActivityInterface mMailActivity;
68 
69     private static final String LOG_TAG = LogTag.getLogTag();
70 
71     private String mAccountType;
72     private MenuItem mSaveItem;
73     private MenuItem mSaveAllItem;
74     private MenuItem mShareItem;
75     private MenuItem mShareAllItem;
76     private MenuItem mPrintItem;
77     /**
78      * Only for attachments that are currently downloading. Attachments that failed show the
79      * retry button.
80      */
81     private MenuItem mDownloadAgainItem;
82     private MenuItem mExtraOption1Item;
83     protected AttachmentActionHandler mActionHandler;
84     private Menu mMenu;
85 
86     private boolean mHideExtraOptionOne;
87 
MailPhotoViewController(ActivityInterface activity)88     public MailPhotoViewController(ActivityInterface activity) {
89         super(activity);
90         mMailActivity = activity;
91     }
92 
93     @Override
onCreate(Bundle savedInstanceState)94     public void onCreate(Bundle savedInstanceState) {
95         super.onCreate(savedInstanceState);
96 
97         mActionHandler = new AttachmentActionHandler(mMailActivity.getContext(), null);
98         mActionHandler.initialize(mMailActivity.getFragmentManager());
99 
100         final Intent intent = mMailActivity.getIntent();
101         mAccountType = intent.getStringExtra(MailPhotoViewActivity.EXTRA_ACCOUNT_TYPE);
102         final String account = intent.getStringExtra(MailPhotoViewActivity.EXTRA_ACCOUNT);
103         final Message msg = intent.getParcelableExtra(MailPhotoViewActivity.EXTRA_MESSAGE);
104         mHideExtraOptionOne = intent.getBooleanExtra(
105                 MailPhotoViewActivity.EXTRA_HIDE_EXTRA_OPTION_ONE, false);
106         mActionHandler.setAccount(account);
107         mActionHandler.setMessage(msg);
108     }
109 
110     @Override
onCreateOptionsMenu(Menu menu)111     public boolean onCreateOptionsMenu(Menu menu) {
112         MenuInflater inflater = mMailActivity.getMenuInflater();
113 
114         inflater.inflate(R.menu.photo_view_menu, menu);
115         mMenu = menu;
116 
117         mSaveItem = mMenu.findItem(R.id.menu_save);
118         mSaveAllItem = mMenu.findItem(R.id.menu_save_all);
119         mShareItem = mMenu.findItem(R.id.menu_share);
120         mShareAllItem = mMenu.findItem(R.id.menu_share_all);
121         mPrintItem = mMenu.findItem(R.id.menu_print);
122         mDownloadAgainItem = mMenu.findItem(R.id.menu_download_again);
123         mExtraOption1Item = mMenu.findItem(R.id.attachment_extra_option1);
124 
125         return true;
126     }
127 
128     @Override
onPrepareOptionsMenu(Menu menu)129     public boolean onPrepareOptionsMenu(Menu menu) {
130         updateActionItems();
131         return true;
132     }
133 
134     @Override
onOptionsItemSelected(MenuItem item)135     public boolean onOptionsItemSelected(MenuItem item) {
136         final int itemId = item.getItemId();
137 
138         Analytics.getInstance().sendMenuItemEvent(Analytics.EVENT_CATEGORY_MENU_ITEM, itemId,
139                 "photo_viewer", 0);
140 
141         if (itemId == android.R.id.home) {
142             // app icon in action bar clicked; go back to conversation
143             mMailActivity.finish();
144         } else if (itemId == R.id.menu_save) { // save the current photo
145             saveAttachment();
146         } else if (itemId == R.id.menu_save_all) { // save all of the photos
147             saveAllAttachments();
148         } else if (itemId == R.id.menu_share) { // share the current photo
149             shareAttachment();
150         } else if (itemId == R.id.menu_share_all) { // share all of the photos
151             shareAllAttachments();
152         } else if (itemId == R.id.menu_print) { // print the current photo
153             printAttachment();
154         } else if (itemId == R.id.menu_download_again) { // redownload the current photo
155             redownloadAttachment();
156         } else if (itemId == R.id.attachment_extra_option1) {
157             mActionHandler.setAttachment(getCurrentAttachment());
158             mActionHandler.handleOption1();
159         } else {
160             return super.onOptionsItemSelected(item);
161         }
162         return true;
163     }
164 
165     /**
166      * Updates the action items to tweak their visibility in case
167      * there is functionality that is not relevant (eg, the Save
168      * button should not appear if the photo has already been saved).
169      */
170     @Override
updateActionItems()171     public void updateActionItems() {
172         final Attachment attachment = getCurrentAttachment();
173 
174         if (attachment != null && mSaveItem != null && mShareItem != null) {
175             mSaveItem.setEnabled(!attachment.isDownloading()
176                     && attachment.canSave() && !attachment.isSavedToExternal());
177             final boolean canShare = attachment.canShare();
178             mShareItem.setEnabled(canShare);
179             mPrintItem.setEnabled(canShare);
180             mDownloadAgainItem.setEnabled(attachment.canSave() && attachment.isDownloading());
181             mExtraOption1Item.setVisible(!mHideExtraOptionOne &&
182                     mActionHandler.shouldShowExtraOption1(mAccountType,
183                             attachment.getContentType()));
184         } else {
185             if (mMenu != null) {
186                 mMenu.setGroupEnabled(R.id.photo_view_menu_group, false);
187             }
188             return;
189         }
190 
191         List<Attachment> attachments = getAllAttachments();
192         if (attachments != null) {
193             boolean enabled = false;
194             for (final Attachment a : attachments) {
195                 // If one attachment can be saved, enable save all
196                 if (!a.isDownloading() && a.canSave() && !a.isSavedToExternal()) {
197                     enabled = true;
198                     break;
199                 }
200             }
201             mSaveAllItem.setEnabled(enabled);
202 
203             // all attachments must be present to be able to share all
204             enabled = true;
205             for (final Attachment a : attachments) {
206                 if (!a.canShare()) {
207                     enabled = false;
208                     break;
209                 }
210             }
211             mShareAllItem.setEnabled(enabled);
212         }
213 
214         // Turn off functionality that only works on JellyBean.
215         if (!Utils.isRunningJellybeanOrLater()) {
216             mShareItem.setVisible(false);
217             mShareAllItem.setVisible(false);
218         }
219 
220         // Turn off functionality that only works on KitKat.
221         if (!Utils.isRunningKitkatOrLater()) {
222             mPrintItem.setVisible(false);
223         }
224     }
225 
226 
227     /**
228      * Adjusts the activity title and subtitle to reflect the image name and size.
229      */
230     @Override
updateActionBar()231     public void updateActionBar() {
232         super.updateActionBar();
233 
234         final Attachment attachment = getCurrentAttachment();
235         final ActionBarInterface actionBar = mMailActivity.getActionBarInterface();
236         final String size = AttachmentUtils.convertToHumanReadableSize(
237                 mMailActivity.getContext(), attachment.size);
238 
239         // update the status
240         // There are 3 states
241         //      1. Saved, Attachment Size
242         //      2. Saving...
243         //      3. Default, Attachment Size
244         if (attachment.isSavedToExternal()) {
245             actionBar.setSubtitle(mMailActivity.getResources().getString(R.string.saved, size));
246         } else if (attachment.isDownloading() &&
247                 attachment.destination == UIProvider.AttachmentDestination.EXTERNAL) {
248             actionBar.setSubtitle(mMailActivity.getResources().getString(R.string.saving));
249         } else {
250             actionBar.setSubtitle(size);
251         }
252         updateActionItems();
253     }
254 
255     @Override
onFragmentVisible(PhotoViewFragment fragment)256     public void onFragmentVisible(PhotoViewFragment fragment) {
257         super.onFragmentVisible(fragment);
258         final Attachment attachment = getCurrentAttachment();
259         if (attachment.state == UIProvider.AttachmentState.PAUSED) {
260             mActionHandler.setAttachment(attachment);
261             mActionHandler.startDownloadingAttachment(attachment.destination);
262         }
263     }
264 
265     @Override
onCursorChanged(PhotoViewFragment fragment, Cursor cursor)266     public void onCursorChanged(PhotoViewFragment fragment, Cursor cursor) {
267         super.onCursorChanged(fragment, cursor);
268         updateProgressAndEmptyViews(fragment, new Attachment(cursor));
269     }
270 
271     /**
272      * Updates the empty views of the fragment based upon the current
273      * state of the attachment.
274      * @param fragment the current fragment
275      */
updateProgressAndEmptyViews( final PhotoViewFragment fragment, final Attachment attachment)276     private void updateProgressAndEmptyViews(
277             final PhotoViewFragment fragment, final Attachment attachment) {
278         final ProgressBarWrapper progressBar = fragment.getPhotoProgressBar();
279         final TextView emptyText = fragment.getEmptyText();
280         final ImageView retryButton = fragment.getRetryButton();
281 
282         // update the progress
283         if (attachment.shouldShowProgress()) {
284             progressBar.setMax(attachment.size);
285             progressBar.setProgress(attachment.downloadedSize);
286             progressBar.setIndeterminate(false);
287         } else if (fragment.isProgressBarNeeded()) {
288             progressBar.setIndeterminate(true);
289         }
290 
291         // If the download failed, show the empty text and retry button
292         if (attachment.isDownloadFailed()) {
293             emptyText.setText(R.string.photo_load_failed);
294             emptyText.setVisibility(View.VISIBLE);
295             retryButton.setVisibility(View.VISIBLE);
296             retryButton.setOnClickListener(new View.OnClickListener() {
297                 @Override
298                 public void onClick(View view) {
299                     redownloadAttachment();
300                     emptyText.setVisibility(View.GONE);
301                     retryButton.setVisibility(View.GONE);
302                 }
303             });
304             progressBar.setVisibility(View.GONE);
305         }
306     }
307 
308     /**
309      * Save the current attachment.
310      */
saveAttachment()311     private void saveAttachment() {
312         saveAttachment(getCurrentAttachment());
313     }
314 
315     /**
316      * Redownloads the attachment.
317      */
redownloadAttachment()318     private void redownloadAttachment() {
319         final Attachment attachment = getCurrentAttachment();
320         if (attachment != null && attachment.canSave()) {
321             // REDOWNLOADING command is only for attachments that are finished or failed.
322             // For an attachment that is downloading (or paused in the DownloadManager), we need to
323             // cancel it first.
324             mActionHandler.setAttachment(attachment);
325             mActionHandler.cancelAttachment();
326             mActionHandler.startDownloadingAttachment(attachment.destination);
327         }
328     }
329 
330     /**
331      * Saves the attachment.
332      * @param attachment the attachment to save.
333      */
saveAttachment(final Attachment attachment)334     private void saveAttachment(final Attachment attachment) {
335         if (attachment != null && attachment.canSave()) {
336             mActionHandler.setAttachment(attachment);
337             mActionHandler.startDownloadingAttachment(UIProvider.AttachmentDestination.EXTERNAL);
338         }
339     }
340 
341     /**
342      * Save all of the attachments in the cursor.
343      */
saveAllAttachments()344     private void saveAllAttachments() {
345         Cursor cursor = getCursorAtProperPosition();
346 
347         if (cursor == null) {
348             return;
349         }
350 
351         int i = -1;
352         while (cursor.moveToPosition(++i)) {
353             saveAttachment(new Attachment(cursor));
354         }
355     }
356 
357     /**
358      * Share the current attachment.
359      */
shareAttachment()360     private void shareAttachment() {
361         shareAttachment(getCurrentAttachment());
362     }
363 
364     /**
365      * Shares the attachment
366      * @param attachment the attachment to share
367      */
shareAttachment(final Attachment attachment)368     private void shareAttachment(final Attachment attachment) {
369         if (attachment != null) {
370             mActionHandler.setAttachment(attachment);
371             mActionHandler.shareAttachment();
372         }
373     }
374 
375     /**
376      * Share all of the attachments in the cursor.
377      */
shareAllAttachments()378     private void shareAllAttachments() {
379         Cursor cursor = getCursorAtProperPosition();
380 
381         if (cursor == null) {
382             return;
383         }
384 
385         ArrayList<Parcelable> uris = new ArrayList<Parcelable>();
386         int i = -1;
387         while (cursor.moveToPosition(++i)) {
388             uris.add(Utils.normalizeUri(new Attachment(cursor).contentUri));
389         }
390 
391         mActionHandler.shareAttachments(uris);
392     }
393 
printAttachment()394     private void printAttachment() {
395         final Attachment attachment = getCurrentAttachment();
396         final Context context = mMailActivity.getContext();
397         final PrintHelper printHelper = new PrintHelper(context);
398         try {
399             printHelper.setScaleMode(PrintHelper.SCALE_MODE_FIT);
400             printHelper.printBitmap(PrintUtils.buildPrintJobName(context, attachment.getName()),
401                     attachment.contentUri);
402         } catch (FileNotFoundException e) {
403             // couldn't print a photo at the particular Uri. Should we notify the user?
404             LogUtils.e(LOG_TAG, e, "Can't print photo");
405         }
406     }
407 
408     /**
409      * Helper method to get the currently visible attachment.
410      */
getCurrentAttachment()411     protected Attachment getCurrentAttachment() {
412         final Cursor cursor = getCursorAtProperPosition();
413 
414         if (cursor == null) {
415             return null;
416         }
417 
418         return new Attachment(cursor);
419     }
420 
getAllAttachments()421     private List<Attachment> getAllAttachments() {
422         final Cursor cursor = getCursor();
423 
424         if (cursor == null || cursor.isClosed() || !cursor.moveToFirst()) {
425             return null;
426         }
427 
428         List<Attachment> list = Lists.newArrayList();
429         do {
430             list.add(new Attachment(cursor));
431         } while (cursor.moveToNext());
432 
433         return list;
434     }
435 
getMailActivity()436     public ActivityInterface getMailActivity() {
437         return mMailActivity;
438     }
439 }
440