Merge pull request #6200 from thundernest/convert_to_kotlin

Convert `MessageContainerView` to Kotlin
This commit is contained in:
cketti 2022-07-29 12:04:03 +02:00 committed by GitHub
commit 8bdd808fd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 542 additions and 550 deletions

View file

@ -4,7 +4,7 @@ package com.fsck.k9.ui.messageview;
import com.fsck.k9.mailstore.AttachmentViewInfo;
interface AttachmentViewCallback {
public interface AttachmentViewCallback {
void onViewAttachment(AttachmentViewInfo attachment);
void onSaveAttachment(AttachmentViewInfo attachment);
}

View file

@ -1,538 +0,0 @@
package com.fsck.k9.ui.messageview;
import java.util.HashMap;
import java.util.Map;
import android.app.DownloadManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Message;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
import android.webkit.WebView;
import android.webkit.WebView.HitTestResult;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.app.ShareCompat;
import com.fsck.k9.DI;
import com.fsck.k9.message.html.DisplayHtml;
import com.fsck.k9.ui.R;
import com.fsck.k9.helper.ClipboardManager;
import com.fsck.k9.helper.Contacts;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mailstore.AttachmentResolver;
import com.fsck.k9.mailstore.AttachmentViewInfo;
import com.fsck.k9.mailstore.MessageViewInfo;
import com.fsck.k9.ui.helper.DisplayHtmlUiFactory;
import com.fsck.k9.view.MessageWebView;
import com.fsck.k9.view.MessageWebView.OnPageFinishedListener;
import com.fsck.k9.view.WebViewConfigProvider;
import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
public class MessageContainerView extends LinearLayout implements OnCreateContextMenuListener {
private static final int MENU_ITEM_LINK_VIEW = Menu.FIRST;
private static final int MENU_ITEM_LINK_SHARE = Menu.FIRST + 1;
private static final int MENU_ITEM_LINK_COPY = Menu.FIRST + 2;
private static final int MENU_ITEM_LINK_TEXT_COPY = Menu.FIRST + 3;
private static final int MENU_ITEM_IMAGE_VIEW = Menu.FIRST;
private static final int MENU_ITEM_IMAGE_SAVE = Menu.FIRST + 1;
private static final int MENU_ITEM_IMAGE_COPY = Menu.FIRST + 2;
private static final int MENU_ITEM_PHONE_CALL = Menu.FIRST;
private static final int MENU_ITEM_PHONE_SAVE = Menu.FIRST + 1;
private static final int MENU_ITEM_PHONE_COPY = Menu.FIRST + 2;
private static final int MENU_ITEM_EMAIL_SEND = Menu.FIRST;
private static final int MENU_ITEM_EMAIL_SAVE = Menu.FIRST + 1;
private static final int MENU_ITEM_EMAIL_COPY = Menu.FIRST + 2;
private final DisplayHtml displayHtml = DI.get(DisplayHtmlUiFactory.class).createForMessageView();
private final WebViewConfigProvider webViewConfigProvider = DI.get(WebViewConfigProvider.class);
private final ClipboardManager clipboardManager = DI.get(ClipboardManager.class);
private MessageWebView mMessageContentView;
private LinearLayout mAttachments;
private View unsignedTextContainer;
private View unsignedTextDivider;
private TextView unsignedText;
private View mAttachmentsContainer;
private boolean showingPictures;
private LayoutInflater mInflater;
private AttachmentViewCallback attachmentCallback;
private Map<AttachmentViewInfo, AttachmentView> attachmentViewMap = new HashMap<>();
private Map<Uri, AttachmentViewInfo> attachments = new HashMap<>();
private boolean hasHiddenExternalImages;
private String currentHtmlText;
private AttachmentResolver currentAttachmentResolver;
@Override
public void onFinishInflate() {
super.onFinishInflate();
mMessageContentView = findViewById(R.id.message_content);
if (!isInEditMode()) {
mMessageContentView.configure(webViewConfigProvider.createForMessageView());
}
mMessageContentView.setOnCreateContextMenuListener(this);
mMessageContentView.setVisibility(View.VISIBLE);
mAttachmentsContainer = findViewById(R.id.attachments_container);
mAttachments = findViewById(R.id.attachments);
unsignedTextContainer = findViewById(R.id.message_unsigned_container);
unsignedTextDivider = findViewById(R.id.message_unsigned_divider);
unsignedText = findViewById(R.id.message_unsigned_text);
showingPictures = false;
Context context = getContext();
mInflater = LayoutInflater.from(context);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu);
WebView webview = (WebView) v;
WebView.HitTestResult result = webview.getHitTestResult();
if (result == null) {
return;
}
int type = result.getType();
Context context = getContext();
switch (type) {
case HitTestResult.SRC_ANCHOR_TYPE: {
final String url = result.getExtra();
OnMenuItemClickListener listener = new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case MENU_ITEM_LINK_VIEW: {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivityIfAvailable(getContext(), intent);
break;
}
case MENU_ITEM_LINK_SHARE: {
new ShareCompat.IntentBuilder(getContext())
.setType("text/plain")
.setText(url)
.startChooser();
break;
}
case MENU_ITEM_LINK_COPY: {
String label = getContext().getString(
R.string.webview_contextmenu_link_clipboard_label);
clipboardManager.setText(label, url);
break;
}
case MENU_ITEM_LINK_TEXT_COPY: {
LinkTextHandler linkTextHandler = DI.get(LinkTextHandler.class);
Message message = linkTextHandler.obtainMessage();
webview.requestFocusNodeHref(message);
break;
}
}
return true;
}
};
menu.setHeaderTitle(url);
menu.add(Menu.NONE, MENU_ITEM_LINK_VIEW, 0,
context.getString(R.string.webview_contextmenu_link_view_action))
.setOnMenuItemClickListener(listener);
menu.add(Menu.NONE, MENU_ITEM_LINK_SHARE, 1,
context.getString(R.string.webview_contextmenu_link_share_action))
.setOnMenuItemClickListener(listener);
menu.add(Menu.NONE, MENU_ITEM_LINK_COPY, 2,
context.getString(R.string.webview_contextmenu_link_copy_action))
.setOnMenuItemClickListener(listener);
menu.add(Menu.NONE, MENU_ITEM_LINK_TEXT_COPY, 3,
context.getString(R.string.webview_contextmenu_link_text_copy_action))
.setOnMenuItemClickListener(listener);
break;
}
case HitTestResult.IMAGE_TYPE:
case HitTestResult.SRC_IMAGE_ANCHOR_TYPE: {
final Uri uri = Uri.parse(result.getExtra());
if (uri == null) {
return;
}
final AttachmentViewInfo attachmentViewInfo = getAttachmentViewInfoIfCidUri(uri);
final boolean inlineImage = attachmentViewInfo != null;
OnMenuItemClickListener listener = new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case MENU_ITEM_IMAGE_VIEW: {
if (inlineImage) {
attachmentCallback.onViewAttachment(attachmentViewInfo);
} else {
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivityIfAvailable(getContext(), intent);
}
break;
}
case MENU_ITEM_IMAGE_SAVE: {
if (inlineImage) {
attachmentCallback.onSaveAttachment(attachmentViewInfo);
} else {
downloadImage(uri);
}
break;
}
case MENU_ITEM_IMAGE_COPY: {
String label = getContext().getString(
R.string.webview_contextmenu_image_clipboard_label);
clipboardManager.setText(label, uri.toString());
break;
}
}
return true;
}
};
menu.setHeaderTitle(inlineImage ?
context.getString(R.string.webview_contextmenu_image_title) : uri.toString());
menu.add(Menu.NONE, MENU_ITEM_IMAGE_VIEW, 0,
context.getString(R.string.webview_contextmenu_image_view_action))
.setOnMenuItemClickListener(listener);
menu.add(Menu.NONE, MENU_ITEM_IMAGE_SAVE, 1,
inlineImage ?
context.getString(R.string.webview_contextmenu_image_save_action) :
context.getString(R.string.webview_contextmenu_image_download_action))
.setOnMenuItemClickListener(listener);
if (!inlineImage) {
menu.add(Menu.NONE, MENU_ITEM_IMAGE_COPY, 2,
context.getString(R.string.webview_contextmenu_image_copy_action))
.setOnMenuItemClickListener(listener);
}
break;
}
case HitTestResult.PHONE_TYPE: {
final String phoneNumber = result.getExtra();
OnMenuItemClickListener listener = new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case MENU_ITEM_PHONE_CALL: {
Uri uri = Uri.parse(WebView.SCHEME_TEL + phoneNumber);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivityIfAvailable(getContext(), intent);
break;
}
case MENU_ITEM_PHONE_SAVE: {
Contacts contacts = Contacts.getInstance(getContext());
contacts.addPhoneContact(phoneNumber);
break;
}
case MENU_ITEM_PHONE_COPY: {
String label = getContext().getString(
R.string.webview_contextmenu_phone_clipboard_label);
clipboardManager.setText(label, phoneNumber);
break;
}
}
return true;
}
};
menu.setHeaderTitle(phoneNumber);
menu.add(Menu.NONE, MENU_ITEM_PHONE_CALL, 0,
context.getString(R.string.webview_contextmenu_phone_call_action))
.setOnMenuItemClickListener(listener);
menu.add(Menu.NONE, MENU_ITEM_PHONE_SAVE, 1,
context.getString(R.string.webview_contextmenu_phone_save_action))
.setOnMenuItemClickListener(listener);
menu.add(Menu.NONE, MENU_ITEM_PHONE_COPY, 2,
context.getString(R.string.webview_contextmenu_phone_copy_action))
.setOnMenuItemClickListener(listener);
break;
}
case WebView.HitTestResult.EMAIL_TYPE: {
final String email = result.getExtra();
OnMenuItemClickListener listener = new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case MENU_ITEM_EMAIL_SEND: {
Uri uri = Uri.parse(WebView.SCHEME_MAILTO + email);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivityIfAvailable(getContext(), intent);
break;
}
case MENU_ITEM_EMAIL_SAVE: {
Contacts contacts = Contacts.getInstance(getContext());
contacts.createContact(new Address(email));
break;
}
case MENU_ITEM_EMAIL_COPY: {
String label = getContext().getString(
R.string.webview_contextmenu_email_clipboard_label);
clipboardManager.setText(label, email);
break;
}
}
return true;
}
};
menu.setHeaderTitle(email);
menu.add(Menu.NONE, MENU_ITEM_EMAIL_SEND, 0,
context.getString(R.string.webview_contextmenu_email_send_action))
.setOnMenuItemClickListener(listener);
menu.add(Menu.NONE, MENU_ITEM_EMAIL_SAVE, 1,
context.getString(R.string.webview_contextmenu_email_save_action))
.setOnMenuItemClickListener(listener);
menu.add(Menu.NONE, MENU_ITEM_EMAIL_COPY, 2,
context.getString(R.string.webview_contextmenu_email_copy_action))
.setOnMenuItemClickListener(listener);
break;
}
}
}
private void downloadImage(Uri uri) {
DownloadManager.Request request = new DownloadManager.Request(uri);
if (Build.VERSION.SDK_INT >= 29) {
String filename = uri.getLastPathSegment();
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
}
request.setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
DownloadManager downloadManager = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
downloadManager.enqueue(request);
}
private AttachmentViewInfo getAttachmentViewInfoIfCidUri(Uri uri) {
if (!"cid".equals(uri.getScheme())) {
return null;
}
String cid = uri.getSchemeSpecificPart();
Uri internalUri = currentAttachmentResolver.getAttachmentUriForContentId(cid);
return attachments.get(internalUri);
}
private void startActivityIfAvailable(Context context, Intent intent) {
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, R.string.error_activity_not_found, Toast.LENGTH_LONG).show();
}
}
public MessageContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
private boolean isShowingPictures() {
return showingPictures;
}
private void setLoadPictures(boolean enable) {
mMessageContentView.blockNetworkData(!enable);
showingPictures = enable;
}
public void showPictures() {
setLoadPictures(true);
refreshDisplayedContent();
}
public void enableAttachmentButtons() {
for (AttachmentView attachmentView : attachmentViewMap.values()) {
attachmentView.enableButtons();
}
}
public void disableAttachmentButtons() {
for (AttachmentView attachmentView : attachmentViewMap.values()) {
attachmentView.disableButtons();
}
}
public void displayMessageViewContainer(MessageViewInfo messageViewInfo,
final OnRenderingFinishedListener onRenderingFinishedListener, boolean loadPictures,
boolean hideUnsignedTextDivider, AttachmentViewCallback attachmentCallback) {
this.attachmentCallback = attachmentCallback;
resetView();
renderAttachments(messageViewInfo);
String textToDisplay = messageViewInfo.text;
if (textToDisplay != null && !isShowingPictures()) {
if (Utility.hasExternalImages(textToDisplay)) {
if (loadPictures) {
setLoadPictures(true);
} else {
hasHiddenExternalImages = true;
}
}
}
if (textToDisplay == null) {
String noTextMessage = getContext().getString(R.string.webview_empty_message);
textToDisplay = displayHtml.wrapStatusMessage(noTextMessage);
}
OnPageFinishedListener onPageFinishedListener = new OnPageFinishedListener() {
@Override
public void onPageFinished() {
onRenderingFinishedListener.onLoadFinished();
}
};
displayHtmlContentWithInlineAttachments(
textToDisplay, messageViewInfo.attachmentResolver, onPageFinishedListener);
if (!TextUtils.isEmpty(messageViewInfo.extraText)) {
unsignedTextContainer.setVisibility(View.VISIBLE);
unsignedTextDivider.setVisibility(hideUnsignedTextDivider ? View.GONE : View.VISIBLE);
unsignedText.setText(messageViewInfo.extraText);
}
}
public boolean hasHiddenExternalImages() {
return hasHiddenExternalImages;
}
private void displayHtmlContentWithInlineAttachments(String htmlText, AttachmentResolver attachmentResolver,
OnPageFinishedListener onPageFinishedListener) {
currentHtmlText = htmlText;
currentAttachmentResolver = attachmentResolver;
mMessageContentView.displayHtmlContentWithInlineAttachments(htmlText, attachmentResolver, onPageFinishedListener);
}
private void refreshDisplayedContent() {
mMessageContentView.displayHtmlContentWithInlineAttachments(currentHtmlText, currentAttachmentResolver, null);
}
private void clearDisplayedContent() {
mMessageContentView.displayHtmlContentWithInlineAttachments("", null, null);
unsignedTextContainer.setVisibility(View.GONE);
unsignedText.setText("");
}
public void renderAttachments(MessageViewInfo messageViewInfo) {
if (messageViewInfo.attachments != null) {
for (AttachmentViewInfo attachment : messageViewInfo.attachments) {
attachments.put(attachment.internalUri, attachment);
if (attachment.inlineAttachment) {
continue;
}
AttachmentView view =
(AttachmentView) mInflater.inflate(R.layout.message_view_attachment, mAttachments, false);
view.setCallback(attachmentCallback);
view.setAttachment(attachment);
attachmentViewMap.put(attachment, view);
mAttachments.addView(view);
}
}
if (messageViewInfo.extraAttachments != null) {
for (AttachmentViewInfo attachment : messageViewInfo.extraAttachments) {
attachments.put(attachment.internalUri, attachment);
if (attachment.inlineAttachment) {
continue;
}
LockedAttachmentView view = (LockedAttachmentView) mInflater
.inflate(R.layout.message_view_attachment_locked, mAttachments, false);
view.setCallback(attachmentCallback);
view.setAttachment(attachment);
// attachments.put(attachment, view);
mAttachments.addView(view);
}
}
}
public void resetView() {
setLoadPictures(false);
mAttachments.removeAllViews();
currentHtmlText = null;
currentAttachmentResolver = null;
/*
* Clear the WebView content
*
* For some reason WebView.clearView() doesn't clear the contents when the WebView changes
* its size because the button to download the complete message was previously shown and
* is now hidden.
*/
clearDisplayedContent();
}
public void enableAttachmentButtons(AttachmentViewInfo attachment) {
getAttachmentView(attachment).enableButtons();
}
public void disableAttachmentButtons(AttachmentViewInfo attachment) {
getAttachmentView(attachment).disableButtons();
}
public void refreshAttachmentThumbnail(AttachmentViewInfo attachment) {
getAttachmentView(attachment).refreshThumbnail();
}
private AttachmentView getAttachmentView(AttachmentViewInfo attachment) {
return attachmentViewMap.get(attachment);
}
interface OnRenderingFinishedListener {
void onLoadFinished();
}
}

