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 androidx.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