3 Commits

Author SHA1 Message Date
soraefir d78ef0b38b Update version name 2026-07-09 20:22:50 +02:00
soraefir 30eb5657ce Fix projection selection 2026-07-09 20:22:49 +02:00
soraefir 4085149e88 Improve settings and regional stats 2026-07-09 20:22:49 +02:00
6 changed files with 321 additions and 206 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ android {
applicationId 'net.helcel.beans' applicationId 'net.helcel.beans'
minSdk = 28 minSdk = 28
targetSdk = 37 targetSdk = 37
versionName "1.4" versionName "1.5"
versionCode project.hasProperty('VERSION_CODE') ? project.property('VERSION_CODE').toInteger() : 1 versionCode project.hasProperty('VERSION_CODE') ? project.property('VERSION_CODE').toInteger() : 1
} }
signingConfigs { signingConfigs {
@@ -14,7 +14,6 @@ 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
@@ -22,10 +21,11 @@ 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.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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
@@ -43,8 +43,8 @@ import net.helcel.beans.svg.SVGWrapper
class MainScreen : ComponentActivity() { class MainScreen : ComponentActivity() {
private lateinit var psvg: SVGWrapper private var psvg by mutableStateOf<SVGWrapper?>(null)
private lateinit var css: CSSWrapper private var css by mutableStateOf<CSSWrapper?>(null)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -53,14 +53,19 @@ class MainScreen : ComponentActivity() {
Data.loadData(this, Int.MIN_VALUE) Data.loadData(this, Int.MIN_VALUE)
GeoLocImporter.importStates(this) GeoLocImporter.importStates(this)
refreshProjection()
setContent { setContent {
SysTheme { SysTheme {
val currentPsvg = psvg
val currentCss = css
if (currentPsvg != null && currentCss != null) {
Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.primary).statusBarsPadding(),) { Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.primary).statusBarsPadding(),) {
AppNavHost(psvg, css) AppNavHost(currentPsvg, currentCss)
}
} }
} }
} }
refreshProjection()
} }
@Composable @Composable
@@ -105,19 +110,25 @@ class MainScreen : ComponentActivity() {
@Composable @Composable
fun MapScreen(psvg: SVGWrapper, css: CSSWrapper) { fun MapScreen(psvg: SVGWrapper, css: CSSWrapper) {
Box { Box {
val cssContent = css.get()
val drawable = remember(psvg, css, cssContent) {
val opt: RenderOptions = RenderOptions.create() val opt: RenderOptions = RenderOptions.create()
opt.css(css.get()) opt.css(cssContent)
val drawable = remember(psvg, css) {
PictureDrawable(psvg.get()?.renderToPicture(opt)) PictureDrawable(psvg.get()?.renderToPicture(opt))
} }
AndroidView(factory = { ctx -> AndroidView(
factory = { ctx ->
PhotoView(ctx).apply { PhotoView(ctx).apply {
setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null) setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null)
setImageDrawable(drawable) maximumScale = 64f
maximumScale = 32f
scaleType = ImageView.ScaleType.FIT_CENTER 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.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog 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.core.content.edit
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.preference.PreferenceManager
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.helcel.beans.R
import net.helcel.beans.activity.sub.AboutScreen import net.helcel.beans.activity.sub.AboutScreen
import net.helcel.beans.activity.sub.EditPlaceColorDialog 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.activity.sub.LicenseScreen
import net.helcel.beans.countries.GeoLocImporter
import net.helcel.beans.helper.Data import net.helcel.beans.helper.Data
import net.helcel.beans.helper.Settings
@Composable @Composable
fun SysTheme( fun SysTheme(
@@ -156,64 +155,23 @@ fun SettingsScreen(navController: NavHostController = settingsNav()) {
val defaultTheme = stringResource(R.string.system) val defaultTheme = stringResource(R.string.system)
val keyProjection = stringResource(R.string.key_projection) val keyProjection = stringResource(R.string.key_projection)
val keyGroup = stringResource(R.string.key_group) 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) val offString = stringResource(R.string.off)
val onString = stringResource(R.string.on)
var showEdit by remember { mutableStateOf(false) }
var theme by remember { mutableStateOf(prefs.getString(keyTheme, defaultTheme)!!) } var theme by remember { mutableStateOf(prefs.getString(keyTheme, defaultTheme)!!) }
var projection by remember { mutableStateOf(prefs.getString(keyProjection, "default")!!) } 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) var showGroupDialog by remember { mutableStateOf(false) }
EditPlaceDialog(true) { var showRegionalDialog by remember { mutableStateOf(false) }
showEdit = false var showLoad by remember { mutableStateOf(false) }
val g = Data.selected_group val scope = rememberCoroutineScope()
if (it && g != null) {
Data.visits.reassignAllVisitedToGroup(g.key)
Data.saveData()
}
}
LazyColumn( if (showGroupDialog) {
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){
EditPlaceColorDialog( EditPlaceColorDialog(
deleteMode = true, deleteMode = true,
onDismiss = { onDismiss = {
@@ -222,88 +180,11 @@ fun SettingsScreen(navController: NavHostController = settingsNav()) {
Data.visits.reassignAllVisitedToGroup(g.key) Data.visits.reassignAllVisitedToGroup(g.key)
Data.saveData() 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 if (showRegionalDialog) {
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)
Dialog( Dialog(
content = { content = {
Column( Column(
@@ -312,25 +193,26 @@ fun RegionalScreen() {
MaterialTheme.colors.background, MaterialTheme.colors.background,
RoundedCornerShape(corner = CornerSize(16.dp)) RoundedCornerShape(corner = CornerSize(16.dp))
) )
.padding(16.dp),){ .padding(16.dp),
Text(style=MaterialTheme.typography.caption, text= stringResource(R.string.delete_regions)) ) {
Text(
style = MaterialTheme.typography.caption,
text = stringResource(R.string.delete_regions)
)
Button(onClick = { Button(onClick = {
GeoLocImporter.clearStates() GeoLocImporter.clearStates()
regional= selected regional = offString
prefs.edit { prefs.edit { putString(keyRegional, offString) }
putString( showRegionalDialog = false
keyRegional,
regional
)
}
showDialog=false
}) { }) {
Text(stringResource(R.string.ok)) Text(stringResource(R.string.ok))
} }
} }
}, },
onDismissRequest = { showDialog=false } onDismissRequest = { showRegionalDialog = false }
) )
}
if (showLoad) { if (showLoad) {
Dialog( Dialog(
content = { content = {
@@ -343,15 +225,73 @@ fun RegionalScreen() {
onDismissRequest = {} onDismissRequest = {}
) )
} }
val scope = rememberCoroutineScope()
MultiPreference(arrayOf(stringResource(R.string.on),stringResource(R.string.off)),regional) { key -> LazyColumn(
when (key) { modifier = Modifier
offString -> { showDialog=true .fillMaxSize()
selected=key .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 -> { HorizontalDivider()
regional = key }
prefs.edit { putString(keyRegional, key) } 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 showLoad = true
scope.launch { scope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@@ -359,25 +299,79 @@ fun RegionalScreen() {
} }
showLoad = false 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 @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(vertical = 2.dp)) {
list.forEach { value -> list.forEach { value ->
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(36.dp) .height(42.dp)
.clickable { onSelected(value) }) { .clickable { onSelected(value) }) {
RadioButton(selected = selected == value, onClick = { onSelected(value) }) RadioButton(selected = selected == value, onClick = { onSelected(value) })
Text( Text(
value, modifier = Modifier.padding(start = 8.dp), value, modifier = Modifier.padding(start = 12.dp),
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onBackground, color = MaterialTheme.colors.onBackground,
) )
} }
@@ -393,3 +387,38 @@ fun PreferenceButton(text: String, onClick: () -> Unit) {
Text(text) 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.AUTO_GROUP
import net.helcel.beans.helper.Data import net.helcel.beans.helper.Data
import net.helcel.beans.helper.Groups 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 import net.helcel.beans.helper.Theme.getContrastColor
private val MODE_LIST = listOf(LocType.WORLD, LocType.COUNTRY, LocType.STATE) 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( fun StatsScreen(
onExit: ()-> Unit 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 selectedTab by remember { mutableIntStateOf(0) }
var countMode by remember { mutableStateOf(true) } var countMode by remember { mutableStateOf(true) }
@@ -115,24 +115,79 @@ 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 isRegionalStats = remember { Settings.isRegionalStats(context) }
val visited = remember(group, activeMode) { val countries = remember { World.WWW.children.flatMap { it.children } }
Data.visits.getVisitedByValue(group.key) 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 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 else -> 0
} }
}
val displayValue = if (countMode) count.toString() else stringResource(R.string.number_with_unit, area, "km²") 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? { fun getStatPref(ctx: Context): String? {
return sp.getString( return sp.getString(
ctx.getString(R.string.key_stats), ctx.getString(R.string.key_stats),
+14 -1
View File
@@ -15,8 +15,21 @@
<string name="percentages">Prefer percentages (%)</string> <string name="percentages">Prefer percentages (%)</string>
<string name="licenses">Licenses</string> <string name="licenses">Licenses</string>
<string name="key_group">Groups</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="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_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_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> <string name="beans_repo">Project repository: %1$s\n Feel free to report issues or contribute.</string>