View file

@ -0,0 +1,538 @@
package com.fsck.k9.ui.messageview
import android.app.DownloadManager
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.util.AttributeSet
import android.view.ContextMenu
import android.view.ContextMenu.ContextMenuInfo
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.View.OnCreateContextMenuListener
import android.view.ViewGroup
import android.webkit.WebView
import android.webkit.WebView.HitTestResult
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.core.app.ShareCompat.IntentBuilder
import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.fsck.k9.helper.ClipboardManager
import com.fsck.k9.helper.Contacts
import com.fsck.k9.helper.Utility
import com.fsck.k9.mail.Address
import com.fsck.k9.mailstore.AttachmentResolver
import com.fsck.k9.mailstore.AttachmentViewInfo
import com.fsck.k9.mailstore.MessageViewInfo
import com.fsck.k9.ui.R
import com.fsck.k9.ui.helper.DisplayHtmlUiFactory
import com.fsck.k9.view.MessageWebView
import com.fsck.k9.view.MessageWebView.OnPageFinishedListener
import com.fsck.k9.view.WebViewConfigProvider
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
import org.koin.core.component.inject
class MessageContainerView(context: Context, attrs: AttributeSet?) :
LinearLayout(context, attrs),
OnCreateContextMenuListener,
KoinComponent {
private val displayHtml by lazy(mode = LazyThreadSafetyMode.NONE) {
get<DisplayHtmlUiFactory>().createForMessageView()
}
private val webViewConfigProvider: WebViewConfigProvider by inject()
private val clipboardManager: ClipboardManager by inject()
private val linkTextHandler: LinkTextHandler by inject()
private lateinit var layoutInflater: LayoutInflater
private lateinit var messageContentView: MessageWebView
private lateinit var attachmentsContainer: ViewGroup
private lateinit var unsignedTextContainer: View
private lateinit var unsignedTextDivider: View
private lateinit var unsignedText: TextView
private var isShowingPictures = false
private var currentHtmlText: String? = null
private val attachmentViewMap = mutableMapOf<AttachmentViewInfo, AttachmentView>()
private val attachments = mutableMapOf<Uri, AttachmentViewInfo>()
private var attachmentCallback: AttachmentViewCallback? = null
private var currentAttachmentResolver: AttachmentResolver? = null
@get:JvmName("hasHiddenExternalImages")
var hasHiddenExternalImages = false
private set
public override fun onFinishInflate() {
super.onFinishInflate()
layoutInflater = LayoutInflater.from(context)
messageContentView = findViewById<MessageWebView>(R.id.message_content).apply {
if (!isInEditMode) {
configure(webViewConfigProvider.createForMessageView())
}
setOnCreateContextMenuListener(this@MessageContainerView)
visibility = VISIBLE
}
attachmentsContainer = findViewById(R.id.attachments_container)
unsignedTextContainer = findViewById(R.id.message_unsigned_container)
unsignedTextDivider = findViewById(R.id.message_unsigned_divider)
unsignedText = findViewById(R.id.message_unsigned_text)
}
override fun onCreateContextMenu(menu: ContextMenu, view: View, menuInfo: ContextMenuInfo) {
super.onCreateContextMenu(menu)
val webView = view as WebView
val hitTestResult = webView.hitTestResult
when (hitTestResult.type) {
HitTestResult.SRC_ANCHOR_TYPE -> {
createLinkMenu(menu, webView, linkUrl = hitTestResult.extra)
}
HitTestResult.IMAGE_TYPE, HitTestResult.SRC_IMAGE_ANCHOR_TYPE -> {
createImageMenu(menu, imageUrl = hitTestResult.extra)
}
HitTestResult.PHONE_TYPE -> {
createPhoneNumberMenu(menu, phoneNumber = hitTestResult.extra)
}
HitTestResult.EMAIL_TYPE -> {
createEmailMenu(menu, email = hitTestResult.extra)
}
}
}
private fun createLinkMenu(
menu: ContextMenu,
webView: WebView,
linkUrl: String?
) {
if (linkUrl == null) return
val listener = MenuItem.OnMenuItemClickListener { item ->
when (item.itemId) {
MENU_ITEM_LINK_VIEW -> {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(linkUrl))
startActivityIfAvailable(context, intent)
}
MENU_ITEM_LINK_SHARE -> {
IntentBuilder(context)
.setType("text/plain")
.setText(linkUrl)
.startChooser()
}
MENU_ITEM_LINK_COPY -> {
val label = context.getString(R.string.webview_contextmenu_link_clipboard_label)
clipboardManager.setText(label, linkUrl)
}
MENU_ITEM_LINK_TEXT_COPY -> {
val message = linkTextHandler.obtainMessage()
webView.requestFocusNodeHref(message)
}
}
true
}
menu.setHeaderTitle(linkUrl)
menu.add(
Menu.NONE,
MENU_ITEM_LINK_VIEW,
0,
context.getString(R.string.webview_contextmenu_link_view_action)
).setOnMenuItemClickListener(listener)
menu.add(
Menu.NONE,
MENU_ITEM_LINK_SHARE,
1,
context.getString(R.string.webview_contextmenu_link_share_action)
).setOnMenuItemClickListener(listener)
menu.add(
Menu.NONE,
MENU_ITEM_LINK_COPY,
2,
context.getString(R.string.webview_contextmenu_link_copy_action)
).setOnMenuItemClickListener(listener)
menu.add(
Menu.NONE,
MENU_ITEM_LINK_TEXT_COPY,
3,
context.getString(R.string.webview_contextmenu_link_text_copy_action)
).setOnMenuItemClickListener(listener)
}
private fun createImageMenu(menu: ContextMenu, imageUrl: String?) {
if (imageUrl == null) return
val imageUri = Uri.parse(imageUrl)
val attachmentViewInfo = getAttachmentViewInfoIfCidUri(imageUri)
val inlineImage = attachmentViewInfo != null
val listener = MenuItem.OnMenuItemClickListener { item ->
val attachmentCallback = checkNotNull(attachmentCallback)
when (item.itemId) {
MENU_ITEM_IMAGE_VIEW -> {
if (inlineImage) {
attachmentCallback.onViewAttachment(attachmentViewInfo)
} else {
val intent = Intent(Intent.ACTION_VIEW, imageUri)
startActivityIfAvailable(context, intent)
}
}
MENU_ITEM_IMAGE_SAVE -> {
if (inlineImage) {
attachmentCallback.onSaveAttachment(attachmentViewInfo)
} else {
downloadImage(imageUri)
}
}
MENU_ITEM_IMAGE_COPY -> {
val label = context.getString(R.string.webview_contextmenu_image_clipboard_label)
clipboardManager.setText(label, imageUri.toString())
}
}
true
}
if (inlineImage) {
menu.setHeaderTitle(R.string.webview_contextmenu_image_title)
} else {
menu.setHeaderTitle(imageUrl)
}
menu.add(
Menu.NONE,
MENU_ITEM_IMAGE_VIEW,
0,
context.getString(R.string.webview_contextmenu_image_view_action)
).setOnMenuItemClickListener(listener)
menu.add(
Menu.NONE,
MENU_ITEM_IMAGE_SAVE,
1,
if (inlineImage) {
context.getString(R.string.webview_contextmenu_image_save_action)
} else {
context.getString(R.string.webview_contextmenu_image_download_action)
}
).setOnMenuItemClickListener(listener)
if (!inlineImage) {
menu.add(
Menu.NONE,
MENU_ITEM_IMAGE_COPY,
2,
context.getString(R.string.webview_contextmenu_image_copy_action)
).setOnMenuItemClickListener(listener)
}
}
private fun createPhoneNumberMenu(menu: ContextMenu, phoneNumber: String?) {
if (phoneNumber == null) return
val listener = MenuItem.OnMenuItemClickListener { item ->
when (item.itemId) {
MENU_ITEM_PHONE_CALL -> {
val uri = Uri.parse(WebView.SCHEME_TEL + phoneNumber)
val intent = Intent(Intent.ACTION_VIEW, uri)
startActivityIfAvailable(context, intent)
}
MENU_ITEM_PHONE_SAVE -> {
val contacts = Contacts.getInstance(context)
contacts.addPhoneContact(phoneNumber)
}
MENU_ITEM_PHONE_COPY -> {
val label = context.getString(R.string.webview_contextmenu_phone_clipboard_label)
clipboardManager.setText(label, phoneNumber)
}
}
true
}
menu.setHeaderTitle(phoneNumber)
menu.add(
Menu.NONE,
MENU_ITEM_PHONE_CALL,
0,
context.getString(R.string.webview_contextmenu_phone_call_action)
).setOnMenuItemClickListener(listener)
menu.add(
Menu.NONE,
MENU_ITEM_PHONE_SAVE,
1,
context.getString(R.string.webview_contextmenu_phone_save_action)
).setOnMenuItemClickListener(listener)
menu.add(
Menu.NONE,
MENU_ITEM_PHONE_COPY,
2,
context.getString(R.string.webview_contextmenu_phone_copy_action)
).setOnMenuItemClickListener(listener)
}
private fun createEmailMenu(menu: ContextMenu, email: String?) {
if (email == null) return
val listener = MenuItem.OnMenuItemClickListener { item ->
when (item.itemId) {
MENU_ITEM_EMAIL_SEND -> {
val uri = Uri.parse(WebView.SCHEME_MAILTO + email)
val intent = Intent(Intent.ACTION_VIEW, uri)
startActivityIfAvailable(context, intent)
}
MENU_ITEM_EMAIL_SAVE -> {
val contacts = Contacts.getInstance(context)
contacts.createContact(Address(email))
}
MENU_ITEM_EMAIL_COPY -> {
val label = context.getString(R.string.webview_contextmenu_email_clipboard_label)
clipboardManager.setText(label, email)
}
}
true
}
menu.setHeaderTitle(email)
menu.add(
Menu.NONE,
MENU_ITEM_EMAIL_SEND,
0,
context.getString(R.string.webview_contextmenu_email_send_action)
).setOnMenuItemClickListener(listener)
menu.add(
Menu.NONE,
MENU_ITEM_EMAIL_SAVE,
1,
context.getString(R.string.webview_contextmenu_email_save_action)
).setOnMenuItemClickListener(listener)
menu.add(
Menu.NONE,
MENU_ITEM_EMAIL_COPY,
2,
context.getString(R.string.webview_contextmenu_email_copy_action)
).setOnMenuItemClickListener(listener)
}
private fun downloadImage(uri: Uri) {
val request = DownloadManager.Request(uri).apply {
if (Build.VERSION.SDK_INT >= 29) {
val filename = uri.lastPathSegment
setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename)
}
setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
}
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
}
private fun getAttachmentViewInfoIfCidUri(uri: Uri): AttachmentViewInfo? {
if (uri.scheme != "cid") return null
val attachmentResolver = checkNotNull(currentAttachmentResolver)
val cid = uri.schemeSpecificPart
val internalUri = attachmentResolver.getAttachmentUriForContentId(cid)
return attachments[internalUri]
}
private fun startActivityIfAvailable(context: Context, intent: Intent) {
try {
context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(context, R.string.error_activity_not_found, Toast.LENGTH_LONG).show()
}
}
private fun setLoadPictures(enable: Boolean) {
messageContentView.blockNetworkData(!enable)
isShowingPictures = enable
}
fun showPictures() {
setLoadPictures(true)
refreshDisplayedContent()
}
fun displayMessageViewContainer(
messageViewInfo: MessageViewInfo,
onRenderingFinishedListener: OnRenderingFinishedListener,
loadPictures: Boolean,
hideUnsignedTextDivider: Boolean,
attachmentCallback: AttachmentViewCallback?
) {
this.attachmentCallback = attachmentCallback
resetView()
renderAttachments(messageViewInfo)
val messageText = messageViewInfo.text
if (messageText != null && !isShowingPictures) {
if (Utility.hasExternalImages(messageText)) {
if (loadPictures) {
setLoadPictures(true)
} else {
hasHiddenExternalImages = true
}
}
}
val textToDisplay = messageText
?: displayHtml.wrapStatusMessage(context.getString(R.string.webview_empty_message))
displayHtmlContentWithInlineAttachments(
htmlText = textToDisplay,
attachmentResolver = messageViewInfo.attachmentResolver,
onPageFinishedListener = onRenderingFinishedListener::onLoadFinished
)
if (!messageViewInfo.extraText.isNullOrEmpty()) {
unsignedTextContainer.isVisible = true
unsignedTextDivider.isGone = hideUnsignedTextDivider
unsignedText.text = messageViewInfo.extraText
}
}
private fun displayHtmlContentWithInlineAttachments(
htmlText: String,
attachmentResolver: AttachmentResolver,
onPageFinishedListener: OnPageFinishedListener
) {
currentHtmlText = htmlText
currentAttachmentResolver = attachmentResolver
messageContentView.displayHtmlContentWithInlineAttachments(htmlText, attachmentResolver, onPageFinishedListener)
}
private fun refreshDisplayedContent() {
val htmlText = checkNotNull(currentHtmlText)
messageContentView.displayHtmlContentWithInlineAttachments(
htmlText = htmlText,
attachmentResolver = currentAttachmentResolver,
onPageFinishedListener = null
)
}
private fun clearDisplayedContent() {
messageContentView.displayHtmlContentWithInlineAttachments(
htmlText = "",
attachmentResolver = null,
onPageFinishedListener = null
)
unsignedTextContainer.isVisible = false
unsignedText.text = ""
}
private fun renderAttachments(messageViewInfo: MessageViewInfo) {
if (messageViewInfo.attachments != null) {
for (attachment in messageViewInfo.attachments) {
attachments[attachment.internalUri] = attachment
if (attachment.inlineAttachment) {
continue
}
val attachmentView = layoutInflater.inflate(
R.layout.message_view_attachment,
attachmentsContainer,
false
) as AttachmentView
attachmentView.setCallback(attachmentCallback)
attachmentView.setAttachment(attachment)
attachmentViewMap[attachment] = attachmentView
attachmentsContainer.addView(attachmentView)
}
}
if (messageViewInfo.extraAttachments != null) {
for (attachment in messageViewInfo.extraAttachments) {
attachments[attachment.internalUri] = attachment
if (attachment.inlineAttachment) {
continue
}
val lockedAttachmentView = layoutInflater.inflate(
R.layout.message_view_attachment_locked,
attachmentsContainer,
false
) as LockedAttachmentView
lockedAttachmentView.setCallback(attachmentCallback)
lockedAttachmentView.setAttachment(attachment)
attachmentsContainer.addView(lockedAttachmentView)
}
}
}
private fun resetView() {
setLoadPictures(false)
attachmentsContainer.removeAllViews()
currentHtmlText = null
currentAttachmentResolver = null
/*
* Clear the WebView content
*
* For some reason WebView.clearView() doesn't clear the contents when the WebView changes
* its size because the button to download the complete message was previously shown and
* is now hidden.
*/
clearDisplayedContent()
}
fun refreshAttachmentThumbnail(attachment: AttachmentViewInfo) {
getAttachmentView(attachment)?.refreshThumbnail()
}
private fun getAttachmentView(attachment: AttachmentViewInfo): AttachmentView? {
return attachmentViewMap[attachment]
}
interface OnRenderingFinishedListener {
fun onLoadFinished()
}
companion object {
private const val MENU_ITEM_LINK_VIEW = Menu.FIRST
private const val MENU_ITEM_LINK_SHARE = Menu.FIRST + 1
private const val MENU_ITEM_LINK_COPY = Menu.FIRST + 2
private const val MENU_ITEM_LINK_TEXT_COPY = Menu.FIRST + 3
private const val MENU_ITEM_IMAGE_VIEW = Menu.FIRST
private const val MENU_ITEM_IMAGE_SAVE = Menu.FIRST + 1
private const val MENU_ITEM_IMAGE_COPY = Menu.FIRST + 2
private const val MENU_ITEM_PHONE_CALL = Menu.FIRST
private const val MENU_ITEM_PHONE_SAVE = Menu.FIRST + 1
private const val MENU_ITEM_PHONE_COPY = Menu.FIRST + 2
private const val MENU_ITEM_EMAIL_SEND = Menu.FIRST
private const val MENU_ITEM_EMAIL_SAVE = Menu.FIRST + 1
private const val MENU_ITEM_EMAIL_COPY = Menu.FIRST + 2
}
}

View file

@ -93,7 +93,7 @@ class MessageWebView : WebView {
resumeTimers()
}
interface OnPageFinishedListener {
fun interface OnPageFinishedListener {
fun onPageFinished()
}
}

View file

@ -68,16 +68,8 @@
<!-- Attachments area -->
<LinearLayout
android:id="@+id/attachments_container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/attachments"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
android:layout_height="wrap_content"
android:orientation="vertical" />
</com.fsck.k9.ui.messageview.MessageContainerView>