summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomasz Mikolajewski <mtomasz@google.com>2016-12-05 14:10:06 +0900
committerTomasz Mikolajewski <mtomasz@google.com>2017-03-01 14:42:17 +0900
commit6b802e8d33c6230907dc0fd0353a808f8e8f5f16 (patch)
treefd747fceb0bb374c5e51d2b5fe74a1811d21af8d
parentb96eb04b2a8ff4b7d47b09b4b2b5493c032e4d36 (diff)
downloadandroid_packages_apps_UnifiedEmail-6b802e8d33c6230907dc0fd0353a808f8e8f5f16.tar.gz
android_packages_apps_UnifiedEmail-6b802e8d33c6230907dc0fd0353a808f8e8f5f16.tar.bz2
android_packages_apps_UnifiedEmail-6b802e8d33c6230907dc0fd0353a808f8e8f5f16.zip
Add basic support for virtual files to Email.
It's basic support which allows users to attach virtual files in the first provided streamable format, usually PDF. Bug: 33609522 Test: Tested manually by attaching a sheets document. Change-Id: Ie75beaadb822c1e97e47c0972447114387e203e5
-rw-r--r--src/com/android/mail/compose/AttachmentComposeView.java3
-rw-r--r--src/com/android/mail/compose/AttachmentsView.java129
-rw-r--r--src/com/android/mail/compose/ComposeActivity.java56
-rw-r--r--src/com/android/mail/providers/Attachment.java16
-rw-r--r--src/com/android/mail/providers/UIProvider.java9
-rw-r--r--src/com/android/mail/utils/AttachmentUtils.java18
-rw-r--r--src/com/android/mail/utils/Utils.java4
7 files changed, 174 insertions, 61 deletions
diff --git a/src/com/android/mail/compose/AttachmentComposeView.java b/src/com/android/mail/compose/AttachmentComposeView.java
index 89216e17f..6f099c50e 100644
--- a/src/com/android/mail/compose/AttachmentComposeView.java
+++ b/src/com/android/mail/compose/AttachmentComposeView.java
@@ -69,7 +69,8 @@ class AttachmentComposeView extends LinearLayout implements AttachmentDeletionIn
private void populateAttachmentData(Context context) {
((TextView) findViewById(R.id.attachment_name)).setText(mAttachment.getName());
- if (mAttachment.size > 0) {
+ // If size is -1, then it's size is unknown, hence do not show it.
+ if (mAttachment.size != -1) {
((TextView) findViewById(R.id.attachment_size)).
setText(AttachmentUtils.convertToHumanReadableSize(context, mAttachment.size));
} else {
diff --git a/src/com/android/mail/compose/AttachmentsView.java b/src/com/android/mail/compose/AttachmentsView.java
index 5edac8a4d..285a43798 100644
--- a/src/com/android/mail/compose/AttachmentsView.java
+++ b/src/com/android/mail/compose/AttachmentsView.java
@@ -15,18 +15,21 @@
*/
package com.android.mail.compose;
+import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
+import android.webkit.MimeTypeMap;
import android.widget.LinearLayout;
import com.android.mail.R;
@@ -37,6 +40,7 @@ import com.android.mail.ui.AttachmentTile.AttachmentPreview;
import com.android.mail.ui.AttachmentTileGrid;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.Utils;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
@@ -206,6 +210,39 @@ class AttachmentsView extends LinearLayout {
}
/**
+ * Checks if the passed Uri is a virtual document.
+ *
+ * @param contentUri
+ * @return true if virtual, false if regular.
+ */
+ @TargetApi(24)
+ private boolean isVirtualDocument(Uri contentUri) {
+ // For SAF files, check if it's a virtual document.
+ if (!DocumentsContract.isDocumentUri(getContext(), contentUri)) {
+ return false;
+ }
+
+ final ContentResolver contentResolver = getContext().getContentResolver();
+ final Cursor metadataCursor = contentResolver.query(contentUri,
+ new String[] { DocumentsContract.Document.COLUMN_FLAGS }, null, null, null);
+ if (metadataCursor != null) {
+ try {
+ int flags = 0;
+ if (metadataCursor.moveToNext()) {
+ flags = metadataCursor.getInt(0);
+ }
+ if ((flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0) {
+ return true;
+ }
+ } finally {
+ metadataCursor.close();
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Generate an {@link Attachment} object for a given local content URI. Attempts to populate
* the {@link Attachment#name}, {@link Attachment#size}, and {@link Attachment#contentType}
* fields using a {@link ContentResolver}.
@@ -226,13 +263,26 @@ class AttachmentsView extends LinearLayout {
if (contentType == null) contentType = "";
final Attachment attachment = new Attachment();
- attachment.uri = null; // URI will be assigned by the provider upon send/save
- attachment.setName(null);
- attachment.size = 0;
+ attachment.uri = null; // URI will be assigned by the provider upon send/save
attachment.contentUri = contentUri;
attachment.thumbnailUri = contentUri;
Cursor metadataCursor = null;
+ String name = null;
+ int size = -1; // Unknown, will be determined either now or in the service
+ final boolean isVirtual = Utils.isRunningNOrLater()
+ ? isVirtualDocument(contentUri) : false;
+
+ if (isVirtual) {
+ final String[] mimeTypes = contentResolver.getStreamTypes(contentUri, "*/*");
+ if (mimeTypes != null && mimeTypes.length > 0) {
+ attachment.virtualMimeType = mimeTypes[0];
+ } else{
+ throw new AttachmentFailureException(
+ "Cannot attach a virtual document without any streamable format.");
+ }
+ }
+
try {
metadataCursor = contentResolver.query(
contentUri, new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE},
@@ -240,8 +290,12 @@ class AttachmentsView extends LinearLayout {
if (metadataCursor != null) {
try {
if (metadataCursor.moveToNext()) {
- attachment.setName(metadataCursor.getString(0));
- attachment.size = metadataCursor.getInt(1);
+ name = metadataCursor.getString(0);
+ // For virtual document this size is not the one which will be attached,
+ // so ignore it.
+ if (!isVirtual) {
+ size = metadataCursor.getInt(1);
+ }
}
} finally {
metadataCursor.close();
@@ -259,38 +313,55 @@ class AttachmentsView extends LinearLayout {
metadataCursor = getOptionalColumn(contentResolver, contentUri,
OpenableColumns.DISPLAY_NAME);
if (metadataCursor != null && metadataCursor.moveToNext()) {
- attachment.setName(metadataCursor.getString(0));
+ name = metadataCursor.getString(0);
}
} finally {
if (metadataCursor != null) metadataCursor.close();
}
// Let's try to get SIZE
- try {
- metadataCursor =
- getOptionalColumn(contentResolver, contentUri, OpenableColumns.SIZE);
- if (metadataCursor != null && metadataCursor.moveToNext()) {
- attachment.size = metadataCursor.getInt(0);
- } else {
- // Unable to get the size from the metadata cursor. Open the file and seek.
- attachment.size = getSizeFromFile(contentUri, contentResolver);
+ if (!isVirtual) {
+ try {
+ metadataCursor =
+ getOptionalColumn(contentResolver, contentUri, OpenableColumns.SIZE);
+ if (metadataCursor != null && metadataCursor.moveToNext()) {
+ size = metadataCursor.getInt(0);
+ } else {
+ // Unable to get the size from the metadata cursor. Open the file and seek.
+ size = getSizeFromFile(contentUri, contentResolver);
+ }
+ } finally {
+ if (metadataCursor != null) metadataCursor.close();
}
- } finally {
- if (metadataCursor != null) metadataCursor.close();
}
} catch (SecurityException e) {
throw new AttachmentFailureException("Security Exception from attachment uri", e);
}
- if (attachment.getName() == null) {
- attachment.setName(contentUri.getLastPathSegment());
+ if (name == null) {
+ name = contentUri.getLastPathSegment();
}
- if (attachment.size == 0) {
+
+ // For virtual files append the inferred extension name.
+ if (isVirtual) {
+ String extension = MimeTypeMap.getSingleton()
+ .getExtensionFromMimeType(attachment.virtualMimeType);
+ if (extension != null) {
+ name += "." + extension;
+ }
+ }
+
+ // TODO: This can't work with pipes. Fix it.
+ if (size == -1 && !isVirtual) {
// if the attachment is not a content:// for example, a file:// URI
- attachment.size = getSizeFromFile(contentUri, contentResolver);
+ size = getSizeFromFile(contentUri, contentResolver);
}
+ // Save the computed values into the attachment.
+ attachment.size = size;
+ attachment.setName(name);
attachment.setContentType(contentType);
+
return attachment;
}
@@ -300,28 +371,28 @@ class AttachmentsView extends LinearLayout {
* @param account
* @param attachment the attachment to be added.
*
- * @return size of the attachment added.
* @throws AttachmentFailureException if an error occurs adding the attachment.
*/
- public long addAttachment(Account account, Attachment attachment)
+ public void addAttachment(Account account, Attachment attachment)
throws AttachmentFailureException {
final int maxSize = account.settings.getMaxAttachmentSize();
- // Error getting the size or the size was too big.
- if (attachment.size == -1 || attachment.size > maxSize) {
+ // The attachment size is known and it's too big.
+ if (attachment.size > maxSize) {
throw new AttachmentFailureException(
"Attachment too large to attach", R.string.too_large_to_attach_single);
- } else if ((getTotalAttachmentsSize()
+ } else if (attachment.size != -1 && (getTotalAttachmentsSize()
+ attachment.size) > maxSize) {
throw new AttachmentFailureException(
"Attachment too large to attach", R.string.too_large_to_attach_additional);
} else {
addAttachment(attachment);
}
-
- return attachment.size;
}
+ /**
+ * @return size of the file or -1 if unknown.
+ */
private static int getSizeFromFile(Uri uri, ContentResolver contentResolver) {
int size = -1;
ParcelFileDescriptor file = null;
@@ -339,9 +410,7 @@ class AttachmentsView extends LinearLayout {
LogUtils.w(LOG_TAG, "Error closing file opened to obtain size.");
}
}
- // We only want to return a non-negative value. (ParcelFileDescriptor#getStatSize() will
- // return -1 if the fd is not a file
- return Math.max(size, 0);
+ return size;
}
/**
diff --git a/src/com/android/mail/compose/ComposeActivity.java b/src/com/android/mail/compose/ComposeActivity.java
index 5f125abe6..7b05ffaa1 100644
--- a/src/com/android/mail/compose/ComposeActivity.java
+++ b/src/com/android/mail/compose/ComposeActivity.java
@@ -36,6 +36,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.content.pm.ActivityInfo;
+import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Rect;
@@ -1834,12 +1835,16 @@ public class ComposeActivity extends ActionBarActivity
addAttachments(refMessage.getAttachments());
}
- public long addAttachments(List<Attachment> attachments) {
- long size = 0;
+ /**
+ * @return true if at least one file is attached.
+ */
+ public boolean addAttachments(List<Attachment> attachments) {
+ boolean attached = false;
AttachmentFailureException error = null;
for (Attachment a : attachments) {
try {
- size += mAttachmentsView.addAttachment(mAccount, a);
+ mAttachmentsView.addAttachment(mAccount, a);
+ attached = true;
} catch (AttachmentFailureException e) {
error = e;
}
@@ -1852,7 +1857,7 @@ public class ComposeActivity extends ActionBarActivity
showAttachmentTooBigToast(error.getErrorRes());
}
}
- return size;
+ return attached;
}
/**
@@ -1881,33 +1886,30 @@ public class ComposeActivity extends ActionBarActivity
}
final String action = intent.getAction();
if (!mAttachmentsChanged) {
- long totalSize = 0;
+ boolean attached = false;
if (extras.containsKey(EXTRA_ATTACHMENTS)) {
final String[] uris = (String[]) extras.getSerializable(EXTRA_ATTACHMENTS);
final ArrayList<Uri> parsedUris = Lists.newArrayListWithCapacity(uris.length);
for (String uri : uris) {
parsedUris.add(Uri.parse(uri));
}
- totalSize += handleAttachmentUrisFromIntent(parsedUris);
+ attached |= handleAttachmentUrisFromIntent(parsedUris);
}
if (extras.containsKey(Intent.EXTRA_STREAM)) {
if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
final ArrayList<Uri> uris = extras
.getParcelableArrayList(Intent.EXTRA_STREAM);
- totalSize += handleAttachmentUrisFromIntent(uris);
+ attached |= handleAttachmentUrisFromIntent(uris);
} else {
final Uri uri = extras.getParcelable(Intent.EXTRA_STREAM);
final ArrayList<Uri> uris = Lists.newArrayList(uri);
- totalSize += handleAttachmentUrisFromIntent(uris);
+ attached |= handleAttachmentUrisFromIntent(uris);
}
}
- if (totalSize > 0) {
+ if (attached) {
mAttachmentsChanged = true;
updateSaveUi();
-
- Analytics.getInstance().sendEvent("send_intent_with_attachments",
- Integer.toString(getAttachments().size()), null, totalSize);
}
}
}
@@ -1923,9 +1925,9 @@ public class ComposeActivity extends ActionBarActivity
/**
* Helper function to handle a list of uris to attach.
- * @return the total size of all successfully attached files.
+ * @return true if anything has been attached.
*/
- private long handleAttachmentUrisFromIntent(List<Uri> uris) {
+ private boolean handleAttachmentUrisFromIntent(List<Uri> uris) {
ArrayList<Attachment> attachments = Lists.newArrayList();
for (Uri uri : uris) {
try {
@@ -2023,8 +2025,8 @@ public class ComposeActivity extends ActionBarActivity
return;
}
- final long size = handleAttachmentUrisFromIntent(Arrays.asList(contentUri));
- if (size > 0) {
+ final boolean attached = handleAttachmentUrisFromIntent(Arrays.asList(contentUri));
+ if (attached) {
mAttachmentsChanged = true;
updateSaveUi();
}
@@ -2042,11 +2044,9 @@ public class ComposeActivity extends ActionBarActivity
private void addAttachmentAndUpdateView(Attachment attachment) {
try {
- long size = mAttachmentsView.addAttachment(mAccount, attachment);
- if (size > 0) {
- mAttachmentsChanged = true;
- updateSaveUi();
- }
+ mAttachmentsView.addAttachment(mAccount, attachment);
+ mAttachmentsChanged = true;
+ updateSaveUi();
} catch (AttachmentFailureException e) {
LogUtils.e(LOG_TAG, e, "Error adding attachment");
showAttachmentTooBigToast(e.getErrorRes());
@@ -2605,7 +2605,7 @@ public class ComposeActivity extends ActionBarActivity
if (openedFds != null) {
final Set<String> keys = openedFds.keySet();
for (final String key : keys) {
- final ParcelFileDescriptor fd = openedFds.getParcelable(key);
+ final AssetFileDescriptor fd = openedFds.getParcelable(key);
if (fd != null) {
try {
fd.close();
@@ -2715,9 +2715,16 @@ public class ComposeActivity extends ActionBarActivity
continue;
}
- ParcelFileDescriptor fileDescriptor;
+ AssetFileDescriptor fileDescriptor;
try {
- fileDescriptor = resolver.openFileDescriptor(attachment.contentUri, "r");
+ if (attachment.virtualMimeType == null) {
+ fileDescriptor = new AssetFileDescriptor(
+ resolver.openFileDescriptor(attachment.contentUri, "r"), 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH);
+ } else {
+ fileDescriptor = resolver.openTypedAssetFileDescriptor(
+ attachment.contentUri, attachment.virtualMimeType, null, null);
+ }
} catch (FileNotFoundException e) {
LogUtils.e(LOG_TAG, e, "Exception attempting to open attachment");
fileDescriptor = null;
@@ -3424,7 +3431,6 @@ public class ComposeActivity extends ActionBarActivity
@SuppressLint("NewApi")
private void doAttach(String type) {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
- i.addCategory(Intent.CATEGORY_OPENABLE);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
i.setType(type);
diff --git a/src/com/android/mail/providers/Attachment.java b/src/com/android/mail/providers/Attachment.java
index b8e86edeb..4ed33f83e 100644
--- a/src/com/android/mail/providers/Attachment.java
+++ b/src/com/android/mail/providers/Attachment.java
@@ -160,6 +160,14 @@ public class Attachment implements Parcelable {
*/
public String providerData;
+ /**
+ * Streamable mime type of the attachment in case it's a virtual file.
+ *
+ * Might be null. If null, then the default type (contentType) is assumed
+ * to be streamable.
+ */
+ public String virtualMimeType;
+
private transient Uri mIdentifierUri;
/**
@@ -186,6 +194,7 @@ public class Attachment implements Parcelable {
supportsDownloadAgain = in.readInt() == 1;
type = in.readInt();
flags = in.readInt();
+ virtualMimeType = in.readString();
}
public Attachment(Cursor cursor) {
@@ -211,6 +220,7 @@ public class Attachment implements Parcelable {
cursor.getColumnIndex(AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN)) == 1;
type = cursor.getInt(cursor.getColumnIndex(AttachmentColumns.TYPE));
flags = cursor.getInt(cursor.getColumnIndex(AttachmentColumns.FLAGS));
+ virtualMimeType = cursor.getString(cursor.getColumnIndex(AttachmentColumns.VIRTUAL_MIME_TYPE));
}
public Attachment(JSONObject srcJson) {
@@ -228,6 +238,7 @@ public class Attachment implements Parcelable {
supportsDownloadAgain = srcJson.optBoolean(AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, true);
type = srcJson.optInt(AttachmentColumns.TYPE);
flags = srcJson.optInt(AttachmentColumns.FLAGS);
+ virtualMimeType = srcJson.optString(AttachmentColumns.VIRTUAL_MIME_TYPE, null);
}
/**
@@ -257,6 +268,7 @@ public class Attachment implements Parcelable {
type = inline ? AttachmentType.INLINE_CURRENT_MESSAGE : AttachmentType.STANDARD;
partId = cid;
flags = 0;
+ virtualMimeType = null;
// insert attachment into content provider so that we can open the file
final ContentResolver resolver = context.getContentResolver();
@@ -303,6 +315,7 @@ public class Attachment implements Parcelable {
type = values.getAsInteger(AttachmentColumns.TYPE);
flags = values.getAsInteger(AttachmentColumns.FLAGS);
partId = values.getAsString(AttachmentColumns.CONTENT_ID);
+ virtualMimeType = values.getAsString(AttachmentColumns.VIRTUAL_MIME_TYPE);
}
/**
@@ -328,6 +341,7 @@ public class Attachment implements Parcelable {
values.put(AttachmentColumns.TYPE, type);
values.put(AttachmentColumns.FLAGS, flags);
values.put(AttachmentColumns.CONTENT_ID, partId);
+ values.put(AttachmentColumns.VIRTUAL_MIME_TYPE, virtualMimeType);
return values;
}
@@ -348,6 +362,7 @@ public class Attachment implements Parcelable {
dest.writeInt(supportsDownloadAgain ? 1 : 0);
dest.writeInt(type);
dest.writeInt(flags);
+ dest.writeString(virtualMimeType);
}
public JSONObject toJSON() throws JSONException {
@@ -367,6 +382,7 @@ public class Attachment implements Parcelable {
result.put(AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, supportsDownloadAgain);
result.put(AttachmentColumns.TYPE, type);
result.put(AttachmentColumns.FLAGS, flags);
+ result.put(AttachmentColumns.VIRTUAL_MIME_TYPE, virtualMimeType);
return result;
}
diff --git a/src/com/android/mail/providers/UIProvider.java b/src/com/android/mail/providers/UIProvider.java
index 783e4f348..3bb6da54a 100644
--- a/src/com/android/mail/providers/UIProvider.java
+++ b/src/com/android/mail/providers/UIProvider.java
@@ -1958,7 +1958,8 @@ public class UIProvider {
AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN,
AttachmentColumns.TYPE,
AttachmentColumns.FLAGS,
- AttachmentColumns.CONTENT_ID
+ AttachmentColumns.CONTENT_ID,
+ AttachmentColumns.VIRTUAL_MIME_TYPE
};
public static final int ATTACHMENT_NAME_COLUMN = 0;
public static final int ATTACHMENT_SIZE_COLUMN = 1;
@@ -1974,6 +1975,7 @@ public class UIProvider {
public static final int ATTACHMENT_TYPE_COLUMN = 11;
public static final int ATTACHMENT_FLAGS_COLUMN = 12;
public static final int ATTACHMENT_CONTENT_ID_COLUMN = 13;
+ public static final int ATTACHMENT_VIRTUAL_MIME_TYPE_COLUMN = 14;
/** Separates attachment info parts in strings in the database. */
public static final String ATTACHMENT_INFO_SEPARATOR = "\n"; // use to join
@@ -2160,6 +2162,11 @@ public class UIProvider {
*/
public static final String CONTENT_ID = "contentId";
+ /**
+ * Holds a streamable mime type for this attachment if it's a virtual file.
+ */
+ public static final String VIRTUAL_MIME_TYPE = "streamableMimeType";
+
private AttachmentColumns() {}
}
diff --git a/src/com/android/mail/utils/AttachmentUtils.java b/src/com/android/mail/utils/AttachmentUtils.java
index 4837f7a02..8cdaef833 100644
--- a/src/com/android/mail/utils/AttachmentUtils.java
+++ b/src/com/android/mail/utils/AttachmentUtils.java
@@ -17,6 +17,8 @@ package com.android.mail.utils;
import android.app.DownloadManager;
import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetFileDescriptor.AutoCloseInputStream;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
@@ -187,13 +189,13 @@ public class AttachmentUtils {
try {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-kk:mm:ss");
file = File.createTempFile(dateFormat.format(new Date()), ".attachment", cacheDir);
- final ParcelFileDescriptor fileDescriptor = attachmentFds != null
- && attachment.contentUri != null ? (ParcelFileDescriptor) attachmentFds
+ final AssetFileDescriptor fileDescriptor = attachmentFds != null
+ && attachment.contentUri != null ? (AssetFileDescriptor) attachmentFds
.getParcelable(attachment.contentUri.toString())
: null;
if (fileDescriptor != null) {
// Get the input stream from the file descriptor
- inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
+ inputStream = new AutoCloseInputStream(fileDescriptor);
} else {
if (attachment.contentUri == null) {
// The contentUri of the attachment is null. This can happen when sending a
@@ -203,7 +205,15 @@ public class AttachmentUtils {
throw new FileNotFoundException("Missing contentUri in attachment");
}
// Attempt to open the file
- inputStream = context.getContentResolver().openInputStream(attachment.contentUri);
+ if (attachment.virtualMimeType == null) {
+ inputStream = context.getContentResolver().openInputStream(attachment.contentUri);
+ } else {
+ AssetFileDescriptor fd = context.getContentResolver().openTypedAssetFileDescriptor(
+ attachment.contentUri, attachment.virtualMimeType, null, null);
+ if (fd != null) {
+ inputStream = new AutoCloseInputStream(fd);
+ }
+ }
}
outputStream = new FileOutputStream(file);
final long now = SystemClock.elapsedRealtime();
diff --git a/src/com/android/mail/utils/Utils.java b/src/com/android/mail/utils/Utils.java
index 95e8ee6ce..4efbccc03 100644
--- a/src/com/android/mail/utils/Utils.java
+++ b/src/com/android/mail/utils/Utils.java
@@ -145,6 +145,10 @@ public class Utils {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
}
+ public static boolean isRunningNOrLater() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
+ }
+
/**
* @return Whether we are running on a low memory device. This is used to disable certain
* memory intensive features in the app.