summaryrefslogtreecommitdiff
path: root/app/src/main/java/sh/lajo/buddy/MainActivity.kt
blob: 3f398b3cadd218d1e59e9636e657f43b25568a4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package sh.lajo.buddy

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.VpnService
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import sh.lajo.buddy.ui.theme.BuddyTheme

class MainActivity : ComponentActivity() {
    private val vpnPrepareLauncher =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            if (result.resultCode == Activity.RESULT_OK) {
                startBuddyVpnServiceIfAllowed()
            }
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()

        if (ConfigManager.shouldFetchConfig(this)) {
            ConfigManager.fetchConfig(this)
        }

        maybePrepareAndStartVpn()

        setContent {
            BuddyTheme {
                val context = LocalContext.current
                val prefs = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
                val navController = rememberNavController()
                val startDestination = if (prefs.getBoolean("onboardingFinished", false)) "home" else "onboarding/step1"
                val navBackStackEntry by navController.currentBackStackEntryAsState()
                val currentRoute = navBackStackEntry?.destination?.route

                if (prefs.getBoolean("onboardingFinished", false)) {
                    val wsIntent = Intent(context, WebSocketService::class.java)

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        context.startForegroundService(wsIntent)
                    } else {
                        context.startService(wsIntent)
                    }
                }

                // Determine which destinations should show the bottom bar
                val showBottomBar = currentRoute in listOf("main", "home", "settings")

                Scaffold(
                    bottomBar = {
                        // Only show the bottom bar on the main screens
                        if (showBottomBar) {
                            NavigationBar {
                                Destination.entries.forEach { destination ->
                                    NavigationBarItem(
                                        selected = currentRoute == destination.route,
                                        onClick = {
                                            navController.navigate(destination.route) {
                                                // Pop up to main to avoid building up a large stack
                                                popUpTo("main") {
                                                    saveState = true
                                                }
                                                // Avoid multiple copies of the same destination when
                                                // reselecting the same item
                                                launchSingleTop = true
                                                // Restore state when reselecting a previously selected item
                                                restoreState = true
                                            }
                                        },
                                        icon = { Icon(destination.icon, contentDescription = destination.contentDescription) },
                                        label = { Text(destination.label) }
                                    )
                                }
                            }
                        }
                    }
                ) { contentPadding ->
                    AppNavHost(
                        navController = navController,
                        startDestination = startDestination,
                        modifier = Modifier.padding(contentPadding)
                    )
                }
            }
        }
    }

    private fun maybePrepareAndStartVpn() {
        // If the user already granted VPN permission, prepare() returns null.
        val prepareIntent = VpnService.prepare(this)
        if (prepareIntent != null) {
            vpnPrepareLauncher.launch(prepareIntent)
        } else {
            startBuddyVpnServiceIfAllowed()
        }
    }

    private fun startBuddyVpnServiceIfAllowed() {
        // Even on Android O+, VpnService isn't started via startForegroundService like typical
        // foreground services; establishing the VPN tunnel is controlled by VpnService APIs.
        // Using startService here is the recommended pattern.
        val intent = Intent(this, BuddyVPNService::class.java)
        startService(intent)
    }
}