Refactoring

This commit is contained in:
Dimitris
2025-12-12 15:32:15 +01:00
parent a02673af36
commit 72872cddeb
21 changed files with 588 additions and 349 deletions

View File

@@ -14,8 +14,8 @@ android {
applicationId = "com.kouros.navigation"
minSdk = 33
targetSdk = 36
versionCode = 7
versionName = "0.1.3.7"
versionCode = 8
versionName = "0.1.3.8"
base.archivesName = "navi-$versionName"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -15,28 +15,36 @@ import androidx.annotation.RequiresPermission
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.BottomSheetScaffold
import androidx.compose.material3.BottomSheetScaffoldState
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableDoubleStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.kouros.data.R
import com.kouros.navigation.data.Constants.homeLocation
import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.StepData
@@ -74,6 +82,10 @@ class MainActivity : ComponentActivity() {
MutableLiveData<StepData>()
}
val nextStepData: MutableLiveData<StepData> by lazy {
MutableLiveData<StepData>()
}
var lastLocation = location(0.0, 0.0)
val observer = Observer<String> { newRoute ->
@@ -147,8 +159,9 @@ class MainActivity : ComponentActivity() {
fun Content() {
val scaffoldState = rememberBottomSheetScaffoldState()
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
val sheetPeekHeightState = remember { mutableStateOf(256.dp) }
val locationProvider = rememberDefaultLocationProvider(
updateInterval = 0.5.seconds,
desiredAccuracy = DesiredAccuracy.Highest
@@ -161,24 +174,27 @@ class MainActivity : ComponentActivity() {
latitude = locationState.value!!.position.latitude
}
val step: StepData? by stepData.observeAsState()
val nextStep: StepData? by nextStepData.observeAsState()
fun openSheet() {
scope.launch { scaffoldState.bottomSheetState.expand() }
}
fun closeSheet() {
scope.launch { scaffoldState.bottomSheetState.partialExpand() }
scope.launch {
scaffoldState.bottomSheetState.partialExpand()
sheetPeekHeightState.value = 128.dp
}
}
NavigationTheme {
NavigationTheme() {
BottomSheetScaffold(
snackbarHost = {
SnackbarHost(hostState = snackbarHostState)
},
scaffoldState = scaffoldState,
sheetPeekHeight = 128.dp,
sheetPeekHeight = sheetPeekHeightState.value,
sheetContent = {
SheetContent(latitude, step) { closeSheet() }
SheetContent(latitude, step, nextStep) { closeSheet() }
},
) { innerPadding ->
Box(
@@ -187,19 +203,31 @@ class MainActivity : ComponentActivity() {
.padding(innerPadding),
contentAlignment = Alignment.Center,
) {
MapView(applicationContext,userLocationState, step, cameraPosition, routeData, tilt)
MapView(
applicationContext,
userLocationState,
step,
cameraPosition,
routeData,
tilt
)
}
}
}
}
@Composable
fun SheetContent(locationState: Double, step: StepData?, closeSheet: () -> Unit) {
fun SheetContent(
locationState: Double,
step: StepData?,
nextStep: StepData?,
closeSheet: () -> Unit
) {
if (!routeModel.isNavigating()) {
SearchSheet(applicationContext, viewModel, lastLocation) { closeSheet() }
} else {
NavigationSheet(
routeModel, step!!,
routeModel, step!!, nextStep!!,
{ stopNavigation { closeSheet() } },
{ simulateNavigation() }
)
@@ -213,11 +241,16 @@ class MainActivity : ComponentActivity() {
&& lastLocation.latitude != location.position.latitude
&& lastLocation.longitude != location.position.longitude
) {
if (routeModel.isNavigating()) {
routeModel.updateLocation(lastLocation)
stepData.value = routeModel.currentStep()
}
val currentLocation = location(location.position.longitude, location.position.latitude)
with(routeModel) {
if (isNavigating()) {
updateLocation(currentLocation)
stepData.value = currentStep()
if (route.currentManeuverIndex + 1 <= route.maneuvers.size) {
nextStepData.value = nextStep()
}
}
}
val bearing = bearing(lastLocation, currentLocation, cameraPosition.value!!.bearing)
val zoom = calculateZoom(location.speed)
cameraPosition.postValue(
@@ -252,7 +285,7 @@ class MainActivity : ComponentActivity() {
val appOpsManager =
getSystemService(APP_OPS_SERVICE) as AppOpsManager
val mode =
appOpsManager.unsafeCheckOp(
appOpsManager.checkOp(
AppOpsManager.OPSTR_MOCK_LOCATION,
Process.myUid(),
packageName
@@ -275,7 +308,7 @@ class MainActivity : ComponentActivity() {
for ((_, loc) in routeModel.route.waypoints.withIndex()) {
if (routeModel.isNavigating()) {
mock.setMockLocation(loc[1], loc[0])
delay(1000L) //
delay(500L) //
}
}
}

View File

@@ -1,30 +1,43 @@
package com.kouros.navigation.ui
import android.R.attr.x
import android.R.attr.y
import android.content.Context
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.absoluteOffset
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.lifecycle.MutableLiveData
import androidx.window.layout.WindowMetricsCalculator
import com.kouros.navigation.car.map.BuildingLayer
import com.kouros.navigation.car.map.DarkMode
import com.kouros.navigation.car.map.MapLibre
import com.kouros.navigation.car.map.NavigationImage
import com.kouros.navigation.car.map.RouteLayer
import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.StepData
import com.kouros.navigation.utils.NavigationUtils
import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.camera.CameraState
import org.maplibre.compose.camera.rememberCameraState
import org.maplibre.compose.location.LocationTrackingEffect
import org.maplibre.compose.location.UserLocationState
import org.maplibre.compose.map.MapOptions
import org.maplibre.compose.map.MaplibreMap
import org.maplibre.compose.map.OrnamentOptions
import org.maplibre.compose.sources.getBaseSource
import org.maplibre.compose.style.BaseStyle
import org.maplibre.spatialk.geojson.Position
import kotlin.time.Duration.Companion.seconds
@@ -57,32 +70,14 @@ fun MapView(
zoom = 15.0,
)
)
val baseStyle = remember {
mutableStateOf(BaseStyle.Uri(Constants.STYLE))
}
DarkMode(applicationContext, baseStyle)
Column {
NavigationInfo(step)
Box(contentAlignment = Alignment.Center) {
MaplibreMap(
options = MapOptions(
ornamentOptions =
OrnamentOptions(isScaleBarEnabled = false)
),
cameraState = cameraState,
baseStyle = BaseStyle.Uri(Constants.STYLE),
) {
getBaseSource(id = "openmaptiles")?.let { tiles ->
if (!NavigationUtils.getBooleanKeyValue(
context = applicationContext,
Constants.SHOW_THREED_BUILDING
)
) {
BuildingLayer(tiles)
}
RouteLayer(route, "", position!!.zoom)
}
if (userLocationState.location != null) {
///PuckState(cameraState, userLocationState)
}
}
MapLibre(applicationContext, cameraState, baseStyle, route, "", position)
LocationTrackingEffect(
locationState = userLocationState,
) {
@@ -97,7 +92,8 @@ fun MapView(
duration = 1.seconds
)
}
NavigationImage(paddingValues, width, height / 6, "")
NavigationImage(paddingValues, width, height / 6)
}
}
}

View File

@@ -25,12 +25,13 @@ import com.kouros.navigation.utils.round
fun NavigationSheet(
routeModel: RouteModel,
step: StepData,
nextStep: StepData,
stopNavigation: () -> Unit,
simulateNavigation: () -> Unit,
) {
val distance = step.leftDistance.round(1)
Column {
FlowRow(horizontalArrangement= Arrangement.SpaceEvenly) {
FlowRow(horizontalArrangement = Arrangement.SpaceEvenly) {
Text(formatDateTime(step.arrivalTime), fontSize = 22.sp)
Spacer(Modifier.size(30.dp))
Text("$distance km", fontSize = 22.sp)
@@ -47,7 +48,9 @@ fun NavigationSheet(
modifier = Modifier.size(24.dp, 24.dp),
)
}
Spacer(Modifier.size(30.dp))
}
Spacer(Modifier.size(30.dp))
if (!routeModel.isNavigating()) {
Button(onClick = {
simulateNavigation()
}) {

View File

@@ -57,7 +57,7 @@ fun PermissionScreen(
errorText = if (rejectedPermissions.none { it in requiredPermissions }) {
""
} else {
"${rejectedPermissions.joinToString()} required for the sample"
"${rejectedPermissions.joinToString()} required for the app"
}
}
val allRequiredPermissionsGranted =
@@ -128,7 +128,7 @@ private fun PermissionScreen(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Sample requires permission/s:",
text = "Navigation requires permission/s:",
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(16.dp),
)

View File

@@ -14,6 +14,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
@@ -56,23 +57,25 @@ fun SearchSheet(
if (search.value != null) {
searchResults.addAll(search.value!!)
}
Home(applicationContext, viewModel, location, closeSheet = { closeSheet() })
if (searchResults.isNotEmpty()) {
val textFieldState = rememberTextFieldState()
val items = listOf(searchResults)
if (items.isNotEmpty()) {
SearchBar(
textFieldState = textFieldState,
searchPlaces = recentPlaces.value!!,
searchPlaces = recentPlaces.value!!,
searchResults = searchResults,
viewModel = viewModel,
context = applicationContext,
location = location,
closeSheet = {closeSheet()}
closeSheet = { closeSheet() }
)
}
}
if (recentPlaces.value != null) {
println("Recent Places ${recentPlaces.value}")
val textFieldState = rememberTextFieldState()
val items = listOf(recentPlaces)
if (items.isNotEmpty()) {
@@ -83,10 +86,44 @@ fun SearchSheet(
viewModel = viewModel,
context = applicationContext,
location = location,
closeSheet = {closeSheet()}
closeSheet = { closeSheet() }
)
}
}
}
@Composable
fun Home(
applicationContext: Context,
viewModel: ViewModel,
location: Location,
closeSheet: () -> Unit
) {
Row(horizontalArrangement = Arrangement.SpaceBetween) {
Button(onClick = {
val places = viewModel.loadRecentPlace()
val toLocation = location(places.first()!!.longitude, places.first()!!.latitude)
viewModel.loadRoute(applicationContext, location, toLocation)
closeSheet()
}) {
Icon(
painter = painterResource(id = com.google.android.gms.base.R.drawable.common_full_open_on_phone),
"Home",
modifier = Modifier.size(24.dp, 24.dp),
)
Text("Home")
}
Button(onClick = {
}) {
Icon(
painter = painterResource(id = R.drawable.ic_favorite_white_24dp),
"Work",
modifier = Modifier.size(24.dp, 24.dp),
)
Text("Arbeit")
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@@ -176,6 +213,16 @@ private fun SearchPlaces(
headlineContent = { Text("${place.address.road} ${place.address.postcode}") },
modifier = Modifier
.clickable {
val pl = Place(
name = place.name,
longitude = place.lon.toDouble(),
latitude = place.lat.toDouble(),
postalCode = place.address.postcode,
city = place.address.city,
street = place.address.road
)
viewModel.saveRecent(pl)
println("Save $pl")
val toLocation =
location(place.lon.toDouble(), place.lat.toDouble())
viewModel.loadRoute(context, location, toLocation)
@@ -212,7 +259,7 @@ private fun RecentPlaces(
modifier = Modifier.size(24.dp, 24.dp),
)
ListItem(
headlineContent = { Text("${place.street!!} ${place.postalCode}") },
headlineContent = { Text("${place.name} ${place.postalCode}") },
modifier = Modifier
.clickable {
val toLocation = location(place.longitude, place.latitude)