Add support for background activity start mode

See https://developer.android.com/about/versions/14/behavior-changes-14#background-activity-restrictions
This commit is contained in:
cketti 2024-04-02 15:14:23 +02:00
parent 62fbfa0102
commit 1c4925b05d
10 changed files with 109 additions and 57 deletions

View file

@ -118,6 +118,7 @@ import com.fsck.k9.ui.helper.SizeFormatter;
import com.fsck.k9.ui.messagelist.DefaultFolderProvider; import com.fsck.k9.ui.messagelist.DefaultFolderProvider;
import org.openintents.openpgp.OpenPgpApiManager; import org.openintents.openpgp.OpenPgpApiManager;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpIntentStarter;
import timber.log.Timber; import timber.log.Timber;
@ -1622,7 +1623,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
public void onMessageBuildReturnPendingIntent(PendingIntent pendingIntent, int requestCode) { public void onMessageBuildReturnPendingIntent(PendingIntent pendingIntent, int requestCode) {
requestCode |= REQUEST_MASK_MESSAGE_BUILDER; requestCode |= REQUEST_MASK_MESSAGE_BUILDER;
try { try {
startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0); OpenPgpIntentStarter.startIntentSenderForResult(this, pendingIntent.getIntentSender(), requestCode);
} catch (SendIntentException e) { } catch (SendIntentException e) {
Timber.e(e, "Error starting pending intent from builder!"); Timber.e(e, "Error starting pending intent from builder!");
} }
@ -1631,9 +1632,9 @@ public class MessageCompose extends K9Activity implements OnClickListener,
public void launchUserInteractionPendingIntent(PendingIntent pendingIntent, int requestCode) { public void launchUserInteractionPendingIntent(PendingIntent pendingIntent, int requestCode) {
requestCode |= REQUEST_MASK_RECIPIENT_PRESENTER; requestCode |= REQUEST_MASK_RECIPIENT_PRESENTER;
try { try {
startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0); OpenPgpIntentStarter.startIntentSenderForResult(this, pendingIntent.getIntentSender(), requestCode);
} catch (SendIntentException e) { } catch (SendIntentException e) {
e.printStackTrace(); Timber.e(e, "Error starting pending intent from builder!");
} }
} }
@ -1689,11 +1690,10 @@ public class MessageCompose extends K9Activity implements OnClickListener,
} }
@Override @Override
public boolean startIntentSenderForMessageLoaderHelper(IntentSender si, int requestCode, Intent fillIntent, public boolean startIntentSenderForMessageLoaderHelper(IntentSender intentSender, int requestCode) {
int flagsMask, int flagValues, int extraFlags) {
try { try {
requestCode |= REQUEST_MASK_LOADER_HELPER; requestCode |= REQUEST_MASK_LOADER_HELPER;
startIntentSenderForResult(si, requestCode, fillIntent, flagsMask, flagValues, extraFlags); OpenPgpIntentStarter.startIntentSenderForResult(MessageCompose.this, intentSender, requestCode);
} catch (SendIntentException e) { } catch (SendIntentException e) {
Timber.e(e, "Irrecoverable error calling PendingIntent!"); Timber.e(e, "Irrecoverable error calling PendingIntent!");
} }

View file

@ -357,14 +357,12 @@ public class MessageLoaderHelper {
} }
@Override @Override
public boolean startPendingIntentForCryptoHelper(IntentSender si, int requestCode, Intent fillIntent, public boolean startPendingIntentForCryptoHelper(IntentSender intentSender, int requestCode) {
int flagsMask, int flagValues, int extraFlags) {
if (callback == null) { if (callback == null) {
throw new IllegalStateException("unexpected call when callback is already detached"); throw new IllegalStateException("unexpected call when callback is already detached");
} }
return callback.startIntentSenderForMessageLoaderHelper(si, requestCode, fillIntent, return callback.startIntentSenderForMessageLoaderHelper(intentSender, requestCode);
flagsMask, flagValues, extraFlags);
} }
}; };
@ -524,8 +522,7 @@ public class MessageLoaderHelper {
void setLoadingProgress(int current, int max); void setLoadingProgress(int current, int max);
boolean startIntentSenderForMessageLoaderHelper(IntentSender si, int requestCode, Intent fillIntent, boolean startIntentSenderForMessageLoaderHelper(IntentSender intentSender, int requestCode);
int flagsMask, int flagValues, int extraFlags);
void onDownloadErrorMessageNotFound(); void onDownloadErrorMessageNotFound();
void onDownloadErrorNetworkError(); void onDownloadErrorNetworkError();

View file

@ -1,7 +1,6 @@
package com.fsck.k9.ui.crypto; package com.fsck.k9.ui.crypto;
import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import com.fsck.k9.mailstore.MessageCryptoAnnotations; import com.fsck.k9.mailstore.MessageCryptoAnnotations;
@ -10,6 +9,5 @@ import com.fsck.k9.mailstore.MessageCryptoAnnotations;
public interface MessageCryptoCallback { public interface MessageCryptoCallback {
void onCryptoHelperProgress(int current, int max); void onCryptoHelperProgress(int current, int max);
void onCryptoOperationsFinished(MessageCryptoAnnotations annotations); void onCryptoOperationsFinished(MessageCryptoAnnotations annotations);
boolean startPendingIntentForCryptoHelper(IntentSender si, int requestCode, Intent fillIntent, boolean startPendingIntentForCryptoHelper(IntentSender intentSender, int requestCode);
int flagsMask, int flagValues, int extraFlags);
} }

View file

@ -795,7 +795,7 @@ public class MessageCryptoHelper {
callback.onCryptoOperationsFinished(queuedResult); callback.onCryptoOperationsFinished(queuedResult);
} else if (queuedPendingIntent != null) { } else if (queuedPendingIntent != null) {
boolean pendingIntentHandled = callback.startPendingIntentForCryptoHelper( boolean pendingIntentHandled = callback.startPendingIntentForCryptoHelper(
queuedPendingIntent.getIntentSender(), REQUEST_CODE_USER_INTERACTION, null, 0, 0, 0); queuedPendingIntent.getIntentSender(), REQUEST_CODE_USER_INTERACTION);
if (pendingIntentHandled) { if (pendingIntentHandled) {
queuedPendingIntent = null; queuedPendingIntent = null;
} }

View file

@ -19,6 +19,7 @@ import com.fsck.k9.view.StatusIndicator
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
import org.openintents.openpgp.util.OpenPgpIntentStarter
import timber.log.Timber import timber.log.Timber
class AutocryptKeyTransferActivity : K9Activity() { class AutocryptKeyTransferActivity : K9Activity() {
@ -161,9 +162,9 @@ class AutocryptKeyTransferActivity : K9Activity() {
fun launchUserInteractionPendingIntent(pendingIntent: PendingIntent) { fun launchUserInteractionPendingIntent(pendingIntent: PendingIntent) {
try { try {
startIntentSender(pendingIntent.intentSender, null, 0, 0, 0) OpenPgpIntentStarter.startIntentSender(this, pendingIntent.intentSender)
} catch (e: SendIntentException) { } catch (e: SendIntentException) {
Timber.e(e) Timber.e(e, "Error starting PendingIntent")
} }
} }

View file

@ -2,6 +2,7 @@ package com.fsck.k9.ui.messagedetails
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Intent import android.content.Intent
import android.content.IntentSender.SendIntentException
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -36,6 +37,8 @@ import com.mikepenz.fastadapter.listeners.ClickEventHook
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
import org.openintents.openpgp.util.OpenPgpIntentStarter
import timber.log.Timber
class MessageDetailsFragment : ToolbarBottomSheetDialogFragment() { class MessageDetailsFragment : ToolbarBottomSheetDialogFragment() {
private val viewModel: MessageDetailsViewModel by viewModel() private val viewModel: MessageDetailsViewModel by viewModel()
@ -333,7 +336,11 @@ class MessageDetailsFragment : ToolbarBottomSheetDialogFragment() {
} }
private fun showCryptoKeys(pendingIntent: PendingIntent) { private fun showCryptoKeys(pendingIntent: PendingIntent) {
requireActivity().startIntentSender(pendingIntent.intentSender, null, 0, 0, 0) try {
OpenPgpIntentStarter.startIntentSender(requireActivity(), pendingIntent.intentSender)
} catch (e: SendIntentException) {
Timber.e(e, "Error starting PendingIntent")
}
} }
private fun searchCryptoKeys() { private fun searchCryptoKeys() {

View file

@ -120,7 +120,7 @@ public class MessageCryptoPresenter {
PendingIntent pendingIntent = cryptoResultAnnotation.getOpenPgpSigningKeyIntentIfAny(); PendingIntent pendingIntent = cryptoResultAnnotation.getOpenPgpSigningKeyIntentIfAny();
if (pendingIntent != null) { if (pendingIntent != null) {
messageCryptoMvpView.startPendingIntentForCryptoPresenter( messageCryptoMvpView.startPendingIntentForCryptoPresenter(
pendingIntent.getIntentSender(), REQUEST_CODE_UNKNOWN_KEY, null, 0, 0, 0); pendingIntent.getIntentSender(), REQUEST_CODE_UNKNOWN_KEY);
} }
} catch (IntentSender.SendIntentException e) { } catch (IntentSender.SendIntentException e) {
Timber.e(e, "SendIntentException"); Timber.e(e, "SendIntentException");
@ -136,7 +136,7 @@ public class MessageCryptoPresenter {
PendingIntent pendingIntent = cryptoResultAnnotation.getOpenPgpInsecureWarningPendingIntent(); PendingIntent pendingIntent = cryptoResultAnnotation.getOpenPgpInsecureWarningPendingIntent();
if (pendingIntent != null) { if (pendingIntent != null) {
messageCryptoMvpView.startPendingIntentForCryptoPresenter( messageCryptoMvpView.startPendingIntentForCryptoPresenter(
pendingIntent.getIntentSender(), REQUEST_CODE_SECURITY_WARNING, null, 0, 0, 0); pendingIntent.getIntentSender(), REQUEST_CODE_SECURITY_WARNING);
} }
} catch (IntentSender.SendIntentException e) { } catch (IntentSender.SendIntentException e) {
Timber.e(e, "SendIntentException"); Timber.e(e, "SendIntentException");
@ -171,8 +171,8 @@ public class MessageCryptoPresenter {
void redisplayMessage(); void redisplayMessage();
void restartMessageCryptoProcessing(); void restartMessageCryptoProcessing();
void startPendingIntentForCryptoPresenter(IntentSender si, Integer requestCode, Intent fillIntent, void startPendingIntentForCryptoPresenter(IntentSender intentSender, Integer requestCode)
int flagsMask, int flagValues, int extraFlags) throws IntentSender.SendIntentException; throws IntentSender.SendIntentException;
void showCryptoConfigDialog(); void showCryptoConfigDialog();
} }

View file

@ -54,6 +54,7 @@ import com.fsck.k9.ui.share.ShareIntentBuilder
import com.fsck.k9.ui.withArguments import com.fsck.k9.ui.withArguments
import java.util.Locale import java.util.Locale
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.openintents.openpgp.util.OpenPgpIntentStarter
import timber.log.Timber import timber.log.Timber
class MessageViewFragment : class MessageViewFragment :
@ -828,24 +829,9 @@ class MessageViewFragment :
} }
@Throws(SendIntentException::class) @Throws(SendIntentException::class)
override fun startPendingIntentForCryptoPresenter( override fun startPendingIntentForCryptoPresenter(intentSender: IntentSender, requestCode: Int) {
intentSender: IntentSender,
requestCode: Int,
fillIntent: Intent?,
flagsMask: Int,
flagValues: Int,
extraFlags: Int,
) {
val maskedRequestCode = requestCode or REQUEST_MASK_CRYPTO_PRESENTER val maskedRequestCode = requestCode or REQUEST_MASK_CRYPTO_PRESENTER
startIntentSenderForResult( OpenPgpIntentStarter.startIntentSenderForResult(this@MessageViewFragment, intentSender, maskedRequestCode)
intentSender,
maskedRequestCode,
fillIntent,
flagsMask,
flagValues,
extraFlags,
null,
)
} }
override fun restartMessageCryptoProcessing() { override fun restartMessageCryptoProcessing() {
@ -921,27 +907,16 @@ class MessageViewFragment :
Toast.makeText(requireContext(), R.string.status_network_error, Toast.LENGTH_LONG).show() Toast.makeText(requireContext(), R.string.status_network_error, Toast.LENGTH_LONG).show()
} }
override fun startIntentSenderForMessageLoaderHelper( override fun startIntentSenderForMessageLoaderHelper(intentSender: IntentSender, requestCode: Int): Boolean {
intentSender: IntentSender,
requestCode: Int,
fillIntent: Intent?,
flagsMask: Int,
flagValues: Int,
extraFlags: Int,
): Boolean {
if (!isActive) return false if (!isActive) return false
showProgressThreshold = null showProgressThreshold = null
try { try {
val maskedRequestCode = requestCode or REQUEST_MASK_LOADER_HELPER val maskedRequestCode = requestCode or REQUEST_MASK_LOADER_HELPER
startIntentSenderForResult( OpenPgpIntentStarter.startIntentSenderForResult(
this@MessageViewFragment,
intentSender, intentSender,
maskedRequestCode, maskedRequestCode,
fillIntent,
flagsMask,
flagValues,
extraFlags,
null,
) )
} catch (e: SendIntentException) { } catch (e: SendIntentException) {
Timber.e(e, "Irrecoverable error calling PendingIntent!") Timber.e(e, "Irrecoverable error calling PendingIntent!")

View file

@ -0,0 +1,75 @@
package org.openintents.openpgp.util
import android.app.Activity
import android.app.ActivityOptions
import android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
import android.content.IntentSender
import android.os.Build
import android.os.Bundle
import androidx.annotation.RequiresApi
import androidx.fragment.app.Fragment
/**
* Start OpenPGP crypto provider intents.
*
* On Android 14+ we need to use [MODE_BACKGROUND_ACTIVITY_START_ALLOWED] in order to start those intents.
*/
object OpenPgpIntentStarter {
@Throws(IntentSender.SendIntentException::class)
fun startIntentSender(activity: Activity, intentSender: IntentSender) {
val options = buildOptionsBundle()
activity.startIntentSender(
intentSender,
/* fillInIntent = */ null,
/* flagsMask = */ 0,
/* flagsValues = */ 0,
/* extraFlags = */ 0,
options,
)
}
@JvmStatic
@Throws(IntentSender.SendIntentException::class)
fun startIntentSenderForResult(activity: Activity, intentSender: IntentSender, requestCode: Int) {
val options = buildOptionsBundle()
activity.startIntentSenderForResult(
intentSender,
requestCode,
/* fillInIntent = */ null,
/* flagsMask = */ 0,
/* flagsValues = */ 0,
/* extraFlags = */ 0,
options,
)
}
@JvmStatic
@Throws(IntentSender.SendIntentException::class)
fun startIntentSenderForResult(fragment: Fragment, intentSender: IntentSender, requestCode: Int) {
val options = buildOptionsBundle()
@Suppress("DEPRECATION")
fragment.startIntentSenderForResult(
intentSender,
requestCode,
/* fillInIntent = */ null,
/* flagsMask = */ 0,
/* flagsValues = */ 0,
/* extraFlags = */ 0,
options,
)
}
private fun buildOptionsBundle(): Bundle? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
ActivityOptions.makeBasic().apply {
setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
setShareIdentityEnabled(true)
}.toBundle()
} else {
null
}
}
}

View file

@ -196,9 +196,8 @@ public class OpenPgpKeyPreference extends Preference implements OpenPgpApiManage
} }
try { try {
intentSenderFragment OpenPgpIntentStarter.startIntentSenderForResult(intentSenderFragment,
.startIntentSenderForResult(pendingIntentSelectKey.getIntentSender(), REQUEST_CODE_KEY_PREFERENCE, pendingIntentSelectKey.getIntentSender(), REQUEST_CODE_KEY_PREFERENCE);
null, 0, 0, 0, null);
} catch (IntentSender.SendIntentException e) { } catch (IntentSender.SendIntentException e) {
Timber.e(e,"Error launching pending intent"); Timber.e(e,"Error launching pending intent");
} finally { } finally {