Skip to content

Commit f1cd8c0

Browse files
committed
Use intent launcher instead of 'in-foreground'
1 parent 41009d4 commit f1cd8c0

File tree

3 files changed

+18
-13
lines changed

3 files changed

+18
-13
lines changed

lib/src/main/java/at/bitfire/cert4android/CustomCertManager.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ package at.bitfire.cert4android
66

77
import android.annotation.SuppressLint
88
import android.content.Context
9+
import android.content.Intent
10+
import androidx.activity.result.ActivityResultLauncher
911
import kotlinx.coroutines.flow.StateFlow
1012
import java.security.cert.CertificateException
1113
import java.security.cert.X509Certificate
@@ -27,7 +29,7 @@ import javax.net.ssl.X509TrustManager
2729
class CustomCertManager @JvmOverloads constructor(
2830
context: Context,
2931
val trustSystemCerts: Boolean = true,
30-
var appInForeground: StateFlow<Boolean>?
32+
var launcher: ActivityResultLauncher<Intent>?
3133
): X509TrustManager {
3234

3335
private val logger
@@ -51,7 +53,7 @@ class CustomCertManager @JvmOverloads constructor(
5153
*/
5254
@Throws(CertificateException::class)
5355
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
54-
if (!certStore.isTrusted(chain, authType, trustSystemCerts, appInForeground))
56+
if (!certStore.isTrusted(chain, authType, trustSystemCerts, launcher))
5557
throw CertificateException("Certificate chain not trusted")
5658
}
5759

@@ -75,7 +77,7 @@ class CustomCertManager @JvmOverloads constructor(
7577
// Allow users to explicitly accept certificates that have a bad hostname here
7678
(session.peerCertificates.firstOrNull() as? X509Certificate)?.let { cert ->
7779
// Check without trusting system certificates so that the user will be asked even for system-trusted certificates
78-
if (certStore.isTrusted(arrayOf(cert), "RSA", false, appInForeground))
80+
if (certStore.isTrusted(arrayOf(cert), "RSA", false, launcher))
7981
return true
8082
}
8183

lib/src/main/java/at/bitfire/cert4android/CustomCertStore.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ package at.bitfire.cert4android
66

77
import android.annotation.SuppressLint
88
import android.content.Context
9+
import android.content.Intent
10+
import androidx.activity.result.ActivityResultLauncher
911
import androidx.annotation.VisibleForTesting
1012
import kotlinx.coroutines.TimeoutCancellationException
1113
import kotlinx.coroutines.flow.StateFlow
@@ -81,7 +83,7 @@ class CustomCertStore internal constructor(
8183
/**
8284
* Determines whether a certificate chain is trusted.
8385
*/
84-
fun isTrusted(chain: Array<X509Certificate>, authType: String, trustSystemCerts: Boolean, appInForeground: StateFlow<Boolean>?): Boolean {
86+
fun isTrusted(chain: Array<X509Certificate>, authType: String, trustSystemCerts: Boolean, launcher: ActivityResultLauncher<Intent>?): Boolean {
8587
if (chain.isEmpty())
8688
throw IllegalArgumentException("Certificate chain must not be empty")
8789
val cert = chain[0]
@@ -107,7 +109,7 @@ class CustomCertStore internal constructor(
107109
}
108110
}
109111

110-
if (appInForeground == null) {
112+
if (launcher == null) {
111113
logger.log(Level.INFO, "Certificate not known and running in non-interactive mode, rejecting")
112114
return false
113115
}
@@ -117,7 +119,7 @@ class CustomCertStore internal constructor(
117119

118120
try {
119121
withTimeout(userTimeout) {
120-
ui.check(cert, appInForeground.value)
122+
ui.check(cert, launcher)
121123
}
122124
} catch (_: TimeoutCancellationException) {
123125
logger.log(Level.WARNING, "User timeout while waiting for certificate decision, rejecting")

lib/src/main/java/at/bitfire/cert4android/UserDecisionRegistry.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
44
import android.app.PendingIntent
55
import android.content.Context
66
import android.content.Intent
7+
import androidx.activity.result.ActivityResultLauncher
78
import androidx.core.app.NotificationCompat
89
import androidx.core.app.TaskStackBuilder
910
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -48,10 +49,10 @@ class UserDecisionRegistry private constructor(
4849
* @param appInForeground whether the app is currently in foreground = whether it can directly launch an Activity
4950
* @return *true* if the user explicitly trusts the certificate, *false* if unknown or untrusted
5051
*/
51-
suspend fun check(cert: X509Certificate, appInForeground: Boolean): Boolean = suspendCancellableCoroutine { cont ->
52+
suspend fun check(cert: X509Certificate, launcher: ActivityResultLauncher<Intent>?): Boolean = suspendCancellableCoroutine { cont ->
5253
// check whether we're able to retrieve user feedback (= start an Activity and/or show a notification)
5354
val notificationsPermitted = NotificationUtils.notificationsPermitted(context)
54-
val userDecisionPossible = appInForeground || notificationsPermitted
55+
val userDecisionPossible = launcher != null || notificationsPermitted
5556

5657
if (userDecisionPossible) {
5758
// User decision possible → remember request in pendingDecisions so that a later decision will be applied to this request
@@ -86,7 +87,7 @@ class UserDecisionRegistry private constructor(
8687
}
8788

8889
if (requestDecision)
89-
requestDecision(cert, launchActivity = appInForeground, showNotification = notificationsPermitted)
90+
requestDecision(cert, launcher, showNotification = notificationsPermitted)
9091

9192
} else {
9293
// We're not able to retrieve user feedback, directly reject request
@@ -108,8 +109,8 @@ class UserDecisionRegistry private constructor(
108109
* @throws IllegalArgumentException when both [launchActivity] and [showNotification] are *false*
109110
*/
110111
@SuppressLint("MissingPermission")
111-
internal fun requestDecision(cert: X509Certificate, launchActivity: Boolean, showNotification: Boolean) {
112-
if (!launchActivity && !showNotification)
112+
internal fun requestDecision(cert: X509Certificate, launcher: ActivityResultLauncher<Intent>?, showNotification: Boolean) {
113+
if (launcher == null && !showNotification)
113114
throw IllegalArgumentException("User decision requires certificate Activity and/or notification")
114115

115116
val rawCert = cert.encoded
@@ -147,9 +148,9 @@ class UserDecisionRegistry private constructor(
147148
nm.notify(CertUtils.getTag(cert), NotificationUtils.ID_CERT_DECISION, notify)
148149
}
149150

150-
if (launchActivity) {
151+
if (launcher != null) {
151152
decisionIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
152-
context.startActivity(decisionIntent)
153+
launcher.launch(decisionIntent)
153154
}
154155
}
155156

0 commit comments

Comments
 (0)