Enabled actions in the photo viewer.

All four actions (save, save all, share, share all)
are working properly. However, other apps do not
implement receiving the ACTION_SEND and ACTION_SEND_MULTIPLE
so the share and share all actions are a bit janky.

Change-Id: Ifcd3ed36c6bfce4a023b54a61f77e8ae4fd8f3b8
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 39f41ef..7cc2e43 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -245,6 +245,8 @@
     <string name="menu_photo_share">Share</string>
     <!-- Photo view screen, button name. Share all attachments. [CHAR LIMIT=10] -->
     <string name="menu_photo_share_all">Share all</string>
+    <!-- Displayed in the action bar as a subtitle. Save in progress. [CHAR LIMIT=15] -->
+    <string name="saving">Saving&#8230;</string>
 
     <!-- Webview Context Menu Strings -->
     <!-- Title of dialog for choosing which activity to share a link with. [CHAR LIMIT=50]-->
diff --git a/src/com/android/mail/browse/AttachmentActionHandler.java b/src/com/android/mail/browse/AttachmentActionHandler.java
index 5fbea62..dbb679b 100644
--- a/src/com/android/mail/browse/AttachmentActionHandler.java
+++ b/src/com/android/mail/browse/AttachmentActionHandler.java
@@ -1,16 +1,23 @@
 package com.android.mail.browse;
 
 import android.app.ProgressDialog;
+import android.content.ActivityNotFoundException;
 import android.content.AsyncQueryHandler;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.Intent;
 import android.net.Uri;
+import android.os.Parcelable;
 
 import com.android.mail.R;
 import com.android.mail.providers.Attachment;
 import com.android.mail.providers.UIProvider.AttachmentColumns;
 import com.android.mail.providers.UIProvider.AttachmentState;
+import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.Utils;
+
+import java.util.ArrayList;
 
 public class AttachmentActionHandler implements DialogInterface.OnCancelListener,
         DialogInterface.OnDismissListener {
@@ -21,6 +28,8 @@
     private final AttachmentViewInterface mView;
     private final Context mContext;
 
+    private static final String LOG_TAG = new LogUtils().getLogTag();
+
     private class AttachmentCommandHandler extends AsyncQueryHandler {
 
         public AttachmentCommandHandler(Context context) {
@@ -122,7 +131,41 @@
             mView.updateProgress(showProgress);
         }
 
-        // Call update status for the view so that it can do some specific things.
-        mView.updateStatus();
+        // Call on update status for the view so that it can do some specific things.
+        mView.onUpdateStatus();
+    }
+
+    public void shareAttachment() {
+        Intent intent = new Intent(Intent.ACTION_SEND);
+        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+
+        final Uri uri = Utils.normalizeUri(mAttachment.contentUri);
+        intent.putExtra(Intent.EXTRA_STREAM, uri);
+        intent.setType(Utils.normalizeMimeType(mAttachment.contentType));
+
+        try {
+            mContext.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            // couldn't find activity for SEND intent
+            LogUtils.e(LOG_TAG, "Couldn't find Activity for intent", e);
+        }
+    }
+
+    public void shareAttachments(ArrayList<Parcelable> uris) {
+        Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
+        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+
+        intent.setType("image/*");
+        intent.putParcelableArrayListExtra(
+                Intent.EXTRA_STREAM, uris);
+
+        try {
+            mContext.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            // couldn't find activity for SEND_MULTIPLE intent
+            LogUtils.e(LOG_TAG, "Couldn't find Activity for intent", e);
+        }
     }
 }
diff --git a/src/com/android/mail/browse/AttachmentViewInterface.java b/src/com/android/mail/browse/AttachmentViewInterface.java
index 8e89fd7..5da8762 100644
--- a/src/com/android/mail/browse/AttachmentViewInterface.java
+++ b/src/com/android/mail/browse/AttachmentViewInterface.java
@@ -20,5 +20,5 @@
      * Allows the view to do some view-specific status updating.
      * Called in {@link AttachmentActionHandler#updateStatus}.
      */
-    public void updateStatus();
+    public void onUpdateStatus();
 }
diff --git a/src/com/android/mail/browse/MessageAttachmentBar.java b/src/com/android/mail/browse/MessageAttachmentBar.java
index 3d0cb75..094b6d6 100644
--- a/src/com/android/mail/browse/MessageAttachmentBar.java
+++ b/src/com/android/mail/browse/MessageAttachmentBar.java
@@ -253,7 +253,7 @@
         setButtonVisible(mOverflowButton, !isDownloading && (canSave || canPreview));
     }
 
