From ca0765bb9db64dadc7ecae0968eb411cfc21e5ff Mon Sep 17 00:00:00 2001 From: M66B Date: Sat, 6 Apr 2019 11:05:33 +0200 Subject: [PATCH] Fixed printing on some devices --- .../java/eu/faircode/email/ActivityView.java | 74 +++++++++++ .../eu/faircode/email/AdapterMessage.java | 115 +++--------------- .../java/eu/faircode/email/HtmlHelper.java | 37 +++++- 3 files changed, 125 insertions(+), 101 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java index 6646a3c8dd..573be64593 100644 --- a/app/src/main/java/eu/faircode/email/ActivityView.java +++ b/app/src/main/java/eu/faircode/email/ActivityView.java @@ -33,11 +33,17 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.ParcelFileDescriptor; +import android.print.PrintAttributes; +import android.print.PrintDocumentAdapter; +import android.print.PrintManager; import android.text.TextUtils; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; import android.widget.AdapterView; import android.widget.ListView; import android.widget.Toast; @@ -105,6 +111,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB private long message = -1; private long attachment = -1; + private WebView printWebView = null; private OpenPgpServiceConnection pgpService; private NumberFormat nf = NumberFormat.getNumberInstance(); @@ -133,6 +140,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB static final String ACTION_EDIT_RULE = BuildConfig.APPLICATION_ID + ".EDIT_RULE"; static final String ACTION_STORE_ATTACHMENT = BuildConfig.APPLICATION_ID + ".STORE_ATTACHMENT"; static final String ACTION_STORE_ATTACHMENTS = BuildConfig.APPLICATION_ID + ".STORE_ATTACHMENTS"; + static final String ACTION_PRINT = BuildConfig.APPLICATION_ID + ".PRINT"; static final String ACTION_DECRYPT = BuildConfig.APPLICATION_ID + ".DECRYPT"; static final String ACTION_SHOW_PRO = BuildConfig.APPLICATION_ID + ".SHOW_PRO"; @@ -575,6 +583,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB iff.addAction(ACTION_EDIT_RULE); iff.addAction(ACTION_STORE_ATTACHMENT); iff.addAction(ACTION_STORE_ATTACHMENTS); + iff.addAction(ACTION_PRINT); iff.addAction(ACTION_DECRYPT); iff.addAction(ACTION_SHOW_PRO); lbm.registerReceiver(receiver, iff); @@ -1140,6 +1149,8 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB onStoreAttachment(intent); else if (ACTION_STORE_ATTACHMENTS.equals(action)) onStoreAttachments(intent); + else if (ACTION_PRINT.equals(action)) + onPrint(intent); else if (ACTION_DECRYPT.equals(action)) onDecrypt(intent); else if (ACTION_SHOW_PRO.equals(action)) @@ -1270,6 +1281,69 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB startActivityForResult(Helper.getChooser(this, tree), REQUEST_ATTACHMENTS); } + private void onPrint(Intent intent) { + long id = intent.getLongExtra("id", -1); + + Bundle args = new Bundle(); + args.putLong("id", id); + + new SimpleTask() { + @Override + protected String[] onExecute(Context context, Bundle args) throws IOException { + long id = args.getLong("id"); + + DB db = DB.getInstance(context); + EntityMessage message = db.message().getMessage(id); + if (message == null) + throw new FileNotFoundException(); + + String html = Helper.readText(message.getFile(context)); + html = HtmlHelper.getHtmlEmbedded(context, id, html); + html = HtmlHelper.removeTracking(context, html); + + return new String[]{message.subject, html}; + } + + @Override + protected void onExecuted(Bundle args, final String[] data) { + // https://developer.android.com/training/printing/html-docs.html + printWebView = new WebView(ActivityView.this); + WebSettings settings = printWebView.getSettings(); + settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + settings.setAllowFileAccess(false); + + printWebView.setWebViewClient(new WebViewClient() { + public boolean shouldOverrideUrlLoading(WebView view, String url) { + return false; + } + + @Override + public void onPageFinished(WebView view, String url) { + try { + PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE); + String jobName = getString(R.string.app_name); + if (!TextUtils.isEmpty(data[0])) + jobName += " - " + data[0]; + PrintDocumentAdapter adapter = printWebView.createPrintDocumentAdapter(jobName); + printManager.print(jobName, adapter, new PrintAttributes.Builder().build()); + } catch (Throwable ex) { + Log.e(ex); + } finally { + printWebView = null; + } + } + }); + + printWebView.loadDataWithBaseURL("email://", data[1], "text/html", "UTF-8", null); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(ActivityView.this, ActivityView.this, ex); + } + }.execute(ActivityView.this, args, "message:print"); + } + private void onDecrypt(Intent intent) { if (Helper.isPro(this)) { if (pgpService.isBound()) { diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index d547ab1482..b198effd1b 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -41,9 +41,6 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.print.PrintAttributes; -import android.print.PrintDocumentAdapter; -import android.print.PrintManager; import android.provider.ContactsContract; import android.provider.Settings; import android.text.Html; @@ -58,7 +55,6 @@ import android.text.style.ImageSpan; import android.text.style.QuoteSpan; import android.text.style.StyleSpan; import android.text.style.URLSpan; -import android.util.Base64; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.Menu; @@ -87,12 +83,9 @@ import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; -import java.io.BufferedInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.text.Collator; import java.text.DateFormat; @@ -252,8 +245,6 @@ public class AdapterMessage extends RecyclerView.Adapter() { - @Override - protected String onExecute(Context context, Bundle args) throws IOException { - long id = args.getLong("id"); - return HtmlHelper.removeTracking(context, getHtmlEmbedded(id)); - } - - @Override - protected void onExecuted(Bundle args, String html) { - // https://developer.android.com/training/printing/html-docs.html - printWebView = new WebView(context); - WebSettings settings = printWebView.getSettings(); - settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); - settings.setAllowFileAccess(false); - - printWebView.setWebViewClient(new WebViewClient() { - public boolean shouldOverrideUrlLoading(WebView view, String url) { - return false; - } - - @Override - public void onPageFinished(WebView view, String url) { - try { - PrintManager printManager = (PrintManager) context.getSystemService(Context.PRINT_SERVICE); - String jobName = context.getString(R.string.app_name); - if (!TextUtils.isEmpty(data.message.subject)) - jobName += " - " + data.message.subject; - PrintDocumentAdapter adapter = printWebView.createPrintDocumentAdapter(jobName); - printManager.print(jobName, adapter, new PrintAttributes.Builder().build()); - } catch (Throwable ex) { - Log.e(ex); - } finally { - printWebView = null; - } - } - }); - - printWebView.loadDataWithBaseURL("email://", html, "text/html", "UTF-8", null); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(context, owner, ex); - } - }.execute(context, owner, args, "message:print"); - } - private void onMenuShowHeaders(ActionData data) { boolean show_headers = !properties.getValue("headers", data.message.id); properties.setValue("headers", data.message.id, show_headers); @@ -2950,42 +2903,6 @@ public class AdapterMessage extends RecyclerView.Adapter'; - EntityAttachment attachment = db.attachment().getAttachment(id, cid); - if (attachment != null && attachment.available) { - File file = attachment.getFile(context); - try (InputStream is = new BufferedInputStream(new FileInputStream(file))) { - byte[] bytes = new byte[(int) file.length()]; - if (is.read(bytes) != bytes.length) - throw new IOException("length"); - - StringBuilder sb = new StringBuilder(); - sb.append("data:"); - sb.append(attachment.type); - sb.append(";base64,"); - sb.append(Base64.encodeToString(bytes, Base64.DEFAULT)); - - img.attr("src", sb.toString()); - } - } - } - } - - return doc.html(); - } - ItemDetailsLookup.ItemDetails getItemDetails(@NonNull MotionEvent motionEvent) { return new ItemDetailsMessage(this); } diff --git a/app/src/main/java/eu/faircode/email/HtmlHelper.java b/app/src/main/java/eu/faircode/email/HtmlHelper.java index fa18b0c2a3..3a81d2ae1b 100644 --- a/app/src/main/java/eu/faircode/email/HtmlHelper.java +++ b/app/src/main/java/eu/faircode/email/HtmlHelper.java @@ -42,8 +42,10 @@ import org.jsoup.safety.Whitelist; import org.jsoup.select.NodeTraversor; import org.jsoup.select.NodeVisitor; +import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -547,7 +549,38 @@ public class HtmlHelper { return sb.toString(); } - static boolean isTrackingPixel(Element img) { + static String getHtmlEmbedded(Context context, long id, String html) throws IOException { + DB db = DB.getInstance(context); + + Document doc = Jsoup.parse(html); + for (Element img : doc.select("img")) { + String src = img.attr("src"); + if (src.startsWith("cid:")) { + String cid = '<' + src.substring(4) + '>'; + EntityAttachment attachment = db.attachment().getAttachment(id, cid); + if (attachment != null && attachment.available) { + File file = attachment.getFile(context); + try (InputStream is = new BufferedInputStream(new FileInputStream(file))) { + byte[] bytes = new byte[(int) file.length()]; + if (is.read(bytes) != bytes.length) + throw new IOException("length"); + + StringBuilder sb = new StringBuilder(); + sb.append("data:"); + sb.append(attachment.type); + sb.append(";base64,"); + sb.append(Base64.encodeToString(bytes, Base64.DEFAULT)); + + img.attr("src", sb.toString()); + } + } + } + } + + return doc.html(); + } + + private static boolean isTrackingPixel(Element img) { String src = img.attr("src"); String width = img.attr("width").trim(); String height = img.attr("height").trim(); @@ -564,7 +597,7 @@ public class HtmlHelper { } } - static void trimEnd(StringBuilder sb) { + private static void trimEnd(StringBuilder sb) { int length = sb.length(); while (length > 0 && sb.charAt(length - 1) == ' ') length--;