Compare commits
179 Commits
b55b834e35
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 18725261e3 | |||
|
|
cc1a0c1aca
|
||
|
|
f2a5efcec5
|
||
|
|
cfa784991b
|
||
|
|
14f8543d3d
|
||
|
|
5705749d12
|
||
| 1d0b814e77 | |||
|
|
ecc26f811a | ||
| 8cd93d866b | |||
|
|
6bf4034e1e | ||
| 977321ddb5 | |||
| bee96b5d9b | |||
|
|
6d21f0da6d | ||
|
|
445ccbcac0 | ||
| 225e5bfdc8 | |||
|
|
0c446f0d59 | ||
| 7c0b9bda80 | |||
|
|
eaa0278764 | ||
| eeac01f638 | |||
|
|
9f8d5aaa18 | ||
| cdb9d51257 | |||
| 2d6b895f57 | |||
|
|
80e191a50d | ||
| 4114185a73 | |||
| 24fe56c02a | |||
|
|
d83234cf5b | ||
|
|
e608b22c0c | ||
|
|
f8ed30cd73
|
||
|
|
c50933908c
|
||
| 77edae3585 | |||
|
|
50c152084e | ||
| b0a107c749 | |||
| 91050826b2 | |||
|
|
1004119fed | ||
| d262331d1b | |||
| 7440e85987 | |||
| 2351512af8 | |||
| ae2cde3545 | |||
|
|
1000319b8f
|
||
|
|
8c4c5e16e0
|
||
|
|
0ce1e604a8
|
||
|
|
757783534c
|
||
|
|
3eba48ea7d
|
||
| cf886b9581 | |||
|
|
f2ff152c07 | ||
| 19b152db8c | |||
|
|
18dff78e5b | ||
| 9b16a30c9a | |||
|
|
62bf040093 | ||
| 60bafc6798 | |||
| bf206efc1f | |||
|
|
c42f72a856 | ||
|
|
c6a890aee3 | ||
| 59450cf480 | |||
|
|
7f068ea10f | ||
| f9763d7b53 | |||
|
|
f2e9f44dee | ||
| 1039e220fb | |||
|
|
a810345e2b | ||
| 4dc5213e6f | |||
| 5f313a20d5 | |||
|
|
01eae6daf9 | ||
|
|
7ae7ebb9b3 | ||
| a4e64e43f3 | |||
|
|
a1e73f5844 | ||
| a6189c77ef | |||
|
|
1b36ae84d7 | ||
| c609cccdc6 | |||
| 1360c5eeef | |||
|
|
d087eddc29 | ||
|
|
1910a8672b | ||
| 787175ee37 | |||
|
|
90285d5efa | ||
| 66b06d608d | |||
|
|
cabaa02f59 | ||
| ee60dffa17 | |||
|
|
98a2558ad2 | ||
| d820c0e5fa | |||
|
|
61bba0c099 | ||
| c58f1359cd | |||
| 942e3a6235 | |||
|
|
d43535f772 | ||
|
|
e12c7194e6 | ||
| 47277d3609 | |||
| 2a8bb6d1b4 | |||
|
|
7ad2eff27b | ||
| b52c16c242 | |||
|
|
caeb0f69a5 | ||
|
|
74b4fe4ac4 | ||
| 504ae87174 | |||
|
|
06bba3ceaf | ||
| 70defaf916 | |||
|
|
dfcebe327f | ||
| 3b27613e99 | |||
|
|
bff39001a4 | ||
| e77c8dc49e | |||
|
|
c4da784b95 | ||
| cbf1871cec | |||
|
|
9ed40a67bc | ||
| 8e699d0b22 | |||
|
|
9424517eb4 | ||
| acd6753112 | |||
|
|
6fa02d117b | ||
| f83ff7aca1 | |||
|
|
89bd79b542 | ||
| 4db4757701 | |||
|
|
f57f29cda6 | ||
| 51ff021d37 | |||
|
|
c6f9bf7fc5 | ||
| 9385442542 | |||
|
|
19a185a6c3 | ||
| 725de6c7b3 | |||
|
|
5228ac2508 | ||
| 80d6693ef3 | |||
|
|
9d3ce7410a | ||
| 989fd1cea0 | |||
| d3b11fbb02 | |||
|
|
9a8c13e148 | ||
|
|
4f65a86302 | ||
| 8c559f73f7 | |||
|
|
1e77399412 | ||
| 6f2d9cacc4 | |||
|
|
f6209562ff | ||
| 9e692dae6a | |||
|
|
16458b014f | ||
| af262b0ad2 | |||
|
|
5a81ae5170 | ||
| 89931b9c32 | |||
| 5a07720457 | |||
|
|
7dd4049f89 | ||
|
|
6aa1253ab6 | ||
| 3391985d20 | |||
| 28bb0ae611 | |||
|
|
d7a3f5389e | ||
| 0552dc33c0 | |||
|
|
b0799070eb | ||
|
|
b000b2e921 | ||
| 10c5f9538f | |||
|
|
5a47c70a6f | ||
| f768306651 | |||
|
|
ff22ea6355 | ||
| f22ff6eb34 | |||
|
|
12812dcc2f | ||
| c348a2c8a6 | |||
|
|
417f53860d | ||
| 7dc7381075 | |||
|
|
b86c1aeebe | ||
| 1d32687848 | |||
|
|
80738118f0 | ||
| 45776a2f9b | |||
|
|
b303bc6aa6 | ||
| 476dcddaa9 | |||
|
|
5229507771 | ||
| 9a16f1b2ce | |||
|
|
633a7b5d06 | ||
| 80ff0458a1 | |||
|
|
5d5d6c6962 | ||
| 2c94f7871b | |||
|
|
26a4aae265 | ||
| 477a8c94fd | |||
|
|
c00078e766 | ||
| 09ba61f3c5 | |||
|
|
71d48d87be | ||
| 9695f69e70 | |||
|
|
992116fcfe | ||
| 270946c082 | |||
|
|
6832f3ebee | ||
| ce47bff4f5 | |||
|
|
47c8cf7812 | ||
| 5cd1b5f9db | |||
|
|
e3a6b11c67 | ||
| 8fb08205f1 | |||
| 863bba990a | |||
|
|
cc7f0f6a47 | ||
|
|
c4432e6494 | ||
| a4237d102e | |||
|
|
5050468192 | ||
| e526ea9974 | |||
|
|
36cd230e16 |
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@@ -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,17 +44,19 @@ 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@v2
|
uses: softprops/action-gh-release@v3
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -71,7 +73,7 @@ Thanks to all contributors, the developers of our dependencies, and our users.
|
|||||||
## 📝 License
|
## 📝 License
|
||||||
|
|
||||||
```
|
```
|
||||||
Copyright (C) 2024 Helcel & MYDOLI
|
Copyright (C) 2026 Helcel & MYDOLI
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -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.4.0'
|
||||||
id 'org.jetbrains.kotlin.plugin.serialization' version '2.3.20'
|
id 'org.jetbrains.kotlin.plugin.compose' version '2.4.0'
|
||||||
id 'org.jetbrains.kotlin.plugin.compose' version '2.3.20'
|
id 'com.mikepenz.aboutlibraries.plugin' version '14.2.1'
|
||||||
id 'com.mikepenz.aboutlibraries.plugin' version '13.2.1'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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.3b"
|
||||||
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,34 +83,39 @@ aboutLibraries {
|
|||||||
library {
|
library {
|
||||||
exclusionPatterns = [~"androidx.*", ~"com.google.android.*", ~"org.jetbrains.*"]
|
exclusionPatterns = [~"androidx.*", ~"com.google.android.*", ~"org.jetbrains.*"]
|
||||||
}
|
}
|
||||||
excludeFields = ["generated"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.compose.material3:material3:1.4.0'
|
implementation 'androidx.compose.material3:material3:1.4.0'
|
||||||
implementation "androidx.compose.material:material:1.10.6"
|
implementation "androidx.compose.material:material:1.11.2"
|
||||||
implementation 'androidx.compose.material:material-icons-extended:1.7.8'
|
implementation 'androidx.compose.material:material-icons-extended:1.7.8'
|
||||||
implementation 'androidx.navigation:navigation-compose:2.9.7'
|
implementation 'androidx.navigation:navigation-compose:2.9.8'
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.1.5'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.1.5'
|
||||||
|
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0'
|
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0'
|
||||||
|
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||||
implementation 'androidx.compose.ui:ui'
|
implementation 'androidx.compose.ui:ui'
|
||||||
implementation "androidx.activity:activity-ktx:1.13.0"
|
implementation "androidx.activity:activity-ktx:1.13.0"
|
||||||
|
|
||||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||||
implementation 'com.google.android.material:material:1.13.0'
|
implementation 'com.google.android.material:material:1.14.0'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0'
|
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0'
|
||||||
|
|
||||||
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:13.2.1'
|
implementation 'com.mikepenz:aboutlibraries:14.2.1'
|
||||||
implementation 'com.mikepenz:aboutlibraries-compose-m3:13.2.1'
|
implementation 'com.mikepenz:aboutlibraries-compose-m3:14.2.1'
|
||||||
implementation 'com.mikepenz:aboutlibraries-core:13.2.1'
|
implementation 'com.mikepenz:aboutlibraries-core:14.2.1'
|
||||||
|
|
||||||
|
|
||||||
implementation platform('androidx.compose:compose-bom:2026.03.01')
|
implementation platform('androidx.compose:compose-bom:2026.05.01')
|
||||||
debugImplementation 'androidx.compose.ui:ui-tooling:1.10.6'
|
debugImplementation 'androidx.compose.ui:ui-tooling:1.11.2'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.configureEach { task ->
|
||||||
|
if (task.name.startsWith("merge") && task.name.endsWith("Assets")) {
|
||||||
|
task.outputs.upToDateWhen { false }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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">
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 6.1 MiB After Width: | Height: | Size: 3.9 MiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 6.0 MiB After Width: | Height: | Size: 3.7 MiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 6.1 MiB After Width: | Height: | Size: 4.0 MiB |
@@ -9,20 +9,23 @@ import androidx.compose.foundation.background
|
|||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.IconButton
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Scaffold
|
import androidx.compose.material.Scaffold
|
||||||
|
import androidx.compose.material.Surface
|
||||||
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
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
@@ -47,12 +50,12 @@ class MainScreen : ComponentActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
actionBar?.hide()
|
actionBar?.hide()
|
||||||
Settings.start(this)
|
Settings.start(this)
|
||||||
GeoLocImporter.importStates(this)
|
|
||||||
Data.loadData(this, Int.MIN_VALUE)
|
Data.loadData(this, Int.MIN_VALUE)
|
||||||
|
GeoLocImporter.importStates(this)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
SysTheme {
|
SysTheme {
|
||||||
Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.background)) {
|
Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.primary).statusBarsPadding(),) {
|
||||||
AppNavHost(psvg, css)
|
AppNavHost(psvg, css)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,6 +114,7 @@ class MainScreen : ComponentActivity() {
|
|||||||
PhotoView(ctx).apply {
|
PhotoView(ctx).apply {
|
||||||
setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null)
|
setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null)
|
||||||
setImageDrawable(drawable)
|
setImageDrawable(drawable)
|
||||||
|
maximumScale = 32f
|
||||||
scaleType = ImageView.ScaleType.FIT_CENTER
|
scaleType = ImageView.ScaleType.FIT_CENTER
|
||||||
}
|
}
|
||||||
}, modifier = Modifier.fillMaxSize())
|
}, modifier = Modifier.fillMaxSize())
|
||||||
|
|||||||
@@ -152,18 +152,26 @@ 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) {
|
||||||
showEdit = false
|
showEdit = false
|
||||||
val g = Data.selected_group
|
val g = Data.selected_group
|
||||||
if (it && g != null)
|
if (it && g != null) {
|
||||||
Data.visits.reassignAllVisitedToGroup(g.key)
|
Data.visits.reassignAllVisitedToGroup(g.key)
|
||||||
|
Data.saveData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
@@ -179,7 +187,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 +200,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()
|
||||||
@@ -210,8 +218,10 @@ fun SettingsScreen(navController: NavHostController = settingsNav()) {
|
|||||||
deleteMode = true,
|
deleteMode = true,
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
val g = Data.selected_group
|
val g = Data.selected_group
|
||||||
if (g != null)
|
if (g != null) {
|
||||||
Data.visits.reassignAllVisitedToGroup(g.key)
|
Data.visits.reassignAllVisitedToGroup(g.key)
|
||||||
|
Data.saveData()
|
||||||
|
}
|
||||||
showDialog = false
|
showDialog = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -219,11 +229,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 +293,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 +319,7 @@ fun RegionalScreen() {
|
|||||||
regional= selected
|
regional= selected
|
||||||
prefs.edit {
|
prefs.edit {
|
||||||
putString(
|
putString(
|
||||||
context.getString(R.string.key_regional),
|
keyRegional,
|
||||||
regional
|
regional
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -332,12 +346,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 +368,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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -54,27 +54,51 @@ fun EditPlaceScreenPreview(){
|
|||||||
EditPlaceScreen(Group.EEE)
|
EditPlaceScreen(Group.EEE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun syncVisited(loc: GeoLoc?=World.WWW){
|
fun syncVisited(loc: GeoLoc?=World.WWW): Boolean {
|
||||||
|
var changed = false
|
||||||
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 })
|
val newState = if(itc.children.any { c -> Data.visits.getVisited(c) != NO_GROUP })
|
||||||
Data.visits.setVisited(itc, AUTO_GROUP)
|
AUTO_GROUP
|
||||||
else
|
else
|
||||||
Data.visits.setVisited(itc, NO_GROUP)
|
NO_GROUP
|
||||||
|
|
||||||
|
if (Data.visits.getVisited(itc) != newState) {
|
||||||
|
Data.visits.setVisited(itc, newState)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(Data.visits.getVisited(tt) in listOf(AUTO_GROUP,NO_GROUP)) {
|
if(Data.visits.getVisited(tt) in listOf(AUTO_GROUP,NO_GROUP)) {
|
||||||
if(tt.children.any { itc -> Data.visits.getVisited(itc) != NO_GROUP })
|
val newState = if(tt.children.any { itc -> Data.visits.getVisited(itc) != NO_GROUP })
|
||||||
Data.visits.setVisited(tt, AUTO_GROUP)
|
AUTO_GROUP
|
||||||
else
|
else
|
||||||
Data.visits.setVisited(tt, NO_GROUP)
|
NO_GROUP
|
||||||
|
|
||||||
|
if (Data.visits.getVisited(tt) != newState) {
|
||||||
|
Data.visits.setVisited(tt, newState)
|
||||||
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// Sync World from Continents
|
||||||
|
if (loc != null && Data.visits.getVisited(loc) in listOf(AUTO_GROUP, NO_GROUP)) {
|
||||||
|
val newState = if(loc.children.any { Data.visits.getVisited(it) != NO_GROUP })
|
||||||
|
AUTO_GROUP
|
||||||
|
else
|
||||||
|
NO_GROUP
|
||||||
|
if (Data.visits.getVisited(loc) != newState) {
|
||||||
|
Data.visits.setVisited(loc, newState)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun EditPlaceScreen(loc: GeoLoc, onExit:()->Unit={}) {
|
fun EditPlaceScreen(loc: GeoLoc, onExit:()->Unit={}) {
|
||||||
|
val visits by Data.visits.visitsFlow.collectAsState()
|
||||||
var showEdit by remember { mutableStateOf(false) }
|
var showEdit by remember { mutableStateOf(false) }
|
||||||
val tabs : SnapshotStateList<GeoLoc> = remember { mutableStateListOf(loc) }
|
val tabs : SnapshotStateList<GeoLoc> = remember { mutableStateListOf(loc) }
|
||||||
val ctx = LocalContext.current
|
val ctx = LocalContext.current
|
||||||
@@ -84,7 +108,12 @@ fun EditPlaceScreen(loc: GeoLoc, onExit:()->Unit={}) {
|
|||||||
selectedTab = tabs.lastIndex
|
selectedTab = tabs.lastIndex
|
||||||
}
|
}
|
||||||
SideEffect {
|
SideEffect {
|
||||||
syncVisited()
|
// visits is used to trigger sync whenever data changes
|
||||||
|
if (visits.isNotEmpty() || true) {
|
||||||
|
if (syncVisited()) {
|
||||||
|
Data.saveData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BackHandler {
|
BackHandler {
|
||||||
if (tabs.size > 1) tabs.removeAt(tabs.lastIndex)
|
if (tabs.size > 1) tabs.removeAt(tabs.lastIndex)
|
||||||
@@ -95,6 +124,7 @@ fun EditPlaceScreen(loc: GeoLoc, onExit:()->Unit={}) {
|
|||||||
showEdit = false
|
showEdit = false
|
||||||
if (it) {
|
if (it) {
|
||||||
Data.visits.setVisited(Data.selected_geoloc, NO_GROUP)
|
Data.visits.setVisited(Data.selected_geoloc, NO_GROUP)
|
||||||
|
syncVisited()
|
||||||
Data.saveData()
|
Data.saveData()
|
||||||
|
|
||||||
if (Data.selected_geoloc!=null && Data.selected_geoloc!!.children.any { itc-> Data.visits.getVisited(itc) != NO_GROUP }) {
|
if (Data.selected_geoloc!=null && Data.selected_geoloc!!.children.any { itc-> Data.visits.getVisited(itc) != NO_GROUP }) {
|
||||||
@@ -103,6 +133,7 @@ fun EditPlaceScreen(loc: GeoLoc, onExit:()->Unit={}) {
|
|||||||
}
|
}
|
||||||
if (Data.selected_group != null && Data.selected_geoloc != null) {
|
if (Data.selected_group != null && Data.selected_geoloc != null) {
|
||||||
Data.visits.setVisited(Data.selected_geoloc, Data.selected_group!!.key)
|
Data.visits.setVisited(Data.selected_geoloc, Data.selected_group!!.key)
|
||||||
|
syncVisited()
|
||||||
Data.saveData()
|
Data.saveData()
|
||||||
}
|
}
|
||||||
Data.selected_geoloc = null
|
Data.selected_geoloc = null
|
||||||
@@ -126,7 +157,6 @@ fun EditPlaceScreen(loc: GeoLoc, onExit:()->Unit={}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
@@ -145,12 +175,14 @@ fun EditPlaceScreen(loc: GeoLoc, onExit:()->Unit={}) {
|
|||||||
Data.visits.getVisited(itc)!= NO_GROUP } == true) AUTO_GROUP
|
Data.visits.getVisited(itc)!= NO_GROUP } == true) AUTO_GROUP
|
||||||
else NO_GROUP
|
else NO_GROUP
|
||||||
)
|
)
|
||||||
|
Data.saveData()
|
||||||
Data.selected_group = null
|
Data.selected_group = null
|
||||||
} else {
|
} else {
|
||||||
Data.selected_group = null
|
Data.selected_group = null
|
||||||
showEdit=true
|
showEdit=true
|
||||||
}
|
}
|
||||||
|
syncVisited()
|
||||||
|
Data.saveData()
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -43,14 +43,14 @@ object Data {
|
|||||||
if (groups.size() == 0) {
|
if (groups.size() == 0) {
|
||||||
groups.setGroup(DEFAULT_GROUP, "Visited",
|
groups.setGroup(DEFAULT_GROUP, "Visited",
|
||||||
ContextCompat.getColor(ctx, R.color.blue).toDrawable())
|
ContextCompat.getColor(ctx, R.color.blue).toDrawable())
|
||||||
saveData()
|
|
||||||
}
|
}
|
||||||
|
saveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveData() {
|
fun saveData() {
|
||||||
if(groups.id != visits.id) return
|
if(groups.id != visits.id) return
|
||||||
val id = groups.id
|
val id = groups.id
|
||||||
sharedPreferences.edit {
|
sharedPreferences.edit(commit=true) {
|
||||||
putString("groups_$id", groupsSerial.writeTo(groups))
|
putString("groups_$id", groupsSerial.writeTo(groups))
|
||||||
putString("visits_$id", visitsSerial.writeTo(visits))
|
putString("visits_$id", visitsSerial.writeTo(visits))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,59 @@ 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)
|
val old = groups[key]
|
||||||
_groupsFlow.value = grps.values.toList()
|
if (old != null && old.name == name && old.color.color == col.color) return
|
||||||
|
groups[key] = Group(key, name, col)
|
||||||
|
updateFlow()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteGroup(key: Int) {
|
fun deleteGroup(key: Int) {
|
||||||
grps.remove(key)
|
groups.remove(key)
|
||||||
_groupsFlow.value = grps.values.toList()
|
updateFlow()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateFlow() {
|
||||||
|
_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, "")
|
||||||
|
|||||||
@@ -19,24 +19,25 @@ class Visits(val id: Int, private val locs: HashMap<String, Int>) {
|
|||||||
val visitsFlow: StateFlow<Map<String,Int>> = _visitsFlow
|
val visitsFlow: StateFlow<Map<String,Int>> = _visitsFlow
|
||||||
|
|
||||||
fun setVisited(key: GeoLoc?, b: Int) {
|
fun setVisited(key: GeoLoc?, b: Int) {
|
||||||
if (key == null)
|
if (key == null || locs[key.code] == b)
|
||||||
return
|
return
|
||||||
_visitsFlow.value = _visitsFlow.value.toMutableMap().apply {
|
|
||||||
this[key.code] = b
|
|
||||||
}
|
|
||||||
locs[key.code] = b
|
locs[key.code] = b
|
||||||
|
updateFlow()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateFlow() {
|
||||||
|
_visitsFlow.value = locs.toMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteVisited(key: Int) {
|
fun deleteVisited(key: Int) {
|
||||||
val keysToDelete = locs
|
val keysToDelete = locs
|
||||||
.filter { it.value == key }
|
.filter { it.value == key }
|
||||||
.map { it.key }
|
.map { it.key }
|
||||||
_visitsFlow.value = _visitsFlow.value.toMutableMap().apply {
|
if (keysToDelete.isEmpty()) return
|
||||||
keysToDelete.forEach { this.remove(it)}
|
|
||||||
}
|
|
||||||
keysToDelete.forEach {
|
keysToDelete.forEach {
|
||||||
locs.remove(it)
|
locs.remove(it)
|
||||||
}
|
}
|
||||||
|
updateFlow()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getVisited(key: GeoLoc): Int {
|
fun getVisited(key: GeoLoc): Int {
|
||||||
@@ -60,13 +61,19 @@ class Visits(val id: Int, private val locs: HashMap<String, Int>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun reassignAllVisitedToGroup(group: Int) {
|
fun reassignAllVisitedToGroup(group: Int) {
|
||||||
|
var changed = false
|
||||||
val keys = locs.filter { (_, grp) ->
|
val keys = locs.filter { (_, grp) ->
|
||||||
grp !in listOf(NO_GROUP, AUTO_GROUP)
|
grp !in listOf(NO_GROUP, AUTO_GROUP)
|
||||||
}.keys
|
}.keys
|
||||||
keys.forEach {
|
keys.forEach {
|
||||||
|
if (locs[it] != group) {
|
||||||
locs[it] = group
|
locs[it] = group
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
updateFlow()
|
||||||
}
|
}
|
||||||
_visitsFlow.value = locs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
|||||||
50
app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
50
app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal 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>
|
||||||
@@ -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>
|
||||||
15
app/src/main/res/values-night/themes.xml
Normal file
15
app/src/main/res/values-night/themes.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="Theme.Beans" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<!-- Status bar color in dark mode -->
|
||||||
|
<item name="android:statusBarColor">@color/blue</item>
|
||||||
|
<!-- Window background in dark mode -->
|
||||||
|
<item name="android:windowBackground">@color/darkgray</item>
|
||||||
|
<item name="android:windowLightStatusBar">false</item>
|
||||||
|
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||||
|
|
||||||
|
<item name="colorPrimary">@color/blue</item>
|
||||||
|
<item name="colorPrimaryDark">@color/blue</item>
|
||||||
|
<item name="colorAccent">@color/blue</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
7
app/src/main/res/values-v21/themes.xml
Normal file
7
app/src/main/res/values-v21/themes.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="Theme.Beans" parent="android:Theme.Material.Light.NoActionBar">
|
||||||
|
<item name="android:statusBarColor">@color/blue</item>
|
||||||
|
<item name="android:windowBackground">@color/blue</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
16
app/src/main/res/values/themes.xml
Normal file
16
app/src/main/res/values/themes.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="Theme.Beans" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<!-- Status bar color -->
|
||||||
|
<item name="android:statusBarColor">@color/blue</item>
|
||||||
|
<!-- Window background - shows through statusBarsPadding() area -->
|
||||||
|
<item name="android:windowBackground">@color/blue</item>
|
||||||
|
<item name="android:windowLightStatusBar">false</item>
|
||||||
|
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||||
|
|
||||||
|
<!-- Optional: Primary colors for the theme -->
|
||||||
|
<item name="colorPrimary">@color/blue</item>
|
||||||
|
<item name="colorPrimaryDark">@color/blue</item>
|
||||||
|
<item name="colorAccent">@color/blue</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application' version '9.1.0' apply false
|
id 'com.android.application' version '9.2.1' apply false
|
||||||
id 'com.android.library' version '9.1.0' apply false
|
id 'com.android.library' version '9.2.1' apply false
|
||||||
id 'org.jetbrains.kotlin.android' version '2.3.20' apply false
|
id 'org.jetbrains.kotlin.android' version '2.4.0' apply false
|
||||||
}
|
}
|
||||||
60
genenum.js
60
genenum.js
@@ -1,8 +1,10 @@
|
|||||||
#!/bin/node
|
#!/bin/node
|
||||||
|
|
||||||
import {readFileSync, existsSync} from 'fs'
|
import {readFileSync,writeFileSync,existsSync} from 'fs'
|
||||||
import area from '@turf/area'
|
import area from '@turf/area'
|
||||||
import * as turf from '@turf/turf'
|
import * as turf from '@turf/turf'
|
||||||
|
import * as path from 'path';
|
||||||
|
import { JSDOM } from 'jsdom';
|
||||||
|
|
||||||
|
|
||||||
const countries =
|
const countries =
|
||||||
@@ -24,7 +26,7 @@ const countries =
|
|||||||
|
|
||||||
|
|
||||||
const groups = {
|
const groups = {
|
||||||
"EEE":["ALB","AND","AUT","BLR","BEL","BIH","BGR","HRV","CYP","CZE","DNK","EST","FIN","FRA","DEU","GRC","HUN","ISL","IRL","ITA","KAZ","XKO","LVA","LIE","LTU","LUX","MLT","MDA","MCO","MNE","NLD","MKD","NOR","POL","PRT","ROU","RUS","SMR","SRB","SVK","SVN","ESP","SWE","CHE","UKR","GBR","VAT","XAD"],
|
"EEE":["ALA","ALB","AND","AUT","BLR","BEL","BIH","BGR","HRV","CYP","CZE","DNK","EST","FIN","FRA","DEU","GIB","GGY","GRC","HUN","ISL","IRL","IMN","JEY","ITA","KAZ","XKO","LVA","LIE","LTU","LUX","MLT","MDA","MCO","MNE","NLD","MKD","NOR","SJM","POL","PRT","ROU","RUS","SMR","SRB","SVK","SVN","ESP","SWE","CHE","UKR","GBR","VAT","XAD"],
|
||||||
"ABB":["AFG","ARM","AZE","BHR","BGD","BTN","BRN","KHM","CHN","GEO","HKG","IND","IDN","IRN","IRQ","ISR","JPN","JOR","KWT","KGZ","LAO","LBN","MAC","MYS","MDV","MNG","MMR","NPL","PRK","OMN","PAK","PSE","PHL","QAT","SAU","SGP","KOR","LKA","SYR","TWN","TJK","THA","TLS","TUR","TKM","ARE","UZB","VNM","YEM","ZNC"],
|
"ABB":["AFG","ARM","AZE","BHR","BGD","BTN","BRN","KHM","CHN","GEO","HKG","IND","IDN","IRN","IRQ","ISR","JPN","JOR","KWT","KGZ","LAO","LBN","MAC","MYS","MDV","MNG","MMR","NPL","PRK","OMN","PAK","PSE","PHL","QAT","SAU","SGP","KOR","LKA","SYR","TWN","TJK","THA","TLS","TUR","TKM","ARE","UZB","VNM","YEM","ZNC"],
|
||||||
"FFF":["DZA","AGO","BDI","BEN","BWA","BFA","BDI","CPV","CMR","CAF","TCD","COM","COG","COD","CIV","DJI","EGY","GNQ","ERI","SWZ","ETH","GAB","GMB","GHA","GIN","GNB","KEN","LSO","LBR","LBY","MDG","MWI","MLI","MRT","MUS","MYT","MAR","MOZ","NAM","NER","NGA","COD","REU","RWA","STP","SEN","SYC","SLE","SOM","ZAF","SSD","SHN","SDN","TZA","TGO","TUN","UGA","COD","ZMB","ZWE","ESH"],
|
"FFF":["DZA","AGO","BDI","BEN","BWA","BFA","BDI","CPV","CMR","CAF","TCD","COM","COG","COD","CIV","DJI","EGY","GNQ","ERI","SWZ","ETH","GAB","GMB","GHA","GIN","GNB","KEN","LSO","LBR","LBY","MDG","MWI","MLI","MRT","MUS","MYT","MAR","MOZ","NAM","NER","NGA","COD","REU","RWA","STP","SEN","SYC","SLE","SOM","ZAF","SSD","SHN","SDN","TZA","TGO","TUN","UGA","COD","ZMB","ZWE","ESH"],
|
||||||
"NNN":["ABW","AIA","ATG","BHS","BRB","BLZ","BMU","VGB","CAN","CYM","CRI","CUB","CUW","DMA","DOM","SLV","GRL","GRD","GLP","GTM","HTI","HND","JAM","MTQ","MEX","MSR","ANT","CUW","NIC","PAN","PRI","KNA","LCA","MAF","SPM","VCT","TTO","TCA","USA","XCL"],
|
"NNN":["ABW","AIA","ATG","BHS","BRB","BLZ","BMU","VGB","CAN","CYM","CRI","CUB","CUW","DMA","DOM","SLV","GRL","GRD","GLP","GTM","HTI","HND","JAM","MTQ","MEX","MSR","ANT","CUW","NIC","PAN","PRI","KNA","LCA","MAF","SPM","VCT","TTO","TCA","USA","XCL"],
|
||||||
@@ -32,7 +34,6 @@ const groups = {
|
|||||||
"UUU":["ASM","AUS","COK","FJI","PYF","GUM","KIR","MHL","FSM","NRU","NCL","NZL","NIU","NFK","MNP","PLW","PNG","PCN","SLB","TKL","TON","TUV","VUT","WLF"],
|
"UUU":["ASM","AUS","COK","FJI","PYF","GUM","KIR","MHL","FSM","NRU","NCL","NZL","NIU","NFK","MNP","PLW","PNG","PCN","SLB","TKL","TON","TUV","VUT","WLF"],
|
||||||
"XXX":[
|
"XXX":[
|
||||||
"ATA", // Antarctica: not in any other region
|
"ATA", // Antarctica: not in any other region
|
||||||
"ALA",// Åland Islands: an autonomous region of Finland, but not a member of the EU or UN
|
|
||||||
"BES",// Bonaire, Sint Eustatius and Saba: special municipalities of the Netherlands in the Caribbean
|
"BES",// Bonaire, Sint Eustatius and Saba: special municipalities of the Netherlands in the Caribbean
|
||||||
"BVT",// Bouvet Island: an uninhabited territory of Norway in the South Atlantic
|
"BVT",// Bouvet Island: an uninhabited territory of Norway in the South Atlantic
|
||||||
"IOT",// British Indian Ocean Territory: a British overseas territory in the Indian Ocean
|
"IOT",// British Indian Ocean Territory: a British overseas territory in the Indian Ocean
|
||||||
@@ -40,16 +41,11 @@ const groups = {
|
|||||||
"CCK",// Cocos (Keeling) Islands: an Australian external territory in the Indian Ocean
|
"CCK",// Cocos (Keeling) Islands: an Australian external territory in the Indian Ocean
|
||||||
"FRO",// Faroe Islands: an autonomous region of Denmark
|
"FRO",// Faroe Islands: an autonomous region of Denmark
|
||||||
"ATF",// French Southern and Antarctic Lands: a territory of France located in the southern Indian Ocean
|
"ATF",// French Southern and Antarctic Lands: a territory of France located in the southern Indian Ocean
|
||||||
"GIB",// Gibraltar: a British overseas territory located at the southern tip of the Iberian Peninsula
|
|
||||||
"GGY",// Guernsey: a British Crown dependency in the English Channel
|
|
||||||
"HMD",// Heard Island and McDonald Islands: an uninhabited Australian external territory in the southern Indian Ocean
|
"HMD",// Heard Island and McDonald Islands: an uninhabited Australian external territory in the southern Indian Ocean
|
||||||
"IMN",// Isle of Man: a British Crown dependency located in the Irish Sea
|
|
||||||
"JEY",// Jersey: a British Crown dependency located in the English Channel
|
|
||||||
"BLM",// Saint Barthélemy: an overseas collectivity of France in the Caribbean
|
"BLM",// Saint Barthélemy: an overseas collectivity of France in the Caribbean
|
||||||
"WSM",// Samoa: an independent island nation in the South Pacific
|
"WSM",// Samoa: an independent island nation in the South Pacific
|
||||||
"SXM",// Sint Maarten: a constituent country of the Kingdom of the Netherlands in the Caribbean
|
"SXM",// Sint Maarten: a constituent country of the Kingdom of the Netherlands in the Caribbean
|
||||||
"SGS",// South Georgia and the South Sandwich Islands: a British overseas territory in the southern Atlantic Ocean
|
"SGS",// South Georgia and the South Sandwich Islands: a British overseas territory in the southern Atlantic Ocean
|
||||||
"SJM",// Svalbard and Jan Mayen: an archipelago administered by Norway
|
|
||||||
"UMI",// United States Minor Outlying Islands: a collection of nine insular areas of the United States
|
"UMI",// United States Minor Outlying Islands: a collection of nine insular areas of the United States
|
||||||
"VIR",// United States Virgin Islands: an unincorporated territory of the United States in the Caribbean
|
"VIR",// United States Virgin Islands: an unincorporated territory of the United States in the Caribbean
|
||||||
]
|
]
|
||||||
@@ -166,4 +162,52 @@ async function run(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function fixSvg(svgPath) {
|
||||||
|
const countryToRegion = {};
|
||||||
|
for (const [region, countries] of Object.entries(groups)) {
|
||||||
|
countries.forEach(country => countryToRegion[country] = region);
|
||||||
|
}
|
||||||
|
const absoluteInputPath = path.resolve(svgPath);
|
||||||
|
if (!existsSync(absoluteInputPath)) {
|
||||||
|
throw new Error(`Input file not found at: ${absoluteInputPath}`);
|
||||||
|
}
|
||||||
|
const svgContent = readFileSync(absoluteInputPath, 'utf8');
|
||||||
|
const dom = new JSDOM(svgContent, { contentType: 'image/svg+xml' });
|
||||||
|
const document = dom.window.document;
|
||||||
|
const svgRoot = document.querySelector('svg');
|
||||||
|
if (!svgRoot) {
|
||||||
|
throw new Error("Invalid or empty SVG structure encountered.");
|
||||||
|
}
|
||||||
|
if (svgRoot.getAttribute('data-processed') === 'true') {
|
||||||
|
console.log(`Skipping: File at "${svgPath}" has already been processed.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const elementGroups = Array.from(svgRoot.querySelectorAll('g'));
|
||||||
|
elementGroups.forEach(group => {
|
||||||
|
const currentId = group.getAttribute('id') || '';
|
||||||
|
const baseIsoCode = currentId.replace(/\d+$/, '');
|
||||||
|
const regionKey = countryToRegion[baseIsoCode] || 'XXXX';
|
||||||
|
let regionGroup = svgRoot.querySelector(`g[id="${regionKey}"]`);
|
||||||
|
if (!regionGroup) {
|
||||||
|
regionGroup = document.createElementNS('http://w3.org', 'g');
|
||||||
|
regionGroup.setAttribute('id', regionKey);
|
||||||
|
svgRoot.appendChild(regionGroup);
|
||||||
|
}
|
||||||
|
regionGroup.appendChild(group);
|
||||||
|
});
|
||||||
|
svgRoot.setAttribute('data-processed', 'true');
|
||||||
|
const absoluteOutputPath = path.resolve(svgPath);
|
||||||
|
let cleanXmlString = svgRoot.outerHTML;
|
||||||
|
cleanXmlString = cleanXmlString.replace(/[\r\n]+/g, '');
|
||||||
|
cleanXmlString = cleanXmlString.replace(/xmlns="http:\/\/w3\.org"\s?/g, '');
|
||||||
|
cleanXmlString = cleanXmlString.replace(/xmlns="http:\/\/www\.w3\.org\/2000\/svg"\s?/g, '');
|
||||||
|
cleanXmlString = cleanXmlString.replace('<svg', '<svg xmlns="http://www.w3.org/2000/svg"');
|
||||||
|
writeFileSync(absoluteOutputPath, cleanXmlString, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
run()
|
run()
|
||||||
|
fixSvg("./app/src/main/assets/loxim01.svg")
|
||||||
|
fixSvg("./app/src/main/assets/webmercator01.svg")
|
||||||
|
fixSvg("./app/src/main/assets/aeqd01.svg")
|
||||||
54
gensvg.sh
54
gensvg.sh
@@ -8,10 +8,11 @@ GADM_BASEPATH="https://geodata.ucdavis.edu/gadm"
|
|||||||
mapshaper="./node_modules/mapshaper/bin/mapshaper"
|
mapshaper="./node_modules/mapshaper/bin/mapshaper"
|
||||||
ATA_URL="https://media.githubusercontent.com/media/wmgeolab/geoBoundaries/905b0baf5f4fb3b9ccf45293647dcacdb2b799d4/releaseData/gbOpen/ATA/ADM0/geoBoundaries-ATA-ADM0_simplified.geojson"
|
ATA_URL="https://media.githubusercontent.com/media/wmgeolab/geoBoundaries/905b0baf5f4fb3b9ccf45293647dcacdb2b799d4/releaseData/gbOpen/ATA/ADM0/geoBoundaries-ATA-ADM0_simplified.geojson"
|
||||||
|
|
||||||
|
# Caspian Sea: "XCA"
|
||||||
countries=(
|
countries=(
|
||||||
"AFG" "XAD" "ALA" "ALB" "DZA" "ASM" "AND" "AGO" "AIA" "ATG" "ARG" "ARM" "ABW" "AUS" "AUT" "AZE"
|
"AFG" "XAD" "ALA" "ALB" "DZA" "ASM" "AND" "AGO" "AIA" "ATG" "ARG" "ARM" "ABW" "AUS" "AUT" "AZE"
|
||||||
"BHS" "BHR" "BGD" "BRB" "BLR" "BEL" "BLZ" "BEN" "BMU" "BTN" "BOL" "BES" "BIH" "BWA" "BVT" "BRA" "IOT" "VGB" "BRN" "BGR" "BFA" "BDI" "KHM"
|
"BHS" "BHR" "BGD" "BRB" "BLR" "BEL" "BLZ" "BEN" "BMU" "BTN" "BOL" "BES" "BIH" "BWA" "BVT" "BRA" "IOT" "VGB" "BRN" "BGR" "BFA" "BDI" "KHM"
|
||||||
"CMR" "CAN" "CPV" "XCA" "CYM" "CAF" "TCD" "CHL" "CHN" "CXR" "XCL" "CCK" "COL" "COM" "COK" "CRI" "CIV" "HRV" "CUB" "CUW" "CYP" "CZE" "COD"
|
"CMR" "CAN" "CPV" "CYM" "CAF" "TCD" "CHL" "CHN" "CXR" "XCL" "CCK" "COL" "COM" "COK" "CRI" "CIV" "HRV" "CUB" "CUW" "CYP" "CZE" "COD"
|
||||||
"DNK" "DJI" "DMA" "DOM" "ECU" "EGY" "SLV" "GNQ" "ERI" "EST" "ETH" "FLK" "FRO" "FJI" "FIN" "FRA" "GUF" "PYF" "ATF"
|
"DNK" "DJI" "DMA" "DOM" "ECU" "EGY" "SLV" "GNQ" "ERI" "EST" "ETH" "FLK" "FRO" "FJI" "FIN" "FRA" "GUF" "PYF" "ATF"
|
||||||
"GAB" "GMB" "GEO" "DEU" "GHA" "GIB" "GRC" "GRL" "GRD" "GLP" "GUM" "GTM" "GGY" "GIN" "GNB" "GUY" "HTI" "HMD" "HND" "HUN"
|
"GAB" "GMB" "GEO" "DEU" "GHA" "GIB" "GRC" "GRL" "GRD" "GLP" "GUM" "GTM" "GGY" "GIN" "GNB" "GUY" "HTI" "HMD" "HND" "HUN"
|
||||||
"ISL" "IND" "IDN" "IRN" "IRQ" "IRL" "IMN" "ISR" "ITA" "JAM" "JPN" "JEY" "JOR" "KAZ" "KEN" "KIR" "XKO" "KWT" "KGZ"
|
"ISL" "IND" "IDN" "IRN" "IRQ" "IRL" "IMN" "ISR" "ITA" "JAM" "JPN" "JEY" "JOR" "KAZ" "KEN" "KIR" "XKO" "KWT" "KGZ"
|
||||||
@@ -115,6 +116,47 @@ toSVG_1() {
|
|||||||
"$mapshaper" -i combine-files ${input_files[@]} -proj aeqd +lat_0=90 -simplify 0.005 weighted keep-shapes resolution=1200x1200 -o ./app/src/main/assets/aeqd1.svg svg-data=GID_0,COUNTRY,GID,NAME id-field=GID
|
"$mapshaper" -i combine-files ${input_files[@]} -proj aeqd +lat_0=90 -simplify 0.005 weighted keep-shapes resolution=1200x1200 -o ./app/src/main/assets/aeqd1.svg svg-data=GID_0,COUNTRY,GID,NAME id-field=GID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generate_svg_map() {
|
||||||
|
local OUT_FILE="$1" # First argument: Output destination path
|
||||||
|
local PROJ_ARGS="$2" # Second argument: Projection parameters
|
||||||
|
shift 2 # Remove the first two arguments, leaving only the files
|
||||||
|
local FILES_TO_RUN=("$@") # Capture all remaining arguments as the file array
|
||||||
|
|
||||||
|
echo "Generating: $OUT_FILE using projection [$PROJ_ARGS]"
|
||||||
|
echo "Processing ${#FILES_TO_RUN[@]} files..."
|
||||||
|
|
||||||
|
local JS_PIPELINE_LOGIC="
|
||||||
|
const conflicts = {
|
||||||
|
'Z01': 'IND', 'Z04': 'IND', 'Z05': 'IND', 'Z07': 'IND', 'Z09': 'IND',
|
||||||
|
'Z02': 'CHN', 'Z03': 'CHN','Z08': 'CHN',
|
||||||
|
'Z06': 'PAK'
|
||||||
|
};
|
||||||
|
let rawCode = GID_0 || 'UNK';
|
||||||
|
let cCode = conflicts[rawCode] ? conflicts[rawCode] : rawCode;
|
||||||
|
let isGidMissing = (!GID || GID === 'undefined' || GID === 'null' || GID === '');
|
||||||
|
if (isGidMissing) {
|
||||||
|
COUNTRY_GROUP = cCode+'2';
|
||||||
|
} else {
|
||||||
|
COUNTRY_GROUP = cCode+'1';
|
||||||
|
}
|
||||||
|
"
|
||||||
|
|
||||||
|
"$mapshaper" -i "${FILES_TO_RUN[@]}" combine-files \
|
||||||
|
-snap \
|
||||||
|
-merge-layers force\
|
||||||
|
-proj $PROJ_ARGS densify \
|
||||||
|
-simplify 2% weighted keep-shapes \
|
||||||
|
-filter-islands min-area=0 \
|
||||||
|
-sort 'this.area' ascending \
|
||||||
|
-each "$JS_PIPELINE_LOGIC" \
|
||||||
|
-split COUNTRY_GROUP \
|
||||||
|
-o "$OUT_FILE" \
|
||||||
|
format=svg \
|
||||||
|
id-field=GID \
|
||||||
|
precision=0.1 \
|
||||||
|
target=*
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
toSVG_01() {
|
toSVG_01() {
|
||||||
input_files=()
|
input_files=()
|
||||||
@@ -136,9 +178,9 @@ toSVG_01() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
"$mapshaper" -i combine-files ${input_files[@]} snap -proj loxim densify -simplify 0.001 weighted keep-shapes -o ./app/src/main/assets/loxim01.svg svg-data=GID_0,COUNTRY,GID,NAME id-field=GID
|
generate_svg_map "./app/src/main/assets/loxim01.svg" "loxim" "${input_files[@]}"
|
||||||
"$mapshaper" -i combine-files ${input_files[@]} snap -proj webmercator densify -simplify 0.001 weighted keep-shapes -o ./app/src/main/assets/webmercator01.svg svg-data=GID_0,COUNTRY,GID,NAME id-field=GID
|
generate_svg_map "./app/src/main/assets/webmercator01.svg" "webmercator" "${input_files[@]}"
|
||||||
"$mapshaper" -i combine-files ${input_files[@]} snap -proj aeqd +lat_0=90 densify -simplify 0.001 weighted keep-shapes -o ./app/src/main/assets/aeqd01.svg svg-data=GID_0,COUNTRY,GID,NAME id-field=GID
|
generate_svg_map "./app/src/main/assets/aeqd01.svg" "aeqd +lat_0=90" "${input_files[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
do_1() {
|
do_1() {
|
||||||
@@ -153,10 +195,12 @@ do_0() {
|
|||||||
do
|
do
|
||||||
download_0 "$country"
|
download_0 "$country"
|
||||||
done
|
done
|
||||||
wget -q -O "./temp/1/ATA.json" "$ATA_URL"
|
wget -q -O "./temp/0/ATA.json" "$ATA_URL"
|
||||||
}
|
}
|
||||||
# do_0
|
# do_0
|
||||||
# do_1
|
# do_1
|
||||||
# toSVG_0
|
# toSVG_0
|
||||||
# toSVG_1
|
# toSVG_1
|
||||||
toSVG_01
|
toSVG_01
|
||||||
|
|
||||||
|
#CUW, CCK, XCL, CXR, IOT, BVT, ABW, FLK, GIB, HMD, KIR, SXM, MDV, MCO, NIU, NFK, PCN, MAF, SGS, VAT
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
6
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,8 +1,10 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
distributionSha256Sum=bafc141b619ad6350fd975fc903156dd5c151998cc8b058e8c1044ab5f7b031f
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
|
retries=0
|
||||||
|
retryBackOffMs=500
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
2
gradlew
vendored
2
gradlew
vendored
@@ -57,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
|||||||
31
gradlew.bat
vendored
31
gradlew.bat
vendored
@@ -23,8 +23,8 @@
|
|||||||
@rem
|
@rem
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
setlocal EnableExtensions
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
@@ -51,7 +51,7 @@ echo. 1>&2
|
|||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
"%COMSPEC%" /c exit 1
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
:findJavaFromJavaHome
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
@@ -65,7 +65,7 @@ echo. 1>&2
|
|||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
"%COMSPEC%" /c exit 1
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
@@ -73,21 +73,10 @@ goto fail
|
|||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
|
||||||
|
@rem which allows us to clear the local environment before executing the java command
|
||||||
|
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
|
||||||
|
|
||||||
:end
|
:exitWithErrorLevel
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||||
|
|
||||||
:fail
|
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
|
||||||
rem the _cmd.exe /c_ return code!
|
|
||||||
set EXIT_CODE=%ERRORLEVEL%
|
|
||||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
|
||||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
|
||||||
exit /b %EXIT_CODE%
|
|
||||||
|
|
||||||
:mainEnd
|
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
|
||||||
|
|
||||||
:omega
|
|
||||||
|
|||||||
@@ -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.6.79"
|
"mapshaper": "^0.7.22"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user