@@ -6,7 +6,6 @@ import androidx.activity.ComponentActivity
6
6
import androidx.browser.customtabs.CustomTabsIntent
7
7
import androidx.lifecycle.DefaultLifecycleObserver
8
8
import androidx.lifecycle.LifecycleOwner
9
- import {{ sdk .namespace | caseDot }}.services.KeepAliveService
10
9
import kotlinx.coroutines.delay
11
10
import kotlin.collections.component1
12
11
import kotlin.collections.component2
@@ -18,34 +17,31 @@ import kotlin.collections.set
18
17
* Used to authenticate with external OAuth2 providers. Launches browser windows and handles
19
18
* suspension until the user completes the process or otherwise returns to the app.
20
19
*/
21
- internal class WebAuthComponent {
20
+ actual class WebAuthComponent(private val activity: ComponentActivity) {
22
21
23
- companion object : DefaultLifecycleObserver {
22
+ internal companion object : DefaultLifecycleObserver {
24
23
private var suspended = false
25
- private val callbacks = mutableMapOf<String , ((( Result <String >) -> Unit)?) >()
24
+ private val callbacks = mutableMapOf<String , ((Result <String >) -> Unit)?>()
26
25
27
26
override fun onResume(owner: LifecycleOwner) {
27
+ // When the activity resumes, end the suspension so that the caller can continue.
28
28
suspended = false
29
29
}
30
30
31
31
/**
32
- * Authenticate Session with OAuth2
32
+ * Authenticate session using OAuth2.
33
33
*
34
- * Launches a chrome custom tab from the given activity and directs to the given url,
35
- * suspending until the user returns to the app, at which point the given [onComplete] callback
36
- * will run, passing the callback url from the intent used to launch the [CallbackActivity],
37
- * or an [IllegalStateException] in the case the user closed the window or returned to the
38
- * app without passing through the [CallbackActivity].
34
+ * Launches a Chrome Custom Tab from the provided activity to open the given URL.
35
+ * Once the user returns to the app (resuming the activity), the provided callback is invoked.
39
36
*
40
- *
41
- * @param activity The activity to launch the browser from and observe the lifecycle of
42
- * @param url The url to launch
43
- * @param callbackUrlScheme The callback url scheme used to key the given callback
44
- * @param onComplete The callback to run when a result (success or failure) is received
37
+ * @param activity The activity used to launch the browser and observe lifecycle events.
38
+ * @param url The URL to open.
39
+ * @param callbackUrlScheme The URL scheme to match for the authentication callback.
40
+ * @param onComplete The callback to run with the authentication result.
45
41
*/
46
- suspend fun authenticate(
42
+ internal suspend fun authenticate(
47
43
activity: ComponentActivity,
48
- url: Uri ,
44
+ url: String ,
49
45
callbackUrlScheme: String,
50
46
onComplete: ((Result<String >) -> Unit)?
51
47
) {
@@ -56,13 +52,14 @@ internal class WebAuthComponent {
56
52
57
53
intent.intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
58
54
intent.intent.putExtra("android.support.customtabs.extra.KEEP_ALIVE", keepAliveIntent)
59
- intent.launchUrl(activity, url)
55
+ intent.launchUrl(activity, Uri.parse( url) )
60
56
57
+ // Add this as a lifecycle observer so that we know when the user returns to the app.
61
58
activity.runOnUiThread {
62
59
activity.lifecycle.addObserver(this)
63
60
}
64
61
65
- // Need to dirty poll block so execution doesn't continue at the callsite of this function
62
+ // Poll until the authentication has been resumed.
66
63
suspended = true
67
64
while (suspended) {
68
65
delay(200)
@@ -71,30 +68,49 @@ internal class WebAuthComponent {
71
68
}
72
69
73
70
/**
74
- * Trigger a web auth callback
71
+ * Invoke the callback for the provided scheme.
75
72
*
76
- * Attempts to find a callback for the given [scheme] and if found, invokes it, passing the
77
- * given [url]. Calling this method stops auth suspension, so any calls to [authenticate]
78
- * will continue execution from their suspension points immediately after this method
79
- * is called.
73
+ * This method ends the suspension, allowing any waiting coroutines to resume.
80
74
*
81
- * @param scheme The scheme to match to a callback's key
82
- * @param url The url received through intent data from the [CallbackActivity]
75
+ * @param scheme The callback scheme key.
76
+ * @param url The URL provided in the callback.
83
77
*/
84
- fun onCallback(scheme: String, url: Uri) {
85
- callbacks.remove(scheme)?.invoke(
86
- Result.success(url.toString())
87
- )
78
+ internal fun onCallback(scheme: String, url: String) {
79
+ callbacks.remove(scheme)?.invoke(Result.success(url))
88
80
suspended = false
89
81
}
90
82
91
83
private fun cleanUp() {
92
- callbacks.forEach { (_, danglingResultCallback) ->
93
- danglingResultCallback?.invoke(
94
- Result.failure(IllegalStateException("User cancelled login"))
95
- )
84
+ callbacks.forEach { (_, callback) ->
85
+ callback?.invoke(Result.failure(IllegalStateException("User cancelled login")))
96
86
}
97
87
callbacks.clear()
98
88
}
99
89
}
90
+
91
+ /**
92
+ * Suspend function to perform authentication.
93
+ *
94
+ * @param url The URL to launch.
95
+ * @param callbackUrlScheme The callback URL scheme.
96
+ * @param onComplete The callback to invoke with the result.
97
+ */
98
+ @Throws(Throwable::class)
99
+ internal actual suspend fun authenticate(
100
+ url: String,
101
+ callbackUrlScheme: String,
102
+ onComplete: ((Result<String >) -> Unit)?
103
+ ) {
104
+ authenticate(activity, url, callbackUrlScheme, onComplete)
105
+ }
106
+
107
+ /**
108
+ * Called when the external callback URL is received.
109
+ *
110
+ * @param scheme The scheme received.
111
+ * @param url The full URL for the callback.
112
+ */
113
+ internal actual fun onCallback(scheme: String, url: String) {
114
+ WebAuthComponent.onCallback(scheme, url)
115
+ }
100
116
}
0 commit comments