diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96f95f2..9d3b54c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,7 +6,7 @@ - + diff --git a/app/src/main/java/com/kouros/navigation/MainApplication.kt b/app/src/main/java/com/kouros/navigation/MainApplication.kt index 5160285..2fbae36 100644 --- a/app/src/main/java/com/kouros/navigation/MainApplication.kt +++ b/app/src/main/java/com/kouros/navigation/MainApplication.kt @@ -29,7 +29,7 @@ class MainApplication : Application() { super.onCreate() ObjectBox.init(this); appContext = applicationContext - setIntKeyValue(appContext!!, RouteEngine.OSRM.ordinal, ROUTE_ENGINE) + setIntKeyValue(appContext!!, RouteEngine.VALHALLA.ordinal, ROUTE_ENGINE) navigationViewModel = getRouteEngine(appContext!!) startKoin { androidLogger(Level.DEBUG) @@ -43,8 +43,6 @@ class MainApplication : Application() { var appContext: Context? = null private set - var useContacts = false - lateinit var navigationViewModel : ViewModel } } \ No newline at end of file diff --git a/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt b/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt index 5ce4c79..2befa83 100644 --- a/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt +++ b/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt @@ -39,20 +39,20 @@ import androidx.lifecycle.Observer import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationServices import com.kouros.navigation.MainApplication.Companion.navigationViewModel +import com.kouros.navigation.data.Constants import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE import com.kouros.navigation.data.Constants.homeLocation 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.ui.theme.NavigationTheme +import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue import com.kouros.navigation.utils.bearing import com.kouros.navigation.utils.calculateZoom import com.kouros.navigation.utils.location import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.maplibre.compose.camera.CameraPosition @@ -60,6 +60,7 @@ import org.maplibre.compose.location.DesiredAccuracy import org.maplibre.compose.location.Location import org.maplibre.compose.location.rememberDefaultLocationProvider import org.maplibre.compose.location.rememberUserLocationState +import org.maplibre.compose.style.BaseStyle import org.maplibre.spatialk.geojson.Position import kotlin.time.Duration.Companion.seconds @@ -101,6 +102,8 @@ class MainActivity : ComponentActivity() { private var overpass = false + lateinit var baseStyle : BaseStyle.Json + init { navigationViewModel.route.observe(this, observer) } @@ -108,6 +111,8 @@ class MainActivity : ComponentActivity() { @RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION]) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val darkModeSettings = getIntKeyValue(applicationContext, Constants.DARK_MODE_SETTINGS) + baseStyle = BaseStyleModel().readStyle(applicationContext, darkModeSettings, false) if (useMock) { checkMockLocationEnabled() } @@ -196,7 +201,8 @@ class MainActivity : ComponentActivity() { step, cameraPosition, routeData, - tilt + tilt, + baseStyle ) } } diff --git a/app/src/main/java/com/kouros/navigation/ui/MapView.kt b/app/src/main/java/com/kouros/navigation/ui/MapView.kt index 58bf246..893f549 100644 --- a/app/src/main/java/com/kouros/navigation/ui/MapView.kt +++ b/app/src/main/java/com/kouros/navigation/ui/MapView.kt @@ -7,17 +7,14 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.runtime.Composable 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.unit.dp import androidx.lifecycle.MutableLiveData import androidx.window.layout.WindowMetricsCalculator import com.kouros.navigation.car.ViewStyle -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.data.Constants +import com.kouros.navigation.car.map.rememberBaseStyle import com.kouros.navigation.data.StepData import org.maplibre.compose.camera.CameraPosition import org.maplibre.compose.camera.rememberCameraState @@ -34,10 +31,12 @@ fun MapView( step: StepData?, cameraPosition: MutableLiveData, routeData: MutableLiveData, - tilt: Double + tilt: Double, + baseStyle: BaseStyle.Json, ) { - val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(applicationContext) + val metrics = + WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(applicationContext) val width = metrics.bounds.width() val height = metrics.bounds.height() val paddingValues = PaddingValues(start = 0.dp, top = 350.dp) @@ -55,21 +54,19 @@ fun MapView( zoom = 15.0, ) ) - val baseStyle = remember { - mutableStateOf(BaseStyle.Uri(Constants.STYLE)) - } - DarkMode(applicationContext, baseStyle) + + val rememberBaseStyle = rememberBaseStyle( baseStyle) Column { NavigationInfo(step) Box(contentAlignment = Alignment.Center) { MapLibre( applicationContext, cameraState, - baseStyle, + rememberBaseStyle, route, ViewStyle.VIEW ) - LocationTrackingEffect( + LocationTrackingEffect( locationState = userLocationState, ) { cameraState.animateTo( diff --git a/common/car/build.gradle.kts b/common/car/build.gradle.kts index 518536a..655b4c7 100644 --- a/common/car/build.gradle.kts +++ b/common/car/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.ui) implementation(libs.maplibre.compose) + implementation(libs.androidx.app.projected) //implementation(libs.maplibre.composeMaterial3) implementation(project(":common:data")) diff --git a/common/car/src/main/AndroidManifest.xml b/common/car/src/main/AndroidManifest.xml index 64c3007..feea63c 100644 --- a/common/car/src/main/AndroidManifest.xml +++ b/common/car/src/main/AndroidManifest.xml @@ -32,6 +32,8 @@ + + diff --git a/common/car/src/main/java/com/kouros/navigation/car/NavigationCarAppService.kt b/common/car/src/main/java/com/kouros/navigation/car/NavigationCarAppService.kt index eb036c3..ae9ce95 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/NavigationCarAppService.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/NavigationCarAppService.kt @@ -38,9 +38,4 @@ class NavigationCarAppService : CarAppService() { } } -public interface LocationCallback { - - fun onLocationChanged(location: Location) { - } -} diff --git a/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt b/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt index 283bc2d..fea4e74 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt @@ -5,6 +5,7 @@ import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.content.pm.PackageManager +import android.content.res.Configuration import android.location.Location import android.location.LocationManager import android.util.Log @@ -12,6 +13,13 @@ import androidx.car.app.CarContext import androidx.car.app.Screen import androidx.car.app.ScreenManager import androidx.car.app.Session +import androidx.car.app.hardware.CarHardwareManager +import androidx.car.app.hardware.common.CarValue +import androidx.car.app.hardware.common.OnCarDataAvailableListener +import androidx.car.app.hardware.info.CarHardwareLocation +import androidx.car.app.hardware.info.CarSensors +import androidx.car.app.hardware.info.Speed +import androidx.compose.foundation.isSystemInDarkTheme import androidx.core.location.LocationListenerCompat import androidx.core.net.toUri import androidx.lifecycle.DefaultLifecycleObserver @@ -22,17 +30,16 @@ import com.kouros.navigation.car.navigation.RouteCarModel import com.kouros.navigation.car.screen.NavigationScreen import com.kouros.navigation.car.screen.RequestPermissionScreen import com.kouros.navigation.car.screen.SearchScreen +import com.kouros.navigation.data.Constants import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION -import com.kouros.navigation.data.Constants.ROUTE_ENGINE import com.kouros.navigation.data.Constants.TAG -import com.kouros.navigation.data.NavigationRepository -import com.kouros.navigation.data.osrm.OsrmRepository -import com.kouros.navigation.data.valhalla.ValhallaRepository +import com.kouros.navigation.model.BaseStyleModel import com.kouros.navigation.model.ViewModel import com.kouros.navigation.utils.GeoUtils.snapLocation import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue import com.kouros.navigation.utils.NavigationUtils.getRouteEngine +import org.maplibre.compose.style.BaseStyle class NavigationSession : Session(), NavigationScreen.Listener { @@ -66,6 +73,10 @@ class NavigationSession : Session(), NavigationScreen.Listener { } override fun onDestroy(owner: LifecycleOwner) { + val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors + val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo + carSensors.removeCarHardwareLocationListener(locationListener) + carInfo.removeSpeedListener(speedListener) Log.i(TAG, "In onDestroy()") val locationManager = carContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager @@ -73,26 +84,47 @@ class NavigationSession : Session(), NavigationScreen.Listener { } } - lateinit var navigationViewModel : ViewModel + lateinit var navigationViewModel: ViewModel + lateinit var baseStyle: BaseStyle.Json + + val locationListener: OnCarDataAvailableListener = + OnCarDataAvailableListener { data -> + if (data.location.status == CarValue.STATUS_SUCCESS) { + val location = data.location.value + surfaceRenderer.updateCarLocation(location!!) + } + } + + val speedListener = OnCarDataAvailableListener { data -> + if (data.displaySpeedMetersPerSecond.status == CarValue.STATUS_SUCCESS) { + val speed = data.displaySpeedMetersPerSecond.value + surfaceRenderer.updateCarSpeed(speed!!) + } + } init { val lifecycle: Lifecycle = lifecycle lifecycle.addObserver(mLifeCycleObserver) } - override fun onCreateScreen(intent: Intent): Screen { navigationViewModel = getRouteEngine(carContext) routeModel = RouteCarModel() - surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel) + val darkMode = getIntKeyValue(carContext, Constants.DARK_MODE_SETTINGS) + baseStyle = BaseStyleModel().readStyle(carContext, darkMode, carContext.isDarkMode) - navigationScreen = NavigationScreen(carContext, surfaceRenderer, routeModel, this, navigationViewModel) + surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel, baseStyle) + + navigationScreen = + NavigationScreen(carContext, surfaceRenderer, routeModel, this, navigationViewModel) if (carContext.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) - == PackageManager.PERMISSION_GRANTED && !useContacts + == PackageManager.PERMISSION_GRANTED + && carContext.checkSelfPermission("com.google.android.gms.permission.CAR_SPEED") == PackageManager.PERMISSION_GRANTED + && !useContacts || (useContacts && carContext.checkSelfPermission(Manifest.permission.READ_CONTACTS) - == PackageManager.PERMISSION_GRANTED) + == PackageManager.PERMISSION_GRANTED) ) { requestLocationUpdates() } else { @@ -111,9 +143,23 @@ class NavigationSession : Session(), NavigationScreen.Listener { } ) } + + addSensors() + return navigationScreen } + fun addSensors() { + val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors + val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo + carSensors.addCarHardwareLocationListener( + CarSensors.UPDATE_RATE_NORMAL, + carContext.mainExecutor, + locationListener + ) + carInfo.addSpeedListener(carContext.mainExecutor, speedListener) + } + override fun onNewIntent(intent: Intent) { val screenManager = carContext.getCarService(ScreenManager::class.java) if ((CarContext.ACTION_NAVIGATE == intent.action)) { @@ -151,6 +197,11 @@ class NavigationSession : Session(), NavigationScreen.Listener { } } + override fun onCarConfigurationChanged(newConfiguration: Configuration) { + println("Configuration: ${newConfiguration.isNightModeActive}") + super.onCarConfigurationChanged(newConfiguration) + } + @SuppressLint("MissingPermission") fun requestLocationUpdates() { val locationManager = diff --git a/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt b/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt index f9dd26b..df2453d 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt @@ -18,8 +18,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect 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.platform.ComposeView import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.Lifecycle @@ -27,13 +25,12 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner -import com.kouros.navigation.car.map.DarkMode import com.kouros.navigation.car.map.DrawNavigationImages import com.kouros.navigation.car.map.MapLibre import com.kouros.navigation.car.map.cameraState import com.kouros.navigation.car.map.getPaddingValues +import com.kouros.navigation.car.map.rememberBaseStyle import com.kouros.navigation.car.navigation.RouteCarModel -import com.kouros.navigation.data.Constants import com.kouros.navigation.data.Constants.homeLocation import com.kouros.navigation.data.ObjectBox import com.kouros.navigation.model.RouteModel @@ -51,7 +48,8 @@ import org.maplibre.spatialk.geojson.Position class SurfaceRenderer( private var carContext: CarContext, lifecycle: Lifecycle, - private var routeModel: RouteCarModel + private var routeModel: RouteCarModel, + private var baseStyle: BaseStyle.Json ) : DefaultLifecycleObserver { var lastLocation = location(0.0, 0.0) @@ -174,12 +172,8 @@ class SurfaceRenderer( val speedCameras: String? by speedCamerasData.observeAsState() val paddingValues = getPaddingValues(height, viewStyle) val cameraState = cameraState(paddingValues, position, tilt) - - val baseStyle = remember { - mutableStateOf(BaseStyle.Uri(Constants.STYLE)) - } - DarkMode(carContext, baseStyle) - MapLibre(carContext, cameraState, baseStyle, route, viewStyle, speedCameras) + val rememberBaseStyle = rememberBaseStyle(baseStyle) + MapLibre(carContext, cameraState, rememberBaseStyle, route, viewStyle, speedCameras) ShowPosition(cameraState, position, paddingValues) } @@ -262,7 +256,7 @@ class SurfaceRenderer( ) lastBearing = cameraPosition.value!!.bearing lastLocation = location - speed.value = location.speed + //speed.value = location.speed if (!countDownTimerActive) { countDownTimerActive = true val mainThreadHandler = Handler(carContext.mainLooper) @@ -331,6 +325,14 @@ class SurfaceRenderer( ) } + fun updateCarLocation(location: Location) { + // updateLocation(location) + } + + fun updateCarSpeed(newSpeed: Float) { + speed.value = newSpeed + } + fun setCategoryLocation(location: Location, category: String) { viewStyle = ViewStyle.AMENITY_VIEW cameraPosition.postValue( diff --git a/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt b/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt index f0284fd..1a30125 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt @@ -1,10 +1,8 @@ package com.kouros.navigation.car.map import android.content.Context -import android.content.res.AssetFileDescriptor import android.location.Location import androidx.compose.foundation.Canvas -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize @@ -13,13 +11,17 @@ import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.scale import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.scale +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.drawText @@ -88,20 +90,19 @@ fun cameraState( fun MapLibre( context: Context, cameraState: CameraState, - baseStyle: MutableState, + baseStyle: BaseStyle.Json, route: String?, viewStyle: ViewStyle, speedCameras: String? = "" ) { - //val baseStyle = BaseStyleModel().readStyle(context) MaplibreMap( options = MapOptions( ornamentOptions = OrnamentOptions(isScaleBarEnabled = false) ), cameraState = cameraState, - baseStyle = baseStyle.value + baseStyle = baseStyle ) { getBaseSource(id = "openmaptiles")?.let { tiles -> if (!getBooleanKeyValue(context = context, SHOW_THREED_BUILDING)) { @@ -372,23 +373,14 @@ private fun MaxSpeed( } @Composable -fun DarkMode(context: Context, baseStyle: MutableState) { - val darkMode = getIntKeyValue(context, Constants.DARK_MODE_SETTINGS) - - if (darkMode == 0) { - baseStyle.value = BaseStyle.Uri(Constants.STYLE) - } - if (darkMode == 1) { - baseStyle.value = BaseStyle.Uri(Constants.STYLE_DARK) - } - if (darkMode == 2) { - baseStyle.value = - (if (isSystemInDarkTheme()) BaseStyle.Uri(Constants.STYLE_DARK) else BaseStyle.Uri( - Constants.STYLE - )) +fun rememberBaseStyle( baseStyle : BaseStyle.Json): BaseStyle.Json { + val rememberBaseStyle by remember() { + mutableStateOf(baseStyle) } + return rememberBaseStyle } + fun getPaddingValues(height: Int, viewStyle: ViewStyle): PaddingValues { return when (viewStyle) { ViewStyle.VIEW, ViewStyle.PAN_VIEW -> PaddingValues( diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/RequestPermissionScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/RequestPermissionScreen.kt index 79129c0..78d444c 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/RequestPermissionScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/RequestPermissionScreen.kt @@ -26,9 +26,10 @@ class RequestPermissionScreen( override fun onGetTemplate(): Template { val permissions: MutableList = ArrayList() permissions.add(permission.ACCESS_FINE_LOCATION) + permissions.add("com.google.android.gms.permission.CAR_SPEED") //permissions.add(permission.READ_CONTACTS) - val message = "This app needs access to location in order to show the map around you" + val message = "This app needs access to location and to car speed" val listener: OnClickListener = ParkedOnlyOnClickListener.create { carContext.requestPermissions( diff --git a/common/data/src/main/java/com/kouros/navigation/model/BaseStyleModel.kt b/common/data/src/main/java/com/kouros/navigation/model/BaseStyleModel.kt index 1c9fa68..6dcc131 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/BaseStyleModel.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/BaseStyleModel.kt @@ -1,13 +1,31 @@ package com.kouros.navigation.model import android.content.Context +import android.content.res.Configuration +import androidx.compose.foundation.isSystemInDarkTheme import com.kouros.data.R import org.maplibre.compose.style.BaseStyle class BaseStyleModel { - fun readStyle(context: Context): BaseStyle.Json { - println("Read Style") - val liberty = context.resources.openRawResource(R.raw.liberty) + + fun isDarkTheme(context: Context): Boolean { + return context.resources.configuration.uiMode and + Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES + } + + fun readStyle(context: Context, darkModeSettings: Int, isCarDarkMode: Boolean): BaseStyle.Json { + println("BaseStyle ${isDarkTheme(context)}") + val liberty = when(darkModeSettings) { + 0 -> context.resources.openRawResource(R.raw.liberty) + 1 -> context.resources.openRawResource(R.raw.liberty_night) + else -> { + if (isDarkTheme(context) || isCarDarkMode) { + context.resources.openRawResource(R.raw.liberty_night) + } else { + context.resources.openRawResource(R.raw.liberty) + } + } + } val libertyString = liberty.bufferedReader().use { it.readText() } val baseStyle = BaseStyle.Json(libertyString) return baseStyle diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a790027..ffc0e93 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -42,6 +42,7 @@ foundationLayout = "1.10.0" [libraries] android-sdk-turf = { module = "org.maplibre.gl:android-sdk-turf", version.ref = "androidSdkTurf" } +androidx-app-projected = { module = "androidx.car.app:app-projected" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" } junit = { group = "junit", name = "junit", version.ref = "junit" }