Fixed printing on some devices

pull/153/head
M66B 6 years ago
parent 6b2591ce32
commit ca0765bb9d

@ -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<String[]>() {
@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()) {

@ -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<AdapterMessage.ViewHold
private AdapterImage adapterImage;
private TwoStateOwner cowner = new TwoStateOwner(owner, "AdapterMessage");
private WebView printWebView = null;
ViewHolder(final View itemView) {
super(itemView);
@ -1477,8 +1468,15 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
protected OriginalMessage 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();
OriginalMessage original = new OriginalMessage();
original.html = HtmlHelper.removeTracking(context, getHtmlEmbedded(id));
original.html = Helper.readText(message.getFile(context));
original.html = HtmlHelper.getHtmlEmbedded(context, id, original.html);
original.html = HtmlHelper.removeTracking(context, original.html);
Document doc = Jsoup.parse(original.html);
for (Element img : doc.select("img")) {
@ -2381,7 +2379,10 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
private void onMenuPrint(final ActionData data) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (prefs.getBoolean("print_html_confirmed", false)) {
onMenuPrintConfirmed(data);
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(
new Intent(ActivityView.ACTION_PRINT)
.putExtra("id", data.message.id));
return;
}
@ -2398,64 +2399,16 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
public void onClick(DialogInterface dialog, int which) {
if (cbNotAgain.isChecked())
prefs.edit().putBoolean("print_html_confirmed", true).apply();
onMenuPrintConfirmed(data);
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(
new Intent(ActivityView.ACTION_PRINT)
.putExtra("id", data.message.id));
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
private void onMenuPrintConfirmed(final ActionData data) {
Bundle args = new Bundle();
args.putLong("id", data.message.id);
new SimpleTask<String>() {
@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<AdapterMessage.ViewHold
}.execute(context, owner, new Bundle(), "message:answer");
}
private String getHtmlEmbedded(long id) throws IOException {
DB db = DB.getInstance(context);
EntityMessage message = db.message().getMessage(id);
if (message == null)
throw new FileNotFoundException();
String html = Helper.readText(message.getFile(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();
}
ItemDetailsLookup.ItemDetails<Long> getItemDetails(@NonNull MotionEvent motionEvent) {
return new ItemDetailsMessage(this);
}

@ -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--;

Loading…
Cancel
Save