-    public void updateStatus() {
+    public void onUpdateStatus() {
         if (mAttachment.state == AttachmentState.FAILED) {
             mSubTitle.setText(getResources().getString(R.string.download_failed));
         } else {
diff --git a/src/com/android/mail/browse/MessageAttachmentTile.java b/src/com/android/mail/browse/MessageAttachmentTile.java
index 7c7826e..aaec720 100644
--- a/src/com/android/mail/browse/MessageAttachmentTile.java
+++ b/src/com/android/mail/browse/MessageAttachmentTile.java
@@ -259,6 +259,6 @@
     public void updateProgress(boolean showDeterminateProgress) {
     }
 
-    public void updateStatus() {
+    public void onUpdateStatus() {
     }
 }
diff --git a/src/com/android/mail/photo/MailPhotoViewActivity.java b/src/com/android/mail/photo/MailPhotoViewActivity.java
index 594de51..588c807 100644
--- a/src/com/android/mail/photo/MailPhotoViewActivity.java
+++ b/src/com/android/mail/photo/MailPhotoViewActivity.java
@@ -2,24 +2,70 @@
 
 import android.app.ActionBar;
 import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Parcelable;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 
 import com.android.mail.R;
-import com.android.mail.providers.UIProvider.AttachmentColumns;
+import com.android.mail.browse.AttachmentActionHandler;
+import com.android.mail.providers.Attachment;
+import com.android.mail.providers.UIProvider.AttachmentDestination;
 import com.android.mail.utils.AttachmentUtils;
+import com.android.mail.utils.Utils;
 
+import java.util.ArrayList;
+
+/**
+ * Derives from {@link PhotoViewActivity} to allow customization
+ * to the {@link ActionBar} from the default implementation.
+ */
 public class MailPhotoViewActivity extends PhotoViewActivity {
+    private MenuItem mSaveItem;
+    private AttachmentActionHandler mActionHandler;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mActionHandler = new AttachmentActionHandler(this, null);
+    }
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         MenuInflater inflater = getMenuInflater();
+
         inflater.inflate(R.menu.photo_view_menu, menu);
 
+        mSaveItem = menu.findItem(R.id.menu_save);
+
+        // Turn off the functionality that only works on JellyBean.
+        menu.findItem(R.id.menu_share)
+                .setVisible(Utils.isRunningJellybeanOrLater());
+        menu.findItem(R.id.menu_share_all)
+                .setVisible(Utils.isRunningJellybeanOrLater());
+
+        updateActionItems();
+
         return true;
     }
 
+    /**
+     * Updates the action items to tweak their visibility in case
+     * there is functionality thati is not relevant (eg, the Save
+     * button should not appear if the photo has already been saved).
+     */
+    private void updateActionItems() {
+        final Attachment attachment = getCurrentAttachment();
+        if (attachment != null) {
+            final boolean isDownloading = attachment.isDownloading();
+            final boolean isSavedToExternal = attachment.isSavedToExternal();
+            final boolean canSave = attachment.canSave();
+            mSaveItem.setVisible(!isDownloading && canSave && !isSavedToExternal);
+        }
+    }
+
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
@@ -27,17 +73,17 @@
                 // app icon in action bar clicked; go back to conversation
                 finish();
                 return true;
-            case R.id.menu_save:
-                // do stuff
+            case R.id.menu_save: // save the current photo
+                saveAttachment();
                 return true;
-            case R.id.menu_save_all:
-                // do stuff
+            case R.id.menu_save_all: // save all of the photos
+                saveAllAttachments();
                 return true;
-            case R.id.menu_share:
-                // do stuff
+            case R.id.menu_share: // share the current photo
+                shareAttachment();
                 return true;
-            case R.id.menu_share_all:
-                // do stuff
+            case R.id.menu_share_all: // share all of the photos
+                shareAllAttachments();
                 return true;
             default:
                 return super.onOptionsItemSelected(item);
@@ -48,20 +94,111 @@
      * Adjusts the activity title and subtitle to reflect the image name and size.
      */
     @Override
-    protected void updateTitleAndSubtitle() {
-        super.updateTitleAndSubtitle();
+    protected void updateActionBar() {
+        super.updateActionBar();
 
+        final Attachment attachment = getCurrentAttachment();
+        final boolean isDownloading = attachment.isDownloading();
+        final boolean isSavedToExternal = attachment.isSavedToExternal();
+
+        final ActionBar actionBar = getActionBar();
+        String subtitle =
+                AttachmentUtils.convertToHumanReadableSize(this, attachment.size);
+
+        if (isSavedToExternal) {
+            subtitle = getResources().getString(R.string.saved) + " " + subtitle;
+            actionBar.setSubtitle(subtitle);
+        } else if (isDownloading) {
+            actionBar.setSubtitle(R.string.saving);
+        } else {
+            actionBar.setSubtitle(subtitle);
+        }
+
+        updateActionItems();
+    }
+
+    /**
+     * Save the current attachment.
+     */
+    private void saveAttachment() {
+        saveAttachment(getCurrentAttachment());
+    }
+
+    /**
+     * Saves the attachment.
+     * @param attachment the attachment to save.
+     */
+    private void saveAttachment(final Attachment attachment) {
+        if (attachment != null && attachment.canSave()) {
+            mActionHandler.setAttachment(attachment);
+            mActionHandler.startDownloadingAttachment(AttachmentDestination.EXTERNAL);
+        }
+    }
+
+    /**
+     * Save all of the attachments in the cursor.
+     */
+    private void saveAllAttachments() {
         Cursor cursor = getCursorAtProperPosition();
 
         if (cursor == null) {
             return;
         }
 
-        final String subtitle = AttachmentUtils.convertToHumanReadableSize(this,
-                cursor.getInt(cursor.getColumnIndex(AttachmentColumns.SIZE)));
-        final ActionBar actionBar = getActionBar();
-
-        actionBar.setSubtitle(subtitle);
-
+        int i = -1;
+        while (cursor.moveToPosition(++i)) {
+            saveAttachment(new Attachment(cursor));
+        }
     }
-}
+
+    /**
+     * Share the current attachment.
+     */
+    private void shareAttachment() {
+        shareAttachment(getCurrentAttachment());
+    }
+
+    /**
+     * Shares the attachment
+     * @param attachment the attachment to share
+     */
+    private void shareAttachment(final Attachment attachment) {
+        if (attachment != null) {
+            mActionHandler.setAttachment(attachment);
+            mActionHandler.shareAttachment();
+        }
+    }
+
+    /**
+     * Share all of the attachments in the cursor.
+     */
+    private void shareAllAttachments() {
+        Cursor cursor = getCursorAtProperPosition();
+
+        if (cursor == null) {
+            return;
+        }
+
+        ArrayList<Parcelable> uris = new ArrayList<Parcelable>();
+        int i = -1;
+        while (cursor.moveToPosition(++i)) {
+            uris.add(Utils.normalizeUri(new Attachment(cursor).contentUri));
+        }
+
+        mActionHandler.shareAttachments(uris);
+    }
+
+    /**
+     * Helper method to get the currently visible attachment.
+     * @return
+     */
+    private Attachment getCurrentAttachment() {
+        final Cursor cursor = getCursorAtProperPosition();
+
+        if (cursor == null) {
+            return null;
+        }
+
+        return new Attachment(cursor);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/mail/photo/PhotoViewActivity.java b/src/com/android/mail/photo/PhotoViewActivity.java
index 86f601c..526aa82 100644
--- a/src/com/android/mail/photo/PhotoViewActivity.java
+++ b/src/com/android/mail/photo/PhotoViewActivity.java
@@ -430,7 +430,7 @@
                         mAdapter.swapCursor(data);
                         updateView(mRootView);
                         mViewPager.setCurrentItem(itemIndex, false);
-                        updateTitleAndSubtitle();
+                        updateActionBar();
                     }
                 });
             }
