mobiler 0.31.0

Build mobile apps in Rust — one core, native UI on Android, iOS, and the web (CLI)
package {{PACKAGE}}

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.browser.customtabs.CustomTabsIntent

/** Static relay between OAuthPlugin (Application context) and the transient OAuthActivity
 *  (which owns the browser launch + the redirect intent). */
object OAuthRelay {
    var authUrl: String? = null
    var onResult: ((String?) -> Unit)? = null
}

/**
 * Transient Activity that runs the OAuth browser round-trip and relays the redirect URL back to
 * OAuthPlugin, then finishes. Shipped by the oauth plugin so it needs no edits to MainActivity.
 *
 * Declared `singleTask` with an intent-filter on `${applicationId}` (see the plugin manifest), so
 * the provider's redirect to "<applicationId>://…" is delivered here as a new VIEW intent. The
 * flow (mirrors AppAuth's management activity, which solves the "onResume fires right after we
 * launch the tab" race):
 *   1st onResume → launch the Custom Tab, set authStarted, return (don't evaluate yet).
 *   redirect    → onNewIntent stores the redirect URL; then onResume runs again.
 *   2nd onResume → if the intent now has redirect data → success; otherwise the user backed out
 *                  of the tab → cancelled.
 */
class OAuthActivity : ComponentActivity() {
    private var authStarted = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (savedInstanceState != null) {
            authStarted = savedInstanceState.getBoolean(KEY_AUTH_STARTED, false)
        }
    }

    override fun onResume() {
        super.onResume()
        if (!authStarted) {
            val url = OAuthRelay.authUrl
            if (url == null) { finishWith(null); return }
            try {
                CustomTabsIntent.Builder().build().launchUrl(this, Uri.parse(url))
                authStarted = true
            } catch (e: Exception) {
                finishWith(null)
            }
            return
        }
        // Back from the browser. A redirect arrived via onNewIntent → intent.data is set (success);
        // otherwise the user dismissed the tab without completing (cancelled).
        finishWith(intent?.data?.toString())
    }

    override fun onNewIntent(newIntent: Intent) {
        super.onNewIntent(newIntent)
        setIntent(newIntent)
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putBoolean(KEY_AUTH_STARTED, authStarted)
    }

    private fun finishWith(result: String?) {
        OAuthRelay.onResult?.invoke(result)
        OAuthRelay.onResult = null
        OAuthRelay.authUrl = null
        finish()
    }

    private companion object {
        const val KEY_AUTH_STARTED = "authStarted"
    }
}