diff --git a/app/ui/legacy/build.gradle b/app/ui/legacy/build.gradle index af3582500..bb856b542 100644 --- a/app/ui/legacy/build.gradle +++ b/app/ui/legacy/build.gradle @@ -58,6 +58,7 @@ dependencies { testImplementation "org.robolectric:robolectric:${versions.robolectric}" testImplementation "androidx.test:core:${versions.androidxTestCore}" testImplementation "junit:junit:${versions.junit}" + testImplementation "org.jetbrains.kotlin:kotlin-test:${versions.kotlin}" testImplementation "com.google.truth:truth:${versions.truth}" testImplementation "org.mockito:mockito-core:${versions.mockito}" testImplementation "org.mockito.kotlin:mockito-kotlin:${versions.mockitoKotlin}" diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientMvpView.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientMvpView.kt index 049c0279b..60f5b84f6 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientMvpView.kt +++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientMvpView.kt @@ -1,514 +1,387 @@ -package com.fsck.k9.activity.compose; +package com.fsck.k9.activity.compose +import android.app.PendingIntent +import android.text.TextWatcher +import android.view.View +import android.widget.TextView +import android.widget.Toast +import android.widget.ViewAnimator +import androidx.core.view.isGone +import androidx.core.view.isVisible +import androidx.interpolator.view.animation.FastOutLinearInInterpolator +import androidx.interpolator.view.animation.LinearOutSlowInInterpolator +import androidx.loader.app.LoaderManager +import com.fsck.k9.FontSizes +import com.fsck.k9.activity.MessageCompose +import com.fsck.k9.mail.Address +import com.fsck.k9.mail.Message.RecipientType +import com.fsck.k9.ui.R +import com.fsck.k9.view.RecipientSelectView +import com.fsck.k9.view.RecipientSelectView.Recipient +import com.fsck.k9.view.ToolableViewAnimator +import java.lang.AssertionError -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +class RecipientMvpView(private val activity: MessageCompose) : View.OnFocusChangeListener, View.OnClickListener { + private val toView: RecipientSelectView = activity.findViewById(R.id.to) + private val ccView: RecipientSelectView = activity.findViewById(R.id.cc) + private val bccView: RecipientSelectView = activity.findViewById(R.id.bcc) + private val ccWrapper: View = activity.findViewById(R.id.cc_wrapper) + private val ccDivider: View = activity.findViewById(R.id.cc_divider) + private val bccWrapper: View = activity.findViewById(R.id.bcc_wrapper) + private val bccDivider: View = activity.findViewById(R.id.bcc_divider) + private val recipientExpanderContainer: ViewAnimator = activity.findViewById(R.id.recipient_expander_container) + private val cryptoStatusView: ToolableViewAnimator = activity.findViewById(R.id.crypto_status) + private val cryptoSpecialModeIndicator: ToolableViewAnimator = activity.findViewById(R.id.crypto_special_mode) + private val textWatchers: MutableSet = HashSet() + private lateinit var presenter: RecipientPresenter -import android.app.PendingIntent; -import androidx.loader.app.LoaderManager; -import androidx.interpolator.view.animation.FastOutLinearInInterpolator; -import androidx.interpolator.view.animation.LinearOutSlowInInterpolator; -import android.text.TextWatcher; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnFocusChangeListener; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.ViewAnimator; + init { + cryptoStatusView.setOnClickListener(this) + cryptoSpecialModeIndicator.setOnClickListener(this) + toView.onFocusChangeListener = this + ccView.onFocusChangeListener = this + bccView.onFocusChangeListener = this -import com.fsck.k9.FontSizes; -import com.fsck.k9.ui.R; -import com.fsck.k9.activity.MessageCompose; -import com.fsck.k9.mail.Address; -import com.fsck.k9.mail.Message.RecipientType; -import com.fsck.k9.view.RecipientSelectView; -import com.fsck.k9.view.RecipientSelectView.Recipient; -import com.fsck.k9.view.RecipientSelectView.TokenListener; -import com.fsck.k9.view.ToolableViewAnimator; - -import static com.fsck.k9.FontSizes.FONT_10SP; -import static com.fsck.k9.FontSizes.FONT_12SP; -import static com.fsck.k9.FontSizes.FONT_16SP; -import static com.fsck.k9.FontSizes.FONT_20SP; -import static com.fsck.k9.FontSizes.FONT_DEFAULT; -import static com.fsck.k9.FontSizes.LARGE; -import static com.fsck.k9.FontSizes.MEDIUM; -import static com.fsck.k9.FontSizes.SMALL; - - -public class RecipientMvpView implements OnFocusChangeListener, OnClickListener { - private static final int VIEW_INDEX_HIDDEN = -1; - - private static final int VIEW_INDEX_BCC_EXPANDER_VISIBLE = 0; - private static final int VIEW_INDEX_BCC_EXPANDER_HIDDEN = 1; - - private static final FastOutLinearInInterpolator CRYPTO_ICON_OUT_ANIMATOR = new FastOutLinearInInterpolator(); - private static final int CRYPTO_ICON_OUT_DURATION = 195; - private static final LinearOutSlowInInterpolator CRYPTO_ICON_IN_ANIMATOR = new LinearOutSlowInInterpolator(); - private static final int CRYPTO_ICON_IN_DURATION = 225; - - private final MessageCompose activity; - private final View ccWrapper; - private final View ccDivider; - private final View bccWrapper; - private final View bccDivider; - private final RecipientSelectView toView; - private final RecipientSelectView ccView; - private final RecipientSelectView bccView; - private final ToolableViewAnimator cryptoStatusView; - private final ViewAnimator recipientExpanderContainer; - private final ToolableViewAnimator cryptoSpecialModeIndicator; - private final Set textWatchers = new HashSet<>(); - private RecipientPresenter presenter; - - - public RecipientMvpView(MessageCompose activity) { - this.activity = activity; - - toView = activity.findViewById(R.id.to); - ccView = activity.findViewById(R.id.cc); - bccView = activity.findViewById(R.id.bcc); - ccWrapper = activity.findViewById(R.id.cc_wrapper); - ccDivider = activity.findViewById(R.id.cc_divider); - bccWrapper = activity.findViewById(R.id.bcc_wrapper); - bccDivider = activity.findViewById(R.id.bcc_divider); - recipientExpanderContainer = activity.findViewById(R.id.recipient_expander_container); - cryptoStatusView = activity.findViewById(R.id.crypto_status); - cryptoStatusView.setOnClickListener(this); - cryptoSpecialModeIndicator = activity.findViewById(R.id.crypto_special_mode); - cryptoSpecialModeIndicator.setOnClickListener(this); - - toView.setOnFocusChangeListener(this); - ccView.setOnFocusChangeListener(this); - bccView.setOnFocusChangeListener(this); - - View recipientExpander = activity.findViewById(R.id.recipient_expander); - recipientExpander.setOnClickListener(this); - - View toLabel = activity.findViewById(R.id.to_label); - View ccLabel = activity.findViewById(R.id.cc_label); - View bccLabel = activity.findViewById(R.id.bcc_label); - toLabel.setOnClickListener(this); - ccLabel.setOnClickListener(this); - bccLabel.setOnClickListener(this); + activity.findViewById(R.id.recipient_expander).setOnClickListener(this) + activity.findViewById(R.id.to_label).setOnClickListener(this) + activity.findViewById(R.id.cc_label).setOnClickListener(this) + activity.findViewById(R.id.bcc_label).setOnClickListener(this) } - public void setPresenter(final RecipientPresenter presenter) { - this.presenter = presenter; + val isCcVisible: Boolean + get() = ccWrapper.isVisible - if (presenter == null) { - toView.setTokenListener(null); - ccView.setTokenListener(null); - bccView.setTokenListener(null); - return; - } + val isBccVisible: Boolean + get() = bccWrapper.isVisible - toView.setTokenListener(new TokenListener() { - @Override - public void onTokenAdded(Recipient recipient) { - presenter.onToTokenAdded(); - } + val toAddresses: List
+ get() = toView.addresses.toList() - @Override - public void onTokenRemoved(Recipient recipient) { - presenter.onToTokenRemoved(); - } + val ccAddresses: List
+ get() = ccView.addresses.toList() - @Override - public void onTokenChanged(Recipient recipient) { - presenter.onToTokenChanged(); - } + val bccAddresses: List
+ get() = bccView.addresses.toList() - @Override - public void onTokenIgnored(Recipient token) { - // Do nothing - } - }); + val toRecipients: List + get() = toView.objects - ccView.setTokenListener(new TokenListener() { - @Override - public void onTokenAdded(Recipient recipient) { - presenter.onCcTokenAdded(); - } + val ccRecipients: List + get() = ccView.objects - @Override - public void onTokenRemoved(Recipient recipient) { - presenter.onCcTokenRemoved(); - } + val bccRecipients: List + get() = bccView.objects - @Override - public void onTokenChanged(Recipient recipient) { - presenter.onCcTokenChanged(); - } + fun setPresenter(presenter: RecipientPresenter) { + this.presenter = presenter + toView.setTokenListener(object : RecipientSelectView.TokenListener { + override fun onTokenAdded(recipient: Recipient) = presenter.onToTokenAdded() - @Override - public void onTokenIgnored(Recipient token) { - // Do nothing - } - }); + override fun onTokenRemoved(recipient: Recipient) = presenter.onToTokenRemoved() - bccView.setTokenListener(new TokenListener() { - @Override - public void onTokenAdded(Recipient recipient) { - presenter.onBccTokenAdded(); - } + override fun onTokenChanged(recipient: Recipient) = presenter.onToTokenChanged() - @Override - public void onTokenRemoved(Recipient recipient) { - presenter.onBccTokenRemoved(); - } + override fun onTokenIgnored(token: Recipient) = Unit + }) - @Override - public void onTokenChanged(Recipient recipient) { - presenter.onBccTokenChanged(); - } + ccView.setTokenListener(object : RecipientSelectView.TokenListener { + override fun onTokenAdded(recipient: Recipient) = presenter.onCcTokenAdded() - @Override - public void onTokenIgnored(Recipient token) { - // Do nothing - } - }); + override fun onTokenRemoved(recipient: Recipient) = presenter.onCcTokenRemoved() + + override fun onTokenChanged(recipient: Recipient) = presenter.onCcTokenChanged() + + override fun onTokenIgnored(token: Recipient) = Unit + }) + + bccView.setTokenListener(object : RecipientSelectView.TokenListener { + override fun onTokenAdded(recipient: Recipient) = presenter.onBccTokenAdded() + + override fun onTokenRemoved(recipient: Recipient) = presenter.onBccTokenRemoved() + + override fun onTokenChanged(recipient: Recipient) = presenter.onBccTokenChanged() + + override fun onTokenIgnored(token: Recipient) = Unit + }) } - public void addTextChangedListener(TextWatcher textWatcher) { - textWatchers.add(textWatcher); - - toView.addTextChangedListener(textWatcher); - ccView.addTextChangedListener(textWatcher); - bccView.addTextChangedListener(textWatcher); + fun addTextChangedListener(textWatcher: TextWatcher) { + textWatchers.add(textWatcher) + toView.addTextChangedListener(textWatcher) + ccView.addTextChangedListener(textWatcher) + bccView.addTextChangedListener(textWatcher) } - private void removeAllTextChangedListeners(TextView view) { - for (TextWatcher textWatcher : textWatchers) { - view.removeTextChangedListener(textWatcher); + private fun removeAllTextChangedListeners(view: TextView) { + for (textWatcher in textWatchers) { + view.removeTextChangedListener(textWatcher) } } - private void addAllTextChangedListeners(TextView view) { - for (TextWatcher textWatcher : textWatchers) { - view.addTextChangedListener(textWatcher); + private fun addAllTextChangedListeners(view: TextView) { + for (textWatcher in textWatchers) { + view.addTextChangedListener(textWatcher) } } - public void setRecipientTokensShowCryptoEnabled(boolean isEnabled) { - toView.setShowCryptoEnabled(isEnabled); - ccView.setShowCryptoEnabled(isEnabled); - bccView.setShowCryptoEnabled(isEnabled); + fun setRecipientTokensShowCryptoEnabled(isEnabled: Boolean) { + toView.setShowCryptoEnabled(isEnabled) + ccView.setShowCryptoEnabled(isEnabled) + bccView.setShowCryptoEnabled(isEnabled) } - public void setCryptoProvider(String openPgpProvider) { + fun setCryptoProvider(openPgpProvider: String?) { // TODO move "show advanced" into settings, or somewhere? - toView.setCryptoProvider(openPgpProvider, false); - ccView.setCryptoProvider(openPgpProvider, false); - bccView.setCryptoProvider(openPgpProvider, false); + toView.setCryptoProvider(openPgpProvider, false) + ccView.setCryptoProvider(openPgpProvider, false) + bccView.setCryptoProvider(openPgpProvider, false) } - public void requestFocusOnToField() { - toView.requestFocus(); + fun requestFocusOnToField() { + toView.requestFocus() } - public void requestFocusOnCcField() { - ccView.requestFocus(); + fun requestFocusOnCcField() { + ccView.requestFocus() } - public void requestFocusOnBccField() { - bccView.requestFocus(); + fun requestFocusOnBccField() { + bccView.requestFocus() } - public void setFontSizes(FontSizes fontSizes, int fontSize) { - int tokenTextSize = getTokenTextSize(fontSize); - toView.setTokenTextSize(tokenTextSize); - ccView.setTokenTextSize(tokenTextSize); - bccView.setTokenTextSize(tokenTextSize); - fontSizes.setViewTextSize(toView, fontSize); - fontSizes.setViewTextSize(ccView, fontSize); - fontSizes.setViewTextSize(bccView, fontSize); + fun setFontSizes(fontSizes: FontSizes, fontSize: Int) { + val tokenTextSize = getTokenTextSize(fontSize) + toView.setTokenTextSize(tokenTextSize) + ccView.setTokenTextSize(tokenTextSize) + bccView.setTokenTextSize(tokenTextSize) + fontSizes.setViewTextSize(toView, fontSize) + fontSizes.setViewTextSize(ccView, fontSize) + fontSizes.setViewTextSize(bccView, fontSize) } - private int getTokenTextSize(int fontSize) { - switch (fontSize) { - case FONT_10SP: return FONT_10SP; - case FONT_12SP: return FONT_12SP; - case SMALL: return SMALL; - case FONT_16SP: return 15; - case MEDIUM: return FONT_16SP; - case FONT_20SP: return MEDIUM; - case LARGE: return FONT_20SP; - default: return FONT_DEFAULT; + private fun getTokenTextSize(fontSize: Int): Int { + return when (fontSize) { + FontSizes.FONT_10SP -> FontSizes.FONT_10SP + FontSizes.FONT_12SP -> FontSizes.FONT_12SP + FontSizes.SMALL -> FontSizes.SMALL + FontSizes.FONT_16SP -> 15 + FontSizes.MEDIUM -> FontSizes.FONT_16SP + FontSizes.FONT_20SP -> FontSizes.MEDIUM + FontSizes.LARGE -> FontSizes.FONT_20SP + else -> FontSizes.FONT_DEFAULT } } - public void addRecipients(RecipientType recipientType, Recipient... recipients) { - switch (recipientType) { - case TO: { - toView.addRecipients(recipients); - break; - } - case CC: { - ccView.addRecipients(recipients); - break; - } - case BCC: { - bccView.addRecipients(recipients); - break; - } + fun addRecipients(recipientType: RecipientType, vararg recipients: Recipient) { + when (recipientType) { + RecipientType.TO -> toView.addRecipients(*recipients) + RecipientType.CC -> ccView.addRecipients(*recipients) + RecipientType.BCC -> bccView.addRecipients(*recipients) + else -> throw AssertionError("Unsupported type: $recipientType") } } - public void silentlyAddBccAddresses(Recipient... recipients) { - removeAllTextChangedListeners(bccView); + fun silentlyAddBccAddresses(vararg recipients: Recipient) { + removeAllTextChangedListeners(bccView) - bccView.addRecipients(recipients); + bccView.addRecipients(*recipients) - addAllTextChangedListeners(bccView); + addAllTextChangedListeners(bccView) } - public void silentlyRemoveBccAddresses(Address[] addressesToRemove) { - if (addressesToRemove.length == 0) { - return; - } + fun silentlyRemoveBccAddresses(addresses: Array
) { + if (addresses.isEmpty()) return - List bccRecipients = new ArrayList<>(getBccRecipients()); - for (Recipient recipient : bccRecipients) { - removeAllTextChangedListeners(bccView); + val addressesToRemove = addresses.toSet() + for (recipient in bccRecipients.toList()) { + removeAllTextChangedListeners(bccView) - for (Address address : addressesToRemove) { - if (recipient.address.equals(address)) { - bccView.removeObjectSync(recipient); - } + if (recipient.address in addressesToRemove) { + bccView.removeObjectSync(recipient) } - addAllTextChangedListeners(bccView); + addAllTextChangedListeners(bccView) } } - public void setCcVisibility(boolean visible) { - ccWrapper.setVisibility(visible ? View.VISIBLE : View.GONE); - ccDivider.setVisibility(visible ? View.VISIBLE : View.GONE); + fun setCcVisibility(visible: Boolean) { + ccWrapper.isVisible = visible + ccDivider.isVisible = visible } - public void setBccVisibility(boolean visible) { - bccWrapper.setVisibility(visible ? View.VISIBLE : View.GONE); - bccDivider.setVisibility(visible ? View.VISIBLE : View.GONE); + fun setBccVisibility(visible: Boolean) { + bccWrapper.isVisible = visible + bccDivider.isVisible = visible } - public void setRecipientExpanderVisibility(boolean visible) { - int childToDisplay = visible ? VIEW_INDEX_BCC_EXPANDER_VISIBLE : VIEW_INDEX_BCC_EXPANDER_HIDDEN; - if (recipientExpanderContainer.getDisplayedChild() != childToDisplay) { - recipientExpanderContainer.setDisplayedChild(childToDisplay); + fun setRecipientExpanderVisibility(visible: Boolean) { + val childToDisplay = if (visible) VIEW_INDEX_BCC_EXPANDER_VISIBLE else VIEW_INDEX_BCC_EXPANDER_HIDDEN + + if (recipientExpanderContainer.displayedChild != childToDisplay) { + recipientExpanderContainer.displayedChild = childToDisplay } } - public boolean isCcVisible() { - return ccWrapper.getVisibility() == View.VISIBLE; + fun showNoRecipientsError() { + toView.error = toView.context.getString(R.string.message_compose_error_no_recipients) } - public boolean isBccVisible() { - return bccWrapper.getVisibility() == View.VISIBLE; + fun recipientToHasUncompletedText(): Boolean { + return toView.hasUncompletedText() } - public void showNoRecipientsError() { - toView.setError(toView.getContext().getString(R.string.message_compose_error_no_recipients)); + fun recipientCcHasUncompletedText(): Boolean { + return ccView.hasUncompletedText() } - public List
getToAddresses() { - return Arrays.asList(toView.getAddresses()); + fun recipientBccHasUncompletedText(): Boolean { + return bccView.hasUncompletedText() } - public List
getCcAddresses() { - return Arrays.asList(ccView.getAddresses()); + fun recipientToTryPerformCompletion(): Boolean { + return toView.tryPerformCompletion() } - public List
getBccAddresses() { - return Arrays.asList(bccView.getAddresses()); + fun recipientCcTryPerformCompletion(): Boolean { + return ccView.tryPerformCompletion() } - public List getToRecipients() { - return toView.getObjects(); + fun recipientBccTryPerformCompletion(): Boolean { + return bccView.tryPerformCompletion() } - public List getCcRecipients() { - return ccView.getObjects(); + fun showToUncompletedError() { + toView.error = toView.context.getString(R.string.compose_error_incomplete_recipient) } - public List getBccRecipients() { - return bccView.getObjects(); + fun showCcUncompletedError() { + ccView.error = ccView.context.getString(R.string.compose_error_incomplete_recipient) } - public boolean recipientToHasUncompletedText() { - return toView.hasUncompletedText(); + fun showBccUncompletedError() { + bccView.error = bccView.context.getString(R.string.compose_error_incomplete_recipient) } - public boolean recipientCcHasUncompletedText() { - return ccView.hasUncompletedText(); - } - - public boolean recipientBccHasUncompletedText() { - return bccView.hasUncompletedText(); - } - - public boolean recipientToTryPerformCompletion() { - return toView.tryPerformCompletion(); - } - - public boolean recipientCcTryPerformCompletion() { - return ccView.tryPerformCompletion(); - } - - public boolean recipientBccTryPerformCompletion() { - return bccView.tryPerformCompletion(); - } - - public void showToUncompletedError() { - toView.setError(toView.getContext().getString(R.string.compose_error_incomplete_recipient)); - } - - public void showCcUncompletedError() { - ccView.setError(ccView.getContext().getString(R.string.compose_error_incomplete_recipient)); - } - - public void showBccUncompletedError() { - bccView.setError(bccView.getContext().getString(R.string.compose_error_incomplete_recipient)); - } - - public void showCryptoSpecialMode(CryptoSpecialModeDisplayType cryptoSpecialModeDisplayType) { - boolean shouldBeHidden = cryptoSpecialModeDisplayType.childIdToDisplay == VIEW_INDEX_HIDDEN; + fun showCryptoSpecialMode(cryptoSpecialModeDisplayType: CryptoSpecialModeDisplayType) { + val shouldBeHidden = cryptoSpecialModeDisplayType.childIdToDisplay == VIEW_INDEX_HIDDEN if (shouldBeHidden) { - cryptoSpecialModeIndicator.setVisibility(View.GONE); - return; + cryptoSpecialModeIndicator.isGone = true + return } - cryptoSpecialModeIndicator.setVisibility(View.VISIBLE); - cryptoSpecialModeIndicator.setDisplayedChildId(cryptoSpecialModeDisplayType.childIdToDisplay); - activity.invalidateOptionsMenu(); + cryptoSpecialModeIndicator.isVisible = true + cryptoSpecialModeIndicator.displayedChildId = cryptoSpecialModeDisplayType.childIdToDisplay + + activity.invalidateOptionsMenu() } - public void showCryptoStatus(CryptoStatusDisplayType cryptoStatusDisplayType) { - boolean shouldBeHidden = cryptoStatusDisplayType.childIdToDisplay == VIEW_INDEX_HIDDEN; + fun showCryptoStatus(cryptoStatusDisplayType: CryptoStatusDisplayType) { + val shouldBeHidden = cryptoStatusDisplayType.childIdToDisplay == VIEW_INDEX_HIDDEN if (shouldBeHidden) { cryptoStatusView.animate() - .translationXBy(100.0f) - .alpha(0.0f) - .setDuration(CRYPTO_ICON_OUT_DURATION) - .setInterpolator(CRYPTO_ICON_OUT_ANIMATOR) - .start(); - return; + .translationXBy(100.0f) + .alpha(0.0f) + .setDuration(CRYPTO_ICON_OUT_DURATION.toLong()) + .setInterpolator(CRYPTO_ICON_OUT_ANIMATOR) + .start() + + return } - cryptoStatusView.setVisibility(View.VISIBLE); - cryptoStatusView.setDisplayedChildId(cryptoStatusDisplayType.childIdToDisplay); + cryptoStatusView.isVisible = true + cryptoStatusView.displayedChildId = cryptoStatusDisplayType.childIdToDisplay cryptoStatusView.animate() - .translationX(0.0f) - .alpha(1.0f) - .setDuration(CRYPTO_ICON_IN_DURATION) - .setInterpolator(CRYPTO_ICON_IN_ANIMATOR) - .start(); + .translationX(0.0f) + .alpha(1.0f) + .setDuration(CRYPTO_ICON_IN_DURATION.toLong()) + .setInterpolator(CRYPTO_ICON_IN_ANIMATOR) + .start() } - public void showContactPicker(int requestCode) { - activity.showContactPicker(requestCode); + fun showContactPicker(requestCode: Int) { + activity.showContactPicker(requestCode) } - public void showErrorIsSignOnly() { - Toast.makeText(activity, R.string.error_sign_only_no_encryption, Toast.LENGTH_LONG).show(); + fun showErrorIsSignOnly() { + Toast.makeText(activity, R.string.error_sign_only_no_encryption, Toast.LENGTH_LONG).show() } - public void showErrorContactNoAddress() { - Toast.makeText(activity, R.string.error_contact_address_not_found, Toast.LENGTH_LONG).show(); + fun showErrorContactNoAddress() { + Toast.makeText(activity, R.string.error_contact_address_not_found, Toast.LENGTH_LONG).show() } - public void showErrorOpenPgpRetrieveStatus() { - Toast.makeText(activity, R.string.error_recipient_crypto_retrieve, Toast.LENGTH_LONG).show(); + fun showErrorOpenPgpIncompatible() { + Toast.makeText(activity, R.string.error_crypto_provider_incompatible, Toast.LENGTH_LONG).show() } - public void showErrorOpenPgpIncompatible() { - Toast.makeText(activity, R.string.error_crypto_provider_incompatible, Toast.LENGTH_LONG).show(); + fun showErrorOpenPgpConnection() { + Toast.makeText(activity, R.string.error_crypto_provider_connect, Toast.LENGTH_LONG).show() } - public void showErrorOpenPgpConnection() { - Toast.makeText(activity, R.string.error_crypto_provider_connect, Toast.LENGTH_LONG).show(); + fun showErrorOpenPgpUserInteractionRequired() { + Toast.makeText(activity, R.string.error_crypto_provider_ui_required, Toast.LENGTH_LONG).show() } - public void showErrorOpenPgpUserInteractionRequired() { - Toast.makeText(activity, R.string.error_crypto_provider_ui_required, Toast.LENGTH_LONG).show(); + fun showErrorNoKeyConfigured() { + Toast.makeText(activity, R.string.compose_error_no_key_configured, Toast.LENGTH_LONG).show() } - public void showErrorNoKeyConfigured() { - Toast.makeText(activity, R.string.compose_error_no_key_configured, Toast.LENGTH_LONG).show(); + fun showErrorInlineAttach() { + Toast.makeText(activity, R.string.error_crypto_inline_attach, Toast.LENGTH_LONG).show() } - public void showErrorInlineAttach() { - Toast.makeText(activity, R.string.error_crypto_inline_attach, Toast.LENGTH_LONG).show(); - } + override fun onFocusChange(view: View, hasFocus: Boolean) { + if (!hasFocus) return - @Override - public void onFocusChange(View view, boolean hasFocus) { - if (!hasFocus) { - return; - } - - int id = view.getId(); - if (id == R.id.to) { - presenter.onToFocused(); - } else if (id == R.id.cc) { - presenter.onCcFocused(); - } else if (id == R.id.bcc) { - presenter.onBccFocused(); + when (view.id) { + R.id.to -> presenter.onToFocused() + R.id.cc -> presenter.onCcFocused() + R.id.bcc -> presenter.onBccFocused() } } - @Override - public void onClick(View view) { - int id = view.getId(); - if (id == R.id.to_label) { - presenter.onClickToLabel(); - } else if (id == R.id.cc_label) { - presenter.onClickCcLabel(); - } else if (id == R.id.bcc_label) { - presenter.onClickBccLabel(); - } else if (id == R.id.recipient_expander) { - presenter.onClickRecipientExpander(); - } else if (id == R.id.crypto_status) { - presenter.onClickCryptoStatus(); - } else if (id == R.id.crypto_special_mode) { - presenter.onClickCryptoSpecialModeIndicator(); + override fun onClick(view: View) { + when (view.id) { + R.id.to_label -> presenter.onClickToLabel() + R.id.cc_label -> presenter.onClickCcLabel() + R.id.bcc_label -> presenter.onClickBccLabel() + R.id.recipient_expander -> presenter.onClickRecipientExpander() + R.id.crypto_status -> presenter.onClickCryptoStatus() + R.id.crypto_special_mode -> presenter.onClickCryptoSpecialModeIndicator() } } - public void showOpenPgpInlineDialog(boolean firstTime) { - PgpInlineDialog dialog = PgpInlineDialog.newInstance(firstTime, R.id.crypto_special_mode); - dialog.show(activity.getSupportFragmentManager(), "openpgp_inline"); + fun showOpenPgpInlineDialog(firstTime: Boolean) { + val dialog = PgpInlineDialog.newInstance(firstTime, R.id.crypto_special_mode) + dialog.show(activity.supportFragmentManager, "openpgp_inline") } - public void showOpenPgpSignOnlyDialog(boolean firstTime) { - PgpSignOnlyDialog dialog = PgpSignOnlyDialog.newInstance(firstTime, R.id.crypto_special_mode); - dialog.show(activity.getSupportFragmentManager(), "openpgp_signonly"); + fun showOpenPgpSignOnlyDialog(firstTime: Boolean) { + val dialog = PgpSignOnlyDialog.newInstance(firstTime, R.id.crypto_special_mode) + dialog.show(activity.supportFragmentManager, "openpgp_signonly") } - public void showOpenPgpEnabledErrorDialog(final boolean isGotItDialog) { - PgpEnabledErrorDialog dialog = PgpEnabledErrorDialog.newInstance(isGotItDialog, R.id.crypto_status_anchor); - dialog.show(activity.getSupportFragmentManager(), "openpgp_error"); + fun showOpenPgpEnabledErrorDialog(isGotItDialog: Boolean) { + val dialog = PgpEnabledErrorDialog.newInstance(isGotItDialog, R.id.crypto_status_anchor) + dialog.show(activity.supportFragmentManager, "openpgp_error") } - public void showOpenPgpEncryptExplanationDialog() { - PgpEncryptDescriptionDialog dialog = PgpEncryptDescriptionDialog.newInstance(R.id.crypto_status_anchor); - dialog.show(activity.getSupportFragmentManager(), "openpgp_description"); + fun showOpenPgpEncryptExplanationDialog() { + val dialog = PgpEncryptDescriptionDialog.newInstance(R.id.crypto_status_anchor) + dialog.show(activity.supportFragmentManager, "openpgp_description") } - public void launchUserInteractionPendingIntent(PendingIntent pendingIntent, int requestCode) { - activity.launchUserInteractionPendingIntent(pendingIntent, requestCode); + fun launchUserInteractionPendingIntent(pendingIntent: PendingIntent?, requestCode: Int) { + activity.launchUserInteractionPendingIntent(pendingIntent, requestCode) } - public void setLoaderManager(LoaderManager loaderManager) { - toView.setLoaderManager(loaderManager); - ccView.setLoaderManager(loaderManager); - bccView.setLoaderManager(loaderManager); + fun setLoaderManager(loaderManager: LoaderManager?) { + toView.setLoaderManager(loaderManager) + ccView.setLoaderManager(loaderManager) + bccView.setLoaderManager(loaderManager) } - public enum CryptoStatusDisplayType { + enum class CryptoStatusDisplayType(val childIdToDisplay: Int) { UNCONFIGURED(VIEW_INDEX_HIDDEN), UNINITIALIZED(VIEW_INDEX_HIDDEN), SIGN_ONLY(R.id.crypto_status_disabled), @@ -518,26 +391,24 @@ public class RecipientMvpView implements OnFocusChangeListener, OnClickListener ENABLED_TRUSTED(R.id.crypto_status_trusted), AVAILABLE(R.id.crypto_status_disabled), ERROR(R.id.crypto_status_error); - - - final int childIdToDisplay; - - CryptoStatusDisplayType(int childIdToDisplay) { - this.childIdToDisplay = childIdToDisplay; - } } - public enum CryptoSpecialModeDisplayType { + enum class CryptoSpecialModeDisplayType(val childIdToDisplay: Int) { NONE(VIEW_INDEX_HIDDEN), PGP_INLINE(R.id.crypto_special_inline), SIGN_ONLY(R.id.crypto_special_sign_only), SIGN_ONLY_PGP_INLINE(R.id.crypto_special_sign_only_inline); + } + companion object { + private const val VIEW_INDEX_HIDDEN = -1 + private const val VIEW_INDEX_BCC_EXPANDER_VISIBLE = 0 + private const val VIEW_INDEX_BCC_EXPANDER_HIDDEN = 1 - final int childIdToDisplay; + private val CRYPTO_ICON_OUT_ANIMATOR = FastOutLinearInInterpolator() + private const val CRYPTO_ICON_OUT_DURATION = 195 - CryptoSpecialModeDisplayType(int childIdToDisplay) { - this.childIdToDisplay = childIdToDisplay; - } + private val CRYPTO_ICON_IN_ANIMATOR = LinearOutSlowInInterpolator() + private const val CRYPTO_ICON_IN_DURATION = 225 } } diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientPresenter.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientPresenter.kt index 7aaf4a8b3..9b0e6d2ed 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientPresenter.kt +++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientPresenter.kt @@ -1,886 +1,770 @@ -package com.fsck.k9.activity.compose; +package com.fsck.k9.activity.compose +import android.Manifest +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.AsyncTask +import android.os.Bundle +import android.view.Menu +import androidx.core.content.ContextCompat +import androidx.loader.app.LoaderManager +import com.fsck.k9.Account +import com.fsck.k9.Identity +import com.fsck.k9.K9 +import com.fsck.k9.activity.compose.ComposeCryptoStatus.AttachErrorState +import com.fsck.k9.activity.compose.ComposeCryptoStatus.SendErrorState +import com.fsck.k9.autocrypt.AutocryptDraftStateHeader +import com.fsck.k9.autocrypt.AutocryptDraftStateHeaderParser +import com.fsck.k9.helper.Contacts +import com.fsck.k9.helper.MailTo +import com.fsck.k9.helper.ReplyToParser +import com.fsck.k9.mail.Address +import com.fsck.k9.mail.Flag +import com.fsck.k9.mail.Message +import com.fsck.k9.mail.Message.RecipientType +import com.fsck.k9.message.AutocryptStatusInteractor +import com.fsck.k9.message.AutocryptStatusInteractor.RecipientAutocryptStatus +import com.fsck.k9.message.ComposePgpEnableByDefaultDecider +import com.fsck.k9.message.ComposePgpInlineDecider +import com.fsck.k9.message.MessageBuilder +import com.fsck.k9.message.PgpMessageBuilder +import com.fsck.k9.ui.R +import com.fsck.k9.view.RecipientSelectView.Recipient +import org.openintents.openpgp.OpenPgpApiManager +import org.openintents.openpgp.OpenPgpApiManager.OpenPgpApiManagerCallback +import org.openintents.openpgp.OpenPgpApiManager.OpenPgpProviderError +import org.openintents.openpgp.OpenPgpApiManager.OpenPgpProviderState +import timber.log.Timber -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +private const val STATE_KEY_CC_SHOWN = "state:ccShown" +private const val STATE_KEY_BCC_SHOWN = "state:bccShown" +private const val STATE_KEY_LAST_FOCUSED_TYPE = "state:lastFocusedType" +private const val STATE_KEY_CURRENT_CRYPTO_MODE = "state:currentCryptoMode" +private const val STATE_KEY_CRYPTO_ENABLE_PGP_INLINE = "state:cryptoEnablePgpInline" -import android.Manifest; -import android.app.Activity; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import androidx.loader.app.LoaderManager; -import android.view.Menu; +private const val CONTACT_PICKER_TO = 1 +private const val CONTACT_PICKER_CC = 2 +private const val CONTACT_PICKER_BCC = 3 +private const val OPENPGP_USER_INTERACTION = 4 +private const val REQUEST_CODE_AUTOCRYPT = 5 -import com.fsck.k9.Account; -import com.fsck.k9.Identity; -import com.fsck.k9.K9; -import com.fsck.k9.activity.compose.ComposeCryptoStatus.AttachErrorState; -import com.fsck.k9.activity.compose.ComposeCryptoStatus.SendErrorState; -import com.fsck.k9.activity.compose.RecipientMvpView.CryptoStatusDisplayType; -import com.fsck.k9.autocrypt.AutocryptDraftStateHeader; -import com.fsck.k9.autocrypt.AutocryptDraftStateHeaderParser; -import com.fsck.k9.helper.Contacts; -import com.fsck.k9.helper.MailTo; -import com.fsck.k9.helper.ReplyToParser; -import com.fsck.k9.helper.ReplyToParser.ReplyToAddresses; -import com.fsck.k9.mail.Address; -import com.fsck.k9.mail.Flag; -import com.fsck.k9.mail.Message; -import com.fsck.k9.mail.Message.RecipientType; -import com.fsck.k9.message.AutocryptStatusInteractor; -import com.fsck.k9.message.AutocryptStatusInteractor.RecipientAutocryptStatus; -import com.fsck.k9.message.ComposePgpEnableByDefaultDecider; -import com.fsck.k9.message.ComposePgpInlineDecider; -import com.fsck.k9.message.MessageBuilder; -import com.fsck.k9.message.PgpMessageBuilder; -import com.fsck.k9.ui.R; -import com.fsck.k9.view.RecipientSelectView.Recipient; -import org.openintents.openpgp.OpenPgpApiManager; -import org.openintents.openpgp.OpenPgpApiManager.OpenPgpApiManagerCallback; -import org.openintents.openpgp.OpenPgpApiManager.OpenPgpProviderError; -import org.openintents.openpgp.OpenPgpApiManager.OpenPgpProviderState; -import org.openintents.openpgp.util.OpenPgpApi; -import timber.log.Timber; +private const val PGP_DIALOG_DISPLAY_THRESHOLD = 2 +class RecipientPresenter( + private val context: Context, + loaderManager: LoaderManager, + private val openPgpApiManager: OpenPgpApiManager, + private val recipientMvpView: RecipientMvpView, + account: Account, + private val composePgpInlineDecider: ComposePgpInlineDecider, + private val composePgpEnableByDefaultDecider: ComposePgpEnableByDefaultDecider, + private val autocryptStatusInteractor: AutocryptStatusInteractor, + private val replyToParser: ReplyToParser, + private val draftStateHeaderParser: AutocryptDraftStateHeaderParser +) { + private lateinit var account: Account + private var alwaysBccAddresses: Array
? = null + private var hasContactPicker: Boolean? = null + private var isReplyToEncryptedMessage = false -public class RecipientPresenter { - private static final String STATE_KEY_CC_SHOWN = "state:ccShown"; - private static final String STATE_KEY_BCC_SHOWN = "state:bccShown"; - private static final String STATE_KEY_LAST_FOCUSED_TYPE = "state:lastFocusedType"; - private static final String STATE_KEY_CURRENT_CRYPTO_MODE = "state:currentCryptoMode"; - private static final String STATE_KEY_CRYPTO_ENABLE_PGP_INLINE = "state:cryptoEnablePgpInline"; + private var lastFocusedType = RecipientType.TO + private var currentCryptoMode = CryptoMode.NO_CHOICE - private static final int CONTACT_PICKER_TO = 1; - private static final int CONTACT_PICKER_CC = 2; - private static final int CONTACT_PICKER_BCC = 3; - private static final int OPENPGP_USER_INTERACTION = 4; - private static final int REQUEST_CODE_AUTOCRYPT = 5; + var isForceTextMessageFormat = false + private set - private static final int PGP_DIALOG_DISPLAY_THRESHOLD = 2; + var currentCachedCryptoStatus: ComposeCryptoStatus? = null + private set + val toAddresses: List
+ get() = recipientMvpView.toAddresses - // transient state, which is either obtained during construction and initialization, or cached - private final Context context; - private final RecipientMvpView recipientMvpView; - private final ComposePgpEnableByDefaultDecider composePgpEnableByDefaultDecider; - private final ComposePgpInlineDecider composePgpInlineDecider; - private final AutocryptStatusInteractor autocryptStatusInteractor; - private final OpenPgpApiManager openPgpApiManager; - private final AutocryptDraftStateHeaderParser draftStateHeaderParser; - private ReplyToParser replyToParser; - private Account account; - private Address[] alwaysBccAddresses; - private Boolean hasContactPicker; - @Nullable - private ComposeCryptoStatus cachedCryptoStatus; + val ccAddresses: List
+ get() = recipientMvpView.ccAddresses + val bccAddresses: List
+ get() = recipientMvpView.bccAddresses - // persistent state, saved during onSaveInstanceState - private RecipientType lastFocusedType = RecipientType.TO; - private CryptoMode currentCryptoMode = CryptoMode.NO_CHOICE; - private boolean cryptoEnablePgpInline = false; - private boolean isReplyToEncryptedMessage = false; + private val allRecipients: List + get() = with(recipientMvpView) { toRecipients + ccRecipients + bccRecipients } + init { + recipientMvpView.setPresenter(this) + recipientMvpView.setLoaderManager(loaderManager) - public RecipientPresenter(Context context, LoaderManager loaderManager, - OpenPgpApiManager openPgpApiManager, RecipientMvpView recipientMvpView, Account account, - ComposePgpInlineDecider composePgpInlineDecider, - ComposePgpEnableByDefaultDecider composePgpEnableByDefaultDecider, - AutocryptStatusInteractor autocryptStatusInteractor, - ReplyToParser replyToParser, AutocryptDraftStateHeaderParser draftStateHeaderParser) { - this.recipientMvpView = recipientMvpView; - this.context = context; - this.autocryptStatusInteractor = autocryptStatusInteractor; - this.composePgpInlineDecider = composePgpInlineDecider; - this.composePgpEnableByDefaultDecider = composePgpEnableByDefaultDecider; - this.replyToParser = replyToParser; - this.openPgpApiManager = openPgpApiManager; - this.draftStateHeaderParser = draftStateHeaderParser; - - recipientMvpView.setPresenter(this); - recipientMvpView.setLoaderManager(loaderManager); - onSwitchAccount(account); + onSwitchAccount(account) } - public List
getToAddresses() { - return recipientMvpView.getToAddresses(); - } - - public List
getCcAddresses() { - return recipientMvpView.getCcAddresses(); - } - - public List
getBccAddresses() { - return recipientMvpView.getBccAddresses(); - } - - private List getAllRecipients() { - ArrayList result = new ArrayList<>(); - - result.addAll(recipientMvpView.getToRecipients()); - result.addAll(recipientMvpView.getCcRecipients()); - result.addAll(recipientMvpView.getBccRecipients()); - - return result; - } - - public boolean checkRecipientsOkForSending() { - recipientMvpView.recipientToTryPerformCompletion(); - recipientMvpView.recipientCcTryPerformCompletion(); - recipientMvpView.recipientBccTryPerformCompletion(); + fun checkRecipientsOkForSending(): Boolean { + recipientMvpView.recipientToTryPerformCompletion() + recipientMvpView.recipientCcTryPerformCompletion() + recipientMvpView.recipientBccTryPerformCompletion() if (recipientMvpView.recipientToHasUncompletedText()) { - recipientMvpView.showToUncompletedError(); - return true; + recipientMvpView.showToUncompletedError() + return true } if (recipientMvpView.recipientCcHasUncompletedText()) { - recipientMvpView.showCcUncompletedError(); - return true; + recipientMvpView.showCcUncompletedError() + return true } if (recipientMvpView.recipientBccHasUncompletedText()) { - recipientMvpView.showBccUncompletedError(); - return true; + recipientMvpView.showBccUncompletedError() + return true } - if (getToAddresses().isEmpty() && getCcAddresses().isEmpty() && getBccAddresses().isEmpty()) { - recipientMvpView.showNoRecipientsError(); - return true; + if (toAddresses.isEmpty() && ccAddresses.isEmpty() && bccAddresses.isEmpty()) { + recipientMvpView.showNoRecipientsError() + return true } - return false; + return false } - public void initFromReplyToMessage(Message message, boolean isReplyAll) { - ReplyToAddresses replyToAddresses = isReplyAll ? - replyToParser.getRecipientsToReplyAllTo(message, account) : - replyToParser.getRecipientsToReplyTo(message, account); + fun initFromReplyToMessage(message: Message?, isReplyAll: Boolean) { + val replyToAddresses = if (isReplyAll) { + replyToParser.getRecipientsToReplyAllTo(message, account) + } else { + replyToParser.getRecipientsToReplyTo(message, account) + } - addToAddresses(replyToAddresses.to); - addCcAddresses(replyToAddresses.cc); + addToAddresses(*replyToAddresses.to) + addCcAddresses(*replyToAddresses.cc) - boolean shouldSendAsPgpInline = composePgpInlineDecider.shouldReplyInline(message); + val shouldSendAsPgpInline = composePgpInlineDecider.shouldReplyInline(message) if (shouldSendAsPgpInline) { - cryptoEnablePgpInline = true; + isForceTextMessageFormat = true } - isReplyToEncryptedMessage = composePgpEnableByDefaultDecider.shouldEncryptByDefault(message); + isReplyToEncryptedMessage = composePgpEnableByDefaultDecider.shouldEncryptByDefault(message) } - public void initFromTrustIdAction(String trustId) { - addToAddresses(Address.parse(trustId)); - currentCryptoMode = CryptoMode.CHOICE_ENABLED; + fun initFromTrustIdAction(trustId: String?) { + addToAddresses(*Address.parse(trustId)) + currentCryptoMode = CryptoMode.CHOICE_ENABLED } - public void initFromMailto(MailTo mailTo) { - addToAddresses(mailTo.getTo()); - addCcAddresses(mailTo.getCc()); - addBccAddresses(mailTo.getBcc()); + fun initFromMailto(mailTo: MailTo) { + addToAddresses(*mailTo.to) + addCcAddresses(*mailTo.cc) + addBccAddresses(*mailTo.bcc) } - public void initFromSendOrViewIntent(Intent intent) { - String[] extraEmail = intent.getStringArrayExtra(Intent.EXTRA_EMAIL); - String[] extraCc = intent.getStringArrayExtra(Intent.EXTRA_CC); - String[] extraBcc = intent.getStringArrayExtra(Intent.EXTRA_BCC); + fun initFromSendOrViewIntent(intent: Intent) { + val toAddresses = intent.getStringArrayExtra(Intent.EXTRA_EMAIL)?.toAddressArray() + val ccAddresses = intent.getStringArrayExtra(Intent.EXTRA_CC)?.toAddressArray() + val bccAddresses = intent.getStringArrayExtra(Intent.EXTRA_BCC)?.toAddressArray() - if (extraEmail != null) { - addToAddresses(addressFromStringArray(extraEmail)); + if (toAddresses != null) { + addToAddresses(*toAddresses) } - if (extraCc != null) { - addCcAddresses(addressFromStringArray(extraCc)); + if (ccAddresses != null) { + addCcAddresses(*ccAddresses) } - if (extraBcc != null) { - addBccAddresses(addressFromStringArray(extraBcc)); + if (bccAddresses != null) { + addBccAddresses(*bccAddresses) } } - public void onRestoreInstanceState(Bundle savedInstanceState) { - recipientMvpView.setCcVisibility(savedInstanceState.getBoolean(STATE_KEY_CC_SHOWN)); - recipientMvpView.setBccVisibility(savedInstanceState.getBoolean(STATE_KEY_BCC_SHOWN)); - lastFocusedType = RecipientType.valueOf(savedInstanceState.getString(STATE_KEY_LAST_FOCUSED_TYPE)); - currentCryptoMode = CryptoMode.valueOf(savedInstanceState.getString(STATE_KEY_CURRENT_CRYPTO_MODE)); - cryptoEnablePgpInline = savedInstanceState.getBoolean(STATE_KEY_CRYPTO_ENABLE_PGP_INLINE); - updateRecipientExpanderVisibility(); + fun onRestoreInstanceState(savedInstanceState: Bundle) { + recipientMvpView.setCcVisibility(savedInstanceState.getBoolean(STATE_KEY_CC_SHOWN)) + recipientMvpView.setBccVisibility(savedInstanceState.getBoolean(STATE_KEY_BCC_SHOWN)) + lastFocusedType = RecipientType.valueOf(savedInstanceState.getString(STATE_KEY_LAST_FOCUSED_TYPE)!!) + currentCryptoMode = CryptoMode.valueOf(savedInstanceState.getString(STATE_KEY_CURRENT_CRYPTO_MODE)!!) + isForceTextMessageFormat = savedInstanceState.getBoolean(STATE_KEY_CRYPTO_ENABLE_PGP_INLINE) + + updateRecipientExpanderVisibility() } - public void onSaveInstanceState(Bundle outState) { - outState.putBoolean(STATE_KEY_CC_SHOWN, recipientMvpView.isCcVisible()); - outState.putBoolean(STATE_KEY_BCC_SHOWN, recipientMvpView.isBccVisible()); - outState.putString(STATE_KEY_LAST_FOCUSED_TYPE, lastFocusedType.toString()); - outState.putString(STATE_KEY_CURRENT_CRYPTO_MODE, currentCryptoMode.toString()); - outState.putBoolean(STATE_KEY_CRYPTO_ENABLE_PGP_INLINE, cryptoEnablePgpInline); + fun onSaveInstanceState(outState: Bundle) { + outState.putBoolean(STATE_KEY_CC_SHOWN, recipientMvpView.isCcVisible) + outState.putBoolean(STATE_KEY_BCC_SHOWN, recipientMvpView.isBccVisible) + outState.putString(STATE_KEY_LAST_FOCUSED_TYPE, lastFocusedType.toString()) + outState.putString(STATE_KEY_CURRENT_CRYPTO_MODE, currentCryptoMode.toString()) + outState.putBoolean(STATE_KEY_CRYPTO_ENABLE_PGP_INLINE, isForceTextMessageFormat) } - public void initFromDraftMessage(Message message) { - initRecipientsFromDraftMessage(message); + fun initFromDraftMessage(message: Message) { + initRecipientsFromDraftMessage(message) - String[] draftStateHeader = message.getHeader(AutocryptDraftStateHeader.AUTOCRYPT_DRAFT_STATE_HEADER); - if (draftStateHeader.length == 1) { - initEncryptionStateFromDraftStateHeader(draftStateHeader[0]); + val draftStateHeader = message.getHeader(AutocryptDraftStateHeader.AUTOCRYPT_DRAFT_STATE_HEADER) + if (draftStateHeader.size == 1) { + initEncryptionStateFromDraftStateHeader(draftStateHeader.first()) } else { - initPgpInlineFromDraftMessage(message); + initPgpInlineFromDraftMessage(message) } } - private void initEncryptionStateFromDraftStateHeader(String headerValue) { - AutocryptDraftStateHeader autocryptDraftStateHeader = - draftStateHeaderParser.parseAutocryptDraftStateHeader(headerValue); + private fun initEncryptionStateFromDraftStateHeader(headerValue: String) { + val autocryptDraftStateHeader = draftStateHeaderParser.parseAutocryptDraftStateHeader(headerValue) if (autocryptDraftStateHeader != null) { - initEncryptionStateFromDraftStateHeader(autocryptDraftStateHeader); + initEncryptionStateFromDraftStateHeader(autocryptDraftStateHeader) } } - private void initRecipientsFromDraftMessage(Message message) { - addToAddresses(message.getRecipients(RecipientType.TO)); - - Address[] ccRecipients = message.getRecipients(RecipientType.CC); - addCcAddresses(ccRecipients); - - Address[] bccRecipients = message.getRecipients(RecipientType.BCC); - addBccAddresses(bccRecipients); + private fun initRecipientsFromDraftMessage(message: Message) { + addToAddresses(*message.getRecipients(RecipientType.TO)) + addCcAddresses(*message.getRecipients(RecipientType.CC)) + addBccAddresses(*message.getRecipients(RecipientType.BCC)) } - private void initEncryptionStateFromDraftStateHeader(AutocryptDraftStateHeader draftState) { - cryptoEnablePgpInline = draftState.isPgpInline(); - isReplyToEncryptedMessage = draftState.isReply(); - if (!draftState.isByChoice()) { + private fun initEncryptionStateFromDraftStateHeader(draftState: AutocryptDraftStateHeader) { + isForceTextMessageFormat = draftState.isPgpInline + isReplyToEncryptedMessage = draftState.isReply + + if (!draftState.isByChoice) { // TODO if it's not by choice, we're going with our defaults. should we do something here if those differ? - return; + return } - if (draftState.isSignOnly()) { - currentCryptoMode = CryptoMode.SIGN_ONLY; - } else { - currentCryptoMode = draftState.isEncrypt() ? CryptoMode.CHOICE_ENABLED : CryptoMode.CHOICE_DISABLED; + currentCryptoMode = when { + draftState.isSignOnly -> CryptoMode.SIGN_ONLY + draftState.isEncrypt -> CryptoMode.CHOICE_ENABLED + else -> CryptoMode.CHOICE_DISABLED } } - private void initPgpInlineFromDraftMessage(Message message) { - cryptoEnablePgpInline = message.isSet(Flag.X_DRAFT_OPENPGP_INLINE); + private fun initPgpInlineFromDraftMessage(message: Message) { + isForceTextMessageFormat = message.isSet(Flag.X_DRAFT_OPENPGP_INLINE) } - private void addToAddresses(Address... toAddresses) { - addRecipientsFromAddresses(RecipientType.TO, toAddresses); + private fun addToAddresses(vararg toAddresses: Address) { + addRecipientsFromAddresses(RecipientType.TO, *toAddresses) } - private void addCcAddresses(Address... ccAddresses) { - if (ccAddresses.length > 0) { - addRecipientsFromAddresses(RecipientType.CC, ccAddresses); - recipientMvpView.setCcVisibility(true); - updateRecipientExpanderVisibility(); + private fun addCcAddresses(vararg ccAddresses: Address) { + if (ccAddresses.isNotEmpty()) { + addRecipientsFromAddresses(RecipientType.CC, *ccAddresses) + recipientMvpView.setCcVisibility(true) + updateRecipientExpanderVisibility() } } - public void addBccAddresses(Address... bccRecipients) { - if (bccRecipients.length > 0) { - addRecipientsFromAddresses(RecipientType.BCC, bccRecipients); - recipientMvpView.setBccVisibility(true); - updateRecipientExpanderVisibility(); + private fun addBccAddresses(vararg bccRecipients: Address) { + if (bccRecipients.isNotEmpty()) { + addRecipientsFromAddresses(RecipientType.BCC, *bccRecipients) + recipientMvpView.setBccVisibility(true) + updateRecipientExpanderVisibility() } } - public void addAlwaysBcc() { - alwaysBccAddresses = Address.parse(account.getAlwaysBcc()); + private fun addAlwaysBcc() { + val alwaysBccAddresses = Address.parse(account.alwaysBcc) + this.alwaysBccAddresses = alwaysBccAddresses + if (alwaysBccAddresses.isEmpty()) return - new RecipientLoader(context, account.getOpenPgpProvider(), alwaysBccAddresses) { - @Override - public void deliverResult(List result) { - Recipient[] recipientArray = result.toArray(new Recipient[result.size()]); - recipientMvpView.silentlyAddBccAddresses(recipientArray); + object : RecipientLoader(context, account.openPgpProvider, *alwaysBccAddresses) { + override fun deliverResult(result: List?) { + val recipientArray = result!!.toTypedArray() + recipientMvpView.silentlyAddBccAddresses(*recipientArray) - stopLoading(); - abandon(); + stopLoading() + abandon() } - }.startLoading(); + }.startLoading() } - private void removeAlwaysBcc() { - if (alwaysBccAddresses != null) { - recipientMvpView.silentlyRemoveBccAddresses(alwaysBccAddresses); + private fun removeAlwaysBcc() { + alwaysBccAddresses?.let { alwaysBccAddresses -> + recipientMvpView.silentlyRemoveBccAddresses(alwaysBccAddresses) } } - public void onPrepareOptionsMenu(Menu menu) { - ComposeCryptoStatus currentCryptoStatus = getCurrentCachedCryptoStatus(); - boolean isCryptoConfigured = currentCryptoStatus != null && currentCryptoStatus.isProviderStateOk(); - if (isCryptoConfigured) { - boolean isEncrypting = currentCryptoStatus.isEncryptionEnabled(); - menu.findItem(R.id.openpgp_encrypt_enable).setVisible(!isEncrypting); - menu.findItem(R.id.openpgp_encrypt_disable).setVisible(isEncrypting); + fun onPrepareOptionsMenu(menu: Menu) { + val currentCryptoStatus = currentCachedCryptoStatus - boolean showSignOnly = !account.isOpenPgpHideSignOnly(); - boolean isSignOnly = currentCryptoStatus.isSignOnly(); - menu.findItem(R.id.openpgp_sign_only).setVisible(showSignOnly && !isSignOnly); - menu.findItem(R.id.openpgp_sign_only_disable).setVisible(showSignOnly && isSignOnly); + if (currentCryptoStatus != null && currentCryptoStatus.isProviderStateOk()) { + val isEncrypting = currentCryptoStatus.isEncryptionEnabled + menu.findItem(R.id.openpgp_encrypt_enable).isVisible = !isEncrypting + menu.findItem(R.id.openpgp_encrypt_disable).isVisible = isEncrypting - boolean pgpInlineModeEnabled = currentCryptoStatus.isPgpInlineModeEnabled(); - boolean showPgpInlineEnable = (isEncrypting || isSignOnly) && !pgpInlineModeEnabled; - menu.findItem(R.id.openpgp_inline_enable).setVisible(showPgpInlineEnable); - menu.findItem(R.id.openpgp_inline_disable).setVisible(pgpInlineModeEnabled); + val showSignOnly = !account.isOpenPgpHideSignOnly + val isSignOnly = currentCryptoStatus.isSignOnly + menu.findItem(R.id.openpgp_sign_only).isVisible = showSignOnly && !isSignOnly + menu.findItem(R.id.openpgp_sign_only_disable).isVisible = showSignOnly && isSignOnly + + val pgpInlineModeEnabled = currentCryptoStatus.isPgpInlineModeEnabled + val showPgpInlineEnable = (isEncrypting || isSignOnly) && !pgpInlineModeEnabled + menu.findItem(R.id.openpgp_inline_enable).isVisible = showPgpInlineEnable + menu.findItem(R.id.openpgp_inline_disable).isVisible = pgpInlineModeEnabled } else { - menu.findItem(R.id.openpgp_inline_enable).setVisible(false); - menu.findItem(R.id.openpgp_inline_disable).setVisible(false); - menu.findItem(R.id.openpgp_encrypt_enable).setVisible(false); - menu.findItem(R.id.openpgp_encrypt_disable).setVisible(false); - menu.findItem(R.id.openpgp_sign_only).setVisible(false); - menu.findItem(R.id.openpgp_sign_only_disable).setVisible(false); + menu.findItem(R.id.openpgp_inline_enable).isVisible = false + menu.findItem(R.id.openpgp_inline_disable).isVisible = false + menu.findItem(R.id.openpgp_encrypt_enable).isVisible = false + menu.findItem(R.id.openpgp_encrypt_disable).isVisible = false + menu.findItem(R.id.openpgp_sign_only).isVisible = false + menu.findItem(R.id.openpgp_sign_only_disable).isVisible = false } - menu.findItem(R.id.add_from_contacts).setVisible(hasContactPicker() && hasContactPermission()); + menu.findItem(R.id.add_from_contacts).isVisible = hasContactPermission() && hasContactPicker() } - public void onSwitchAccount(Account account) { - this.account = account; + fun onSwitchAccount(account: Account) { + this.account = account - if (account.isAlwaysShowCcBcc()) { - recipientMvpView.setCcVisibility(true); - recipientMvpView.setBccVisibility(true); - updateRecipientExpanderVisibility(); + if (account.isAlwaysShowCcBcc) { + recipientMvpView.setCcVisibility(true) + recipientMvpView.setBccVisibility(true) + updateRecipientExpanderVisibility() } - removeAlwaysBcc(); - addAlwaysBcc(); + removeAlwaysBcc() + addAlwaysBcc() - String openPgpProvider = account.getOpenPgpProvider(); - recipientMvpView.setCryptoProvider(openPgpProvider); - openPgpApiManager.setOpenPgpProvider(openPgpProvider, openPgpCallback); + val openPgpProvider = account.openPgpProvider + recipientMvpView.setCryptoProvider(openPgpProvider) + openPgpApiManager.setOpenPgpProvider(openPgpProvider, openPgpCallback) } - @SuppressWarnings("UnusedParameters") - public void onSwitchIdentity(Identity identity) { - + fun onSwitchIdentity(identity: Identity) { // TODO decide what actually to do on identity switch? - asyncUpdateCryptoStatus(); - /* - if (mIdentityChanged) { - mBccWrapper.setVisibility(View.VISIBLE); - } - mBccView.setText(""); - mBccView.addAddress(new Address(mAccount.getAlwaysBcc(), "")); - */ - + asyncUpdateCryptoStatus() } - private static Address[] addressFromStringArray(String[] addresses) { - return addressFromStringArray(Arrays.asList(addresses)); + fun onClickToLabel() { + recipientMvpView.requestFocusOnToField() } - private static Address[] addressFromStringArray(List addresses) { - ArrayList
result = new ArrayList<>(addresses.size()); - - for (String addressStr : addresses) { - Collections.addAll(result, Address.parseUnencoded(addressStr)); - } - - return result.toArray(new Address[result.size()]); + fun onClickCcLabel() { + recipientMvpView.requestFocusOnCcField() } - void onClickToLabel() { - recipientMvpView.requestFocusOnToField(); + fun onClickBccLabel() { + recipientMvpView.requestFocusOnBccField() } - void onClickCcLabel() { - recipientMvpView.requestFocusOnCcField(); + fun onClickRecipientExpander() { + recipientMvpView.setCcVisibility(true) + recipientMvpView.setBccVisibility(true) + updateRecipientExpanderVisibility() } - void onClickBccLabel() { - recipientMvpView.requestFocusOnBccField(); - } - - void onClickRecipientExpander() { - recipientMvpView.setCcVisibility(true); - recipientMvpView.setBccVisibility(true); - updateRecipientExpanderVisibility(); - } - - private void hideEmptyExtendedRecipientFields() { - if (recipientMvpView.getCcAddresses().isEmpty()) { - recipientMvpView.setCcVisibility(false); + private fun hideEmptyExtendedRecipientFields() { + if (recipientMvpView.ccAddresses.isEmpty()) { + recipientMvpView.setCcVisibility(false) if (lastFocusedType == RecipientType.CC) { - lastFocusedType = RecipientType.TO; + lastFocusedType = RecipientType.TO } } - if (recipientMvpView.getBccAddresses().isEmpty()) { - recipientMvpView.setBccVisibility(false); + + if (recipientMvpView.bccAddresses.isEmpty()) { + recipientMvpView.setBccVisibility(false) if (lastFocusedType == RecipientType.BCC) { - lastFocusedType = RecipientType.TO; + lastFocusedType = RecipientType.TO } } - updateRecipientExpanderVisibility(); + + updateRecipientExpanderVisibility() } - private void updateRecipientExpanderVisibility() { - boolean notBothAreVisible = !(recipientMvpView.isCcVisible() && recipientMvpView.isBccVisible()); - recipientMvpView.setRecipientExpanderVisibility(notBothAreVisible); + private fun updateRecipientExpanderVisibility() { + val notBothAreVisible = !(recipientMvpView.isCcVisible && recipientMvpView.isBccVisible) + recipientMvpView.setRecipientExpanderVisibility(notBothAreVisible) } - public void asyncUpdateCryptoStatus() { - cachedCryptoStatus = null; + fun asyncUpdateCryptoStatus() { + currentCachedCryptoStatus = null - OpenPgpProviderState openPgpProviderState = openPgpApiManager.getOpenPgpProviderState(); - - Long accountCryptoKey = account.getOpenPgpKey(); + val openPgpProviderState = openPgpApiManager.openPgpProviderState + var accountCryptoKey: Long? = account.openPgpKey if (accountCryptoKey == Account.NO_OPENPGP_KEY) { - accountCryptoKey = null; + accountCryptoKey = null } - final ComposeCryptoStatus composeCryptoStatus = new ComposeCryptoStatus( - openPgpProviderState, - accountCryptoKey, - getAllRecipients(), - cryptoEnablePgpInline, - account.getAutocryptPreferEncryptMutual(), - isReplyToEncryptedMessage, - account.isOpenPgpEncryptAllDrafts(), - account.isOpenPgpEncryptSubject(), - currentCryptoMode); + val composeCryptoStatus = ComposeCryptoStatus( + openPgpProviderState = openPgpProviderState, + openPgpKeyId = accountCryptoKey, + recipientAddresses = allRecipients, + isPgpInlineModeEnabled = isForceTextMessageFormat, + isSenderPreferEncryptMutual = account.autocryptPreferEncryptMutual, + isReplyToEncrypted = isReplyToEncryptedMessage, + isEncryptAllDrafts = account.isOpenPgpEncryptAllDrafts, + isEncryptSubject = account.isOpenPgpEncryptSubject, + cryptoMode = currentCryptoMode + ) if (openPgpProviderState != OpenPgpProviderState.OK) { - cachedCryptoStatus = composeCryptoStatus; - redrawCachedCryptoStatusIcon(); - return; + currentCachedCryptoStatus = composeCryptoStatus + redrawCachedCryptoStatusIcon() + return } - final String[] recipientAddresses = composeCryptoStatus.getRecipientAddressesAsArray(); - - new AsyncTask() { - @Override - protected RecipientAutocryptStatus doInBackground(Void... voids) { - OpenPgpApi openPgpApi = openPgpApiManager.getOpenPgpApi(); - if (openPgpApi == null) { - return null; - } - return autocryptStatusInteractor.retrieveCryptoProviderRecipientStatus(openPgpApi, recipientAddresses); + val recipientAddresses = composeCryptoStatus.recipientAddressesAsArray + object : AsyncTask() { + override fun doInBackground(vararg params: Void?): RecipientAutocryptStatus? { + val openPgpApi = openPgpApiManager.openPgpApi ?: return null + return autocryptStatusInteractor.retrieveCryptoProviderRecipientStatus(openPgpApi, recipientAddresses) } - @Override - protected void onPostExecute(RecipientAutocryptStatus recipientAutocryptStatus) { - if (recipientAutocryptStatus != null) { - cachedCryptoStatus = composeCryptoStatus.withRecipientAutocryptStatus(recipientAutocryptStatus); + override fun onPostExecute(recipientAutocryptStatus: RecipientAutocryptStatus?) { + currentCachedCryptoStatus = if (recipientAutocryptStatus != null) { + composeCryptoStatus.withRecipientAutocryptStatus(recipientAutocryptStatus) } else { - cachedCryptoStatus = composeCryptoStatus; + composeCryptoStatus } - redrawCachedCryptoStatusIcon(); + redrawCachedCryptoStatusIcon() } - }.execute(); + }.execute() } - private void redrawCachedCryptoStatusIcon() { - if (cachedCryptoStatus == null) { - throw new IllegalStateException("must have cached crypto status to redraw it!"); - } + private fun redrawCachedCryptoStatusIcon() { + val cryptoStatus = checkNotNull(currentCachedCryptoStatus) { "must have cached crypto status to redraw it!" } - recipientMvpView.setRecipientTokensShowCryptoEnabled(cachedCryptoStatus.isEncryptionEnabled()); - - CryptoStatusDisplayType cryptoStatusDisplayType = cachedCryptoStatus.getDisplayType(); - recipientMvpView.showCryptoStatus(cryptoStatusDisplayType); - recipientMvpView.showCryptoSpecialMode(cachedCryptoStatus.getSpecialModeDisplayType()); + recipientMvpView.setRecipientTokensShowCryptoEnabled(cryptoStatus.isEncryptionEnabled) + recipientMvpView.showCryptoStatus(cryptoStatus.displayType) + recipientMvpView.showCryptoSpecialMode(cryptoStatus.specialModeDisplayType) } - @Nullable - public ComposeCryptoStatus getCurrentCachedCryptoStatus() { - return cachedCryptoStatus; + fun onToTokenAdded() { + asyncUpdateCryptoStatus() } - public boolean isForceTextMessageFormat() { - return cryptoEnablePgpInline; + fun onToTokenRemoved() { + asyncUpdateCryptoStatus() } - void onToTokenAdded() { - asyncUpdateCryptoStatus(); + fun onToTokenChanged() { + asyncUpdateCryptoStatus() } - void onToTokenRemoved() { - asyncUpdateCryptoStatus(); + fun onCcTokenAdded() { + asyncUpdateCryptoStatus() } - void onToTokenChanged() { - asyncUpdateCryptoStatus(); + fun onCcTokenRemoved() { + asyncUpdateCryptoStatus() } - void onCcTokenAdded() { - asyncUpdateCryptoStatus(); + fun onCcTokenChanged() { + asyncUpdateCryptoStatus() } - void onCcTokenRemoved() { - asyncUpdateCryptoStatus(); + fun onBccTokenAdded() { + asyncUpdateCryptoStatus() } - void onCcTokenChanged() { - asyncUpdateCryptoStatus(); + fun onBccTokenRemoved() { + asyncUpdateCryptoStatus() } - void onBccTokenAdded() { - asyncUpdateCryptoStatus(); + fun onBccTokenChanged() { + asyncUpdateCryptoStatus() } - void onBccTokenRemoved() { - asyncUpdateCryptoStatus(); + fun onCryptoModeChanged(cryptoMode: CryptoMode) { + currentCryptoMode = cryptoMode + asyncUpdateCryptoStatus() } - void onBccTokenChanged() { - asyncUpdateCryptoStatus(); + fun onCryptoPgpInlineChanged(enablePgpInline: Boolean) { + isForceTextMessageFormat = enablePgpInline + asyncUpdateCryptoStatus() } - public void onCryptoModeChanged(CryptoMode cryptoMode) { - currentCryptoMode = cryptoMode; - asyncUpdateCryptoStatus(); - } + private fun addRecipientsFromAddresses(recipientType: RecipientType, vararg addresses: Address) { + object : RecipientLoader(context, account.openPgpProvider, *addresses) { + override fun deliverResult(result: List?) { + val recipientArray = result!!.toTypedArray() + recipientMvpView.addRecipients(recipientType, *recipientArray) - public void onCryptoPgpInlineChanged(boolean enablePgpInline) { - cryptoEnablePgpInline = enablePgpInline; - asyncUpdateCryptoStatus(); - } - - private void addRecipientsFromAddresses(final RecipientType recipientType, final Address... addresses) { - new RecipientLoader(context, account.getOpenPgpProvider(), addresses) { - @Override - public void deliverResult(List result) { - Recipient[] recipientArray = result.toArray(new Recipient[result.size()]); - recipientMvpView.addRecipients(recipientType, recipientArray); - - stopLoading(); - abandon(); + stopLoading() + abandon() } - }.startLoading(); + }.startLoading() } - private void addRecipientFromContactUri(final RecipientType recipientType, final Uri uri) { - new RecipientLoader(context, account.getOpenPgpProvider(), uri, false) { - @Override - public void deliverResult(List result) { + private fun addRecipientFromContactUri(recipientType: RecipientType, uri: Uri?) { + object : RecipientLoader(context, account.openPgpProvider, uri, false) { + override fun deliverResult(result: List?) { // TODO handle multiple available mail addresses for a contact? - if (result.isEmpty()) { - recipientMvpView.showErrorContactNoAddress(); - return; + if (result!!.isEmpty()) { + recipientMvpView.showErrorContactNoAddress() + return } - Recipient recipient = result.get(0); - recipientMvpView.addRecipients(recipientType, recipient); + val recipient = result[0] + recipientMvpView.addRecipients(recipientType, recipient) - stopLoading(); - abandon(); + stopLoading() + abandon() } - }.startLoading(); + }.startLoading() } - void onToFocused() { - lastFocusedType = RecipientType.TO; + fun onToFocused() { + lastFocusedType = RecipientType.TO } - void onCcFocused() { - lastFocusedType = RecipientType.CC; + fun onCcFocused() { + lastFocusedType = RecipientType.CC } - void onBccFocused() { - lastFocusedType = RecipientType.BCC; + fun onBccFocused() { + lastFocusedType = RecipientType.BCC } - public void onMenuAddFromContacts() { - int requestCode = recipientTypeToRequestCode(lastFocusedType); - recipientMvpView.showContactPicker(requestCode); + fun onMenuAddFromContacts() { + val requestCode = lastFocusedType.toRequestCode() + recipientMvpView.showContactPicker(requestCode) } - public void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case CONTACT_PICKER_TO: - case CONTACT_PICKER_CC: - case CONTACT_PICKER_BCC: - if (resultCode != Activity.RESULT_OK || data == null) { - return; - } - RecipientType recipientType = recipientTypeFromRequestCode(requestCode); - addRecipientFromContactUri(recipientType, data.getData()); - break; - case OPENPGP_USER_INTERACTION: - openPgpApiManager.onUserInteractionResult(); - break; - case REQUEST_CODE_AUTOCRYPT: - asyncUpdateCryptoStatus(); - break; + fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + when (requestCode) { + CONTACT_PICKER_TO, CONTACT_PICKER_CC, CONTACT_PICKER_BCC -> { + if (resultCode != Activity.RESULT_OK || data == null) return + + val recipientType = requestCode.toRecipientType() + addRecipientFromContactUri(recipientType, data.data) + } + OPENPGP_USER_INTERACTION -> { + openPgpApiManager.onUserInteractionResult() + } + REQUEST_CODE_AUTOCRYPT -> { + asyncUpdateCryptoStatus() + } + } + } + + fun onNonRecipientFieldFocused() { + if (!account.isAlwaysShowCcBcc) { + hideEmptyExtendedRecipientFields() } } - private static int recipientTypeToRequestCode(RecipientType type) { - switch (type) { - case TO: { - return CONTACT_PICKER_TO; + fun onClickCryptoStatus() { + when (openPgpApiManager.openPgpProviderState) { + OpenPgpProviderState.UNCONFIGURED -> { + Timber.e("click on crypto status while unconfigured - this should not really happen?!") } - case CC: { - return CONTACT_PICKER_CC; + OpenPgpProviderState.OK -> { + toggleEncryptionState(false) } - case BCC: { - return CONTACT_PICKER_BCC; - } - } - - throw new AssertionError("Unhandled case: " + type); - } - - private static RecipientType recipientTypeFromRequestCode(int type) { - switch (type) { - case CONTACT_PICKER_TO: { - return RecipientType.TO; - } - case CONTACT_PICKER_CC: { - return RecipientType.CC; - } - case CONTACT_PICKER_BCC: { - return RecipientType.BCC; - } - } - - throw new AssertionError("Unhandled case: " + type); - } - - public void onNonRecipientFieldFocused() { - if (!account.isAlwaysShowCcBcc()) { - hideEmptyExtendedRecipientFields(); - } - } - - void onClickCryptoStatus() { - switch (openPgpApiManager.getOpenPgpProviderState()) { - case UNCONFIGURED: - Timber.e("click on crypto status while unconfigured - this should not really happen?!"); - return; - case OK: - toggleEncryptionState(false); - return; - case UI_REQUIRED: + OpenPgpProviderState.UI_REQUIRED -> { // TODO show openpgp settings - PendingIntent pendingIntent = openPgpApiManager.getUserInteractionPendingIntent(); - recipientMvpView.launchUserInteractionPendingIntent(pendingIntent, OPENPGP_USER_INTERACTION); - break; - case UNINITIALIZED: - case ERROR: - openPgpApiManager.refreshConnection(); + val pendingIntent = openPgpApiManager.userInteractionPendingIntent + recipientMvpView.launchUserInteractionPendingIntent(pendingIntent, OPENPGP_USER_INTERACTION) + } + OpenPgpProviderState.UNINITIALIZED, OpenPgpProviderState.ERROR -> { + openPgpApiManager.refreshConnection() + } } } - private void toggleEncryptionState(boolean showGotIt) { - ComposeCryptoStatus currentCryptoStatus = getCurrentCachedCryptoStatus(); + private fun toggleEncryptionState(showGotIt: Boolean) { + val currentCryptoStatus = currentCachedCryptoStatus if (currentCryptoStatus == null) { - Timber.e("click on crypto status while crypto status not available - should not really happen?!"); - return; + Timber.e("click on crypto status while crypto status not available - should not really happen?!") + return } - if (currentCryptoStatus.isEncryptionEnabled() && !currentCryptoStatus.allRecipientsCanEncrypt()) { - recipientMvpView.showOpenPgpEnabledErrorDialog(false); - return; + if (currentCryptoStatus.isEncryptionEnabled && !currentCryptoStatus.allRecipientsCanEncrypt()) { + recipientMvpView.showOpenPgpEnabledErrorDialog(false) + return } if (currentCryptoMode == CryptoMode.SIGN_ONLY) { - recipientMvpView.showErrorIsSignOnly(); - return; + recipientMvpView.showErrorIsSignOnly() + return } - boolean isEncryptOnNoChoice = currentCryptoStatus.canEncryptAndIsMutualDefault() || - currentCryptoStatus.isReplyToEncrypted(); + val isEncryptOnNoChoice = currentCryptoStatus.canEncryptAndIsMutualDefault() || + currentCryptoStatus.isReplyToEncrypted + if (currentCryptoMode == CryptoMode.NO_CHOICE) { if (currentCryptoStatus.hasAutocryptPendingIntent()) { recipientMvpView.launchUserInteractionPendingIntent( - currentCryptoStatus.getAutocryptPendingIntent(), REQUEST_CODE_AUTOCRYPT); + currentCryptoStatus.autocryptPendingIntent, REQUEST_CODE_AUTOCRYPT + ) } else if (isEncryptOnNoChoice) { // TODO warning dialog if we override, especially from reply! - onCryptoModeChanged(CryptoMode.CHOICE_DISABLED); + onCryptoModeChanged(CryptoMode.CHOICE_DISABLED) } else { - onCryptoModeChanged(CryptoMode.CHOICE_ENABLED); + onCryptoModeChanged(CryptoMode.CHOICE_ENABLED) if (showGotIt) { - recipientMvpView.showOpenPgpEncryptExplanationDialog(); + recipientMvpView.showOpenPgpEncryptExplanationDialog() } } } else if (currentCryptoMode == CryptoMode.CHOICE_DISABLED && !isEncryptOnNoChoice) { - onCryptoModeChanged(CryptoMode.CHOICE_ENABLED); + onCryptoModeChanged(CryptoMode.CHOICE_ENABLED) } else { - onCryptoModeChanged(CryptoMode.NO_CHOICE); + onCryptoModeChanged(CryptoMode.NO_CHOICE) } } /** - * Does the device actually have a Contacts application suitable for - * picking a contact. As hard as it is to believe, some vendors ship - * without it. - * - * @return True, if the device supports picking contacts. False, otherwise. + * Does the device actually have a Contacts application suitable for picking a contact. + * As hard as it is to believe, some vendors ship without it. */ - private boolean hasContactPicker() { - if (hasContactPicker == null) { - Contacts contacts = Contacts.getInstance(context); - - PackageManager packageManager = context.getPackageManager(); - List resolveInfoList = packageManager.queryIntentActivities(contacts.contactPickerIntent(), 0); - hasContactPicker = !resolveInfoList.isEmpty(); - } - - return hasContactPicker; + private fun hasContactPicker(): Boolean { + return hasContactPicker ?: isContactPickerAvailable().also { hasContactPicker = it } } - private boolean hasContactPermission() { - return ContextCompat.checkSelfPermission(context, - Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED; + private fun isContactPickerAvailable(): Boolean { + val contacts = Contacts.getInstance(context) + val resolveInfoList = context.packageManager.queryIntentActivities(contacts.contactPickerIntent(), 0) + return resolveInfoList.isNotEmpty() } - public void showPgpSendError(SendErrorState sendErrorState) { - switch (sendErrorState) { - case ENABLED_ERROR: - recipientMvpView.showOpenPgpEnabledErrorDialog(false); - break; - case PROVIDER_ERROR: - recipientMvpView.showErrorOpenPgpConnection(); - break; - case KEY_CONFIG_ERROR: - recipientMvpView.showErrorNoKeyConfigured(); - break; - default: - throw new AssertionError("not all error states handled, this is a bug!"); + private fun hasContactPermission(): Boolean { + val permissionState = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) + return permissionState == PackageManager.PERMISSION_GRANTED + } + + fun showPgpSendError(sendErrorState: SendErrorState) { + when (sendErrorState) { + SendErrorState.ENABLED_ERROR -> recipientMvpView.showOpenPgpEnabledErrorDialog(false) + SendErrorState.PROVIDER_ERROR -> recipientMvpView.showErrorOpenPgpConnection() + SendErrorState.KEY_CONFIG_ERROR -> recipientMvpView.showErrorNoKeyConfigured() + else -> throw AssertionError("not all error states handled, this is a bug!") } } - void showPgpAttachError(AttachErrorState attachErrorState) { - switch (attachErrorState) { - case IS_INLINE: - recipientMvpView.showErrorInlineAttach(); - break; - default: - throw new AssertionError("not all error states handled, this is a bug!"); + fun showPgpAttachError(attachErrorState: AttachErrorState) { + when (attachErrorState) { + AttachErrorState.IS_INLINE -> recipientMvpView.showErrorInlineAttach() + else -> throw AssertionError("not all error states handled, this is a bug!") } } - public void builderSetProperties(MessageBuilder messageBuilder) { - if (messageBuilder instanceof PgpMessageBuilder) { - throw new IllegalArgumentException("PpgMessageBuilder must be called with ComposeCryptoStatus argument!"); + fun builderSetProperties(messageBuilder: MessageBuilder) { + require(messageBuilder !is PgpMessageBuilder) { + "PpgMessageBuilder must be called with ComposeCryptoStatus argument!" } - messageBuilder.setTo(getToAddresses()); - messageBuilder.setCc(getCcAddresses()); - messageBuilder.setBcc(getBccAddresses()); + messageBuilder.setTo(toAddresses) + messageBuilder.setCc(ccAddresses) + messageBuilder.setBcc(bccAddresses) } - public void builderSetProperties(PgpMessageBuilder pgpMessageBuilder, ComposeCryptoStatus cryptoStatus) { - pgpMessageBuilder.setTo(getToAddresses()); - pgpMessageBuilder.setCc(getCcAddresses()); - pgpMessageBuilder.setBcc(getBccAddresses()); - - pgpMessageBuilder.setOpenPgpApi(openPgpApiManager.getOpenPgpApi()); - pgpMessageBuilder.setCryptoStatus(cryptoStatus); + fun builderSetProperties(pgpMessageBuilder: PgpMessageBuilder, cryptoStatus: ComposeCryptoStatus) { + pgpMessageBuilder.setTo(toAddresses) + pgpMessageBuilder.setCc(ccAddresses) + pgpMessageBuilder.setBcc(bccAddresses) + pgpMessageBuilder.setOpenPgpApi(openPgpApiManager.openPgpApi) + pgpMessageBuilder.setCryptoStatus(cryptoStatus) } - public void onMenuSetPgpInline(boolean enablePgpInline) { - onCryptoPgpInlineChanged(enablePgpInline); + fun onMenuSetPgpInline(enablePgpInline: Boolean) { + onCryptoPgpInlineChanged(enablePgpInline) + if (enablePgpInline) { - boolean shouldShowPgpInlineDialog = checkAndIncrementPgpInlineDialogCounter(); + val shouldShowPgpInlineDialog = checkAndIncrementPgpInlineDialogCounter() if (shouldShowPgpInlineDialog) { - recipientMvpView.showOpenPgpInlineDialog(true); + recipientMvpView.showOpenPgpInlineDialog(true) } } } - public void onMenuSetSignOnly(boolean enableSignOnly) { + fun onMenuSetSignOnly(enableSignOnly: Boolean) { if (enableSignOnly) { - onCryptoModeChanged(CryptoMode.SIGN_ONLY); - boolean shouldShowPgpSignOnlyDialog = checkAndIncrementPgpSignOnlyDialogCounter(); + onCryptoModeChanged(CryptoMode.SIGN_ONLY) + + val shouldShowPgpSignOnlyDialog = checkAndIncrementPgpSignOnlyDialogCounter() if (shouldShowPgpSignOnlyDialog) { - recipientMvpView.showOpenPgpSignOnlyDialog(true); + recipientMvpView.showOpenPgpSignOnlyDialog(true) } } else { - onCryptoModeChanged(CryptoMode.NO_CHOICE); + onCryptoModeChanged(CryptoMode.NO_CHOICE) } } - public void onMenuToggleEncryption() { - toggleEncryptionState(true); + fun onMenuToggleEncryption() { + toggleEncryptionState(true) } - public void onCryptoPgpClickDisable() { - onCryptoModeChanged(CryptoMode.CHOICE_DISABLED); + fun onCryptoPgpClickDisable() { + onCryptoModeChanged(CryptoMode.CHOICE_DISABLED) } - public void onCryptoPgpSignOnlyDisabled() { - onCryptoPgpInlineChanged(false); - onCryptoModeChanged(CryptoMode.NO_CHOICE); + fun onCryptoPgpSignOnlyDisabled() { + onCryptoPgpInlineChanged(false) + onCryptoModeChanged(CryptoMode.NO_CHOICE) } - private boolean checkAndIncrementPgpInlineDialogCounter() { - int pgpInlineDialogCounter = K9.getPgpInlineDialogCounter(); + private fun checkAndIncrementPgpInlineDialogCounter(): Boolean { + val pgpInlineDialogCounter = K9.pgpInlineDialogCounter if (pgpInlineDialogCounter < PGP_DIALOG_DISPLAY_THRESHOLD) { - K9.setPgpInlineDialogCounter(pgpInlineDialogCounter + 1); - K9.saveSettingsAsync(); - return true; + K9.pgpInlineDialogCounter = pgpInlineDialogCounter + 1 + K9.saveSettingsAsync() + return true } - return false; + + return false } - private boolean checkAndIncrementPgpSignOnlyDialogCounter() { - int pgpSignOnlyDialogCounter = K9.getPgpSignOnlyDialogCounter(); + private fun checkAndIncrementPgpSignOnlyDialogCounter(): Boolean { + val pgpSignOnlyDialogCounter = K9.pgpSignOnlyDialogCounter if (pgpSignOnlyDialogCounter < PGP_DIALOG_DISPLAY_THRESHOLD) { - K9.setPgpSignOnlyDialogCounter(pgpSignOnlyDialogCounter + 1); - K9.saveSettingsAsync(); - return true; + K9.pgpSignOnlyDialogCounter = pgpSignOnlyDialogCounter + 1 + K9.saveSettingsAsync() + return true } - return false; + + return false } - void onClickCryptoSpecialModeIndicator() { - if (currentCryptoMode == CryptoMode.SIGN_ONLY) { - recipientMvpView.showOpenPgpSignOnlyDialog(false); - } else if (cryptoEnablePgpInline) { - recipientMvpView.showOpenPgpInlineDialog(false); - } else { - throw new IllegalStateException("This icon should not be clickable while no special mode is active!"); - } - } - - public boolean shouldSaveRemotely() { - // TODO more appropriate logic? - return cachedCryptoStatus == null || !cachedCryptoStatus.isEncryptionEnabled(); - } - - private final OpenPgpApiManagerCallback openPgpCallback = new OpenPgpApiManagerCallback() { - @Override - public void onOpenPgpProviderStatusChanged() { - if (openPgpApiManager.getOpenPgpProviderState() == OpenPgpProviderState.UI_REQUIRED) { - recipientMvpView.showErrorOpenPgpUserInteractionRequired(); + fun onClickCryptoSpecialModeIndicator() { + when { + currentCryptoMode == CryptoMode.SIGN_ONLY -> { + recipientMvpView.showOpenPgpSignOnlyDialog(false) } - - asyncUpdateCryptoStatus(); - } - - @Override - public void onOpenPgpProviderError(OpenPgpProviderError error) { - switch (error) { - case ConnectionLost: - openPgpApiManager.refreshConnection(); - break; - case VersionIncompatible: - recipientMvpView.showErrorOpenPgpIncompatible(); - break; - case ConnectionFailed: - default: - recipientMvpView.showErrorOpenPgpConnection(); - break; + isForceTextMessageFormat -> { + recipientMvpView.showOpenPgpInlineDialog(false) + } + else -> { + error("This icon should not be clickable while no special mode is active!") } } - }; + } - public enum CryptoMode { - SIGN_ONLY, - NO_CHOICE, - CHOICE_DISABLED, - CHOICE_ENABLED, + private val openPgpCallback = object : OpenPgpApiManagerCallback { + override fun onOpenPgpProviderStatusChanged() { + if (openPgpApiManager.openPgpProviderState == OpenPgpProviderState.UI_REQUIRED) { + recipientMvpView.showErrorOpenPgpUserInteractionRequired() + } + + asyncUpdateCryptoStatus() + } + + override fun onOpenPgpProviderError(error: OpenPgpProviderError) { + when (error) { + OpenPgpProviderError.ConnectionLost -> openPgpApiManager.refreshConnection() + OpenPgpProviderError.VersionIncompatible -> recipientMvpView.showErrorOpenPgpIncompatible() + OpenPgpProviderError.ConnectionFailed -> recipientMvpView.showErrorOpenPgpConnection() + else -> recipientMvpView.showErrorOpenPgpConnection() + } + } + } + + private fun Array.toAddressArray(): Array
{ + return flatMap { addressString -> + Address.parseUnencoded(addressString).toList() + }.toTypedArray() + } + + private fun RecipientType.toRequestCode(): Int = when (this) { + RecipientType.TO -> CONTACT_PICKER_TO + RecipientType.CC -> CONTACT_PICKER_CC + RecipientType.BCC -> CONTACT_PICKER_BCC + else -> throw AssertionError("Unhandled case: $this") + } + + private fun Int.toRecipientType(): RecipientType = when (this) { + CONTACT_PICKER_TO -> RecipientType.TO + CONTACT_PICKER_CC -> RecipientType.CC + CONTACT_PICKER_BCC -> RecipientType.BCC + else -> throw AssertionError("Unhandled case: $this") + } + + enum class CryptoMode { + SIGN_ONLY, NO_CHOICE, CHOICE_DISABLED, CHOICE_ENABLED } } diff --git a/app/ui/legacy/src/test/java/com/fsck/k9/activity/compose/RecipientPresenterTest.kt b/app/ui/legacy/src/test/java/com/fsck/k9/activity/compose/RecipientPresenterTest.kt index d6b1c2156..853170cba 100644 --- a/app/ui/legacy/src/test/java/com/fsck/k9/activity/compose/RecipientPresenterTest.kt +++ b/app/ui/legacy/src/test/java/com/fsck/k9/activity/compose/RecipientPresenterTest.kt @@ -1,301 +1,307 @@ -package com.fsck.k9.activity.compose; +package com.fsck.k9.activity.compose +import androidx.test.core.app.ApplicationProvider +import com.fsck.k9.Account +import com.fsck.k9.K9RobolectricTest +import com.fsck.k9.activity.compose.RecipientMvpView.CryptoSpecialModeDisplayType +import com.fsck.k9.activity.compose.RecipientMvpView.CryptoStatusDisplayType +import com.fsck.k9.activity.compose.RecipientPresenter.CryptoMode +import com.fsck.k9.autocrypt.AutocryptDraftStateHeaderParser +import com.fsck.k9.helper.ReplyToParser +import com.fsck.k9.helper.ReplyToParser.ReplyToAddresses +import com.fsck.k9.mail.Address +import com.fsck.k9.mail.Message +import com.fsck.k9.mail.Message.RecipientType +import com.fsck.k9.message.AutocryptStatusInteractor +import com.fsck.k9.message.AutocryptStatusInteractor.RecipientAutocryptStatus +import com.fsck.k9.message.AutocryptStatusInteractor.RecipientAutocryptStatusType +import com.fsck.k9.message.ComposePgpEnableByDefaultDecider +import com.fsck.k9.message.ComposePgpInlineDecider +import com.fsck.k9.view.RecipientSelectView.Recipient +import com.google.common.truth.Truth.assertThat +import kotlin.test.assertNotNull +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +import org.koin.test.inject +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.stubbing +import org.openintents.openpgp.OpenPgpApiManager +import org.openintents.openpgp.OpenPgpApiManager.OpenPgpApiManagerCallback +import org.openintents.openpgp.OpenPgpApiManager.OpenPgpProviderState +import org.openintents.openpgp.util.OpenPgpApi +import org.robolectric.Robolectric +import org.robolectric.annotation.LooperMode -import java.util.Arrays; -import java.util.List; +private val TO_ADDRESS = Address("to@domain.example") +private val CC_ADDRESS = Address("cc@domain.example") +private const val CRYPTO_PROVIDER = "crypto_provider" +private const val CRYPTO_KEY_ID = 123L -import android.content.Context; -import androidx.loader.app.LoaderManager; - -import com.fsck.k9.Account; -import com.fsck.k9.DI; -import com.fsck.k9.K9RobolectricTest; -import com.fsck.k9.activity.compose.RecipientMvpView.CryptoSpecialModeDisplayType; -import com.fsck.k9.activity.compose.RecipientMvpView.CryptoStatusDisplayType; -import com.fsck.k9.activity.compose.RecipientPresenter.CryptoMode; -import com.fsck.k9.autocrypt.AutocryptDraftStateHeaderParser; -import com.fsck.k9.helper.ReplyToParser; -import com.fsck.k9.helper.ReplyToParser.ReplyToAddresses; -import com.fsck.k9.mail.Address; -import com.fsck.k9.mail.Message; -import com.fsck.k9.mail.Message.RecipientType; -import com.fsck.k9.message.AutocryptStatusInteractor; -import com.fsck.k9.message.AutocryptStatusInteractor.RecipientAutocryptStatus; -import com.fsck.k9.message.AutocryptStatusInteractor.RecipientAutocryptStatusType; -import com.fsck.k9.message.ComposePgpEnableByDefaultDecider; -import com.fsck.k9.message.ComposePgpInlineDecider; -import com.fsck.k9.view.RecipientSelectView.Recipient; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.openintents.openpgp.OpenPgpApiManager; -import org.openintents.openpgp.OpenPgpApiManager.OpenPgpApiManagerCallback; -import org.openintents.openpgp.OpenPgpApiManager.OpenPgpProviderState; -import org.openintents.openpgp.util.OpenPgpApi; -import org.robolectric.Robolectric; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.LooperMode; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - - -@SuppressWarnings("ConstantConditions") @LooperMode(LooperMode.Mode.LEGACY) -public class RecipientPresenterTest extends K9RobolectricTest { - private static final ReplyToAddresses TO_ADDRESSES = new ReplyToAddresses(Address.parse("to@example.org")); - private static final List
ALL_TO_ADDRESSES = Arrays.asList(Address.parse("allTo@example.org")); - private static final List
ALL_CC_ADDRESSES = Arrays.asList(Address.parse("allCc@example.org")); - private static final String CRYPTO_PROVIDER = "crypto_provider"; - private static final long CRYPTO_KEY_ID = 123L; - - - private RecipientPresenter recipientPresenter; - private ReplyToParser replyToParser; - private ComposePgpInlineDecider composePgpInlineDecider; - private ComposePgpEnableByDefaultDecider composePgpEnableByDefaultDecider; - private Account account; - private RecipientMvpView recipientMvpView; - private AutocryptStatusInteractor autocryptStatusInteractor; - private RecipientAutocryptStatus noRecipientsAutocryptResult; - private OpenPgpApiManager openPgpApiManager; - private OpenPgpApiManagerCallback openPgpApiManagerCallback; +class RecipientPresenterTest : K9RobolectricTest() { + private val openPgpApiManager = mock { + on { openPgpProviderState } doReturn OpenPgpProviderState.UNCONFIGURED + on { setOpenPgpProvider(any(), any()) } doAnswer { invocation -> + openPgpApiManagerCallback = invocation.getArgument(1) + } + } + private val recipientMvpView = mock() + private val account = mock() + private val composePgpInlineDecider = mock() + private val composePgpEnableByDefaultDecider = mock() + private val autocryptStatusInteractor = mock() + private val replyToParser = mock() + private val autocryptDraftStateHeaderParser: AutocryptDraftStateHeaderParser by inject() + private lateinit var recipientPresenter: RecipientPresenter + private val noRecipientsAutocryptResult = RecipientAutocryptStatus(RecipientAutocryptStatusType.NO_RECIPIENTS, null) + private var openPgpApiManagerCallback: OpenPgpApiManagerCallback? = null @Before - public void setUp() throws Exception { - Context context = RuntimeEnvironment.application; - Robolectric.getBackgroundThreadScheduler().pause(); + fun setUp() { + Robolectric.getBackgroundThreadScheduler().pause() - recipientMvpView = mock(RecipientMvpView.class); - openPgpApiManager = mock(OpenPgpApiManager.class); - account = mock(Account.class); - composePgpInlineDecider = mock(ComposePgpInlineDecider.class); - composePgpEnableByDefaultDecider = mock(ComposePgpEnableByDefaultDecider.class); - autocryptStatusInteractor = mock(AutocryptStatusInteractor.class); - replyToParser = mock(ReplyToParser.class); - LoaderManager loaderManager = mock(LoaderManager.class); - - when(openPgpApiManager.getOpenPgpProviderState()).thenReturn(OpenPgpProviderState.UNCONFIGURED); - - recipientPresenter = new RecipientPresenter( - context, loaderManager, openPgpApiManager, recipientMvpView, account, composePgpInlineDecider, - composePgpEnableByDefaultDecider, autocryptStatusInteractor, replyToParser, - DI.get(AutocryptDraftStateHeaderParser.class) - ); - - ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(OpenPgpApiManagerCallback.class); - verify(openPgpApiManager).setOpenPgpProvider(isNull(String.class), callbackCaptor.capture()); - openPgpApiManagerCallback = callbackCaptor.getValue(); - - noRecipientsAutocryptResult = new RecipientAutocryptStatus(RecipientAutocryptStatusType.NO_RECIPIENTS, null); + recipientPresenter = RecipientPresenter( + ApplicationProvider.getApplicationContext(), + mock(), + openPgpApiManager, + recipientMvpView, + account, + composePgpInlineDecider, + composePgpEnableByDefaultDecider, + autocryptStatusInteractor, + replyToParser, + autocryptDraftStateHeaderParser + ) } @Test @Ignore("It looks like the support version of AsyncTaskLoader handles background tasks differently") - public void testInitFromReplyToMessage() throws Exception { - Message message = mock(Message.class); - when(replyToParser.getRecipientsToReplyTo(message, account)).thenReturn(TO_ADDRESSES); + fun testInitFromReplyToMessage() { + val message = mock() + stubbing(replyToParser) { + on { getRecipientsToReplyTo(message, account) } doReturn ReplyToAddresses(arrayOf(TO_ADDRESS)) + } - recipientPresenter.initFromReplyToMessage(message, false); - runBackgroundTask(); + recipientPresenter.initFromReplyToMessage(message, false) + runBackgroundTask() - Recipient toRecipient = new Recipient(TO_ADDRESSES.to[0]); - verify(recipientMvpView).addRecipients(eq(RecipientType.TO), eq(toRecipient)); + verify(recipientMvpView).addRecipients(eq(RecipientType.TO), eq(Recipient(TO_ADDRESS))) } @Test @Ignore("It looks like the support version of AsyncTaskLoader handles background tasks differently") - public void testInitFromReplyToAllMessage() throws Exception { - Message message = mock(Message.class); - when(replyToParser.getRecipientsToReplyTo(message, account)).thenReturn(TO_ADDRESSES); - ReplyToAddresses replyToAddresses = new ReplyToAddresses(ALL_TO_ADDRESSES, ALL_CC_ADDRESSES); - when(replyToParser.getRecipientsToReplyAllTo(message, account)).thenReturn(replyToAddresses); + fun testInitFromReplyToAllMessage() { + val message = mock() + val replyToAddresses = ReplyToAddresses(listOf(TO_ADDRESS), listOf(CC_ADDRESS)) + stubbing(replyToParser) { + on { getRecipientsToReplyAllTo(message, account) } doReturn replyToAddresses + } - recipientPresenter.initFromReplyToMessage(message, true); - // one for To, one for Cc - runBackgroundTask(); - runBackgroundTask(); + recipientPresenter.initFromReplyToMessage(message, true) + runBackgroundTask() + runBackgroundTask() - verify(recipientMvpView).addRecipients(eq(RecipientType.TO), any(Recipient.class)); - verify(recipientMvpView).addRecipients(eq(RecipientType.CC), any(Recipient.class)); + verify(recipientMvpView).addRecipients(eq(RecipientType.TO), eq(Recipient(TO_ADDRESS))) + verify(recipientMvpView).addRecipients(eq(RecipientType.CC), eq(Recipient(CC_ADDRESS))) } @Test - public void initFromReplyToMessage_shouldCallComposePgpInlineDecider() throws Exception { - Message message = mock(Message.class); - when(replyToParser.getRecipientsToReplyTo(message, account)).thenReturn(TO_ADDRESSES); + fun initFromReplyToMessage_shouldCallComposePgpInlineDecider() { + val message = mock() + stubbing(replyToParser) { + on { getRecipientsToReplyTo(message, account) } doReturn ReplyToAddresses(arrayOf(TO_ADDRESS)) + } - recipientPresenter.initFromReplyToMessage(message, false); + recipientPresenter.initFromReplyToMessage(message, false) - verify(composePgpInlineDecider).shouldReplyInline(message); + verify(composePgpInlineDecider).shouldReplyInline(message) } @Test - public void getCurrentCryptoStatus_withoutCryptoProvider() throws Exception { - when(openPgpApiManager.getOpenPgpProviderState()).thenReturn(OpenPgpProviderState.UNCONFIGURED); - recipientPresenter.asyncUpdateCryptoStatus(); + fun getCurrentCryptoStatus_withoutCryptoProvider() { + stubbing(openPgpApiManager) { + on { openPgpProviderState } doReturn OpenPgpProviderState.UNCONFIGURED + } - ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus(); + recipientPresenter.asyncUpdateCryptoStatus() - assertEquals(CryptoStatusDisplayType.UNCONFIGURED, status.getDisplayType()); - assertEquals(CryptoSpecialModeDisplayType.NONE, status.getSpecialModeDisplayType()); - assertNull(status.getAttachErrorStateOrNull()); - assertFalse(status.isProviderStateOk()); - assertFalse(status.isOpenPgpConfigured()); + assertNotNull(recipientPresenter.currentCachedCryptoStatus) { status -> + assertThat(status.displayType).isEqualTo(CryptoStatusDisplayType.UNCONFIGURED) + assertThat(status.specialModeDisplayType).isEqualTo(CryptoSpecialModeDisplayType.NONE) + assertThat(status.attachErrorStateOrNull).isNull() + assertThat(status.isProviderStateOk()).isFalse() + assertThat(status.isOpenPgpConfigured).isFalse() + } } @Test - public void getCurrentCryptoStatus_withCryptoProvider() throws Exception { - setupCryptoProvider(noRecipientsAutocryptResult); + fun getCurrentCryptoStatus_withCryptoProvider() { + setupCryptoProvider(noRecipientsAutocryptResult) - ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus(); - - assertEquals(CryptoStatusDisplayType.UNAVAILABLE, status.getDisplayType()); - assertTrue(status.isProviderStateOk()); - assertTrue(status.isOpenPgpConfigured()); + assertNotNull(recipientPresenter.currentCachedCryptoStatus) { status -> + assertThat(status.displayType).isEqualTo(CryptoStatusDisplayType.UNAVAILABLE) + assertThat(status.isProviderStateOk()).isTrue() + assertThat(status.isOpenPgpConfigured).isTrue() + } } @Test - public void getCurrentCryptoStatus_withOpportunistic() throws Exception { - RecipientAutocryptStatus recipientAutocryptStatus = new RecipientAutocryptStatus( - RecipientAutocryptStatusType.AVAILABLE_UNCONFIRMED, null); - setupCryptoProvider(recipientAutocryptStatus); + fun getCurrentCryptoStatus_withOpportunistic() { + val recipientAutocryptStatus = RecipientAutocryptStatus( + RecipientAutocryptStatusType.AVAILABLE_UNCONFIRMED, null + ) - ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus(); + setupCryptoProvider(recipientAutocryptStatus) - assertEquals(CryptoStatusDisplayType.AVAILABLE, status.getDisplayType()); - assertTrue(status.isProviderStateOk()); - assertTrue(status.isOpenPgpConfigured()); + assertNotNull(recipientPresenter.currentCachedCryptoStatus) { status -> + assertThat(status.displayType).isEqualTo(CryptoStatusDisplayType.AVAILABLE) + assertThat(status.isProviderStateOk()).isTrue() + assertThat(status.isOpenPgpConfigured).isTrue() + } } @Test - public void getCurrentCryptoStatus_withOpportunistic__confirmed() throws Exception { - RecipientAutocryptStatus recipientAutocryptStatus = new RecipientAutocryptStatus( - RecipientAutocryptStatusType.AVAILABLE_CONFIRMED, null); - setupCryptoProvider(recipientAutocryptStatus); + fun getCurrentCryptoStatus_withOpportunistic__confirmed() { + val recipientAutocryptStatus = RecipientAutocryptStatus( + RecipientAutocryptStatusType.AVAILABLE_CONFIRMED, null + ) - ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus(); + setupCryptoProvider(recipientAutocryptStatus) - assertEquals(CryptoStatusDisplayType.AVAILABLE, status.getDisplayType()); - assertTrue(status.isProviderStateOk()); - assertTrue(status.isOpenPgpConfigured()); + assertNotNull(recipientPresenter.currentCachedCryptoStatus) { status -> + assertThat(status.displayType).isEqualTo(CryptoStatusDisplayType.AVAILABLE) + assertThat(status.isProviderStateOk()).isTrue() + assertThat(status.isOpenPgpConfigured).isTrue() + } } @Test - public void getCurrentCryptoStatus_withOpportunistic__missingKeys() throws Exception { - RecipientAutocryptStatus recipientAutocryptStatus = new RecipientAutocryptStatus( - RecipientAutocryptStatusType.UNAVAILABLE, null); - setupCryptoProvider(recipientAutocryptStatus); + fun getCurrentCryptoStatus_withOpportunistic__missingKeys() { + val recipientAutocryptStatus = RecipientAutocryptStatus( + RecipientAutocryptStatusType.UNAVAILABLE, null + ) - ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus(); + setupCryptoProvider(recipientAutocryptStatus) - assertEquals(CryptoStatusDisplayType.UNAVAILABLE, status.getDisplayType()); - assertTrue(status.isProviderStateOk()); - assertTrue(status.isOpenPgpConfigured()); + assertNotNull(recipientPresenter.currentCachedCryptoStatus) { status -> + assertThat(status.displayType).isEqualTo(CryptoStatusDisplayType.UNAVAILABLE) + assertThat(status.isProviderStateOk()).isTrue() + assertThat(status.isOpenPgpConfigured).isTrue() + } } @Test - public void getCurrentCryptoStatus_withOpportunistic__privateMissingKeys() throws Exception { - RecipientAutocryptStatus recipientAutocryptStatus = new RecipientAutocryptStatus( - RecipientAutocryptStatusType.UNAVAILABLE, null); - setupCryptoProvider(recipientAutocryptStatus); + fun getCurrentCryptoStatus_withOpportunistic__privateMissingKeys() { + val recipientAutocryptStatus = RecipientAutocryptStatus( + RecipientAutocryptStatusType.UNAVAILABLE, null + ) - recipientPresenter.onCryptoModeChanged(CryptoMode.CHOICE_ENABLED); - runBackgroundTask(); - ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus(); + setupCryptoProvider(recipientAutocryptStatus) + recipientPresenter.onCryptoModeChanged(CryptoMode.CHOICE_ENABLED) + runBackgroundTask() - assertEquals(CryptoStatusDisplayType.ENABLED_ERROR, status.getDisplayType()); - assertTrue(status.isProviderStateOk()); - assertTrue(status.isOpenPgpConfigured()); + assertNotNull(recipientPresenter.currentCachedCryptoStatus) { status -> + assertThat(status.displayType).isEqualTo(CryptoStatusDisplayType.ENABLED_ERROR) + assertThat(status.isProviderStateOk()).isTrue() + assertThat(status.isOpenPgpConfigured).isTrue() + } } @Test - public void getCurrentCryptoStatus_withModeDisabled() throws Exception { - RecipientAutocryptStatus recipientAutocryptStatus = new RecipientAutocryptStatus( - RecipientAutocryptStatusType.AVAILABLE_UNCONFIRMED, null); - setupCryptoProvider(recipientAutocryptStatus); + fun getCurrentCryptoStatus_withModeDisabled() { + val recipientAutocryptStatus = RecipientAutocryptStatus( + RecipientAutocryptStatusType.AVAILABLE_UNCONFIRMED, null + ) - recipientPresenter.onCryptoModeChanged(CryptoMode.CHOICE_DISABLED); - runBackgroundTask(); - ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus(); + setupCryptoProvider(recipientAutocryptStatus) + recipientPresenter.onCryptoModeChanged(CryptoMode.CHOICE_DISABLED) + runBackgroundTask() - assertEquals(CryptoStatusDisplayType.AVAILABLE, status.getDisplayType()); - assertTrue(status.isProviderStateOk()); - assertTrue(status.isOpenPgpConfigured()); + assertNotNull(recipientPresenter.currentCachedCryptoStatus) { status -> + assertThat(status.displayType).isEqualTo(CryptoStatusDisplayType.AVAILABLE) + assertThat(status.isProviderStateOk()).isTrue() + assertThat(status.isOpenPgpConfigured).isTrue() + } } @Test - public void getCurrentCryptoStatus_withModePrivate() throws Exception { - RecipientAutocryptStatus recipientAutocryptStatus = new RecipientAutocryptStatus( - RecipientAutocryptStatusType.AVAILABLE_UNCONFIRMED, null); - setupCryptoProvider(recipientAutocryptStatus); + fun getCurrentCryptoStatus_withModePrivate() { + val recipientAutocryptStatus = RecipientAutocryptStatus( + RecipientAutocryptStatusType.AVAILABLE_UNCONFIRMED, null + ) - recipientPresenter.onCryptoModeChanged(CryptoMode.CHOICE_ENABLED); - runBackgroundTask(); - ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus(); + setupCryptoProvider(recipientAutocryptStatus) + recipientPresenter.onCryptoModeChanged(CryptoMode.CHOICE_ENABLED) + runBackgroundTask() - assertEquals(CryptoStatusDisplayType.ENABLED, status.getDisplayType()); - assertTrue(status.isProviderStateOk()); - assertTrue(status.isOpenPgpConfigured()); + assertNotNull(recipientPresenter.currentCachedCryptoStatus) { status -> + assertThat(status.displayType).isEqualTo(CryptoStatusDisplayType.ENABLED) + assertThat(status.isProviderStateOk()).isTrue() + assertThat(status.isOpenPgpConfigured).isTrue() + } } @Test - public void getCurrentCryptoStatus_withModeSignOnly() throws Exception { - setupCryptoProvider(noRecipientsAutocryptResult); + fun getCurrentCryptoStatus_withModeSignOnly() { + setupCryptoProvider(noRecipientsAutocryptResult) - recipientPresenter.onMenuSetSignOnly(true); - runBackgroundTask(); - ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus(); + recipientPresenter.onMenuSetSignOnly(true) + runBackgroundTask() - assertEquals(CryptoStatusDisplayType.SIGN_ONLY, status.getDisplayType()); - assertTrue(status.isProviderStateOk()); - assertTrue(status.isSigningEnabled()); - assertTrue(status.isSignOnly()); + assertNotNull(recipientPresenter.currentCachedCryptoStatus) { status -> + assertThat(status.displayType).isEqualTo(CryptoStatusDisplayType.SIGN_ONLY) + assertThat(status.isProviderStateOk()).isTrue() + assertThat(status.isOpenPgpConfigured).isTrue() + assertThat(status.isSignOnly).isTrue() + } } @Test - public void getCurrentCryptoStatus_withModeInline() throws Exception { - setupCryptoProvider(noRecipientsAutocryptResult); + fun getCurrentCryptoStatus_withModeInline() { + setupCryptoProvider(noRecipientsAutocryptResult) - recipientPresenter.onMenuSetPgpInline(true); - runBackgroundTask(); - ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus(); + recipientPresenter.onMenuSetPgpInline(true) + runBackgroundTask() - assertEquals(CryptoStatusDisplayType.UNAVAILABLE, status.getDisplayType()); - assertTrue(status.isProviderStateOk()); - assertTrue(status.isPgpInlineModeEnabled()); + assertNotNull(recipientPresenter.currentCachedCryptoStatus) { status -> + assertThat(status.displayType).isEqualTo(CryptoStatusDisplayType.UNAVAILABLE) + assertThat(status.isProviderStateOk()).isTrue() + assertThat(status.isPgpInlineModeEnabled).isTrue() + } } - private void runBackgroundTask() { - boolean taskRun = Robolectric.getBackgroundThreadScheduler().runOneTask(); - assertTrue(taskRun); + private fun runBackgroundTask() { + assertThat(Robolectric.getBackgroundThreadScheduler().runOneTask()).isTrue() } - private void setupCryptoProvider(RecipientAutocryptStatus autocryptStatusResult) throws Exception { - Account account = mock(Account.class); - OpenPgpApi openPgpApi = mock(OpenPgpApi.class); + private fun setupCryptoProvider(autocryptStatusResult: RecipientAutocryptStatus) { + stubbing(account) { + on { openPgpProvider } doReturn CRYPTO_PROVIDER + on { isOpenPgpProviderConfigured } doReturn true + on { openPgpKey } doReturn CRYPTO_KEY_ID + } - when(account.getOpenPgpProvider()).thenReturn(CRYPTO_PROVIDER); - when(account.isOpenPgpProviderConfigured()).thenReturn(true); - when(account.getOpenPgpKey()).thenReturn(CRYPTO_KEY_ID); - recipientPresenter.onSwitchAccount(account); + recipientPresenter.onSwitchAccount(account) - when(openPgpApiManager.getOpenPgpProviderState()).thenReturn(OpenPgpProviderState.OK); - when(openPgpApiManager.getOpenPgpApi()).thenReturn(openPgpApi); - when(autocryptStatusInteractor.retrieveCryptoProviderRecipientStatus( - any(OpenPgpApi.class), any(String[].class))).thenReturn(autocryptStatusResult); + val openPgpApiMock = mock() - openPgpApiManagerCallback.onOpenPgpProviderStatusChanged(); - runBackgroundTask(); + stubbing(autocryptStatusInteractor) { + on { retrieveCryptoProviderRecipientStatus(eq(openPgpApiMock), any()) } doReturn autocryptStatusResult + } + + stubbing(openPgpApiManager) { + on { openPgpApi } doReturn openPgpApiMock + on { openPgpProviderState } doReturn OpenPgpProviderState.OK + } + + openPgpApiManagerCallback!!.onOpenPgpProviderStatusChanged() + runBackgroundTask() } }