Non-image attachments now display as a list.
There are now two separate attachment areas in
the MessageFooterView. The AttachmentTileGrid
contains all images in the gridded tile look.
Additionally, there is a LinearLayout that contains
0 or more MessageAttachmentBars. These bars contain
a small icon, info about each attachment, and
an overflow button that contains additional options
other than the default for that attachment type.
In addition, there is now AttachmentActionHandler
that performs a lot of the interaction that both
attachment views share.
Change-Id: I60d297b7514e6ee35d23a9ec250833e886328d4d
diff --git a/res/layout/conversation_message_attachment.xml b/res/layout/conversation_message_attachment.xml
deleted file mode 100644
index e6a8ad6..0000000
--- a/res/layout/conversation_message_attachment.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2012 Google Inc.
- Licensed to The Android Open Source Project.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.mail.browse.MessageAttachmentTile
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/attachment_tile"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:orientation="vertical"
- android:divider="?android:attr/dividerHorizontal"
- android:showDividers="middle"
- android:background="@drawable/attachment_bg_holo">
-
- <RelativeLayout
- android:id="@+id/attachment_tile_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <ImageView
- android:id="@+id/attachment_tile_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop" />
-
- <ProgressBar
- android:id="@+id/attachment_progress"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- style="?android:attr/progressBarStyleHorizontal"
- android:indeterminate="false"
- android:visibility="gone" />
-
- <View
- android:id="@+id/attachment_tile_push_state"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackground" />
-
- </RelativeLayout>
-
-</com.android.mail.browse.MessageAttachmentTile>
diff --git a/res/layout/conversation_message_attachment_bar.xml b/res/layout/conversation_message_attachment_bar.xml
new file mode 100644
index 0000000..cda1ba9
--- /dev/null
+++ b/res/layout/conversation_message_attachment_bar.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2012 Google Inc.
+ Licensed to The Android Open Source Project.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.mail.browse.MessageAttachmentBar
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dip"
+ android:rowCount="3"
+ android:columnCount="3"
+ android:background="@drawable/attachment_bg_holo">
+
+ <ImageView
+ android:id="@+id/attachment_icon"
+ android:layout_width="48dip"
+ android:layout_height="48dip"
+ android:layout_column="0"
+ android:layout_row="0"
+ android:layout_rowSpan="3"
+ android:scaleType="center"
+ android:src="@drawable/ic_menu_attachment_holo_light"
+ android:background="#e5e5e5" />
+
+ <TextView
+ android:id="@+id/attachment_title"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_horizontal"
+ android:layout_marginLeft="8dip"
+ android:layout_column="1"
+ android:layout_row="0"
+ android:singleLine="true"
+ android:ellipsize="end" />
+
+ <TextView
+ android:id="@+id/attachment_subtitle"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_horizontal"
+ android:layout_marginLeft="8dip"
+ android:layout_column="1"
+ android:layout_row="1"
+ android:singleLine="true"
+ android:ellipsize="end" />
+
+ <ProgressBar
+ android:id="@+id/attachment_progress"
+ android:layout_height="0dip"
+ android:layout_gravity="fill"
+ android:layout_column="1"
+ android:layout_row="2"
+ android:layout_columnSpan="2"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:indeterminate="false"
+ android:visibility="invisible" />
+
+ <ImageView
+ android:id="@+id/overflow"
+ android:layout_width="48dip"
+ android:layout_height="match_parent"
+ android:layout_column="2"
+ android:layout_row="0"
+ android:layout_rowSpan="3"
+ android:visibility="gone"
+ style="@android:style/Widget.Holo.Light.ActionButton.Overflow" />
+
+ <ImageView
+ android:id="@+id/cancel_attachment"
+ android:src="@drawable/ic_cancel_holo_light"
+ android:layout_width="24dip"
+ android:layout_height="24dip"
+ android:layout_marginTop="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_marginBottom="12dip"
+ android:layout_marginLeft="12dip"
+ android:layout_column="2"
+ android:layout_row="0"
+ android:layout_rowSpan="3"
+ android:visibility="gone"/>
+
+</com.android.mail.browse.MessageAttachmentBar>
diff --git a/res/layout/conversation_message_attachment_tile.xml b/res/layout/conversation_message_attachment_tile.xml
new file mode 100644
index 0000000..051a038
--- /dev/null
+++ b/res/layout/conversation_message_attachment_tile.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2012 Google Inc.
+ Licensed to The Android Open Source Project.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.mail.browse.MessageAttachmentTile
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/attachment_tile"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/attachment_bg_holo">
+
+ <ImageView
+ android:id="@+id/attachment_tile_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop" />
+
+ <View
+ android:id="@+id/attachment_tile_push_state"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground" />
+
+</com.android.mail.browse.MessageAttachmentTile>
diff --git a/res/layout/conversation_message_footer.xml b/res/layout/conversation_message_footer.xml
index 6b7627a..7ceedc7 100644
--- a/res/layout/conversation_message_footer.xml
+++ b/res/layout/conversation_message_footer.xml
@@ -45,4 +45,12 @@
android:visibility="gone"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"/>
+ <LinearLayout
+ android:id="@+id/attachment_bar_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:orientation="vertical"/>
</com.android.mail.browse.MessageFooterView>
diff --git a/res/menu/message_footer_overflow_menu.xml b/res/menu/message_footer_overflow_menu.xml
new file mode 100644
index 0000000..9de486c
--- /dev/null
+++ b/res/menu/message_footer_overflow_menu.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2012 Google Inc.
+ Licensed to The Android Open Source Project.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/preview_attachment"
+ android:title="@string/preview_attachment" />
+ <item android:id="@+id/save_attachment"
+ android:title="@string/save_attachment" />
+</menu>
diff --git a/src/com/android/mail/browse/AttachmentActionHandler.java b/src/com/android/mail/browse/AttachmentActionHandler.java
new file mode 100644
index 0000000..5fbea62
--- /dev/null
+++ b/src/com/android/mail/browse/AttachmentActionHandler.java
@@ -0,0 +1,128 @@
+package com.android.mail.browse;
+
+import android.app.ProgressDialog;
+import android.content.AsyncQueryHandler;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.net.Uri;
+
+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;
+
+public class AttachmentActionHandler implements DialogInterface.OnCancelListener,
+ DialogInterface.OnDismissListener {
+ private ProgressDialog mViewProgressDialog;
+ private Attachment mAttachment;
+
+ private final AttachmentCommandHandler mCommandHandler;
+ private final AttachmentViewInterface mView;
+ private final Context mContext;
+
+ private class AttachmentCommandHandler extends AsyncQueryHandler {
+
+ public AttachmentCommandHandler(Context context) {
+ super(context.getContentResolver());
+ }
+
+ /**
+ * Asynchronously begin an update() on a ContentProvider.
+ *
+ */
+ public void sendCommand(Uri uri, ContentValues params) {
+ startUpdate(0, null, uri, params, null, null);
+ }
+
+ }
+
+ public AttachmentActionHandler(Context context, AttachmentViewInterface view) {
+ mCommandHandler = new AttachmentCommandHandler(context);
+ mView = view;
+ mContext = context;
+ }
+
+ public void setAttachment(Attachment attachment) {
+ mAttachment = attachment;
+ }
+
+ public void showAttachment(int destination) {
+ if (mAttachment.isPresentLocally()) {
+ mView.viewAttachment();
+ } else {
+ showDownloadingDialog();
+ startDownloadingAttachment(destination);
+ }
+ }
+
+ public void startDownloadingAttachment(int destination) {
+ final ContentValues params = new ContentValues(2);
+ params.put(AttachmentColumns.STATE, AttachmentState.DOWNLOADING);
+ params.put(AttachmentColumns.DESTINATION, destination);
+
+ mCommandHandler.sendCommand(mAttachment.uri, params);
+ }
+
+ public void cancelAttachment() {
+ final ContentValues params = new ContentValues(1);
+ params.put(AttachmentColumns.STATE, AttachmentState.NOT_SAVED);
+
+ mCommandHandler.sendCommand(mAttachment.uri, params);
+ }
+
+ /**
+ * Displays a loading dialog to be used for downloading attachments.
+ * Must be called on the UI thread.
+ */
+ private void showDownloadingDialog() {
+ mViewProgressDialog = new ProgressDialog(mContext);
+ mViewProgressDialog.setTitle(R.string.fetching_attachment);
+ mViewProgressDialog.setMessage(mContext.getResources().getString(R.string.please_wait));
+ mViewProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ mViewProgressDialog.setMax(mAttachment.size);
+ mViewProgressDialog.setOnDismissListener(this);
+ mViewProgressDialog.setOnCancelListener(this);
+ mViewProgressDialog.show();
+
+ // The progress number format needs to be set after the dialog is shown. See bug: 5149918
+ mViewProgressDialog.setProgressNumberFormat(null);
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mViewProgressDialog = null;
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ cancelAttachment();
+ }
+
+ /**
+ * Update progress-related views. Will also trigger a view intent if a progress dialog was
+ * previously brought up (by tapping 'View') and the download has now finished.
+ */
+ public void updateStatus() {
+ final boolean showProgress = mAttachment.size > 0 && mAttachment.downloadedSize > 0
+ && mAttachment.downloadedSize < mAttachment.size;
+
+ if (mViewProgressDialog != null && mViewProgressDialog.isShowing()) {
+ mViewProgressDialog.setProgress(mAttachment.downloadedSize);
+ mViewProgressDialog.setIndeterminate(!showProgress);
+
+ if (!mAttachment.isDownloading()) {
+ mViewProgressDialog.dismiss();
+ }
+
+ if (mAttachment.state == AttachmentState.SAVED) {
+ mView.viewAttachment();
+ }
+ } else {
+ mView.updateProgress(showProgress);
+ }
+
+ // Call update status for the view so that it can do some specific things.
+ mView.updateStatus();
+ }
+}
diff --git a/src/com/android/mail/browse/AttachmentViewInterface.java b/src/com/android/mail/browse/AttachmentViewInterface.java
new file mode 100644
index 0000000..8e89fd7
--- /dev/null
+++ b/src/com/android/mail/browse/AttachmentViewInterface.java
@@ -0,0 +1,24 @@
+package com.android.mail.browse;
+
+public interface AttachmentViewInterface {
+
+ /**
+ * View an attachment. The different attachment types handle this
+ * action differently and so each view handles it in their
+ * own manner.
+ */
+ public void viewAttachment();
+
+ /**
+ * Allows the view to know when it should update its progress.
+ * @param showProgress true if the the view should show a determinate
+ * progress value
+ */
+ public void updateProgress(boolean showDeterminateProgress);
+
+ /**
+ * Allows the view to do some view-specific status updating.
+ * Called in {@link AttachmentActionHandler#updateStatus}.
+ */
+ public void updateStatus();
+}
diff --git a/src/com/android/mail/browse/MessageAttachmentBar.java b/src/com/android/mail/browse/MessageAttachmentBar.java
new file mode 100644
index 0000000..ec7101b
--- /dev/null
+++ b/src/com/android/mail/browse/MessageAttachmentBar.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.browse;
+
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.GridLayout;
+import android.widget.ImageView;
+import android.widget.PopupMenu;
+import android.widget.PopupMenu.OnMenuItemClickListener;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.android.mail.R;
+import com.android.mail.providers.Attachment;
+import com.android.mail.providers.UIProvider.AttachmentDestination;
+import com.android.mail.providers.UIProvider.AttachmentState;
+import com.android.mail.utils.AttachmentUtils;
+import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.MimeType;
+import com.android.mail.utils.Utils;
+
+/**
+ * View for a single attachment in conversation view. Shows download status and allows launching
+ * intents to act on an attachment.
+ *
+ */
+public class MessageAttachmentBar extends GridLayout implements OnClickListener,
+ OnMenuItemClickListener, AttachmentViewInterface {
+
+ private Attachment mAttachment;
+ private TextView mTitle;
+ private TextView mSubTitle;
+ private String mAttachmentSizeText;
+ private String mDisplayType;
+ private ProgressBar mProgress;
+ private ImageView mCancelButton;
+ private PopupMenu mPopup;
+ private ImageView mOverflowButton;
+
+ private final AttachmentActionHandler mActionHandler;
+
+ private static final String LOG_TAG = new LogUtils().getLogTag();
+
+ public MessageAttachmentBar(Context context) {
+ this(context, null);
+ }
+
+ public MessageAttachmentBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mActionHandler = new AttachmentActionHandler(context, this);
+ }
+
+ public static MessageAttachmentBar inflate(LayoutInflater inflater, ViewGroup parent) {
+ MessageAttachmentBar view = (MessageAttachmentBar) inflater.inflate(
+ R.layout.conversation_message_attachment_bar, parent, false);
+ return view;
+ }
+
+ /**
+ * Render or update an attachment's view. This happens immediately upon instantiation, and
+ * repeatedly as status updates stream in, so only properties with new or changed values will
+ * cause sub-views to update.
+ *
+ */
+ public void render(Attachment attachment) {
+ final Attachment prevAttachment = mAttachment;
+ mAttachment = attachment;
+ mActionHandler.setAttachment(mAttachment);
+
+ LogUtils.d(LOG_TAG, "got attachment list row: name=%s state/dest=%d/%d dled=%d" +
+ " contentUri=%s MIME=%s", attachment.name, attachment.state,
+ attachment.destination, attachment.downloadedSize, attachment.contentUri,
+ attachment.contentType);
+
+ if (prevAttachment == null || TextUtils.equals(attachment.name, prevAttachment.name)) {
+ mTitle.setText(attachment.name);
+ }
+
+ if (prevAttachment == null || attachment.size != prevAttachment.size) {
+ mAttachmentSizeText = AttachmentUtils.convertToHumanReadableSize(getContext(),
+ attachment.size);
+ mDisplayType = AttachmentUtils.getDisplayType(getContext(), attachment);
+ updateSubtitleText(null);
+ }
+
+ mProgress.setMax(attachment.size);
+
+ updateActions();
+ mActionHandler.updateStatus();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mTitle = (TextView) findViewById(R.id.attachment_title);
+ mSubTitle = (TextView) findViewById(R.id.attachment_subtitle);
+ mProgress = (ProgressBar) findViewById(R.id.attachment_progress);
+ mOverflowButton = (ImageView) findViewById(R.id.overflow);
+ mCancelButton = (ImageView) findViewById(R.id.cancel_attachment);
+
+ setOnClickListener(this);
+ mOverflowButton.setOnClickListener(this);
+ mCancelButton.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ onClick(v.getId(), v);
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ mPopup.dismiss();
+ return onClick(item.getItemId(), null);
+ }
+
+ private boolean onClick(int res, View v) {
+ switch (res) {
+ case R.id.preview_attachment:
+ previewAttachment();
+ break;
+ case R.id.save_attachment:
+ if (mAttachment.canSave()) {
+ mActionHandler.startDownloadingAttachment(AttachmentDestination.EXTERNAL);
+ }
+ break;
+ case R.id.cancel_attachment:
+ mActionHandler.cancelAttachment();
+ break;
+ case R.id.overflow: {
+ final boolean canSave = mAttachment.canSave() && !mAttachment.isDownloading();
+ final boolean canPreview = (mAttachment.previewIntent != null);
+
+ // If no overflow items are visible, just bail out.
+ // We shouldn't be able to get here anyhow since the overflow
+ // button should be hidden.
+ if (!canSave && !canPreview) {
+ break;
+ }
+
+ if (mPopup == null) {
+ mPopup = new PopupMenu(getContext(), v);
+ mPopup.getMenuInflater().inflate(R.menu.message_footer_overflow_menu,
+ mPopup.getMenu());
+ mPopup.setOnMenuItemClickListener(this);
+ }
+
+ final Menu menu = mPopup.getMenu();
+ menu.findItem(R.id.preview_attachment).setVisible(canPreview);
+ menu.findItem(R.id.save_attachment).setVisible(canSave);
+
+ mPopup.show();
+ break;
+ }
+ default:
+ // Handles clicking the attachment
+ // in any area that is not the overflow
+ // button or cancel button or one of the
+ // overflow items.
+
+ // If we can install, install.
+ if (MimeType.isInstallable(mAttachment.contentType)) {
+ mActionHandler.showAttachment(AttachmentDestination.EXTERNAL);
+ }
+ // If we can view or play with an on-device app,
+ // view or play.
+ else if (MimeType.isViewable(getContext(), mAttachment.contentType)
+ || MimeType.isPlayable(mAttachment.contentType)) {
+ mActionHandler.showAttachment(AttachmentDestination.CACHE);
+ }
+ // If we can only preview the attachment, preview.
+ else if (mAttachment.previewIntent != null) {
+ previewAttachment();
+ }
+ // Otherwise, if we cannot do anything, show the info dialog.
+ else {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ int dialogMessage = MimeType.isBlocked(mAttachment.contentType)
+ ? R.string.attachment_type_blocked : R.string.no_application_found;
+ builder.setTitle(R.string.more_info_attachment)
+ .setMessage(dialogMessage)
+ .show();
+ }
+ break;
+ }
+
+ return true;
+ }
+
+ public void viewAttachment() {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ Utils.setIntentDataAndTypeAndNormalize(intent, mAttachment.contentUri,
+ mAttachment.contentType);
+ try {
+ getContext().startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ // couldn't find activity for View intent
+ LogUtils.e(LOG_TAG, "Coun't find Activity for intent", e);
+ }
+ }
+
+ private void previewAttachment() {
+ getContext().startActivity(mAttachment.previewIntent);
+ }
+
+ private void setButtonVisible(View button, boolean visible) {
+ button.setVisibility(visible ? VISIBLE : GONE);
+ }
+
+ /**
+ * Update all actions based on current downloading state.
+ */
+ private void updateActions() {
+ // To avoid visibility state transition bugs, every button's visibility should be touched
+ // once by this routine.
+
+ final boolean isDownloading = mAttachment.isDownloading();
+ final boolean canSave = mAttachment.canSave();
+ final boolean canPreview = (mAttachment.previewIntent != null);
+
+ setButtonVisible(mCancelButton, isDownloading);
+ setButtonVisible(mOverflowButton, !isDownloading && (canSave || canPreview));
+ }
+
+ public void updateStatus() {
+ if (mAttachment.state == AttachmentState.FAILED) {
+ mSubTitle.setText(getResources().getString(R.string.download_failed));
+ } else {
+ updateSubtitleText(mAttachment.isSavedToExternal() ?
+ getResources().getString(R.string.saved) : null);
+ }
+ }
+
+ public void updateProgress(boolean showProgress) {
+ if (mAttachment.isDownloading()) {
+ mProgress.setProgress(mAttachment.downloadedSize);
+ setProgressVisible(true);
+ mProgress.setIndeterminate(!showProgress);
+ } else {
+ setProgressVisible(false);
+ }
+ }
+
+ private void setProgressVisible(boolean visible) {
+ if (visible) {
+ mProgress.setVisibility(VISIBLE);
+ mSubTitle.setVisibility(INVISIBLE);
+ } else {
+ mProgress.setVisibility(INVISIBLE);
+ mSubTitle.setVisibility(VISIBLE);
+ }
+ }
+
+ private void updateSubtitleText(String prefix) {
+ // TODO: make this a formatted resource when we have a UX design.
+ // not worth translation right now.
+ StringBuilder sb = new StringBuilder();
+ if (prefix != null) {
+ sb.append(prefix);
+ }
+ sb.append(mAttachmentSizeText);
+ sb.append(' ');
+ sb.append(mDisplayType);
+ mSubTitle.setText(sb.toString());
+ }
+}
diff --git a/src/com/android/mail/browse/MessageAttachmentTile.java b/src/com/android/mail/browse/MessageAttachmentTile.java
index 5b55b64..56acf20 100644
--- a/src/com/android/mail/browse/MessageAttachmentTile.java
+++ b/src/com/android/mail/browse/MessageAttachmentTile.java
@@ -17,13 +17,8 @@
package com.android.mail.browse;
-import android.app.AlertDialog;
-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.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
@@ -37,20 +32,16 @@
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.PopupMenu.OnMenuItemClickListener;
-import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
import com.android.mail.R;
import com.android.mail.photo.Intents;
import com.android.mail.photo.Intents.PhotoViewIntentBuilder;
import com.android.mail.photo.util.ImageUtils;
import com.android.mail.providers.Attachment;
-import com.android.mail.providers.UIProvider.AttachmentColumns;
import com.android.mail.providers.UIProvider.AttachmentDestination;
-import com.android.mail.providers.UIProvider.AttachmentState;
import com.android.mail.utils.LogUtils;
-import com.android.mail.utils.MimeType;
import com.android.mail.utils.Utils;
import java.io.IOException;
@@ -60,39 +51,21 @@
* intents to act on an attachment.
*
*/
-public class MessageAttachmentTile extends LinearLayout implements OnClickListener,
- OnMenuItemClickListener, DialogInterface.OnCancelListener,
- DialogInterface.OnDismissListener {
+public class MessageAttachmentTile extends RelativeLayout implements OnClickListener,
+ OnMenuItemClickListener, AttachmentViewInterface {
private Attachment mAttachment;
private ImageView mIcon;
private ImageView.ScaleType mIconScaleType;
private int mPhotoIndex;
private Uri mAttachmentsListUri;
- private ProgressDialog mViewProgressDialog;
- private AttachmentCommandHandler mCommandHandler;
- private ProgressBar mProgress;
+
+ private final AttachmentActionHandler mActionHandler;
private ThumbnailLoadTask mThumbnailTask;
private static final String LOG_TAG = new LogUtils().getLogTag();
- private class AttachmentCommandHandler extends AsyncQueryHandler {
-
- public AttachmentCommandHandler() {
- super(getContext().getContentResolver());
- }
-
- /**
- * Asynchronously begin an update() on a ContentProvider.
- *
- */
- public void sendCommand(ContentValues params) {
- startUpdate(0, null, mAttachment.uri, params, null, null);
- }
-
- }
-
private class ThumbnailLoadTask extends AsyncTask<Uri, Void, Bitmap> {
private final int mWidth;
@@ -163,18 +136,18 @@
}
public MessageAttachmentTile(Context context) {
- super(context);
+ this(context, null);
}
public MessageAttachmentTile(Context context, AttributeSet attrs) {
super(context, attrs);
- mCommandHandler = new AttachmentCommandHandler();
+ mActionHandler = new AttachmentActionHandler(context, this);
}
public static MessageAttachmentTile inflate(LayoutInflater inflater, ViewGroup parent) {
MessageAttachmentTile view = (MessageAttachmentTile) inflater.inflate(
- R.layout.conversation_message_attachment, parent, false);
+ R.layout.conversation_message_attachment_tile, parent, false);
return view;
}
@@ -192,6 +165,7 @@
final Attachment prevAttachment = mAttachment;
mAttachment = attachment;
+ mActionHandler.setAttachment(mAttachment);
mAttachmentsListUri = attachmentsListUri;
mPhotoIndex = index;
@@ -217,11 +191,7 @@
setThumbnailToDefault();
}
- if (mProgress != null) {
- mProgress.setMax(attachment.size);
- }
-
- updateStatus();
+ mActionHandler.updateStatus();
}
private void setThumbnailToDefault() {
@@ -229,52 +199,13 @@
mIcon.setScaleType(ImageView.ScaleType.CENTER);
}
- /**
- * Update progress-related views. Will also trigger a view intent if a progress dialog was
- * previously brought up (by tapping 'View') and the download has now finished.
- */
- private void updateStatus() {
- final boolean showProgress = mAttachment.size > 0 && mAttachment.downloadedSize > 0
- && mAttachment.downloadedSize < mAttachment.size;
- if (mViewProgressDialog != null && mViewProgressDialog.isShowing()) {
- mViewProgressDialog.setProgress(mAttachment.downloadedSize);
- mViewProgressDialog.setIndeterminate(!showProgress);
-
- if (!mAttachment.isDownloading()) {
- mViewProgressDialog.dismiss();
- }
-
- if (mAttachment.state == AttachmentState.SAVED) {
- sendViewIntent();
- }
- } else {
-
- if (mAttachment.isDownloading()) {
- mProgress.setProgress(mAttachment.downloadedSize);
- setProgressVisible(true);
- mProgress.setIndeterminate(!showProgress);
- } else {
- setProgressVisible(false);
- }
-
- }
- }
-
- private void setProgressVisible(boolean visible) {
- if (visible) {
- mProgress.setVisibility(VISIBLE);
- } else {
- mProgress.setVisibility(GONE);
- }
- }
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mIcon = (ImageView) findViewById(R.id.attachment_tile_image);
- mProgress = (ProgressBar) findViewById(R.id.attachment_progress);
setOnClickListener(this);
@@ -292,66 +223,12 @@
}
private boolean onClick(int res, View v) {
- switch (res) {
- case R.id.preview_attachment:
- getContext().startActivity(mAttachment.previewIntent);
- break;
- case R.id.view_attachment:
- case R.id.play_attachment:
- case R.id.attachment_tile:
- showAttachment(AttachmentDestination.CACHE);
- break;
- case R.id.save_attachment:
- if (mAttachment.canSave()) {
- startDownloadingAttachment(AttachmentDestination.EXTERNAL);
- }
- break;
- case R.id.info_attachment:
- AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
- int dialogMessage = MimeType.isBlocked(mAttachment.contentType)
- ? R.string.attachment_type_blocked : R.string.no_application_found;
- builder.setTitle(R.string.more_info_attachment).setMessage(dialogMessage).show();
- break;
- case R.id.install_attachment:
- showAttachment(AttachmentDestination.EXTERNAL);
- break;
- case R.id.cancel_attachment:
- cancelAttachment();
- break;
- default:
- break;
- }
+ mActionHandler.showAttachment(AttachmentDestination.CACHE);
+
return true;
}
- private void showAttachment(int destination) {
- if (mAttachment.isPresentLocally()) {
- sendViewIntent();
- } else {
- showDownloadingDialog();
- startDownloadingAttachment(destination);
- }
- }
-
- private void startDownloadingAttachment(int destination) {
- final ContentValues params = new ContentValues(2);
- params.put(AttachmentColumns.STATE, AttachmentState.DOWNLOADING);
- params.put(AttachmentColumns.DESTINATION, destination);
-
- mCommandHandler.sendCommand(params);
- }
-
- private void cancelAttachment() {
- final ContentValues params = new ContentValues(1);
- params.put(AttachmentColumns.STATE, AttachmentState.NOT_SAVED);
-
- mCommandHandler.sendCommand(params);
- }
-
- /**
- * View an attachment by an application on device.
- */
- private void sendViewIntent() {
+ public void viewAttachment() {
if (ImageUtils.isImageMimeType(Utils.normalizeMimeType(mAttachment.contentType))) {
final PhotoViewIntentBuilder builder =
Intents.newPhotoViewActivityIntentBuilder(getContext());
@@ -376,32 +253,9 @@
}
}
- /**
- * Displays a loading dialog to be used for downloading attachments.
- * Must be called on the UI thread.
- */
- private void showDownloadingDialog() {
- mViewProgressDialog = new ProgressDialog(getContext());
- mViewProgressDialog.setTitle(R.string.fetching_attachment);
- mViewProgressDialog.setMessage(getResources().getString(R.string.please_wait));
- mViewProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mViewProgressDialog.setMax(mAttachment.size);
- mViewProgressDialog.setOnDismissListener(this);
- mViewProgressDialog.setOnCancelListener(this);
- mViewProgressDialog.show();
-
- // The progress number format needs to be set after the dialog is shown. See bug: 5149918
- mViewProgressDialog.setProgressNumberFormat(null);
+ public void updateProgress(boolean showDeterminateProgress) {
}
- @Override
- public void onDismiss(DialogInterface dialog) {
- mViewProgressDialog = null;
+ public void updateStatus() {
}
-
- @Override
- public void onCancel(DialogInterface dialog) {
- cancelAttachment();
- }
-
}
diff --git a/src/com/android/mail/browse/MessageFooterView.java b/src/com/android/mail/browse/MessageFooterView.java
index de9af60..4262c4c 100644
--- a/src/com/android/mail/browse/MessageFooterView.java
+++ b/src/com/android/mail/browse/MessageFooterView.java
@@ -23,6 +23,7 @@
import android.database.Cursor;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -31,11 +32,13 @@
import com.android.mail.browse.AttachmentLoader.AttachmentCursor;
import com.android.mail.browse.ConversationContainer.DetachListener;
import com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem;
+import com.android.mail.photo.util.ImageUtils;
import com.android.mail.providers.Attachment;
import com.android.mail.providers.Message;
import com.android.mail.utils.LogUtils;
import com.google.common.collect.Lists;
+import java.util.ArrayList;
import java.util.List;
public class MessageFooterView extends LinearLayout implements DetachListener,
@@ -47,6 +50,8 @@
private TextView mTitleText;
private View mTitleBar;
private AttachmentTileGrid mAttachmentGrid;
+ private LinearLayout mAttachmentBarList;
+ private final LayoutInflater mInflater;
/**
* An easy way for the conversation view to disable immediately kicking off attachment loaders
@@ -62,6 +67,8 @@
public MessageFooterView(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ mInflater = LayoutInflater.from(context);
}
/**
@@ -81,6 +88,7 @@
mTitleText = (TextView) findViewById(R.id.attachments_header_text);
mTitleBar = findViewById(R.id.attachments_header_bar);
mAttachmentGrid = (AttachmentTileGrid) findViewById(R.id.attachment_tile_grid);
+ mAttachmentBarList = (LinearLayout) findViewById(R.id.attachment_bar_list);
}
public void initialize(LoaderManager loaderManager) {
@@ -96,8 +104,11 @@
*/
mAttachmentGrid.removeAllViewsInLayout();
+ mAttachmentBarList.removeAllViewsInLayout();
mTitleText.setVisibility(View.GONE);
mTitleBar.setVisibility(View.GONE);
+ mAttachmentGrid.setVisibility(View.GONE);
+ mAttachmentBarList.setVisibility(View.GONE);
// kick off load of Attachment objects in background thread
final Integer attachmentLoaderId = getAttachmentLoaderId();
@@ -108,7 +119,8 @@
}
// Do an initial render if initLoader didn't already do one
- if (mAttachmentGrid.getChildCount() == 0) {
+ if (mAttachmentGrid.getChildCount() == 0 &&
+ mAttachmentBarList.getChildCount() == 0) {
renderAttachments();
}
setVisibility(mMessageHeaderItem.isExpanded() ? VISIBLE : GONE);
@@ -144,12 +156,53 @@
return;
}
+ // filter the attachments into tiled and non-tiled
+ final int maxSize = attachments.size();
+ List<Attachment> tiledAttachments = new ArrayList<Attachment>(maxSize);
+ List<Attachment> barAttachments = new ArrayList<Attachment>(maxSize);
+
+ for (Attachment attachment : attachments) {
+ if (isTiledAttachment(attachment)) {
+ tiledAttachments.add(attachment);
+ } else {
+ barAttachments.add(attachment);
+ }
+ }
+
mTitleText.setVisibility(View.VISIBLE);
mTitleBar.setVisibility(View.VISIBLE);
+
+ renderTiledAttachments(tiledAttachments);
+ renderBarAttachments(barAttachments);
+ }
+
+ private void renderTiledAttachments(List<Attachment> tiledAttachments) {
mAttachmentGrid.setVisibility(View.VISIBLE);
// Setup the tiles.
- mAttachmentGrid.configureGrid(mMessageHeaderItem.message.attachmentListUri, attachments);
+ mAttachmentGrid.configureGrid(
+ mMessageHeaderItem.message.attachmentListUri, tiledAttachments);
+ }
+
+ private void renderBarAttachments(List<Attachment> barAttachments) {
+ mAttachmentBarList.setVisibility(View.VISIBLE);
+
+ for (Attachment attachment : barAttachments) {
+ MessageAttachmentBar barAttachmentView =
+ (MessageAttachmentBar) mAttachmentBarList.findViewWithTag(attachment.uri);
+
+ if (barAttachmentView == null) {
+ barAttachmentView = MessageAttachmentBar.inflate(mInflater, this);
+ barAttachmentView.setTag(attachment.uri);
+ mAttachmentBarList.addView(barAttachmentView);
+ }
+
+ barAttachmentView.render(attachment);
+ }
+ }
+
+ private boolean isTiledAttachment(final Attachment attachment) {
+ return ImageUtils.isImageMimeType(attachment.contentType);
}
private Integer getAttachmentLoaderId() {