@@ -448,7 +448,7 @@
     @Override
     public void onPageSelected(int position) {
         setViewActivated();
-        updateTitleAndSubtitle();
+        updateActionBar();
         mPhotoIndex = position;
     }
 
@@ -592,7 +592,7 @@
     /**
      * Adjusts the activity title and subtitle to reflect the photo name and count.
      */
-    protected void updateTitleAndSubtitle() {
+    protected void updateActionBar() {
         final int position = mViewPager.getCurrentItem() + 1;
         final String subtitle;
         final boolean hasAlbumCount = mAlbumCount >= 0;
diff --git a/src/com/android/mail/photo/loaders/PhotoCursorLoader.java b/src/com/android/mail/photo/loaders/PhotoCursorLoader.java
index e19a490..f7567ce 100644
--- a/src/com/android/mail/photo/loaders/PhotoCursorLoader.java
+++ b/src/com/android/mail/photo/loaders/PhotoCursorLoader.java
@@ -93,44 +93,7 @@
             setSortOrder((sortOrder != null ? sortOrder : "") + " LIMIT 0, " + loadLimit);
         }
 
-        Cursor returnCursor = super.esLoadInBackground();
-
-        // commenting out the network request stuff
-//        int cursorCount = (returnCursor != null) ? returnCursor.getCount() : 0;
-//        boolean cursorFull = cursorCount == loadLimit;
-//        mHasMore = mPageable && (cursorFull /*|| isLoadingCirclePhotos()*/);
-//        mIsLoadingMore = (loadLimit != mLoadLimit);
-//
-//        // Either the database is empty or we only have a partial response; load more
-//        if (cursorCount == 0 || (!cursorFull && mHasMore)) {
-//            returnCursor.close();
-//            returnCursor = null;
-//        }
-//
-//        // If we don't have data to return, make network fetch and re-query
-//        if (returnCursor == null) {
-//            // adjust the loading offset
-//            mCircleOffset = cursorCount;
-//
-//            // issue network fetch
-//            doNetworkRequest();
-//
-//            // re-run the query
-//            returnCursor = super.esLoadInBackground();
-//
-//            cursorCount = (returnCursor != null) ? returnCursor.getCount() : 0;
-//            cursorFull = cursorCount == loadLimit;
-//            // If we didn't download anything new, disable paging
-//            mPageable = cursorCount != mCircleOffset;
-//            mHasMore = mPageable && (cursorFull /*|| isLoadingCirclePhotos()*/);
-//        }
-//
-//        // If we changed the sort order of the query, revert it
-//        if (changeSortOrder) {
-//            setSortOrder(origSortOrder);
-//        }
-
-        return returnCursor;
+        return super.esLoadInBackground();
     }
 
     @Override