| /* |
| * 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 + "}"; |
| } |
| } |