mirror of
https://github.com/M66B/FairEmail.git
synced 2026-01-03 03:19:24 +01:00
Fixed printing on some devices
This commit is contained in:
@@ -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--;
|
||||
|
||||
Reference in New Issue
Block a user