package com.hawhamburg.micr0bu import android.Manifest import android.os.Build import android.os.Bundle import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.appcompat.app.AppCompatActivity import androidx.activity.SystemBarStyle import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.SideEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalView import androidx.core.view.WindowCompat import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import com.hawhamburg.micr0bu.ui.components.StatusTopBar import com.hawhamburg.micr0bu.ui.navigation.BottomNavBar import com.hawhamburg.micr0bu.ui.navigation.Screen import com.hawhamburg.micr0bu.ui.screens.ConnectionSetupScreen import com.hawhamburg.micr0bu.ui.screens.DashboardScreen import com.hawhamburg.micr0bu.ui.screens.MqttTopicViewerScreen import com.hawhamburg.micr0bu.ui.screens.RecordingScreen import com.hawhamburg.micr0bu.ui.screens.SensorScreen import com.hawhamburg.micr0bu.ui.screens.SessionLogScreen import com.hawhamburg.micr0bu.ui.screens.MapScreen import com.hawhamburg.micr0bu.ui.screens.TripHistoryScreen import com.hawhamburg.micr0bu.ui.screens.TripReviewScreen import com.hawhamburg.micr0bu.ui.screens.AboutSettingsScreen import com.hawhamburg.micr0bu.ui.screens.AppearanceSettingsScreen import com.hawhamburg.micr0bu.ui.screens.ConnectionSettingsScreen import com.hawhamburg.micr0bu.ui.screens.DeveloperSettingsScreen import com.hawhamburg.micr0bu.ui.screens.LanguageSettingsScreen import com.hawhamburg.micr0bu.ui.screens.MqttSettingsScreen import com.hawhamburg.micr0bu.ui.screens.SensorsSettingsScreen import com.hawhamburg.micr0bu.ui.screens.SettingsScreen import com.hawhamburg.micr0bu.ui.screens.UnitsSettingsScreen import com.hawhamburg.micr0bu.ui.theme.MicrOBUTheme import com.hawhamburg.micr0bu.viewmodel.MqttViewModel import com.hawhamburg.micr0bu.viewmodel.SensorViewModel import com.hawhamburg.micr0bu.viewmodel.TripRecordingViewModel import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class MainActivity : AppCompatActivity() { private val viewModel: SensorViewModel by viewModels() private val mqttViewModel: MqttViewModel by viewModels() private val tripViewModel: TripRecordingViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { installSplashScreen() super.onCreate(savedInstanceState) enableEdgeToEdge( statusBarStyle = SystemBarStyle.dark(android.graphics.Color.TRANSPARENT), navigationBarStyle = SystemBarStyle.dark(android.graphics.Color.TRANSPARENT), ) setContent { val state by viewModel.state.collectAsState() val mqttPrefs by mqttViewModel.mqttPrefs.collectAsState() val mqttConnectionState by mqttViewModel.connectionState.collectAsState() val activeTransport by mqttViewModel.activeTransport.collectAsState() val usbConnected by mqttViewModel.usbConnected.collectAsState() val obuStationTypeWarning by mqttViewModel.obuStationTypeWarning.collectAsState() val tripServiceState by tripViewModel.serviceState.collectAsState() MicrOBUTheme(darkTheme = state.darkTheme) { val view = LocalView.current SideEffect { WindowCompat.getInsetsController(window, view).apply { isAppearanceLightStatusBars = !state.darkTheme isAppearanceLightNavigationBars = !state.darkTheme } } val navController = rememberNavController() val locationLauncher = rememberLauncherForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> val granted = permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true || permissions[Manifest.permission.ACCESS_COARSE_LOCATION] == true if (granted) viewModel.onLocationPermissionGranted() } // Request POST_NOTIFICATIONS on Android 13+ val notificationLauncher = rememberLauncherForActivityResult( ActivityResultContracts.RequestPermission() ) { /* ignored — notification permission is best-effort */ } LaunchedEffect(Unit) { viewModel.startImuStreams() locationLauncher.launch( arrayOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, ) ) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { notificationLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) } } Scaffold( topBar = { StatusTopBar(state, mqttConnectionState) }, bottomBar = { BottomNavBar(navController) }, ) { innerPadding -> NavHost( navController = navController, startDestination = Screen.Dashboard.route, modifier = Modifier.padding(innerPadding), ) { composable(Screen.Dashboard.route) { DashboardScreen( state = state, mqttConnectionState = mqttConnectionState, activeTransport = activeTransport, usbCableConnected = usbConnected, obuStationTypeWarning = obuStationTypeWarning, onNavigateToConnection = { navController.navigate(Screen.Connection.route) }, onNavigateToSensors = { navController.navigate(Screen.Sensors.route) { popUpTo(Screen.Dashboard.route) { saveState = true } launchSingleTop = true restoreState = true } }, onNavigateToMap = { navController.navigate(Screen.Map.route) }, ) } composable(Screen.Sensors.route) { SensorScreen(state = state) } composable(Screen.Record.route) { RecordingScreen( state = state, mqttConnectionState = mqttConnectionState, tripServiceState = tripServiceState, onToggleRecording = { // Start / stop both CSV recording and the event detection service viewModel.toggleRecording() tripViewModel.toggleRecording() }, onOpenSessionLog = { navController.navigate(Screen.Log.route) }, ) } composable(Screen.Log.route) { SessionLogScreen( state = state, onDeleteSession = viewModel::deleteSession, ) } // ── Phase A: Trip History ───────────────────────────── composable(Screen.TripHistory.route) { TripHistoryScreen( viewModel = tripViewModel, onOpenTrip = { tripId -> navController.navigate(Screen.TripReview.route(tripId)) }, ) } composable( route = Screen.TripReview.route, arguments = listOf( navArgument("tripId") { type = NavType.LongType } ), ) { backStackEntry -> val tripId = backStackEntry.arguments ?.getLong("tripId", -1L) ?: -1L if (tripId < 0L) return@composable val trips by tripViewModel.trips.collectAsState(initial = emptyList()) val trip = trips.firstOrNull { it.id == tripId } if (trip != null) { TripReviewScreen( trip = trip, viewModel = tripViewModel, ) } } composable(Screen.MqttViewer.route) { MqttTopicViewerScreen(viewModel = mqttViewModel) } composable(Screen.Settings.route) { SettingsScreen( onOpenLanguage = { navController.navigate(Screen.SettingsLanguage.route) }, onOpenAppearance = { navController.navigate(Screen.SettingsAppearance.route) }, onOpenSensors = { navController.navigate(Screen.SettingsSensors.route) }, onOpenUnits = { navController.navigate(Screen.SettingsUnits.route) }, onOpenConnection = { navController.navigate(Screen.SettingsConnection.route) }, onOpenMqtt = { navController.navigate(Screen.SettingsMqtt.route) }, onOpenDeveloper = { navController.navigate(Screen.SettingsDeveloper.route) }, onOpenAbout = { navController.navigate(Screen.SettingsAbout.route) }, ) } composable(Screen.SettingsConnection.route) { ConnectionSettingsScreen( mqttPrefs = mqttPrefs, onMqttPrefsChange = mqttViewModel::updatePrefs, onBack = { navController.popBackStack() }, ) } composable(Screen.SettingsLanguage.route) { LanguageSettingsScreen(onBack = { navController.popBackStack() }) } composable(Screen.SettingsAppearance.route) { AppearanceSettingsScreen( darkTheme = state.darkTheme, onDarkTheme = viewModel::setDarkTheme, onBack = { navController.popBackStack() }, ) } composable(Screen.SettingsSensors.route) { SensorsSettingsScreen( state = state, onAccelEnabled = viewModel::setAccelEnabled, onGyroEnabled = viewModel::setGyroEnabled, onMagnetEnabled = viewModel::setMagnetEnabled, onBarometerEnabled = viewModel::setBarometerEnabled, onGnssEnabled = viewModel::setGnssEnabled, onBack = { navController.popBackStack() }, ) } composable(Screen.SettingsUnits.route) { UnitsSettingsScreen( useMetric = state.useMetric, onUseMetric = viewModel::setUseMetric, onBack = { navController.popBackStack() }, ) } composable(Screen.SettingsMqtt.route) { MqttSettingsScreen( mqttPrefs = mqttPrefs, onMqttPrefsChange = mqttViewModel::updatePrefs, onBack = { navController.popBackStack() }, ) } composable(Screen.SettingsDeveloper.route) { DeveloperSettingsScreen( state = state, onDeveloperMode = viewModel::setDeveloperMode, onBack = { navController.popBackStack() }, ) } composable(Screen.SettingsAbout.route) { AboutSettingsScreen(onBack = { navController.popBackStack() }) } composable(Screen.Connection.route) { ConnectionSetupScreen() } composable(Screen.Map.route) { MapScreen(gnss = state.gnss) } } } } } } }