This commit is contained in:
Dimitris
2026-03-26 07:13:31 +01:00
parent 5098dad9d6
commit a3370e42a8
18 changed files with 590 additions and 101 deletions

View File

@@ -6,6 +6,10 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- <uses-permission android:name="android.permission.READ_CONTACTS"/>-->
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
tools:ignore="MockLocation" />
@@ -36,6 +40,12 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.kouros.navigation.car.navigation.NavigationService"
android:enabled="true"
android:foregroundServiceType="location"
android:exported="true">
</service>
</application>
</manifest>

View File

@@ -1,11 +1,13 @@
package com.kouros.navigation.ui
import android.Manifest
import android.app.AppOpsManager
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.location.LocationManager
import android.os.Bundle
import android.os.Process
import android.widget.Toast
import android.os.IBinder
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
@@ -41,19 +43,16 @@ import com.google.android.gms.location.LocationServices
import com.kouros.data.R
import com.kouros.navigation.MainApplication.Companion.navigationViewModel
import com.kouros.navigation.car.TextToSpeechManager
import com.kouros.navigation.car.navigation.NavigationService
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
import com.kouros.navigation.data.Constants.INSTRUCTION_DISTANCE
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.Constants.TILT
import com.kouros.navigation.data.Constants.homeVogelhart
import com.kouros.navigation.data.StepData
import com.kouros.navigation.model.BaseStyleModel
import com.kouros.navigation.model.MockLocation
import com.kouros.navigation.model.RouteModel
import com.kouros.navigation.model.SimulationType
import com.kouros.navigation.model.simulate
import com.kouros.navigation.model.simulationJob
import com.kouros.navigation.model.test
import com.kouros.navigation.model.testSingle
import com.kouros.navigation.ui.app.AppViewModel
import com.kouros.navigation.ui.app.appViewModel
import com.kouros.navigation.ui.navigation.AppNavGraph
@@ -80,10 +79,13 @@ import kotlin.time.Duration.Companion.seconds
class MainActivity : ComponentActivity() {
var navigationService: NavigationService? = null
var isBound: Boolean = false
val routeData = MutableLiveData("")
val routeModel = RouteModel()
var tilt = TILT
val useMock = false
val type = SimulationType.SIMULATE
val stepData: MutableLiveData<StepData> by lazy {
@@ -96,26 +98,21 @@ class MainActivity : ComponentActivity() {
var lastLocation = location(0.0, 0.0)
val observer = Observer<String> { newRoute ->
if (newRoute.isNotEmpty()) {
val repository = getSettingsRepository(applicationContext)
val routingEngine = runBlocking { repository.routingEngineFlow.first() }
routeModel.navState = routeModel.navState.copy(routingEngine = routingEngine)
routeModel.startNavigation(newRoute)
routeData.value = routeModel.curRoute.routeGeoJson
// checkMock()
startNavigation(newRoute)
}
}
// Monitors the state of the connection to the navigation service.
private val serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val binder: NavigationService.LocalBinder = service as NavigationService.LocalBinder
navigationService = binder.service
isBound = true
}
private fun checkMock() {
if (useMock) {
when (type) {
SimulationType.SIMULATE -> simulate(routeModel, mock)
SimulationType.TEST -> test(applicationContext, routeModel)
SimulationType.TEST_SINGLE -> testSingle(applicationContext, routeModel, mock)
else -> {}
}
override fun onServiceDisconnected(name: ComponentName?) {
navigationService = null
isBound = false
}
}
@@ -127,9 +124,7 @@ class MainActivity : ComponentActivity() {
private lateinit var locationManager: LocationManager
private lateinit var fusedLocationClient: FusedLocationProviderClient
private lateinit var mock: MockLocation
private var loadRecentPlaces = false
lateinit var textToSpeechManager: TextToSpeechManager
var guidanceAudio = 0
@@ -152,20 +147,10 @@ class MainActivity : ComponentActivity() {
repository.guidanceAudioFlow.asLiveData().observe(this, Observer {
guidanceAudio = it
})
if (useMock) {
checkMockLocationEnabled()
}
locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
fusedLocationClient.lastLocation.addOnSuccessListener { _: android.location.Location? ->
navigationViewModel.route.observe(this, observer)
if (useMock) {
mock = MockLocation(locationManager)
mock.setMockLocation(
homeVogelhart.latitude, homeVogelhart.longitude, 0F
)
}
}
lifecycleScope.launch {
getSettingsViewModel(applicationContext).routingEngine.first()
@@ -183,6 +168,27 @@ class MainActivity : ComponentActivity() {
}
}
override fun onStart() {
super.onStart()
Log.i(TAG, "In onStart()")
bindService(
Intent(this, NavigationService::class.java),
serviceConnection,
BIND_AUTO_CREATE
)
requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)
}
override fun onStop() {
Log.i(TAG, "In onStop(). bound $isBound")
if (isBound) {
unbindService(serviceConnection)
isBound = false
navigationService = null
}
super.onStop()
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun StartScreen(
@@ -206,10 +212,8 @@ class MainActivity : ComponentActivity() {
// navigationViewModel.route.value = lastRoute
//}
val userLocationState = rememberUserLocationState(locationProvider)
if (!useMock) {
val locationState = locationProvider.location.collectAsState()
updateLocation(locationState.value)
}
val step: StepData? by stepData.observeAsState()
val nextStep: StepData? by nextStepData.observeAsState()
@@ -284,7 +288,7 @@ class MainActivity : ComponentActivity() {
step,
nextStep,
{ stopNavigation {} },
{ simulateNavigation() })
{ })
}
}
}
@@ -346,18 +350,21 @@ class MainActivity : ComponentActivity() {
}
}
fun startNavigation(newRoute: String) {
val repository = getSettingsRepository(applicationContext)
val routingEngine = runBlocking { repository.routingEngineFlow.first() }
routeModel.navState = routeModel.navState.copy(routingEngine = routingEngine)
routeModel.startNavigation(newRoute)
routeData.value = routeModel.curRoute.routeGeoJson
navigationService?.startNavigation()
}
fun stopNavigation(closeSheet: () -> Unit) {
val latitude = routeModel.curRoute.waypoints[0][1]
val longitude = routeModel.curRoute.waypoints[0][0]
closeSheet()
routeModel.stopNavigation()
getSettingsViewModel(applicationContext).onLastRouteChanged("")
if (useMock) {
simulationJob?.cancel()
mock.setMockLocation(latitude, longitude, 0F)
}
routeData.value = ""
stepData.value = StepData("", "", 0.0, 0, 0, 0, 0.0)
navigationService?.stopNavigation()
}
fun textToSpeech() {
@@ -368,37 +375,5 @@ class MainActivity : ComponentActivity() {
lastStepIndex = currentStep.index
}
}
fun simulateNavigation() {
simulate(
routeModel = routeModel, mock = mock
)
}
private fun checkMockLocationEnabled() {
try {
// Check if mock location is enabled for this app
val appOpsManager = getSystemService(APP_OPS_SERVICE) as AppOpsManager
val mode = appOpsManager.checkOp(
AppOpsManager.OPSTR_MOCK_LOCATION, Process.myUid(), packageName
)
if (mode != AppOpsManager.MODE_ALLOWED) {
Toast.makeText(
this,
"Please select this app as mock location app in Developer Options",
Toast.LENGTH_LONG
).show()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
enum class ExpandedType {
HALF, FULL, COLLAPSED
}
}