Launcher Icons, NotificationService
@@ -1,10 +1,14 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import java.util.Properties
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
|
||||
val properties = Properties().apply {
|
||||
load(File("signing.properties").reader())
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.kouros.navigation"
|
||||
compileSdk = 36
|
||||
@@ -13,8 +17,8 @@ android {
|
||||
applicationId = "com.kouros.navigation"
|
||||
minSdk = 33
|
||||
targetSdk = 36
|
||||
versionCode = 76
|
||||
versionName = "0.2.0.76"
|
||||
versionCode = 82
|
||||
versionName = "0.2.0.82"
|
||||
base.archivesName = "navi-$versionName"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@@ -22,23 +26,22 @@ android {
|
||||
signingConfigs {
|
||||
getByName("debug") {
|
||||
keyAlias = "release"
|
||||
keyPassword = "zeta67#gAe3aN3"
|
||||
storeFile = file("/home/kouros/work/keystore/keystoreRelease")
|
||||
storePassword = "zeta67#gAe3aN3"
|
||||
keyPassword = properties.getProperty("keyPassword")
|
||||
storeFile = file(properties.getProperty("storeFile"))
|
||||
storePassword = properties.getProperty("storePassword")
|
||||
}
|
||||
create("release") {
|
||||
keyAlias = "release"
|
||||
keyPassword = "zeta67#gAe3aN3"
|
||||
storeFile = file("/home/kouros/work/keystore/keystoreRelease")
|
||||
storePassword = "zeta67#gAe3aN3"
|
||||
keyPassword = properties.getProperty("keyPassword")
|
||||
storeFile = file(properties.getProperty("storeFile"))
|
||||
storePassword = properties.getProperty("storePassword")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// Enables code-related app optimization.
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
isMinifyEnabled = false
|
||||
// Enables resource shrinking.
|
||||
isShrinkResources = false
|
||||
|
||||
proguardFiles(
|
||||
@@ -48,15 +51,20 @@ android {
|
||||
}
|
||||
}
|
||||
// Specifies one flavor dimension.
|
||||
flavorDimensions += "version"
|
||||
flavorDimensions += "store"
|
||||
productFlavors {
|
||||
create("play") {
|
||||
dimension = "store"
|
||||
applicationIdSuffix = ".play"
|
||||
versionNameSuffix = "-play"
|
||||
}
|
||||
create("demo") {
|
||||
dimension = "version"
|
||||
dimension = "store"
|
||||
applicationIdSuffix = ".demo"
|
||||
versionNameSuffix = "-demo"
|
||||
}
|
||||
create("full") {
|
||||
dimension = "version"
|
||||
dimension = "store"
|
||||
applicationIdSuffix = ".full"
|
||||
versionNameSuffix = "-full"
|
||||
}
|
||||
@@ -66,11 +74,20 @@ android {
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
packaging {
|
||||
resources {
|
||||
excludes +=
|
||||
setOf(
|
||||
"/META-INF/{AL2.0,LGPL2.1}",
|
||||
"/META-INF/*.version",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
buildConfig = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -95,7 +112,6 @@ dependencies {
|
||||
implementation(libs.androidx.compose.ui.graphics)
|
||||
implementation(libs.androidx.window)
|
||||
implementation(libs.androidx.compose.foundation.layout)
|
||||
implementation(libs.android.gpx.parser)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.androidx.compose.foundation.layout)
|
||||
@@ -107,4 +123,3 @@ dependencies {
|
||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 28 KiB |
@@ -1,20 +1,12 @@
|
||||
package com.kouros.navigation.model
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.MainApplication.Companion.navigationViewModel
|
||||
import com.kouros.navigation.utils.location
|
||||
import io.ticofab.androidgpxparser.parser.GPXParser
|
||||
import io.ticofab.androidgpxparser.parser.domain.Gpx
|
||||
import io.ticofab.androidgpxparser.parser.domain.TrackSegment
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.joda.time.DateTime
|
||||
import kotlin.collections.forEach
|
||||
|
||||
var simulationJob: Job? = null
|
||||
fun simulate(routeModel: RouteModel, mock: MockLocation) {
|
||||
@@ -91,43 +83,6 @@ fun testSingleUpdate(
|
||||
Thread.sleep(1_000)
|
||||
}
|
||||
|
||||
fun gpx(context: Context, mock: MockLocation) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
var lastLocation = location(0.0, 0.0)
|
||||
val parser = GPXParser()
|
||||
val resourceId: Int = context.resources
|
||||
.getIdentifier("vh", "raw", context.packageName)
|
||||
val input = context.resources.openRawResource(resourceId)
|
||||
val parsedGpx: Gpx? = parser.parse(input) // consider using a background thread
|
||||
parsedGpx?.let {
|
||||
val tracks = parsedGpx.tracks
|
||||
tracks.forEach { tr ->
|
||||
val segments: MutableList<TrackSegment?>? = tr.trackSegments
|
||||
segments!!.forEach { seg ->
|
||||
var lastTime = DateTime.now()
|
||||
seg!!.trackPoints.forEach { p ->
|
||||
val curLocation = location(p.longitude, p.latitude)
|
||||
val ext = p.extensions
|
||||
val speed: Double?
|
||||
if (ext != null) {
|
||||
speed = ext.speed
|
||||
mock.curSpeed = speed.toFloat()
|
||||
}
|
||||
val duration = p.time.millis - lastTime.millis
|
||||
val bearing = lastLocation.bearingTo(curLocation)
|
||||
mock.setMockLocation(p.latitude, p.longitude, bearing)
|
||||
if (duration > 0) {
|
||||
delay(duration / 5)
|
||||
}
|
||||
lastTime = p.time
|
||||
lastLocation = curLocation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class SimulationType {
|
||||
SIMULATE, TEST, GPX, TEST_SINGLE
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ 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.gpx
|
||||
import com.kouros.navigation.model.simulate
|
||||
import com.kouros.navigation.model.simulationJob
|
||||
import com.kouros.navigation.model.test
|
||||
@@ -112,11 +111,10 @@ class MainActivity : ComponentActivity() {
|
||||
when (type) {
|
||||
SimulationType.SIMULATE -> simulate(routeModel, mock)
|
||||
SimulationType.TEST -> test(applicationContext, routeModel)
|
||||
SimulationType.GPX -> gpx(
|
||||
context = applicationContext, mock
|
||||
)
|
||||
|
||||
|
||||
SimulationType.TEST_SINGLE -> testSingle(applicationContext, routeModel, mock)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ 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.MapLibre
|
||||
import com.kouros.navigation.car.map.NavigationImage
|
||||
import com.kouros.navigation.data.StepData
|
||||
import com.kouros.navigation.data.ViewStyle
|
||||
import com.kouros.navigation.ui.app.AppViewModel
|
||||
import com.kouros.navigation.ui.app.appViewModel
|
||||
import com.kouros.navigation.ui.navigation.NavigationInfo
|
||||
|
||||
@@ -48,5 +48,11 @@ fun AppNavGraph(mainActivity: MainActivity) {
|
||||
navController
|
||||
) { navController.popBackStack() }
|
||||
}
|
||||
composable("car_settings") {
|
||||
SettingsRoute(
|
||||
"car_settings",
|
||||
navController
|
||||
) { navController.popBackStack() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.kouros.navigation.ui.settings
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.model.SettingsViewModel
|
||||
import com.kouros.navigation.ui.components.RadioButtonSingleSelection
|
||||
import com.kouros.navigation.ui.components.SectionTitle
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CarScreen(viewModel: SettingsViewModel, navigateBack: () -> Unit) {
|
||||
|
||||
val engineType by viewModel.engineType.collectAsState()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
stringResource(id = R.string.car_settings),
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = navigateBack) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.arrow_back_24px),
|
||||
contentDescription = stringResource(id = R.string.accept_action_title),
|
||||
modifier = Modifier.size(48.dp, 48.dp),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
{ paddingValues ->
|
||||
val scrollState = rememberScrollState()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
.padding(top = 10.dp)
|
||||
.verticalScroll(scrollState)
|
||||
) {
|
||||
|
||||
|
||||
OutlinedCard(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(
|
||||
modifier = Modifier.padding(10.dp),
|
||||
) {
|
||||
SectionTitle(stringResource(R.string.engine_type))
|
||||
|
||||
val radioOptions = listOf(
|
||||
stringResource(R.string.combustion),
|
||||
stringResource(R.string.electric),
|
||||
)
|
||||
RadioButtonSingleSelection(
|
||||
modifier = Modifier.padding(),
|
||||
selectedOption = engineType,
|
||||
radioOptions = radioOptions,
|
||||
onClick = viewModel::onEngineTypeChanged
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,4 +39,7 @@ fun SettingsRoute(route: String, navController: NavHostController, function: ()
|
||||
if (route == "settings_screen") {
|
||||
SettingsScreen(viewModel, navController, function)
|
||||
}
|
||||
if (route == "car_settings") {
|
||||
CarScreen (viewModel = viewModel, function)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,14 @@ fun SettingsScreen(
|
||||
name = "Navigation Settings",
|
||||
description = "",
|
||||
icon = R.drawable.navigation_24px
|
||||
),
|
||||
Item(
|
||||
id = "car_settings",
|
||||
name = "Car Settings",
|
||||
description = "",
|
||||
icon = R.drawable.electric_car_24px
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
android:height="108dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="#000000">
|
||||
android:tint="#1A7416">
|
||||
<group android:scaleX="0.7888"
|
||||
android:scaleY="0.7888"
|
||||
android:translateX="101.376"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 12 KiB |
4
app/src/main/res/values/ic_launcher_background.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#98DABB</color>
|
||||
</resources>
|
||||