blob: aaec720bff38bfbe27f9398f3a084df2307f69fe [file] [log] [blame]
/*
* 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.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.PopupMenu.OnMenuItemClickListener;
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.MailPhotoViewActivity;
import com.android.mail.photo.util.ImageUtils;
import com.android.mail.providers.Attachment;
import com.android.mail.providers.UIProvider;
import com.android.mail.providers.UIProvider.AttachmentDestination;
import com.android.mail.utils.LogUtils;
import com.android.mail.utils.Utils;
import java.io.IOException;
/**
* View for a single attachment in conversation view. Shows download status and allows launching
* intents to act on an attachment.
*
*/
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 final AttachmentActionHandler mActionHandler;
private ThumbnailLoadTask mThumbnailTask;
private static final String LOG_TAG = new LogUtils().getLogTag();
private class ThumbnailLoadTask extends AsyncTask<Uri, Void, Bitmap> {
private final int mWidth;
private final int mHeight;
public ThumbnailLoadTask(int width, int height) {
mWidth = width;
mHeight = height;
}
@Override
protected Bitmap doInBackground(Uri... params) {
final Uri thumbnailUri = params[0];
AssetFileDescriptor fd = null;
Bitmap result = null;
try {
fd = getContext().getContentResolver().openAssetFileDescriptor(thumbnailUri, "r");
if (isCancelled() || fd == null) {
return null;
}
final BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor(), null, opts);
if (isCancelled() || opts.outWidth == -1 || opts.outHeight == -1) {
return null;
}
opts.inJustDecodeBounds = false;
LogUtils.d(LOG_TAG, "in background, src w/h=%d/%d dst w/h=%d/%d, divider=%d",
opts.outWidth, opts.outHeight, mWidth, mHeight, opts.inSampleSize);
result = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor(), null, opts);
} catch (Throwable t) {
LogUtils.e(LOG_TAG, t, "Unable to decode thumbnail %s", thumbnailUri);
} finally {
if (fd != null) {
try {
fd.close();
} catch (IOException e) {
LogUtils.e(LOG_TAG, e, "");
}
}
}
return result;
}
@Override
protected void onPostExecute(Bitmap result) {
if (result == null) {
LogUtils.d(LOG_TAG, "back in UI thread, decode failed");
setThumbnailToDefault();
return;
}
LogUtils.d(LOG_TAG, "back in UI thread, decode success, w/h=%d/%d", result.getWidth(),
result.getHeight());
mIcon.setImageBitmap(result);
mIcon.setScaleType(mIconScaleType);
}
}
public MessageAttachmentTile(Context context) {
this(context, null);
}
public MessageAttachmentTile(Context context, AttributeSet attrs) {
super(context, attrs);
mActionHandler = new AttachmentActionHandler(context, this);
}
public static MessageAttachmentTile inflate(LayoutInflater inflater, ViewGroup parent) {
MessageAttachmentTile view = (MessageAttachmentTile) inflater.inflate(
R.layout.conversation_message_attachment_tile, 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, Uri attachmentsListUri, int index) {
if (attachment == null) {
setVisibility(View.INVISIBLE);
return;
}
final Attachment prevAttachment = mAttachment;
mAttachment = attachment;
mActionHandler.setAttachment(mAttachment);
mAttachmentsListUri = attachmentsListUri;
mPhotoIndex = index;
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);
final Uri imageUri = attachment.getImageUri();
final Uri prevImageUri = (prevAttachment == null) ? null : prevAttachment.getImageUri();
// begin loading a thumbnail if this is an image and either the thumbnail or the original
// content is ready (and different from any existing image)
if (imageUri != null && (prevImageUri == null || !imageUri.equals(prevImageUri))) {
// cancel/dispose any existing task and start a new one
if (mThumbnailTask != null) {
mThumbnailTask.cancel(true);
}
mThumbnailTask = new ThumbnailLoadTask(mIcon.getWidth(), mIcon.getHeight());
mThumbnailTask.execute(imageUri);
} else if (imageUri == null) {
// not an image, or no thumbnail exists. fall back to default.
// async image load must separately ensure the default appears upon load failure.
setThumbnailToDefault();
}
mActionHandler.updateStatus();
}
private void setThumbnailToDefault() {
mIcon.setImageResource(R.drawable.ic_menu_attachment_holo_light);
mIcon.setScaleType(ImageView.ScaleType.CENTER);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mIcon = (ImageView) findViewById(R.id.attachment_tile_image);
setOnClickListener(this);
mIconScaleType = mIcon.getScaleType();
}
@Override
public void onClick(View v) {
onClick(v.getId(), v);
}
@Override
public boolean onMenuItemClick(MenuItem item) {
return onClick(item.getItemId(), null);
}
private boolean onClick(int res, View v) {
mActionHandler.showAttachment(AttachmentDestination.CACHE);
return true;
}
public void viewAttachment() {
if (ImageUtils.isImageMimeType(Utils.normalizeMimeType(mAttachment.contentType))) {
final PhotoViewIntentBuilder builder =
Intents.newPhotoViewIntentBuilder(getContext(), MailPhotoViewActivity.class);
builder.setPhotoName(mAttachment.name)
.setPhotosUri(mAttachmentsListUri.toString())
.setProjection(UIProvider.ATTACHMENT_PROJECTION)
.setPhotoIndex(mPhotoIndex);
getContext().startActivity(builder.build());
return;
}
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);
}
}
public void updateProgress(boolean showDeterminateProgress) {
}
public void onUpdateStatus() {
}
}