Compare commits
19 Commits
1.3.1
...
d78ef0b38b
| Author | SHA1 | Date | |
|---|---|---|---|
|
d78ef0b38b
|
|||
|
30eb5657ce
|
|||
|
4085149e88
|
|||
| 1788d734d2 | |||
| b53215e4ba | |||
| df19f9640f | |||
| 31aa9630d0 | |||
| b1019aeb0b | |||
| 63aa14e31a | |||
|
2d87507fa4
|
|||
| bb8dc15554 | |||
| b3369f6390 | |||
| 46df3b5dbe | |||
| 3ab93c7342 | |||
| a8c4529ee8 | |||
| e7a3f09547 | |||
| 579cf75ca2 | |||
| 121b34c034 | |||
| 97014e9454 |
+1
-1
@@ -15,7 +15,7 @@ android {
|
||||
applicationId 'net.helcel.beans'
|
||||
minSdk = 28
|
||||
targetSdk = 37
|
||||
versionName "1.3b"
|
||||
versionName "1.5"
|
||||
versionCode project.hasProperty('VERSION_CODE') ? project.property('VERSION_CODE').toInteger() : 1
|
||||
}
|
||||
signingConfigs {
|
||||
|
||||
@@ -14,7 +14,6 @@ import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -22,10 +21,11 @@ import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.Percent
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
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.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
@@ -43,8 +43,8 @@ import net.helcel.beans.svg.SVGWrapper
|
||||
|
||||
class MainScreen : ComponentActivity() {
|
||||
|
||||
private lateinit var psvg: SVGWrapper
|
||||
private lateinit var css: CSSWrapper
|
||||
private var psvg by mutableStateOf<SVGWrapper?>(null)
|
||||
private var css by mutableStateOf<CSSWrapper?>(null)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -53,14 +53,19 @@ class MainScreen : ComponentActivity() {
|
||||
Data.loadData(this, Int.MIN_VALUE)
|
||||
GeoLocImporter.importStates(this)
|
||||
|
||||
refreshProjection()
|
||||
|
||||
setContent {
|
||||
SysTheme {
|
||||
val currentPsvg = psvg
|
||||
val currentCss = css
|
||||
if (currentPsvg != null && currentCss != null) {
|
||||
Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.primary).statusBarsPadding(),) {
|
||||
AppNavHost(psvg, css)
|
||||
AppNavHost(currentPsvg, currentCss)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
refreshProjection()
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -105,19 +110,25 @@ class MainScreen : ComponentActivity() {
|
||||
@Composable
|
||||
fun MapScreen(psvg: SVGWrapper, css: CSSWrapper) {
|
||||
Box {
|
||||
val cssContent = css.get()
|
||||
val drawable = remember(psvg, css, cssContent) {
|
||||
val opt: RenderOptions = RenderOptions.create()
|
||||
opt.css(css.get())
|
||||
val drawable = remember(psvg, css) {
|
||||
opt.css(cssContent)
|
||||
PictureDrawable(psvg.get()?.renderToPicture(opt))
|
||||
}
|
||||
AndroidView(factory = { ctx ->
|
||||
AndroidView(
|
||||
factory = { ctx ->
|
||||
PhotoView(ctx).apply {
|
||||
setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null)
|
||||
setImageDrawable(drawable)
|
||||
maximumScale = 32f
|
||||
maximumScale = 64f
|
||||
scaleType = ImageView.ScaleType.FIT_CENTER
|
||||
}
|
||||
}, modifier = Modifier.fillMaxSize())
|
||||
},
|
||||
update = { view ->
|
||||
view.setImageDrawable(drawable)
|
||||
},
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,23 +48,22 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.preference.PreferenceManager
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.countries.GeoLocImporter
|
||||
import net.helcel.beans.helper.Settings
|
||||
import androidx.core.content.edit
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.activity.sub.AboutScreen
|
||||
import net.helcel.beans.activity.sub.EditPlaceColorDialog
|
||||
import net.helcel.beans.activity.sub.EditPlaceDialog
|
||||
import net.helcel.beans.activity.sub.LicenseScreen
|
||||
import net.helcel.beans.countries.GeoLocImporter
|
||||
import net.helcel.beans.helper.Data
|
||||
import net.helcel.beans.helper.Settings
|
||||
|
||||
@Composable
|
||||
fun SysTheme(
|
||||
@@ -156,64 +155,23 @@ fun SettingsScreen(navController: NavHostController = settingsNav()) {
|
||||
val defaultTheme = stringResource(R.string.system)
|
||||
val keyProjection = stringResource(R.string.key_projection)
|
||||
val keyGroup = stringResource(R.string.key_group)
|
||||
val keyRegional = stringResource(R.string.key_regional)
|
||||
val keyRegionalStats = stringResource(R.string.key_regional_stats)
|
||||
val offString = stringResource(R.string.off)
|
||||
|
||||
var showEdit by remember { mutableStateOf(false) }
|
||||
val onString = stringResource(R.string.on)
|
||||
|
||||
var theme by remember { mutableStateOf(prefs.getString(keyTheme, defaultTheme)!!) }
|
||||
var projection by remember { mutableStateOf(prefs.getString(keyProjection, "default")!!) }
|
||||
var groups by remember { mutableStateOf(prefs.getString(keyGroup,offString)!!) }
|
||||
var groups by remember { mutableStateOf(prefs.getString(keyGroup, offString)!!) }
|
||||
var regional by remember { mutableStateOf(prefs.getString(keyRegional, offString)!!) }
|
||||
var regionalStats by remember { mutableStateOf(prefs.getString(keyRegionalStats, offString)!!) }
|
||||
|
||||
if(showEdit)
|
||||
EditPlaceDialog(true) {
|
||||
showEdit = false
|
||||
val g = Data.selected_group
|
||||
if (it && g != null) {
|
||||
Data.visits.reassignAllVisitedToGroup(g.key)
|
||||
Data.saveData()
|
||||
}
|
||||
}
|
||||
var showGroupDialog by remember { mutableStateOf(false) }
|
||||
var showRegionalDialog by remember { mutableStateOf(false) }
|
||||
var showLoad by remember { mutableStateOf(false) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
.background(MaterialTheme.colors.background)
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
"Theme", style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
)
|
||||
MultiPreference(arrayOf(stringResource(R.string.system),stringResource(R.string.light),stringResource(R.string.dark)), theme) { newTheme ->
|
||||
theme = newTheme
|
||||
prefs.edit { putString(keyTheme, newTheme) }
|
||||
}
|
||||
HorizontalDivider()
|
||||
}
|
||||
item {
|
||||
Text(
|
||||
"Map Projection",
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
MultiPreference(arrayOf(stringResource(R.string.mercator), stringResource(R.string.azimuthalequidistant)), projection) { newProj ->
|
||||
projection = newProj
|
||||
prefs.edit { putString(keyProjection, newProj) }
|
||||
Settings.refreshProjection()
|
||||
}
|
||||
HorizontalDivider()
|
||||
}
|
||||
item {
|
||||
Text(
|
||||
"Groups",
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
var showDialog by remember{mutableStateOf(false)}
|
||||
if(showDialog){
|
||||
if (showGroupDialog) {
|
||||
EditPlaceColorDialog(
|
||||
deleteMode = true,
|
||||
onDismiss = {
|
||||
@@ -222,88 +180,11 @@ fun SettingsScreen(navController: NavHostController = settingsNav()) {
|
||||
Data.visits.reassignAllVisitedToGroup(g.key)
|
||||
Data.saveData()
|
||||
}
|
||||
showDialog = false
|
||||
showGroupDialog = false
|
||||
})
|
||||
}
|
||||
MultiPreference(
|
||||
arrayOf(stringResource(R.string.on), stringResource(R.string.off)),
|
||||
groups
|
||||
) { key ->
|
||||
if (key == offString) {
|
||||
showDialog=true
|
||||
}
|
||||
groups = key
|
||||
prefs.edit { putString(keyGroup, key) }
|
||||
}
|
||||
HorizontalDivider()
|
||||
}
|
||||
item {
|
||||
Text(
|
||||
text = "Regional",
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
modifier = Modifier
|
||||
.padding(top = 16.dp)
|
||||
.clickable(onClick = {}),
|
||||
)
|
||||
RegionalScreen()
|
||||
HorizontalDivider()
|
||||
}
|
||||
item{
|
||||
val launcher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.OpenDocument(),
|
||||
onResult = { uri -> Data.doImport(context, uri) }
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Button(onClick = {
|
||||
launcher.launch(arrayOf("*/*"))
|
||||
}, modifier = Modifier
|
||||
.fillMaxWidth(fraction = 0.4f)
|
||||
.padding(vertical = 8.dp)) {
|
||||
Text("Import")
|
||||
}
|
||||
Spacer(
|
||||
modifier = Modifier.fillMaxWidth(0.4f)
|
||||
)
|
||||
Button(onClick = {
|
||||
Data.doExport(context)
|
||||
}, modifier = Modifier
|
||||
.fillMaxWidth(fraction = 1f)
|
||||
.padding(vertical = 8.dp)) {
|
||||
Text("Export")
|
||||
}
|
||||
}
|
||||
HorizontalDivider()
|
||||
}
|
||||
item {
|
||||
PreferenceButton("Licenses") {
|
||||
if (navController.currentDestination?.route != "licenses")
|
||||
navController.navigate("licenses")
|
||||
}
|
||||
PreferenceButton("About") {
|
||||
if (navController.currentDestination?.route != "about")
|
||||
navController.navigate("about")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RegionalScreen() {
|
||||
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)
|
||||
var selected by remember { mutableStateOf(prefs.getString(keyRegional, offString)!!)}
|
||||
var regional by remember{ mutableStateOf(prefs.getString(keyRegional, offString)!!)}
|
||||
var showDialog by remember{mutableStateOf(false)}
|
||||
var showLoad by remember{mutableStateOf(false)}
|
||||
|
||||
if(showDialog)
|
||||
if (showRegionalDialog) {
|
||||
Dialog(
|
||||
content = {
|
||||
Column(
|
||||
@@ -312,26 +193,27 @@ fun RegionalScreen() {
|
||||
MaterialTheme.colors.background,
|
||||
RoundedCornerShape(corner = CornerSize(16.dp))
|
||||
)
|
||||
.padding(16.dp),){
|
||||
Text(style=MaterialTheme.typography.caption, text= stringResource(R.string.delete_regions))
|
||||
.padding(16.dp),
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.typography.caption,
|
||||
text = stringResource(R.string.delete_regions)
|
||||
)
|
||||
Button(onClick = {
|
||||
GeoLocImporter.clearStates()
|
||||
regional= selected
|
||||
prefs.edit {
|
||||
putString(
|
||||
keyRegional,
|
||||
regional
|
||||
)
|
||||
}
|
||||
showDialog=false
|
||||
}){
|
||||
regional = offString
|
||||
prefs.edit { putString(keyRegional, offString) }
|
||||
showRegionalDialog = false
|
||||
}) {
|
||||
Text(stringResource(R.string.ok))
|
||||
}
|
||||
}
|
||||
},
|
||||
onDismissRequest = { showDialog=false }
|
||||
onDismissRequest = { showRegionalDialog = false }
|
||||
)
|
||||
if(showLoad){
|
||||
}
|
||||
|
||||
if (showLoad) {
|
||||
Dialog(
|
||||
content = {
|
||||
CircularProgressIndicator(
|
||||
@@ -343,41 +225,153 @@ fun RegionalScreen() {
|
||||
onDismissRequest = {}
|
||||
)
|
||||
}
|
||||
val scope = rememberCoroutineScope()
|
||||
MultiPreference(arrayOf(stringResource(R.string.on),stringResource(R.string.off)),regional) { key ->
|
||||
when (key) {
|
||||
offString -> { showDialog=true
|
||||
selected=key
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
.background(MaterialTheme.colors.background)
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
stringResource(R.string.key_theme),
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
)
|
||||
MultiPreference(
|
||||
arrayOf(
|
||||
stringResource(R.string.system),
|
||||
stringResource(R.string.light),
|
||||
stringResource(R.string.dark)
|
||||
), theme
|
||||
) { newTheme ->
|
||||
theme = newTheme
|
||||
prefs.edit { putString(keyTheme, newTheme) }
|
||||
}
|
||||
onString -> {
|
||||
regional = key
|
||||
prefs.edit { putString(keyRegional, key) }
|
||||
showLoad=true
|
||||
HorizontalDivider()
|
||||
}
|
||||
item {
|
||||
Text(
|
||||
stringResource(R.string.key_projection),
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
MultiPreference(
|
||||
arrayOf(stringResource(R.string.mercator), stringResource(R.string.azimuthalequidistant)),
|
||||
projection
|
||||
) { newProj ->
|
||||
projection = newProj
|
||||
prefs.edit { putString(keyProjection, newProj) }
|
||||
Settings.refreshProjection()
|
||||
}
|
||||
HorizontalDivider()
|
||||
}
|
||||
item {
|
||||
SettingSwitch(
|
||||
label = stringResource(R.string.key_group),
|
||||
subtitle = stringResource(R.string.key_group_desc),
|
||||
isChecked = groups == onString,
|
||||
onCheckedChange = { isChecked ->
|
||||
if (!isChecked) {
|
||||
showGroupDialog = true
|
||||
} else {
|
||||
groups = onString
|
||||
prefs.edit { putString(keyGroup, onString) }
|
||||
}
|
||||
}
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
item {
|
||||
SettingSwitch(
|
||||
label = stringResource(R.string.key_regional),
|
||||
subtitle = stringResource(R.string.key_regional_desc),
|
||||
isChecked = regional == onString,
|
||||
onCheckedChange = { isChecked ->
|
||||
if (isChecked) {
|
||||
regional = onString
|
||||
prefs.edit { putString(keyRegional, onString) }
|
||||
showLoad = true
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
GeoLocImporter.importStates(context, true)
|
||||
}
|
||||
showLoad = false
|
||||
}
|
||||
} else {
|
||||
showRegionalDialog = true
|
||||
}
|
||||
}
|
||||
)
|
||||
if (regional == onString) {
|
||||
SettingSwitch(
|
||||
label = stringResource(R.string.pref_regional_stats),
|
||||
subtitle = stringResource(R.string.pref_regional_stats_desc),
|
||||
isChecked = regionalStats == onString,
|
||||
onCheckedChange = { isChecked ->
|
||||
regionalStats = if (isChecked) onString else offString
|
||||
prefs.edit { putString(keyRegionalStats, regionalStats) }
|
||||
}
|
||||
)
|
||||
}
|
||||
HorizontalDivider()
|
||||
}
|
||||
item {
|
||||
val launcher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.OpenDocument(),
|
||||
onResult = { uri -> Data.doImport(context, uri) }
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp)
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
launcher.launch(arrayOf("*/*"))
|
||||
}, modifier = Modifier
|
||||
.weight(1f)
|
||||
) {
|
||||
Text(stringResource(R.string.action_import))
|
||||
}
|
||||
Spacer(modifier = Modifier.size(16.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
Data.doExport(context)
|
||||
}, modifier = Modifier
|
||||
.weight(1f)
|
||||
) {
|
||||
Text(stringResource(R.string.action_export))
|
||||
}
|
||||
}
|
||||
HorizontalDivider()
|
||||
}
|
||||
item {
|
||||
PreferenceButton(stringResource(R.string.licenses)) {
|
||||
if (navController.currentDestination?.route != "licenses")
|
||||
navController.navigate("licenses")
|
||||
}
|
||||
PreferenceButton(stringResource(R.string.about)) {
|
||||
if (navController.currentDestination?.route != "about")
|
||||
navController.navigate("about")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun MultiPreference(list: Array<String>, selected: String, onSelected: (String) -> Unit) {
|
||||
Column(Modifier.padding(2.dp)) {
|
||||
Column(Modifier.padding(vertical = 2.dp)) {
|
||||
list.forEach { value ->
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(36.dp)
|
||||
.height(42.dp)
|
||||
.clickable { onSelected(value) }) {
|
||||
RadioButton(selected = selected == value, onClick = { onSelected(value) })
|
||||
Text(
|
||||
value, modifier = Modifier.padding(start = 8.dp),
|
||||
value, modifier = Modifier.padding(start = 12.dp),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
)
|
||||
}
|
||||
@@ -393,3 +387,38 @@ fun PreferenceButton(text: String, onClick: () -> Unit) {
|
||||
Text(text)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingSwitch(
|
||||
label: String,
|
||||
subtitle: String? = null,
|
||||
isChecked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onCheckedChange(!isChecked) }
|
||||
.padding(vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text=label,
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
)
|
||||
if (subtitle != null) {
|
||||
Text(
|
||||
text = subtitle,
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
|
||||
)
|
||||
}
|
||||
}
|
||||
androidx.compose.material.Switch(
|
||||
checked = isChecked,
|
||||
onCheckedChange = onCheckedChange
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import net.helcel.beans.countries.World
|
||||
import net.helcel.beans.helper.AUTO_GROUP
|
||||
import net.helcel.beans.helper.Data
|
||||
import net.helcel.beans.helper.Groups
|
||||
import net.helcel.beans.helper.Settings.isRegional
|
||||
import net.helcel.beans.helper.Settings
|
||||
import net.helcel.beans.helper.Theme.getContrastColor
|
||||
|
||||
private val MODE_LIST = listOf(LocType.WORLD, LocType.COUNTRY, LocType.STATE)
|
||||
@@ -46,7 +46,7 @@ private val MODE_LIST = listOf(LocType.WORLD, LocType.COUNTRY, LocType.STATE)
|
||||
fun StatsScreen(
|
||||
onExit: ()-> Unit
|
||||
) {
|
||||
val modes = if (isRegional(LocalContext.current)) MODE_LIST else MODE_LIST.take(2)
|
||||
val modes = if (Settings.isRegional(LocalContext.current)) MODE_LIST else MODE_LIST.take(2)
|
||||
var selectedTab by remember { mutableIntStateOf(0) }
|
||||
var countMode by remember { mutableStateOf(true) }
|
||||
|
||||
@@ -57,7 +57,7 @@ fun StatsScreen(
|
||||
TopAppBar(
|
||||
title = {
|
||||
Row(verticalAlignment = Alignment.CenterVertically){
|
||||
Text(text=stringResource(R.string.action_edit), modifier = Modifier.weight(1f))
|
||||
Text(text=stringResource(R.string.action_stat), modifier = Modifier.weight(1f))
|
||||
Button(onClick = { countMode = !countMode }) {
|
||||
Text(if (countMode) "Count" else "Area")
|
||||
}
|
||||
@@ -115,24 +115,79 @@ fun StatsList(activeMode: LocType, countMode: Boolean) {
|
||||
|
||||
@Composable
|
||||
fun StatsRow(group: Groups.Group, activeMode: LocType, countMode: Boolean) {
|
||||
val context = LocalContext.current
|
||||
val isRegionalStats = remember { Settings.isRegionalStats(context) }
|
||||
|
||||
val visited = remember(group, activeMode) {
|
||||
Data.visits.getVisitedByValue(group.key)
|
||||
val countries = remember { World.WWW.children.flatMap { it.children } }
|
||||
val continents = remember { World.WWW.children.toList() }
|
||||
|
||||
val count = remember(group, activeMode, isRegionalStats) {
|
||||
when (activeMode) {
|
||||
LocType.WORLD -> continents.filter { continent ->
|
||||
if (group.key == AUTO_GROUP) {
|
||||
val isUncat = Data.visits.getVisited(continent) == AUTO_GROUP || continent.children.any { Data.visits.getVisited(it) == AUTO_GROUP }
|
||||
val isInRealGroup = Data.groups.groupsFlow.value.any { g ->
|
||||
Data.visits.getVisited(continent) == g.key || continent.children.any { Data.visits.getVisited(it) == g.key }
|
||||
}
|
||||
isUncat && (!isRegionalStats || !isInRealGroup)
|
||||
} else {
|
||||
Data.visits.getVisited(continent) == group.key || (isRegionalStats && continent.children.any { Data.visits.getVisited(it) == group.key })
|
||||
}
|
||||
}.size
|
||||
|
||||
LocType.COUNTRY -> countries.filter { country ->
|
||||
if (group.key == AUTO_GROUP) {
|
||||
val isUncat = Data.visits.getVisited(country) == AUTO_GROUP || country.children.any { Data.visits.getVisited(it) == AUTO_GROUP }
|
||||
val isInRealGroup = Data.groups.groupsFlow.value.any { g ->
|
||||
Data.visits.getVisited(country) == g.key || country.children.any { Data.visits.getVisited(it) == g.key }
|
||||
}
|
||||
isUncat && (!isRegionalStats || !isInRealGroup)
|
||||
} else {
|
||||
Data.visits.getVisited(country) == group.key || (isRegionalStats && country.children.any { Data.visits.getVisited(it) == group.key })
|
||||
}
|
||||
}.size
|
||||
|
||||
LocType.STATE -> countries.flatMap { it.children }.filter { region ->
|
||||
Data.visits.getVisited(region) == group.key
|
||||
}.size
|
||||
|
||||
val count = when (activeMode) {
|
||||
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.STATE -> World.WWW.children.flatMap { a->a.children.flatMap { b->b.children.filter { c->c.code in visited } } }.size
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
val area = remember(group, activeMode, isRegionalStats) {
|
||||
when (activeMode) {
|
||||
LocType.WORLD -> continents.filter { continent ->
|
||||
if (group.key == AUTO_GROUP) {
|
||||
val isUncat = Data.visits.getVisited(continent) == AUTO_GROUP || continent.children.any { Data.visits.getVisited(it) == AUTO_GROUP }
|
||||
val isInRealGroup = Data.groups.groupsFlow.value.any { g ->
|
||||
Data.visits.getVisited(continent) == g.key || continent.children.any { Data.visits.getVisited(it) == g.key }
|
||||
}
|
||||
isUncat && (!isRegionalStats || !isInRealGroup)
|
||||
} else {
|
||||
Data.visits.getVisited(continent) == group.key || (isRegionalStats && continent.children.any { Data.visits.getVisited(it) == group.key })
|
||||
}
|
||||
}.sumOf { it.area }
|
||||
|
||||
LocType.COUNTRY -> countries.filter { country ->
|
||||
if (group.key == AUTO_GROUP) {
|
||||
val isUncat = Data.visits.getVisited(country) == AUTO_GROUP || country.children.any { Data.visits.getVisited(it) == AUTO_GROUP }
|
||||
val isInRealGroup = Data.groups.groupsFlow.value.any { g ->
|
||||
Data.visits.getVisited(country) == g.key || country.children.any { Data.visits.getVisited(it) == g.key }
|
||||
}
|
||||
isUncat && (!isRegionalStats || !isInRealGroup)
|
||||
} else {
|
||||
Data.visits.getVisited(country) == group.key || (isRegionalStats && country.children.any { Data.visits.getVisited(it) == group.key })
|
||||
}
|
||||
}.sumOf { it.area }
|
||||
|
||||
LocType.STATE -> countries.flatMap { it.children }.filter { region ->
|
||||
Data.visits.getVisited(region) == group.key
|
||||
}.sumOf { it.area }
|
||||
|
||||
val area = when (activeMode) {
|
||||
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.STATE -> World.WWW.children.flatMap { a->a.children.flatMap { b->b.children.filter { c->c.code in visited } } }.sumOf { it.area }
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
val displayValue = if (countMode) count.toString() else stringResource(R.string.number_with_unit, area, "km²")
|
||||
|
||||
|
||||
@@ -29,6 +29,13 @@ object Settings {
|
||||
)
|
||||
}
|
||||
|
||||
fun isRegionalStats(ctx: Context): Boolean {
|
||||
return getBooleanValue(
|
||||
ctx,
|
||||
sp.getString(ctx.getString(R.string.key_regional_stats), ctx.getString(R.string.off))
|
||||
)
|
||||
}
|
||||
|
||||
fun getStatPref(ctx: Context): String? {
|
||||
return sp.getString(
|
||||
ctx.getString(R.string.key_stats),
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
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"/>
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
@@ -15,8 +15,21 @@
|
||||
<string name="percentages">Prefer percentages (%)</string>
|
||||
<string name="licenses">Licenses</string>
|
||||
<string name="key_group">Groups</string>
|
||||
<string name="key_regional">Regional</string>
|
||||
<string name="key_regional">Regional mode</string>
|
||||
<string name="key_regional_desc">Enable tracking of states and provinces</string>
|
||||
<string name="key_regional_stats">key_regional_stats</string>
|
||||
<string name="pref_regional_stats">Regional statistics</string>
|
||||
<string name="pref_regional_stats_desc">Count countries with visited regions</string>
|
||||
<string name="key_group_desc">Enable multiple colors</string>
|
||||
<string name="about">About</string>
|
||||
<string name="pref_category_appearance">Appearance</string>
|
||||
<string name="pref_category_features">Features</string>
|
||||
<string name="pref_category_data">Data</string>
|
||||
<string name="action_import">Import</string>
|
||||
<string name="action_export">Export</string>
|
||||
<string name="import_desc">Load data from a previously exported file.</string>
|
||||
<string name="export_desc">Save your current data to a file for backup or transfer.</string>
|
||||
<string name="key_projection_desc">Select how the map is projected onto a 2D surface.</string>
|
||||
<string name="beans_is_foss">Beans is free and open source software, licensed under the GNU General Public License (version 3 or later)</string>
|
||||
<string name="beans_repo_uri">https://github.com/helcel-net/beans</string>
|
||||
<string name="beans_repo">Project repository: %1$s\n Feel free to report issues or contribute.</string>
|
||||
|
||||
@@ -70,9 +70,9 @@
|
||||
integrity sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==
|
||||
|
||||
"@csstools/css-syntax-patches-for-csstree@^1.1.3":
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.5.tgz#b8e26e0fe25e9a6ec607d045470cc46d2f62731e"
|
||||
integrity sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.6.tgz#3a1e91cefb65b4a74ae7dd78c9b2dd60cdbf3ad4"
|
||||
integrity sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==
|
||||
|
||||
"@csstools/css-tokenizer@^4.0.0":
|
||||
version "4.0.0"
|
||||
@@ -1988,9 +1988,9 @@
|
||||
integrity sha512-tdJz7jaWFu4nR+8b2B+CdPZ6811ighYylWsu2hpsivapzW058yP0KdfZuNY89IiRe5jbKvBGXN3LQdN2KPXVdQ==
|
||||
|
||||
"@types/node@*":
|
||||
version "26.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-26.1.0.tgz#aa85f0727fc5611347091c478341c63650903439"
|
||||
integrity sha512-O0A1G3xPGy4w7AgQdAQYUlQ+BKk2Oovw8eRpofyp5KdBZULnbe+WqaOVNrm705SHphCiG4XHsACrSmPu1f+Kgw==
|
||||
version "26.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-26.1.1.tgz#bad758d601e97d6cf457d204ee76a35fce7bd119"
|
||||
integrity sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==
|
||||
dependencies:
|
||||
undici-types "~8.3.0"
|
||||
|
||||
@@ -2559,16 +2559,16 @@ iconv-lite@^0.6.3:
|
||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||
|
||||
iconv-lite@^0.7.2:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.2.tgz#d0bdeac3f12b4835b7359c2ad89c422a4d1cc72e"
|
||||
integrity sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.3.tgz#84ee12f963e7de50bc01a13e160a078b3b0f415f"
|
||||
integrity sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||
|
||||
idb-keyval@^6.2.0:
|
||||
version "6.2.6"
|
||||
resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.2.6.tgz#6a1204e20dca567f7b8fef2fac5fc52af02fd553"
|
||||
integrity sha512-FY64UEhw+5liMzMQ1R9Mw6AF0+wyBrg1CIA1z4CjI/EvT5ty/SvQcWZgd8s9sgaNhX10Y8UzScTh89tEAls5nA==
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.3.0.tgz#3fa4c062fcaddd7e577c51b51f69924bccfb58e6"
|
||||
integrity sha512-um+2dgAWmYsu615EXpWVwSmapJhON0G43t3Ka/EVaohzPQXSMqKEqeDK/oIW3Ow+BXaF2PvSc+oBTFp793A5Ow==
|
||||
|
||||
ieee754@^1.1.13, ieee754@^1.2.1:
|
||||
version "1.2.1"
|
||||
@@ -2708,9 +2708,9 @@ map-stream@0.0.7:
|
||||
integrity sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==
|
||||
|
||||
mapshaper@^0.7.22:
|
||||
version "0.7.35"
|
||||
resolved "https://registry.yarnpkg.com/mapshaper/-/mapshaper-0.7.35.tgz#5de71cea5d322272a77c7fa1c0fac5f491403479"
|
||||
integrity sha512-+UuAIVcuRWr3ZR0Lv/weIqUmSn/NMeHxWdfgyc0P2Dbi1NE7PDJ2yUg4utH2wIUqVpMvGzfIG6Uk/mPj/KlGNw==
|
||||
version "0.7.41"
|
||||
resolved "https://registry.yarnpkg.com/mapshaper/-/mapshaper-0.7.41.tgz#c03fa2aed375abbc92af47008dc81695a604ed23"
|
||||
integrity sha512-TC1KL8OtYZPRuq/Wl0AW2LQOic/q/AQbEEiPkCXlN5dGFXPIRj8+Qllc3EWBhN7aViHStI2IIthP2/L7BDzobQ==
|
||||
dependencies:
|
||||
"@bokuweb/zstd-wasm" "^0.0.27"
|
||||
"@ngageoint/geopackage" "^4.2.6"
|
||||
@@ -3293,17 +3293,17 @@ tinyqueue@^2.0.0, tinyqueue@^2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08"
|
||||
integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==
|
||||
|
||||
tldts-core@^7.4.4:
|
||||
version "7.4.4"
|
||||
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.4.4.tgz#a06de228c70a334e26d1597af7a82376e8588bce"
|
||||
integrity sha512-vwVLJVvvpslm7vqAH7+XNj/neA/Ynq7DT2EEcMuwc5YzN5XaMyRAqxwU+uX3azZ1FQtB2gvrvnLnAEkvYlVdfg==
|
||||
tldts-core@^7.4.6:
|
||||
version "7.4.6"
|
||||
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.4.6.tgz#4cc8ccdae554742f09e5999708cac5a7d83cb287"
|
||||
integrity sha512-TkQNGJIhlEphpHCjKodMTSe23egUZr/g+flI2qkLgiJ/maAzSgXypSLRTNH3nCmqgayEmtcJBiLcfODSAr1xoA==
|
||||
|
||||
tldts@^7.0.5:
|
||||
version "7.4.4"
|
||||
resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.4.4.tgz#343191348abbff80118732a1a84e90fcf95b29df"
|
||||
integrity sha512-kFXFK7O4WPextIUAOk8qtnw9dxR9UIXP9CjuH1cTBVBZMDeQcUPgr/IazGiw1B0Yiw5L75gHLWeW4iD793r90g==
|
||||
version "7.4.6"
|
||||
resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.4.6.tgz#3af5da04b35afd0e00cf0fe2827af30393a28202"
|
||||
integrity sha512-rbP0Gyx8b3Ae9yO//CU2wbSnQNoQ66m1nJdSbSHmnwKwzkkz/u8mERYU8T2rmlmy+bJvRNn84yNCW8gYqox44Q==
|
||||
dependencies:
|
||||
tldts-core "^7.4.4"
|
||||
tldts-core "^7.4.6"
|
||||
|
||||
token-types@^4.1.1:
|
||||
version "4.2.1"
|
||||
|
||||
Reference in New Issue
Block a user