summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Sapperstein <asapperstein@google.com>2013-09-16 11:56:13 -0700
committerAndrew Sapperstein <asapperstein@google.com>2013-09-26 11:42:34 -0700
commit5c1692a5faeab220881a17a3427a8986ef874403 (patch)
treee18e2f908c449c2d21c2066cc093d4baed4a8e20 /src
parent09a4198253fe3a342a4b2868d27a29fe3686debc (diff)
downloadandroid_packages_apps_UnifiedEmail-5c1692a5faeab220881a17a3427a8986ef874403.tar.gz
android_packages_apps_UnifiedEmail-5c1692a5faeab220881a17a3427a8986ef874403.tar.bz2
android_packages_apps_UnifiedEmail-5c1692a5faeab220881a17a3427a8986ef874403.zip
Initial printing support.
Needs mocks and redlines and such. b/10712542 TODO: Logos for Email and Gmail. Attachment support. Use offscreen webview. Secure conversation view support. Change-Id: Iec37a9a46e506ccf12fbbb775c1b47b08a0d9724
Diffstat (limited to 'src')
-rw-r--r--src/com/android/mail/FormattedDateBuilder.java4
-rw-r--r--src/com/android/mail/browse/EmlMessageViewFragment.java6
-rw-r--r--src/com/android/mail/browse/MessageCursor.java4
-rw-r--r--src/com/android/mail/browse/MessageHeaderDetailsDialogFragment.java5
-rw-r--r--src/com/android/mail/browse/MessageHeaderView.java22
-rw-r--r--src/com/android/mail/print/HtmlPrintTemplates.java124
-rw-r--r--src/com/android/mail/print/Printer.java158
-rw-r--r--src/com/android/mail/ui/AbstractConversationViewFragment.java7
-rw-r--r--src/com/android/mail/ui/AbstractHtmlTemplates.java95
-rw-r--r--src/com/android/mail/ui/ConversationViewFragment.java17
-rw-r--r--src/com/android/mail/ui/HtmlConversationTemplates.java73
-rw-r--r--src/com/android/mail/ui/SecureConversationViewController.java4
-rw-r--r--src/com/android/mail/ui/SecureConversationViewControllerCallbacks.java2
-rw-r--r--src/com/android/mail/ui/SecureConversationViewFragment.java4
-rw-r--r--src/com/android/mail/utils/Utils.java29
15 files changed, 457 insertions, 97 deletions
diff --git a/src/com/android/mail/FormattedDateBuilder.java b/src/com/android/mail/FormattedDateBuilder.java
index e53b10c90..d7c17b27c 100644
--- a/src/com/android/mail/FormattedDateBuilder.java
+++ b/src/com/android/mail/FormattedDateBuilder.java
@@ -58,7 +58,7 @@ public class FormattedDateBuilder {
}
}
- private CharSequence formatLongDayAndDate(long when) {
+ public CharSequence formatLongDayAndDate(long when) {
sb.setLength(0);
DateUtils.formatDateRange(mContext, dateFormatter, when, when,
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY
@@ -66,7 +66,7 @@ public class FormattedDateBuilder {
return sb.toString();
}
- private CharSequence formatLongTime(long when) {
+ public CharSequence formatLongTime(long when) {
sb.setLength(0);
DateUtils.formatDateRange(mContext, dateFormatter, when, when,
DateUtils.FORMAT_SHOW_TIME);
diff --git a/src/com/android/mail/browse/EmlMessageViewFragment.java b/src/com/android/mail/browse/EmlMessageViewFragment.java
index ed357cf91..ab9c59c4b 100644
--- a/src/com/android/mail/browse/EmlMessageViewFragment.java
+++ b/src/com/android/mail/browse/EmlMessageViewFragment.java
@@ -19,6 +19,7 @@ package com.android.mail.browse;
import android.app.Fragment;
import android.app.LoaderManager;
+import android.content.Context;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
@@ -236,6 +237,11 @@ public class EmlMessageViewFragment extends Fragment
return mAccountUri;
}
+ @Override
+ public Context getContext() {
+ return getActivity().getApplicationContext();
+ }
+
// End SecureConversationViewControllerCallbacks
private class MessageLoadCallbacks
diff --git a/src/com/android/mail/browse/MessageCursor.java b/src/com/android/mail/browse/MessageCursor.java
index 5a4146a7b..8437d8748 100644
--- a/src/com/android/mail/browse/MessageCursor.java
+++ b/src/com/android/mail/browse/MessageCursor.java
@@ -73,6 +73,10 @@ public class MessageCursor extends ObjectCursor<ConversationMessage> {
return m;
}
+ public Conversation getConversation() {
+ return mController != null ? mController.getConversation() : null;
+ }
+
// Is the conversation starred?
public boolean isConversationStarred() {
int pos = -1;
diff --git a/src/com/android/mail/browse/MessageHeaderDetailsDialogFragment.java b/src/com/android/mail/browse/MessageHeaderDetailsDialogFragment.java
index 92a4f3cd1..62099fcfc 100644
--- a/src/com/android/mail/browse/MessageHeaderDetailsDialogFragment.java
+++ b/src/com/android/mail/browse/MessageHeaderDetailsDialogFragment.java
@@ -19,15 +19,16 @@ package com.android.mail.browse;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.app.DialogFragment;
import android.content.Context;
import android.os.Bundle;
-import android.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import com.android.mail.R;
import com.android.mail.providers.Account;
import com.android.mail.providers.Address;
+import com.android.mail.utils.Utils;
import java.util.HashMap;
import java.util.Map;
@@ -93,7 +94,7 @@ public class MessageHeaderDetailsDialogFragment extends DialogFragment {
private static void addAddressesToBundle(
Bundle addresses, Map<String, Address> addressCache, String[] emails) {
for (final String email : emails) {
- addresses.putParcelable(email, MessageHeaderView.getAddress(addressCache, email));
+ addresses.putParcelable(email, Utils.getAddress(addressCache, email));
}
}
diff --git a/src/com/android/mail/browse/MessageHeaderView.java b/src/com/android/mail/browse/MessageHeaderView.java
index ed47bf51d..1c3aa3351 100644
--- a/src/com/android/mail/browse/MessageHeaderView.java
+++ b/src/com/android/mail/browse/MessageHeaderView.java
@@ -495,23 +495,7 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener,
}
public Address getAddress(String emailStr) {
- return getAddress(mAddressCache, emailStr);
- }
-
- public static Address getAddress(Map<String, Address> cache, String emailStr) {
- Address addr = null;
- synchronized (cache) {
- if (cache != null) {
- addr = cache.get(emailStr);
- }
- if (addr == null) {
- addr = Address.getEmailAddress(emailStr);
- if (cache != null) {
- cache.put(emailStr, addr);
- }
- }
- }
- return addr;
+ return Utils.getAddress(mAddressCache, emailStr);
}
private void updateSpacerHeight() {
@@ -758,7 +742,7 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener,
final int len = Math.min(maxToCopy, rawAddrs.length);
boolean first = true;
for (int i = 0; i < len; i++) {
- final Address email = getAddress(mAddressCache, rawAddrs[i]);
+ final Address email = Utils.getAddress(mAddressCache, rawAddrs[i]);
final String emailAddress = email.getAddress();
final String name;
if (mMatcher != null && mMatcher.isVeiledAddress(emailAddress)) {
@@ -1326,7 +1310,7 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener,
}
final String[] formattedEmails = new String[emails.length];
for (int i = 0; i < emails.length; i++) {
- final Address email = getAddress(addressCache, emails[i]);
+ final Address email = Utils.getAddress(addressCache, emails[i]);
String name = email.getName();
final String address = email.getAddress();
// Check if the address here is a veiled address. If it is, we need to display an
diff --git a/src/com/android/mail/print/HtmlPrintTemplates.java b/src/com/android/mail/print/HtmlPrintTemplates.java
new file mode 100644
index 000000000..27dfba352
--- /dev/null
+++ b/src/com/android/mail/print/HtmlPrintTemplates.java
@@ -0,0 +1,124 @@
+/**
+ * Copyright (C) 2013 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.print;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import com.android.mail.R;
+import com.android.mail.ui.AbstractHtmlTemplates;
+import com.android.mail.utils.LogTag;
+import com.android.mail.utils.LogUtils;
+
+/**
+ * Renders data into very simple string-substitution HTML templates for printing conversations.
+ */
+public class HtmlPrintTemplates extends AbstractHtmlTemplates {
+
+ private static final String TAG = LogTag.getLogTag();
+
+ private final String mConversationUpper;
+ private final String mMessage;
+ private final String mConversationLower;
+ private final String mConversationLowerNoJs;
+
+ public HtmlPrintTemplates(Context context) {
+ super(context);
+
+ mConversationUpper = readTemplate(R.raw.template_print_conversation_upper);
+ mMessage = readTemplate(R.raw.template_print_message);
+ mConversationLower = readTemplate(R.raw.template_print_conversation_lower);
+ mConversationLowerNoJs = readTemplate(R.raw.template_print_conversation_lower_no_js);
+ }
+
+ /**
+ * Start building the html for a printed conversation. Can only be called once
+ * until {@link #endPrintConversation()} or {@link #endPrintConversationNoJavascript()}
+ * is called.
+ */
+ public void startPrintConversation(String accountName, String accountAddress,
+ String subject, int numMessages) {
+ if (mInProgress) {
+ throw new IllegalStateException("Should not call startPrintConversation twice");
+ }
+
+ reset();
+
+ final Resources res = mContext.getResources();
+ final String numMessageString = res.getQuantityString(
+ R.plurals.num_messages, numMessages, numMessages);
+ append(mConversationUpper, mContext.getString(R.string.app_name),
+ accountName == null ? "" : accountName,
+ accountAddress, subject, numMessageString);
+
+ mInProgress = true;
+ }
+
+ /**
+ * Add a message to the html for this printed conversation.
+ */
+ public void appendMessage(String senderName, String senderAddress, String date,
+ String recipients, String bodyHtml, String attachments) {
+ append(mMessage, senderName, senderAddress, date, recipients, bodyHtml, attachments);
+ }
+
+ /**
+ * Adds the end of the printed conversation to the html. NOTE: this method
+ * includes JavaScript. If you need a version without JavaScript,
+ * use {@link #endPrintConversationNoJavascript()}.<br/><br/>
+ *
+ * One example where we use JavaScript is to hide quoted text.
+ *
+ * @return a {@link String} containing the html for the conversation.
+ */
+ public String endPrintConversation() {
+ if (!mInProgress) {
+ throw new IllegalStateException("must call startConversation first");
+ }
+
+ append(mConversationLower, mContext.getString(R.string.quoted_text_hidden_print));
+
+ mInProgress = false;
+
+ LogUtils.d(TAG, "rendered conversation of %d bytes, buffer capacity=%d",
+ mBuilder.length() << 1, mBuilder.capacity() << 1);
+
+ return emit();
+ }
+
+ /**
+ * Adds the end of the printed conversation to the html. NOTE: this method
+ * does not include any JavaScript. If you need a version with JavaScript,
+ * use {@link #endPrintConversation()}.
+ * @return a {@link String} containing the html for the conversation.
+ */
+ public String endPrintConversationNoJavascript() {
+ if (!mInProgress) {
+ throw new IllegalStateException("must call startConversation first");
+ }
+
+ append(mConversationLowerNoJs);
+
+ mInProgress = false;
+
+ LogUtils.d(TAG, "rendered conversation of %d bytes, buffer capacity=%d",
+ mBuilder.length() << 1, mBuilder.capacity() << 1);
+
+ return emit();
+ }
+}
diff --git a/src/com/android/mail/print/Printer.java b/src/com/android/mail/print/Printer.java
new file mode 100644
index 000000000..7744c37ca
--- /dev/null
+++ b/src/com/android/mail/print/Printer.java
@@ -0,0 +1,158 @@
+/**
+ * Copyright (C) 2013 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.print;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.text.TextUtils;
+
+import com.android.mail.FormattedDateBuilder;
+import com.android.mail.R;
+import com.android.mail.browse.ConversationMessage;
+import com.android.mail.browse.MessageCursor;
+import com.android.mail.providers.Account;
+import com.android.mail.providers.Address;
+import com.android.mail.providers.Conversation;
+import com.android.mail.providers.UIProvider;
+import com.android.mail.utils.Utils;
+
+import java.util.Map;
+
+/**
+ * Static class that provides a {@link #print} function to build a print html document.
+ */
+public class Printer {
+ private static final String DIV_START = "<div>";
+ private static final String REPLY_TO_DIV_START = "<div class=\"replyto\">";
+ private static final String DIV_END = "</div>";
+
+ /**
+ * Builds an html document that is suitable for printing and returns it as a {@link String}.
+ */
+ public static String print(Context context, Account account,
+ MessageCursor cursor, Map<String, Address> addressCache, boolean useJavascript) {
+ final HtmlPrintTemplates templates = new HtmlPrintTemplates(context);
+ final FormattedDateBuilder dateBuilder = new FormattedDateBuilder(context);
+
+ if (!cursor.moveToFirst()) {
+ throw new IllegalStateException("trying to print without a conversation");
+ }
+
+ // TODO - remove account name(not account.name which is email address) or get it somehow
+ final Conversation conversation = cursor.getConversation();
+ templates.startPrintConversation("", account.name,
+ conversation.subject, conversation.getNumMessages());
+
+ // for each message in the conversation, add message html
+ final Resources res = context.getResources();
+ do {
+ final ConversationMessage message = cursor.getMessage();
+ final Address fromAddress = Utils.getAddress(addressCache, message.getFrom());
+ final long when = message.dateReceivedMs;
+ final String date = res.getString(R.string.date_message_received_print,
+ dateBuilder.formatLongDayAndDate(when), dateBuilder.formatLongTime(when));
+
+
+ templates.appendMessage(fromAddress.getName(), fromAddress.getAddress(), date,
+ renderRecipients(res, addressCache, message), message.getBodyAsHtml(),
+ ""); // TODO - attachment html
+ } while (cursor.moveToNext());
+
+ // only include JavaScript if specifically requested
+ return useJavascript ?
+ templates.endPrintConversation() : templates.endPrintConversationNoJavascript();
+ }
+
+ /**
+ * Builds html for the message header. Specifically, the (optional) lists of
+ * reply-to, to, cc, and bcc.
+ */
+ private static String renderRecipients(Resources res, Map<String, Address> addressCache,
+ ConversationMessage message) {
+ final StringBuilder recipients = new StringBuilder();
+
+ // reply-to
+ final String replyTo = renderEmailList(res, message.getReplyToAddresses(), addressCache);
+ buildEmailDiv(res, recipients, replyTo, REPLY_TO_DIV_START, DIV_END,
+ R.string.replyto_heading);
+
+ // to
+ // To has special semantics since the message can be a draft.
+ // If it is a draft and there are no to addresses, we just print "Draft".
+ // If it is a draft and there are to addresses, we print "Draft To: "
+ // If not a draft, we just use "To: ".
+ final boolean isDraft = message.draftType != UIProvider.DraftType.NOT_A_DRAFT;
+ final String to = renderEmailList(res, message.getToAddresses(), addressCache);
+ if (isDraft && to == null) {
+ recipients.append(DIV_START).append(res.getString(R.string.draft_heading))
+ .append(DIV_END);
+ } else {
+ buildEmailDiv(res, recipients, to, DIV_START, DIV_END,
+ isDraft ? R.string.draft_to_heading : R.string.to_heading);
+ }
+
+ // cc
+ final String cc = renderEmailList(res, message.getCcAddresses(), addressCache);
+ buildEmailDiv(res, recipients, cc, DIV_START, DIV_END,
+ R.string.cc_heading);
+
+ // bcc
+ final String bcc = renderEmailList(res, message.getBccAddresses(), addressCache);
+ buildEmailDiv(res, recipients, bcc, DIV_START, DIV_END,
+ R.string.bcc_heading);
+
+ return recipients.toString();
+ }
+
+ /**
+ * Appends an html div containing a list of emails based on the passed in data.
+ */
+ private static void buildEmailDiv(Resources res, StringBuilder recipients, String emailList,
+ String divStart, String divEnd, int headingId) {
+ if (emailList != null) {
+ recipients.append(divStart).append(res.getString(headingId))
+ .append(emailList).append(divEnd);
+ }
+ }
+
+ /**
+ * Builds and returns a list of comma-separated emails of the form "Name &lt;email&gt;".
+ * If the email does not contain a name, "email" is used instead.
+ */
+ private static String renderEmailList(Resources resources, String[] emails,
+ Map<String, Address> addressCache) {
+ if (emails == null || emails.length == 0) {
+ return null;
+ }
+ final String[] formattedEmails = new String[emails.length];
+ for (int i = 0; i < emails.length; i++) {
+ final Address email = Utils.getAddress(addressCache, emails[i]);
+ final String name = email.getName();
+ final String address = email.getAddress();
+
+ if (TextUtils.isEmpty(name)) {
+ formattedEmails[i] = address;
+ } else {
+ formattedEmails[i] = resources.getString(R.string.address_print_display_format,
+ name, address);
+ }
+ }
+
+ return TextUtils.join(", ", formattedEmails);
+ }
+}
diff --git a/src/com/android/mail/ui/AbstractConversationViewFragment.java b/src/com/android/mail/ui/AbstractConversationViewFragment.java
index 00234037b..473d1588d 100644
--- a/src/com/android/mail/ui/AbstractConversationViewFragment.java
+++ b/src/com/android/mail/ui/AbstractConversationViewFragment.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
@@ -326,6 +327,9 @@ public abstract class AbstractConversationViewFragment extends Fragment implemen
} else if (itemId == R.id.show_original) {
showUntransformedConversation();
handled = true;
+ } else if (itemId == R.id.print) {
+ printConversation();
+ handled = true;
}
return handled;
}
@@ -335,6 +339,7 @@ public abstract class AbstractConversationViewFragment extends Fragment implemen
// Only show option if we support message transforms and message has been transformed.
Utils.setMenuItemVisibility(menu, R.id.show_original, supportsMessageTransforms() &&
mHasConversationBeenTransformed && !mHasConversationTransformBeenReverted);
+ Utils.setMenuItemVisibility(menu, R.id.print, Utils.isRunningKitkatOrLater());
}
abstract boolean supportsMessageTransforms();
@@ -634,4 +639,6 @@ public abstract class AbstractConversationViewFragment extends Fragment implemen
return (mAccount.enableMessageTransforms > 0) &&
!mHasConversationTransformBeenReverted;
}
+
+ protected abstract void printConversation();
}
diff --git a/src/com/android/mail/ui/AbstractHtmlTemplates.java b/src/com/android/mail/ui/AbstractHtmlTemplates.java
new file mode 100644
index 000000000..25c3bc50b
--- /dev/null
+++ b/src/com/android/mail/ui/AbstractHtmlTemplates.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2013 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.ui;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Formatter;
+
+/**
+ * Abstract class to support common functionality for both
+ * {@link com.android.mail.ui.HtmlConversationTemplates} and
+ * {@link com.android.mail.print.HtmlPrintTemplates}.
+ *
+ * Renders data into very simple string-substitution HTML templates.
+ *
+ * Templates should be UTF-8 encoded HTML with '%s' placeholders to be substituted upon render.
+ * Plain-jane string substitution with '%s' is slightly faster than typed substitution.
+ */
+public abstract class AbstractHtmlTemplates {
+ // TODO: refine. too expensive to iterate over cursor and pre-calculate total. so either
+ // estimate it, or defer assembly until the end when size is known (deferring increases
+ // working set size vs. estimation but is exact).
+ private static final int BUFFER_SIZE_CHARS = 64 * 1024;
+
+ protected Context mContext;
+ protected Formatter mFormatter;
+ protected StringBuilder mBuilder;
+ protected boolean mInProgress = false;
+
+ public AbstractHtmlTemplates(Context context) {
+ mContext = context;
+ }
+
+ public String emit() {
+ final String out = mFormatter.toString();
+ // release the builder memory ASAP
+ mFormatter = null;
+ mBuilder = null;
+ return out;
+ }
+
+ public void reset() {
+ mBuilder = new StringBuilder(BUFFER_SIZE_CHARS);
+ mFormatter = new Formatter(mBuilder, null /* no localization */);
+ }
+
+ protected String readTemplate(int id) throws Resources.NotFoundException {
+ final StringBuilder out = new StringBuilder();
+ InputStreamReader in = null;
+ try {
+ try {
+ in = new InputStreamReader(
+ mContext.getResources().openRawResource(id), "UTF-8");
+ final char[] buf = new char[4096];
+ int chars;
+
+ while ((chars=in.read(buf)) > 0) {
+ out.append(buf, 0, chars);
+ }
+
+ return out.toString();
+
+ } finally {
+ if (in != null) {
+ in.close();
+ }
+ }
+ } catch (IOException e) {
+ throw new Resources.NotFoundException("Unable to open template id="
+ + Integer.toHexString(id) + " exception=" + e.getMessage());
+ }
+ }
+
+ protected void append(String template, Object... args) {
+ mFormatter.format(template, args);
+ }
+}
diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java
index e850d4896..51484b195 100644
--- a/src/com/android/mail/ui/ConversationViewFragment.java
+++ b/src/com/android/mail/ui/ConversationViewFragment.java
@@ -27,6 +27,9 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
+import android.print.PrintAttributes;
+import android.print.PrintJob;
+import android.print.PrintManager;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.ScaleGestureDetector;
@@ -63,6 +66,7 @@ import com.android.mail.browse.ScrollIndicatorsView;
import com.android.mail.browse.SuperCollapsedBlock;
import com.android.mail.browse.WebViewContextMenu;
import com.android.mail.content.ObjectCursor;
+import com.android.mail.print.Printer;
import com.android.mail.providers.Account;
import com.android.mail.providers.Address;
import com.android.mail.providers.Conversation;
@@ -1574,4 +1578,17 @@ public class ConversationViewFragment extends AbstractConversationViewFragment i
}
}
+
+ protected void printConversation() {
+ // TODO - offscreen webview stuff so that we don't clobber
+ final String convHtml =
+ Printer.print(getContext(), mAccount, getMessageCursor(),
+ mAddressCache, true /* userJavascript */);
+ mWebView.loadDataWithBaseURL(mBaseUri, convHtml, "text/html", "utf-8", null);
+ final PrintManager printManager =
+ (PrintManager) getContext().getSystemService(Context.PRINT_SERVICE);
+ printManager.print(getConversation().subject,
+ mWebView.createPrintDocumentAdapter(),
+ new PrintAttributes.Builder().build());
+ }
}
diff --git a/src/com/android/mail/ui/HtmlConversationTemplates.java b/src/com/android/mail/ui/HtmlConversationTemplates.java
index d368454d3..104d0c03e 100644
--- a/src/com/android/mail/ui/HtmlConversationTemplates.java
+++ b/src/com/android/mail/ui/HtmlConversationTemplates.java
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2012 Google Inc.
* Licensed to The Android Open Source Project.
*
@@ -18,7 +18,6 @@
package com.android.mail.ui;
import android.content.Context;
-import android.content.res.Resources.NotFoundException;
import com.android.mail.R;
import com.android.mail.utils.LogTag;
@@ -26,30 +25,17 @@ import com.android.mail.utils.LogUtils;
import com.android.mail.utils.Utils;
import com.google.common.annotations.VisibleForTesting;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.Formatter;
import java.util.regex.Pattern;
/**
* Renders data into very simple string-substitution HTML templates for conversation view.
- *
- * Templates should be UTF-8 encoded HTML with '%s' placeholders to be substituted upon render.
- * Plain-jane string substitution with '%s' is slightly faster than typed substitution.
- *
*/
-public class HtmlConversationTemplates {
+public class HtmlConversationTemplates extends AbstractHtmlTemplates {
/**
* Prefix applied to a message id for use as a div id
*/
public static final String MESSAGE_PREFIX = "m";
- public static final int MESSAGE_PREFIX_LENGTH = MESSAGE_PREFIX.length();
-
- // TODO: refine. too expensive to iterate over cursor and pre-calculate total. so either
- // estimate it, or defer assembly until the end when size is known (deferring increases
- // working set size vs. estimation but is exact).
- private static final int BUFFER_SIZE_CHARS = 64 * 1024;
private static final String TAG = LogTag.getLogTag();
@@ -92,13 +78,8 @@ public class HtmlConversationTemplates {
private static String sConversationUpper;
private static String sConversationLower;
- private Context mContext;
- private Formatter mFormatter;
- private StringBuilder mBuilder;
- private boolean mInProgress = false;
-
public HtmlConversationTemplates(Context context) {
- mContext = context;
+ super(context);
// The templates are small (~2KB total in ICS MR2), so it's okay to load them once and keep
// them in memory.
@@ -178,7 +159,8 @@ public class HtmlConversationTemplates {
public void startConversation(int sideMargin, int conversationHeaderHeight) {
if (mInProgress) {
- throw new IllegalStateException("must call startConversation first");
+ throw new IllegalStateException(
+ "Should not call start conversation until end conversation has been called");
}
reset();
@@ -209,49 +191,4 @@ public class HtmlConversationTemplates {
return emit();
}
-
- public String emit() {
- String out = mFormatter.toString();
- // release the builder memory ASAP
- mFormatter = null;
- mBuilder = null;
- return out;
- }
-
- public void reset() {
- mBuilder = new StringBuilder(BUFFER_SIZE_CHARS);
- mFormatter = new Formatter(mBuilder, null /* no localization */);
- }
-
- private String readTemplate(int id) throws NotFoundException {
- StringBuilder out = new StringBuilder();
- InputStreamReader in = null;
- try {
- try {
- in = new InputStreamReader(
- mContext.getResources().openRawResource(id), "UTF-8");
- char[] buf = new char[4096];
- int chars;
-
- while ((chars=in.read(buf)) > 0) {
- out.append(buf, 0, chars);
- }
-
- return out.toString();
-
- } finally {
- if (in != null) {
- in.close();
- }
- }
- } catch (IOException e) {
- throw new NotFoundException("Unable to open template id=" + Integer.toHexString(id)
- + " exception=" + e.getMessage());
- }
- }
-
- private void append(String template, Object... args) {
- mFormatter.format(template, args);
- }
-
}
diff --git a/src/com/android/mail/ui/SecureConversationViewController.java b/src/com/android/mail/ui/SecureConversationViewController.java
index 1f9b13e87..4a89bd292 100644
--- a/src/com/android/mail/ui/SecureConversationViewController.java
+++ b/src/com/android/mail/ui/SecureConversationViewController.java
@@ -183,6 +183,10 @@ public class SecureConversationViewController implements
mConversationHeaderView.setSubject(subject);
}
+ public void printConversation() {
+ // TODO - implement this
+ }
+
// Start MessageHeaderViewCallbacks implementations
@Override
diff --git a/src/com/android/mail/ui/SecureConversationViewControllerCallbacks.java b/src/com/android/mail/ui/SecureConversationViewControllerCallbacks.java
index 2e4f5d97e..0284a494d 100644
--- a/src/com/android/mail/ui/SecureConversationViewControllerCallbacks.java
+++ b/src/com/android/mail/ui/SecureConversationViewControllerCallbacks.java
@@ -18,6 +18,7 @@
package com.android.mail.ui;
import android.app.Fragment;
+import android.content.Context;
import android.net.Uri;
import android.os.Handler;
@@ -46,4 +47,5 @@ public interface SecureConversationViewControllerCallbacks {
public String getBaseUri();
public boolean isViewOnlyMode();
public Uri getAccountUri();
+ public Context getContext();
}
diff --git a/src/com/android/mail/ui/SecureConversationViewFragment.java b/src/com/android/mail/ui/SecureConversationViewFragment.java
index 14937b788..729a77480 100644
--- a/src/com/android/mail/ui/SecureConversationViewFragment.java
+++ b/src/com/android/mail/ui/SecureConversationViewFragment.java
@@ -261,4 +261,8 @@ public class SecureConversationViewFragment extends AbstractConversationViewFrag
public boolean supportsMessageTransforms() {
return false;
}
+
+ protected void printConversation() {
+ mViewController.printConversation();
+ }
}
diff --git a/src/com/android/mail/utils/Utils.java b/src/com/android/mail/utils/Utils.java
index 8286befdc..e6a0312d3 100644
--- a/src/com/android/mail/utils/Utils.java
+++ b/src/com/android/mail/utils/Utils.java
@@ -16,6 +16,7 @@
package com.android.mail.utils;
+import com.android.mail.providers.Address;
import com.google.android.mail.common.html.parser.HtmlDocument;
import com.google.android.mail.common.html.parser.HtmlParser;
import com.google.android.mail.common.html.parser.HtmlTree;
@@ -726,7 +727,7 @@ public class Utils {
/**
* Create an intent to show a conversation.
* @param conversation Conversation to open.
- * @param folder
+ * @param folderUri
* @param account
* @return
*/
@@ -749,7 +750,7 @@ public class Utils {
/**
* Create an intent to open a folder.
*
- * @param folder Folder to open.
+ * @param folderUri Folder to open.
* @param account
* @return
*/
@@ -1043,8 +1044,8 @@ public class Utils {
*
* @param type MIME data type to normalize
* @return normalized MIME data type, or null if the input was null
- * @see {@link #setType}
- * @see {@link #setTypeAndNormalize}
+ * @see {@link android.content.Intent#setType}
+ * @see {@link android.content.Intent#setTypeAndNormalize}
*/
public static String normalizeMimeType(String type) {
if (type == null) {
@@ -1061,7 +1062,7 @@ public class Utils {
}
/**
- * (copied from {@link Uri#normalize()} for pre-J)
+ * (copied from {@link android.net.Uri#normalizeScheme()} for pre-J)
*
* Return a normalized representation of this Uri.
*
@@ -1083,7 +1084,7 @@ public class Utils {
*
* @return normalized Uri (never null)
* @see {@link android.content.Intent#setData}
- * @see {@link #setNormalizedData}
+ * @see {@link android.content.Intent#setNormalizedData}
*/
public static Uri normalizeUri(Uri uri) {
String scheme = uri.getScheme();
@@ -1429,4 +1430,20 @@ public class Utils {
}
}
+
+ public static Address getAddress(Map<String, Address> cache, String emailStr) {
+ Address addr = null;
+ synchronized (cache) {
+ if (cache != null) {
+ addr = cache.get(emailStr);
+ }
+ if (addr == null) {
+ addr = Address.getEmailAddress(emailStr);
+ if (cache != null) {
+ cache.put(emailStr, addr);
+ }
+ }
+ }
+ return addr;
+ }
}