Merge pull request #7613 from thunderbird/fix-build-warnings-and-lint-reports

Fix build warnings and lint reports
This commit is contained in:
Wolf-Martell Montwé 2024-02-12 13:07:39 +00:00 committed by GitHub
commit 04565aa99f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 278 additions and 138 deletions

View file

@ -5,11 +5,11 @@ import java.io.InputStream;
import android.app.PendingIntent;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import androidx.core.content.IntentCompat;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi;
import timber.log.Timber;
@ -34,11 +34,19 @@ public class AutocryptStatusInteractor {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
RecipientAutocryptStatusType type = getRecipientAutocryptStatusFromIntent(result);
PendingIntent pendingIntent = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
PendingIntent pendingIntent = IntentCompat.getParcelableExtra(
result,
OpenPgpApi.RESULT_INTENT,
PendingIntent.class
);
return new RecipientAutocryptStatus(type, pendingIntent);
case OpenPgpApi.RESULT_CODE_ERROR:
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
OpenPgpError error = IntentCompat.getParcelableExtra(
result,
OpenPgpApi.RESULT_ERROR,
OpenPgpError.class
);
if (error != null) {
Timber.w("OpenPGP API Error #%s: %s", error.getErrorId(), error.getMessage());
} else {

View file

@ -9,10 +9,11 @@ import java.util.Arrays;
import android.app.PendingIntent;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.IntentCompat;
import com.fsck.k9.CoreResourceProvider;
import com.fsck.k9.DI;
import com.fsck.k9.K9;
@ -27,7 +28,6 @@ import com.fsck.k9.mail.Message.RecipientType;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
import com.fsck.k9.mail.internet.BinaryTempFileBody;
import com.fsck.k9.mail.internet.Headers;
import com.fsck.k9.mail.internet.MessageIdGenerator;
import com.fsck.k9.mail.internet.MimeBodyPart;
import com.fsck.k9.mail.internet.MimeHeader;
@ -310,14 +310,22 @@ public class PgpMessageBuilder extends MessageBuilder {
return null;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
PendingIntent returnedPendingIntent = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
PendingIntent returnedPendingIntent = IntentCompat.getParcelableExtra(
result,
OpenPgpApi.RESULT_INTENT,
PendingIntent.class
);
if (returnedPendingIntent == null) {
throw new MessagingException("openpgp api needs user interaction, but returned no pendingintent!");
}
return returnedPendingIntent;
case OpenPgpApi.RESULT_CODE_ERROR:
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
OpenPgpError error = IntentCompat.getParcelableExtra(
result,
OpenPgpApi.RESULT_ERROR,
OpenPgpError.class
);
if (error == null) {
throw new MessagingException("internal openpgp api error");
}

View file

@ -56,6 +56,8 @@ dependencies {
implementation(libs.glide)
annotationProcessor(libs.glide.compiler)
// This is necessary as RecipientPresenterTest fails to inject
testImplementation(projects.app.common)
testImplementation(projects.core.testing)
testImplementation(projects.mail.testing)
testImplementation(projects.app.storage)

View file

@ -6,6 +6,8 @@ import android.view.MenuItem
import android.view.View
import android.widget.CheckBox
import android.widget.TextView
import androidx.core.content.IntentCompat
import androidx.core.os.BundleCompat
import androidx.core.view.isVisible
import androidx.core.widget.doAfterTextChanged
import com.fsck.k9.Account
@ -44,8 +46,16 @@ class EditIdentity : K9Activity() {
account = Preferences.getPreferences().getAccount(accountUuid) ?: error("Couldn't find account")
identity = when {
savedInstanceState != null -> savedInstanceState.getParcelable(EXTRA_IDENTITY) ?: error("Missing state")
identityIndex != -1 -> intent.getParcelableExtra(EXTRA_IDENTITY) ?: error("Missing argument")
savedInstanceState != null -> {
BundleCompat.getParcelable(savedInstanceState, EXTRA_IDENTITY, Identity::class.java)
?: error("Missing state")
}
identityIndex != -1 -> {
IntentCompat.getParcelableExtra(intent, EXTRA_IDENTITY, Identity::class.java)
?: error("Missing argument")
}
else -> Identity()
}

View file

@ -25,9 +25,6 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.ActionBar;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.ContextThemeWrapper;
@ -45,6 +42,12 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.ActionBar;
import androidx.core.content.IntentCompat;
import androidx.core.os.BundleCompat;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.fsck.k9.Account;
@ -108,12 +111,11 @@ import com.fsck.k9.search.LocalSearch;
import com.fsck.k9.ui.R;
import com.fsck.k9.ui.base.K9Activity;
import com.fsck.k9.ui.base.ThemeManager;
import com.fsck.k9.ui.compose.WrapUriTextWatcher;
import com.fsck.k9.ui.compose.QuotedMessageMvpView;
import com.fsck.k9.ui.compose.QuotedMessagePresenter;
import com.fsck.k9.ui.compose.WrapUriTextWatcher;
import com.fsck.k9.ui.helper.SizeFormatter;
import com.fsck.k9.ui.messagelist.DefaultFolderProvider;
import org.openintents.openpgp.OpenPgpApiManager;
import org.openintents.openpgp.util.OpenPgpApi;
import timber.log.Timber;
@ -429,7 +431,11 @@ public class MessageCompose extends K9Activity implements OnClickListener,
if (action == Action.FORWARD_AS_ATTACHMENT) {
messageLoaderHelper.asyncStartOrResumeLoadingMessageMetadata(relatedMessageReference);
} else {
Parcelable cachedDecryptionResult = intent.getParcelableExtra(EXTRA_MESSAGE_DECRYPTION_RESULT);
Parcelable cachedDecryptionResult = IntentCompat.getParcelableExtra(
intent,
EXTRA_MESSAGE_DECRYPTION_RESULT,
Parcelable.class
);
messageLoaderHelper.asyncStartOrResumeLoadingMessage(
relatedMessageReference, cachedDecryptionResult);
}
@ -537,12 +543,16 @@ public class MessageCompose extends K9Activity implements OnClickListener,
String type = intent.getType();
if (Intent.ACTION_SEND.equals(action)) {
Uri stream = intent.getParcelableExtra(Intent.EXTRA_STREAM);
Uri stream = IntentCompat.getParcelableExtra(intent,Intent.EXTRA_STREAM, Uri.class);
if (stream != null) {
attachmentPresenter.addExternalAttachment(stream, type);
}
} else {
List<Parcelable> list = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
List<Parcelable> list = IntentCompat.getParcelableArrayListExtra(
intent,
Intent.EXTRA_STREAM,
Parcelable.class
);
if (list != null) {
for (Parcelable parcelable : list) {
Uri stream = (Uri) parcelable;
@ -634,7 +644,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
attachmentsView.removeAllViews();
@ -651,7 +661,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
} else {
draftMessageId = null;
}
identity = savedInstanceState.getParcelable(STATE_IDENTITY);
identity = BundleCompat.getParcelable(savedInstanceState, STATE_IDENTITY, Identity.class);
identityChanged = savedInstanceState.getBoolean(STATE_IDENTITY_CHANGED);
repliedToMessageId = savedInstanceState.getString(STATE_IN_REPLY_TO);
referencedMessageIds = savedInstanceState.getString(STATE_REFERENCES);
@ -1055,6 +1065,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
return true;
}
@SuppressLint("MissingSuperCall")
@Override
public void onBackPressed() {
prepareToFinish(false);

View file

@ -26,6 +26,7 @@ import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.fragment.app.commit
import androidx.fragment.app.commitNow
import app.k9mail.core.android.common.compat.BundleCompat
import app.k9mail.core.android.common.contact.CachingRepository
import app.k9mail.core.android.common.contact.ContactRepository
import app.k9mail.feature.launcher.FeatureLauncherActivity
@ -168,6 +169,7 @@ open class MessageList :
window.statusBarColor = Color.TRANSPARENT
val rootLayout = findViewById<View>(R.id.drawerLayout)
rootLayout.systemUiVisibility = rootLayout.systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@ -301,8 +303,13 @@ open class MessageList :
}
if (savedInstanceState != null) {
val savedDisplayMode = savedInstanceState.getSerializable(STATE_DISPLAY_MODE) as DisplayMode
if (savedDisplayMode != DisplayMode.SPLIT_VIEW) {
val savedDisplayMode = BundleCompat.getSerializable(
savedInstanceState,
STATE_DISPLAY_MODE,
DisplayMode::class.java,
)
if (savedDisplayMode != null && savedDisplayMode != DisplayMode.SPLIT_VIEW) {
displayMode = savedDisplayMode
return
}

View file

@ -8,16 +8,17 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.widget.TextView;
import androidx.core.content.IntentCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.ui.R;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.mailstore.LocalStore;
import com.fsck.k9.service.DatabaseUpgradeService;
import com.fsck.k9.ui.R;
import com.fsck.k9.ui.base.K9Activity;
@ -137,7 +138,7 @@ public class UpgradeDatabases extends K9Activity {
*/
private void decodeExtras() {
Intent intent = getIntent();
mStartIntent = intent.getParcelableExtra(EXTRA_START_INTENT);
mStartIntent = IntentCompat.getParcelableExtra(intent, EXTRA_START_INTENT, Intent.class);
}
/**

View file

@ -13,9 +13,10 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import androidx.core.os.BundleCompat;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import com.fsck.k9.activity.compose.ComposeCryptoStatus.AttachErrorState;
import com.fsck.k9.activity.loader.AttachmentContentLoader;
import com.fsck.k9.activity.loader.AttachmentInfoLoader;
@ -286,7 +287,7 @@ public class AttachmentPresenter {
new LoaderManager.LoaderCallbacks<Attachment>() {
@Override
public Loader<Attachment> onCreateLoader(int id, Bundle args) {
Uri uri = args.getParcelable(LOADER_ARG_ATTACHMENT);
Uri uri = BundleCompat.getParcelable(args, LOADER_ARG_ATTACHMENT, Uri.class);
return new AttachmentInfoLoader(context, attachments.get(uri));
}
@ -319,7 +320,7 @@ public class AttachmentPresenter {
new LoaderManager.LoaderCallbacks<Attachment>() {
@Override
public Loader<Attachment> onCreateLoader(int id, Bundle args) {
Uri uri = args.getParcelable(LOADER_ARG_ATTACHMENT);
Uri uri = BundleCompat.getParcelable(args, LOADER_ARG_ATTACHMENT, Uri.class);
return new AttachmentContentLoader(context, attachments.get(uri));
}
@ -353,7 +354,7 @@ public class AttachmentPresenter {
new LoaderManager.LoaderCallbacks<Attachment>() {
@Override
public Loader<Attachment> onCreateLoader(int id, Bundle args) {
Uri uri = args.getParcelable(LOADER_ARG_ATTACHMENT);
Uri uri = BundleCompat.getParcelable(args, LOADER_ARG_ATTACHMENT, Uri.class);
return new AttachmentContentLoader(context, inlineAttachments.get(uri).getAttachment());
}

View file

@ -4,7 +4,6 @@ import android.content.Context
import com.fsck.k9.ui.base.ThemeProvider
import com.fsck.k9.ui.helper.DisplayHtmlUiFactory
import com.fsck.k9.ui.helper.HtmlSettingsProvider
import com.fsck.k9.ui.helper.HtmlToSpanned
import com.fsck.k9.ui.helper.SizeFormatter
import com.fsck.k9.ui.messageview.LinkTextHandler
import com.fsck.k9.ui.share.ShareIntentBuilder
@ -12,7 +11,6 @@ import org.koin.core.qualifier.named
import org.koin.dsl.module
val uiModule = module {
single { HtmlToSpanned() }
single<ThemeProvider> { K9ThemeProvider() }
single { HtmlSettingsProvider(get()) }
single { DisplayHtmlUiFactory(get()) }

View file

@ -9,6 +9,7 @@ import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import app.k9mail.core.android.common.compat.BundleCompat
import com.fsck.k9.ui.R
import com.fsck.k9.ui.base.loader.observeLoading
import de.cketti.changelog.ReleaseItem
@ -20,7 +21,9 @@ import org.koin.core.parameter.parametersOf
*/
class ChangelogFragment : Fragment() {
private val viewModel: ChangelogViewModel by viewModel {
val mode = arguments?.getSerializable(ARG_MODE) as? ChangeLogMode ?: error("Missing argument '$ARG_MODE'")
val mode = arguments?.let {
BundleCompat.getSerializable(it, ARG_MODE, ChangeLogMode::class.java)
} ?: error("Missing argument '$ARG_MODE'")
parametersOf(mode)
}
@ -85,6 +88,7 @@ class ChangelogAdapter(releaseItems: List<ReleaseItem>) : RecyclerView.Adapter<V
viewHolder.versionName.text = context.getString(R.string.changelog_version_title, item.versionName)
viewHolder.versionDate.text = item.date
}
is String -> {
val viewHolder = holder as ChangeItemViewHolder
viewHolder.changeText.text = item

View file

@ -5,19 +5,14 @@ import java.util.Map;
import android.os.Bundle;
import com.fsck.k9.DI;
import timber.log.Timber;
import androidx.annotation.NonNull;
import app.k9mail.core.android.common.compat.BundleCompat;
import com.fsck.k9.Account;
import com.fsck.k9.Account.MessageFormat;
import com.fsck.k9.Account.QuoteStyle;
import com.fsck.k9.DI;
import com.fsck.k9.activity.MessageCompose;
import com.fsck.k9.activity.MessageCompose.Action;
import com.fsck.k9.message.extractors.BodyTextExtractor;
import com.fsck.k9.message.html.HtmlConverter;
import com.fsck.k9.message.quote.HtmlQuoteCreator;
import com.fsck.k9.message.quote.TextQuoteCreator;
import com.fsck.k9.message.signature.HtmlSignatureRemover;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.internet.MessageExtractor;
@ -25,11 +20,17 @@ import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mailstore.AttachmentResolver;
import com.fsck.k9.mailstore.MessageViewInfo;
import com.fsck.k9.message.IdentityField;
import com.fsck.k9.message.quote.InsertableHtmlContent;
import com.fsck.k9.message.MessageBuilder;
import com.fsck.k9.message.QuotedTextMode;
import com.fsck.k9.message.SimpleMessageFormat;
import com.fsck.k9.message.extractors.BodyTextExtractor;
import com.fsck.k9.message.html.HtmlConverter;
import com.fsck.k9.message.quote.HtmlQuoteCreator;
import com.fsck.k9.message.quote.InsertableHtmlContent;
import com.fsck.k9.message.quote.TextQuoteCreator;
import com.fsck.k9.message.signature.HtmlSignatureRemover;
import com.fsck.k9.message.signature.TextSignatureRemover;
import timber.log.Timber;
public class QuotedMessagePresenter {
@ -153,18 +154,32 @@ public class QuotedMessagePresenter {
outState.putBoolean(STATE_KEY_FORCE_PLAIN_TEXT, forcePlainText);
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
quotedHtmlContent = (InsertableHtmlContent) savedInstanceState.getSerializable(STATE_KEY_HTML_QUOTE);
public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
quotedHtmlContent = BundleCompat.INSTANCE.getSerializable(
savedInstanceState,
STATE_KEY_HTML_QUOTE,
InsertableHtmlContent.class
);
quotedTextFormat = BundleCompat.getSerializable(
savedInstanceState,
STATE_KEY_QUOTED_TEXT_FORMAT,
SimpleMessageFormat.class
);
forcePlainText = savedInstanceState.getBoolean(STATE_KEY_FORCE_PLAIN_TEXT);
showOrHideQuotedText(
BundleCompat.getSerializable(
savedInstanceState,
STATE_KEY_QUOTED_TEXT_MODE,
QuotedTextMode.class
)
);
if (quotedHtmlContent != null && quotedHtmlContent.getQuotedContent() != null) {
// we don't have the part here, but inline-displayed images are cached by the webview
view.setQuotedHtml(quotedHtmlContent.getQuotedContent(), null);
}
quotedTextFormat = (SimpleMessageFormat) savedInstanceState.getSerializable(
STATE_KEY_QUOTED_TEXT_FORMAT);
forcePlainText = savedInstanceState.getBoolean(STATE_KEY_FORCE_PLAIN_TEXT);
showOrHideQuotedText(
(QuotedTextMode) savedInstanceState.getSerializable(STATE_KEY_QUOTED_TEXT_MODE));
}
public void processMessageToForward(MessageViewInfo messageViewInfo) throws MessagingException {

View file

@ -13,10 +13,11 @@ import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.core.content.IntentCompat;
import com.fsck.k9.autocrypt.AutocryptOperations;
import com.fsck.k9.crypto.MessageCryptoStructureDetector;
import com.fsck.k9.mail.Address;
@ -537,7 +538,11 @@ public class MessageCryptoHelper {
}
private void handleUserInteractionRequest() {
PendingIntent pendingIntent = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
PendingIntent pendingIntent = IntentCompat.getParcelableExtra(
currentCryptoResult,
OpenPgpApi.RESULT_INTENT,
PendingIntent.class
);
if (pendingIntent == null) {
throw new AssertionError("Expecting PendingIntent on USER_INTERACTION_REQUIRED!");
}
@ -546,22 +551,40 @@ public class MessageCryptoHelper {
}
private void handleCryptoOperationError() {
OpenPgpError error = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
OpenPgpError error = IntentCompat.getParcelableExtra(
currentCryptoResult,
OpenPgpApi.RESULT_ERROR,
OpenPgpError.class
);
Timber.w("OpenPGP API error: %s", error.getMessage());
onCryptoOperationFailed(error);
}
private void handleCryptoOperationSuccess(MimeBodyPart outputPart) {
OpenPgpDecryptionResult decryptionResult =
currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_DECRYPTION);
OpenPgpSignatureResult signatureResult =
currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
OpenPgpDecryptionResult decryptionResult = IntentCompat.getParcelableExtra(
currentCryptoResult,
OpenPgpApi.RESULT_DECRYPTION,
OpenPgpDecryptionResult.class
);
OpenPgpSignatureResult signatureResult = IntentCompat.getParcelableExtra(
currentCryptoResult,
OpenPgpApi.RESULT_SIGNATURE,
OpenPgpSignatureResult.class
);
if (decryptionResult.getResult() == OpenPgpDecryptionResult.RESULT_ENCRYPTED) {
parseAutocryptGossipHeadersFromDecryptedPart(outputPart);
}
PendingIntent pendingIntent = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
PendingIntent insecureWarningPendingIntent = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_INSECURE_DETAIL_INTENT);
PendingIntent pendingIntent = IntentCompat.getParcelableExtra(
currentCryptoResult,
OpenPgpApi.RESULT_INTENT,
PendingIntent.class
);
PendingIntent insecureWarningPendingIntent = IntentCompat.getParcelableExtra(
currentCryptoResult,
OpenPgpApi.RESULT_INSECURE_DETAIL_INTENT,
PendingIntent.class
);
boolean overrideCryptoWarning = currentCryptoResult.getBooleanExtra(
OpenPgpApi.RESULT_OVERRIDE_CRYPTO_WARNING, false);

View file

@ -2,6 +2,7 @@ package com.fsck.k9.ui.endtoend
import android.app.PendingIntent
import android.content.Intent
import androidx.core.content.IntentCompat
import com.fsck.k9.Account
import com.fsck.k9.autocrypt.AutocryptTransferMessageCreator
import com.fsck.k9.helper.SingleLiveEvent
@ -36,7 +37,11 @@ class AutocryptSetupMessageLiveEvent(
val result = openPgpApi.executeApi(intent, null as InputStream?, baos)
val keyData = baos.toByteArray()
val pi: PendingIntent = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT) ?: error("Missing result intent")
val pi: PendingIntent = IntentCompat.getParcelableExtra(
result,
OpenPgpApi.RESULT_INTENT,
PendingIntent::class.java,
) ?: error("Missing result intent")
val setupMessage = messageCreator.createAutocryptTransferMessage(keyData, address)

View file

@ -1,45 +0,0 @@
package com.fsck.k9.ui.helper
import android.text.Editable
import android.text.Html
import android.text.Html.TagHandler
import android.text.Spanned
import org.xml.sax.XMLReader
/**
* Convert HTML to a [Spanned] that can be used in a [android.widget.TextView].
*/
class HtmlToSpanned {
fun convert(html: String): Spanned {
return Html.fromHtml(html, null, ListTagHandler())
}
}
/**
* [TagHandler] that supports unordered lists.
*/
private class ListTagHandler : TagHandler {
override fun handleTag(opening: Boolean, tag: String, output: Editable, xmlReader: XMLReader) {
if (tag == "ul") {
if (opening) {
var lastChar: Char = 0.toChar()
if (output.isNotEmpty()) {
lastChar = output[output.length - 1]
}
if (lastChar != '\n') {
output.append("\r\n")
}
} else {
output.append("\r\n")
}
}
if (tag == "li") {
if (opening) {
output.append("\t")
} else {
output.append("\r\n")
}
}
}
}

View file

@ -15,6 +15,7 @@ import android.widget.TextView
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.appcompat.view.ActionMode
import androidx.core.os.BundleCompat
import androidx.core.os.bundleOf
import androidx.core.view.isGone
import androidx.core.view.isVisible
@ -207,7 +208,7 @@ class MessageListFragment :
val arguments = requireArguments()
showingThreadedList = arguments.getBoolean(ARG_THREADED_LIST, false)
isThreadDisplay = arguments.getBoolean(ARG_IS_THREAD_DISPLAY, false)
localSearch = arguments.getParcelable(ARG_SEARCH)!!
localSearch = BundleCompat.getParcelable(arguments, ARG_SEARCH, LocalSearch::class.java)!!
allAccounts = localSearch.searchAllAccounts()
val searchAccounts = localSearch.getAccounts(accountManager)

View file

@ -3,6 +3,7 @@ package com.fsck.k9.ui.settings.export
import android.content.Context
import android.net.Uri
import android.os.Bundle
import androidx.core.os.BundleCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@ -101,7 +102,7 @@ class SettingsExportViewModel(
statusText = StatusText.valueOf(savedInstanceState.getString(STATE_STATUS_TEXT, StatusText.HIDDEN.name))
}
contentUri = savedInstanceState.getParcelable(STATE_CONTENT_URI)
contentUri = BundleCompat.getParcelable(savedInstanceState, STATE_CONTENT_URI, Uri::class.java)
}
fun onExportButtonClicked() {

View file

@ -3,7 +3,6 @@ package com.fsck.k9
import android.app.Application
import com.fsck.k9.preferences.InMemoryStoragePersister
import com.fsck.k9.preferences.StoragePersister
import com.fsck.k9.storage.storageModule
import org.koin.dsl.module
class TestApp : Application() {
@ -13,7 +12,7 @@ class TestApp : Application() {
super.onCreate()
DI.start(
application = this,
modules = coreModules + storageModule + testModule,
modules = coreModules + commonAppModules + uiModules + testModule,
allowOverride = true,
)

View file

@ -44,15 +44,15 @@ internal class MessageListLoader(
val mapper = MessageListItemMapper(messageHelper, account)
return if (config.showingThreadedList) {
val (selection, selectionArgs) = buildSelection(account, config)
val (selection, selectionArgs) = buildSelection(config)
messageListRepository.getThreadedMessages(accountUuid, selection, selectionArgs, sortOrder, mapper)
} else {
val (selection, selectionArgs) = buildSelection(account, config)
val (selection, selectionArgs) = buildSelection(config)
messageListRepository.getMessages(accountUuid, selection, selectionArgs, sortOrder, mapper)
}
}
private fun buildSelection(account: Account, config: MessageListConfig): Pair<String, Array<String>> {
private fun buildSelection(config: MessageListConfig): Pair<String, Array<String>> {
val query = StringBuilder()
val queryArgs = mutableListOf<String>()
@ -95,25 +95,31 @@ internal class MessageListLoader(
SortType.SORT_DATE -> {
compareBy(config.sortAscending) { it.sortMessageDate }
}
SortType.SORT_ARRIVAL -> {
compareBy(config.sortAscending) { it.sortInternalDate }
}
SortType.SORT_SUBJECT -> {
compareStringBy<MessageListItem>(config.sortAscending) { it.sortSubject.orEmpty() }
.thenByDate(config)
}
SortType.SORT_SENDER -> {
compareStringBy<MessageListItem>(config.sortAscending) { it.displayName }
.thenByDate(config)
}
SortType.SORT_UNREAD -> {
compareBy<MessageListItem>(config.sortAscending) { it.isRead }
.thenByDate(config)
}
SortType.SORT_FLAGGED -> {
compareBy<MessageListItem>(!config.sortAscending) { it.sortIsStarred }
.thenByDate(config)
}
SortType.SORT_ATTACHMENT -> {
compareBy<MessageListItem>(!config.sortAscending) { it.hasAttachments }
.thenByDate(config)

View file

@ -0,0 +1,22 @@
package app.k9mail.core.android.common.compat
import android.os.Build
import android.os.Bundle
import java.io.Serializable
// This class resolves a deprecation warning and issue with the Bundle.getSerializable method
// Fixes https://issuetracker.google.com/issues/314250395
// Could be removed once releases in androidx.core.os.BundleCompat
object BundleCompat {
@JvmStatic
fun <T : Serializable> getSerializable(bundle: Bundle, key: String?, clazz: Class<T>): T? = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> bundle.getSerializable(key, clazz)
else -> {
@Suppress("DEPRECATION")
val serializable = bundle.getSerializable(key)
@Suppress("UNCHECKED_CAST")
if (clazz.isInstance(serializable)) serializable as T else null
}
}
}

View file

@ -0,0 +1,55 @@
package app.k9mail.core.android.common.compat
import android.os.Bundle
import assertk.assertThat
import assertk.assertions.isEqualTo
import java.io.Serializable
import kotlin.test.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class BundleCompatTest {
@Test
fun `getSerializable returns Serializable`() {
val bundle = Bundle()
val key = "keySerializable"
val serializable = TestSerializable("value")
val clazz = TestSerializable::class.java
bundle.putSerializable(key, serializable)
val result = BundleCompat.getSerializable(bundle, key, clazz)
assertThat(result).isEqualTo(serializable)
}
@Test
fun `getSerializable returns null when class mismatch`() {
val bundle = Bundle()
val key = "keySerializable"
val serializable = TestSerializable("value")
val clazz = OtherTestSerializable::class.java
bundle.putSerializable(key, serializable)
val result = BundleCompat.getSerializable(bundle, key, clazz)
assertThat(result).isEqualTo(null)
}
internal class TestSerializable(
val value: String,
) : Serializable {
companion object {
private const val serialVersionUID = 1L
}
}
internal class OtherTestSerializable(
val value: String,
) : Serializable {
companion object {
private const val serialVersionUID = 2L
}
}
}

View file

@ -46,8 +46,8 @@ class CommonTextFieldTest(
fun `should be enabled by default`() = runComposeTest {
setContent {
testSubject(
modifier = Modifier.testTag(testSubjectName),
textFieldConfig = TextFieldConfig(
Modifier.testTag(testSubjectName),
TextFieldConfig(
label = null,
isEnabled = null,
isReadOnly = false,
@ -63,8 +63,8 @@ class CommonTextFieldTest(
fun `should be disabled when enabled is false`() = runComposeTest {
setContent {
testSubject(
modifier = Modifier.testTag(testSubjectName),
textFieldConfig = TextFieldConfig(
Modifier.testTag(testSubjectName),
TextFieldConfig(
label = null,
isEnabled = false,
isReadOnly = false,
@ -80,8 +80,8 @@ class CommonTextFieldTest(
fun `should show label when label is not null`() = runComposeTest {
setContent {
testSubject(
modifier = Modifier.testTag(testSubjectName),
textFieldConfig = TextFieldConfig(
Modifier.testTag(testSubjectName),
TextFieldConfig(
label = LABEL,
isEnabled = null,
isReadOnly = false,
@ -97,8 +97,8 @@ class CommonTextFieldTest(
fun `should show asterisk when isRequired is true`() = runComposeTest {
setContent {
testSubject(
modifier = Modifier.testTag(testSubjectName),
textFieldConfig = TextFieldConfig(
Modifier.testTag(testSubjectName),
TextFieldConfig(
label = LABEL,
isEnabled = null,
isReadOnly = false,
@ -114,8 +114,8 @@ class CommonTextFieldTest(
fun `should not show asterisk when isRequired is false`() = runComposeTest {
setContent {
testSubject(
modifier = Modifier.testTag(testSubjectName),
textFieldConfig = TextFieldConfig(
Modifier.testTag(testSubjectName),
TextFieldConfig(
label = LABEL,
isEnabled = null,
isReadOnly = false,
@ -131,8 +131,8 @@ class CommonTextFieldTest(
fun `should not allow editing when isReadOnly is true`() = runComposeTest {
setContent {
testSubject(
modifier = Modifier.testTag(testSubjectName),
textFieldConfig = TextFieldConfig(
Modifier.testTag(testSubjectName),
TextFieldConfig(
label = LABEL,
isEnabled = null,
isReadOnly = true,

View file

@ -37,9 +37,9 @@ class TextInputTextFieldTest(
var value = testInput
setContent {
testSubject(
value = value,
onValueChange = { value = it },
modifier = Modifier.testTag(testSubjectName),
value,
{ value = it },
Modifier.testTag(testSubjectName),
)
}

View file

@ -1,17 +1,15 @@
package app.k9mail.core.ui.compose.theme
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Cancel
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.Error
import androidx.compose.material.icons.filled.MoveToInbox
import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.Outbox
import androidx.compose.material.icons.filled.Security
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material.icons.outlined.ArrowDropDown
import androidx.compose.material.icons.outlined.ArrowDropUp
import androidx.compose.material.icons.outlined.Celebration
@ -34,9 +32,6 @@ object Icons {
val inbox: ImageVector
get() = MaterialIcons.Filled.MoveToInbox
val notification: ImageVector
get() = MaterialIcons.Filled.Notifications
val outbox: ImageVector
get() = MaterialIcons.Filled.Outbox
@ -61,7 +56,7 @@ object Icons {
object Outlined {
val arrowBack: ImageVector
get() = MaterialIcons.Outlined.ArrowBack
get() = MaterialIcons.AutoMirrored.Outlined.ArrowBack
val arrowDropDown: ImageVector
get() = MaterialIcons.Outlined.ArrowDropDown

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
import androidx.core.os.BundleCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@ -70,7 +71,7 @@ internal class SettingsImportViewModel(
}
fun initializeFromSavedState(savedInstanceState: Bundle) {
contentUri = savedInstanceState.getParcelable(STATE_CONTENT_URI)
contentUri = BundleCompat.getParcelable(savedInstanceState, STATE_CONTENT_URI, Uri::class.java)
currentlyAuthorizingAccountUuid = savedInstanceState.getString(STATE_CURRENTLY_AUTHORIZING_ACCOUNT_UUID)
updateUiModel {
@ -204,12 +205,15 @@ internal class SettingsImportViewModel(
ImportStatus.NOT_AVAILABLE -> updateUiModel {
toggleSettingsListItemSelection(position)
}
ImportStatus.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED -> {
startAuthorization(settingsListItem as SettingsListItem.Account)
}
ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED -> {
showPasswordPromptDialog(settingsListItem as SettingsListItem.Account)
}
else -> Unit
}
}

View file

@ -24,11 +24,12 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.res.TypedArray;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import androidx.core.content.IntentCompat;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import org.openintents.openpgp.OpenPgpApiManager;
import org.openintents.openpgp.OpenPgpApiManager.OpenPgpApiManagerCallback;
import org.openintents.openpgp.OpenPgpApiManager.OpenPgpProviderError;
@ -146,7 +147,11 @@ public class OpenPgpKeyPreference extends Preference implements OpenPgpApiManage
switch (resultCode) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
PendingIntent pendingIntentSelectKey = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
PendingIntent pendingIntentSelectKey = IntentCompat.getParcelableExtra(
result,
OpenPgpApi.RESULT_INTENT,
PendingIntent.class
);
if (result.hasExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
long keyId = result.getLongExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, NO_KEY);
@ -161,7 +166,11 @@ public class OpenPgpKeyPreference extends Preference implements OpenPgpApiManage
break;
}
case OpenPgpApi.RESULT_CODE_ERROR: {
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
OpenPgpError error = IntentCompat.getParcelableExtra(
result,
OpenPgpApi.RESULT_ERROR,
OpenPgpError.class
);
Timber.e("RESULT_CODE_ERROR: %s", error.getMessage());
break;