Reroute
This commit is contained in:
@@ -6,6 +6,9 @@
|
||||
<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.READ_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
|
||||
tools:ignore="MockLocation" />
|
||||
|
||||
<application
|
||||
android:name="com.kouros.navigation.MainApplication"
|
||||
@@ -23,7 +26,7 @@
|
||||
android:resource="@xml/automotive_app_desc" />
|
||||
|
||||
<activity
|
||||
android:name="com.kouros.navigation.MainActivity"
|
||||
android:name="com.kouros.navigation.ui.MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Navigation">
|
||||
<intent-filter>
|
||||
|
||||
@@ -24,5 +24,7 @@ class MainApplication : Application() {
|
||||
companion object {
|
||||
var appContext: Context? = null
|
||||
private set
|
||||
|
||||
var useContacts = true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.kouros.navigation.model
|
||||
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import android.os.SystemClock
|
||||
|
||||
class MockLocation (private var locationManager: LocationManager) {
|
||||
|
||||
fun setMockLocation(latitude: Double, longitude: Double) {
|
||||
try {
|
||||
// Set mock location for all providers
|
||||
setMockLocationForProvider(LocationManager.GPS_PROVIDER, latitude, longitude)
|
||||
setMockLocationForProvider(LocationManager.NETWORK_PROVIDER, latitude, longitude)
|
||||
} catch (e: NumberFormatException) {
|
||||
} catch (e: SecurityException) {
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setMockLocationForProvider(provider: String, latitude: Double, longitude: Double) {
|
||||
try {
|
||||
// Check if provider exists
|
||||
if (!locationManager.allProviders.contains(provider)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Enable test provider
|
||||
// For API 31+
|
||||
locationManager.addTestProvider(
|
||||
provider,
|
||||
false, // requiresNetwork
|
||||
false, // requiresSatellite
|
||||
false, // requiresCell
|
||||
false, // hasMonetaryCost
|
||||
true, // supportsAltitude
|
||||
true, // supportsSpeed
|
||||
true, // supportsBearing
|
||||
android.location.provider.ProviderProperties.POWER_USAGE_LOW,
|
||||
android.location.provider.ProviderProperties.ACCURACY_FINE
|
||||
)
|
||||
|
||||
locationManager.setTestProviderEnabled(provider, true)
|
||||
|
||||
// Create mock location
|
||||
val mockLocation = Location(provider).apply {
|
||||
this.latitude = latitude
|
||||
this.longitude = longitude
|
||||
this.altitude = 0.0
|
||||
this.accuracy = 1.0f
|
||||
this.speed = 15F
|
||||
this.time = System.currentTimeMillis()
|
||||
this.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
|
||||
|
||||
this.bearingAccuracyDegrees = 0.0f
|
||||
this.verticalAccuracyMeters = 0.0f
|
||||
this.speedAccuracyMetersPerSecond = 0.0f
|
||||
}
|
||||
|
||||
// Set the mock location
|
||||
locationManager.setTestProviderLocation(provider, mockLocation)
|
||||
|
||||
} catch (e: SecurityException) {
|
||||
throw e
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// Provider already exists, just update location
|
||||
try {
|
||||
locationManager.setTestProviderEnabled(provider, true)
|
||||
|
||||
val mockLocation = Location(provider).apply {
|
||||
this.latitude = latitude
|
||||
this.longitude = longitude
|
||||
this.altitude = 0.0
|
||||
this.accuracy = 1.0f
|
||||
this.time = System.currentTimeMillis()
|
||||
this.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
|
||||
|
||||
this.bearingAccuracyDegrees = 0.0f
|
||||
this.verticalAccuracyMeters = 0.0f
|
||||
this.speedAccuracyMetersPerSecond = 0.0f
|
||||
}
|
||||
|
||||
locationManager.setTestProviderLocation(provider, mockLocation)
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +1,30 @@
|
||||
package com.kouros.navigation
|
||||
package com.kouros.navigation.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AppOpsManager
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import android.os.Bundle
|
||||
import android.os.Process
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalDrawerSheet
|
||||
import androidx.compose.material3.ModalNavigationDrawer
|
||||
import androidx.compose.material3.NavigationDrawerItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SegmentedButtonDefaults.Icon
|
||||
import androidx.compose.material3.SegmentedButtonDefaults
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
@@ -38,46 +37,40 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.kouros.navigation.ui.theme.NavigationTheme
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
import com.google.android.gms.location.FusedLocationProviderClient
|
||||
import com.google.android.gms.location.LocationServices
|
||||
import com.kouros.android.cars.carappservice.R
|
||||
import com.kouros.navigation.MainApplication
|
||||
import com.kouros.navigation.car.BuildingLayer
|
||||
import com.kouros.navigation.car.Puck
|
||||
import com.kouros.navigation.car.PuckState
|
||||
import com.kouros.navigation.car.RouteLayer
|
||||
|
||||
import com.kouros.navigation.data.Category
|
||||
import com.kouros.navigation.data.Constants
|
||||
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
|
||||
import com.kouros.navigation.data.NavigationRepository
|
||||
import com.kouros.navigation.data.StepData
|
||||
import com.kouros.navigation.model.MockLocation
|
||||
import com.kouros.navigation.model.RouteModel
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
||||
import com.kouros.navigation.utils.NavigationUtils.snapLocation
|
||||
import com.kouros.navigation.ui.theme.NavigationTheme
|
||||
import com.kouros.navigation.utils.NavigationUtils
|
||||
import com.kouros.navigation.utils.calculateZoom
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.maplibre.compose.camera.CameraPosition
|
||||
import org.maplibre.compose.camera.rememberCameraState
|
||||
import org.maplibre.compose.location.DesiredAccuracy
|
||||
import org.maplibre.compose.location.LocationPuck
|
||||
import org.maplibre.compose.location.LocationPuckColors
|
||||
import org.maplibre.compose.location.LocationPuckSizes
|
||||
import org.maplibre.compose.location.LocationTrackingEffect
|
||||
import org.maplibre.compose.location.rememberDefaultLocationProvider
|
||||
import org.maplibre.compose.location.rememberUserLocationState
|
||||
@@ -87,10 +80,12 @@ import org.maplibre.compose.style.BaseStyle
|
||||
import org.maplibre.spatialk.geojson.Position
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
|
||||
private val LOCATION_PERMISSION_REQUEST_CODE = 1001
|
||||
|
||||
private val CONTACTS_PERMISSION_REQUEST_CODE = 1002
|
||||
|
||||
val routeData = MutableLiveData("")
|
||||
|
||||
val vieModel = ViewModel(NavigationRepository())
|
||||
@@ -107,6 +102,8 @@ class MainActivity : ComponentActivity() {
|
||||
val observer = Observer<String> { newRoute ->
|
||||
routeModel.startNavigation(newRoute)
|
||||
routeData.value = routeModel.route.routeGeoJson
|
||||
println("Start simulating $newRoute")
|
||||
simulate()
|
||||
}
|
||||
|
||||
val cameraPosition = MutableLiveData(
|
||||
@@ -116,77 +113,104 @@ class MainActivity : ComponentActivity() {
|
||||
)
|
||||
)
|
||||
|
||||
var locationIndex = 0
|
||||
|
||||
var simulate = false
|
||||
private lateinit var locationManager: LocationManager
|
||||
private lateinit var fusedLocationClient: FusedLocationProviderClient
|
||||
|
||||
private lateinit var mock: MockLocation
|
||||
|
||||
init {
|
||||
vieModel.route.observe(this, observer)
|
||||
if (simulate) {
|
||||
vieModel.loadRoute(
|
||||
Constants.homeLocation,
|
||||
Constants.home2Location
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
checkLocationPermissions()
|
||||
|
||||
if (MainApplication.useContacts) {
|
||||
checkContactsPermissions()
|
||||
}
|
||||
|
||||
checkMockLocationEnabled()
|
||||
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
val scope = rememberCoroutineScope()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
if ((checkPermissionForLocation() && !MainApplication.useContacts)
|
||||
|| (checkPermissionForLocation() && MainApplication.useContacts && checkPermissionForContact())) {
|
||||
Content()
|
||||
} else {
|
||||
|
||||
NavigationTheme {
|
||||
ModalNavigationDrawer(
|
||||
drawerContent = {
|
||||
ModalDrawerSheet {
|
||||
Text("Drawer title", modifier = Modifier.padding(16.dp))
|
||||
HorizontalDivider()
|
||||
NavigationDrawerItem(
|
||||
label = { Text(text = "Drawer Item") },
|
||||
selected = false,
|
||||
onClick = { /*TODO*/ }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Content() {
|
||||
locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
|
||||
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
|
||||
mock = MockLocation(locationManager)
|
||||
mock.setMockLocation(
|
||||
Constants.homeLocation.latitude,
|
||||
Constants.homeLocation.longitude
|
||||
)
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
var simulationText by remember { mutableStateOf("Start Simulation") }
|
||||
|
||||
NavigationTheme {
|
||||
ModalNavigationDrawer(
|
||||
drawerContent = {
|
||||
ModalDrawerSheet {
|
||||
Text("Drawer title", modifier = Modifier.Companion.padding(16.dp))
|
||||
HorizontalDivider()
|
||||
NavigationDrawerItem(
|
||||
label = { Text(text = "Drawer Item") },
|
||||
selected = false,
|
||||
onClick = { /*TODO*/ }
|
||||
)
|
||||
}
|
||||
},
|
||||
gesturesEnabled = false
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
snackbarHost = {
|
||||
SnackbarHost(hostState = snackbarHostState)
|
||||
},
|
||||
gesturesEnabled = false
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
snackbarHost = {
|
||||
SnackbarHost(hostState = snackbarHostState)
|
||||
},
|
||||
floatingActionButton = {
|
||||
ExtendedFloatingActionButton(
|
||||
text = {
|
||||
Text("Navigate")
|
||||
},
|
||||
icon = { Icon(true) },
|
||||
onClick = {
|
||||
scope.launch {
|
||||
snackbarHostState.showSnackbar("Starte Navigation")
|
||||
}
|
||||
if (!routeModel.isNavigating() && lastLocation.latitude != 0.0) {
|
||||
tilt = 60.0
|
||||
vieModel.loadRoute(
|
||||
lastLocation,
|
||||
Constants.home2Location
|
||||
)
|
||||
} else {
|
||||
tilt = 0.0
|
||||
routeModel.stopNavigation()
|
||||
routeData.value = ""
|
||||
}
|
||||
|
||||
floatingActionButton = {
|
||||
ExtendedFloatingActionButton(
|
||||
text = {
|
||||
Text(simulationText)
|
||||
},
|
||||
icon = { SegmentedButtonDefaults.Icon(true) },
|
||||
onClick = {
|
||||
scope.launch {
|
||||
snackbarHostState.showSnackbar("Starte Navigation")
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Column(modifier = Modifier.padding(innerPadding)) {
|
||||
CheckPermission()
|
||||
}
|
||||
if (!routeModel.isNavigating()) {
|
||||
tilt = 60.0
|
||||
vieModel.loadRoute(
|
||||
applicationContext,
|
||||
lastLocation,
|
||||
Constants.home2Location
|
||||
)
|
||||
simulationText = "Stop Simulation"
|
||||
} else {
|
||||
tilt = 0.0
|
||||
routeModel.stopNavigation()
|
||||
routeData.value = ""
|
||||
println("stopNavigation")
|
||||
simulationText = "Start Simulation"
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Column(modifier = Modifier.Companion.padding(innerPadding)) {
|
||||
//CheckPermission()
|
||||
Map()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,7 +230,7 @@ class MainActivity : ComponentActivity() {
|
||||
listOf(
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
Manifest.permission.READ_CONTACTS,
|
||||
//Manifest.permission.READ_CONTACTS,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -272,11 +296,7 @@ class MainActivity : ComponentActivity() {
|
||||
)
|
||||
val userLocationState = rememberUserLocationState(locationProvider)
|
||||
val locationState = locationProvider.location.collectAsState()
|
||||
if (!simulate) {
|
||||
updateLocation(locationState.value)
|
||||
} else {
|
||||
simulate()
|
||||
}
|
||||
updateLocation(locationState.value)
|
||||
if (locationState.value != null && lastLocation.latitude == 0.0) {
|
||||
lastLocation.latitude = locationState.value?.position!!.latitude
|
||||
lastLocation.longitude = locationState.value?.position!!.longitude
|
||||
@@ -300,29 +320,33 @@ class MainActivity : ComponentActivity() {
|
||||
baseStyle = BaseStyle.Uri(Constants.STYLE),
|
||||
) {
|
||||
getBaseSource(id = "openmaptiles")?.let { tiles ->
|
||||
if (!getBooleanKeyValue(context = applicationContext, SHOW_THREED_BUILDING)) {
|
||||
if (!NavigationUtils.getBooleanKeyValue(
|
||||
context = applicationContext,
|
||||
Constants.SHOW_THREED_BUILDING
|
||||
)
|
||||
) {
|
||||
BuildingLayer(tiles)
|
||||
}
|
||||
RouteLayer(route, "")
|
||||
}
|
||||
val location = Location(LocationManager.GPS_PROVIDER)
|
||||
if (userLocationState.location != null) {
|
||||
val location = Location(LocationManager.GPS_PROVIDER)
|
||||
location.longitude = userLocationState.location!!.position.longitude
|
||||
location.latitude = userLocationState.location!!.position.latitude
|
||||
PuckState(cameraState, userLocationState,)
|
||||
PuckState(cameraState, userLocationState)
|
||||
}
|
||||
}
|
||||
|
||||
LocationTrackingEffect(
|
||||
locationState = userLocationState,
|
||||
) {
|
||||
//cameraState.updateFromLocation()
|
||||
cameraState.animateTo(
|
||||
finalPosition = CameraPosition(
|
||||
bearing = position!!.bearing,
|
||||
zoom = position!!.zoom,
|
||||
target = position!!.target,
|
||||
tilt = tilt
|
||||
tilt = tilt,
|
||||
padding = PaddingValues(start = 0.dp, top = 350.dp)
|
||||
),
|
||||
duration = 1.seconds
|
||||
)
|
||||
@@ -345,87 +369,108 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun updateTestLocation(location: Location) {
|
||||
var snapedLocation = location
|
||||
var bearing: Double
|
||||
if (routeModel.isNavigating()) {
|
||||
snapedLocation = snapLocation(location, routeModel.route.maneuverLocations())
|
||||
routeModel.updateLocation(location)
|
||||
bearing = routeModel.currentStep().bearing
|
||||
instruction.postValue(routeModel.currentStep())
|
||||
} else {
|
||||
bearing = cameraPosition.value!!.bearing
|
||||
}
|
||||
val zoom = calculateZoom(snapedLocation.speed.toDouble())
|
||||
cameraPosition.postValue(
|
||||
cameraPosition.value!!.copy(
|
||||
bearing = bearing,
|
||||
zoom = zoom,
|
||||
target = Position(snapedLocation.longitude, snapedLocation.latitude)
|
||||
),
|
||||
private fun checkLocationPermissions() {
|
||||
val permissions = mutableListOf(
|
||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||
)
|
||||
if (MainApplication.useContacts) {
|
||||
permissions.add(Manifest.permission.READ_CONTACTS)
|
||||
}
|
||||
|
||||
val permissionsToRequest = permissions.filter {
|
||||
ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
if (permissionsToRequest.isNotEmpty()) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
permissionsToRequest.toTypedArray(),
|
||||
LOCATION_PERMISSION_REQUEST_CODE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun simulate() {
|
||||
if (routeModel.isNavigating() && locationIndex < routeModel.route.waypoints.size) {
|
||||
coroutineScope.launch {
|
||||
delay(
|
||||
100
|
||||
fun checkPermissionForLocation(): Boolean {
|
||||
val permissions = mutableListOf(
|
||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||
)
|
||||
|
||||
if (MainApplication.useContacts) {
|
||||
permissions.add(Manifest.permission.READ_CONTACTS)
|
||||
}
|
||||
val permissionsToRequest = permissions.filter {
|
||||
ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
return permissionsToRequest.isEmpty()
|
||||
}
|
||||
|
||||
fun checkPermissionForContact(): Boolean {
|
||||
val permissions = arrayOf(
|
||||
Manifest.permission.READ_CONTACTS,
|
||||
)
|
||||
val permissionsToRequest = permissions.filter {
|
||||
ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
return permissionsToRequest.isEmpty()
|
||||
}
|
||||
|
||||
private fun checkContactsPermissions() {
|
||||
val permissions = arrayOf(
|
||||
Manifest.permission.READ_CONTACTS,
|
||||
)
|
||||
|
||||
val permissionsToRequest = permissions.filter {
|
||||
ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
if (permissionsToRequest.isNotEmpty()) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
permissionsToRequest.toTypedArray(),
|
||||
CONTACTS_PERMISSION_REQUEST_CODE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkMockLocationEnabled() {
|
||||
try {
|
||||
// Check if mock location is enabled for this app
|
||||
val appOpsManager =
|
||||
getSystemService(APP_OPS_SERVICE) as AppOpsManager
|
||||
val mode =
|
||||
appOpsManager.unsafeCheckOp(
|
||||
AppOpsManager.OPSTR_MOCK_LOCATION,
|
||||
Process.myUid(),
|
||||
packageName
|
||||
)
|
||||
val loc = routeModel.route.waypoints[locationIndex]
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun simulate() = GlobalScope.async {
|
||||
for ((i, loc) in routeModel.route.waypoints.withIndex()) {
|
||||
if (routeModel.isNavigating()) {
|
||||
lastLocation.longitude = loc[0]
|
||||
lastLocation.latitude = loc[1]
|
||||
updateTestLocation(lastLocation)
|
||||
Thread.sleep(1_000)
|
||||
locationIndex++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PlaceList(viewModel: ViewModel = koinViewModel()) {
|
||||
var categories: List<Category>
|
||||
val places = viewModel.places.observeAsState().value ?: return
|
||||
|
||||
val countries = places.groupBy { it.category }.map {
|
||||
Category(id = Constants.RECENT, name = it.key!!)
|
||||
}
|
||||
categories = countries
|
||||
val context = LocalContext.current
|
||||
LazyColumn {
|
||||
items(categories.size) {
|
||||
val place = categories[it]
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
.border(
|
||||
2.dp,
|
||||
color = MaterialTheme.colorScheme.outline,
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
//.clickable {
|
||||
//context.startActivity(place.toIntent(Intent.ACTION_VIEW))
|
||||
//}
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = place.name,
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
Text(
|
||||
text = place.name,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
|
||||
if (i == 20) {
|
||||
mock.setMockLocation(loc[1] + 0.03, loc[0])
|
||||
} else {
|
||||
mock.setMockLocation(loc[1], loc[0])
|
||||
}
|
||||
delay(1000L) //
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.kouros.navigation
|
||||
package com.kouros.navigation.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -26,6 +26,7 @@ import com.google.accompanist.permissions.MultiplePermissionsState
|
||||
import com.google.accompanist.permissions.PermissionState
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
|
||||
|
||||
/**
|
||||
* Simple screen that manages the location permission state
|
||||
*/
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.kouros.navigation
|
||||
package com.kouros.navigation.ui
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
Reference in New Issue
Block a user