15 Commits

Author SHA1 Message Date
soraefir
c50933908c Fix crash due to rename 2026-05-25 14:14:42 +02:00
bot
77edae3585 Merge pull request 'Update dependency com.mikepenz:aboutlibraries to v14.2.1' (#503) from renovate/com.mikepenz-aboutlibraries-14.x into main 2026-05-25 04:01:29 +02:00
Renovate Bot
50c152084e Update dependency com.mikepenz:aboutlibraries to v14.2.1 2026-05-25 02:01:22 +00:00
b0a107c749 Update README.md 2026-05-24 11:58:56 +02:00
bot
91050826b2 Merge pull request 'Lock file maintenance' (#502) from renovate/lock-file-maintenance into main 2026-05-24 04:04:58 +02:00
Renovate Bot
1004119fed Lock file maintenance 2026-05-24 02:04:55 +00:00
d262331d1b Update .github/workflows/build.yml 2026-05-23 14:45:03 +02:00
7440e85987 Update app/build.gradle 2026-05-23 14:42:40 +02:00
2351512af8 Update app/build.gradle 2026-05-23 14:42:30 +02:00
ae2cde3545 Update app/build.gradle 2026-05-23 14:06:23 +02:00
soraefir
1000319b8f Bump java version workflow to 21 2026-05-23 11:45:32 +02:00
soraefir
8c4c5e16e0 Fix build error with backuprules 2026-05-23 11:42:03 +02:00
soraefir
0ce1e604a8 Update node deps 2026-05-23 11:39:04 +02:00
soraefir
757783534c Fix build, errors and spring cleaning 2026-05-23 11:35:02 +02:00
soraefir
3eba48ea7d WIP
# Conflicts:
#	build.gradle
#	gradle/wrapper/gradle-wrapper.properties
2026-05-23 10:53:36 +02:00
17 changed files with 167 additions and 121 deletions

View File

@@ -24,6 +24,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
with:
fetch-depth: 0
- name: set up secrets - name: set up secrets
run: | run: |
@@ -42,14 +44,16 @@ jobs:
- name: set up JDK - name: set up JDK
uses: actions/setup-java@v5 uses: actions/setup-java@v5
with: with:
java-version: 17 java-version: 21
distribution: "temurin" distribution: "temurin"
- name: Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v6 uses: gradle/actions/setup-gradle@v6
- name: Build APK - name: Build APK
run: ./gradlew assembleSignedRelease run: |
VERSION_CODE=$(git rev-list --count HEAD)
./gradlew assembleSignedRelease -PVERSION_CODE=$VERSION_CODE
- name: Release - name: Release
uses: softprops/action-gh-release@v3 uses: softprops/action-gh-release@v3

View File

@@ -5,6 +5,8 @@
<p>A virtual scratchmap of the world</p> <p>A virtual scratchmap of the world</p>
<a href="https://ko-fi.com/I2I615VP5M"><img src="https://ko-fi.com/img/githubbutton_sm.svg" alt="ko-fi"></a>
<br>
<img src="https://forthebadge.com/images/badges/built-for-android.svg" alt="Built for Android"> <img src="https://forthebadge.com/images/badges/built-for-android.svg" alt="Built for Android">
<img src="https://forthebadge.com/images/badges/built-with-love.svg" alt="Built with love"> <img src="https://forthebadge.com/images/badges/built-with-love.svg" alt="Built with love">
<br> <br>

View File

@@ -1,27 +1,25 @@
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'org.jetbrains.kotlin.plugin.serialization' version '2.3.21' id 'org.jetbrains.kotlin.plugin.serialization' version '2.3.21'
id 'org.jetbrains.kotlin.plugin.compose' version '2.3.21' id 'org.jetbrains.kotlin.plugin.compose' version '2.3.21'
id 'com.mikepenz.aboutlibraries.plugin' version '14.2.0' id 'com.mikepenz.aboutlibraries.plugin' version '14.2.0'
} }
android { android {
namespace 'net.helcel.beans' namespace 'net.helcel.beans'
compileSdk 36 compileSdk = 37
defaultConfig { defaultConfig {
buildConfigField("String", "APP_NAME", "\"Beans\"") buildConfigField("String", "APP_NAME", "\"Beans\"")
manifestPlaceholders["APP_NAME"] = "Beans" manifestPlaceholders["APP_NAME"] = "Beans"
applicationId 'net.helcel.beans' applicationId 'net.helcel.beans'
minSdk 28 minSdk = 28
targetSdk 36 targetSdk = 37
versionCode 4 versionName "1.3"
versionName "1.1a" versionCode project.hasProperty('VERSION_CODE') ? project.property('VERSION_CODE').toInteger() : 1
} }
signingConfigs { signingConfigs {
create("release") { register("release") {
try { try {
def keystorePropertiesFile = rootProject.file("app/keystore.properties") def keystorePropertiesFile = rootProject.file("app/keystore.properties")
def keystoreProperties = new Properties() def keystoreProperties = new Properties()
@@ -36,20 +34,18 @@ android {
} }
} }
} }
buildTypes { buildTypes {
debug { debug {
debuggable true debuggable true
} }
release { release {
minifyEnabled true minifyEnabled true
shrinkResources false shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
signedRelease { signedRelease {
minifyEnabled true minifyEnabled true
shrinkResources false shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig = signingConfigs.getByName("release") signingConfig = signingConfigs.getByName("release")
} }
@@ -78,10 +74,6 @@ android {
kotlinCompilerExtensionVersion = "2.2.20" kotlinCompilerExtensionVersion = "2.2.20"
} }
kotlin {
jvmToolchain(21)
}
lint { lint {
disable 'UsingMaterialAndMaterial3Libraries' disable 'UsingMaterialAndMaterial3Libraries'
} }
@@ -91,7 +83,6 @@ aboutLibraries {
library { library {
exclusionPatterns = [~"androidx.*", ~"com.google.android.*", ~"org.jetbrains.*"] exclusionPatterns = [~"androidx.*", ~"com.google.android.*", ~"org.jetbrains.*"]
} }
excludeFields = ["generated"]
} }
dependencies { dependencies {
@@ -114,7 +105,7 @@ dependencies {
implementation 'com.caverock:androidsvg-aar:1.4' implementation 'com.caverock:androidsvg-aar:1.4'
implementation 'com.github.chrisbanes:PhotoView:2.3.0' implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'com.mikepenz:aboutlibraries:14.2.0' implementation 'com.mikepenz:aboutlibraries:14.2.1'
implementation 'com.mikepenz:aboutlibraries-compose-m3:14.2.0' implementation 'com.mikepenz:aboutlibraries-compose-m3:14.2.0'
implementation 'com.mikepenz:aboutlibraries-core:14.2.0' implementation 'com.mikepenz:aboutlibraries-core:14.2.0'

View File

@@ -8,8 +8,7 @@
android:hardwareAccelerated="false" android:hardwareAccelerated="false"
android:icon="@mipmap/ic_launcher_round" android:icon="@mipmap/ic_launcher_round"
android:label="${APP_NAME}" android:label="${APP_NAME}"
android:supportsRtl="true" android:supportsRtl="true">
tools:replace="android:allowBackup">
<activity <activity
android:name=".activity.MainScreen" android:name=".activity.MainScreen"
android:exported="true"> android:exported="true">

View File

@@ -16,7 +16,6 @@ import androidx.compose.material.Scaffold
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TopAppBar import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DateRange
import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Percent import androidx.compose.material.icons.filled.Percent
import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Settings

View File

@@ -152,11 +152,17 @@ fun SettingsMainScreen(onExit: ()->Unit = {}) {
fun SettingsScreen(navController: NavHostController = settingsNav()) { fun SettingsScreen(navController: NavHostController = settingsNav()) {
val context = LocalContext.current val context = LocalContext.current
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
val keyTheme = stringResource(R.string.key_theme)
val defaultTheme = stringResource(R.string.system)
val keyProjection = stringResource(R.string.key_projection)
val keyGroup = stringResource(R.string.key_group)
val offString = stringResource(R.string.off)
var showEdit by remember { mutableStateOf(false) } var showEdit by remember { mutableStateOf(false) }
var theme by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_theme), context.getString(R.string.system))!!) } var theme by remember { mutableStateOf(prefs.getString(keyTheme, defaultTheme)!!) }
var projection by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_projection), "default")!!) } var projection by remember { mutableStateOf(prefs.getString(keyProjection, "default")!!) }
var groups by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_group), context.getString(R.string.off))!!) } var groups by remember { mutableStateOf(prefs.getString(keyGroup,offString)!!) }
if(showEdit) if(showEdit)
EditPlaceDialog(true) { EditPlaceDialog(true) {
@@ -179,7 +185,7 @@ fun SettingsScreen(navController: NavHostController = settingsNav()) {
) )
MultiPreference(arrayOf(stringResource(R.string.system),stringResource(R.string.light),stringResource(R.string.dark)), theme) { newTheme -> MultiPreference(arrayOf(stringResource(R.string.system),stringResource(R.string.light),stringResource(R.string.dark)), theme) { newTheme ->
theme = newTheme theme = newTheme
prefs.edit { putString(context.getString(R.string.key_theme), newTheme) } prefs.edit { putString(keyTheme, newTheme) }
} }
HorizontalDivider() HorizontalDivider()
} }
@@ -192,7 +198,7 @@ fun SettingsScreen(navController: NavHostController = settingsNav()) {
) )
MultiPreference(arrayOf(stringResource(R.string.mercator), stringResource(R.string.azimuthalequidistant)), projection) { newProj -> MultiPreference(arrayOf(stringResource(R.string.mercator), stringResource(R.string.azimuthalequidistant)), projection) { newProj ->
projection = newProj projection = newProj
prefs.edit { putString(context.getString(R.string.key_projection), newProj) } prefs.edit { putString(keyProjection, newProj) }
Settings.refreshProjection() Settings.refreshProjection()
} }
HorizontalDivider() HorizontalDivider()
@@ -219,11 +225,11 @@ fun SettingsScreen(navController: NavHostController = settingsNav()) {
arrayOf(stringResource(R.string.on), stringResource(R.string.off)), arrayOf(stringResource(R.string.on), stringResource(R.string.off)),
groups groups
) { key -> ) { key ->
if (key == context.getString(R.string.off)) { if (key == offString) {
showDialog=true showDialog=true
} }
groups = key groups = key
prefs.edit { putString(context.getString(R.string.key_group), key) } prefs.edit { putString(keyGroup, key) }
} }
HorizontalDivider() HorizontalDivider()
} }
@@ -283,9 +289,13 @@ fun SettingsScreen(navController: NavHostController = settingsNav()) {
@Composable @Composable
fun RegionalScreen() { fun RegionalScreen() {
val context = LocalContext.current val context = LocalContext.current
val keyRegional = stringResource(R.string.key_regional)
val offString = stringResource(R.string.off)
val onString = stringResource(R.string.on)
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
var selected by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_regional),context.getString(R.string.off))!!)} var selected by remember { mutableStateOf(prefs.getString(keyRegional, offString)!!)}
var regional by remember{ mutableStateOf(prefs.getString(context.getString(R.string.key_regional), context.getString(R.string.off))!!)} var regional by remember{ mutableStateOf(prefs.getString(keyRegional, offString)!!)}
var showDialog by remember{mutableStateOf(false)} var showDialog by remember{mutableStateOf(false)}
var showLoad by remember{mutableStateOf(false)} var showLoad by remember{mutableStateOf(false)}
@@ -305,7 +315,7 @@ fun RegionalScreen() {
regional= selected regional= selected
prefs.edit { prefs.edit {
putString( putString(
context.getString(R.string.key_regional), keyRegional,
regional regional
) )
} }
@@ -332,12 +342,12 @@ fun RegionalScreen() {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
MultiPreference(arrayOf(stringResource(R.string.on),stringResource(R.string.off)),regional) { key -> MultiPreference(arrayOf(stringResource(R.string.on),stringResource(R.string.off)),regional) { key ->
when (key) { when (key) {
context.getString(R.string.off) -> { showDialog=true offString -> { showDialog=true
selected=key selected=key
} }
context.getString(R.string.on) -> { onString -> {
regional = key regional = key
prefs.edit { putString(context.getString(R.string.key_regional), key) } prefs.edit { putString(keyRegional, key) }
showLoad=true showLoad=true
scope.launch { scope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@@ -354,7 +364,7 @@ fun RegionalScreen() {
@Composable @Composable
fun MultiPreference(list: Array<String>, selected: String, onSelected: (String) -> Unit) { fun MultiPreference(list: Array<String>, selected: String, onSelected: (String) -> Unit) {
Column(Modifier.padding(2.dp)) { Column(Modifier.padding(2.dp)) {
list.map { value -> list.forEach { value ->
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier modifier = Modifier

View File

@@ -115,7 +115,6 @@ fun StatsList(activeMode: LocType, countMode: Boolean) {
@Composable @Composable
fun StatsRow(group: Groups.Group, activeMode: LocType, countMode: Boolean) { fun StatsRow(group: Groups.Group, activeMode: LocType, countMode: Boolean) {
val context = LocalContext.current
val visited = remember(group, activeMode) { val visited = remember(group, activeMode) {
Data.visits.getVisitedByValue(group.key) Data.visits.getVisitedByValue(group.key)
@@ -124,18 +123,18 @@ fun StatsRow(group: Groups.Group, activeMode: LocType, countMode: Boolean) {
val count = when (activeMode) { val count = when (activeMode) {
LocType.WORLD -> World.WWW.children.filter { it.code in visited }.size LocType.WORLD -> World.WWW.children.filter { it.code in visited }.size
LocType.COUNTRY -> World.WWW.children.flatMap { it.children.filter { c -> c.code in visited } }.size LocType.COUNTRY -> World.WWW.children.flatMap { it.children.filter { c -> c.code in visited } }.size
LocType.STATE -> World.WWW.children.flatMap { itc->itc.children.flatMap { it.children.filter { it.code in visited } } }.size LocType.STATE -> World.WWW.children.flatMap { a->a.children.flatMap { b->b.children.filter { c->c.code in visited } } }.size
else -> 0 else -> 0
} }
val area = when (activeMode) { val area = when (activeMode) {
LocType.WORLD -> World.WWW.children.filter { it.code in visited }.sumOf { it.area } LocType.WORLD -> World.WWW.children.filter { it.code in visited }.sumOf { it.area }
LocType.COUNTRY -> World.WWW.children.flatMap { it.children.filter { c -> c.code in visited } }.sumOf { it.area } LocType.COUNTRY -> World.WWW.children.flatMap { it.children.filter { c -> c.code in visited } }.sumOf { it.area }
LocType.STATE -> World.WWW.children.flatMap { it.children.flatMap { it.children.filter { it.code in visited } } }.sumOf { it.area } LocType.STATE -> World.WWW.children.flatMap { a->a.children.flatMap { b->b.children.filter { c->c.code in visited } } }.sumOf { it.area }
else -> 0 else -> 0
} }
val displayValue = if (countMode) count.toString() else context.getString(R.string.number_with_unit, area, "km²") val displayValue = if (countMode) count.toString() else stringResource(R.string.number_with_unit, area, "km²")
val backgroundColor = group.color.color val backgroundColor = group.color.color
val textColor = getContrastColor(backgroundColor) val textColor = getContrastColor(backgroundColor)

View File

@@ -58,7 +58,7 @@ fun syncVisited(loc: GeoLoc?=World.WWW){
loc?.children?.forEach { tt -> loc?.children?.forEach { tt ->
tt.children.forEach {itc-> tt.children.forEach {itc->
if(Data.visits.getVisited(itc) in listOf(AUTO_GROUP,NO_GROUP)) { if(Data.visits.getVisited(itc) in listOf(AUTO_GROUP,NO_GROUP)) {
if(itc.children.any { itcc -> Data.visits.getVisited(itcc) != NO_GROUP }) if(itc.children.any { c -> Data.visits.getVisited(c) != NO_GROUP })
Data.visits.setVisited(itc, AUTO_GROUP) Data.visits.setVisited(itc, AUTO_GROUP)
else else
Data.visits.setVisited(itc, NO_GROUP) Data.visits.setVisited(itc, NO_GROUP)
@@ -111,21 +111,20 @@ fun EditPlaceScreen(loc: GeoLoc, onExit:()->Unit={}) {
Column { Column {
val currentTab = tabs.getOrNull(selectedTab) ?: return@Column val currentTab = tabs.getOrNull(selectedTab) ?: return@Column
TabRow( TabRow(
selectedTabIndex = min(tabs.lastIndex, selectedTab), selectedTabIndex = min(tabs.lastIndex, selectedTab),
) { ) {
tabs.forEachIndexed { index, tab -> tabs.forEachIndexed { index, tab ->
Tab( Tab(
selected = selectedTab == index, selected = selectedTab == index,
onClick = { onClick = {
while (tabs.size > index + 1) while (tabs.size > index + 1)
tabs.removeAt(tabs.lastIndex) tabs.removeAt(tabs.lastIndex)
}, },
text = { Text(tab.fullName) } text = { Text(tab.fullName) }
) )
}
} }
}
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()

View File

@@ -7,7 +7,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import com.mikepenz.aboutlibraries.ui.compose.DefaultChipColors import com.mikepenz.aboutlibraries.ui.compose.DefaultChipColors
import com.mikepenz.aboutlibraries.ui.compose.DefaultLibraryColors import com.mikepenz.aboutlibraries.ui.compose.DefaultLibraryColors
import com.mikepenz.aboutlibraries.ui.compose.android.rememberLibraries import com.mikepenz.aboutlibraries.ui.compose.android.produceLibraries
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
import net.helcel.beans.R import net.helcel.beans.R
import net.helcel.beans.activity.SysTheme import net.helcel.beans.activity.SysTheme
@@ -16,14 +16,14 @@ import net.helcel.beans.activity.SysTheme
@Preview @Preview
@Composable @Composable
fun LicenseScreen() { fun LicenseScreen() {
val libraries = rememberLibraries(R.raw.aboutlibraries) val libraries = produceLibraries(R.raw.aboutlibraries)
SysTheme { SysTheme {
LibrariesContainer( LibrariesContainer(
libraries = libraries.value, libraries = libraries.value,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
colors = DefaultLibraryColors( colors = DefaultLibraryColors(
backgroundColor = MaterialTheme.colors.background, libraryBackgroundColor = MaterialTheme.colors.background,
contentColor = MaterialTheme.colors.onBackground, libraryContentColor = MaterialTheme.colors.onBackground,
licenseChipColors = DefaultChipColors( licenseChipColors = DefaultChipColors(
containerColor = MaterialTheme.colors.primary, containerColor = MaterialTheme.colors.primary,
contentColor = MaterialTheme.colors.onPrimary, contentColor = MaterialTheme.colors.onPrimary,
@@ -37,6 +37,8 @@ fun LicenseScreen() {
contentColor = MaterialTheme.colors.onSecondary, contentColor = MaterialTheme.colors.onSecondary,
), ),
dialogConfirmButtonColor = MaterialTheme.colors.primary, dialogConfirmButtonColor = MaterialTheme.colors.primary,
dialogBackgroundColor = MaterialTheme.colors.onPrimary,
dialogContentColor = MaterialTheme.colors.primary,
) )
) )
} }

View File

@@ -2,6 +2,7 @@ package net.helcel.beans.countries
import net.helcel.beans.countries.Country.* import net.helcel.beans.countries.Country.*
@Suppress("RedundantSuppression", "SpellCheckingInspection", "unused")
enum class Group(override val fullName: String, override val children: Set<GeoLoc>) : GeoLoc { enum class Group(override val fullName: String, override val children: Set<GeoLoc>) : GeoLoc {
EEE( EEE(

View File

@@ -3,6 +3,7 @@ package net.helcel.beans.helper
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Serializer import kotlinx.serialization.Serializer
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@@ -24,53 +25,53 @@ const val AUTO_GROUP = -1
@Serializable @Serializable
class Groups(val id: Int, private val grps: HashMap<Int, Group>) { class Groups(val id: Int, @SerialName("grps") private val groups: HashMap<Int, Group>) {
@kotlinx.serialization.Transient @kotlinx.serialization.Transient
private val _groupsFlow = MutableStateFlow<List<Group>>(grps.values.toList()) private val _groupsFlow = MutableStateFlow<List<Group>>(groups.values.toList())
@kotlinx.serialization.Transient @kotlinx.serialization.Transient
val groupsFlow: StateFlow<List<Group>> = _groupsFlow.asStateFlow() val groupsFlow: StateFlow<List<Group>> = _groupsFlow.asStateFlow()
fun setGroup(key: Int, name: String, col: ColorDrawable) { fun setGroup(key: Int, name: String, col: ColorDrawable) {
grps[key] = Group(key, name, col) groups[key] = Group(key, name, col)
_groupsFlow.value = grps.values.toList() _groupsFlow.value = groups.values.toList()
} }
fun deleteGroup(key: Int) { fun deleteGroup(key: Int) {
grps.remove(key) groups.remove(key)
_groupsFlow.value = grps.values.toList() _groupsFlow.value = groups.values.toList()
} }
fun getGroupFromKey(key: Int): Group { fun getGroupFromKey(key: Int): Group {
return grps.getOrDefault(key, EmptyGroup()) return groups.getOrDefault(key, EmptyGroup())
} }
fun genKey(): Int { fun genKey(): Int {
val key = rnd.nextInt() val key = rnd.nextInt()
if (grps.containsKey(key) || key in listOf(NO_GROUP, DEFAULT_GROUP, AUTO_GROUP)) return genKey() if (groups.containsKey(key) || key in listOf(NO_GROUP, DEFAULT_GROUP, AUTO_GROUP)) return genKey()
return key return key
} }
fun size(): Int { fun size(): Int {
return grps.size return groups.size
} }
fun getUniqueEntry(): Group? { fun getUniqueEntry(): Group? {
assert(size() == 1) assert(size() == 1)
return if (grps.size == 1) { return if (groups.size == 1) {
grps[grps.keys.first()] groups[groups.keys.first()]
} else { } else {
null null
} }
} }
fun getGroupFromPos(pos: Int): Pair<Int, Group> { fun getGroupFromPos(pos: Int): Pair<Int, Group> {
if(grps.keys.isEmpty()) return Pair(NO_GROUP,Group(NO_GROUP,"-")) if(groups.keys.isEmpty()) return Pair(NO_GROUP,Group(NO_GROUP,"-"))
val key = grps.keys.toList()[pos] val key = groups.keys.toList()[pos]
return Pair(key, getGroupFromKey(key)) return Pair(key, getGroupFromKey(key))
} }
fun findGroupPos(key: Int): Int { fun findGroupPos(key: Int): Int {
return grps.keys.toList().indexOf(key) return groups.keys.toList().indexOf(key)
} }
class EmptyGroup : Group(0, "") class EmptyGroup : Group(0, "")

View File

@@ -0,0 +1,50 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="128"
android:viewportHeight="128">
<group android:scaleX="0.8918919"
android:scaleY="0.8918919"
android:translateX="6.918919"
android:translateY="6.918919">
<group
android:scaleX="1.5"
android:scaleY="1.5"
android:translateX="10"
android:translateY="10">
<path
android:pathData="M36,36m-28,0a28,28 0,1 1,56 0a28,28 0,1 1,-56 0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="@color/blue"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</group>
<group
android:scaleX="1.25"
android:scaleY="1.25"
android:translateX="18"
android:translateY="18">
<path
android:pathData="M56.803,45.75c0.599,-0.299 1.157,-0.672 1.663,-1.11 0.804,-0.698 1.463,-1.546 1.94,-2.497 0.599,-1.303 0.883,-2.728 0.829,-4.161 0.034,-1.498 -0.154,-2.994 -0.558,-4.437 -0.451,-1.645 -1.104,-3.229 -1.944,-4.714 -0.967,-2.141 -2.28,-4.108 -3.887,-5.822 -1.242,-1.326 -2.757,-2.366 -4.44,-3.048 -0.877,-0.372 -1.821,-0.56 -2.774,-0.553 -1.402,0.074 -2.729,0.697 -3.882,1.389 -1.285,0.81 -2.244,1.735 -2.771,3.053 -0.739,2.362 0.827,4.821 1.113,5.269 0.003,0.005 0.187,0.281 0.555,0.832 1.512,2.264 1.589,2.358 1.666,2.495 0.646,1.221 1.114,2.528 1.389,3.882 1.043,4.001 1.565,6.001 2.223,6.932 1.208,1.71 3.455,3.414 5.826,3.325 1.059,-0.088 2.093,-0.371 3.05,-0.835Z"
android:strokeWidth="2"
android:fillColor="@color/white"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M22,41c-1.54,0.554 -2.83,1.642 -3.636,3.066 -0.714,1.365 -0.957,2.928 -0.693,4.445 0.178,1.247 0.632,2.439 1.329,3.488 2.032,3.228 5.383,4.423 7,5 1.613,0.561 3.295,0.897 5,1 2.387,0.205 6.923,0.535 11,-2 1.287,-0.665 2.335,-1.713 3,-3 0.661,-1.601 0.661,-3.399 0,-5 -0.389,-1.156 -1.121,-2.165 -2.099,-2.894 -0.919,-0.599 -1.974,-0.956 -3.069,-1.039 -2.057,-0.313 -2.756,0.139 -5.014,0.057 -1.271,-0.019 -2.534,-0.214 -3.753,-0.577 -3.036,-0.95 -3.33,-2.457 -5.889,-2.829 -1.066,-0.158 -2.155,-0.061 -3.177,0.282Z"
android:strokeWidth="2"
android:fillColor="@color/white"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M12.7,26.975c1.758,-3.604 5.311,-3.191 9.58,-7.091 2.701,-2.469 2.858,-4.078 5.613,-5.202 1.407,-0.644 2.984,-0.819 4.498,-0.5 2.053,0.475 3.761,1.891 4.61,3.819 1.116,2.742 -0.314,5.437 -1.398,7.482 -0.951,1.689 -2.167,3.215 -3.602,4.518 -1.479,1.552 -3.161,2.898 -5,4 -2.629,1.541 -6.332,3.711 -9.989,2.43 -1.481,-0.548 -2.768,-1.521 -3.699,-2.796 -1.496,-1.906 -1.735,-4.512 -0.612,-6.659Z"
android:strokeWidth="2"
android:fillColor="@color/white"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</group>
</group>
</vector>

View File

@@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/> <background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

View File

@@ -1,24 +1,9 @@
# Project-wide Gradle settings. # Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=false android.enableJetifier=false
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
android.uniquePackageNames=false
android.dependency.useConstraints=false
android.r8.strictFullModeForKeepRules=false

View File

@@ -1,9 +1,9 @@
{ {
"dependencies": { "dependencies": {
"@turf/area": "^7.0.0", "@turf/area": "^7.3.5",
"@turf/turf": "^7.0.0", "@turf/turf": "^7.3.5",
"jsdom": "^29.0.0", "jsdom": "^29.1.1",
"mapshaper": "^0.7.0" "mapshaper": "^0.7.19"
}, },
"type": "module" "type": "module"
} }

View File

@@ -6,6 +6,9 @@ pluginManagement {
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
} }
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}
dependencyResolutionManagement { dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories { repositories {

View File

@@ -200,7 +200,7 @@
"@types/geojson" "^7946.0.10" "@types/geojson" "^7946.0.10"
tslib "^2.8.1" tslib "^2.8.1"
"@turf/area@7.3.5", "@turf/area@^7.0.0": "@turf/area@7.3.5", "@turf/area@^7.3.5":
version "7.3.5" version "7.3.5"
resolved "https://registry.yarnpkg.com/@turf/area/-/area-7.3.5.tgz#97e3c94305a5f1674b542855ddfb9871cb0a5de2" resolved "https://registry.yarnpkg.com/@turf/area/-/area-7.3.5.tgz#97e3c94305a5f1674b542855ddfb9871cb0a5de2"
integrity sha512-sSn80wPT7XfBIDN3vurCPxhk9W4U8ozS/XImSqeLN8qveTICOxzZkhsGDMp0CuncaN+plWut4a2TdNM7mzZB6Q== integrity sha512-sSn80wPT7XfBIDN3vurCPxhk9W4U8ozS/XImSqeLN8qveTICOxzZkhsGDMp0CuncaN+plWut4a2TdNM7mzZB6Q==
@@ -1668,7 +1668,7 @@
"@types/geojson" "^7946.0.10" "@types/geojson" "^7946.0.10"
tslib "^2.8.1" tslib "^2.8.1"
"@turf/turf@^7.0.0": "@turf/turf@^7.3.5":
version "7.3.5" version "7.3.5"
resolved "https://registry.yarnpkg.com/@turf/turf/-/turf-7.3.5.tgz#a98b79933f03f2c2748582424b9fcd42f8257aef" resolved "https://registry.yarnpkg.com/@turf/turf/-/turf-7.3.5.tgz#a98b79933f03f2c2748582424b9fcd42f8257aef"
integrity sha512-l5Z1ZFEizN9p5GxX3mzUGf+i4t7AP3YpWcNdf9+kIzJcQD3eYuGBabj2hLrfrluqFJ+uxsuo4RgPtortQ9Dwpg== integrity sha512-l5Z1ZFEizN9p5GxX3mzUGf+i4t7AP3YpWcNdf9+kIzJcQD3eYuGBabj2hLrfrluqFJ+uxsuo4RgPtortQ9Dwpg==
@@ -2449,9 +2449,9 @@ hyparquet@1.25.6:
integrity sha512-Q9W5IjkVch3ZMnYd4qFv2q8suu5Jc36yt7J+zUNM9grwnP1S189icp0jdEQKM5HJvQkTVy8NMiQ8n/dM5QAt1A== integrity sha512-Q9W5IjkVch3ZMnYd4qFv2q8suu5Jc36yt7J+zUNM9grwnP1S189icp0jdEQKM5HJvQkTVy8NMiQ8n/dM5QAt1A==
hyparquet@^1.25.6: hyparquet@^1.25.6:
version "1.25.8" version "1.26.0"
resolved "https://registry.yarnpkg.com/hyparquet/-/hyparquet-1.25.8.tgz#9d81d9b8aea1347de3701ca619fe2e1def8dcf10" resolved "https://registry.yarnpkg.com/hyparquet/-/hyparquet-1.26.0.tgz#65b147befb99932e38c2bb1dd007eb7da0d7a0dc"
integrity sha512-KYRKZCHBgEY9FOuqTe9m+zdrKBcTo9ZcaIUqG7SdZygv5hwrGD3C6wuTrGt07cbEkgO71UIih+KbojLKf3ENbg== integrity sha512-yxUiViPZ+z5h+xdX4rA1G+k30jXoEsG9I2xEpjaM84imGznbKjZzxuZFsdzqg6C4LxNnnAlDFvzpk4uxQWTbTQ==
hysnappy@1.0.0: hysnappy@1.0.0:
version "1.0.0" version "1.0.0"
@@ -2557,7 +2557,7 @@ jpeg-js@^0.4.4:
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa"
integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg== integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==
jsdom@^29.0.0: jsdom@^29.1.1:
version "29.1.1" version "29.1.1"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-29.1.1.tgz#5b9704906f3cd510c34aa941ae2f8f7f8179df01" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-29.1.1.tgz#5b9704906f3cd510c34aa941ae2f8f7f8179df01"
integrity sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q== integrity sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==
@@ -2631,10 +2631,10 @@ map-stream@0.0.7:
resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.7.tgz#8a1f07896d82b10926bd3744a2420009f88974a8" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.7.tgz#8a1f07896d82b10926bd3744a2420009f88974a8"
integrity sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ== integrity sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==
mapshaper@^0.7.0: mapshaper@^0.7.19:
version "0.7.19" version "0.7.20"
resolved "https://registry.yarnpkg.com/mapshaper/-/mapshaper-0.7.19.tgz#52db581df39d79064b6a4ab42857523e669f1ff5" resolved "https://registry.yarnpkg.com/mapshaper/-/mapshaper-0.7.20.tgz#0b1643d89bdaf7d347e6abea97a3c890ada0799a"
integrity sha512-G2SQjdyKCICdxv/gNee0W07TZNfh6f0SJVwnkP9RtUmo3xyFc98SDS/wBMyBoBrpnf8kdmNXllqJLerAfVV6WQ== integrity sha512-YmH1Q0hh4G+zNBxQdkaPyvKnIkGPZwtTGSrGme6QcyhXb1B1NXzzJqTE/YdhmGOWKjR2ZTn+wQSYjFilYudegw==
dependencies: dependencies:
"@bokuweb/zstd-wasm" "^0.0.27" "@bokuweb/zstd-wasm" "^0.0.27"
"@ngageoint/geopackage" "^4.2.6" "@ngageoint/geopackage" "^4.2.6"
@@ -2663,7 +2663,7 @@ mapshaper@^0.7.0:
idb-keyval "^6.2.0" idb-keyval "^6.2.0"
jpeg-js "^0.4.4" jpeg-js "^0.4.4"
kdbush "^3.0.0" kdbush "^3.0.0"
mproj "0.1.2" mproj "0.1.3"
msgpackr "^1.10.1" msgpackr "^1.10.1"
open "^11.0.0" open "^11.0.0"
pngjs "^7.0.0" pngjs "^7.0.0"
@@ -2702,10 +2702,10 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
mproj@0.1.2: mproj@0.1.3:
version "0.1.2" version "0.1.3"
resolved "https://registry.yarnpkg.com/mproj/-/mproj-0.1.2.tgz#9827ed5ff6a171a9be7dbd9d767c4615d969ec50" resolved "https://registry.yarnpkg.com/mproj/-/mproj-0.1.3.tgz#295b990970a80380dac85b29d605a5f4f66b25ff"
integrity sha512-kl38Oi47W7XMmA3jh1xPMiGpxpWJG5UdwJpa/yhVtQshFrxY6MnCWSupYrMmeYylp1shUvpGWPEBY7TfgqfWqw== integrity sha512-XPxty3HPv1j5eXfxId1FSGX7JB8+KgozVOyCu5l40u/I0pW7gpWTaxouA8MNKHfEXN5oE8r9eowkM+0LuQduIQ==
dependencies: dependencies:
geographiclib "1.48.0" geographiclib "1.48.0"
rw "~1.3.2" rw "~1.3.2"
@@ -3271,17 +3271,17 @@ tinyqueue@^2.0.0, tinyqueue@^2.0.3:
resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08" resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08"
integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA== integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==
tldts-core@^7.0.30: tldts-core@^7.1.0:
version "7.0.30" version "7.1.0"
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.0.30.tgz#c495dba27778f2220bea94f3f6399005c7aca61c" resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.1.0.tgz#62dd1a0faa96d10a8ee52546215c741943f33714"
integrity sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q== integrity sha512-Ou+YJ467tR700sk2mZBB1/uFah6LDeH/VKO0N4xfjFOXE0hJkZgiOgXj16gEi1wQ4L6334UTKjxF6gum8ftZzQ==
tldts@^7.0.5: tldts@^7.0.5:
version "7.0.30" version "7.1.0"
resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.0.30.tgz#497cea8d610953222f9dcb3ceb07c7207efcd816" resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.1.0.tgz#b271856a7bd507fa67d9a063b877a2c72eb944c6"
integrity sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw== integrity sha512-Y+iRHtDrPOlNH9ZZpqpIogonv7jwSPHIt3Gpe+kRvYJlBaK+QGNXu+xooJY0QEmb62/ERo3GF3zCxRMbMtKEEA==
dependencies: dependencies:
tldts-core "^7.0.30" tldts-core "^7.1.0"
tmp@^0.0.33: tmp@^0.0.33:
version "0.0.33" version "0.0.33"