Rewrite Share functionality in message view screen
The shared text now contains subject, date, sender, and recipients in addition to the message body text.
This commit is contained in:
parent
8f7e9ee73c
commit
3133573139
10 changed files with 107 additions and 63 deletions
|
@ -4,8 +4,6 @@ interface CoreResourceProvider {
|
|||
fun defaultSignature(): String
|
||||
fun defaultIdentityDescription(): String
|
||||
|
||||
fun sendAlternateChooserTitle(): String
|
||||
|
||||
fun internalStorageProviderName(): String
|
||||
fun externalStorageProviderName(): String
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
|
@ -36,7 +35,6 @@ import com.fsck.k9.Account.Expunge;
|
|||
import com.fsck.k9.CoreResourceProvider;
|
||||
import com.fsck.k9.DI;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.K9.Intents;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.backend.BackendManager;
|
||||
import com.fsck.k9.backend.api.Backend;
|
||||
|
@ -56,7 +54,6 @@ import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveOrCopy;
|
|||
import com.fsck.k9.controller.MessagingControllerCommands.PendingSetFlag;
|
||||
import com.fsck.k9.controller.ProgressBodyFactory.ProgressListener;
|
||||
import com.fsck.k9.helper.MutableBoolean;
|
||||
import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.mail.AuthenticationFailedException;
|
||||
import com.fsck.k9.mail.CertificateValidationException;
|
||||
import com.fsck.k9.mail.FetchProfile;
|
||||
|
@ -64,12 +61,9 @@ import com.fsck.k9.mail.FetchProfile.Item;
|
|||
import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.FolderClass;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.Message.RecipientType;
|
||||
import com.fsck.k9.mail.MessageRetrievalListener;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Part;
|
||||
import com.fsck.k9.mail.internet.MessageExtractor;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mailstore.LocalFolder;
|
||||
import com.fsck.k9.mailstore.LocalMessage;
|
||||
import com.fsck.k9.mailstore.LocalStore;
|
||||
|
@ -2203,50 +2197,6 @@ public class MessagingController {
|
|||
return !backend.getSupportsTrashFolder();
|
||||
}
|
||||
|
||||
public void sendAlternate(Context context, Account account, LocalMessage message) {
|
||||
Timber.d("Got message %s:%s:%s for sendAlternate",
|
||||
account.getDescription(), message.getFolder(), message.getUid());
|
||||
|
||||
Intent msg = new Intent(Intent.ACTION_SEND);
|
||||
String quotedText = null;
|
||||
Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
||||
if (part == null) {
|
||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||
}
|
||||
if (part != null) {
|
||||
quotedText = MessageExtractor.getTextFromPart(part);
|
||||
}
|
||||
if (quotedText != null) {
|
||||
msg.putExtra(Intent.EXTRA_TEXT, quotedText);
|
||||
}
|
||||
msg.putExtra(Intent.EXTRA_SUBJECT, message.getSubject());
|
||||
|
||||
Address[] from = message.getFrom();
|
||||
String[] senders = new String[from.length];
|
||||
for (int i = 0; i < from.length; i++) {
|
||||
senders[i] = from[i].toString();
|
||||
}
|
||||
msg.putExtra(Intents.Share.EXTRA_FROM, senders);
|
||||
|
||||
Address[] to = message.getRecipients(RecipientType.TO);
|
||||
String[] recipientsTo = new String[to.length];
|
||||
for (int i = 0; i < to.length; i++) {
|
||||
recipientsTo[i] = to[i].toString();
|
||||
}
|
||||
msg.putExtra(Intent.EXTRA_EMAIL, recipientsTo);
|
||||
|
||||
Address[] cc = message.getRecipients(RecipientType.CC);
|
||||
String[] recipientsCc = new String[cc.length];
|
||||
for (int i = 0; i < cc.length; i++) {
|
||||
recipientsCc[i] = cc[i].toString();
|
||||
}
|
||||
msg.putExtra(Intent.EXTRA_CC, recipientsCc);
|
||||
|
||||
msg.setType("text/plain");
|
||||
Intent chooserIntent = Intent.createChooser(msg, resourceProvider.sendAlternateChooserTitle());
|
||||
context.startActivity(chooserIntent);
|
||||
}
|
||||
|
||||
public boolean performPeriodicMailSync(Account account) {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
MutableBoolean syncError = new MutableBoolean(false);
|
||||
|
|
|
@ -4,4 +4,5 @@ import org.koin.dsl.module
|
|||
|
||||
val extractorModule = module {
|
||||
single { AttachmentInfoExtractor(get()) }
|
||||
single { TextPartFinder() }
|
||||
}
|
||||
|
|
|
@ -5,10 +5,6 @@ class TestCoreResourceProvider : CoreResourceProvider {
|
|||
|
||||
override fun defaultIdentityDescription() = "initial identity"
|
||||
|
||||
override fun sendAlternateChooserTitle(): String {
|
||||
throw UnsupportedOperationException("not implemented")
|
||||
}
|
||||
|
||||
override fun internalStorageProviderName(): String {
|
||||
throw UnsupportedOperationException("not implemented")
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ class K9CoreResourceProvider(private val context: Context) : CoreResourceProvide
|
|||
override fun defaultSignature(): String = context.getString(R.string.default_signature)
|
||||
override fun defaultIdentityDescription(): String = context.getString(R.string.default_identity_description)
|
||||
|
||||
override fun sendAlternateChooserTitle(): String = context.getString(R.string.send_alternate_chooser_title)
|
||||
|
||||
override fun internalStorageProviderName(): String =
|
||||
context.getString(R.string.local_storage_provider_internal_label)
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@ class K9CoreResourceProvider(private val context: Context) : CoreResourceProvide
|
|||
override fun defaultSignature(): String = context.getString(R.string.default_signature)
|
||||
override fun defaultIdentityDescription(): String = context.getString(R.string.default_identity_description)
|
||||
|
||||
override fun sendAlternateChooserTitle(): String = context.getString(R.string.send_alternate_chooser_title)
|
||||
|
||||
override fun internalStorageProviderName(): String =
|
||||
context.getString(R.string.local_storage_provider_internal_label)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.fsck.k9.ui.helper.DisplayHtmlUiFactory
|
|||
import com.fsck.k9.ui.helper.HtmlSettingsProvider
|
||||
import com.fsck.k9.ui.helper.HtmlToSpanned
|
||||
import com.fsck.k9.ui.helper.SizeFormatter
|
||||
import com.fsck.k9.ui.share.ShareIntentBuilder
|
||||
import org.koin.dsl.module
|
||||
|
||||
val uiModule = module {
|
||||
|
@ -14,4 +15,5 @@ val uiModule = module {
|
|||
single { HtmlSettingsProvider(get()) }
|
||||
single { DisplayHtmlUiFactory(get()) }
|
||||
factory { (context: Context) -> SizeFormatter(context.resources) }
|
||||
factory { ShareIntentBuilder(resourceProvider = get(), textPartFinder = get(), quoteDateFormatter = get()) }
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ import com.fsck.k9.ui.base.ThemeManager;
|
|||
import com.fsck.k9.ui.messageview.CryptoInfoDialog.OnClickShowCryptoKeyListener;
|
||||
import com.fsck.k9.ui.messageview.MessageCryptoPresenter.MessageCryptoMvpView;
|
||||
import com.fsck.k9.ui.settings.account.AccountSettingsActivity;
|
||||
import com.fsck.k9.ui.share.ShareIntentBuilder;
|
||||
import com.fsck.k9.view.MessageCryptoDisplayStatus;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
@ -491,7 +492,13 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
|
||||
public void onSendAlternate() {
|
||||
if (mMessage != null) {
|
||||
mController.sendAlternate(getActivity(), mAccount, mMessage);
|
||||
ShareIntentBuilder shareIntentBuilder = DI.get(ShareIntentBuilder.class);
|
||||
Intent shareIntent = shareIntentBuilder.createShareIntent(mMessage);
|
||||
|
||||
String shareTitle = getString(R.string.send_alternate_chooser_title);
|
||||
Intent chooserIntent = Intent.createChooser(shareIntent, shareTitle);
|
||||
|
||||
startActivity(chooserIntent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package com.fsck.k9.ui.share
|
||||
|
||||
import android.content.Intent
|
||||
import com.fsck.k9.CoreResourceProvider
|
||||
import com.fsck.k9.mail.Address
|
||||
import com.fsck.k9.mail.Message.RecipientType
|
||||
import com.fsck.k9.mail.Part
|
||||
import com.fsck.k9.mail.internet.MessageExtractor
|
||||
import com.fsck.k9.mail.internet.MimeUtility
|
||||
import com.fsck.k9.mailstore.LocalMessage
|
||||
import com.fsck.k9.message.extractors.TextPartFinder
|
||||
import com.fsck.k9.message.html.HtmlConverter
|
||||
import com.fsck.k9.message.quote.QuoteDateFormatter
|
||||
|
||||
/**
|
||||
* Create a Share intent containing important headers and the body text of a message
|
||||
*/
|
||||
class ShareIntentBuilder(
|
||||
private val resourceProvider: CoreResourceProvider,
|
||||
private val textPartFinder: TextPartFinder,
|
||||
private val quoteDateFormatter: QuoteDateFormatter
|
||||
) {
|
||||
// TODO: Pass MessageViewInfo and extract text from there
|
||||
// TODO: Use display HTML for EXTRA_HTML_TEXT and convert it to plain text for EXTRA_TEXT
|
||||
fun createShareIntent(message: LocalMessage): Intent {
|
||||
val shareText = createShareText(message)
|
||||
|
||||
return Intent(Intent.ACTION_SEND).apply {
|
||||
type = "text/plain"
|
||||
putExtra(Intent.EXTRA_TEXT, shareText)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createShareText(message: LocalMessage): String {
|
||||
val bodyText = extractBodyText(message)
|
||||
val sentDate = quoteDateFormatter.format(message.sentDate)
|
||||
|
||||
return buildString {
|
||||
message.subject?.let { subject ->
|
||||
append(resourceProvider.messageHeaderSubject())
|
||||
append(' ')
|
||||
append(subject)
|
||||
append('\n')
|
||||
}
|
||||
|
||||
if (sentDate.isNotEmpty()) {
|
||||
append(resourceProvider.messageHeaderDate())
|
||||
append(' ')
|
||||
append(sentDate)
|
||||
append('\n')
|
||||
}
|
||||
|
||||
message.from.displayString()?.let { fromAddresses ->
|
||||
append(resourceProvider.messageHeaderFrom())
|
||||
append(' ')
|
||||
append(fromAddresses)
|
||||
append('\n')
|
||||
}
|
||||
|
||||
message.getRecipients(RecipientType.TO).displayString()?.let { toAddresses ->
|
||||
append(resourceProvider.messageHeaderTo())
|
||||
append(' ')
|
||||
append(toAddresses)
|
||||
append('\n')
|
||||
}
|
||||
|
||||
message.getRecipients(RecipientType.CC).displayString()?.let { ccAddresses ->
|
||||
append(resourceProvider.messageHeaderCc())
|
||||
append(' ')
|
||||
append(ccAddresses)
|
||||
append('\n')
|
||||
}
|
||||
|
||||
append('\n')
|
||||
append(bodyText)
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractBodyText(message: LocalMessage): String {
|
||||
val part = textPartFinder.findFirstTextPart(message)
|
||||
if (part == null || part.body == null) return ""
|
||||
|
||||
val text = MessageExtractor.getTextFromPart(part) ?: return ""
|
||||
return convertFromHtmlIfNecessary(part, text)
|
||||
}
|
||||
|
||||
private fun convertFromHtmlIfNecessary(textPart: Part, text: String): String {
|
||||
return if (MimeUtility.isSameMimeType(textPart.mimeType, "text/html")) {
|
||||
HtmlConverter.htmlToText(text)
|
||||
} else {
|
||||
text
|
||||
}
|
||||
}
|
||||
|
||||
private fun Array<Address>.displayString() = Address.toString(this)?.let { if (it.isEmpty()) null else it }
|
||||
}
|
|
@ -5,8 +5,6 @@ class TestCoreResourceProvider : CoreResourceProvider {
|
|||
|
||||
override fun defaultIdentityDescription() = throw UnsupportedOperationException("not implemented")
|
||||
|
||||
override fun sendAlternateChooserTitle() = throw UnsupportedOperationException("not implemented")
|
||||
|
||||
override fun internalStorageProviderName() = throw UnsupportedOperationException("not implemented")
|
||||
|
||||
override fun externalStorageProviderName() = throw UnsupportedOperationException("not implemented")
|
||||
|
|
Loading…
Reference in a new issue