MessageAttachmentBar.java revision d49db8e3c4fff459f4ae3869ea80d083461eda2c
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 20import android.app.AlertDialog; 21import android.content.ActivityNotFoundException; 22import android.content.Context; 23import android.content.Intent; 24import android.text.TextUtils; 25import android.util.AttributeSet; 26import android.view.LayoutInflater; 27import android.view.Menu; 28import android.view.MenuItem; 29import android.view.View; 30import android.view.View.OnClickListener; 31import android.view.ViewGroup; 32import android.widget.GridLayout; 33import android.widget.ImageButton; 34import android.widget.ImageView; 35import android.widget.PopupMenu; 36import android.widget.PopupMenu.OnMenuItemClickListener; 37import android.widget.ProgressBar; 38import android.widget.TextView; 39 40import com.android.mail.R; 41import com.android.mail.providers.Attachment; 42import com.android.mail.providers.UIProvider.AttachmentDestination; 43import com.android.mail.providers.UIProvider.AttachmentState; 44import com.android.mail.utils.AttachmentUtils; 45import com.android.mail.utils.LogTag; 46import com.android.mail.utils.LogUtils; 47import com.android.mail.utils.MimeType; 48import com.android.mail.utils.Utils; 49 50import java.util.List; 51/** 52 * View for a single attachment in conversation view. Shows download status and allows launching 53 * intents to act on an attachment. 54 * 55 */ 56public class MessageAttachmentBar extends GridLayout implements OnClickListener, 57 OnMenuItemClickListener, AttachmentViewInterface { 58 59 private Attachment mAttachment; 60 private TextView mTitle; 61 private TextView mSubTitle; 62 private String mAttachmentSizeText; 63 private String mDisplayType; 64 private ProgressBar mProgress; 65 private ImageButton mCancelButton; 66 private PopupMenu mPopup; 67 private ImageView mOverflowButton; 68 69 private final AttachmentActionHandler mActionHandler; 70 private boolean mSaveClicked; 71 72 private static final String LOG_TAG = LogTag.getLogTag(); 73 74 public MessageAttachmentBar(Context context) { 75 this(context, null); 76 } 77 78 public MessageAttachmentBar(Context context, AttributeSet attrs) { 79 super(context, attrs); 80 81 mActionHandler = new AttachmentActionHandler(context, this); 82 } 83 84 public static MessageAttachmentBar inflate(LayoutInflater inflater, ViewGroup parent) { 85 MessageAttachmentBar view = (MessageAttachmentBar) inflater.inflate( 86 R.layout.conversation_message_attachment_bar, parent, false); 87 return view; 88 } 89 90 /** 91 * Render or update an attachment's view. This happens immediately upon instantiation, and 92 * repeatedly as status updates stream in, so only properties with new or changed values will 93 * cause sub-views to update. 94 * 95 */ 96 public void render(Attachment attachment) { 97 final Attachment prevAttachment = mAttachment; 98 mAttachment = attachment; 99 mActionHandler.setAttachment(mAttachment); 100 101 // reset mSaveClicked if we are not currently downloading 102 // So if the download fails or the download completes, we stop 103 // showing progress, etc 104 mSaveClicked = !attachment.isDownloading() ? false : mSaveClicked; 105 106 LogUtils.d(LOG_TAG, "got attachment list row: name=%s state/dest=%d/%d dled=%d" + 107 " contentUri=%s MIME=%s", attachment.name, attachment.state, 108 attachment.destination, attachment.downloadedSize, attachment.contentUri, 109 attachment.contentType); 110 111 if (prevAttachment == null || !TextUtils.equals(attachment.name, prevAttachment.name)) { 112 mTitle.setText(attachment.name); 113 } 114 115 if (prevAttachment == null || attachment.size != prevAttachment.size) { 116 mAttachmentSizeText = AttachmentUtils.convertToHumanReadableSize(getContext(), 117 attachment.size); 118 mDisplayType = AttachmentUtils.getDisplayType(getContext(), attachment); 119 updateSubtitleText(null); 120 } 121 122 updateActions(); 123 mActionHandler.updateStatus(); 124 } 125 126 @Override 127 protected void onFinishInflate() { 128 super.onFinishInflate(); 129 130 mTitle = (TextView) findViewById(R.id.attachment_title); 131 mSubTitle = (TextView) findViewById(R.id.attachment_subtitle); 132 mProgress = (ProgressBar) findViewById(R.id.attachment_progress); 133 mOverflowButton = (ImageView) findViewById(R.id.overflow); 134 mCancelButton = (ImageButton) findViewById(R.id.cancel_attachment); 135 136 setOnClickListener(this); 137 mOverflowButton.setOnClickListener(this); 138 mCancelButton.setOnClickListener(this); 139 } 140 141 @Override 142 public void onClick(View v) { 143 onClick(v.getId(), v); 144 } 145 146 @Override 147 public boolean onMenuItemClick(MenuItem item) { 148 mPopup.dismiss(); 149 return onClick(item.getItemId(), null); 150 } 151 152 private boolean onClick(int res, View v) { 153 switch (res) { 154 case R.id.preview_attachment: 155 previewAttachment(); 156 break; 157 case R.id.save_attachment: 158 if (mAttachment.canSave()) { 159 mActionHandler.startDownloadingAttachment(AttachmentDestination.EXTERNAL); 160 mSaveClicked = true; 161 } 162 break; 163 case R.id.cancel_attachment: 164 mActionHandler.cancelAttachment(); 165 mSaveClicked = false; 166 break; 167 case R.id.overflow: { 168 final boolean canSave = mAttachment.canSave() && !mAttachment.isDownloading(); 169 final boolean canPreview = (mAttachment.previewIntentUri != null); 170 171 // If no overflow items are visible, just bail out. 172 // We shouldn't be able to get here anyhow since the overflow 173 // button should be hidden. 174 if (!canSave && !canPreview) { 175 break; 176 } 177 178 if (mPopup == null) { 179 mPopup = new PopupMenu(getContext(), v); 180 mPopup.getMenuInflater().inflate(R.menu.message_footer_overflow_menu, 181 mPopup.getMenu()); 182 mPopup.setOnMenuItemClickListener(this); 183 } 184 185 final Menu menu = mPopup.getMenu(); 186 menu.findItem(R.id.preview_attachment).setVisible(canPreview); 187 menu.findItem(R.id.save_attachment).setVisible(canSave); 188 189 mPopup.show(); 190 break; 191 } 192 default: 193 // Handles clicking the attachment 194 // in any area that is not the overflow 195 // button or cancel button or one of the 196 // overflow items. 197 198 // If we can install, install. 199 if (MimeType.isInstallable(mAttachment.contentType)) { 200 mActionHandler.showAttachment(AttachmentDestination.EXTERNAL); 201 } 202 // If we can view or play with an on-device app, 203 // view or play. 204 else if (MimeType.isViewable(getContext(), mAttachment.contentUri, 205 mAttachment.contentType)) { 206 mActionHandler.showAttachment(AttachmentDestination.CACHE); 207 } 208 // If we can only preview the attachment, preview. 209 else if (mAttachment.previewIntentUri != null) { 210 previewAttachment(); 211 } 212 // Otherwise, if we cannot do anything, show the info dialog. 213 else { 214 AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); 215 int dialogMessage = MimeType.isBlocked(mAttachment.contentType) 216 ? R.string.attachment_type_blocked : R.string.no_application_found; 217 builder.setTitle(R.string.more_info_attachment) 218 .setMessage(dialogMessage) 219 .show(); 220 } 221 break; 222 } 223 224 return true; 225 } 226 227 public void viewAttachment() { 228 if (mAttachment.contentUri == null) { 229 LogUtils.e(LOG_TAG, "viewAttachment with null content uri"); 230 return; 231 } 232 233 Intent intent = new Intent(Intent.ACTION_VIEW); 234 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION 235 | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 236 Utils.setIntentDataAndTypeAndNormalize(intent, mAttachment.contentUri, 237 mAttachment.contentType); 238 try { 239 getContext().startActivity(intent); 240 } catch (ActivityNotFoundException e) { 241 // couldn't find activity for View intent 242 LogUtils.e(LOG_TAG, e, "Couldn't find Activity for intent"); 243 } 244 } 245 246 private void previewAttachment() { 247 if (mAttachment.previewIntentUri != null) { 248 final Intent previewIntent = 249 new Intent(Intent.ACTION_VIEW, mAttachment.previewIntentUri); 250 getContext().startActivity(previewIntent); 251 } 252 } 253 254 private void setButtonVisible(View button, boolean visible) { 255 button.setVisibility(visible ? VISIBLE : GONE); 256 } 257 258 /** 259 * Update all actions based on current downloading state. 260 */ 261 private void updateActions() { 262 // If the progress dialog is visible, skip any of the updating 263 if (mActionHandler.isProgressDialogVisible() || mActionHandler.dialogJustClosed()) { 264 return; 265 } 266 267 // To avoid visibility state transition bugs, every button's visibility should be touched 268 // once by this routine. 269 270 final boolean isDownloading = mAttachment.isDownloading(); 271 final boolean canSave = mAttachment.canSave() && 272 MimeType.isViewable(getContext(), 273 mAttachment.contentUri, mAttachment.contentType); 274 final boolean canPreview = mAttachment.previewIntentUri != null; 275 final boolean isInstallable = MimeType.isInstallable(mAttachment.contentType); 276 277 setButtonVisible(mCancelButton, isDownloading && mSaveClicked); 278 setButtonVisible(mOverflowButton, !isDownloading && !mSaveClicked 279 && !isInstallable && (canSave || canPreview)); 280 } 281 282 public void onUpdateStatus() { 283 if (mAttachment.state == AttachmentState.FAILED) { 284 mSubTitle.setText(getResources().getString(R.string.download_failed)); 285 } else { 286 updateSubtitleText(mAttachment.isSavedToExternal() ? 287 getResources().getString(R.string.saved) : null); 288 } 289 } 290 291 public void updateProgress(boolean showProgress) { 292 if (mAttachment.isDownloading() && mSaveClicked) { 293 mProgress.setMax(mAttachment.size); 294 mProgress.setProgress(mAttachment.downloadedSize); 295 mProgress.setIndeterminate(!showProgress); 296 mProgress.setVisibility(VISIBLE); 297 } else { 298 mProgress.setVisibility(INVISIBLE); 299 } 300 } 301 302 private void updateSubtitleText(String prefix) { 303 // TODO: make this a formatted resource when we have a UX design. 304 // not worth translation right now. 305 StringBuilder sb = new StringBuilder(); 306 if (prefix != null) { 307 sb.append(prefix); 308 } 309 sb.append(mAttachmentSizeText); 310 sb.append(' '); 311 sb.append(mDisplayType); 312 mSubTitle.setText(sb.toString()); 313 } 314 315 @Override 316 public List<Attachment> getAttachments() { 317 return null; 318 } 319} 320