diff options
| author | JustZvan <justzvan@justzvan.xyz> | 2026-02-06 13:38:36 +0100 |
|---|---|---|
| committer | JustZvan <justzvan@justzvan.xyz> | 2026-02-06 13:38:36 +0100 |
| commit | adb6a4fd9ec3a23c04d5e4c2ce799448237915c4 (patch) | |
| tree | 786edcf5888788e0667a90fae96d7ebec68c507a /app/src/main/java/sh/lajo/buddy/MainActivity.kt | |
feat: initial commit
Diffstat (limited to 'app/src/main/java/sh/lajo/buddy/MainActivity.kt')
| -rw-r--r-- | app/src/main/java/sh/lajo/buddy/MainActivity.kt | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/app/src/main/java/sh/lajo/buddy/MainActivity.kt b/app/src/main/java/sh/lajo/buddy/MainActivity.kt new file mode 100644 index 0000000..3f398b3 --- /dev/null +++ b/app/src/main/java/sh/lajo/buddy/MainActivity.kt @@ -0,0 +1,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) + } +}
\ No newline at end of file |