OXYGENHUB
◆ v0.1.9 oxygenhub-android / app / src / main / java / io / oxygenhub / android / MainActivity.kt
4.5 KiB · last changed oxygenhub.io links open in the app (closes #36) 1 day ago source blame history raw
1
package io.oxygenhub.android
2
3
import android.content.Intent
4
import android.os.Bundle
5
import androidx.activity.ComponentActivity
6
import androidx.activity.compose.setContent
7
import androidx.activity.enableEdgeToEdge
8
import androidx.compose.runtime.getValue
9
import androidx.compose.runtime.key
10
import androidx.compose.runtime.mutableStateOf
11
import androidx.glance.appwidget.updateAll
12
import androidx.lifecycle.compose.collectAsStateWithLifecycle
13
import androidx.lifecycle.lifecycleScope
14
import io.oxygenhub.android.alerts.LandAlerts
15
import io.oxygenhub.android.ui.nav.ProposalRoute
16
import io.oxygenhub.android.ui.nav.SignedInApp
17
import io.oxygenhub.android.ui.nav.rememberViewModel
18
import io.oxygenhub.android.ui.nav.routeFor
19
import io.oxygenhub.android.ui.signin.SignInScreen
20
import io.oxygenhub.android.ui.signin.SignInViewModel
21
import io.oxygenhub.android.ui.theme.OxygenHubTheme
22
import io.oxygenhub.android.widget.HubWidget
23
import io.oxygenhub.android.widget.WidgetTaps
24
import kotlinx.coroutines.launch
25
import uniffi.rust_core.oxygenGreeting
26
27
class MainActivity : ComponentActivity() {
28
    /** A proposal a notification asked to open (#20), until it is shown. */
29
    /** A screen to open, as a notification or the widget asks: a route of the app's navigation. */
30
    private val opening = mutableStateOf<Any?>(null)
31
32
    override fun onCreate(savedInstanceState: Bundle?) {
33
        super.onCreate(savedInstanceState)
34
        if (savedInstanceState == null) opening.value = routeIn(intent)
35
        enableEdgeToEdge()
36
        val container = (application as OxygenHubApp).container
37
        val sessions = container.sessions
38
        setContent {
39
            OxygenHubTheme {
40
                val session by sessions.session.collectAsStateWithLifecycle()
41
                val signedIn = session
42
                val api = signedIn?.let { sessions.api() }
43
                if (signedIn == null || api == null) {
44
                    SignInScreen(
45
                        rememberViewModel { SignInViewModel(sessions, container::deviceAuth, container.clientName, DEFAULT_HUB) },
46
                    )
47
                } else {
48
                    // A new session starts the signed-in app afresh: nothing of the last account's is kept.
49
                    key(signedIn) {
50
                        SignedInApp(
51
                            session = signedIn,
52
                            api = api,
53
                            nativeCore = { oxygenGreeting(signedIn.username) },
54
                            onSignOut = { sessions.signOut() },
55
                            open = opening.value,
56
                            onOpened = { opening.value = null },
57
                        )
58
                    }
59
                }
60
            }
61
        }
62
    }
63
64
    /** The widget (#28) shows what the app knows now, whenever the app comes to the front. */
65
    override fun onResume() {
66
        super.onResume()
67
        lifecycleScope.launch { runCatching { HubWidget().updateAll(this@MainActivity) } }
68
    }
69
70
    override fun onNewIntent(intent: Intent) {
71
        super.onNewIntent(intent)
72
        routeIn(intent)?.let { opening.value = it }
73
    }
74
75
    /** A land alert's proposal, the screen a tap on the widget's counts names (#31), or a link's (#36). */
76
    private fun routeIn(intent: Intent?): Any? = proposalIn(intent) ?: WidgetTaps.route(intent) ?: linkIn(intent)
77
78
    /**
79
     * An oxygenhub.io link opened in the app (App Links, #36): its screen,
80
     * as links inside the app go. One the app cannot show (signed in to
81
     * another hub, say) goes to the browser, which cannot hand it back.
82
     */
83
    private fun linkIn(intent: Intent?): Any? {
84
        if (intent?.action != Intent.ACTION_VIEW) return null
85
        val url = intent.dataString ?: return null
86
        val hub = (application as OxygenHubApp).container.sessions.session.value?.hub ?: DEFAULT_HUB
87
        routeFor(url, hub)?.let { return it }
88
        val browser = Intent(Intent.ACTION_VIEW, intent.data).apply {
89
            selector = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, Intent.CATEGORY_APP_BROWSER)
90
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
91
        }
92
        runCatching { startActivity(browser) }
93
        return null
94
    }
95
96
    private fun proposalIn(intent: Intent?): ProposalRoute? {
97
        val repository = intent?.getStringExtra(LandAlerts.EXTRA_REPOSITORY) ?: return null
98
        val number = intent.getLongExtra(LandAlerts.EXTRA_NUMBER, 0)
99
        if ('/' !in repository || number <= 0) return null
100
        return ProposalRoute(repository.substringBefore('/'), repository.substringAfter('/'), number)
101
    }
102
}