Compare commits
272 Commits
8e2304f5fc
...
renovate/c
Author | SHA1 | Date | |
---|---|---|---|
|
59b2988697 | ||
89134681f3 | |||
|
3b670b8ce2 | ||
b0ba07a7fb | |||
|
07adda1d74 | ||
6a86d05bad | |||
|
dc94c465d6 | ||
950bfbb42e | |||
|
927ac05f03 | ||
d6d84c6892 | |||
|
a34c1dbb11 | ||
6df13a044b | |||
68fb0023da | |||
|
686982c096 | ||
|
84898895df | ||
|
472237ba8f
|
||
|
2ae7e1fb8c | ||
|
ed4d751dce | ||
|
b5274d2113 | ||
|
d7ad8ebd74 | ||
|
43a521de16 | ||
|
4cf8c497a8 | ||
|
fd57b5dee4 | ||
|
49b80dc3b9 | ||
|
c0e3943700 | ||
f1608179f8 | |||
|
86aa888be6
|
||
|
9c1374c99f
|
||
|
6b2f786afe
|
||
|
2ded750c46 | ||
|
686be17fd2 | ||
|
7339e16e61 | ||
|
e43264b327 | ||
|
532a34cc7f | ||
|
59a02332d0 | ||
|
02a83a491f | ||
|
0a01aa95f1 | ||
|
438eae23b0 | ||
|
796faf393e | ||
|
9643d0ebb8 | ||
|
0999a0c512 | ||
|
7a3eb6c8f3 | ||
|
5a01c846fa | ||
|
4533f36bd2 | ||
|
ec45f36ec4 | ||
|
5396b570ec | ||
|
d0c656e3d0 | ||
|
adc416a5e3 | ||
|
b30430ec7d | ||
|
767d018d45 | ||
|
cbfc484ed8 | ||
|
39418809f6 | ||
|
40ab056bc0 | ||
|
ad64b289d4 | ||
|
5bdb16b48b | ||
|
89ca3200b4 | ||
|
54dbf74721 | ||
|
2cc7a69b0f | ||
|
37026870c7 | ||
|
8a9e389a53 | ||
|
7a2bae0e6b | ||
|
5b199aef8e | ||
|
99ff29af88 | ||
|
1d36d282f4 | ||
|
122dfe8981 | ||
|
0f9f428eed | ||
|
ff4a2513c3 | ||
|
14a3fb9ffd | ||
|
952bddf9e2 | ||
|
f346a93b1f | ||
|
7ac9dee6d8 | ||
|
767b98468b | ||
|
84c20349c7 | ||
|
097b86b6a5 | ||
|
30f6853d98 | ||
|
fe201a139d | ||
|
4da09b509f | ||
|
c009e750bb | ||
|
69ea4cf7c0 | ||
|
2f3c3ee84c | ||
|
dbd84a91bf | ||
|
34560b2da2 | ||
|
c8bb846d0f | ||
|
5bd6920857 | ||
|
5080498b96 | ||
|
3cc522aaa5 | ||
|
832b06f424 | ||
|
19dfd83351 | ||
|
75058f0705 | ||
|
da2d72246e | ||
|
e13517317f | ||
|
ca6d74b581 | ||
|
23b47df111 | ||
|
38422cc30a | ||
|
2f314e5082 | ||
|
27befa1790 | ||
|
86aaa3899b | ||
|
205f81bdab | ||
|
9899f8038b | ||
|
62b3ff8500 | ||
|
2dbb4a2d77 | ||
|
f4f56e0711 | ||
|
8b4ceae602 | ||
|
6619e7c891 | ||
|
0aac38c05e | ||
|
cb67901e41 | ||
|
4b418942da | ||
|
5e03bb33e3 | ||
|
22a75e9bc7 | ||
|
8ef3702f89 | ||
|
a89c916e1a | ||
|
6eeab1103a | ||
|
89c10a67fd | ||
|
aa2b503732 | ||
|
f01fdbad00 | ||
|
0fc5da8693 | ||
|
891a15c1f9 | ||
|
ff7e05a747 | ||
|
9e741fe04e | ||
|
e8307d79f5 | ||
|
ac694850da | ||
|
2aaf3661e5 | ||
|
b661a7da47 | ||
|
c38ca8d8c4 | ||
|
80a8c21cb0 | ||
|
944ff3529b | ||
|
fe4ae26428 | ||
|
bee13854f5 | ||
|
c02cfa4344 | ||
|
a4376e5513 | ||
|
731ef956d2 | ||
|
714abc2a96 | ||
|
542fc9b01f | ||
|
0e436cb1fc | ||
|
324e887d68 | ||
|
c6c4c072a8 | ||
|
02f289d4d0 | ||
|
fb89deabdf | ||
|
5ef6f76355 | ||
|
40edcff528 | ||
|
370fa6f15f | ||
|
1b2a2f5ff4 | ||
|
0c7490dea4 | ||
|
5ffc1cce50 | ||
|
bebfaf0921 | ||
|
21db9c6d23 | ||
|
5cb12372e1 | ||
|
1c45957cc3 | ||
|
86e233e93b | ||
|
2575e0f159 | ||
|
4442b94211 | ||
|
9a88c4a943 | ||
|
6bf9570916 | ||
|
1c7aca98c1 | ||
|
4982e168ff | ||
|
0b9e42ac6c | ||
|
05d44d274e | ||
|
25c06cd390 | ||
|
0e7a939172 | ||
|
0b8d699d68 | ||
|
34f9fb53bc | ||
|
ae782b1c32 | ||
|
bc78898f8e | ||
|
8ae862e292 | ||
2b1b82e163 | |||
|
edc5df342d | ||
|
7d32e267c6 | ||
fa8f4218be | |||
f7a166c9f2 | |||
8cc871203d | |||
|
4449b5cf8f | ||
|
0f1046fcd2 | ||
|
395fab45f4 | ||
|
0b44d67d61 | ||
|
d3242a1304 | ||
|
9a451764c2 | ||
|
898fe6e862 | ||
|
e357b60b14 | ||
|
cfb23ed1a0 | ||
|
4769901955 | ||
|
5c577ec763 | ||
|
0724c9a021 | ||
|
d031aa4ee4 | ||
|
6c7a82475e | ||
|
3bc34cef94 | ||
|
c0b489ae21 | ||
|
96f5e01d5f | ||
|
8c5793a75b | ||
|
858162ba47 | ||
|
191b3c3eff | ||
|
7ddc29275d | ||
|
0df7bb7f2c | ||
|
43fe9ab868 | ||
43f6acfab3 | |||
|
126cbfe7b1 | ||
|
4aad449a18 | ||
|
ee7fbf4d5a | ||
|
2d48cc8dae | ||
|
bdca9fe2a1 | ||
|
ff83b0abe3 | ||
|
dbe93b6884 | ||
|
53f2bd5a57 | ||
|
ff0714f942 | ||
|
ed067a616e | ||
|
e259d401ad | ||
|
9599933c5f | ||
|
00b0b6c746 | ||
|
adbae39d27 | ||
|
8097d25a18 | ||
|
fed3e55572 | ||
|
d76057f17c | ||
|
aab452f798 | ||
|
40fd4522ad | ||
|
fad65f76ee | ||
|
8ad3a26fb0 | ||
|
1118ed9b10 | ||
|
53db8be5f9 | ||
|
111a587793 | ||
|
cc5ade027a | ||
|
026fc7562f | ||
|
63a49455e7 | ||
|
3f59d876a1 | ||
|
05b78ed9a9 | ||
|
7241cdd5a1 | ||
|
8c65aeb2b9 | ||
|
4119518ff5 | ||
|
af839915cc
|
||
|
9fb11df99e | ||
|
9e18619271 | ||
|
755c0cd5c2 | ||
|
64c5f54eb8 | ||
|
18a037421c | ||
|
9660c19db7 | ||
|
bad189507d | ||
|
58ad43fffe | ||
|
54f4bb9138 | ||
|
4f578b027d | ||
cd4649b329 | |||
2a29237e26 | |||
|
17dd26b3b0 | ||
|
942f713a2f | ||
|
df4e01352a | ||
e7ab816c46 | |||
|
d171437e6f | ||
|
f33711f075 | ||
|
e854b50515 | ||
|
38d11574b1 | ||
|
96bb3e9d37 | ||
|
fb132f81a6 | ||
|
8bfc9c21eb | ||
|
bd7f61e1f7 | ||
|
108c805409 | ||
3875413fa4 | |||
|
9630608934 | ||
|
fcbef4b992 | ||
|
38397ac27b | ||
|
785c0491b9 | ||
|
24e547a294 | ||
|
f636b0c884 | ||
|
86b0ad59f8 | ||
|
436e793200 | ||
|
192179e3af | ||
|
6aedb64207 | ||
|
81ef0185b9 | ||
|
1636934e42 | ||
36bd6c9a44 | |||
|
e58df0291c | ||
|
7e87ed360c | ||
|
cb6ae76a67 | ||
|
4d519fc9a2 | ||
|
25abde0ba3
|
||
|
eda0bc19a0
|
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: set up secrets
|
||||
run: |
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
run: git checkout -B "$BRANCH"
|
||||
|
||||
- name: set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: "temurin"
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -20,8 +20,6 @@ app/build/
|
||||
app/debug/
|
||||
app/release/
|
||||
captures/
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
keystore.properties
|
||||
key.jks
|
@@ -39,8 +39,11 @@
|
||||
## 📳 Installation
|
||||
|
||||
<div style="display: flex; justify-content: center; align-items: center; flex-direction: row;">
|
||||
<a href="https://f-droid.org/packages/net.helcel.beans/">
|
||||
<!--<a href="https://f-droid.org/packages/net.helcel.beans/">
|
||||
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" width="206">
|
||||
</a>-->
|
||||
<a href="https://apt.izzysoft.de/fdroid/index/apk/net.helcel.beans">
|
||||
<img width="200" height="80" alt="Izzy Download" src=".github/images/izzy.png">
|
||||
</a>
|
||||
<a href="https://github.com/helcel-net/beans/releases/latest">
|
||||
<img width="200" height="84" alt="APK Download" src=".github/images/apk.png">
|
||||
@@ -68,7 +71,7 @@ Thanks to all contributors, the developers of our dependencies, and our users.
|
||||
## 📝 License
|
||||
|
||||
```
|
||||
Copyright (C) 2024 Helcel MYDOLI
|
||||
Copyright (C) 2024 Helcel & MYDOLI
|
||||
|
||||
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
|
||||
|
@@ -1,21 +1,24 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.23'
|
||||
id 'com.mikepenz.aboutlibraries.plugin' version '11.1.3'
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version '2.2.20'
|
||||
id 'org.jetbrains.kotlin.plugin.compose' version '2.2.20'
|
||||
id 'com.mikepenz.aboutlibraries.plugin' version '12.2.4'
|
||||
}
|
||||
|
||||
|
||||
android {
|
||||
namespace 'net.helcel.beans'
|
||||
compileSdk 34
|
||||
compileSdk 36
|
||||
|
||||
defaultConfig {
|
||||
buildConfigField("String", "APP_NAME", "\"Beans\"")
|
||||
manifestPlaceholders["APP_NAME"] = "Beans"
|
||||
applicationId 'net.helcel.beans'
|
||||
minSdk 28
|
||||
targetSdk 34
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
targetSdk 36
|
||||
versionCode 4
|
||||
versionName "1.1a"
|
||||
}
|
||||
signingConfigs {
|
||||
create("release") {
|
||||
@@ -54,17 +57,15 @@ android {
|
||||
compileOptions {
|
||||
coreLibraryDesugaringEnabled true
|
||||
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
sourceCompatibility JavaVersion.VERSION_21
|
||||
targetCompatibility JavaVersion.VERSION_21
|
||||
encoding 'utf-8'
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
compose true
|
||||
buildConfig true
|
||||
}
|
||||
|
||||
dependenciesInfo {
|
||||
@@ -73,19 +74,51 @@ android {
|
||||
// Disables dependency metadata when building Android App Bundles.
|
||||
includeInBundle = false
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "2.2.20"
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
}
|
||||
|
||||
lint {
|
||||
disable 'UsingMaterialAndMaterial3Libraries'
|
||||
}
|
||||
|
||||
}
|
||||
aboutLibraries {
|
||||
exclusionPatterns = [~"androidx.*", ~"com.google.android.*", ~"org.jetbrains.*"]
|
||||
library {
|
||||
exclusionPatterns = [~"androidx.*", ~"com.google.android.*", ~"org.jetbrains.*"]
|
||||
}
|
||||
excludeFields = ["generated"]
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.0.4'
|
||||
implementation 'androidx.compose.material3:material3:1.4.0'
|
||||
implementation "androidx.compose.material:material:1.9.3"
|
||||
implementation 'androidx.compose.material:material-icons-extended:1.7.8'
|
||||
implementation 'androidx.navigation:navigation-compose:2.9.5'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.1.5'
|
||||
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0'
|
||||
|
||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||
implementation 'com.google.android.material:material:1.11.0'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3'
|
||||
implementation 'androidx.compose.ui:ui'
|
||||
implementation "androidx.activity:activity-ktx:1.11.0"
|
||||
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||
implementation 'com.google.android.material:material:1.13.0'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0'
|
||||
|
||||
implementation 'com.caverock:androidsvg-aar:1.4'
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
||||
implementation 'com.mikepenz:aboutlibraries:11.1.3'
|
||||
|
||||
implementation 'com.mikepenz:aboutlibraries:13.0.0'
|
||||
implementation 'com.mikepenz:aboutlibraries-compose-m3:13.1.0'
|
||||
implementation 'com.mikepenz:aboutlibraries-core:13.0.0'
|
||||
|
||||
|
||||
implementation platform('androidx.compose:compose-bom:2025.10.00')
|
||||
debugImplementation 'androidx.compose.ui:ui-tooling:1.9.3'
|
||||
}
|
@@ -1,50 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:hardwareAccelerated="false"
|
||||
android:icon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name"
|
||||
android:label="${APP_NAME}"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Beans"
|
||||
tools:replace="android:allowBackup"
|
||||
tools:targetApi="31">
|
||||
<profileable android:shell="true" />
|
||||
|
||||
tools:replace="android:allowBackup">
|
||||
<activity
|
||||
android:name=".activity.MainActivity"
|
||||
android:name=".activity.MainScreen"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.EditActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.StatsActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.SettingsActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
@@ -1,71 +0,0 @@
|
||||
package net.helcel.beans.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.addCallback
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.activity.adapter.ViewPagerAdapter
|
||||
import net.helcel.beans.activity.fragment.EditGroupAddFragment
|
||||
import net.helcel.beans.activity.fragment.EditPlaceColorFragment
|
||||
import net.helcel.beans.activity.fragment.EditPlaceFragment
|
||||
import net.helcel.beans.countries.World
|
||||
import net.helcel.beans.databinding.ActivityEditBinding
|
||||
import net.helcel.beans.helper.Data
|
||||
import net.helcel.beans.helper.DialogCloser
|
||||
import net.helcel.beans.helper.Settings
|
||||
import net.helcel.beans.helper.Theme.createActionBar
|
||||
|
||||
|
||||
class EditActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var _binding: ActivityEditBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
_binding = ActivityEditBinding.inflate(layoutInflater)
|
||||
|
||||
setContentView(_binding.root)
|
||||
createActionBar(this, getString(R.string.action_edit))
|
||||
|
||||
val adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, _binding.pager)
|
||||
_binding.pager.adapter = adapter
|
||||
adapter.addFragment(null, EditPlaceFragment(World.WWW, adapter))
|
||||
|
||||
TabLayoutMediator(_binding.tab, _binding.pager) { tab, position ->
|
||||
tab.text = adapter.getLabel(position)
|
||||
}.attach()
|
||||
|
||||
onBackPressedDispatcher.addCallback {
|
||||
if (!adapter.backPressed()) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
if (Settings.isSingleGroup(this)) {
|
||||
menuInflater.inflate(R.menu.menu_edit, menu)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_color -> {
|
||||
Data.groups.getUniqueEntry()?.let { group ->
|
||||
EditGroupAddFragment(group.key, {
|
||||
(_binding.pager.adapter as ViewPagerAdapter?)?.refreshColors(group.color)
|
||||
}, {}, false).show(supportFragmentManager, "AddColorDialogFragment")
|
||||
}
|
||||
}
|
||||
else -> finish()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
|
||||
}
|
66
app/src/main/java/net/helcel/beans/activity/EditScreen.kt
Normal file
66
app/src/main/java/net/helcel/beans/activity/EditScreen.kt
Normal file
@@ -0,0 +1,66 @@
|
||||
package net.helcel.beans.activity
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.activity.sub.EditPlaceScreen
|
||||
import net.helcel.beans.countries.World
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun EditScreen(onExit:()->Unit = {}) {
|
||||
SysTheme {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.action_edit)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onExit) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
) { innerPadding ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colors.background)
|
||||
.padding(innerPadding) // ensures content is below the app bar
|
||||
) {
|
||||
Column {
|
||||
Spacer(
|
||||
modifier = Modifier.height(2.dp).fillMaxWidth()
|
||||
.background(MaterialTheme.colors.background)
|
||||
)
|
||||
EditPlaceScreen(World.WWW, onExit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,76 +0,0 @@
|
||||
package net.helcel.beans.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.PictureDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.caverock.androidsvg.RenderOptions
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.countries.GeoLocImporter
|
||||
import net.helcel.beans.databinding.ActivityMainBinding
|
||||
import net.helcel.beans.helper.Data
|
||||
import net.helcel.beans.helper.Settings
|
||||
import net.helcel.beans.helper.Theme.colorWrapper
|
||||
import net.helcel.beans.svg.CSSWrapper
|
||||
import net.helcel.beans.svg.SVGWrapper
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var _binding: ActivityMainBinding
|
||||
|
||||
private lateinit var psvg: SVGWrapper
|
||||
private lateinit var css: CSSWrapper
|
||||
|
||||
override fun onRestart() {
|
||||
refreshProjection()
|
||||
refreshMap()
|
||||
super.onRestart()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_main, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val d = when (item.itemId) {
|
||||
R.id.action_edit -> EditActivity::class.java
|
||||
R.id.action_stats -> StatsActivity::class.java
|
||||
R.id.action_settings -> SettingsActivity::class.java
|
||||
else -> throw Exception("Non Existent Menu Item")
|
||||
}
|
||||
startActivity(Intent(this@MainActivity, d))
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
_binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
Settings.start(this)
|
||||
|
||||
setContentView(_binding.root)
|
||||
|
||||
_binding.photoView.minimumScale = 1f
|
||||
_binding.photoView.maximumScale = 40f
|
||||
|
||||
GeoLocImporter.importStates(this)
|
||||
Data.loadData(this, Int.MIN_VALUE)
|
||||
|
||||
refreshProjection()
|
||||
refreshMap()
|
||||
}
|
||||
|
||||
private fun refreshMap() {
|
||||
val opt: RenderOptions = RenderOptions.create()
|
||||
opt.css(css.get())
|
||||
_binding.photoView.setImageDrawable(PictureDrawable(psvg.get()?.renderToPicture(opt)))
|
||||
}
|
||||
|
||||
fun refreshProjection() {
|
||||
psvg = SVGWrapper(this)
|
||||
css = CSSWrapper(this)
|
||||
}
|
||||
|
||||
}
|
124
app/src/main/java/net/helcel/beans/activity/MainScreen.kt
Normal file
124
app/src/main/java/net/helcel/beans/activity/MainScreen.kt
Normal file
@@ -0,0 +1,124 @@
|
||||
package net.helcel.beans.activity
|
||||
|
||||
import android.graphics.drawable.PictureDrawable
|
||||
import android.os.Bundle
|
||||
import android.widget.ImageView
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TopAppBar
|
||||
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.Percent
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.caverock.androidsvg.RenderOptions
|
||||
import com.github.chrisbanes.photoview.PhotoView
|
||||
import net.helcel.beans.BuildConfig
|
||||
import net.helcel.beans.countries.GeoLocImporter
|
||||
import net.helcel.beans.helper.Data
|
||||
import net.helcel.beans.helper.Settings
|
||||
import net.helcel.beans.svg.CSSWrapper
|
||||
import net.helcel.beans.svg.SVGWrapper
|
||||
|
||||
|
||||
class MainScreen : ComponentActivity() {
|
||||
|
||||
private lateinit var psvg: SVGWrapper
|
||||
private lateinit var css: CSSWrapper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
actionBar?.hide()
|
||||
Settings.start(this)
|
||||
GeoLocImporter.importStates(this)
|
||||
Data.loadData(this, Int.MIN_VALUE)
|
||||
|
||||
setContent {
|
||||
SysTheme {
|
||||
Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.background)) {
|
||||
AppNavHost(psvg, css)
|
||||
}
|
||||
}
|
||||
}
|
||||
refreshProjection()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppNavHost(psvg: SVGWrapper, css: CSSWrapper) {
|
||||
val navController = rememberNavController()
|
||||
NavHost(navController, startDestination = "main") {
|
||||
composable("main") { MainScreenC(psvg,css, navController) }
|
||||
composable("settings") { SettingsMainScreen { navController.navigate("main")} }
|
||||
composable("edit") { EditScreen { navController.navigate("main") } }
|
||||
composable("stats") { StatsScreen { navController.navigate("main") } }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainScreenC(psvg: SVGWrapper,css: CSSWrapper, nav: NavHostController){
|
||||
SysTheme {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(BuildConfig.APP_NAME) },
|
||||
actions = {
|
||||
IconButton(onClick = { nav.navigate("edit") }) {
|
||||
Icon(Icons.Default.Edit, contentDescription = "Edit")
|
||||
}
|
||||
IconButton(onClick = { nav.navigate("stats") }){
|
||||
Icon(Icons.Default.Percent, contentDescription = "Stats")
|
||||
}
|
||||
IconButton(onClick = { nav.navigate("settings") }) {
|
||||
Icon(Icons.Default.Settings, contentDescription = "Settings")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Box(modifier = Modifier.padding(innerPadding)) {
|
||||
MapScreen(psvg, css)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MapScreen(psvg: SVGWrapper, css: CSSWrapper) {
|
||||
Box {
|
||||
val opt: RenderOptions = RenderOptions.create()
|
||||
opt.css(css.get())
|
||||
val drawable = remember(psvg, css) {
|
||||
PictureDrawable(psvg.get()?.renderToPicture(opt))
|
||||
}
|
||||
AndroidView(factory = { ctx ->
|
||||
PhotoView(ctx).apply {
|
||||
setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null)
|
||||
setImageDrawable(drawable)
|
||||
scaleType = ImageView.ScaleType.FIT_CENTER
|
||||
}
|
||||
}, modifier = Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshProjection() {
|
||||
psvg = SVGWrapper(this)
|
||||
css = CSSWrapper(this)
|
||||
}
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
package net.helcel.beans.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.activity.fragment.AboutFragment
|
||||
import net.helcel.beans.activity.fragment.LicenseFragment
|
||||
import net.helcel.beans.activity.fragment.SettingsFragment
|
||||
import net.helcel.beans.helper.Theme.createActionBar
|
||||
|
||||
class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.activity_settings)
|
||||
createActionBar(this, getString(R.string.action_settings))
|
||||
|
||||
// Populate activity with settings fragment
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_view, SettingsFragment(), getString(R.string.action_settings))
|
||||
.commit()
|
||||
|
||||
// Change title in action bar according to current fragment
|
||||
supportFragmentManager.addFragmentOnAttachListener { _, _ ->
|
||||
supportActionBar?.title =
|
||||
supportFragmentManager.findFragmentById(R.id.fragment_view).let {
|
||||
when (it) {
|
||||
is LicenseFragment -> getString(R.string.licenses)
|
||||
is AboutFragment -> getString(R.string.about)
|
||||
else -> getString(R.string.action_settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
// Configure on back pressed
|
||||
supportFragmentManager.findFragmentById(R.id.fragment_view).let {
|
||||
when (it) {
|
||||
is LicenseFragment, is AboutFragment -> {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.remove(it)
|
||||
.commit()
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(
|
||||
R.id.fragment_view,
|
||||
SettingsFragment(),
|
||||
getString(R.string.action_settings)
|
||||
)
|
||||
.commit()
|
||||
supportActionBar?.title = getString(R.string.action_settings)
|
||||
}
|
||||
|
||||
else -> {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
381
app/src/main/java/net/helcel/beans/activity/SettingsScreen.kt
Normal file
381
app/src/main/java/net/helcel/beans/activity/SettingsScreen.kt
Normal file
@@ -0,0 +1,381 @@
|
||||
package net.helcel.beans.activity
|
||||
|
||||
import android.os.Build
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.CornerSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.CircularProgressIndicator
|
||||
import androidx.compose.material.Colors
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.RadioButton
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
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 kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
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.helper.Data
|
||||
|
||||
@Composable
|
||||
fun SysTheme(
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val themeKey = prefs.getString(stringResource(R.string.key_theme), stringResource(R.string.system))
|
||||
val darkTheme = when (themeKey) {
|
||||
stringResource(R.string.system) -> isSystemInDarkTheme()
|
||||
stringResource(R.string.light) -> false
|
||||
stringResource(R.string.dark) -> true
|
||||
else -> isSystemInDarkTheme()
|
||||
}
|
||||
val colorScheme = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if(darkTheme) dynamicDarkColorScheme(LocalContext.current ) else dynamicLightColorScheme(LocalContext.current )
|
||||
} else {
|
||||
if(darkTheme) darkColorScheme() else lightColorScheme()
|
||||
}
|
||||
val m2colors = Colors(
|
||||
primary = colorScheme.primary,
|
||||
primaryVariant = colorScheme.primaryContainer,
|
||||
secondary = colorScheme.secondary,
|
||||
background = colorScheme.background,
|
||||
surface = colorScheme.surface,
|
||||
onPrimary = colorScheme.onPrimary,
|
||||
onSecondary = colorScheme.onSecondary,
|
||||
onBackground = colorScheme.onBackground,
|
||||
onSurface = colorScheme.onSurface,
|
||||
secondaryVariant = colorScheme.secondary,
|
||||
error = colorScheme.error,
|
||||
onError = colorScheme.onError,
|
||||
isLight = !darkTheme,
|
||||
)
|
||||
|
||||
MaterialTheme(
|
||||
colors = m2colors,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun settingsNav(): NavHostController {
|
||||
val navController = rememberNavController()
|
||||
NavHost(navController, startDestination= "settings"){
|
||||
composable("settings"){SettingsScreen(navController)}
|
||||
composable("licenses"){ LicenseScreen() }
|
||||
composable("about"){ AboutScreen() }
|
||||
}
|
||||
return navController
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun SettingsMainScreen(onExit: ()->Unit = {}) {
|
||||
val nav: NavHostController = settingsNav()
|
||||
SysTheme {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.action_settings)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
if(!nav.popBackStack())
|
||||
onExit()
|
||||
}) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Box(modifier = Modifier.padding(innerPadding)) {
|
||||
SettingsScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(navController: NavHostController = settingsNav()) {
|
||||
val context = LocalContext.current
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
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 projection by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_projection), "default")!!) }
|
||||
var groups by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_group), context.getString(R.string.off))!!) }
|
||||
|
||||
if(showEdit)
|
||||
EditPlaceDialog(true) {
|
||||
showEdit = false
|
||||
val g = Data.selected_group
|
||||
if (it && g != null)
|
||||
Data.visits.reassignAllVisitedToGroup(g.key)
|
||||
}
|
||||
|
||||
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(context.getString(R.string.key_theme), 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(context.getString(R.string.key_projection), 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(
|
||||
deleteMode = true,
|
||||
onDismiss = {
|
||||
val g = Data.selected_group
|
||||
if (g != null)
|
||||
Data.visits.reassignAllVisitedToGroup(g.key)
|
||||
showDialog = false
|
||||
})
|
||||
}
|
||||
MultiPreference(
|
||||
arrayOf(stringResource(R.string.on), stringResource(R.string.off)),
|
||||
groups
|
||||
) { key ->
|
||||
if (key == context.getString(R.string.off)) {
|
||||
showDialog=true
|
||||
}
|
||||
groups = key
|
||||
prefs.edit { putString(context.getString(R.string.key_group), 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 prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
var selected by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_regional),context.getString(R.string.off))!!)}
|
||||
var regional by remember{ mutableStateOf(prefs.getString(context.getString(R.string.key_regional), context.getString(R.string.off))!!)}
|
||||
var showDialog by remember{mutableStateOf(false)}
|
||||
var showLoad by remember{mutableStateOf(false)}
|
||||
|
||||
if(showDialog)
|
||||
Dialog(
|
||||
content = {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
MaterialTheme.colors.background,
|
||||
RoundedCornerShape(corner = CornerSize(16.dp))
|
||||
)
|
||||
.padding(16.dp),){
|
||||
Text(style=MaterialTheme.typography.caption, text= stringResource(R.string.delete_regions))
|
||||
Button(onClick = {
|
||||
GeoLocImporter.clearStates()
|
||||
regional= selected
|
||||
prefs.edit {
|
||||
putString(
|
||||
context.getString(R.string.key_regional),
|
||||
regional
|
||||
)
|
||||
}
|
||||
showDialog=false
|
||||
}){
|
||||
Text(stringResource(R.string.ok))
|
||||
}
|
||||
}
|
||||
},
|
||||
onDismissRequest = { showDialog=false }
|
||||
)
|
||||
if(showLoad){
|
||||
Dialog(
|
||||
content = {
|
||||
CircularProgressIndicator(
|
||||
color = MaterialTheme.colors.primary,
|
||||
strokeWidth = 4.dp,
|
||||
modifier = Modifier.size(50.dp)
|
||||
)
|
||||
},
|
||||
onDismissRequest = {}
|
||||
)
|
||||
}
|
||||
val scope = rememberCoroutineScope()
|
||||
MultiPreference(arrayOf(stringResource(R.string.on),stringResource(R.string.off)),regional) { key ->
|
||||
when (key) {
|
||||
context.getString(R.string.off) -> { showDialog=true
|
||||
selected=key
|
||||
}
|
||||
context.getString(R.string.on) -> {
|
||||
regional = key
|
||||
prefs.edit { putString(context.getString(R.string.key_regional), key) }
|
||||
showLoad=true
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
GeoLocImporter.importStates(context, true)
|
||||
}
|
||||
showLoad = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun MultiPreference(list: Array<String>, selected: String, onSelected: (String) -> Unit) {
|
||||
Column(Modifier.padding(2.dp)) {
|
||||
list.map { value ->
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(36.dp)
|
||||
.clickable { onSelected(value) }) {
|
||||
RadioButton(selected = selected == value, onClick = { onSelected(value) })
|
||||
Text(
|
||||
value, modifier = Modifier.padding(start = 8.dp),
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PreferenceButton(text: String, onClick: () -> Unit) {
|
||||
Button(onClick = onClick, modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp)) {
|
||||
Text(text)
|
||||
}
|
||||
}
|
@@ -1,57 +1,159 @@
|
||||
package net.helcel.beans.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Tab
|
||||
import androidx.compose.material.TabRow
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.activity.adapter.StatsListAdapter
|
||||
import net.helcel.beans.countries.GeoLoc.LocType
|
||||
import net.helcel.beans.databinding.ActivityStatBinding
|
||||
import net.helcel.beans.helper.Settings
|
||||
import net.helcel.beans.helper.Theme.createActionBar
|
||||
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.Theme.getContrastColor
|
||||
|
||||
private val MODE_LIST = listOf(LocType.WORLD, LocType.COUNTRY, LocType.STATE)
|
||||
|
||||
class StatsActivity : AppCompatActivity() {
|
||||
private lateinit var _binding: ActivityStatBinding
|
||||
private var activeMode = LocType.WORLD
|
||||
@Composable
|
||||
fun StatsScreen(
|
||||
onExit: ()-> Unit
|
||||
) {
|
||||
val modes = if (isRegional(LocalContext.current)) MODE_LIST else MODE_LIST.take(2)
|
||||
var selectedTab by remember { mutableIntStateOf(0) }
|
||||
var countMode by remember { mutableStateOf(true) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
_binding = ActivityStatBinding.inflate(layoutInflater)
|
||||
setContentView(_binding.root)
|
||||
createActionBar(this, getString(R.string.action_stat))
|
||||
|
||||
_binding.stats.layoutManager =
|
||||
LinearLayoutManager(this, RecyclerView.VERTICAL, false)
|
||||
val adapter = StatsListAdapter(_binding.stats, _binding.name)
|
||||
_binding.groupColor.setOnClickListener { adapter.invertCountMode() }
|
||||
_binding.stats.adapter = adapter
|
||||
SysTheme {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Row(verticalAlignment = Alignment.CenterVertically){
|
||||
Text(text=stringResource(R.string.action_edit), modifier = Modifier.weight(1f))
|
||||
Button(onClick = { countMode = !countMode }) {
|
||||
Text(if (countMode) "Count" else "Area")
|
||||
}
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onExit) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
_binding.pager.adapter = object : FragmentStateAdapter(supportFragmentManager, lifecycle) {
|
||||
override fun getItemCount(): Int = if (Settings.isRegional(applicationContext)) 3 else 2
|
||||
override fun createFragment(position: Int): Fragment = Fragment()
|
||||
}
|
||||
TabLayoutMediator(_binding.tab, _binding.pager) { tab, position ->
|
||||
tab.text = MODE_LIST[position].txt
|
||||
}.attach()
|
||||
)
|
||||
},
|
||||
) { padding ->
|
||||
Column(Modifier.padding(padding)) {
|
||||
TabRow(selectedTabIndex = selectedTab) {
|
||||
modes.forEachIndexed { index, mode ->
|
||||
Tab(
|
||||
selected = selectedTab == index,
|
||||
onClick = { selectedTab = index },
|
||||
text = { Text(mode.txt) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
_binding.pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
activeMode = MODE_LIST[position]
|
||||
adapter.refreshMode(activeMode)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
}
|
||||
|
||||
val activeMode = modes.getOrNull(selectedTab) ?: LocType.WORLD
|
||||
StatsList(activeMode, countMode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun StatsList(activeMode: LocType, countMode: Boolean) {
|
||||
val groups = remember { Data.groups.groupsFlow }
|
||||
val unCat = stringResource(R.string.uncategorized)
|
||||
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
items(groups.value + listOf(Groups.Group(AUTO_GROUP, unCat))) { group ->
|
||||
StatsRow(group, activeMode, countMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun StatsRow(group: Groups.Group, activeMode: LocType, countMode: Boolean) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val visited = remember(group, activeMode) {
|
||||
Data.visits.getVisitedByValue(group.key)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
finish()
|
||||
return super.onOptionsItemSelected(item)
|
||||
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 { itc->itc.children.flatMap { it.children.filter { it.code in visited } } }.size
|
||||
else -> 0
|
||||
}
|
||||
|
||||
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 { it.children.flatMap { it.children.filter { it.code in visited } } }.sumOf { it.area }
|
||||
else -> 0
|
||||
}
|
||||
|
||||
val displayValue = if (countMode) count.toString() else context.getString(R.string.number_with_unit, area, "km²")
|
||||
|
||||
val backgroundColor = group.color.color
|
||||
val textColor = getContrastColor(backgroundColor)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(Color(backgroundColor))
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text=group.name,
|
||||
modifier= Modifier.weight(1f),
|
||||
color = Color(textColor)
|
||||
)
|
||||
Text(text=displayValue,
|
||||
color = Color(textColor)
|
||||
)
|
||||
}
|
||||
}
|
@@ -1,193 +0,0 @@
|
||||
package net.helcel.beans.activity.adapter
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.checkbox.MaterialCheckBox
|
||||
import net.helcel.beans.activity.fragment.EditPlaceColorFragment
|
||||
import net.helcel.beans.activity.fragment.EditPlaceFragment
|
||||
import net.helcel.beans.countries.GeoLoc
|
||||
import net.helcel.beans.databinding.ItemListGeolocBinding
|
||||
import net.helcel.beans.helper.*
|
||||
import net.helcel.beans.helper.Theme.colorWrapper
|
||||
|
||||
class GeolocListAdapter(
|
||||
private val ctx: EditPlaceFragment, private val l: GeoLoc, private val pager: ViewPagerAdapter,
|
||||
private val parentHolder: FoldingListViewHolder?
|
||||
) : RecyclerView.Adapter<GeolocListAdapter.FoldingListViewHolder>() {
|
||||
|
||||
private val sortedList = l.children.toList().sortedBy { it.fullName }
|
||||
private val holders: MutableSet<FoldingListViewHolder> = mutableSetOf()
|
||||
|
||||
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): FoldingListViewHolder {
|
||||
val binding = ItemListGeolocBinding.inflate(
|
||||
LayoutInflater.from(viewGroup.context),
|
||||
viewGroup,
|
||||
false
|
||||
)
|
||||
val holder = FoldingListViewHolder(ctx.requireActivity(), binding, parentHolder, l)
|
||||
holders.add(holder)
|
||||
return holder
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: FoldingListViewHolder, position: Int) {
|
||||
val el = sortedList[position]
|
||||
holder.bind(el)
|
||||
holder.addListeners(el) {
|
||||
if (el.children.isNotEmpty())
|
||||
pager.addFragment(ctx, EditPlaceFragment(el, pager, holder))
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return l.children.size
|
||||
}
|
||||
|
||||
fun refreshColors(colorDrawable: ColorDrawable) {
|
||||
holders.forEach { it.refreshColor(colorDrawable) }
|
||||
}
|
||||
|
||||
class FoldingListViewHolder(
|
||||
private val ctx: FragmentActivity,
|
||||
private val _binding: ItemListGeolocBinding,
|
||||
private val _parentHolder: FoldingListViewHolder? = null,
|
||||
private val _parentGeoLoc: GeoLoc,
|
||||
) : RecyclerView.ViewHolder(_binding.root), DialogCloser {
|
||||
private lateinit var el: GeoLoc
|
||||
|
||||
private fun bindGroup(el: GeoLoc) {
|
||||
refreshCount(el)
|
||||
_binding.textView.setTypeface(null, Typeface.BOLD)
|
||||
_binding.textView.backgroundTintList = ColorStateList.valueOf(
|
||||
colorWrapper(
|
||||
ctx,
|
||||
android.R.attr.panelColorBackground
|
||||
).color
|
||||
).withAlpha(64)
|
||||
}
|
||||
|
||||
fun bind(el: GeoLoc) {
|
||||
this.el = el
|
||||
_binding.textView.text = el.fullName
|
||||
_binding.textView.backgroundTintList =
|
||||
ColorStateList.valueOf(colorWrapper(ctx, android.R.attr.colorBackground).color)
|
||||
|
||||
if (el.children.isNotEmpty())
|
||||
bindGroup(el)
|
||||
|
||||
refreshCheck(el)
|
||||
}
|
||||
|
||||
fun refreshColor(colorDrawable: ColorDrawable) {
|
||||
if (Data.visits.getVisited(el) !in listOf(NO_GROUP, AUTO_GROUP)) {
|
||||
_binding.checkBox.buttonTintList =
|
||||
ColorStateList.valueOf(colorDrawable.color)
|
||||
refreshCheck(el)
|
||||
}
|
||||
}
|
||||
|
||||
fun addListeners(el: GeoLoc, expandLambda: () -> Boolean) {
|
||||
if (el.children.isNotEmpty()) {
|
||||
_binding.textView.setOnClickListener { expandLambda() }
|
||||
}
|
||||
_binding.checkBox.setOnClickListener {
|
||||
Data.selected_geoloc = el
|
||||
if (Data.groups.size() == 1 && Settings.isSingleGroup(ctx)) {
|
||||
if (_binding.checkBox.isChecked) {
|
||||
// If one has just checked the box (assign unique group)
|
||||
Data.selected_group = Data.groups.getUniqueEntry()
|
||||
onDialogDismiss(false)
|
||||
} else {
|
||||
// If one has just unchecked the box (unassign unique group)
|
||||
Data.selected_group = null
|
||||
onDialogDismiss(true)
|
||||
}
|
||||
} else {
|
||||
Data.selected_group = null
|
||||
EditPlaceColorFragment(this).show(
|
||||
ctx.supportFragmentManager,
|
||||
"AddColorDialogFragment"
|
||||
)
|
||||
}
|
||||
_parentHolder?.refresh(_parentGeoLoc)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDialogDismiss(clear: Boolean) {
|
||||
if (clear) {
|
||||
Data.visits.setVisited(Data.selected_geoloc, NO_GROUP)
|
||||
Data.saveData()
|
||||
|
||||
if (_parentGeoLoc.children.all { Data.visits.getVisited(it) == NO_GROUP }) {
|
||||
Data.clearing_geoloc = _parentGeoLoc
|
||||
}
|
||||
}
|
||||
if (Data.selected_group != null && Data.selected_geoloc != null) {
|
||||
Data.visits.setVisited(Data.selected_geoloc, Data.selected_group?.key ?: NO_GROUP)
|
||||
Data.saveData()
|
||||
}
|
||||
Data.selected_geoloc?.let { refreshCheck(it) }
|
||||
Data.selected_geoloc = null
|
||||
Data.selected_group = null
|
||||
_parentHolder?.refresh(_parentGeoLoc)
|
||||
}
|
||||
|
||||
private fun refreshCheck(geoLoc: GeoLoc) {
|
||||
_binding.checkBox.checkedState =
|
||||
if (Data.visits.getVisited(geoLoc) !in listOf(NO_GROUP, AUTO_GROUP)) {
|
||||
MaterialCheckBox.STATE_CHECKED
|
||||
} else if (geoLoc.children.isNotEmpty() &&
|
||||
geoLoc.children.all {
|
||||
Data.visits.getVisited(it) !in listOf(NO_GROUP, AUTO_GROUP)
|
||||
}
|
||||
) {
|
||||
Data.visits.setVisited(geoLoc, AUTO_GROUP)
|
||||
MaterialCheckBox.STATE_CHECKED
|
||||
} else if (geoLoc.children.isEmpty() && Data.visits.getVisited(geoLoc) == AUTO_GROUP) {
|
||||
MaterialCheckBox.STATE_CHECKED
|
||||
} else if (geoLoc.children.any { Data.visits.getVisited(it) != NO_GROUP }) {
|
||||
Data.visits.setVisited(geoLoc, AUTO_GROUP)
|
||||
MaterialCheckBox.STATE_INDETERMINATE
|
||||
} else {
|
||||
Data.visits.setVisited(geoLoc, NO_GROUP)
|
||||
if (Data.clearing_geoloc == geoLoc) {
|
||||
Data.clearing_geoloc = null
|
||||
}
|
||||
MaterialCheckBox.STATE_UNCHECKED
|
||||
}
|
||||
Data.saveData()
|
||||
|
||||
var col = Data.groups.getGroupFromKey(Data.visits.getVisited(geoLoc)).color
|
||||
if (Data.visits.getVisited(geoLoc) == AUTO_GROUP) {
|
||||
col = colorWrapper(ctx, android.R.attr.colorPrimary)
|
||||
} else if (col.color == Color.TRANSPARENT) {
|
||||
col = colorWrapper(ctx, android.R.attr.panelColorBackground)
|
||||
col.alpha = 64
|
||||
}
|
||||
_binding.checkBox.buttonTintList = ColorStateList.valueOf(col.color)
|
||||
}
|
||||
|
||||
private fun refreshCount(geoLoc: GeoLoc) {
|
||||
val numerator =
|
||||
geoLoc.children.map { Data.visits.getVisited(it) != NO_GROUP }.count { it }
|
||||
val denominator = geoLoc.children.size
|
||||
_binding.count.text = Settings.getStats(ctx, numerator, denominator)
|
||||
}
|
||||
|
||||
private fun refresh(geoLoc: GeoLoc) {
|
||||
// Refresh
|
||||
refreshCheck(geoLoc)
|
||||
refreshCount(geoLoc)
|
||||
|
||||
// Recursively refresh parent
|
||||
_parentHolder?.refresh(_parentGeoLoc)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,78 +0,0 @@
|
||||
package net.helcel.beans.activity.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import net.helcel.beans.activity.fragment.EditGroupAddFragment
|
||||
import net.helcel.beans.databinding.ItemListGroupBinding
|
||||
import net.helcel.beans.helper.Data
|
||||
import net.helcel.beans.helper.Groups
|
||||
import net.helcel.beans.helper.Theme.getContrastColor
|
||||
|
||||
class GroupListAdapter(
|
||||
private val activity: FragmentActivity,
|
||||
private val selectDialog: DialogFragment,
|
||||
private val delete: Boolean = false
|
||||
) : RecyclerView.Adapter<GroupListAdapter.GroupViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GroupViewHolder {
|
||||
val binding =
|
||||
ItemListGroupBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return GroupViewHolder(binding, activity, selectDialog)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: GroupViewHolder, pos: Int) {
|
||||
holder.bind(Data.groups.getGroupFromPos(pos))
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return Data.groups.size()
|
||||
}
|
||||
|
||||
inner class GroupViewHolder(
|
||||
private val _binding: ItemListGroupBinding,
|
||||
private val activity: FragmentActivity,
|
||||
private val selectDialog: DialogFragment
|
||||
) : RecyclerView.ViewHolder(_binding.root) {
|
||||
private lateinit var dialogFragment: EditGroupAddFragment
|
||||
fun bind(entry: Pair<Int, Groups.Group>) {
|
||||
_binding.groupColor.text = entry.second.name
|
||||
dialogFragment = EditGroupAddFragment(entry.first, {
|
||||
val newEntry = Data.groups.getGroupFromKey(entry.first)
|
||||
_binding.groupColor.text = newEntry.name
|
||||
val newEntryColor = newEntry.color.color
|
||||
val contrastNewEntryColor =
|
||||
getContrastColor(newEntryColor)
|
||||
_binding.groupColor.setBackgroundColor(newEntryColor)
|
||||
_binding.groupColor.setTextColor(contrastNewEntryColor)
|
||||
_binding.name.setTextColor(contrastNewEntryColor)
|
||||
_binding.name.text = "0"
|
||||
}, {
|
||||
notifyItemRemoved(it)
|
||||
})
|
||||
|
||||
val entryColor = entry.second.color.color
|
||||
val contrastEntryColor = getContrastColor(entryColor)
|
||||
_binding.groupColor.setBackgroundColor(entryColor)
|
||||
_binding.groupColor.setTextColor(contrastEntryColor)
|
||||
_binding.name.setTextColor(contrastEntryColor)
|
||||
_binding.name.text = Data.visits.countVisited(entry.first).toString()
|
||||
|
||||
_binding.groupColor.setOnClickListener {
|
||||
Data.selected_group = entry.second
|
||||
selectDialog.dismiss()
|
||||
}
|
||||
if (!delete) {
|
||||
_binding.groupColor.setOnLongClickListener {
|
||||
dialogFragment.show(
|
||||
activity.supportFragmentManager,
|
||||
"AddColorDialogFragment"
|
||||
)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,152 +0,0 @@
|
||||
package net.helcel.beans.activity.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.textview.MaterialTextView
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.countries.GeoLoc
|
||||
import net.helcel.beans.countries.GeoLoc.LocType
|
||||
import net.helcel.beans.countries.World
|
||||
import net.helcel.beans.databinding.ItemListGroupBinding
|
||||
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
|
||||
import net.helcel.beans.helper.Theme.getContrastColor
|
||||
|
||||
class StatsListAdapter(private val stats: RecyclerView, private val total: MaterialTextView) :
|
||||
RecyclerView.Adapter<StatsListAdapter.StatsViewHolder>() {
|
||||
private val unit = "km²"
|
||||
|
||||
private var locMode = LocType.WORLD
|
||||
private lateinit var ctx: Context
|
||||
private var countMode: Boolean = true
|
||||
private var initialSum: Int = 0
|
||||
|
||||
private val wwwTotal: List<GeoLoc> = World.WWW.children.toList()
|
||||
private val countryTotal: List<GeoLoc> = World.WWW.children.flatMap { it.children }
|
||||
private val stateTotal: List<GeoLoc> =
|
||||
World.WWW.children.flatMap { it.children.flatMap { itt -> itt.children } }
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatsViewHolder {
|
||||
ctx = parent.context
|
||||
val binding =
|
||||
ItemListGroupBinding.inflate(LayoutInflater.from(ctx), parent, false)
|
||||
|
||||
return StatsViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: StatsViewHolder, pos: Int) {
|
||||
initialSum += if (pos == itemCount - 1) {
|
||||
holder.bind(
|
||||
Pair(
|
||||
AUTO_GROUP,
|
||||
Groups.Group(AUTO_GROUP, ctx.getString(R.string.uncategorized))
|
||||
)
|
||||
)
|
||||
} else {
|
||||
holder.bind(Data.groups.getGroupFromPos(pos))
|
||||
}
|
||||
val unitNow = if (!countMode) unit else ""
|
||||
total.text = Settings.getStats(ctx, initialSum, getTotal(), unitNow)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return Data.groups.size() + 1
|
||||
}
|
||||
|
||||
private fun getTotal(): Int {
|
||||
return if (countMode) {
|
||||
when (locMode) {
|
||||
LocType.WORLD -> wwwTotal.size
|
||||
LocType.COUNTRY -> countryTotal.size
|
||||
LocType.STATE -> stateTotal.size
|
||||
else -> 0
|
||||
}
|
||||
} else {
|
||||
when (locMode) {
|
||||
LocType.WORLD -> wwwTotal.sumOf { it.area }
|
||||
LocType.COUNTRY -> countryTotal.sumOf { it.area }
|
||||
LocType.STATE -> stateTotal.sumOf { it.area }
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshMode(mode: LocType) {
|
||||
val sum = (0 until itemCount).map {
|
||||
val viewHolder = stats.findViewHolderForAdapterPosition(it) as? StatsViewHolder
|
||||
viewHolder?.refresh(mode)
|
||||
}.reduce { acc, i -> acc?.plus((i ?: 0)) }
|
||||
val unitNow = if (!countMode) unit else ""
|
||||
total.text = Settings.getStats(ctx, sum, getTotal(), unitNow)
|
||||
}
|
||||
|
||||
fun invertCountMode() {
|
||||
countMode = !countMode
|
||||
refreshMode(locMode)
|
||||
}
|
||||
|
||||
inner class StatsViewHolder(
|
||||
private val _binding: ItemListGroupBinding
|
||||
) : RecyclerView.ViewHolder(_binding.root) {
|
||||
|
||||
private lateinit var data: Pair<Int, Groups.Group>
|
||||
|
||||
private lateinit var wwwCount: List<GeoLoc>
|
||||
private lateinit var countryCount: List<GeoLoc>
|
||||
private lateinit var stateCount: List<GeoLoc>
|
||||
|
||||
fun bind(entry: Pair<Int, Groups.Group>): Int {
|
||||
data = entry
|
||||
_binding.groupColor.text = entry.second.name
|
||||
|
||||
val entryColor = data.second.color.color
|
||||
val contrastEntryColor = getContrastColor(entryColor)
|
||||
_binding.groupColor.setBackgroundColor(entryColor)
|
||||
_binding.groupColor.setTextColor(contrastEntryColor)
|
||||
_binding.name.setTextColor(contrastEntryColor)
|
||||
|
||||
_binding.groupColor.setOnClickListener { invertCountMode() }
|
||||
compute()
|
||||
return refresh(locMode)
|
||||
}
|
||||
|
||||
private fun compute() {
|
||||
val visited = Data.visits.getVisitedByValue(data.first)
|
||||
wwwCount = World.WWW.children.filter { it.code in visited }
|
||||
countryCount =
|
||||
World.WWW.children.map { it.children.filter { itt -> itt.code in visited } }
|
||||
.flatten()
|
||||
stateCount =
|
||||
World.WWW.children.map { it.children.map { itt -> itt.children.filter { ittt -> ittt.code in visited } } }
|
||||
.flatten().flatten()
|
||||
}
|
||||
|
||||
fun refresh(mode: LocType): Int {
|
||||
locMode = mode
|
||||
return if (countMode) {
|
||||
val count = when (locMode) {
|
||||
LocType.WORLD -> wwwCount.size
|
||||
LocType.COUNTRY -> countryCount.size
|
||||
LocType.STATE -> stateCount.size
|
||||
else -> -1
|
||||
}
|
||||
_binding.name.text = count.toString()
|
||||
count
|
||||
} else {
|
||||
val area = when (locMode) {
|
||||
LocType.WORLD -> wwwCount.sumOf { it.area }
|
||||
LocType.COUNTRY -> countryCount.sumOf { it.area }
|
||||
LocType.STATE -> stateCount.sumOf { it.area }
|
||||
else -> -1
|
||||
}
|
||||
_binding.name.text = ctx.getString(R.string.number_with_unit, area, unit)
|
||||
area
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
package net.helcel.beans.activity.adapter
|
||||
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import net.helcel.beans.activity.fragment.EditPlaceFragment
|
||||
import kotlin.math.max
|
||||
|
||||
class ViewPagerAdapter(
|
||||
fragmentManager: FragmentManager,
|
||||
lifecycle: Lifecycle,
|
||||
private val viewPager: ViewPager2
|
||||
) :
|
||||
FragmentStateAdapter(fragmentManager, lifecycle) {
|
||||
|
||||
private val fragmentList: MutableList<EditPlaceFragment> = ArrayList()
|
||||
|
||||
fun addFragment(src: EditPlaceFragment?, target: EditPlaceFragment) {
|
||||
val idx = fragmentList.indexOf(src)
|
||||
viewPager.currentItem = max(0, idx)
|
||||
if (src != null && idx >= 0) {
|
||||
fragmentList.subList(idx + 1, fragmentList.size).clear()
|
||||
}
|
||||
fragmentList.add(target)
|
||||
notifyItemRangeChanged(max(0, idx), fragmentList.size)
|
||||
viewPager.currentItem = fragmentList.size - 1
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return fragmentList.size
|
||||
}
|
||||
|
||||
fun backPressed(): Boolean {
|
||||
if (viewPager.currentItem == 0) {
|
||||
return false
|
||||
}
|
||||
val target = viewPager.currentItem
|
||||
while (fragmentList.size > target) {
|
||||
fragmentList.removeLast()
|
||||
notifyItemRemoved(fragmentList.size)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun getLabel(pos: Int): String {
|
||||
return fragmentList[pos].loc.fullName
|
||||
}
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return fragmentList[position]
|
||||
}
|
||||
|
||||
fun refreshColors(colorDrawable: ColorDrawable) {
|
||||
fragmentList.forEach{ it.refreshColors(colorDrawable)}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,21 +0,0 @@
|
||||
package net.helcel.beans.activity.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import net.helcel.beans.databinding.FragmentAboutBinding
|
||||
|
||||
class AboutFragment : Fragment() {
|
||||
private lateinit var _binding: FragmentAboutBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentAboutBinding.inflate(inflater, container, false)
|
||||
return _binding.root
|
||||
}
|
||||
}
|
@@ -1,155 +0,0 @@
|
||||
package net.helcel.beans.activity.fragment
|
||||
|
||||
import android.app.Dialog
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.View
|
||||
import androidx.core.graphics.blue
|
||||
import androidx.core.graphics.green
|
||||
import androidx.core.graphics.red
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.slider.Slider
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.databinding.FragmentEditGroupsAddBinding
|
||||
import net.helcel.beans.helper.Data
|
||||
import net.helcel.beans.helper.Groups
|
||||
import net.helcel.beans.helper.Theme.colorToHex6
|
||||
|
||||
|
||||
class EditGroupAddFragment(
|
||||
private val key: Int = 0,
|
||||
val onAddCb: (Int) -> Unit,
|
||||
val onDelCb: (Int) -> Unit,
|
||||
private val deleteEnabled: Boolean = true
|
||||
) : DialogFragment() {
|
||||
|
||||
private lateinit var _binding: FragmentEditGroupsAddBinding
|
||||
private val grp = Data.groups.getGroupFromKey(key)
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||
_binding = FragmentEditGroupsAddBinding.inflate(layoutInflater)
|
||||
|
||||
setupSlider(_binding.colorR, grp.color.color.red / 255F)
|
||||
setupSlider(_binding.colorG, grp.color.color.green / 255F)
|
||||
setupSlider(_binding.colorB, grp.color.color.blue / 255F)
|
||||
setupText(_binding.groupColor, grp)
|
||||
|
||||
_binding.colorView.background = ColorDrawable(grp.color.color)
|
||||
|
||||
|
||||
if (key == 0 || !deleteEnabled) {
|
||||
_binding.btnDelete.visibility = View.INVISIBLE
|
||||
_binding.btnDelete.isEnabled = false
|
||||
}
|
||||
_binding.btnDelete.setOnClickListener {
|
||||
MaterialAlertDialogBuilder(requireActivity())
|
||||
.setMessage(R.string.delete_group)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val pos = Data.groups.findGroupPos(key)
|
||||
// Remove all countries belonging to that group
|
||||
Data.visits.deleteVisited(key)
|
||||
// Delete the group
|
||||
Data.groups.deleteGroup(key)
|
||||
Data.saveData()
|
||||
onDelCb(pos)
|
||||
dialog?.dismiss()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.show()
|
||||
}
|
||||
|
||||
_binding.btnOk.setOnClickListener {
|
||||
val name = _binding.groupName.text.toString()
|
||||
val color = _binding.groupColor.text.toString()
|
||||
val key = (if (key != 0) key else Data.groups.genKey())
|
||||
Data.groups.setGroup(key, name, ColorDrawable(Color.parseColor("#$color")))
|
||||
Data.saveData()
|
||||
onAddCb(key)
|
||||
dialog?.dismiss()
|
||||
}
|
||||
|
||||
_binding.btnCancel.setOnClickListener {
|
||||
dialog?.cancel()
|
||||
}
|
||||
|
||||
_binding.groupName.setText(grp.name)
|
||||
builder.setView(_binding.root)
|
||||
return builder.create()
|
||||
}
|
||||
|
||||
private fun setupText(s: TextInputEditText, grp: Groups.Group?) {
|
||||
s.setText(colorToHex6(ColorDrawable(grp?.color?.color ?: 0)).substring(1))
|
||||
s.addTextChangedListener(
|
||||
EditTextListener(
|
||||
_binding.colorR,
|
||||
_binding.colorG,
|
||||
_binding.colorB,
|
||||
_binding.groupColor,
|
||||
_binding.colorView
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupSlider(s: Slider, v: Float) {
|
||||
s.valueFrom = 0F
|
||||
s.valueTo = 1F
|
||||
s.value = v
|
||||
s.addOnChangeListener(
|
||||
SliderOnChangeListener(
|
||||
_binding.colorR,
|
||||
_binding.colorG,
|
||||
_binding.colorB,
|
||||
_binding.groupColor,
|
||||
_binding.colorView
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class EditTextListener(
|
||||
private val colorEditR: Slider,
|
||||
private val colorEditG: Slider,
|
||||
private val colorEditB: Slider,
|
||||
private val colorEditText: TextInputEditText,
|
||||
private val colorView: View
|
||||
) : TextWatcher {
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
val col: Color
|
||||
try {
|
||||
col = Color.valueOf(Color.parseColor("#${colorEditText.text}"))
|
||||
} catch (e: Exception) {
|
||||
return
|
||||
}
|
||||
|
||||
colorEditR.value = col.red()
|
||||
colorEditG.value = col.green()
|
||||
colorEditB.value = col.blue()
|
||||
colorView.background = ColorDrawable(col.toArgb())
|
||||
}
|
||||
}
|
||||
|
||||
private class SliderOnChangeListener(
|
||||
private val colorEditR: Slider,
|
||||
private val colorEditG: Slider,
|
||||
private val colorEditB: Slider,
|
||||
private val colorEditText: TextInputEditText,
|
||||
private val colorView: View
|
||||
) : Slider.OnChangeListener {
|
||||
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
|
||||
val rgb =
|
||||
ColorDrawable(Color.argb(1F, colorEditR.value, colorEditG.value, colorEditB.value))
|
||||
colorEditText.setText(colorToHex6(rgb).substring(1))
|
||||
colorView.background = rgb
|
||||
}
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
package net.helcel.beans.activity.fragment
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.activity.adapter.GroupListAdapter
|
||||
import net.helcel.beans.databinding.FragmentEditPlacesColorsBinding
|
||||
import net.helcel.beans.helper.Data
|
||||
import net.helcel.beans.helper.DialogCloser
|
||||
|
||||
|
||||
class EditPlaceColorFragment(private val parent: DialogCloser, private val delete: Boolean = false) :
|
||||
DialogFragment() {
|
||||
|
||||
private lateinit var _binding: FragmentEditPlacesColorsBinding
|
||||
private lateinit var listAdapt: GroupListAdapter
|
||||
private var clear: Boolean = false
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val ctx = requireContext()
|
||||
val builder = MaterialAlertDialogBuilder(ctx)
|
||||
_binding = FragmentEditPlacesColorsBinding.inflate(layoutInflater)
|
||||
_binding.btnAdd.setOnClickListener {
|
||||
EditGroupAddFragment(0, {
|
||||
listAdapt.notifyItemInserted(Data.groups.findGroupPos(it))
|
||||
}, {}).show(requireActivity().supportFragmentManager, "AddColorDialogFragment")
|
||||
}
|
||||
_binding.btnClear.setOnClickListener {
|
||||
clear = true
|
||||
dialog?.dismiss()
|
||||
}
|
||||
|
||||
val dialog = builder.setView(_binding.root).create()
|
||||
listAdapt = GroupListAdapter(requireActivity(), this, delete)
|
||||
_binding.groupsColor.layoutManager =
|
||||
LinearLayoutManager(ctx, RecyclerView.VERTICAL, false)
|
||||
_binding.groupsColor.adapter = listAdapt
|
||||
|
||||
if (delete) {
|
||||
_binding.btnAdd.visibility = View.GONE
|
||||
_binding.btnClear.text = ctx.getString(R.string.cancel)
|
||||
_binding.warningText.text = ctx.getString(R.string.select_group)
|
||||
} else {
|
||||
_binding.warningText.text = ctx.getString(R.string.edit_group)
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
super.onDismiss(dialog)
|
||||
parent.onDialogDismiss(clear)
|
||||
}
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
package net.helcel.beans.activity.fragment
|
||||
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import net.helcel.beans.activity.adapter.GeolocListAdapter
|
||||
import net.helcel.beans.activity.adapter.GeolocListAdapter.FoldingListViewHolder
|
||||
import net.helcel.beans.activity.adapter.ViewPagerAdapter
|
||||
import net.helcel.beans.countries.GeoLoc
|
||||
import net.helcel.beans.databinding.FragmentEditPlacesBinding
|
||||
|
||||
class EditPlaceFragment(val loc: GeoLoc, private val pager: ViewPagerAdapter, private val holder: FoldingListViewHolder? = null) : Fragment() {
|
||||
private lateinit var _binding: FragmentEditPlacesBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentEditPlacesBinding.inflate(inflater, container, false)
|
||||
|
||||
_binding.list.setItemViewCacheSize(5)
|
||||
_binding.list.setHasFixedSize(true)
|
||||
_binding.list.layoutManager =
|
||||
LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
|
||||
_binding.list.adapter = GeolocListAdapter(this, loc, pager, holder)
|
||||
return _binding.root
|
||||
}
|
||||
|
||||
fun refreshColors(colorDrawable: ColorDrawable) {
|
||||
(_binding.list.adapter as GeolocListAdapter?)?.refreshColors(colorDrawable)
|
||||
}
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
package net.helcel.beans.activity.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.databinding.FragmentLicenseBinding
|
||||
|
||||
class LicenseFragment : Fragment() {
|
||||
private lateinit var _binding: FragmentLicenseBinding
|
||||
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentLicenseBinding.inflate(inflater, container, false)
|
||||
|
||||
val librariesFragment = LibsBuilder()
|
||||
.withLicenseShown(true)
|
||||
.supportFragment()
|
||||
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.license_fragment_view, librariesFragment)
|
||||
.commit()
|
||||
|
||||
return _binding.root
|
||||
}
|
||||
}
|
@@ -1,142 +0,0 @@
|
||||
package net.helcel.beans.activity.fragment
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.countries.GeoLocImporter
|
||||
import net.helcel.beans.helper.Data
|
||||
import net.helcel.beans.helper.DialogCloser
|
||||
import net.helcel.beans.helper.Settings
|
||||
|
||||
|
||||
class SettingsFragment : PreferenceFragmentCompat(), DialogCloser {
|
||||
private var savedInstanceState: Bundle? = null
|
||||
private var rootKey: String? = null
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
this.savedInstanceState = savedInstanceState
|
||||
this.rootKey = rootKey
|
||||
|
||||
setPreferencesFromResource(R.xml.fragment_settings, rootKey)
|
||||
val ctx = requireContext()
|
||||
|
||||
// Select Light/Dark/System Mode
|
||||
findPreference<Preference>(getString(R.string.key_theme))?.setOnPreferenceChangeListener { _, key ->
|
||||
setTheme(ctx, key as String)
|
||||
}
|
||||
|
||||
// Select map projection
|
||||
findPreference<Preference>(getString(R.string.key_projection))?.setOnPreferenceChangeListener { _, key ->
|
||||
Settings.refreshProjection()
|
||||
}
|
||||
|
||||
// Toggle groups
|
||||
findPreference<Preference>(getString(R.string.key_group))?.setOnPreferenceChangeListener { _, key ->
|
||||
if (key as String == ctx.getString(R.string.off)) {
|
||||
val fragment = EditPlaceColorFragment(this, true)
|
||||
fragment.show(
|
||||
this.parentFragmentManager,
|
||||
"AddColorDialogFragment"
|
||||
)
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle regional geolocs
|
||||
findPreference<Preference>(getString(R.string.key_regional))?.setOnPreferenceChangeListener { _, key ->
|
||||
when (key as String) {
|
||||
ctx.getString(R.string.off) -> {
|
||||
MaterialAlertDialogBuilder(requireActivity())
|
||||
.setMessage(R.string.delete_regions)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
GeoLocImporter.clearStates()
|
||||
PreferenceManager.getDefaultSharedPreferences(ctx).edit().putString(
|
||||
ctx.getString(R.string.key_regional),
|
||||
ctx.getString(R.string.off)
|
||||
).apply()
|
||||
refreshPreferences()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.show()
|
||||
false
|
||||
}
|
||||
|
||||
ctx.getString(R.string.on) -> {
|
||||
GeoLocImporter.importStates(ctx, true)
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Open license fragment
|
||||
findPreference<Preference>(getString(R.string.licenses))?.setOnPreferenceClickListener {
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_view, LicenseFragment(), getString(R.string.licenses))
|
||||
.commit()
|
||||
true
|
||||
}
|
||||
|
||||
// Open about fragment
|
||||
findPreference<Preference>(getString(R.string.about))?.setOnPreferenceClickListener {
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_view, AboutFragment(), getString(R.string.about))
|
||||
.commit()
|
||||
true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun setTheme(ctx: Context, key: String?): Boolean {
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
when (key) {
|
||||
ctx.getString(R.string.system) -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
ctx.getString(R.string.light) -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
ctx.getString(R.string.dark) -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
||||
)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDialogDismiss(clear: Boolean) {
|
||||
// When turning groups off, select one group to keep and reassign everything
|
||||
Data.selected_group?.let { selectedGroup ->
|
||||
// Reassign all visited that are not to selectedGroup to selectedGroup
|
||||
Data.visits.reassignAllVisitedToGroup(selectedGroup.key)
|
||||
|
||||
// Delete all groups that are not selectedGroup
|
||||
Data.groups.deleteAllExcept(selectedGroup.key)
|
||||
|
||||
// Save and clear global variables
|
||||
Data.saveData()
|
||||
Data.selected_geoloc = null
|
||||
Data.selected_group = null
|
||||
|
||||
// Actually change preference
|
||||
val ctx = requireContext()
|
||||
val sp = PreferenceManager.getDefaultSharedPreferences(ctx)
|
||||
sp.edit().putString(ctx.getString(R.string.key_group), ctx.getString(R.string.off))
|
||||
.apply()
|
||||
|
||||
// Refresh entire preference fragment to reflect changes
|
||||
refreshPreferences()
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshPreferences() {
|
||||
preferenceScreen.removeAll()
|
||||
onCreatePreferences(savedInstanceState, rootKey)
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
package net.helcel.beans.activity.sub
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.BuildConfig
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun AboutScreen(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(top = 20.dp).background(MaterialTheme.colors.background),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_launcher_foreground),
|
||||
contentDescription = "Logo",
|
||||
modifier = Modifier
|
||||
.size(300.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = BuildConfig.APP_NAME,
|
||||
fontSize = 30.sp,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(vertical = 15.dp, horizontal = 10.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = BuildConfig.VERSION_NAME,
|
||||
fontSize = 25.sp,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(vertical = 15.dp, horizontal = 10.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.beans_is_foss),
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
modifier = Modifier.padding(vertical = 15.dp, horizontal = 10.dp)
|
||||
)
|
||||
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val uri = stringResource(R.string.beans_repo_uri)
|
||||
Text(
|
||||
text = stringResource(id = R.string.beans_repo,uri),
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
uriHandler.openUri(uri)
|
||||
}
|
||||
.padding(vertical = 15.dp, horizontal = 10.dp)
|
||||
)
|
||||
}
|
||||
}
|
@@ -0,0 +1,222 @@
|
||||
package net.helcel.beans.activity.sub
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CornerSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Slider
|
||||
import androidx.compose.material.SliderDefaults
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import net.helcel.beans.helper.Data
|
||||
import net.helcel.beans.helper.Theme.colorToHex6
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.core.graphics.toColorInt
|
||||
import net.helcel.beans.R
|
||||
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun EditGroupPreview(){
|
||||
EditGroupDialog(0,true,{},{},{})
|
||||
}
|
||||
@Composable
|
||||
fun EditGroupDialog(
|
||||
key: Int = 0,
|
||||
deleteEnabled: Boolean = true,
|
||||
onAddCb: (Int) -> Unit,
|
||||
onDelCb: (Int) -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
val group by remember { mutableStateOf(Data.groups.getGroupFromKey(key)) }
|
||||
var name by remember { mutableStateOf(group.name) }
|
||||
var colorHex by remember {
|
||||
mutableStateOf(colorToHex6(group.color.color.toDrawable()).substring(1))
|
||||
}
|
||||
|
||||
// Convert hex to Color safely
|
||||
var color = remember {try {
|
||||
Color("#$colorHex".toColorInt())
|
||||
} catch (_: Exception) {
|
||||
Color.Gray
|
||||
}}
|
||||
var r by remember { mutableIntStateOf((color.red *255).toInt()) }
|
||||
var g by remember { mutableIntStateOf((color.green*255).toInt()) }
|
||||
var b by remember { mutableIntStateOf((color.blue*255).toInt()) }
|
||||
|
||||
fun updateHexFromSliders() {
|
||||
val newColor = Color(r, g, b)
|
||||
colorHex = colorToHex6(newColor.toArgb().toDrawable()).substring(1)
|
||||
color = newColor
|
||||
}
|
||||
Dialog(
|
||||
onDismissRequest = onDismiss,
|
||||
content = {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
MaterialTheme.colors.background,
|
||||
RoundedCornerShape(corner = CornerSize(16.dp))
|
||||
)
|
||||
.padding(16.dp),
|
||||
|
||||
) {
|
||||
Text(
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
style = MaterialTheme.typography.h6,
|
||||
text = if (key == 0) stringResource(R.string.action_add)
|
||||
else stringResource(R.string.action_edit),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
// Group name
|
||||
OutlinedTextField(
|
||||
value = name,
|
||||
onValueChange = { it: String -> name = it },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = { Text(stringResource(R.string.name)) },
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
unfocusedTextColor = MaterialTheme.colors.onBackground,
|
||||
focusedTextColor = MaterialTheme.colors.onBackground,
|
||||
),
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
// Color preview
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(96.dp, (96).dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(color),
|
||||
propagateMinConstraints = true,
|
||||
|
||||
content = {}
|
||||
)
|
||||
Column {
|
||||
ColorSlider(
|
||||
r.toFloat(),
|
||||
{ r = it.toInt(); updateHexFromSliders() },
|
||||
Color(255, 0, 0)
|
||||
)
|
||||
ColorSlider(
|
||||
g.toFloat(),
|
||||
{ g = it.toInt(); updateHexFromSliders() },
|
||||
Color(0, 255, 0)
|
||||
)
|
||||
ColorSlider(
|
||||
b.toFloat(),
|
||||
{ b = it.toInt(); updateHexFromSliders() },
|
||||
Color(0, 0, 255)
|
||||
)
|
||||
}
|
||||
}
|
||||
// Hex input
|
||||
OutlinedTextField(
|
||||
value = colorHex,
|
||||
onValueChange = { n:String->
|
||||
colorHex = n.filter { it.isLetterOrDigit() }
|
||||
},
|
||||
label = { Text(text="Color (hex)", color=MaterialTheme.colors.onBackground) },
|
||||
singleLine = true,
|
||||
textStyle = TextStyle(
|
||||
fontSize = 12.sp
|
||||
),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
unfocusedTextColor =MaterialTheme.colors.onBackground,
|
||||
focusedTextColor = MaterialTheme.colors.onBackground,
|
||||
),
|
||||
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
Button(onClick = {
|
||||
val newKey = if (key != 0) key else Data.groups.genKey()
|
||||
Data.groups.setGroup(
|
||||
newKey,
|
||||
name,
|
||||
"#$colorHex".toColorInt().toDrawable()
|
||||
)
|
||||
Data.saveData()
|
||||
onAddCb(newKey)
|
||||
onDismiss()
|
||||
}, ) {
|
||||
Text("OK")
|
||||
}
|
||||
if (key != 0 && deleteEnabled) {
|
||||
TextButton(onClick = {
|
||||
val pos = Data.groups.findGroupPos(key)
|
||||
Data.visits.deleteVisited(key)
|
||||
Data.groups.deleteGroup(key)
|
||||
Data.saveData()
|
||||
onDelCb(pos)
|
||||
onDismiss()
|
||||
}) {
|
||||
Text("Delete")
|
||||
}
|
||||
}
|
||||
TextButton(onClick = { onDismiss() }) {
|
||||
Text("Cancel")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ColorSlider(v: Float, onChange:(Float)->Unit, c:Color ){
|
||||
Slider(
|
||||
value = v,
|
||||
onValueChange = onChange,
|
||||
valueRange = 0f..255f,
|
||||
steps = 255,
|
||||
modifier = Modifier.height(32.dp),
|
||||
colors = SliderDefaults.colors(
|
||||
thumbColor = c,
|
||||
activeTickColor = c,
|
||||
inactiveTickColor = MaterialTheme.colors.onBackground,
|
||||
)
|
||||
)
|
||||
}
|
@@ -0,0 +1,193 @@
|
||||
package net.helcel.beans.activity.sub
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.CornerSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
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 net.helcel.beans.R
|
||||
import net.helcel.beans.helper.Data
|
||||
import net.helcel.beans.helper.Groups
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import net.helcel.beans.activity.SysTheme
|
||||
|
||||
@Composable
|
||||
fun EditPlaceDialog(delete: Boolean, onDialogDismiss: (Boolean)->Unit){
|
||||
SysTheme {
|
||||
var showEditGroupDialog by remember { mutableStateOf(false) }
|
||||
var showEditPlaceColorDialog by remember { mutableStateOf(true) }
|
||||
var showSelectedKey by remember { mutableIntStateOf(-1) }
|
||||
var showDelete by remember { mutableStateOf(false) }
|
||||
if (showEditGroupDialog)
|
||||
EditGroupDialog(
|
||||
key = showSelectedKey,
|
||||
deleteEnabled = showDelete,
|
||||
onAddCb = { },
|
||||
onDelCb = {
|
||||
|
||||
},
|
||||
onDismiss = {
|
||||
showEditGroupDialog = false
|
||||
},
|
||||
)
|
||||
if (showEditPlaceColorDialog)
|
||||
EditPlaceColorDialog(
|
||||
delete,
|
||||
onAdd = {
|
||||
showSelectedKey = it
|
||||
showDelete = false
|
||||
showEditGroupDialog = true
|
||||
},
|
||||
onDelete = {
|
||||
showSelectedKey = it
|
||||
showDelete = true
|
||||
showEditGroupDialog = true
|
||||
},
|
||||
onClear = {
|
||||
showEditPlaceColorDialog = false
|
||||
onDialogDismiss(true)
|
||||
},
|
||||
onDismiss = {
|
||||
showEditPlaceColorDialog = false
|
||||
onDialogDismiss(false)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun GroupListPreview() {
|
||||
Data.groups = Groups(0, HashMap())
|
||||
Data.groups.setGroup(0, "Testing", Color.Red.toArgb().toDrawable())
|
||||
Data.groups.setGroup(1, "Testing", Color.Blue.toArgb().toDrawable())
|
||||
EditPlaceColorDialog(false,{},{},{},{})
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EditPlaceColorDialog(
|
||||
deleteMode: Boolean = false,
|
||||
onAdd: (Int) -> Unit = {},
|
||||
onDelete: (Int) -> Unit= {},
|
||||
onClear: () -> Unit= {},
|
||||
onDismiss: () -> Unit= {},
|
||||
) {
|
||||
val groups by Data.groups.groupsFlow.collectAsState()
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = onDismiss,
|
||||
content = {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
MaterialTheme.colors.background,
|
||||
RoundedCornerShape(corner = CornerSize(16.dp)))
|
||||
.padding(16.dp)
|
||||
,
|
||||
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.typography.h6,
|
||||
color=MaterialTheme.colors.onBackground,
|
||||
text = if (deleteMode) stringResource(R.string.select_group)
|
||||
else stringResource(R.string.edit_group)
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.caption,
|
||||
color=MaterialTheme.colors.onBackground,
|
||||
text = if (deleteMode) stringResource(R.string.select_group_sub)
|
||||
else stringResource(R.string.edit_group_sub)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = 300.dp) // cap dialog growth
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
//.weight(1f)
|
||||
) {
|
||||
items(groups, key = { it.key }) { group ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.combinedClickable(
|
||||
onClick = { Data.selected_group = group; onDismiss() },
|
||||
onLongClick = { onDelete(group.key) })
|
||||
.background(
|
||||
Color(88, 88, 88, 88),
|
||||
RoundedCornerShape(corner = CornerSize(16.dp))
|
||||
)
|
||||
.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.background(Color(group.color.color), CircleShape)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(color=MaterialTheme.colors.onBackground,text=group.name)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End) {
|
||||
if (!deleteMode) {
|
||||
Button(onClick = { onAdd(0) }) {
|
||||
Text(stringResource(R.string.action_add))
|
||||
}
|
||||
}
|
||||
TextButton(onClick = {
|
||||
if (deleteMode) onDismiss() else onClear()
|
||||
}) {
|
||||
Text(
|
||||
text = if (deleteMode) stringResource(R.string.cancel)
|
||||
else stringResource(R.string.action_clear)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
@@ -0,0 +1,218 @@
|
||||
package net.helcel.beans.activity.sub
|
||||
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.CheckboxDefaults
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Tab
|
||||
import androidx.compose.material.TabRow
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TriStateCheckbox
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.state.ToggleableState
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import net.helcel.beans.countries.GeoLoc
|
||||
import net.helcel.beans.countries.Group
|
||||
import net.helcel.beans.countries.World
|
||||
import net.helcel.beans.helper.AUTO_GROUP
|
||||
import net.helcel.beans.helper.Data
|
||||
import net.helcel.beans.helper.NO_GROUP
|
||||
import net.helcel.beans.helper.Settings
|
||||
import kotlin.math.min
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun EditPlaceScreenPreview(){
|
||||
EditPlaceScreen(Group.EEE)
|
||||
}
|
||||
|
||||
fun syncVisited(loc: GeoLoc?=World.WWW){
|
||||
loc?.children?.forEach { tt ->
|
||||
tt.children.forEach {itc->
|
||||
if(Data.visits.getVisited(itc) in listOf(AUTO_GROUP,NO_GROUP)) {
|
||||
if(itc.children.any { itcc -> Data.visits.getVisited(itcc) != NO_GROUP })
|
||||
Data.visits.setVisited(itc, AUTO_GROUP)
|
||||
else
|
||||
Data.visits.setVisited(itc, NO_GROUP)
|
||||
}
|
||||
}
|
||||
if(Data.visits.getVisited(tt) in listOf(AUTO_GROUP,NO_GROUP)) {
|
||||
if(tt.children.any { itc -> Data.visits.getVisited(itc) != NO_GROUP })
|
||||
Data.visits.setVisited(tt, AUTO_GROUP)
|
||||
else
|
||||
Data.visits.setVisited(tt, NO_GROUP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EditPlaceScreen(loc: GeoLoc, onExit:()->Unit={}) {
|
||||
var showEdit by remember { mutableStateOf(false) }
|
||||
val tabs : SnapshotStateList<GeoLoc> = remember { mutableStateListOf(loc) }
|
||||
val ctx = LocalContext.current
|
||||
var selectedTab by remember { mutableIntStateOf(0) }
|
||||
|
||||
LaunchedEffect(tabs.size) {
|
||||
selectedTab = tabs.lastIndex
|
||||
}
|
||||
SideEffect {
|
||||
syncVisited()
|
||||
}
|
||||
BackHandler {
|
||||
if (tabs.size > 1) tabs.removeAt(tabs.lastIndex)
|
||||
else onExit()
|
||||
}
|
||||
if(showEdit)
|
||||
EditPlaceDialog(false) {
|
||||
showEdit = false
|
||||
if (it) {
|
||||
Data.visits.setVisited(Data.selected_geoloc, NO_GROUP)
|
||||
Data.saveData()
|
||||
|
||||
if (Data.selected_geoloc!=null && Data.selected_geoloc!!.children.any { itc-> Data.visits.getVisited(itc) != NO_GROUP }) {
|
||||
Data.clearing_geoloc = Data.selected_geoloc
|
||||
}
|
||||
}
|
||||
if (Data.selected_group != null && Data.selected_geoloc != null) {
|
||||
Data.visits.setVisited(Data.selected_geoloc, Data.selected_group!!.key)
|
||||
Data.saveData()
|
||||
}
|
||||
Data.selected_geoloc = null
|
||||
Data.selected_group = null
|
||||
}
|
||||
|
||||
Column {
|
||||
val currentTab = tabs.getOrNull(selectedTab) ?: return@Column
|
||||
TabRow(
|
||||
selectedTabIndex = min(tabs.lastIndex, selectedTab),
|
||||
) {
|
||||
tabs.forEachIndexed { index, tab ->
|
||||
Tab(
|
||||
selected = selectedTab == index,
|
||||
onClick = {
|
||||
while (tabs.size > index + 1)
|
||||
tabs.removeAt(tabs.lastIndex)
|
||||
},
|
||||
text = { Text(tab.fullName) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
items(currentTab.children.toList(), key= {it.code}) { loc ->
|
||||
|
||||
GeoLocRow(loc, {
|
||||
if (loc.children.isNotEmpty()){
|
||||
tabs.add(loc)
|
||||
}
|
||||
}, {
|
||||
Data.selected_geoloc = loc
|
||||
if (Data.groups.size() == 1 && Settings.isSingleGroup(ctx)) {
|
||||
Data.visits.setVisited(Data.selected_geoloc,
|
||||
if (it != ToggleableState.On) Data.groups.getUniqueEntry()!!.key
|
||||
else if(Data.selected_geoloc?.children?.any{ itc->
|
||||
Data.visits.getVisited(itc)!= NO_GROUP } == true) AUTO_GROUP
|
||||
else NO_GROUP
|
||||
)
|
||||
Data.selected_group = null
|
||||
} else {
|
||||
Data.selected_group = null
|
||||
showEdit=true
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun GeoLocRow(
|
||||
loc: GeoLoc,
|
||||
onClick: () -> Unit,
|
||||
onCheckedChange: (ToggleableState) -> Unit
|
||||
) {
|
||||
val visits by Data.visits.visitsFlow.collectAsState()
|
||||
val checked by remember(visits, loc) {
|
||||
derivedStateOf {
|
||||
when (visits.getOrElse(loc.code) { NO_GROUP }) {
|
||||
NO_GROUP -> ToggleableState.Off
|
||||
AUTO_GROUP -> ToggleableState.Indeterminate
|
||||
else -> ToggleableState.On
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val color = if (Data.visits.getVisited(loc) !in listOf(NO_GROUP, AUTO_GROUP))
|
||||
Color(Data.groups.getGroupFromKey(Data.visits.getVisited(loc)).color.color)
|
||||
else MaterialTheme.colors.onBackground
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp)
|
||||
.clickable(onClick = onClick) // whole row clickable
|
||||
.padding(horizontal = 20.dp, vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = loc.fullName,
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "",//loc.children.size.toString(),
|
||||
style = MaterialTheme.typography.body2,
|
||||
modifier = Modifier.padding(end = 16.dp)
|
||||
)
|
||||
|
||||
TriStateCheckbox(
|
||||
state = checked,
|
||||
onClick= { onCheckedChange(checked) },
|
||||
colors = CheckboxDefaults.colors(
|
||||
checkedColor = color,
|
||||
),
|
||||
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier
|
||||
.height(2.dp)
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colors.onBackground))
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package net.helcel.beans.activity.sub
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.mikepenz.aboutlibraries.ui.compose.DefaultChipColors
|
||||
import com.mikepenz.aboutlibraries.ui.compose.DefaultLibraryColors
|
||||
import com.mikepenz.aboutlibraries.ui.compose.android.rememberLibraries
|
||||
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.activity.SysTheme
|
||||
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun LicenseScreen() {
|
||||
val libraries = rememberLibraries(R.raw.aboutlibraries)
|
||||
SysTheme {
|
||||
LibrariesContainer(
|
||||
libraries = libraries.value,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
colors = DefaultLibraryColors(
|
||||
backgroundColor = MaterialTheme.colors.background,
|
||||
contentColor = MaterialTheme.colors.onBackground,
|
||||
licenseChipColors = DefaultChipColors(
|
||||
containerColor = MaterialTheme.colors.primary,
|
||||
contentColor = MaterialTheme.colors.onPrimary,
|
||||
),
|
||||
versionChipColors = DefaultChipColors(
|
||||
containerColor = MaterialTheme.colors.secondary,
|
||||
contentColor = MaterialTheme.colors.onSecondary,
|
||||
),
|
||||
fundingChipColors = DefaultChipColors(
|
||||
containerColor = MaterialTheme.colors.secondary,
|
||||
contentColor = MaterialTheme.colors.onSecondary,
|
||||
),
|
||||
dialogConfirmButtonColor = MaterialTheme.colors.primary,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@@ -7,8 +7,8 @@ enum class Country(
|
||||
ATA("Antarctica", 14000000),
|
||||
|
||||
// HKG("Hong Kong", 1104),
|
||||
// MAC("Macao", 32),
|
||||
// ANT("Netherlands Antilles", 800),
|
||||
// MAC("Macao", 32),
|
||||
// ANT("Netherlands Antilles", 800),
|
||||
AFG("Afghanistan", 645487),
|
||||
XAD("Akrotiri and Dhekelia", 234),
|
||||
ALA("Åland", 1483),
|
||||
|
@@ -12,4 +12,4 @@ interface GeoLoc {
|
||||
|
||||
val type: LocType
|
||||
val children: Set<GeoLoc>
|
||||
}
|
||||
}
|
@@ -287,8 +287,8 @@ enum class Group(override val fullName: String, override val children: Set<GeoLo
|
||||
),
|
||||
|
||||
XXX(
|
||||
"Antarctica", setOf(
|
||||
ATA, // Antarctica: not in any other region
|
||||
"Other", setOf(
|
||||
|
||||
)
|
||||
),
|
||||
|
||||
|
@@ -6,14 +6,15 @@ enum class World(override val fullName: String, override val children: Set<GeoLo
|
||||
|
||||
WWW(
|
||||
"World", setOf(
|
||||
EEE, ABB, FFF, NNN, SRR, UUU, XXX
|
||||
)
|
||||
EEE, ABB, FFF, NNN, SRR, UUU, Country.ATA,
|
||||
|
||||
)
|
||||
);
|
||||
|
||||
override val area = children.fold(0) { acc, i ->
|
||||
acc + i.area
|
||||
}
|
||||
|
||||
|
||||
override val type = GeoLoc.LocType.WORLD
|
||||
override val code = this.name
|
||||
}
|
@@ -1,13 +1,21 @@
|
||||
package net.helcel.beans.helper
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.countries.GeoLoc
|
||||
import java.util.HashMap
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.core.content.edit
|
||||
import android.content.Intent
|
||||
import java.io.File
|
||||
|
||||
object Data {
|
||||
var visits : Visits = Visits(0, HashMap())
|
||||
@@ -33,7 +41,8 @@ object Data {
|
||||
|
||||
// Add default group "Visited" with app's color if there is no group already
|
||||
if (groups.size() == 0) {
|
||||
groups.setGroup(DEFAULT_GROUP, "Visited", ColorDrawable(ContextCompat.getColor(ctx, R.color.blue)))
|
||||
groups.setGroup(DEFAULT_GROUP, "Visited",
|
||||
ContextCompat.getColor(ctx, R.color.blue).toDrawable())
|
||||
saveData()
|
||||
}
|
||||
}
|
||||
@@ -41,9 +50,73 @@ object Data {
|
||||
fun saveData() {
|
||||
if(groups.id != visits.id) return
|
||||
val id = groups.id
|
||||
val editor = sharedPreferences.edit()
|
||||
editor.putString("groups_$id", groupsSerial.writeTo(groups))
|
||||
editor.putString("visits_$id", visitsSerial.writeTo(visits))
|
||||
editor.apply()
|
||||
sharedPreferences.edit {
|
||||
putString("groups_$id", groupsSerial.writeTo(groups))
|
||||
putString("visits_$id", visitsSerial.writeTo(visits))
|
||||
}
|
||||
}
|
||||
|
||||
fun exportData(ctx: Context, filepath: Uri){
|
||||
val groupsJson = groupsSerial.writeTo(groups)
|
||||
val visitsJson = visitsSerial.writeTo(visits)
|
||||
val outputStream = ctx.contentResolver.openOutputStream(filepath)
|
||||
outputStream?.write(
|
||||
buildString {
|
||||
append(groupsJson)
|
||||
append("\n---\n") // optional separator
|
||||
append(visitsJson)
|
||||
}.toByteArray())
|
||||
outputStream?.flush()
|
||||
outputStream?.close()
|
||||
}
|
||||
|
||||
|
||||
fun importData(ctx: Context, filePath: Uri) {
|
||||
val inputStream = ctx.contentResolver.openInputStream(filePath)
|
||||
val data = inputStream?.bufferedReader().use { it?.readText() }
|
||||
if(data==null) return
|
||||
val lines = data.split("\n---\n")
|
||||
val groupsJson = lines[0]
|
||||
val visitsJson = lines[1]
|
||||
|
||||
groups = if(groupsJson.isNotEmpty()) groupsSerial.readFrom(groupsJson.byteInputStream()) else groupsSerial.defaultValue
|
||||
visits = if(visitsJson.isNotEmpty()) visitsSerial.readFrom(visitsJson.byteInputStream()) else visitsSerial.defaultValue
|
||||
|
||||
// Add default group "Visited" with app's color if there is no group already
|
||||
if (groups.size() == 0) {
|
||||
groups.setGroup(DEFAULT_GROUP, "Visited",
|
||||
ContextCompat.getColor(ctx, R.color.blue).toDrawable())
|
||||
}
|
||||
saveData()
|
||||
}
|
||||
|
||||
fun doImport(ctx: Context, file: Uri?){
|
||||
if(file!=null) {
|
||||
importData(ctx, file)
|
||||
val intent = ctx.packageManager
|
||||
.getLaunchIntentForPackage(ctx.packageName)?.apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
ctx.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
fun doExport(ctx: Context){
|
||||
val fileName = "beans_backup.json"
|
||||
val resolver = ctx.contentResolver
|
||||
val contentValues = ContentValues().apply {
|
||||
put(MediaStore.Downloads.DISPLAY_NAME, fileName) // "backup.json"
|
||||
put(MediaStore.Downloads.MIME_TYPE, "text/*")
|
||||
put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
|
||||
}
|
||||
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
|
||||
} else {
|
||||
val downloadsDir =
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||
val file = File(downloadsDir, fileName)
|
||||
Uri.fromFile(file)
|
||||
}
|
||||
if(uri!=null) exportData(ctx, uri)
|
||||
}
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
package net.helcel.beans.helper
|
||||
|
||||
interface DialogCloser {
|
||||
fun onDialogDismiss(clear: Boolean)
|
||||
}
|
@@ -2,15 +2,16 @@ package net.helcel.beans.helper
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import androidx.core.content.ContextCompat
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.helcel.beans.R
|
||||
import java.io.InputStream
|
||||
import kotlin.coroutines.coroutineContext
|
||||
import kotlin.random.Random
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
|
||||
private const val randSeed = 0
|
||||
@@ -20,20 +21,23 @@ const val NO_GROUP = 0
|
||||
const val DEFAULT_GROUP = 1
|
||||
const val AUTO_GROUP = -1
|
||||
|
||||
|
||||
|
||||
@Serializable
|
||||
class Groups(val id: Int, private val grps: HashMap<Int, Group>) {
|
||||
@kotlinx.serialization.Transient
|
||||
private val _groupsFlow = MutableStateFlow<List<Group>>(grps.values.toList())
|
||||
@kotlinx.serialization.Transient
|
||||
val groupsFlow: StateFlow<List<Group>> = _groupsFlow.asStateFlow()
|
||||
|
||||
fun setGroup(key: Int, name: String, col: ColorDrawable) {
|
||||
grps[key] = Group(key, name, col)
|
||||
_groupsFlow.value = grps.values.toList()
|
||||
}
|
||||
|
||||
fun deleteGroup(key: Int) {
|
||||
grps.remove(key)
|
||||
}
|
||||
|
||||
fun deleteAllExcept(grp: Int) {
|
||||
val keysToDelete = grps.keys.filter { it != grp }
|
||||
keysToDelete.forEach { grps.remove(it) }
|
||||
_groupsFlow.value = grps.values.toList()
|
||||
}
|
||||
|
||||
fun getGroupFromKey(key: Int): Group {
|
||||
@@ -60,6 +64,7 @@ class Groups(val id: Int, private val grps: HashMap<Int, Group>) {
|
||||
}
|
||||
|
||||
fun getGroupFromPos(pos: Int): Pair<Int, Group> {
|
||||
if(grps.keys.isEmpty()) return Pair(NO_GROUP,Group(NO_GROUP,"-"))
|
||||
val key = grps.keys.toList()[pos]
|
||||
return Pair(key, getGroupFromKey(key))
|
||||
}
|
||||
@@ -74,9 +79,7 @@ class Groups(val id: Int, private val grps: HashMap<Int, Group>) {
|
||||
open class Group(
|
||||
val key: Int,
|
||||
val name: String,
|
||||
@Serializable(with = Theme.ColorDrawableSerializer::class) val color: ColorDrawable = ColorDrawable(
|
||||
Color.GRAY
|
||||
)
|
||||
@Serializable(with = Theme.ColorDrawableSerializer::class) val color: ColorDrawable = Color.GRAY.toDrawable()
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
|
@@ -4,19 +4,15 @@ import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceManager
|
||||
import net.helcel.beans.R
|
||||
import net.helcel.beans.activity.MainActivity
|
||||
import net.helcel.beans.activity.fragment.SettingsFragment
|
||||
import net.helcel.beans.activity.MainScreen
|
||||
|
||||
object Settings {
|
||||
|
||||
private lateinit var sp: SharedPreferences
|
||||
private lateinit var mainActivity: MainActivity
|
||||
fun start(ctx: MainActivity) {
|
||||
private lateinit var mainActivity: MainScreen
|
||||
fun start(ctx: MainScreen) {
|
||||
mainActivity = ctx
|
||||
sp = PreferenceManager.getDefaultSharedPreferences(ctx)
|
||||
SettingsFragment.setTheme(
|
||||
ctx, sp.getString(ctx.getString(R.string.key_theme), ctx.getString(R.string.system))
|
||||
)
|
||||
}
|
||||
|
||||
fun isSingleGroup(ctx: Context): Boolean {
|
||||
@@ -41,7 +37,7 @@ object Settings {
|
||||
}
|
||||
|
||||
fun refreshProjection(): Boolean {
|
||||
mainActivity.refreshProjection()
|
||||
(mainActivity).refreshProjection()
|
||||
return true
|
||||
}
|
||||
|
||||
|
@@ -1,23 +1,16 @@
|
||||
package net.helcel.beans.helper
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.util.TypedValue
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
|
||||
object Theme {
|
||||
fun colorWrapper(ctx: Context, res: Int): ColorDrawable {
|
||||
val colorPrimaryTyped = TypedValue()
|
||||
ctx.theme.resolveAttribute(res, colorPrimaryTyped, true)
|
||||
return ColorDrawable(colorPrimaryTyped.data)
|
||||
}
|
||||
|
||||
fun colorToHex6(c: ColorDrawable): String {
|
||||
return '#' + colorToHex8(c).substring(3)
|
||||
@@ -28,11 +21,6 @@ object Theme {
|
||||
return '#' + c.color.toHexString()
|
||||
}
|
||||
|
||||
fun createActionBar(ctx: AppCompatActivity, title: String) {
|
||||
ctx.supportActionBar?.title = title
|
||||
ctx.supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
fun getContrastColor(color: Int): Int {
|
||||
val whiteContrast = ColorUtils.calculateContrast(Color.WHITE, color)
|
||||
val blackContrast = ColorUtils.calculateContrast(Color.BLACK, color)
|
||||
@@ -43,7 +31,7 @@ object Theme {
|
||||
override val descriptor = PrimitiveSerialDescriptor("ColorDrawable", PrimitiveKind.INT)
|
||||
|
||||
override fun deserialize(decoder: Decoder): ColorDrawable {
|
||||
return ColorDrawable(decoder.decodeInt())
|
||||
return decoder.decodeInt().toDrawable()
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: ColorDrawable) {
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package net.helcel.beans.helper
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Serializer
|
||||
@@ -11,9 +13,17 @@ import java.io.InputStream
|
||||
@Serializable
|
||||
class Visits(val id: Int, private val locs: HashMap<String, Int>) {
|
||||
|
||||
@kotlinx.serialization.Transient
|
||||
private val _visitsFlow = MutableStateFlow<Map<String,Int>>(locs.toMutableMap())
|
||||
@kotlinx.serialization.Transient
|
||||
val visitsFlow: StateFlow<Map<String,Int>> = _visitsFlow
|
||||
|
||||
fun setVisited(key: GeoLoc?, b: Int) {
|
||||
if (key == null)
|
||||
return
|
||||
_visitsFlow.value = _visitsFlow.value.toMutableMap().apply {
|
||||
this[key.code] = b
|
||||
}
|
||||
locs[key.code] = b
|
||||
}
|
||||
|
||||
@@ -21,6 +31,9 @@ class Visits(val id: Int, private val locs: HashMap<String, Int>) {
|
||||
val keysToDelete = locs
|
||||
.filter { it.value == key }
|
||||
.map { it.key }
|
||||
_visitsFlow.value = _visitsFlow.value.toMutableMap().apply {
|
||||
keysToDelete.forEach { this.remove(it)}
|
||||
}
|
||||
keysToDelete.forEach {
|
||||
locs.remove(it)
|
||||
}
|
||||
@@ -53,6 +66,7 @@ class Visits(val id: Int, private val locs: HashMap<String, Int>) {
|
||||
keys.forEach {
|
||||
locs[it] = group
|
||||
}
|
||||
_visitsFlow.value = locs
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
|
@@ -1,6 +1,10 @@
|
||||
package net.helcel.beans.svg
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import net.helcel.beans.countries.World
|
||||
import net.helcel.beans.helper.AUTO_GROUP
|
||||
import net.helcel.beans.helper.Data.groups
|
||||
@@ -8,15 +12,10 @@ import net.helcel.beans.helper.Data.visits
|
||||
import net.helcel.beans.helper.NO_GROUP
|
||||
import net.helcel.beans.helper.Settings
|
||||
import net.helcel.beans.helper.Theme.colorToHex6
|
||||
import net.helcel.beans.helper.Theme.colorWrapper
|
||||
|
||||
|
||||
class CSSWrapper(private val ctx: Context) {
|
||||
|
||||
private val colorForeground: String =
|
||||
colorToHex6(colorWrapper(ctx, android.R.attr.panelColorBackground))
|
||||
private val colorBackground: String =
|
||||
colorToHex6(colorWrapper(ctx, android.R.attr.colorBackground))
|
||||
|
||||
private val continents: String = World.WWW.children.joinToString(",") { "#${it.code}2" }
|
||||
private val countries: String = World.WWW.children.joinToString(",") { itt ->
|
||||
itt.children.joinToString(",") { "#${it.code}2" }
|
||||
@@ -24,18 +23,22 @@ class CSSWrapper(private val ctx: Context) {
|
||||
private val regional: String = World.WWW.children.joinToString(",") { itt ->
|
||||
itt.children.joinToString(",") { "#${it.code}1" }
|
||||
}
|
||||
private val countryOnlyCSS: String =
|
||||
"svg{fill:$colorForeground;stroke:$colorBackground;stroke-width:0.1;}" +
|
||||
"${regional}{display:none;}"
|
||||
private val countryRegionalCSS: String =
|
||||
"svg{fill:$colorForeground;stroke:$colorBackground;stroke-width:0.01;}" +
|
||||
"$continents,$countries{fill:none;stroke:$colorBackground;stroke-width:0.1;}"
|
||||
|
||||
@Composable
|
||||
fun getBaseColors() : Pair<String, String> {
|
||||
val colorForeground = colorToHex6(MaterialTheme.colors.onBackground.toArgb().toDrawable())
|
||||
val colorBackground = colorToHex6(MaterialTheme.colors.background.toArgb().toDrawable())
|
||||
|
||||
return Pair(colorForeground, colorBackground)
|
||||
}
|
||||
|
||||
private var customCSS: String = ""
|
||||
|
||||
init {
|
||||
refresh()
|
||||
}
|
||||
|
||||
|
||||
private fun refresh() {
|
||||
val id = if (Settings.isRegional(ctx)) "1" else "2"
|
||||
customCSS = visits.getVisitedByValue().map { (k, v) ->
|
||||
@@ -47,20 +50,24 @@ class CSSWrapper(private val ctx: Context) {
|
||||
emptyList()
|
||||
}).takeIf { it.isNotEmpty() }
|
||||
?.joinToString(",") { "#${it}$id,#${it}" } + "{fill:${
|
||||
colorToHex6(
|
||||
if (k == AUTO_GROUP)
|
||||
colorWrapper(ctx, android.R.attr.colorPrimary)
|
||||
else groups.getGroupFromKey(k).color
|
||||
)
|
||||
if (k == AUTO_GROUP) colorToHex6(groups.getGroupFromPos(0).second.color)
|
||||
else colorToHex6(groups.getGroupFromKey(k).color)
|
||||
};}"
|
||||
}.joinToString("")
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun get(): String {
|
||||
val (colorForeground,colorBackground) = getBaseColors()
|
||||
refresh()
|
||||
return if (Settings.isRegional(ctx)) {
|
||||
val countryRegionalCSS: String =
|
||||
"svg{fill:$colorForeground;stroke:$colorBackground;stroke-width:0.01;}" +
|
||||
"$continents,$countries{fill:none;stroke:$colorBackground;stroke-width:0.1;}"
|
||||
countryRegionalCSS + customCSS
|
||||
} else {
|
||||
val countryOnlyCSS: String =
|
||||
"svg{fill:$colorForeground;stroke:$colorBackground;stroke-width:0.1;}" +
|
||||
"${regional}{display:none;}"
|
||||
countryOnlyCSS + customCSS
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,15 @@
|
||||
package net.helcel.beans.svg
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.caverock.androidsvg.SVG
|
||||
import net.helcel.beans.R
|
||||
|
||||
class SVGWrapper(ctx: Context) {
|
||||
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(ctx)
|
||||
private val sharedPreferences: SharedPreferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(ctx)
|
||||
private val svgFile = when (sharedPreferences.getString(
|
||||
ctx.getString(R.string.key_projection),
|
||||
ctx.getString(R.string.mercator)
|
||||
|
@@ -1,13 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="?attr/colorOnBackground"
|
||||
android:pathData="M80,160Q80,127 103.5,103.5Q127,80 160,80L800,80Q833,80 856.5,103.5Q880,127 880,160L880,640Q880,673 856.5,696.5Q833,720 800,720L240,720L80,880ZM206,640L800,640Q800,640 800,640Q800,640 800,640L800,160Q800,160 800,160Q800,160 800,160L160,160Q160,160 160,160Q160,160 160,160L160,685L206,640ZM160,640L160,640L160,160Q160,160 160,160Q160,160 160,160L160,160Q160,160 160,160Q160,160 160,160L160,640Q160,640 160,640Q160,640 160,640L160,640Z" />
|
||||
|
||||
<path
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:pathData="M480,280Q497,280 508.5,268.5Q520,257 520,240Q520,223 508.5,211.5Q497,200 480,200Q463,200 451.5,211.5Q440,223 440,240Q440,257 451.5,268.5Q463,280 480,280ZM440,600L520,600L520,360L440,360L440,600ZM80,880L80,160" />
|
||||
</vector>
|
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="?attr/colorOnBackground"
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
|
||||
</vector>
|
@@ -1,5 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||
|
||||
</vector>
|
@@ -1,10 +0,0 @@
|
||||
<vector
|
||||
android:height="24dp"
|
||||
android:viewportHeight="960"
|
||||
android:viewportWidth="960"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path
|
||||
android:fillColor="@color/white"
|
||||
android:pathData="M346,820L100,574Q90,564 85,552Q80,540 80,527Q80,514 85,502Q90,490 100,480L330,251L224,145L286,80L686,480Q696,490 700.5,502Q705,514 705,527Q705,540 700.5,552Q696,564 686,574L440,820Q430,830 418,835Q406,840 393,840Q380,840 368,835Q356,830 346,820ZM393,314L179,528Q179,528 179,528Q179,528 179,528L607,528Q607,528 607,528Q607,528 607,528L393,314ZM792,840Q756,840 731,814.5Q706,789 706,752Q706,725 719.5,701Q733,677 750,654L792,600L836,654Q852,677 866,701Q880,725 880,752Q880,789 854,814.5Q828,840 792,840Z"/>
|
||||
</vector>
|
@@ -1,41 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="72dp"
|
||||
android:height="72dp"
|
||||
android:viewportWidth="72"
|
||||
android:viewportHeight="72">
|
||||
<path
|
||||
android:pathData="M31,16l0,-4l10,0l0,4"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="4"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/colorOnBackground"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M51,25v31c0,2.209 -1.791,4 -4,4H25c-2.209,0 -4,-1.791 -4,-4V25"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="4"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/colorOnBackground"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M17,16h38v4h-38z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="4"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/colorOnBackground"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M41,28.25L41,55"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="4"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/colorOnBackground"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M31,28.25L31,55"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="4"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/colorOnBackground"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
@@ -1,10 +0,0 @@
|
||||
<vector
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path
|
||||
android:fillColor="@color/white"
|
||||
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||
</vector>
|
@@ -1,16 +0,0 @@
|
||||
<vector
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<path android:fillColor="?attr/colorOnBackground" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
|
||||
|
||||
<path android:fillColor="?attr/colorPrimary" android:pathData="M8,14m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
|
||||
|
||||
<path android:fillColor="?attr/colorPrimary" android:pathData="M12,8m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
|
||||
|
||||
<path android:fillColor="?attr/colorPrimary" android:pathData="M16,14m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
|
||||
|
||||
</vector>
|
@@ -1,18 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
|
||||
<path
|
||||
android:fillColor="?attr/colorOnBackground"
|
||||
android:pathData="M22,7h-9v2h9V7zM22,15h-9v2h9V15z"/>
|
||||
|
||||
<path
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:pathData="M5.54,11L2,7.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,11z"/>
|
||||
|
||||
<path
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:pathData="M5.54,19L2,15.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,19z"/>
|
||||
</vector>
|
@@ -1,16 +0,0 @@
|
||||
<vector
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<path
|
||||
android:fillColor="?attr/colorOnBackground"
|
||||
android:pathData="M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48L3,20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48L21,3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM10,5.47l4,1.4v11.66l-4,-1.4L10,5.47zM5,6.46l3,-1.01v11.7l-3,1.16L5,6.46zM19,17.54l-3,1.01L16,6.86l3,-1.16v11.84z" />
|
||||
|
||||
<path
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:pathData="M15,18.89l-6,-2.11L9,5.11l6,2.11v11.67z"/>
|
||||
|
||||
</vector>
|
@@ -1,14 +0,0 @@
|
||||
<vector
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<path
|
||||
android:fillColor="?attr/colorOnBackground"
|
||||
android:pathData="M12,22C6.49,22 2,17.51 2,12S6.49,2 12,2s10,4.04 10,9c0,3.31 -2.69,6 -6,6h-1.77c-0.28,0 -0.5,0.22 -0.5,0.5c0,0.12 0.05,0.23 0.13,0.33c0.41,0.47 0.64,1.06 0.64,1.67C14.5,20.88 13.38,22 12,22zM12,4c-4.41,0 -8,3.59 -8,8s3.59,8 8,8c0.28,0 0.5,-0.22 0.5,-0.5c0,-0.16 -0.08,-0.28 -0.14,-0.35c-0.41,-0.46 -0.63,-1.05 -0.63,-1.65c0,-1.38 1.12,-2.5 2.5,-2.5H16c2.21,0 4,-1.79 4,-4C20,7.14 16.41,4 12,4z" />
|
||||
<path
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:pathData="M17.5,13c-0.83,0 -1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5C19,12.33 18.33,13 17.5,13zM14.5,9C13.67,9 13,8.33 13,7.5C13,6.67 13.67,6 14.5,6S16,6.67 16,7.5C16,8.33 15.33,9 14.5,9zM5,11.5C5,10.67 5.67,10 6.5,10S8,10.67 8,11.5C8,12.33 7.33,13 6.5,13S5,12.33 5,11.5zM11,7.5C11,8.33 10.33,9 9.5,9S8,8.33 8,7.5C8,6.67 8.67,6 9.5,6S11,6.67 11,7.5z" />
|
||||
</vector>
|
@@ -1,14 +0,0 @@
|
||||
<vector
|
||||
android:height="24dp"
|
||||
android:viewportHeight="960"
|
||||
android:viewportWidth="960"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<path
|
||||
android:fillColor="?attr/colorOnBackground"
|
||||
android:pathData="M105,727L40,680L240,360L360,500L520,240L629,403Q606,404 585.5,408.5Q565,413 545,421L523,388L371,635L250,494L105,727ZM732,423Q713,415 692.5,410Q672,405 650,404L855,80L920,127L732,423Z" />
|
||||
<path
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:pathData="M863,920L738,795Q718,809 693.5,816Q669,823 643,823Q568,823 515.5,770.5Q463,718 463,643Q463,568 515.5,515.5Q568,463 643,463Q718,463 770.5,515.5Q823,568 823,643Q823,669 816,693.5Q809,718 795,739L920,863L863,920ZM643,743Q685,743 714,714Q743,685 743,643Q743,601 714,572Q685,543 643,543Q601,543 572,572Q543,601 543,643Q543,685 572,714Q601,743 643,743Z" />
|
||||
</vector>
|
@@ -1,12 +0,0 @@
|
||||
<vector
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<path android:fillColor="?attr/colorOnBackground" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||
|
||||
<path android:fillColor="?attr/colorPrimary" android:pathData="M12,10h-2v2H9v-2H7V9h2V7h1v2h2v1z"/>
|
||||
|
||||
</vector>
|
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:theme="@style/Theme.Beans"
|
||||
tools:context=".activity.EditActivity">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tab"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:theme="@style/Theme.Beans"
|
||||
tools:context=".activity.MainActivity">
|
||||
|
||||
<com.github.chrisbanes.photoview.PhotoView
|
||||
android:id="@+id/photo_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</LinearLayout>
|
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:theme="@style/Theme.Beans"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activity.SettingsActivity">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/fragment_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
@@ -1,66 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:theme="@style/Theme.Beans"
|
||||
tools:context=".activity.StatsActivity">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tab"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/group_color"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:paddingStart="56dp"
|
||||
android:text="@string/total"
|
||||
android:textAlignment="textStart"
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
app:cornerRadius="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="52dp"
|
||||
android:text=""
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
app:layout_constraintBottom_toBottomOf="@id/group_color"
|
||||
app:layout_constraintEnd_toEndOf="@id/group_color"
|
||||
app:layout_constraintTop_toTopOf="@id/group_color" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/stats"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
@@ -1,67 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activity.fragment.AboutFragment">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="300dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:contentDescription="@string/logo"
|
||||
android:src="@drawable/ic_launcher_foreground" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/app_name"
|
||||
android:textAlignment="center"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:text="@string/app_version"
|
||||
android:textAlignment="center"
|
||||
android:textSize="25sp" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:text="@string/beans_is_foss"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:autoLink="web"
|
||||
android:text="@string/beans_repo"
|
||||
android:textAlignment="center" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@@ -1,137 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
tools:context=".activity.fragment.EditGroupAddFragment">
|
||||
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/group_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:autofillHints=""
|
||||
android:hint="@string/name"
|
||||
android:inputType="text" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/colorView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/black"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/colorR"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/colorR"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@id/colorG"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/colorView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:thumbColor="@color/red"
|
||||
app:trackColorActive="@color/red" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/colorG"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:foregroundTint="#FF0000"
|
||||
app:layout_constraintBottom_toTopOf="@id/colorB"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/colorView"
|
||||
app:layout_constraintTop_toBottomOf="@id/colorR"
|
||||
app:thumbColor="@color/green"
|
||||
app:trackColorActive="@color/green" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/colorB"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/colorView"
|
||||
app:layout_constraintTop_toBottomOf="@id/colorG"
|
||||
app:thumbColor="@color/blue"
|
||||
app:trackColorActive="@color/blue" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@id/group_color"
|
||||
android:text="@string/hashtag" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/group_color"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:autofillHints=""
|
||||
android:hint="@string/color_rrggbb"
|
||||
android:inputType="text"
|
||||
android:maxLength="6" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnDelete"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:tooltipText="@string/delete"
|
||||
app:icon="@drawable/delete"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnCancel"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:text="@string/cancel"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btnOk"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnOk"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:text="@string/ok"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activity.fragment.EditPlaceFragment">
|
||||
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:scrollbars="vertical" />
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/warning_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="4dp">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/groups_color"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</ScrollView>
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnAdd"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:text="@string/add"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnClear"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:text="@string/clear"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activity.fragment.LicenseFragment">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/license_fragment_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@@ -1,52 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="50dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="start|center_vertical"
|
||||
android:insetTop="4dp"
|
||||
android:insetBottom="4dp"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:textAllCaps="false"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?attr/colorOnBackground"
|
||||
|
||||
app:cornerRadius="4dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/checkBox"
|
||||
app:layout_constraintEnd_toStartOf="@id/checkBox"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/count"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/checkBox"
|
||||
app:layout_constraintEnd_toStartOf="@id/checkBox"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/checkBox"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
app:checkedState="indeterminate"
|
||||
app:layout_constraintBottom_toBottomOf="@id/textView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintStart_toEndOf="@id/textView"
|
||||
app:layout_constraintTop_toTopOf="@id/textView"
|
||||
app:layout_constraintVertical_bias="0.5" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/group_color"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:textAlignment="textStart"
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
app:layout_constraintBottom_toBottomOf="@id/group_color"
|
||||
app:layout_constraintEnd_toEndOf="@id/group_color"
|
||||
app:layout_constraintTop_toTopOf="@id/group_color" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@@ -1,13 +0,0 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="net.helcel.beans.activity.EditActivity" >
|
||||
|
||||
<item
|
||||
android:id="@+id/action_color"
|
||||
android:orderInCategory="100"
|
||||
android:icon="@drawable/color"
|
||||
android:title="@string/action_color"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
@@ -1,25 +0,0 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="net.helcel.beans.activity.MainActivity" >
|
||||
|
||||
<item
|
||||
android:id="@+id/action_edit"
|
||||
android:orderInCategory="100"
|
||||
android:icon="@drawable/edit"
|
||||
android:title="@string/action_edit"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/action_stats"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orderInCategory="100"
|
||||
android:title="@string/action_stat"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:orderInCategory="100"
|
||||
android:title="@string/action_settings"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="entries_theme">
|
||||
<item>@string/system</item>
|
||||
<item>@string/light</item>
|
||||
<item>@string/dark</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="entries_stats">
|
||||
<item>@string/counters</item>
|
||||
<item>@string/percentages</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="entries_onoff">
|
||||
<item>@string/on</item>
|
||||
<item>@string/off</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="map_projection">
|
||||
<item>@string/azimuthalequidistant</item>
|
||||
<item>@string/loximuthal</item>
|
||||
<item>@string/mercator</item>
|
||||
|
||||
</string-array>
|
||||
</resources>
|
@@ -1,10 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Beans</string>
|
||||
<string name="app_version">1.0</string>
|
||||
<string name="action_settings">Settings</string>
|
||||
<string name="action_stat">Stats</string>
|
||||
<string name="action_edit">Edit</string>
|
||||
<string name="action_add">Add</string>
|
||||
<string name="action_clear">Clear</string>
|
||||
<string name="action_color">Color</string>
|
||||
<string name="key_theme">App theme</string>
|
||||
<string name="system">System</string>
|
||||
@@ -18,16 +18,17 @@
|
||||
<string name="key_regional">Regional</string>
|
||||
<string name="about">About</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">Project repository: https://github.com/helcel-net/beans\n Feel free to report issues or contribute to the project.</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="foss_licenses">Free and open source dependencies and licenses</string>
|
||||
<string name="about_beans">About the Beans application</string>
|
||||
<string name="edit_group">Select the group to assign. Long press on a group to edit its name and color.</string>
|
||||
<string name="edit_group">Select the group to assign.</string>
|
||||
<string name="edit_group_sub">Long press on a group to edit its name and color.</string>
|
||||
<string name="select_group">Select the group to keep.</string>
|
||||
<string name="select_group_sub">All others will be deleted and its mappings reassigned to the group you choose here.</string>
|
||||
<string name="delete_group">Are your sure you want to delete this group and remove all its country mappings?</string>
|
||||
<string name="select_group">Select one group you want to keep. All others will be deleted and its mappings reassigned to the group you choose here.</string>
|
||||
<string name="delete_regions">Are you sure you want to disable regions and reassign all regional mappings to the corresponding countries?</string>
|
||||
<string name="add">Add</string>
|
||||
<string name="clear">Clear</string>
|
||||
<string name="logo">Logo</string>
|
||||
|
||||
<string name="name">Name</string>
|
||||
<string name="rate">%1$d/%2$d</string>
|
||||
<string name="rate_with_unit">%1$d / %2$d %3$s</string>
|
||||
@@ -39,9 +40,10 @@
|
||||
<string name="off">Off</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="ok">Ok</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="total">Total</string>
|
||||
<string name="uncategorized">Uncategorized</string>
|
||||
|
||||
<string name="azimuthalequidistant">Azimuthal Equidistant</string>
|
||||
<string name="mercator">Mercator</string>
|
||||
<string name="loximuthal">Loximuthal</string>
|
||||
|
@@ -1,32 +0,0 @@
|
||||
<resources>
|
||||
<style name="Theme.Beans" parent="Theme.Material3.DayNight">
|
||||
<item name="colorPrimary">@color/blue</item>
|
||||
<item name="background">@color/darkgray</item>
|
||||
<item name="android:colorPrimary">?attr/colorPrimary</item>
|
||||
<item name="android:panelColorBackground">@color/lightgray</item>
|
||||
<item name="android:statusBarColor">?attr/colorPrimary</item>
|
||||
|
||||
<item name="checkboxStyle">@style/Theme.Beans.CheckBox</item>
|
||||
<item name="actionBarStyle">@style/Theme.Beans.ActionBar</item>
|
||||
<item name="android:actionOverflowButtonStyle">@style/Theme.Beans.ActionBar.ButtonOverflow</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Beans.CheckBox" parent="Widget.Material3.CompoundButton.CheckBox">
|
||||
</style>
|
||||
|
||||
<style name="Theme.Beans.ActionBar" parent="Widget.Material3.ActionBar.Solid">
|
||||
<item name="background">?attr/colorPrimary</item>
|
||||
<item name="titleTextStyle">@style/Theme.Beans.ActionBar.Text</item>
|
||||
<item name="android:tint">@color/white</item>
|
||||
<item name="actionMenuTextColor">@color/white</item>
|
||||
<item name="homeAsUpIndicator">@drawable/back</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Beans.ActionBar.Text" parent="TextAppearance.Material3.ActionBar.Title">
|
||||
<item name="android:textColor">@color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Beans.ActionBar.ButtonOverflow" parent="Widget.Material3.Search.ActionButton.Overflow">
|
||||
<item name="android:tint">@color/white</item>
|
||||
</style>
|
||||
</resources>
|
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:theme="@style/Theme.Beans">
|
||||
|
||||
<ListPreference
|
||||
app:defaultValue="@string/system"
|
||||
app:enabled="true"
|
||||
app:entries="@array/entries_theme"
|
||||
app:entryValues="@array/entries_theme"
|
||||
app:icon="@drawable/palette"
|
||||
app:key="@string/key_theme"
|
||||
app:title="@string/key_theme"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
app:defaultValue="@string/mercator"
|
||||
app:enabled="true"
|
||||
app:entries="@array/map_projection"
|
||||
app:entryValues="@array/map_projection"
|
||||
app:icon="@drawable/map"
|
||||
app:key="@string/key_projection"
|
||||
app:title="@string/key_projection"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
app:defaultValue="@string/counters"
|
||||
app:enabled="true"
|
||||
app:entries="@array/entries_stats"
|
||||
app:entryValues="@array/entries_stats"
|
||||
app:icon="@drawable/stats"
|
||||
app:key="@string/key_stats"
|
||||
app:title="@string/key_stats"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
app:defaultValue="@string/off"
|
||||
app:enabled="true"
|
||||
app:allowDividerAbove="true"
|
||||
app:entries="@array/entries_onoff"
|
||||
app:entryValues="@array/entries_onoff"
|
||||
app:icon="@drawable/group"
|
||||
app:key="@string/key_group"
|
||||
app:title="@string/key_group"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
app:defaultValue="@string/off"
|
||||
app:enabled="true"
|
||||
app:entries="@array/entries_onoff"
|
||||
app:entryValues="@array/entries_onoff"
|
||||
app:icon="@drawable/zoomin"
|
||||
app:key="@string/key_regional"
|
||||
app:title="@string/key_regional"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<Preference
|
||||
android:summary="@string/foss_licenses"
|
||||
app:enabled="true"
|
||||
app:allowDividerAbove="true"
|
||||
app:icon="@drawable/licenses"
|
||||
app:key="@string/licenses"
|
||||
app:title="@string/licenses" />
|
||||
|
||||
<Preference
|
||||
android:summary="@string/about_beans"
|
||||
app:enabled="true"
|
||||
app:icon="@drawable/about"
|
||||
app:key="@string/about"
|
||||
app:title="@string/about" />
|
||||
|
||||
|
||||
</PreferenceScreen>
|
@@ -1,6 +1,6 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id 'com.android.application' version '8.3.2' apply false
|
||||
id 'com.android.library' version '8.3.2' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.9.23' apply false
|
||||
id 'com.android.application' version '8.13.0' apply false
|
||||
id 'com.android.library' version '8.13.0' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '2.2.20' apply false
|
||||
}
|
21
config/libraries/lib_gadm.json
Normal file
21
config/libraries/lib_gadm.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"uniqueId": "org.gadm:data",
|
||||
"developers": [
|
||||
{
|
||||
"organisationUrl": "https://gadm.org/index.html",
|
||||
"name": "GADM"
|
||||
}
|
||||
],
|
||||
"artifactVersion": "4.1",
|
||||
"description": "GADM provides maps and spatial data for all countries and their sub-divisions.",
|
||||
"scm": {
|
||||
"connection": "scm:git@github.com:mikepenz/MaterialDrawer.git",
|
||||
"url": "https://github.com/mikepenz/MaterialDrawer",
|
||||
"developerConnection": "scm:git@github.com:mikepenz/MaterialDrawer.git"
|
||||
},
|
||||
"name": "GADM maps and data",
|
||||
"website": "https://gadm.org/index.html",
|
||||
"licenses": [
|
||||
"0151ac7b561a385c536ad4c94532e60b"
|
||||
]
|
||||
}
|
6
config/licenses/lic_gadm.json
Normal file
6
config/licenses/lic_gadm.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"content": "<b>The data are freely available for academic use and other non-commercial use. Redistribution or commercial use is not allowed without prior permission.</b>\n\nUsing the data to create maps for publishing of academic research articles is allowed. Thus you can use the maps you made with GADM data for figures in articles published by PLoS, Springer Nature, Elsevier, MDPI, etc. You are allowed (but not required) to publish these articles (and the maps they contain) under an open license such as CC-BY as is the case with PLoS journals and may be the case with other open access articles. <b>Data for the following countries is covered by a a different license</b> <b>Austria</b>: Creative Commons Attribution-ShareAlike 2.0 (source: Government of Ausria)",
|
||||
"hash": "0151ac7b561a385c536ad4c94532e60b",
|
||||
"url": "https://gadm.org/license.html",
|
||||
"name": "GADM license"
|
||||
}
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,8 +1,8 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionSha256Sum=a17ddd85a26b6a7f5ddb71ff8b05fc5104c0202c6e64782429790c933686c806
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
|
||||
|
15
gradlew
vendored
15
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -84,7 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -112,7 +114,6 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -170,7 +171,6 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -203,15 +203,14 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
|
5
gradlew.bat
vendored
5
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@@ -68,11 +70,10 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
@@ -2,10 +2,10 @@ Beans is a scratchmap of the world for Android.
|
||||
|
||||
Keep track of your discovery of the world on a colorful visual map.
|
||||
|
||||
• Color a map of places based on custom labels
|
||||
• Country/State based coloring
|
||||
• Single/Multi color modes
|
||||
• Different map projections available
|
||||
• Small & Fast
|
||||
• Statistics (WIP)
|
||||
• 100% Free and Open Source software, with no proprietary dependencies
|
||||
* Color a map of places based on custom labels
|
||||
* Country/State based coloring
|
||||
* Single/Multi color modes
|
||||
* Different map projections available
|
||||
* Small & Fast
|
||||
* Statistics (WIP)
|
||||
* 100% Free and Open Source software, with no proprietary dependencies
|
||||
|
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@turf/area": "^6.5.0",
|
||||
"@turf/turf": "^6.5.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"@turf/area": "^7.0.0",
|
||||
"@turf/turf": "^7.0.0",
|
||||
"jsdom": "^27.0.0",
|
||||
"mapshaper": "^0.6.79"
|
||||
},
|
||||
"type": "module"
|
||||
|
Reference in New Issue
Block a user