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