From c49be8bd69895af5c8074a1d921fc9b801e03bde Mon Sep 17 00:00:00 2001 From: M66B Date: Sun, 3 Feb 2019 09:38:48 +0000 Subject: [PATCH] Show original message inline --- .../java/eu/faircode/email/ActivityView.java | 13 - .../eu/faircode/email/AdapterMessage.java | 205 +++++++++++++-- .../eu/faircode/email/FragmentWebView.java | 247 ------------------ .../main/res/layout/item_message_compact.xml | 16 +- .../main/res/layout/item_message_normal.xml | 18 +- app/src/main/res/values/strings.xml | 1 - 6 files changed, 215 insertions(+), 285 deletions(-) delete mode 100644 app/src/main/java/eu/faircode/email/FragmentWebView.java diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java index 53834b7bc3..fbe1abe2b3 100644 --- a/app/src/main/java/eu/faircode/email/ActivityView.java +++ b/app/src/main/java/eu/faircode/email/ActivityView.java @@ -126,7 +126,6 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB static final String ACTION_VIEW_MESSAGES = BuildConfig.APPLICATION_ID + ".VIEW_MESSAGES"; static final String ACTION_VIEW_THREAD = BuildConfig.APPLICATION_ID + ".VIEW_THREAD"; - static final String ACTION_VIEW_FULL = BuildConfig.APPLICATION_ID + ".VIEW_FULL"; static final String ACTION_STORE_RAW = BuildConfig.APPLICATION_ID + ".STORE_RAW"; static final String ACTION_EDIT_FOLDER = BuildConfig.APPLICATION_ID + ".EDIT_FOLDER"; static final String ACTION_EDIT_ANSWERS = BuildConfig.APPLICATION_ID + ".EDIT_ANSWERS"; @@ -481,7 +480,6 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB IntentFilter iff = new IntentFilter(); iff.addAction(ACTION_VIEW_MESSAGES); iff.addAction(ACTION_VIEW_THREAD); - iff.addAction(ACTION_VIEW_FULL); iff.addAction(ACTION_STORE_RAW); iff.addAction(ACTION_EDIT_FOLDER); iff.addAction(ACTION_EDIT_ANSWERS); @@ -1031,8 +1029,6 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB onViewMessages(intent); else if (ACTION_VIEW_THREAD.equals(action)) onViewThread(intent); - else if (ACTION_VIEW_FULL.equals(action)) - onViewFull(intent); else if (ACTION_STORE_RAW.equals(action)) onStoreRaw(intent); else if (ACTION_EDIT_FOLDER.equals(action)) @@ -1099,15 +1095,6 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB fragmentTransaction.commit(); } - private void onViewFull(Intent intent) { - FragmentWebView fragment = new FragmentWebView(); - fragment.setArguments(intent.getExtras()); - - FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); - fragmentTransaction.replace(R.id.content_frame, fragment).addToBackStack("webview"); - fragmentTransaction.commit(); - } - private void onStoreRaw(Intent intent) { message = intent.getLongExtra("id", -1); Intent create = new Intent(Intent.ACTION_CREATE_DOCUMENT); diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index 539ba9abc4..ecbd7e2eab 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -52,6 +52,7 @@ 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.LongSparseArray; import android.util.TypedValue; import android.view.LayoutInflater; @@ -61,6 +62,10 @@ import android.view.MotionEvent; import android.view.TouchDelegate; import android.view.View; import android.view.ViewGroup; +import android.webkit.DownloadListener; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -73,10 +78,16 @@ import android.widget.Toast; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.snackbar.Snackbar; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; import org.xml.sax.XMLReader; +import java.io.BufferedInputStream; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.text.Collator; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -95,6 +106,7 @@ import javax.mail.internet.InternetAddress; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.PopupMenu; +import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.Group; import androidx.core.content.ContextCompat; import androidx.lifecycle.LifecycleOwner; @@ -204,6 +216,7 @@ public class AdapterMessage extends RecyclerView.Adapter 0 && show_expanded ? View.VISIBLE : View.GONE); - btnHtml.setVisibility(viewType == ViewType.THREAD && show_expanded ? View.INVISIBLE : View.GONE); - ibQuotes.setVisibility(viewType == ViewType.THREAD && show_expanded ? View.INVISIBLE : View.GONE); - ibImages.setVisibility(viewType == ViewType.THREAD && show_expanded ? View.INVISIBLE : View.GONE); + btnHtml.setVisibility(viewType == ViewType.THREAD && show_expanded && !show_html ? View.INVISIBLE : View.GONE); + ibQuotes.setVisibility(viewType == ViewType.THREAD && show_expanded && !show_html ? View.INVISIBLE : View.GONE); + ibImages.setVisibility(viewType == ViewType.THREAD && show_expanded && !show_html ? View.INVISIBLE : View.GONE); + tvBody.setVisibility(viewType == ViewType.THREAD && show_expanded && !show_html ? View.INVISIBLE : View.GONE); + vwBody.setVisibility(viewType == ViewType.THREAD && show_expanded && show_html ? View.INVISIBLE : View.GONE); pbBody.setVisibility(View.GONE); tvNoInternetBody.setVisibility(View.GONE); @@ -697,11 +716,14 @@ public class AdapterMessage extends RecyclerView.Adapter attachments = idAttachments.get(message.id); if (attachments != null) @@ -758,9 +780,9 @@ public class AdapterMessage extends RecyclerView.Adapter 0 ? View.VISIBLE : View.INVISIBLE); + rvImage.setVisibility(images.size() > 0 ? View.VISIBLE : View.GONE); - if (message.content) { + if (message.content && !show_html) { Bundle args = new Bundle(); args.putSerializable("message", message); bodyTask.execute(context, owner, args, "message:body"); @@ -1176,12 +1198,164 @@ public class AdapterMessage extends RecyclerView.Adapter() { + @Override + protected String onExecute(Context context, Bundle args) throws Throwable { + long id = args.getLong("id"); + + String html = Helper.readText(EntityMessage.getFile(context, id)); + + Document doc = Jsoup.parse(html); + for (Element img : doc.select("img")) + try { + String src = img.attr("src"); + if (src.startsWith("cid:")) { + String cid = src.substring(4); + EntityAttachment attachment = DB.getInstance(context).attachment().getAttachment(id, cid); + if (attachment != null && attachment.available) { + InputStream is = null; + try { + File file = EntityAttachment.getFile(context, attachment.id); + + 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()); + } finally { + if (is != null) + is.close(); + } + } + } + } catch (Throwable ex) { + Log.e(ex); + } + + return doc.html(); + } + + @Override + protected void onExecuted(Bundle args, String html) { + webView.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:webview"); } private void onShowQuotes(final TupleMessageEx message) { @@ -1284,6 +1458,7 @@ public class AdapterMessage extends RecyclerView.Adapter. - - Copyright 2018-2019 by Marcel Bokhorst (M66B) -*/ - -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Bundle; -import android.util.Base64; -import android.view.ContextMenu; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.webkit.DownloadListener; -import android.webkit.WebChromeClient; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.ProgressBar; -import android.widget.Toast; - -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.IOException; -import java.io.InputStream; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.lifecycle.Lifecycle; - -// https://developer.android.com/reference/android/webkit/WebView - -public class FragmentWebView extends FragmentBase { - private ProgressBar progressBar; - private WebView webview; - - @Override - @Nullable - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_webview, container, false); - - progressBar = view.findViewById(R.id.progressbar); - webview = view.findViewById(R.id.webview); - - progressBar.setProgress(0); - progressBar.setVisibility(View.VISIBLE); - - WebSettings settings = webview.getSettings(); - settings.setJavaScriptEnabled(true); - settings.setUseWideViewPort(true); - settings.setLoadWithOverviewMode(true); - settings.setBuiltInZoomControls(true); - settings.setDisplayZoomControls(false); - settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); - - webview.setWebViewClient(new WebViewClient() { - public boolean shouldOverrideUrlLoading(WebView view, String url) { - if (getViewLifecycleOwner().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) { - Helper.view(getContext(), getViewLifecycleOwner(), Uri.parse(url), true); - return true; - } - return false; - } - }); - - webview.setWebChromeClient(new WebChromeClient() { - public void onProgressChanged(WebView view, int progress) { - progressBar.setProgress(progress); - if (progress == 100) - progressBar.setVisibility(View.GONE); - } - }); - - webview.setDownloadListener(new DownloadListener() { - public void onDownloadStart( - String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { - Log.i("Download url=" + url + " mime type=" + mimetype); - - Uri uri = Uri.parse(url); - - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(uri); - - PackageManager pm = getContext().getPackageManager(); - if (intent.resolveActivity(pm) == null) - Toast.makeText(getContext(), getString(R.string.title_no_viewer, uri.toString()), Toast.LENGTH_LONG).show(); - else - startActivity(intent); - } - }); - - registerForContextMenu(webview); - - onLoad(); - - ((ActivityBase) getActivity()).addBackPressedListener(onBackPressedListener); - - return view; - } - - private void onLoad() { - Bundle args = getArguments(); - if (args.containsKey("url")) { - String url = args.getString("url"); - webview.loadUrl(url); - setSubtitle(url); - } else if (args.containsKey("id")) { - new SimpleTask() { - @Override - protected String onExecute(Context context, Bundle args) throws Throwable { - long id = args.getLong("id"); - - String html = Helper.readText(EntityMessage.getFile(context, id)); - - Document doc = Jsoup.parse(html); - for (Element img : doc.select("img")) - try { - String src = img.attr("src"); - if (src.startsWith("cid")) { - String[] cids = src.split(":"); - if (cids.length > 1) { - String cid = "<" + cids[1] + ">"; - EntityAttachment attachment = DB.getInstance(context).attachment().getAttachment(id, cid); - if (attachment != null && attachment.available) { - InputStream is = null; - try { - File file = EntityAttachment.getFile(context, attachment.id); - - 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()); - } finally { - if (is != null) - is.close(); - } - } - } - } - } catch (Throwable ex) { - Log.e(ex); - } - - return doc.html(); - } - - @Override - protected void onExecuted(Bundle args, String html) { - String from = args.getString("from"); - webview.loadDataWithBaseURL("email://", html, "text/html", "UTF-8", null); - setSubtitle(from); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(this, args, "webview:format"); - } - } - - @Override - public void onDestroyView() { - ((ActivityBase) getActivity()).removeBackPressedListener(onBackPressedListener); - super.onDestroyView(); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, view, menuInfo); - - if (view instanceof WebView) { - final WebView.HitTestResult result = ((WebView) view).getHitTestResult(); - if (result.getType() == WebView.HitTestResult.IMAGE_TYPE || - result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { - Log.i("Context menu url=" + result.getExtra()); - - menu.add(Menu.NONE, 1, 0, R.string.title_view) - .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - Uri uri = Uri.parse(result.getExtra()); - - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(uri); - - PackageManager pm = getContext().getPackageManager(); - if (intent.resolveActivity(pm) == null) - Toast.makeText(getContext(), getString(R.string.title_no_viewer, uri.toString()), Toast.LENGTH_LONG).show(); - else - startActivity(intent); - - return true; - } - }); - } - } - } - - ActivityBase.IBackPressedListener onBackPressedListener = new ActivityBase.IBackPressedListener() { - @Override - public boolean onBackPressed() { - boolean can = webview.canGoBack(); - if (can) - webview.goBack(); - - Bundle args = getArguments(); - if (args.containsKey("from") && !webview.canGoBack()) - setSubtitle(args.getString("from")); - - return can; - } - }; -} diff --git a/app/src/main/res/layout/item_message_compact.xml b/app/src/main/res/layout/item_message_compact.xml index cadc51ce2b..e4e43879c0 100644 --- a/app/src/main/res/layout/item_message_compact.xml +++ b/app/src/main/res/layout/item_message_compact.xml @@ -739,13 +739,21 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/barrier_body" /> + + @@ -756,7 +764,7 @@ android:layout_height="wrap_content" android:text="@string/title_no_internet" android:textAppearance="@style/TextAppearance.AppCompat.Small" - app:layout_constraintBottom_toBottomOf="@id/tvBody" + app:layout_constraintBottom_toBottomOf="@id/vwBody" app:layout_constraintEnd_toEndOf="@id/tvBody" app:layout_constraintStart_toStartOf="@id/tvBody" app:layout_constraintTop_toTopOf="@id/tvBody" /> @@ -768,7 +776,7 @@ android:layout_marginTop="3dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/tvBody" /> + app:layout_constraintTop_toBottomOf="@id/vwBody" /> + app:constraint_referenced_ids="vSeparatorAddress,ivExpanderAddress,bnvActions" /> diff --git a/app/src/main/res/layout/item_message_normal.xml b/app/src/main/res/layout/item_message_normal.xml index 9eb2374143..4770e7d3a2 100644 --- a/app/src/main/res/layout/item_message_normal.xml +++ b/app/src/main/res/layout/item_message_normal.xml @@ -721,7 +721,6 @@ android:layout_marginTop="3dp" android:layout_marginEnd="6dp" android:fontFamily="monospace" - android:minHeight="60dp" android:text="Body" android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textIsSelectable="true" @@ -729,13 +728,22 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/barrier_body" /> + + @@ -746,7 +754,7 @@ android:layout_height="wrap_content" android:text="@string/title_no_internet" android:textAppearance="@style/TextAppearance.AppCompat.Small" - app:layout_constraintBottom_toBottomOf="@id/tvBody" + app:layout_constraintBottom_toBottomOf="@id/vwBody" app:layout_constraintEnd_toEndOf="@id/tvBody" app:layout_constraintStart_toStartOf="@id/tvBody" app:layout_constraintTop_toTopOf="@id/tvBody" /> @@ -758,7 +766,7 @@ android:layout_marginTop="3dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/tvBody" /> + app:layout_constraintTop_toBottomOf="@id/vwBody" /> + app:constraint_referenced_ids="vSeparatorAddress,ivExpanderAddress,bnvActions" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e661aef116..91d4b91e30 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -354,7 +354,6 @@ Discard Save Send - View Send at … Nothing selected