From 6b2f786afea9b19cb51ea631d6a499b682efeea4 Mon Sep 17 00:00:00 2001 From: soraefir Date: Wed, 17 Sep 2025 18:11:46 +0200 Subject: [PATCH] Update and cleanup --- .gitignore | 2 - app/build.gradle | 47 ++- app/src/main/AndroidManifest.xml | 38 +- .../net/helcel/beans/activity/EditActivity.kt | 69 ---- .../net/helcel/beans/activity/EditScreen.kt | 64 +++ .../net/helcel/beans/activity/MainActivity.kt | 75 ---- .../net/helcel/beans/activity/MainScreen.kt | 123 ++++++ .../helcel/beans/activity/SettingsActivity.kt | 63 --- .../helcel/beans/activity/SettingsScreen.kt | 381 ++++++++++++++++++ .../helcel/beans/activity/StatsActivity.kt | 184 +++++++-- .../activity/adapter/GeolocListAdapter.kt | 193 --------- .../activity/adapter/GroupListAdapter.kt | 78 ---- .../activity/adapter/StatsListAdapter.kt | 152 ------- .../activity/adapter/ViewPagerAdapter.kt | 61 --- .../beans/activity/fragment/AboutFragment.kt | 21 - .../activity/fragment/EditGroupAddFragment.kt | 155 ------- .../fragment/EditPlaceColorFragment.kt | 60 --- .../activity/fragment/EditPlaceFragment.kt | 38 -- .../activity/fragment/LicenseFragment.kt | 33 -- .../activity/fragment/SettingsFragment.kt | 142 ------- .../helcel/beans/activity/sub/AboutScreen.kt | 78 ++++ .../beans/activity/sub/EditGroupAddDialog.kt | 222 ++++++++++ .../activity/sub/EditPlaceColorDialog.kt | 193 +++++++++ .../beans/activity/sub/EditPlaceScreen.kt | 218 ++++++++++ .../beans/activity/sub/LicenseScreen.kt | 43 ++ .../net/helcel/beans/countries/Country.kt | 4 +- .../java/net/helcel/beans/countries/GeoLoc.kt | 2 +- .../main/java/net/helcel/beans/helper/Data.kt | 85 +++- .../net/helcel/beans/helper/DialogCloser.kt | 5 - .../java/net/helcel/beans/helper/Groups.kt | 25 +- .../java/net/helcel/beans/helper/Settings.kt | 12 +- .../java/net/helcel/beans/helper/Theme.kt | 16 +- .../java/net/helcel/beans/helper/Visits.kt | 14 + .../java/net/helcel/beans/svg/CSSWrapper.kt | 43 +- app/src/main/res/drawable/about.xml | 13 - app/src/main/res/drawable/add.xml | 9 - app/src/main/res/drawable/back.xml | 5 - app/src/main/res/drawable/color.xml | 10 - app/src/main/res/drawable/delete.xml | 41 -- app/src/main/res/drawable/edit.xml | 10 - app/src/main/res/drawable/group.xml | 16 - app/src/main/res/drawable/licenses.xml | 18 - app/src/main/res/drawable/map.xml | 16 - app/src/main/res/drawable/palette.xml | 14 - app/src/main/res/drawable/stats.xml | 14 - app/src/main/res/drawable/zoomin.xml | 12 - app/src/main/res/layout/activity_edit.xml | 21 - app/src/main/res/layout/activity_main.xml | 14 - app/src/main/res/layout/activity_settings.xml | 15 - app/src/main/res/layout/activity_stat.xml | 66 --- app/src/main/res/layout/fragment_about.xml | 67 --- .../res/layout/fragment_edit_groups_add.xml | 137 ------- .../main/res/layout/fragment_edit_places.xml | 26 -- .../layout/fragment_edit_places_colors.xml | 59 --- app/src/main/res/layout/fragment_license.xml | 13 - app/src/main/res/layout/item_list_geoloc.xml | 52 --- app/src/main/res/layout/item_list_group.xml | 34 -- app/src/main/res/menu/menu_edit.xml | 13 - app/src/main/res/menu/menu_main.xml | 25 -- app/src/main/res/values/arrays.xml | 25 -- app/src/main/res/values/en.xml | 20 +- app/src/main/res/values/themes.xml | 32 -- app/src/main/res/xml/fragment_settings.xml | 73 ---- 63 files changed, 1656 insertions(+), 2153 deletions(-) delete mode 100644 app/src/main/java/net/helcel/beans/activity/EditActivity.kt create mode 100644 app/src/main/java/net/helcel/beans/activity/EditScreen.kt delete mode 100644 app/src/main/java/net/helcel/beans/activity/MainActivity.kt create mode 100644 app/src/main/java/net/helcel/beans/activity/MainScreen.kt delete mode 100644 app/src/main/java/net/helcel/beans/activity/SettingsActivity.kt create mode 100644 app/src/main/java/net/helcel/beans/activity/SettingsScreen.kt delete mode 100644 app/src/main/java/net/helcel/beans/activity/adapter/GeolocListAdapter.kt delete mode 100644 app/src/main/java/net/helcel/beans/activity/adapter/GroupListAdapter.kt delete mode 100644 app/src/main/java/net/helcel/beans/activity/adapter/StatsListAdapter.kt delete mode 100644 app/src/main/java/net/helcel/beans/activity/adapter/ViewPagerAdapter.kt delete mode 100644 app/src/main/java/net/helcel/beans/activity/fragment/AboutFragment.kt delete mode 100644 app/src/main/java/net/helcel/beans/activity/fragment/EditGroupAddFragment.kt delete mode 100644 app/src/main/java/net/helcel/beans/activity/fragment/EditPlaceColorFragment.kt delete mode 100644 app/src/main/java/net/helcel/beans/activity/fragment/EditPlaceFragment.kt delete mode 100644 app/src/main/java/net/helcel/beans/activity/fragment/LicenseFragment.kt delete mode 100644 app/src/main/java/net/helcel/beans/activity/fragment/SettingsFragment.kt create mode 100644 app/src/main/java/net/helcel/beans/activity/sub/AboutScreen.kt create mode 100644 app/src/main/java/net/helcel/beans/activity/sub/EditGroupAddDialog.kt create mode 100644 app/src/main/java/net/helcel/beans/activity/sub/EditPlaceColorDialog.kt create mode 100644 app/src/main/java/net/helcel/beans/activity/sub/EditPlaceScreen.kt create mode 100644 app/src/main/java/net/helcel/beans/activity/sub/LicenseScreen.kt delete mode 100644 app/src/main/java/net/helcel/beans/helper/DialogCloser.kt delete mode 100644 app/src/main/res/drawable/about.xml delete mode 100644 app/src/main/res/drawable/add.xml delete mode 100644 app/src/main/res/drawable/back.xml delete mode 100644 app/src/main/res/drawable/color.xml delete mode 100644 app/src/main/res/drawable/delete.xml delete mode 100644 app/src/main/res/drawable/edit.xml delete mode 100644 app/src/main/res/drawable/group.xml delete mode 100644 app/src/main/res/drawable/licenses.xml delete mode 100644 app/src/main/res/drawable/map.xml delete mode 100644 app/src/main/res/drawable/palette.xml delete mode 100644 app/src/main/res/drawable/stats.xml delete mode 100644 app/src/main/res/drawable/zoomin.xml delete mode 100644 app/src/main/res/layout/activity_edit.xml delete mode 100644 app/src/main/res/layout/activity_main.xml delete mode 100644 app/src/main/res/layout/activity_settings.xml delete mode 100644 app/src/main/res/layout/activity_stat.xml delete mode 100644 app/src/main/res/layout/fragment_about.xml delete mode 100644 app/src/main/res/layout/fragment_edit_groups_add.xml delete mode 100644 app/src/main/res/layout/fragment_edit_places.xml delete mode 100644 app/src/main/res/layout/fragment_edit_places_colors.xml delete mode 100644 app/src/main/res/layout/fragment_license.xml delete mode 100644 app/src/main/res/layout/item_list_geoloc.xml delete mode 100644 app/src/main/res/layout/item_list_group.xml delete mode 100644 app/src/main/res/menu/menu_edit.xml delete mode 100644 app/src/main/res/menu/menu_main.xml delete mode 100644 app/src/main/res/values/arrays.xml delete mode 100644 app/src/main/res/values/themes.xml delete mode 100644 app/src/main/res/xml/fragment_settings.xml diff --git a/.gitignore b/.gitignore index c0850d2..455a8bc 100644 --- a/.gitignore +++ b/.gitignore @@ -20,8 +20,6 @@ app/build/ app/debug/ app/release/ captures/ -.externalNativeBuild -.cxx local.properties keystore.properties key.jks \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 5cc6f01..a7198d0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,20 +2,23 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' 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 2 - versionName "1.0b" + targetSdk 36 + versionCode 3 + versionName "1.0c" } 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,21 +74,43 @@ android { // Disables dependency metadata when building Android App Bundles. includeInBundle = false } + composeOptions { + kotlinCompilerExtensionVersion = "2.2.20" + } + + lint { + disable 'UsingMaterialAndMaterial3Libraries' + } } aboutLibraries { - exclusionPatterns = [~"androidx.*", ~"com.google.android.*", ~"org.jetbrains.*"] - configPath = "config" + library { + exclusionPatterns = [~"androidx.*", ~"com.google.android.*", ~"org.jetbrains.*"] + } excludeFields = ["generated"] } dependencies { + implementation 'androidx.compose.material3:material3:1.3.2' + implementation 'androidx.navigation:navigation-compose:2.9.4' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.1.5' implementation 'androidx.preference:preference-ktx:1.2.1' + implementation 'androidx.compose.ui:ui' + implementation "androidx.compose.material:material:1.9.1" + 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:12.2.4' + implementation 'com.mikepenz:aboutlibraries-compose-m3:12.2.4' + implementation 'com.mikepenz:aboutlibraries-core:12.2.4' + + + implementation platform('androidx.compose:compose-bom:2025.09.00') + debugImplementation 'androidx.compose.ui:ui-tooling:1.9.1' } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c859234..e3b6df2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,52 +1,22 @@ - + xmlns:tools="http://schemas.android.com/tools"> - - + tools:replace="android:allowBackup"> - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/EditActivity.kt b/app/src/main/java/net/helcel/beans/activity/EditActivity.kt deleted file mode 100644 index b51c8f3..0000000 --- a/app/src/main/java/net/helcel/beans/activity/EditActivity.kt +++ /dev/null @@ -1,69 +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 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.EditPlaceFragment -import net.helcel.beans.countries.World -import net.helcel.beans.databinding.ActivityEditBinding -import net.helcel.beans.helper.Data -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) - } - - -} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/EditScreen.kt b/app/src/main/java/net/helcel/beans/activity/EditScreen.kt new file mode 100644 index 0000000..628e2f1 --- /dev/null +++ b/app/src/main/java/net/helcel/beans/activity/EditScreen.kt @@ -0,0 +1,64 @@ +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.unit.dp +import net.helcel.beans.R +import net.helcel.beans.activity.sub.EditPlaceScreen +import net.helcel.beans.countries.World + +@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) + } + } + } + } +} + + diff --git a/app/src/main/java/net/helcel/beans/activity/MainActivity.kt b/app/src/main/java/net/helcel/beans/activity/MainActivity.kt deleted file mode 100644 index 5608420..0000000 --- a/app/src/main/java/net/helcel/beans/activity/MainActivity.kt +++ /dev/null @@ -1,75 +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.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) - } - -} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/MainScreen.kt b/app/src/main/java/net/helcel/beans/activity/MainScreen.kt new file mode 100644 index 0000000..3381aa0 --- /dev/null +++ b/app/src/main/java/net/helcel/beans/activity/MainScreen.kt @@ -0,0 +1,123 @@ +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.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.DateRange, 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) + } +} diff --git a/app/src/main/java/net/helcel/beans/activity/SettingsActivity.kt b/app/src/main/java/net/helcel/beans/activity/SettingsActivity.kt deleted file mode 100644 index 89ea76d..0000000 --- a/app/src/main/java/net/helcel/beans/activity/SettingsActivity.kt +++ /dev/null @@ -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) - } -} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/SettingsScreen.kt b/app/src/main/java/net/helcel/beans/activity/SettingsScreen.kt new file mode 100644 index 0000000..13743d4 --- /dev/null +++ b/app/src/main/java/net/helcel/beans/activity/SettingsScreen.kt @@ -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 +} + +@Composable +fun SettingsMainScreen(onExit: ()->Unit = {}) { + var nav: NavHostController? = null + SysTheme { + Scaffold( + topBar = { + TopAppBar( + title = { Text(stringResource(R.string.action_settings)) }, + navigationIcon = { + IconButton(onClick = { + if(nav!=null && !nav!!.popBackStack()) + onExit() + }) { + Icon( + Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = null + ) + } + } + ) + } + ) { innerPadding -> + Box(modifier = Modifier.padding(innerPadding)) { + nav = settingsNav() + } + } + } +} + +@Preview +@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, 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) + } +} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/StatsActivity.kt b/app/src/main/java/net/helcel/beans/activity/StatsActivity.kt index b13c776..0f01c2d 100644 --- a/app/src/main/java/net/helcel/beans/activity/StatsActivity.kt +++ b/app/src/main/java/net/helcel/beans/activity/StatsActivity.kt @@ -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) + ) } } \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/adapter/GeolocListAdapter.kt b/app/src/main/java/net/helcel/beans/activity/adapter/GeolocListAdapter.kt deleted file mode 100644 index af7db05..0000000 --- a/app/src/main/java/net/helcel/beans/activity/adapter/GeolocListAdapter.kt +++ /dev/null @@ -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() { - - private val sortedList = l.children.toList().sortedBy { it.fullName } - private val holders: MutableSet = 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) - } - - } -} diff --git a/app/src/main/java/net/helcel/beans/activity/adapter/GroupListAdapter.kt b/app/src/main/java/net/helcel/beans/activity/adapter/GroupListAdapter.kt deleted file mode 100644 index 1dae265..0000000 --- a/app/src/main/java/net/helcel/beans/activity/adapter/GroupListAdapter.kt +++ /dev/null @@ -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() { - - 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) { - _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 - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/adapter/StatsListAdapter.kt b/app/src/main/java/net/helcel/beans/activity/adapter/StatsListAdapter.kt deleted file mode 100644 index f36efef..0000000 --- a/app/src/main/java/net/helcel/beans/activity/adapter/StatsListAdapter.kt +++ /dev/null @@ -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() { - 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 = World.WWW.children.toList() - private val countryTotal: List = World.WWW.children.flatMap { it.children } - private val stateTotal: List = - 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 - - private lateinit var wwwCount: List - private lateinit var countryCount: List - private lateinit var stateCount: List - - fun bind(entry: Pair): 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 - } - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/adapter/ViewPagerAdapter.kt b/app/src/main/java/net/helcel/beans/activity/adapter/ViewPagerAdapter.kt deleted file mode 100644 index d5a7697..0000000 --- a/app/src/main/java/net/helcel/beans/activity/adapter/ViewPagerAdapter.kt +++ /dev/null @@ -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 = 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)} - } -} - - diff --git a/app/src/main/java/net/helcel/beans/activity/fragment/AboutFragment.kt b/app/src/main/java/net/helcel/beans/activity/fragment/AboutFragment.kt deleted file mode 100644 index b82b86e..0000000 --- a/app/src/main/java/net/helcel/beans/activity/fragment/AboutFragment.kt +++ /dev/null @@ -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 - } -} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/fragment/EditGroupAddFragment.kt b/app/src/main/java/net/helcel/beans/activity/fragment/EditGroupAddFragment.kt deleted file mode 100644 index 80038db..0000000 --- a/app/src/main/java/net/helcel/beans/activity/fragment/EditGroupAddFragment.kt +++ /dev/null @@ -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 - } -} diff --git a/app/src/main/java/net/helcel/beans/activity/fragment/EditPlaceColorFragment.kt b/app/src/main/java/net/helcel/beans/activity/fragment/EditPlaceColorFragment.kt deleted file mode 100644 index 58bb328..0000000 --- a/app/src/main/java/net/helcel/beans/activity/fragment/EditPlaceColorFragment.kt +++ /dev/null @@ -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) - } -} diff --git a/app/src/main/java/net/helcel/beans/activity/fragment/EditPlaceFragment.kt b/app/src/main/java/net/helcel/beans/activity/fragment/EditPlaceFragment.kt deleted file mode 100644 index 82855ac..0000000 --- a/app/src/main/java/net/helcel/beans/activity/fragment/EditPlaceFragment.kt +++ /dev/null @@ -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) - } -} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/fragment/LicenseFragment.kt b/app/src/main/java/net/helcel/beans/activity/fragment/LicenseFragment.kt deleted file mode 100644 index d7d64fe..0000000 --- a/app/src/main/java/net/helcel/beans/activity/fragment/LicenseFragment.kt +++ /dev/null @@ -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 - } -} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/fragment/SettingsFragment.kt b/app/src/main/java/net/helcel/beans/activity/fragment/SettingsFragment.kt deleted file mode 100644 index 3606407..0000000 --- a/app/src/main/java/net/helcel/beans/activity/fragment/SettingsFragment.kt +++ /dev/null @@ -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(getString(R.string.key_theme))?.setOnPreferenceChangeListener { _, key -> - setTheme(ctx, key as String) - } - - // Select map projection - findPreference(getString(R.string.key_projection))?.setOnPreferenceChangeListener { _, key -> - Settings.refreshProjection() - } - - // Toggle groups - findPreference(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(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(getString(R.string.licenses))?.setOnPreferenceClickListener { - requireActivity().supportFragmentManager.beginTransaction() - .replace(R.id.fragment_view, LicenseFragment(), getString(R.string.licenses)) - .commit() - true - } - - // Open about fragment - findPreference(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) - } -} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/sub/AboutScreen.kt b/app/src/main/java/net/helcel/beans/activity/sub/AboutScreen.kt new file mode 100644 index 0000000..9c030f7 --- /dev/null +++ b/app/src/main/java/net/helcel/beans/activity/sub/AboutScreen.kt @@ -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) + ) + } +} diff --git a/app/src/main/java/net/helcel/beans/activity/sub/EditGroupAddDialog.kt b/app/src/main/java/net/helcel/beans/activity/sub/EditGroupAddDialog.kt new file mode 100644 index 0000000..78b5c68 --- /dev/null +++ b/app/src/main/java/net/helcel/beans/activity/sub/EditGroupAddDialog.kt @@ -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, + ) + ) +} diff --git a/app/src/main/java/net/helcel/beans/activity/sub/EditPlaceColorDialog.kt b/app/src/main/java/net/helcel/beans/activity/sub/EditPlaceColorDialog.kt new file mode 100644 index 0000000..2546515 --- /dev/null +++ b/app/src/main/java/net/helcel/beans/activity/sub/EditPlaceColorDialog.kt @@ -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) + ) + } + } + } + }, + ) +} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/sub/EditPlaceScreen.kt b/app/src/main/java/net/helcel/beans/activity/sub/EditPlaceScreen.kt new file mode 100644 index 0000000..8f16cc1 --- /dev/null +++ b/app/src/main/java/net/helcel/beans/activity/sub/EditPlaceScreen.kt @@ -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.ScrollableTabRow +import androidx.compose.material.Tab +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 = 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 + + ScrollableTabRow( + 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)) +} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/activity/sub/LicenseScreen.kt b/app/src/main/java/net/helcel/beans/activity/sub/LicenseScreen.kt new file mode 100644 index 0000000..36b1b71 --- /dev/null +++ b/app/src/main/java/net/helcel/beans/activity/sub/LicenseScreen.kt @@ -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, + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/countries/Country.kt b/app/src/main/java/net/helcel/beans/countries/Country.kt index 0b39838..39a39f7 100644 --- a/app/src/main/java/net/helcel/beans/countries/Country.kt +++ b/app/src/main/java/net/helcel/beans/countries/Country.kt @@ -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), diff --git a/app/src/main/java/net/helcel/beans/countries/GeoLoc.kt b/app/src/main/java/net/helcel/beans/countries/GeoLoc.kt index 941c96d..eddb270 100644 --- a/app/src/main/java/net/helcel/beans/countries/GeoLoc.kt +++ b/app/src/main/java/net/helcel/beans/countries/GeoLoc.kt @@ -12,4 +12,4 @@ interface GeoLoc { val type: LocType val children: Set -} +} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/helper/Data.kt b/app/src/main/java/net/helcel/beans/helper/Data.kt index bd59bbc..38415a1 100644 --- a/app/src/main/java/net/helcel/beans/helper/Data.kt +++ b/app/src/main/java/net/helcel/beans/helper/Data.kt @@ -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) } } \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/helper/DialogCloser.kt b/app/src/main/java/net/helcel/beans/helper/DialogCloser.kt deleted file mode 100644 index eba0822..0000000 --- a/app/src/main/java/net/helcel/beans/helper/DialogCloser.kt +++ /dev/null @@ -1,5 +0,0 @@ -package net.helcel.beans.helper - -interface DialogCloser { - fun onDialogDismiss(clear: Boolean) -} \ No newline at end of file diff --git a/app/src/main/java/net/helcel/beans/helper/Groups.kt b/app/src/main/java/net/helcel/beans/helper/Groups.kt index 60e88c6..8f11d7c 100644 --- a/app/src/main/java/net/helcel/beans/helper/Groups.kt +++ b/app/src/main/java/net/helcel/beans/helper/Groups.kt @@ -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) { + @kotlinx.serialization.Transient + private val _groupsFlow = MutableStateFlow>(grps.values.toList()) + @kotlinx.serialization.Transient + val groupsFlow: StateFlow> = _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) { } fun getGroupFromPos(pos: Int): Pair { + 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) { 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) diff --git a/app/src/main/java/net/helcel/beans/helper/Settings.kt b/app/src/main/java/net/helcel/beans/helper/Settings.kt index af64328..cb92bf4 100644 --- a/app/src/main/java/net/helcel/beans/helper/Settings.kt +++ b/app/src/main/java/net/helcel/beans/helper/Settings.kt @@ -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 } diff --git a/app/src/main/java/net/helcel/beans/helper/Theme.kt b/app/src/main/java/net/helcel/beans/helper/Theme.kt index f3897f6..6858310 100644 --- a/app/src/main/java/net/helcel/beans/helper/Theme.kt +++ b/app/src/main/java/net/helcel/beans/helper/Theme.kt @@ -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) { diff --git a/app/src/main/java/net/helcel/beans/helper/Visits.kt b/app/src/main/java/net/helcel/beans/helper/Visits.kt index f472ef7..a9254bb 100644 --- a/app/src/main/java/net/helcel/beans/helper/Visits.kt +++ b/app/src/main/java/net/helcel/beans/helper/Visits.kt @@ -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) { + @kotlinx.serialization.Transient + private val _visitsFlow = MutableStateFlow>(locs.toMutableMap()) + @kotlinx.serialization.Transient + val visitsFlow: StateFlow> = _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) { 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) { keys.forEach { locs[it] = group } + _visitsFlow.value = locs } @OptIn(ExperimentalSerializationApi::class) diff --git a/app/src/main/java/net/helcel/beans/svg/CSSWrapper.kt b/app/src/main/java/net/helcel/beans/svg/CSSWrapper.kt index 84a8aea..76829e0 100644 --- a/app/src/main/java/net/helcel/beans/svg/CSSWrapper.kt +++ b/app/src/main/java/net/helcel/beans/svg/CSSWrapper.kt @@ -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 { + 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 } } diff --git a/app/src/main/res/drawable/about.xml b/app/src/main/res/drawable/about.xml deleted file mode 100644 index 9cd6750..0000000 --- a/app/src/main/res/drawable/about.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/add.xml b/app/src/main/res/drawable/add.xml deleted file mode 100644 index 37f4023..0000000 --- a/app/src/main/res/drawable/add.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/back.xml b/app/src/main/res/drawable/back.xml deleted file mode 100644 index 6e18b25..0000000 --- a/app/src/main/res/drawable/back.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/color.xml b/app/src/main/res/drawable/color.xml deleted file mode 100644 index 5fa2d9c..0000000 --- a/app/src/main/res/drawable/color.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/delete.xml b/app/src/main/res/drawable/delete.xml deleted file mode 100644 index 9018c6f..0000000 --- a/app/src/main/res/drawable/delete.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/drawable/edit.xml b/app/src/main/res/drawable/edit.xml deleted file mode 100644 index 5cd1ea2..0000000 --- a/app/src/main/res/drawable/edit.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/group.xml b/app/src/main/res/drawable/group.xml deleted file mode 100644 index f2af1ac..0000000 --- a/app/src/main/res/drawable/group.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/drawable/licenses.xml b/app/src/main/res/drawable/licenses.xml deleted file mode 100644 index 1ecc582..0000000 --- a/app/src/main/res/drawable/licenses.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/map.xml b/app/src/main/res/drawable/map.xml deleted file mode 100644 index 981ef93..0000000 --- a/app/src/main/res/drawable/map.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/drawable/palette.xml b/app/src/main/res/drawable/palette.xml deleted file mode 100644 index 6e1e804..0000000 --- a/app/src/main/res/drawable/palette.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/stats.xml b/app/src/main/res/drawable/stats.xml deleted file mode 100644 index 29c544b..0000000 --- a/app/src/main/res/drawable/stats.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/zoomin.xml b/app/src/main/res/drawable/zoomin.xml deleted file mode 100644 index c18a81e..0000000 --- a/app/src/main/res/drawable/zoomin.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/layout/activity_edit.xml b/app/src/main/res/layout/activity_edit.xml deleted file mode 100644 index 3cbd689..0000000 --- a/app/src/main/res/layout/activity_edit.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 0ddb6d5..0000000 --- a/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml deleted file mode 100644 index 052b000..0000000 --- a/app/src/main/res/layout/activity_settings.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_stat.xml b/app/src/main/res/layout/activity_stat.xml deleted file mode 100644 index 15dc12a..0000000 --- a/app/src/main/res/layout/activity_stat.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml deleted file mode 100644 index 81cfcf2..0000000 --- a/app/src/main/res/layout/fragment_about.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_edit_groups_add.xml b/app/src/main/res/layout/fragment_edit_groups_add.xml deleted file mode 100644 index ab39507..0000000 --- a/app/src/main/res/layout/fragment_edit_groups_add.xml +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_edit_places.xml b/app/src/main/res/layout/fragment_edit_places.xml deleted file mode 100644 index f0ea949..0000000 --- a/app/src/main/res/layout/fragment_edit_places.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_edit_places_colors.xml b/app/src/main/res/layout/fragment_edit_places_colors.xml deleted file mode 100644 index e7a11e5..0000000 --- a/app/src/main/res/layout/fragment_edit_places_colors.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_license.xml b/app/src/main/res/layout/fragment_license.xml deleted file mode 100644 index 173fb63..0000000 --- a/app/src/main/res/layout/fragment_license.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_list_geoloc.xml b/app/src/main/res/layout/item_list_geoloc.xml deleted file mode 100644 index f04218b..0000000 --- a/app/src/main/res/layout/item_list_geoloc.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_list_group.xml b/app/src/main/res/layout/item_list_group.xml deleted file mode 100644 index 6793698..0000000 --- a/app/src/main/res/layout/item_list_group.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/menu/menu_edit.xml b/app/src/main/res/menu/menu_edit.xml deleted file mode 100644 index ef139f8..0000000 --- a/app/src/main/res/menu/menu_edit.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml deleted file mode 100644 index e4825fc..0000000 --- a/app/src/main/res/menu/menu_main.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml deleted file mode 100644 index b481b83..0000000 --- a/app/src/main/res/values/arrays.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - @string/system - @string/light - @string/dark - - - - @string/counters - @string/percentages - - - - @string/on - @string/off - - - - @string/azimuthalequidistant - @string/loximuthal - @string/mercator - - - diff --git a/app/src/main/res/values/en.xml b/app/src/main/res/values/en.xml index 5465a87..cf73a55 100644 --- a/app/src/main/res/values/en.xml +++ b/app/src/main/res/values/en.xml @@ -1,10 +1,10 @@ - Beans - 1.0a Settings Stats Edit + Add + Clear Color App theme System @@ -18,16 +18,17 @@ Regional About Beans is free and open source software, licensed under the GNU General Public License (version 3 or later) - Project repository: https://github.com/helcel-net/beans\n Feel free to report issues or contribute to the project. + https://github.com/helcel-net/beans + Project repository: %1$s\n Feel free to report issues or contribute. Free and open source dependencies and licenses About the Beans application - Select the group to assign. Long press on a group to edit its name and color. + Select the group to assign. + Long press on a group to edit its name and color. + Select the group to keep. + All others will be deleted and its mappings reassigned to the group you choose here. Are your sure you want to delete this group and remove all its country mappings? - Select one group you want to keep. All others will be deleted and its mappings reassigned to the group you choose here. Are you sure you want to disable regions and reassign all regional mappings to the corresponding countries? - Add - Clear - Logo + Name %1$d/%2$d %1$d / %2$d %3$s @@ -39,9 +40,10 @@ Off Delete Cancel - Ok + OK Total Uncategorized + Azimuthal Equidistant Mercator Loximuthal diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml deleted file mode 100644 index e7de5c2..0000000 --- a/app/src/main/res/values/themes.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/fragment_settings.xml b/app/src/main/res/xml/fragment_settings.xml deleted file mode 100644 index 3c0ba15..0000000 --- a/app/src/main/res/xml/fragment_settings.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file