blob: b6751b527608e49282a755f0870ef54965bd6c32 [file] [log] [blame]
/*
* Copyright (C) 2011 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.email;
import com.android.emailcommon.internet.MimeUtility;
import com.android.emailcommon.provider.EmailContent.Attachment;
import com.android.emailcommon.utility.AttachmentUtilities;
import com.android.emailcommon.utility.Utility;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.provider.Settings;
import android.text.TextUtils;
import java.util.List;
/**
* Encapsulates commonly used attachment information related to suitability for viewing and saving,
* based on the attachment's filename and mime type.
*/
public class AttachmentInfo {
// Projection which can be used with the constructor taking a Cursor argument
public static final String[] PROJECTION = new String[] {Attachment.RECORD_ID, Attachment.SIZE,
Attachment.FILENAME, Attachment.MIME_TYPE, Attachment.ACCOUNT_KEY, Attachment.FLAGS};
// Offsets into PROJECTION
public static final int COLUMN_ID = 0;
public static final int COLUMN_SIZE = 1;
public static final int COLUMN_FILENAME = 2;
public static final int COLUMN_MIME_TYPE = 3;
public static final int COLUMN_ACCOUNT_KEY = 4;
public static final int COLUMN_FLAGS = 5;
/** Attachment not denied */
public static final int ALLOW = 0x00;
/** Attachment suspected of being malware */
public static final int DENY_MALWARE = 0x01;
/** Attachment too large; must download over wi-fi */
public static final int DENY_WIFIONLY = 0x02;
/** No receiving intent to handle attachment type */
public static final int DENY_NOINTENT = 0x04;
/** Side load of applications is disabled */
public static final int DENY_NOSIDELOAD = 0x08;
// TODO Remove DENY_APKINSTALL when we can install directly from the Email activity
/** Unable to install any APK */
public static final int DENY_APKINSTALL = 0x10;
/** Security policy prohibits install */
public static final int DENY_POLICY = 0x20;
public final long mId;
public final long mSize;
public final String mName;
public final String mContentType;
public final long mAccountKey;
public final int mFlags;
/** Whether or not this attachment can be viewed */
public final boolean mAllowView;
/** Whether or not this attachment can be saved */
public final boolean mAllowSave;
/** Whether or not this attachment can be installed [only true for APKs] */
public final boolean mAllowInstall;
/** Reason(s) why this attachment is denied from being viewed */
public final int mDenyFlags;
public AttachmentInfo(Context context, Attachment attachment) {
this(context, attachment.mId, attachment.mSize, attachment.mFileName, attachment.mMimeType,
attachment.mAccountKey, attachment.mFlags);
}
public AttachmentInfo(Context context, Cursor c) {
this(context, c.getLong(COLUMN_ID), c.getLong(COLUMN_SIZE), c.getString(COLUMN_FILENAME),
c.getString(COLUMN_MIME_TYPE), c.getLong(COLUMN_ACCOUNT_KEY),
c.getInt(COLUMN_FLAGS));
}
public AttachmentInfo(Context context, AttachmentInfo info) {
this(context, info.mId, info.mSize, info.mName, info.mContentType, info.mAccountKey,
info.mFlags);
}
public AttachmentInfo(Context context, long id, long size, String fileName, String mimeType,
long accountKey, int flags) {
mSize = size;
mContentType = AttachmentUtilities.inferMimeType(fileName, mimeType);
mName = fileName;
mId = id;
mAccountKey = accountKey;
mFlags = flags;
boolean canView = true;
boolean canSave = true;
boolean canInstall = false;
int denyFlags = ALLOW;
// Don't enable the "save" button if we've got no place to save the file
if (!Utility.isExternalStorageMounted()) {
canSave = false;
}
// Check for acceptable / unacceptable attachments by MIME-type
if ((!MimeUtility.mimeTypeMatches(mContentType,
AttachmentUtilities.ACCEPTABLE_ATTACHMENT_VIEW_TYPES)) ||
(MimeUtility.mimeTypeMatches(mContentType,
AttachmentUtilities.UNACCEPTABLE_ATTACHMENT_VIEW_TYPES))) {
canView = false;
}
// Check for unacceptable attachments by filename extension
String extension = AttachmentUtilities.getFilenameExtension(mName);
if (!TextUtils.isEmpty(extension) &&
Utility.arrayContains(AttachmentUtilities.UNACCEPTABLE_ATTACHMENT_EXTENSIONS,
extension)) {
canView = false;
canSave = false;
denyFlags |= DENY_MALWARE;
}
// Check for policy restrictions on download
if ((flags & Attachment.FLAG_POLICY_DISALLOWS_DOWNLOAD) != 0) {
canView = false;
canSave = false;
denyFlags |= DENY_POLICY;
}
// Check for installable attachments by filename extension
extension = AttachmentUtilities.getFilenameExtension(mName);
if (!TextUtils.isEmpty(extension) &&
Utility.arrayContains(AttachmentUtilities.INSTALLABLE_ATTACHMENT_EXTENSIONS,
extension)) {
boolean sideloadEnabled;
sideloadEnabled = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.INSTALL_NON_MARKET_APPS, 0 /* sideload disabled */) != 0;
canSave &= sideloadEnabled;
canView = canSave;
canInstall = canSave;
if (!sideloadEnabled) {
denyFlags |= DENY_NOSIDELOAD;
}
}
// Check for file size exceeded
// The size limit is overridden when on a wifi connection - any size is OK
if (mSize > AttachmentUtilities.MAX_ATTACHMENT_DOWNLOAD_SIZE) {
int networkType = EmailConnectivityManager.getActiveNetworkType(context);
if (networkType != ConnectivityManager.TYPE_WIFI) {
canView = false;
canSave = false;
denyFlags |= DENY_WIFIONLY;
}
}
// Check to see if any activities can view this attachment; if none, we can't view it
Intent intent = getAttachmentIntent(context, 0);
PackageManager pm = context.getPackageManager();
List<ResolveInfo> activityList = pm.queryIntentActivities(intent, 0 /*no account*/);
if (activityList.isEmpty()) {
canView = false;
canSave = false;
denyFlags |= DENY_NOINTENT;
}
mAllowView = canView;
mAllowSave = canSave;
mAllowInstall = canInstall;
mDenyFlags = denyFlags;
}
/**
* Returns an <code>Intent</code> to load the given attachment.
* @param context the caller's context
* @param accountId the account associated with the attachment (or 0 if we don't need to
* resolve from attachmentUri to contentUri)
* @return an Intent suitable for viewing the attachment
*/
public Intent getAttachmentIntent(Context context, long accountId) {
Uri contentUri = getUriForIntent(context, accountId);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(contentUri, mContentType);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
return intent;
}
protected Uri getUriForIntent(Context context, long accountId) {
Uri contentUri = AttachmentUtilities.getAttachmentUri(accountId, mId);
if (accountId > 0) {
contentUri = AttachmentUtilities.resolveAttachmentIdToContentUri(
context.getContentResolver(), contentUri);
}
return contentUri;
}
/**
* An attachment is eligible for download if it can either be viewed or saved (or both)
* @return whether the attachment is eligible for download
*/
public boolean isEligibleForDownload() {
return mAllowView || mAllowSave;
}
@Override
public int hashCode() {
return (int) (mId ^ (mId >>> 32));
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if ((o == null) || (o.getClass() != getClass())) {
return false;
}
return ((AttachmentInfo) o).mId == mId;
}
@Override
public String toString() {
return "{Attachment " + mId + ":" + mName + "," + mContentType + "," + mSize + "}";
}
}