diff options
-rw-r--r-- | Android.mk | 3 | ||||
-rw-r--r-- | res/layout/compose.xml | 4 | ||||
-rw-r--r-- | res/values/strings.xml | 27 | ||||
-rw-r--r-- | src/com/android/email/compose/AttachmentComposeView.java | 70 | ||||
-rw-r--r-- | src/com/android/email/compose/AttachmentsView.java | 143 | ||||
-rw-r--r-- | src/com/android/email/compose/ComposeActivity.java | 14 | ||||
-rw-r--r-- | src/com/android/email/providers/protos/Attachment.java | 33 | ||||
-rw-r--r-- | src/com/android/email/providers/protos/exchange/ExchangeAttachment.java | 78 | ||||
-rw-r--r-- | src/com/android/email/providers/protos/exchange/README | 1 | ||||
-rw-r--r-- | src/com/android/email/providers/protos/longshadow/README | 1 | ||||
-rw-r--r-- | src/com/android/email/providers/protos/mock/MockAttachment.java | 87 | ||||
-rw-r--r-- | src/com/android/email/utils/AttachmentUtils.java | 28 | ||||
-rw-r--r-- | src/com/android/email/utils/LogUtils.java | 374 | ||||
-rw-r--r-- | src/com/android/email/utils/MimeType.java | 173 | ||||
-rw-r--r-- | src/com/android/email/utils/README | 1 | ||||
-rw-r--r-- | src/com/android/email/utils/Utils.java | 24 |
16 files changed, 1055 insertions, 6 deletions
diff --git a/Android.mk b/Android.mk index a9a69b700..34d639480 100644 --- a/Android.mk +++ b/Android.mk @@ -11,7 +11,8 @@ include $(CLEAR_VARS) src_dirs := src LOCAL_PACKAGE_NAME := UnifiedEmail -LOCAL_STATIC_JAVA_LIBRARIES = android-common-chips +LOCAL_STATIC_JAVA_LIBRARIES := android-common-chips +LOCAL_STATIC_JAVA_LIBRARIES += guava LOCAL_SDK_VERSION := 14 diff --git a/res/layout/compose.xml b/res/layout/compose.xml index 5781b6373..ea870a315 100644 --- a/res/layout/compose.xml +++ b/res/layout/compose.xml @@ -47,14 +47,14 @@ </LinearLayout> <!-- Attachments --> - <!--<com.google.android.gm.AttachmentsView android:id="@+id/attachments" + <com.android.email.compose.AttachmentsView android:id="@+id/attachments" android:layout_height="wrap_content" android:layout_width="match_parent" android:orientation="vertical" android:paddingTop="2dip" android:paddingRight="5dip" android:paddingBottom="0dip" - android:paddingLeft="5dip" />--> + android:paddingLeft="5dip" /> <!-- Body --> <include layout="@layout/compose_body"/> diff --git a/res/values/strings.xml b/res/values/strings.xml index ae1894060..11a488259 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -56,6 +56,33 @@ <!-- Menu item that displays the help page for Gmail. --> <string name="help_and_info">Help</string> + <!-- Attachments --> + <!-- Size unit, displayed in a button next to an attachment [CHAR LIMIT=5]--> + <string name="bytes">B</string> + <!-- Size unit, displayed in a button next to an attachment [CHAR LIMIT=5] --> + <string name="kilobytes">KB</string> + <!-- Size unit, displayed in a button next to an attachment [CHAR LIMIT=5]--> + <string name="megabytes">MB</string> + <!-- Attachment description for image files [CHAR LIMIT=30] --> + <string name="attachment_image">Image</string> + <!-- Attachment description for video files [CHAR LIMIT=30] --> + <string name="attachment_video">Video</string> + <!-- Attachment description for audio files [CHAR LIMIT=30] --> + <string name="attachment_audio">Audio</string> + <!-- Attachment description for text files [CHAR LIMIT=30] --> + <string name="attachment_text">Text</string> + <!-- Attachment description for .doc files [CHAR LIMIT=30] --> + <string name="attachment_application_msword">Document</string> + <!-- Attachment description for .ppt files [CHAR LIMIT=30] --> + <string name="attachment_application_vnd_ms_powerpoint">Presentation</string> + <!-- Attachment description for .pdf files [CHAR LIMIT=30] --> + <string name="attachment_application_vnd_ms_excel">Spreadsheet</string> + <!-- Attachment description for .pdf files [CHAR LIMIT=30] --> + <string name="attachment_application_pdf">PDF</string> + <!-- Attachment description for unknown files [CHAR LIMIT=30]--> + <string name="attachment_unknown"><xliff:g id="attachmentExtension">%s</xliff:g> File</string> + + <!-- Webview Context Menu Strings --> <!-- Title of dialog for choosing which activity to share a link with. [CHAR LIMIT=50]--> <string name="choosertitle_sharevia">Share via</string> diff --git a/src/com/android/email/compose/AttachmentComposeView.java b/src/com/android/email/compose/AttachmentComposeView.java new file mode 100644 index 000000000..a376cd996 --- /dev/null +++ b/src/com/android/email/compose/AttachmentComposeView.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2007, Google Inc. + * + * 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.compose; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.email.providers.protos.Attachment; +import com.android.email.R; +import com.android.email.utils.AttachmentUtils; +import com.android.email.utils.Utils; +import com.android.email.utils.LogUtils; + +/** + * This view is used in the ComposeActivity to display an attachment along with its name/size + * and a Remove button. + */ +class AttachmentComposeView extends LinearLayout { + private final long mSize; + private final String mFilename; + + public AttachmentComposeView(Context c, Attachment attachment) { + super(c); + mFilename = attachment.getName(); + mSize = attachment.getSize(); + + LogUtils.d(Utils.LOG_TAG, ">>>>> Attachment uri: %s", attachment.getOriginExtras()); + LogUtils.d(Utils.LOG_TAG, ">>>>> type: %s", attachment.getContentType()); + LogUtils.d(Utils.LOG_TAG, ">>>>> name: %s", mFilename); + LogUtils.d(Utils.LOG_TAG, ">>>>> size: %d", mSize); + + LayoutInflater factory = LayoutInflater.from(getContext()); + + factory.inflate(R.layout.attachment, this); + populateAttachmentData(c); + } + + public void addDeleteListener(OnClickListener clickListener) { + ImageView deleteButton = (ImageView) findViewById(R.id.remove_attachment); + deleteButton.setOnClickListener(clickListener); + } + + private void populateAttachmentData(Context context) { + ((TextView) findViewById(R.id.attachment_name)).setText(mFilename); + + if (mSize != 0) { + ((TextView) findViewById(R.id.attachment_size)). + setText(AttachmentUtils.convertToHumanReadableSize(context, mSize)); + } else { + ((TextView) findViewById(R.id.attachment_size)).setVisibility(View.GONE); + } + } +} diff --git a/src/com/android/email/compose/AttachmentsView.java b/src/com/android/email/compose/AttachmentsView.java new file mode 100644 index 000000000..926598416 --- /dev/null +++ b/src/com/android/email/compose/AttachmentsView.java @@ -0,0 +1,143 @@ +/** + * Copyright (c) 2011, Google Inc. + * + * 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.compose; + +import com.android.email.providers.protos.Attachment; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; + +import java.util.ArrayList; + +/* + * View for displaying attachments in the compose screen. + */ +class AttachmentsView extends LinearLayout { + private ArrayList<Attachment> mAttachments; + private AttachmentChangesListener mChangeListener; + + public AttachmentsView(Context context) { + this(context, null); + } + + public AttachmentsView(Context context, AttributeSet attrs) { + super(context, attrs); + mAttachments = Lists.newArrayList(); + } + + /** + * Set a listener for changes to the attachments. + * @param listener + */ + public void setAttachmentChangesListener(AttachmentChangesListener listener) { + mChangeListener = listener; + } + + /** + * Add an attachment and update the ui accordingly. + * @param attachment + */ + public void addAttachment(final Attachment attachment) { + if (!isShown()) { + setVisibility(View.VISIBLE); + } + mAttachments.add(attachment); + + final AttachmentComposeView attachmentView = + new AttachmentComposeView(getContext(), attachment); + + attachmentView.addDeleteListener(new OnClickListener() { + public void onClick(View v) { + deleteAttachment(attachmentView, attachment); + } + }); + + + addView(attachmentView, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT)); + + if (mChangeListener != null) { + mChangeListener.onAttachmentAdded(); + } + } + + @VisibleForTesting + protected void deleteAttachment(final AttachmentComposeView attachmentView, + final Attachment attachment) { + mAttachments.remove(attachment); + removeView(attachmentView); + if (mChangeListener != null) { + mChangeListener.onAttachmentDeleted(); + } + if (mAttachments.size() == 0) { + setVisibility(View.GONE); + } + } + + /** + * Get all attachments being managed by this view. + * @return attachments. + */ + public ArrayList<Attachment> getAttachments() { + return mAttachments; + } + + /** + * Delete all attachments being managed by this view. + */ + public void deleteAllAttachments() { + mAttachments.clear(); + removeAllViews(); + } + + /** + * See if all the attachments in this view are synced. + */ + public boolean areAttachmentsSynced() { + for (Attachment a : mAttachments) { + if (a.isSynced()) { + return true; + } + } + return false; + } + + /** + * Get the total size of all attachments currently in this view. + */ + public int getTotalAttachmentsSize() { + int totalSize = 0; + for (Attachment attachment : mAttachments) { + totalSize += attachment.getSize(); + } + return totalSize; + } + + /** + * Interface to implement to be notified about changes to the attachments. + * @author mindyp@google.com + * + */ + public interface AttachmentChangesListener { + public void onAttachmentDeleted(); + public void onAttachmentAdded(); + } +} diff --git a/src/com/android/email/compose/ComposeActivity.java b/src/com/android/email/compose/ComposeActivity.java index c5edd4d55..31d842169 100644 --- a/src/com/android/email/compose/ComposeActivity.java +++ b/src/com/android/email/compose/ComposeActivity.java @@ -27,12 +27,17 @@ import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; + +import com.android.email.providers.protos.Attachment; +import com.android.email.providers.protos.mock.MockAttachment; import com.android.email.R; +import com.android.email.utils.MimeType; public class ComposeActivity extends Activity implements OnClickListener { private Button mCcBccButton; private CcBccView mCcBccView; + private AttachmentsView mAttachmentsView; @Override public void onCreate(Bundle savedInstanceState) { @@ -43,6 +48,7 @@ public class ComposeActivity extends Activity implements OnClickListener { mCcBccButton.setOnClickListener(this); } mCcBccView = (CcBccView) findViewById(R.id.cc_bcc_wrapper); + mAttachmentsView = (AttachmentsView)findViewById(R.id.attachments); } @Override @@ -71,6 +77,14 @@ public class ComposeActivity extends Activity implements OnClickListener { int id = item.getItemId(); boolean handled = false; switch (id) { + case R.id.add_attachment: + MockAttachment attachment = new MockAttachment(); + attachment.partId = "0"; + attachment.name = "testattachment.png"; + attachment.contentType = MimeType.inferMimeType(attachment.name, null); + attachment.originExtras = ""; + mAttachmentsView.addAttachment(attachment); + break; case R.id.add_cc: case R.id.add_bcc: mCcBccView.show(); diff --git a/src/com/android/email/providers/protos/Attachment.java b/src/com/android/email/providers/protos/Attachment.java new file mode 100644 index 000000000..6a2073e00 --- /dev/null +++ b/src/com/android/email/providers/protos/Attachment.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2011, Google Inc. + * + * 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.providers.protos; + +public interface Attachment { + + String getName(); + + long getSize(); + + String getOriginExtras(); + + String getContentType(); + + Object getOrigin(); + + String getPartId(); + + boolean isSynced(); +} diff --git a/src/com/android/email/providers/protos/exchange/ExchangeAttachment.java b/src/com/android/email/providers/protos/exchange/ExchangeAttachment.java new file mode 100644 index 000000000..d4ccf1b83 --- /dev/null +++ b/src/com/android/email/providers/protos/exchange/ExchangeAttachment.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2011, Google Inc. + * + * 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.providers.protos.exchange; + +import com.android.email.providers.protos.Attachment; + +import java.io.Serializable; + +public class ExchangeAttachment implements Serializable, Attachment { + private static final long serialVersionUID = 1L; + + public String mFileName; + public String mMimeType; + public long mSize; + public String mContentId; + public String mContentUri; + public long mMessageKey; + public String mLocation; + public String mEncoding; + public String mContent; // Not currently used + public int mFlags; + public byte[] mContentBytes; + public long mAccountKey; + + + @Override + public String getName() { + return mFileName; + } + + @Override + public long getSize() { + return mSize; + } + + @Override + public String getOriginExtras() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getContentType() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Object getOrigin() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getPartId() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isSynced() { + // TODO Auto-generated method stub + return false; + } +} diff --git a/src/com/android/email/providers/protos/exchange/README b/src/com/android/email/providers/protos/exchange/README deleted file mode 100644 index a3fcb7533..000000000 --- a/src/com/android/email/providers/protos/exchange/README +++ /dev/null @@ -1 +0,0 @@ -Unified email directories
\ No newline at end of file diff --git a/src/com/android/email/providers/protos/longshadow/README b/src/com/android/email/providers/protos/longshadow/README deleted file mode 100644 index a3fcb7533..000000000 --- a/src/com/android/email/providers/protos/longshadow/README +++ /dev/null @@ -1 +0,0 @@ -Unified email directories
\ No newline at end of file diff --git a/src/com/android/email/providers/protos/mock/MockAttachment.java b/src/com/android/email/providers/protos/mock/MockAttachment.java new file mode 100644 index 000000000..3a884af56 --- /dev/null +++ b/src/com/android/email/providers/protos/mock/MockAttachment.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2011, Google Inc. + * + * 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.providers.protos.mock; + +import com.android.email.providers.protos.Attachment; + +import java.io.Serializable; + + + +public class MockAttachment implements Serializable, Attachment { + + private static final long serialVersionUID = 1L; + + /** Identifies the attachment uniquely when combined wih a message id.*/ + public String partId; + + /** The intended filename of the attachment.*/ + public String name; + + /** The native content type.*/ + public String contentType; + + /** The size of the attachment in its native form.*/ + public int size; + + /** + * The content type of the simple version of the attachment. Blank if no simple version is + * available. + */ + public String simpleContentType; + + public String originExtras; + + public String cachedContent; + + + @Override + public String getName() { + return name; + } + + @Override + public long getSize() { + return size; + } + + @Override + public String getOriginExtras() { + return originExtras; + } + + @Override + public String getContentType() { + return contentType; + } + + @Override + public Object getOrigin() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getPartId() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isSynced() { + return true; + } +}
\ No newline at end of file diff --git a/src/com/android/email/utils/AttachmentUtils.java b/src/com/android/email/utils/AttachmentUtils.java new file mode 100644 index 000000000..2e9a03a11 --- /dev/null +++ b/src/com/android/email/utils/AttachmentUtils.java @@ -0,0 +1,28 @@ +package com.android.email.utils; + +import android.content.Context; + +import com.android.email.R; + +import java.text.DecimalFormat; + +public class AttachmentUtils { + private static final int KILO = 1024; + private static final int MEGA = KILO * KILO; + + /** + * @return A string suitable for display in bytes, kilobytes or megabytes + * depending on its size. + */ + public static String convertToHumanReadableSize(Context context, long size) { + if (size < KILO) { + return size + context.getString(R.string.bytes); + } else if (size < MEGA) { + return (size / KILO) + context.getString(R.string.kilobytes); + } else { + DecimalFormat onePlace = new DecimalFormat("0.#"); + return onePlace.format((float) size / (float) MEGA) + + context.getString(R.string.megabytes); + } + } +} diff --git a/src/com/android/email/utils/LogUtils.java b/src/com/android/email/utils/LogUtils.java new file mode 100644 index 000000000..8c065a0e3 --- /dev/null +++ b/src/com/android/email/utils/LogUtils.java @@ -0,0 +1,374 @@ +/** + * Copyright (c) 2011, Google Inc. + * + * 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.utils; + +import android.net.Uri; +import android.util.Log; +import com.google.common.annotations.VisibleForTesting; + +import java.util.List; + +public class LogUtils { + private static final String TAG = "Gmail"; + + /** + * Priority constant for the println method; use LogUtils.v. + */ + public static final int VERBOSE = Log.VERBOSE; + + /** + * Priority constant for the println method; use LogUtils.d. + */ + public static final int DEBUG = Log.DEBUG; + + /** + * Priority constant for the println method; use LogUtils.i. + */ + public static final int INFO = Log.INFO; + + /** + * Priority constant for the println method; use LogUtils.w. + */ + public static final int WARN = Log.WARN; + + /** + * Priority constant for the println method; use LogUtils.e. + */ + public static final int ERROR = Log.ERROR; + + /** + * Used to enable/disable logging that we don't want included in + * production releases. + */ + private static final int MAX_ENABLED_LOG_LEVEL = VERBOSE; + + + private static Boolean sDebugLoggingEnabledForTests = null; + + /** + * Enable debug logging for unit tests. + */ + @VisibleForTesting + static void setDebugLoggingEnabledForTests(boolean enabled) { + sDebugLoggingEnabledForTests = Boolean.valueOf(enabled); + } + + /** + * Returns true if the build configuration prevents debug logging. + */ + @VisibleForTesting + static boolean buildPreventsDebugLogging() { + return MAX_ENABLED_LOG_LEVEL > VERBOSE; + } + + /** + * Returns a boolean indicating whether debug logging is enabled. + */ + private static boolean isDebugLoggingEnabled() { + if (buildPreventsDebugLogging()) { + return false; + } + if (sDebugLoggingEnabledForTests != null) { + return sDebugLoggingEnabledForTests.booleanValue(); + } + return Log.isLoggable(TAG, Log.DEBUG); + } + + /** + * Returns a String for the specified content provider uri. This will do + * sanitation of the uri to remove PII if debug logging is not enabled. + */ + public static String contentUriToString(Uri uri) { + + if (isDebugLoggingEnabled()) { + // Debug logging has been enabled, so log the uri as is + return uri.toString(); + } else { + // Debug logging is not enabled, we want to remove the email address from the uri. + List<String> pathSegments = uri.getPathSegments(); + + Uri.Builder builder = new Uri.Builder() + .scheme(uri.getScheme()) + .authority(uri.getAuthority()) + .query(uri.getQuery()) + .fragment(uri.getFragment()); + + // This assumes that the first path segment is the account + final String account = pathSegments.get(0); + + builder = builder.appendPath(String.valueOf(account.hashCode())); + for (int i = 1; i < pathSegments.size(); i++) { + builder.appendPath(pathSegments.get(i)); + } + return builder.toString(); + } + } + + /* TODO: what is the correct behavior for base case and the Gmail case? Seems like this + * belongs in override code in UnifiedGmail. + *Converts the specified set of labels to a string, and removes any PII as necessary + * public static String labelSetToString(Set<String> labelSet) { + if (isDebugLoggingEnabled() || labelSet == null) { + return labelSet != null ? labelSet.toString() : ""; + } else { + final StringBuilder builder = new StringBuilder("["); + int i = 0; + for(String label : labelSet) { + if (i > 0) { + builder.append(", "); + } + builder.append(sanitizeLabelName(label)); + i++; + } + builder.append(']'); + return builder.toString(); + } + } + + private static String sanitizeLabelName(String canonicalName) { + if (TextUtils.isEmpty(canonicalName)) { + return ""; + } + + if (Gmail.isSystemLabel(canonicalName)) { + return canonicalName; + } + + return USER_LABEL_PREFIX + String.valueOf(canonicalName.hashCode()); + }*/ + + /** + * Checks to see whether or not a log for the specified tag is loggable at the specified level. + */ + public static boolean isLoggable(String tag, int level) { + if (MAX_ENABLED_LOG_LEVEL > level) { + return false; + } + return Log.isLoggable(tag, level); + } + + /** + * Send a {@link #VERBOSE} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + */ + public static int v(String tag, String format, Object... args) { + if (isLoggable(tag, VERBOSE)) { + return Log.v(tag, String.format(format, args)); + } + return 0; + } + + /** + * Send a {@link #VERBOSE} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param tr An exception to log + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + */ + public static int v(String tag, Throwable tr, String format, Object... args) { + if (isLoggable(tag, VERBOSE)) { + return Log.v(tag, String.format(format, args), tr); + } + return 0; + } + + /** + * Send a {@link #DEBUG} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + */ + public static int d(String tag, String format, Object... args) { + if (isLoggable(tag, DEBUG)) { + return Log.d(tag, String.format(format, args)); + } + return 0; + } + + /** + * Send a {@link #DEBUG} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param tr An exception to log + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + */ + public static int d(String tag, Throwable tr, String format, Object... args) { + if (isLoggable(tag, DEBUG)) { + return Log.d(tag, String.format(format, args), tr); + } + return 0; + } + + /** + * Send a {@link #INFO} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + */ + public static int i(String tag, String format, Object... args) { + if (isLoggable(tag, INFO)) { + return Log.i(tag, String.format(format, args)); + } + return 0; + } + + /** + * Send a {@link #INFO} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param tr An exception to log + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + */ + public static int i(String tag, Throwable tr, String format, Object... args) { + if (isLoggable(tag, INFO)) { + return Log.i(tag, String.format(format, args), tr); + } + return 0; + } + + /** + * Send a {@link #WARN} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + */ + public static int w(String tag, String format, Object... args) { + if (isLoggable(tag, WARN)) { + return Log.w(tag, String.format(format, args)); + } + return 0; + } + + /** + * Send a {@link #WARN} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param tr An exception to log + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + */ + public static int w(String tag, Throwable tr, String format, Object... args) { + if (isLoggable(tag, WARN)) { + return Log.w(tag, String.format(format, args), tr); + } + return 0; + } + + /** + * Send a {@link #ERROR} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + */ + public static int e(String tag, String format, Object... args) { + if (isLoggable(tag, ERROR)) { + return Log.e(tag, String.format(format, args)); + } + return 0; + } + + /** + * Send a {@link #ERROR} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param tr An exception to log + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + */ + public static int e(String tag, Throwable tr, String format, Object... args) { + if (isLoggable(tag, ERROR)) { + return Log.e(tag, String.format(format, args), tr); + } + return 0; + } + + /** + * What a Terrible Failure: Report a condition that should never happen. + * The error will always be logged at level ASSERT with the call stack. + * Depending on system configuration, a report may be added to the + * {@link android.os.DropBoxManager} and/or the process may be terminated + * immediately with an error dialog. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + */ + public static int wtf(String tag, String format, Object... args) { + return Log.wtf(tag, String.format(format, args)); + } + + /** + * What a Terrible Failure: Report a condition that should never happen. + * The error will always be logged at level ASSERT with the call stack. + * Depending on system configuration, a report may be added to the + * {@link android.os.DropBoxManager} and/or the process may be terminated + * immediately with an error dialog. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param tr An exception to log + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + */ + public static int wtf(String tag, Throwable tr, String format, Object... args) { + return Log.wtf(tag, String.format(format, args), tr); + } +} diff --git a/src/com/android/email/utils/MimeType.java b/src/com/android/email/utils/MimeType.java new file mode 100644 index 000000000..b27c30038 --- /dev/null +++ b/src/com/android/email/utils/MimeType.java @@ -0,0 +1,173 @@ +/** + * Copyright (c) 2010, Google Inc. + * + * 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.utils; + +import android.text.TextUtils; +import android.webkit.MimeTypeMap; +import com.google.common.collect.ImmutableSet; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; + +import com.google.common.annotations.VisibleForTesting; + +import java.util.List; +import java.util.Set; + +/** + * Utilities for working with different content types within Gmail. + */ +public class MimeType { + public static final String ANDROID_ARCHIVE = "application/vnd.android.package-archive"; + private static final String TEXT_PLAIN = "text/plain"; + @VisibleForTesting + static final String GENERIC_MIMETYPE = "application/octet-stream"; + + @VisibleForTesting + static final String EML_ATTACHMENT_CONTENT_TYPE = "application/eml"; + private static final String NULL_ATTACHMENT_CONTENT_TYPE = "null"; + private static final Set<String> UNACCEPTABLE_ATTACHMENT_TYPES = ImmutableSet.of( + "application/zip", "application/x-gzip", "application/x-bzip2", + "application/x-compress", "application/x-compressed", "application/x-tar"); + + private static Set<String> sGviewSupportedTypes = ImmutableSet.of( + "application/pdf", + "application/vnd.ms-powerpoint", + "image/tiff", + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.openxmlformats-officedocument.presentationml.presentation"); + + /** + * Returns whether or not an attachment of the specified type is installable (e.g. an apk). + */ + public static boolean isInstallable(String type) { + return ANDROID_ARCHIVE.equals(type); + } + + /** + * Returns whether or not an attachment of the specified type is playable (e.g. a video). + */ + public static boolean isPlayable(String type) { + return type.startsWith("video/") || type.startsWith("audio/"); + } + + /** + * Returns whether or not an attachment of the specified type is viewable. + */ + public static boolean isViewable(Context context, Uri contentUri, String contentType) { + // The provider returns a contentType of "null" instead of null, when the + // content type is not known. Changing the provider to return null, + // breaks other areas that will need to be fixed in a later CL. + // Bug 2922948 has been written up to track this + if (contentType == null || contentType.length() == 0 || + NULL_ATTACHMENT_CONTENT_TYPE.equals(contentType)) { + return false; + } + + if (isBlocked(contentType)) { + return false; + } + + Intent mimetypeIntent = new Intent(Intent.ACTION_VIEW); + + mimetypeIntent.setDataAndType(contentUri, contentType); + PackageManager manager; + // We need to catch the exception to make CanvasConversationHeaderView + // test pass. Bug: http://b/issue?id=3470653. + try { + manager = context.getPackageManager(); + } catch (UnsupportedOperationException e) { + return false; + } + List<ResolveInfo> list = manager.queryIntentActivities(mimetypeIntent, + PackageManager.MATCH_DEFAULT_ONLY); + return list.size() > 0; + } + + /** + * @return whether the specified type is blocked. + */ + public static boolean isBlocked(String contentType) { + return UNACCEPTABLE_ATTACHMENT_TYPES.contains(contentType); + } + + /* TODO: what do we want to do about GSF keys for the unified app? + public static boolean isPreviewable(Context context, String contentType) { + final String supportedTypes = Gservices.getString( + context.getContentResolver(), GservicesKeys.GMAIL_GVIEW_SUPPORTED_TYPES); + if (supportedTypes != null) { + sGviewSupportedTypes = ImmutableSet.of(supportedTypes.split(",")); + } + return sGviewSupportedTypes.contains(contentType); + }*/ + + /** + * Extract and return filename's extension, converted to lower case, and not including the "." + * + * @return extension, or null if not found (or null/empty filename) + */ + private static String getFilenameExtension(String fileName) { + String extension = null; + if (!TextUtils.isEmpty(fileName)) { + int lastDot = fileName.lastIndexOf('.'); + if ((lastDot > 0) && (lastDot < fileName.length() - 1)) { + extension = fileName.substring(lastDot + 1).toLowerCase(); + } + } + return extension; + } + + + /** + * Returns the mime type of the attachment based on its name and + * original mime type. This is an workaround for bugs where Gmail + * server doesn't set content-type for certain types correctly. + * 1) EML files -> "application/eml". + * @param name name of the attachment. + * @param mimeType original mime type of the attachment. + * @return the inferred mime type of the attachment. + */ + public static String inferMimeType(String name, String mimeType) { + final String extension = getFilenameExtension(name); + if (TextUtils.isEmpty(extension)) { + // Attachment doesn't have extension, just return original mime + // type. + return mimeType; + } else { + final boolean isTextPlain = TEXT_PLAIN.equalsIgnoreCase(mimeType); + final boolean isGenericType = + isTextPlain || GENERIC_MIMETYPE.equalsIgnoreCase(mimeType); + + String type = null; + if (isGenericType || TextUtils.isEmpty(mimeType)) { + type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + } + if (!TextUtils.isEmpty(type)) { + return type; + } if (extension.equals("eml")) { + // Extension is ".eml", return mime type "application/eml" + return EML_ATTACHMENT_CONTENT_TYPE; + } else { + // Extension is not ".eml", just return original mime type. + return !TextUtils.isEmpty(mimeType) ? mimeType : GENERIC_MIMETYPE; + } + } + } +} diff --git a/src/com/android/email/utils/README b/src/com/android/email/utils/README deleted file mode 100644 index a3fcb7533..000000000 --- a/src/com/android/email/utils/README +++ /dev/null @@ -1 +0,0 @@ -Unified email directories
\ No newline at end of file diff --git a/src/com/android/email/utils/Utils.java b/src/com/android/email/utils/Utils.java new file mode 100644 index 000000000..fd6a5878e --- /dev/null +++ b/src/com/android/email/utils/Utils.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2011, Google Inc. + * + * 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.utils; + +public class Utils { + public static final String LOG_TAG = "Email"; + + public String getLogTag() { + return LOG_TAG; + } +} |