Compare commits
	
		
			1 Commits
		
	
	
		
			main
			...
			a6032d8d89
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | a6032d8d89 | 
							
								
								
									
										4
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -23,7 +23,7 @@ jobs: | ||||
|       contents: write | ||||
|  | ||||
|     steps: | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: actions/checkout@v4 | ||||
|  | ||||
|       - name: set up secrets | ||||
|         run: | | ||||
| @@ -41,7 +41,7 @@ jobs: | ||||
|         run: git checkout -B "$BRANCH" | ||||
|  | ||||
|       - name: set up JDK | ||||
|         uses: actions/setup-java@v5 | ||||
|         uses: actions/setup-java@v4 | ||||
|         with: | ||||
|           java-version: 17 | ||||
|           distribution: "temurin" | ||||
|   | ||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -20,6 +20,8 @@ app/build/ | ||||
| app/debug/ | ||||
| app/release/ | ||||
| captures/ | ||||
| .externalNativeBuild | ||||
| .cxx | ||||
| local.properties | ||||
| keystore.properties | ||||
| key.jks | ||||
| @@ -1,24 +1,21 @@ | ||||
| plugins { | ||||
|     id 'com.android.application' | ||||
|     id 'org.jetbrains.kotlin.android' | ||||
|     id 'org.jetbrains.kotlin.plugin.serialization' version '2.2.21' | ||||
|     id 'org.jetbrains.kotlin.plugin.compose' version '2.2.21' | ||||
|     id 'com.mikepenz.aboutlibraries.plugin' version '13.1.0' | ||||
|     id 'org.jetbrains.kotlin.plugin.serialization' version '2.2.0' | ||||
|     id 'com.mikepenz.aboutlibraries.plugin' version '12.2.3' | ||||
| } | ||||
|  | ||||
|  | ||||
| android { | ||||
|     namespace 'net.helcel.beans' | ||||
|     compileSdk 36 | ||||
|     compileSdk 34 | ||||
|  | ||||
|     defaultConfig { | ||||
|         buildConfigField("String", "APP_NAME", "\"Beans\"") | ||||
|         manifestPlaceholders["APP_NAME"] = "Beans" | ||||
|         applicationId 'net.helcel.beans' | ||||
|         minSdk 28 | ||||
|         targetSdk 36 | ||||
|         versionCode 4 | ||||
|         versionName "1.1a" | ||||
|         targetSdk 34 | ||||
|         versionCode 2 | ||||
|         versionName "1.0b" | ||||
|     } | ||||
|     signingConfigs { | ||||
|         create("release") { | ||||
| @@ -57,15 +54,17 @@ android { | ||||
|     compileOptions { | ||||
|         coreLibraryDesugaringEnabled true | ||||
|  | ||||
|         sourceCompatibility JavaVersion.VERSION_21 | ||||
|         targetCompatibility JavaVersion.VERSION_21 | ||||
|         sourceCompatibility JavaVersion.VERSION_17 | ||||
|         targetCompatibility JavaVersion.VERSION_17 | ||||
|         encoding 'utf-8' | ||||
|     } | ||||
|  | ||||
|     kotlinOptions { | ||||
|         jvmTarget = JavaVersion.VERSION_17 | ||||
|     } | ||||
|  | ||||
|     buildFeatures { | ||||
|         viewBinding true | ||||
|         compose true | ||||
|         buildConfig true | ||||
|     } | ||||
|  | ||||
|     dependenciesInfo { | ||||
| @@ -74,51 +73,21 @@ android { | ||||
|         // Disables dependency metadata when building Android App Bundles. | ||||
|         includeInBundle = false | ||||
|     } | ||||
|     composeOptions { | ||||
|         kotlinCompilerExtensionVersion = "2.2.20" | ||||
|     } | ||||
|  | ||||
|     kotlin { | ||||
|         jvmToolchain(21) | ||||
|     } | ||||
|  | ||||
|     lint { | ||||
|         disable 'UsingMaterialAndMaterial3Libraries' | ||||
|     } | ||||
|  | ||||
| } | ||||
| aboutLibraries { | ||||
|     library { | ||||
|     exclusionPatterns = [~"androidx.*", ~"com.google.android.*", ~"org.jetbrains.*"] | ||||
|     } | ||||
|     configPath = "config" | ||||
|     excludeFields = ["generated"] | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|     implementation 'androidx.compose.material3:material3:1.4.0' | ||||
|     implementation "androidx.compose.material:material:1.9.4" | ||||
|     implementation 'androidx.compose.material:material-icons-extended:1.7.8' | ||||
|     implementation 'androidx.navigation:navigation-compose:2.9.5' | ||||
|     coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.1.5' | ||||
|  | ||||
|     implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0' | ||||
|  | ||||
|     implementation 'androidx.preference:preference-ktx:1.2.1' | ||||
|     implementation 'androidx.compose.ui:ui' | ||||
|     implementation "androidx.activity:activity-ktx:1.11.0" | ||||
|  | ||||
|     implementation 'androidx.compose.ui:ui-tooling-preview' | ||||
|     implementation 'com.google.android.material:material:1.13.0' | ||||
|     implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0' | ||||
|     implementation 'com.google.android.material:material:1.12.0' | ||||
|     implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1' | ||||
|  | ||||
|     implementation 'com.caverock:androidsvg-aar:1.4' | ||||
|     implementation 'com.github.chrisbanes:PhotoView:2.3.0' | ||||
|  | ||||
|     implementation 'com.mikepenz:aboutlibraries:13.1.0' | ||||
|     implementation 'com.mikepenz:aboutlibraries-compose-m3:13.1.0' | ||||
|     implementation 'com.mikepenz:aboutlibraries-core:13.1.0' | ||||
|  | ||||
|  | ||||
|     implementation platform('androidx.compose:compose-bom:2025.10.01') | ||||
|     debugImplementation 'androidx.compose.ui:ui-tooling:1.9.4' | ||||
|     implementation 'com.mikepenz:aboutlibraries:12.2.3' | ||||
| } | ||||
| @@ -1,22 +1,52 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools"> | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:versionCode="2" | ||||
|     android:versionName="1.0c"> | ||||
|  | ||||
|     <application | ||||
|         android:allowBackup="true" | ||||
|         android:dataExtractionRules="@xml/data_extraction_rules" | ||||
|         android:fullBackupContent="@xml/backup_rules" | ||||
|         android:hardwareAccelerated="false" | ||||
|         android:icon="@mipmap/ic_launcher_round" | ||||
|         android:label="${APP_NAME}" | ||||
|         android:label="@string/app_name" | ||||
|         android:supportsRtl="true" | ||||
|         tools:replace="android:allowBackup"> | ||||
|         android:theme="@style/Theme.Beans" | ||||
|         tools:replace="android:allowBackup" | ||||
|         tools:targetApi="31"> | ||||
|         <profileable android:shell="true" /> | ||||
|  | ||||
|         <activity | ||||
|             android:name=".activity.MainScreen" | ||||
|             android:name=".activity.MainActivity" | ||||
|             android:exported="true"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MAIN" /> | ||||
|                 <category android:name="android.intent.category.LAUNCHER" /> | ||||
|             </intent-filter> | ||||
|         </activity> | ||||
|         <activity | ||||
|             android:name=".activity.EditActivity" | ||||
|             android:exported="true"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MAIN" /> | ||||
|             </intent-filter> | ||||
|         </activity> | ||||
|         <activity | ||||
|             android:name=".activity.StatsActivity" | ||||
|             android:exported="true"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MAIN" /> | ||||
|             </intent-filter> | ||||
|         </activity> | ||||
|         <activity | ||||
|             android:name=".activity.SettingsActivity" | ||||
|             android:exported="true"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MAIN" /> | ||||
|             </intent-filter> | ||||
|         </activity> | ||||
|  | ||||
|     </application> | ||||
|  | ||||
| </manifest> | ||||
							
								
								
									
										69
									
								
								app/src/main/java/net/helcel/beans/activity/EditActivity.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								app/src/main/java/net/helcel/beans/activity/EditActivity.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| 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) | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -1,66 +0,0 @@ | ||||
| package net.helcel.beans.activity | ||||
|  | ||||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.Spacer | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.height | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.material.Icon | ||||
| import androidx.compose.material.IconButton | ||||
| import androidx.compose.material.MaterialTheme | ||||
| import androidx.compose.material.Scaffold | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.material.TopAppBar | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.automirrored.filled.ArrowBack | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import androidx.compose.ui.unit.dp | ||||
| import net.helcel.beans.R | ||||
| import net.helcel.beans.activity.sub.EditPlaceScreen | ||||
| import net.helcel.beans.countries.World | ||||
|  | ||||
| @Preview | ||||
| @Composable | ||||
| fun EditScreen(onExit:()->Unit = {}) { | ||||
|     SysTheme { | ||||
|         Scaffold( | ||||
|             topBar = { | ||||
|                 TopAppBar( | ||||
|                     title = { Text(stringResource(R.string.action_edit)) }, | ||||
|                     navigationIcon = { | ||||
|                         IconButton(onClick = onExit) { | ||||
|                             Icon( | ||||
|                                 Icons.AutoMirrored.Filled.ArrowBack, | ||||
|                                 contentDescription = null | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 ) | ||||
|             }, | ||||
|  | ||||
|             ) { innerPadding -> | ||||
|             Box( | ||||
|                 modifier = Modifier | ||||
|                     .fillMaxSize() | ||||
|                     .background(MaterialTheme.colors.background) | ||||
|                     .padding(innerPadding) // ensures content is below the app bar | ||||
|             ) { | ||||
|                 Column { | ||||
|                     Spacer( | ||||
|                         modifier = Modifier.height(2.dp).fillMaxWidth() | ||||
|                             .background(MaterialTheme.colors.background) | ||||
|                     ) | ||||
|                     EditPlaceScreen(World.WWW, onExit) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										75
									
								
								app/src/main/java/net/helcel/beans/activity/MainActivity.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								app/src/main/java/net/helcel/beans/activity/MainActivity.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| 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) | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,124 +0,0 @@ | ||||
| package net.helcel.beans.activity | ||||
|  | ||||
| import android.graphics.drawable.PictureDrawable | ||||
| import android.os.Bundle | ||||
| import android.widget.ImageView | ||||
| import androidx.activity.ComponentActivity | ||||
| import androidx.activity.compose.setContent | ||||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.material.Icon | ||||
| import androidx.compose.material.IconButton | ||||
| import androidx.compose.material.MaterialTheme | ||||
| import androidx.compose.material.Scaffold | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.material.TopAppBar | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.DateRange | ||||
| import androidx.compose.material.icons.filled.Edit | ||||
| import androidx.compose.material.icons.filled.Percent | ||||
| import androidx.compose.material.icons.filled.Settings | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.viewinterop.AndroidView | ||||
| import androidx.navigation.NavHostController | ||||
| import androidx.navigation.compose.NavHost | ||||
| import androidx.navigation.compose.composable | ||||
| import androidx.navigation.compose.rememberNavController | ||||
| import com.caverock.androidsvg.RenderOptions | ||||
| import com.github.chrisbanes.photoview.PhotoView | ||||
| import net.helcel.beans.BuildConfig | ||||
| import net.helcel.beans.countries.GeoLocImporter | ||||
| import net.helcel.beans.helper.Data | ||||
| import net.helcel.beans.helper.Settings | ||||
| import net.helcel.beans.svg.CSSWrapper | ||||
| import net.helcel.beans.svg.SVGWrapper | ||||
|  | ||||
|  | ||||
| class MainScreen : ComponentActivity() { | ||||
|  | ||||
|     private lateinit var psvg: SVGWrapper | ||||
|     private lateinit var css: CSSWrapper | ||||
|  | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|         actionBar?.hide() | ||||
|         Settings.start(this) | ||||
|         GeoLocImporter.importStates(this) | ||||
|         Data.loadData(this, Int.MIN_VALUE) | ||||
|  | ||||
|         setContent { | ||||
|             SysTheme { | ||||
|                 Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.background)) { | ||||
|                     AppNavHost(psvg, css) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         refreshProjection() | ||||
|     } | ||||
|  | ||||
|     @Composable | ||||
|     fun AppNavHost(psvg: SVGWrapper, css: CSSWrapper) { | ||||
|         val navController = rememberNavController() | ||||
|         NavHost(navController, startDestination = "main") { | ||||
|             composable("main") { MainScreenC(psvg,css, navController) } | ||||
|             composable("settings") { SettingsMainScreen { navController.navigate("main")} } | ||||
|             composable("edit") { EditScreen { navController.navigate("main") } } | ||||
|             composable("stats") { StatsScreen { navController.navigate("main") } } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Composable | ||||
|     fun MainScreenC(psvg: SVGWrapper,css: CSSWrapper, nav: NavHostController){ | ||||
|         SysTheme { | ||||
|             Scaffold( | ||||
|                 topBar = { | ||||
|                     TopAppBar( | ||||
|                         title = { Text(BuildConfig.APP_NAME) }, | ||||
|                         actions = { | ||||
|                             IconButton(onClick = { nav.navigate("edit") }) { | ||||
|                                 Icon(Icons.Default.Edit, contentDescription = "Edit") | ||||
|                             } | ||||
|                             IconButton(onClick = {  nav.navigate("stats") }){ | ||||
|                                 Icon(Icons.Default.Percent, contentDescription = "Stats") | ||||
|                             } | ||||
|                             IconButton(onClick = { nav.navigate("settings") }) { | ||||
|                                 Icon(Icons.Default.Settings, contentDescription = "Settings") | ||||
|                             } | ||||
|                         } | ||||
|                     ) | ||||
|                 } | ||||
|             ) { innerPadding -> | ||||
|                 Box(modifier = Modifier.padding(innerPadding)) { | ||||
|                     MapScreen(psvg, css) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Composable | ||||
|     fun MapScreen(psvg: SVGWrapper, css: CSSWrapper) { | ||||
|         Box { | ||||
|             val opt: RenderOptions = RenderOptions.create() | ||||
|             opt.css(css.get()) | ||||
|             val drawable = remember(psvg, css) { | ||||
|                 PictureDrawable(psvg.get()?.renderToPicture(opt)) | ||||
|             } | ||||
|             AndroidView(factory = { ctx -> | ||||
|                 PhotoView(ctx).apply { | ||||
|                     setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null) | ||||
|                     setImageDrawable(drawable) | ||||
|                     scaleType = ImageView.ScaleType.FIT_CENTER | ||||
|                 } | ||||
|             }, modifier = Modifier.fillMaxSize()) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun refreshProjection() { | ||||
|         psvg = SVGWrapper(this) | ||||
|         css = CSSWrapper(this) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,63 @@ | ||||
| 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) | ||||
|     } | ||||
| } | ||||
| @@ -1,381 +0,0 @@ | ||||
| package net.helcel.beans.activity | ||||
|  | ||||
| import android.os.Build | ||||
| import androidx.activity.compose.rememberLauncherForActivityResult | ||||
| import androidx.activity.result.contract.ActivityResultContracts | ||||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.clickable | ||||
| import androidx.compose.foundation.isSystemInDarkTheme | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.Spacer | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.height | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.layout.size | ||||
| import androidx.compose.foundation.lazy.LazyColumn | ||||
| import androidx.compose.foundation.shape.CornerSize | ||||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||||
| import androidx.compose.material.Button | ||||
| import androidx.compose.material.CircularProgressIndicator | ||||
| import androidx.compose.material.Colors | ||||
| import androidx.compose.material.Icon | ||||
| import androidx.compose.material.IconButton | ||||
| import androidx.compose.material.MaterialTheme | ||||
| import androidx.compose.material.RadioButton | ||||
| import androidx.compose.material.Scaffold | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.material.TopAppBar | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.automirrored.filled.ArrowBack | ||||
| import androidx.compose.material3.HorizontalDivider | ||||
| import androidx.compose.material3.darkColorScheme | ||||
| import androidx.compose.material3.dynamicDarkColorScheme | ||||
| import androidx.compose.material3.dynamicLightColorScheme | ||||
| import androidx.compose.material3.lightColorScheme | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.rememberCoroutineScope | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.window.Dialog | ||||
| import androidx.preference.PreferenceManager | ||||
| import net.helcel.beans.R | ||||
| import net.helcel.beans.countries.GeoLocImporter | ||||
| import net.helcel.beans.helper.Settings | ||||
| import androidx.core.content.edit | ||||
| import androidx.navigation.NavHostController | ||||
| import androidx.navigation.compose.NavHost | ||||
| import androidx.navigation.compose.composable | ||||
| import androidx.navigation.compose.rememberNavController | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.launch | ||||
| import kotlinx.coroutines.withContext | ||||
| import net.helcel.beans.activity.sub.AboutScreen | ||||
| import net.helcel.beans.activity.sub.EditPlaceColorDialog | ||||
| import net.helcel.beans.activity.sub.EditPlaceDialog | ||||
| import net.helcel.beans.activity.sub.LicenseScreen | ||||
| import net.helcel.beans.helper.Data | ||||
|  | ||||
| @Composable | ||||
| fun SysTheme( | ||||
|     content: @Composable () -> Unit | ||||
| ) { | ||||
|     val context = LocalContext.current | ||||
|     val prefs = PreferenceManager.getDefaultSharedPreferences(context) | ||||
|     val themeKey = prefs.getString(stringResource(R.string.key_theme), stringResource(R.string.system)) | ||||
|     val darkTheme = when (themeKey) { | ||||
|         stringResource(R.string.system) -> isSystemInDarkTheme() | ||||
|         stringResource(R.string.light) -> false | ||||
|         stringResource(R.string.dark) -> true | ||||
|         else -> isSystemInDarkTheme() | ||||
|     } | ||||
|     val colorScheme = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { | ||||
|         if(darkTheme) dynamicDarkColorScheme(LocalContext.current ) else dynamicLightColorScheme(LocalContext.current ) | ||||
|     } else { | ||||
|         if(darkTheme) darkColorScheme() else lightColorScheme() | ||||
|     } | ||||
|     val m2colors = Colors( | ||||
|         primary = colorScheme.primary, | ||||
|         primaryVariant = colorScheme.primaryContainer, | ||||
|         secondary = colorScheme.secondary, | ||||
|         background = colorScheme.background, | ||||
|         surface = colorScheme.surface, | ||||
|         onPrimary = colorScheme.onPrimary, | ||||
|         onSecondary = colorScheme.onSecondary, | ||||
|         onBackground = colorScheme.onBackground, | ||||
|         onSurface = colorScheme.onSurface, | ||||
|         secondaryVariant = colorScheme.secondary, | ||||
|         error = colorScheme.error, | ||||
|         onError = colorScheme.onError, | ||||
|         isLight = !darkTheme, | ||||
|     ) | ||||
|  | ||||
|     MaterialTheme( | ||||
|         colors = m2colors, | ||||
|         content = content | ||||
|     ) | ||||
| } | ||||
|  | ||||
|  | ||||
| @Composable | ||||
| fun settingsNav(): NavHostController { | ||||
|     val navController = rememberNavController() | ||||
|     NavHost(navController, startDestination= "settings"){ | ||||
|         composable("settings"){SettingsScreen(navController)} | ||||
|         composable("licenses"){ LicenseScreen() } | ||||
|         composable("about"){ AboutScreen() } | ||||
|     } | ||||
|     return navController | ||||
| } | ||||
|  | ||||
| @Preview | ||||
| @Composable | ||||
| fun SettingsMainScreen(onExit: ()->Unit = {}) { | ||||
|     val nav: NavHostController = settingsNav() | ||||
|     SysTheme { | ||||
|         Scaffold( | ||||
|             topBar = { | ||||
|                 TopAppBar( | ||||
|                     title = { Text(stringResource(R.string.action_settings)) }, | ||||
|                     navigationIcon = { | ||||
|                         IconButton(onClick = { | ||||
|                             if(!nav.popBackStack()) | ||||
|                                 onExit() | ||||
|                         }) { | ||||
|                             Icon( | ||||
|                                 Icons.AutoMirrored.Filled.ArrowBack, | ||||
|                                 contentDescription = null | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 ) | ||||
|             } | ||||
|         ) { innerPadding -> | ||||
|             Box(modifier = Modifier.padding(innerPadding)) { | ||||
|                 SettingsScreen() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| fun SettingsScreen(navController: NavHostController = settingsNav()) { | ||||
|     val context = LocalContext.current | ||||
|     val prefs = PreferenceManager.getDefaultSharedPreferences(context) | ||||
|     var showEdit by remember { mutableStateOf(false) } | ||||
|  | ||||
|     var theme by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_theme), context.getString(R.string.system))!!) } | ||||
|     var projection by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_projection), "default")!!) } | ||||
|     var groups by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_group), context.getString(R.string.off))!!) } | ||||
|  | ||||
|         if(showEdit) | ||||
|             EditPlaceDialog(true) { | ||||
|                 showEdit = false | ||||
|                 val g = Data.selected_group | ||||
|                 if (it && g != null) | ||||
|                     Data.visits.reassignAllVisitedToGroup(g.key) | ||||
|             } | ||||
|  | ||||
|         LazyColumn( | ||||
|             modifier = Modifier | ||||
|                 .fillMaxSize() | ||||
|                 .padding(16.dp) | ||||
|                 .background(MaterialTheme.colors.background) | ||||
|         ) { | ||||
|             item { | ||||
|                 Text( | ||||
|                     "Theme", style = MaterialTheme.typography.h6, | ||||
|                     color = MaterialTheme.colors.onBackground, | ||||
|                 ) | ||||
|                 MultiPreference(arrayOf(stringResource(R.string.system),stringResource(R.string.light),stringResource(R.string.dark)), theme) { newTheme -> | ||||
|                     theme = newTheme | ||||
|                     prefs.edit { putString(context.getString(R.string.key_theme), newTheme) } | ||||
|                 } | ||||
|                 HorizontalDivider() | ||||
|             } | ||||
|             item { | ||||
|                 Text( | ||||
|                     "Map Projection", | ||||
|                     style = MaterialTheme.typography.h6, | ||||
|                     color = MaterialTheme.colors.onBackground, | ||||
|                     modifier = Modifier.padding(top = 16.dp) | ||||
|                 ) | ||||
|                 MultiPreference(arrayOf(stringResource(R.string.mercator), stringResource(R.string.azimuthalequidistant)), projection) { newProj -> | ||||
|                     projection = newProj | ||||
|                     prefs.edit { putString(context.getString(R.string.key_projection), newProj) } | ||||
|                     Settings.refreshProjection() | ||||
|                 } | ||||
|                 HorizontalDivider() | ||||
|             } | ||||
|             item { | ||||
|                 Text( | ||||
|                     "Groups", | ||||
|                     style = MaterialTheme.typography.h6, | ||||
|                     color = MaterialTheme.colors.onBackground, | ||||
|                     modifier = Modifier.padding(top = 16.dp) | ||||
|                 ) | ||||
|                 var showDialog by remember{mutableStateOf(false)} | ||||
|                 if(showDialog){ | ||||
|                     EditPlaceColorDialog( | ||||
|                         deleteMode = true, | ||||
|                         onDismiss = { | ||||
|                             val g = Data.selected_group | ||||
|                             if (g != null) | ||||
|                                 Data.visits.reassignAllVisitedToGroup(g.key) | ||||
|                             showDialog = false | ||||
|                         }) | ||||
|                 } | ||||
|                 MultiPreference( | ||||
|                     arrayOf(stringResource(R.string.on), stringResource(R.string.off)), | ||||
|                     groups | ||||
|                 ) { key -> | ||||
|                     if (key == context.getString(R.string.off)) { | ||||
|                         showDialog=true | ||||
|                     } | ||||
|                     groups = key | ||||
|                     prefs.edit { putString(context.getString(R.string.key_group), key) } | ||||
|                 } | ||||
|                 HorizontalDivider() | ||||
|             } | ||||
|             item { | ||||
|                 Text( | ||||
|                     text = "Regional", | ||||
|                     style = MaterialTheme.typography.h6, | ||||
|                     color = MaterialTheme.colors.onBackground, | ||||
|                     modifier = Modifier | ||||
|                         .padding(top = 16.dp) | ||||
|                         .clickable(onClick = {}), | ||||
|                 ) | ||||
|                 RegionalScreen() | ||||
|                 HorizontalDivider() | ||||
|             } | ||||
|             item{ | ||||
|                 val launcher = rememberLauncherForActivityResult( | ||||
|                 contract = ActivityResultContracts.OpenDocument(), | ||||
|                 onResult = { uri -> Data.doImport(context, uri)   } | ||||
|                 ) | ||||
|                 Row( | ||||
|                     modifier = Modifier.fillMaxWidth() | ||||
|                 ) { | ||||
|                     Button(onClick = { | ||||
|                         launcher.launch(arrayOf("*/*")) | ||||
|                     }, modifier = Modifier | ||||
|                         .fillMaxWidth(fraction = 0.4f) | ||||
|                         .padding(vertical = 8.dp)) { | ||||
|                         Text("Import") | ||||
|                     } | ||||
|                     Spacer( | ||||
|                         modifier = Modifier.fillMaxWidth(0.4f) | ||||
|                     ) | ||||
|                     Button(onClick = { | ||||
|                         Data.doExport(context) | ||||
|                     }, modifier = Modifier | ||||
|                         .fillMaxWidth(fraction = 1f) | ||||
|                         .padding(vertical = 8.dp)) { | ||||
|                         Text("Export") | ||||
|                     } | ||||
|                 } | ||||
|                 HorizontalDivider() | ||||
|             } | ||||
|             item { | ||||
|                 PreferenceButton("Licenses") { | ||||
|                     if (navController.currentDestination?.route != "licenses") | ||||
|                         navController.navigate("licenses") | ||||
|                 } | ||||
|                 PreferenceButton("About") { | ||||
|                     if (navController.currentDestination?.route != "about") | ||||
|                         navController.navigate("about") | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @Composable | ||||
| fun RegionalScreen() { | ||||
|     val context = LocalContext.current | ||||
|     val prefs = PreferenceManager.getDefaultSharedPreferences(context) | ||||
|     var selected by remember {  mutableStateOf(prefs.getString(context.getString(R.string.key_regional),context.getString(R.string.off))!!)} | ||||
|     var regional by remember{ mutableStateOf(prefs.getString(context.getString(R.string.key_regional), context.getString(R.string.off))!!)} | ||||
|     var showDialog by remember{mutableStateOf(false)} | ||||
|     var showLoad by remember{mutableStateOf(false)} | ||||
|  | ||||
|     if(showDialog) | ||||
|         Dialog( | ||||
|             content = { | ||||
|                 Column( | ||||
|                     modifier = Modifier | ||||
|                         .background( | ||||
|                             MaterialTheme.colors.background, | ||||
|                             RoundedCornerShape(corner = CornerSize(16.dp)) | ||||
|                         ) | ||||
|                         .padding(16.dp),){ | ||||
|                 Text(style=MaterialTheme.typography.caption, text=  stringResource(R.string.delete_regions)) | ||||
|                     Button(onClick = { | ||||
|                         GeoLocImporter.clearStates() | ||||
|                         regional= selected | ||||
|                         prefs.edit { | ||||
|                             putString( | ||||
|                                 context.getString(R.string.key_regional), | ||||
|                                 regional | ||||
|                             ) | ||||
|                         } | ||||
|                         showDialog=false | ||||
|                     }){ | ||||
|                        Text(stringResource(R.string.ok)) | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             onDismissRequest = { showDialog=false } | ||||
|         ) | ||||
|     if(showLoad){ | ||||
|         Dialog( | ||||
|             content = { | ||||
|                 CircularProgressIndicator( | ||||
|                     color = MaterialTheme.colors.primary, | ||||
|                     strokeWidth = 4.dp, | ||||
|                     modifier = Modifier.size(50.dp) | ||||
|                 ) | ||||
|             }, | ||||
|             onDismissRequest = {} | ||||
|         ) | ||||
|     } | ||||
|     val scope = rememberCoroutineScope() | ||||
|     MultiPreference(arrayOf(stringResource(R.string.on),stringResource(R.string.off)),regional) { key -> | ||||
|                 when (key) { | ||||
|                     context.getString(R.string.off) -> { showDialog=true | ||||
|                         selected=key | ||||
|                     } | ||||
|                     context.getString(R.string.on) -> { | ||||
|                         regional = key | ||||
|                         prefs.edit { putString(context.getString(R.string.key_regional), key) } | ||||
|                         showLoad=true | ||||
|                         scope.launch { | ||||
|                             withContext(Dispatchers.IO) { | ||||
|                                 GeoLocImporter.importStates(context, true) | ||||
|                             } | ||||
|                             showLoad = false | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| } | ||||
|  | ||||
|  | ||||
| @Composable | ||||
| fun MultiPreference(list: Array<String>, selected: String, onSelected: (String) -> Unit) { | ||||
|     Column(Modifier.padding(2.dp)) { | ||||
|         list.map { value -> | ||||
|             Row( | ||||
|                 verticalAlignment = Alignment.CenterVertically, | ||||
|                 modifier = Modifier | ||||
|                     .fillMaxWidth() | ||||
|                     .height(36.dp) | ||||
|                     .clickable { onSelected(value) }) { | ||||
|                 RadioButton(selected = selected == value, onClick = { onSelected(value) }) | ||||
|                 Text( | ||||
|                     value, modifier = Modifier.padding(start = 8.dp), | ||||
|                     color = MaterialTheme.colors.onBackground, | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| fun PreferenceButton(text: String, onClick: () -> Unit) { | ||||
|     Button(onClick = onClick, modifier = Modifier | ||||
|         .fillMaxWidth() | ||||
|         .padding(vertical = 8.dp)) { | ||||
|         Text(text) | ||||
|     } | ||||
| } | ||||
| @@ -1,159 +1,57 @@ | ||||
| package net.helcel.beans.activity | ||||
|  | ||||
| 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 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 net.helcel.beans.R | ||||
| import net.helcel.beans.activity.adapter.StatsListAdapter | ||||
| import net.helcel.beans.countries.GeoLoc.LocType | ||||
| 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 | ||||
| import net.helcel.beans.databinding.ActivityStatBinding | ||||
| import net.helcel.beans.helper.Settings | ||||
| import net.helcel.beans.helper.Theme.createActionBar | ||||
|  | ||||
| private val MODE_LIST = listOf(LocType.WORLD, LocType.COUNTRY, LocType.STATE) | ||||
|  | ||||
| @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) } | ||||
| class StatsActivity : AppCompatActivity() { | ||||
|     private lateinit var _binding: ActivityStatBinding | ||||
|     private var activeMode = LocType.WORLD | ||||
|  | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|         _binding = ActivityStatBinding.inflate(layoutInflater) | ||||
|         setContentView(_binding.root) | ||||
|         createActionBar(this, getString(R.string.action_stat)) | ||||
|  | ||||
|     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.stats.layoutManager = | ||||
|             LinearLayoutManager(this, RecyclerView.VERTICAL, false) | ||||
|         val adapter = StatsListAdapter(_binding.stats, _binding.name) | ||||
|         _binding.groupColor.setOnClickListener { adapter.invertCountMode() } | ||||
|         _binding.stats.adapter = adapter | ||||
|  | ||||
|                 ) | ||||
|             }, | ||||
|         ) { 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.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() | ||||
|  | ||||
|         _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) | ||||
|     } | ||||
|  | ||||
|     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) | ||||
|         ) | ||||
|     override fun onOptionsItemSelected(item: MenuItem): Boolean { | ||||
|         finish() | ||||
|         return super.onOptionsItemSelected(item) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,193 @@ | ||||
| package net.helcel.beans.activity.adapter | ||||
|  | ||||
| import android.content.res.ColorStateList | ||||
| import android.graphics.Color | ||||
| import android.graphics.Typeface | ||||
| import android.graphics.drawable.ColorDrawable | ||||
| import android.view.LayoutInflater | ||||
| import android.view.ViewGroup | ||||
| import androidx.fragment.app.FragmentActivity | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import com.google.android.material.checkbox.MaterialCheckBox | ||||
| import net.helcel.beans.activity.fragment.EditPlaceColorFragment | ||||
| import net.helcel.beans.activity.fragment.EditPlaceFragment | ||||
| import net.helcel.beans.countries.GeoLoc | ||||
| import net.helcel.beans.databinding.ItemListGeolocBinding | ||||
| import net.helcel.beans.helper.* | ||||
| import net.helcel.beans.helper.Theme.colorWrapper | ||||
|  | ||||
| class GeolocListAdapter( | ||||
|     private val ctx: EditPlaceFragment, private val l: GeoLoc, private val pager: ViewPagerAdapter, | ||||
|     private val parentHolder: FoldingListViewHolder? | ||||
| ) : RecyclerView.Adapter<GeolocListAdapter.FoldingListViewHolder>() { | ||||
|  | ||||
|     private val sortedList = l.children.toList().sortedBy { it.fullName } | ||||
|     private val holders: MutableSet<FoldingListViewHolder> = mutableSetOf() | ||||
|  | ||||
|     override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): FoldingListViewHolder { | ||||
|         val binding = ItemListGeolocBinding.inflate( | ||||
|             LayoutInflater.from(viewGroup.context), | ||||
|             viewGroup, | ||||
|             false | ||||
|         ) | ||||
|         val holder = FoldingListViewHolder(ctx.requireActivity(), binding, parentHolder, l) | ||||
|         holders.add(holder) | ||||
|         return holder | ||||
|     } | ||||
|  | ||||
|     override fun onBindViewHolder(holder: FoldingListViewHolder, position: Int) { | ||||
|         val el = sortedList[position] | ||||
|         holder.bind(el) | ||||
|         holder.addListeners(el) { | ||||
|             if (el.children.isNotEmpty()) | ||||
|                 pager.addFragment(ctx, EditPlaceFragment(el, pager, holder)) | ||||
|             true | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun getItemCount(): Int { | ||||
|         return l.children.size | ||||
|     } | ||||
|  | ||||
|     fun refreshColors(colorDrawable: ColorDrawable) { | ||||
|         holders.forEach { it.refreshColor(colorDrawable) } | ||||
|     } | ||||
|  | ||||
|     class FoldingListViewHolder( | ||||
|         private val ctx: FragmentActivity, | ||||
|         private val _binding: ItemListGeolocBinding, | ||||
|         private val _parentHolder: FoldingListViewHolder? = null, | ||||
|         private val _parentGeoLoc: GeoLoc, | ||||
|     ) : RecyclerView.ViewHolder(_binding.root), DialogCloser { | ||||
|         private lateinit var el: GeoLoc | ||||
|  | ||||
|         private fun bindGroup(el: GeoLoc) { | ||||
|             refreshCount(el) | ||||
|             _binding.textView.setTypeface(null, Typeface.BOLD) | ||||
|             _binding.textView.backgroundTintList = ColorStateList.valueOf( | ||||
|                 colorWrapper( | ||||
|                     ctx, | ||||
|                     android.R.attr.panelColorBackground | ||||
|                 ).color | ||||
|             ).withAlpha(64) | ||||
|         } | ||||
|  | ||||
|         fun bind(el: GeoLoc) { | ||||
|             this.el = el | ||||
|             _binding.textView.text = el.fullName | ||||
|             _binding.textView.backgroundTintList = | ||||
|                 ColorStateList.valueOf(colorWrapper(ctx, android.R.attr.colorBackground).color) | ||||
|  | ||||
|             if (el.children.isNotEmpty()) | ||||
|                 bindGroup(el) | ||||
|  | ||||
|             refreshCheck(el) | ||||
|         } | ||||
|  | ||||
|         fun refreshColor(colorDrawable: ColorDrawable) { | ||||
|             if (Data.visits.getVisited(el) !in listOf(NO_GROUP, AUTO_GROUP)) { | ||||
|                 _binding.checkBox.buttonTintList = | ||||
|                     ColorStateList.valueOf(colorDrawable.color) | ||||
|                 refreshCheck(el) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         fun addListeners(el: GeoLoc, expandLambda: () -> Boolean) { | ||||
|             if (el.children.isNotEmpty()) { | ||||
|                 _binding.textView.setOnClickListener { expandLambda() } | ||||
|             } | ||||
|             _binding.checkBox.setOnClickListener { | ||||
|                 Data.selected_geoloc = el | ||||
|                 if (Data.groups.size() == 1 && Settings.isSingleGroup(ctx)) { | ||||
|                     if (_binding.checkBox.isChecked) { | ||||
|                         // If one has just checked the box (assign unique group) | ||||
|                         Data.selected_group = Data.groups.getUniqueEntry() | ||||
|                         onDialogDismiss(false) | ||||
|                     } else { | ||||
|                         // If one has just unchecked the box (unassign unique group) | ||||
|                         Data.selected_group = null | ||||
|                         onDialogDismiss(true) | ||||
|                     } | ||||
|                 } else { | ||||
|                     Data.selected_group = null | ||||
|                     EditPlaceColorFragment(this).show( | ||||
|                         ctx.supportFragmentManager, | ||||
|                         "AddColorDialogFragment" | ||||
|                     ) | ||||
|                 } | ||||
|                 _parentHolder?.refresh(_parentGeoLoc) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         override fun onDialogDismiss(clear: Boolean) { | ||||
|             if (clear) { | ||||
|                 Data.visits.setVisited(Data.selected_geoloc, NO_GROUP) | ||||
|                 Data.saveData() | ||||
|  | ||||
|                 if (_parentGeoLoc.children.all { Data.visits.getVisited(it) == NO_GROUP }) { | ||||
|                     Data.clearing_geoloc = _parentGeoLoc | ||||
|                 } | ||||
|             } | ||||
|             if (Data.selected_group != null && Data.selected_geoloc != null) { | ||||
|                 Data.visits.setVisited(Data.selected_geoloc, Data.selected_group?.key ?: NO_GROUP) | ||||
|                 Data.saveData() | ||||
|             } | ||||
|             Data.selected_geoloc?.let { refreshCheck(it) } | ||||
|             Data.selected_geoloc = null | ||||
|             Data.selected_group = null | ||||
|             _parentHolder?.refresh(_parentGeoLoc) | ||||
|         } | ||||
|  | ||||
|         private fun refreshCheck(geoLoc: GeoLoc) { | ||||
|             _binding.checkBox.checkedState = | ||||
|                 if (Data.visits.getVisited(geoLoc) !in listOf(NO_GROUP, AUTO_GROUP)) { | ||||
|                     MaterialCheckBox.STATE_CHECKED | ||||
|                 } else if (geoLoc.children.isNotEmpty() && | ||||
|                     geoLoc.children.all { | ||||
|                         Data.visits.getVisited(it) !in listOf(NO_GROUP, AUTO_GROUP) | ||||
|                     } | ||||
|                 ) { | ||||
|                     Data.visits.setVisited(geoLoc, AUTO_GROUP) | ||||
|                     MaterialCheckBox.STATE_CHECKED | ||||
|                 } else if (geoLoc.children.isEmpty() && Data.visits.getVisited(geoLoc) == AUTO_GROUP) { | ||||
|                     MaterialCheckBox.STATE_CHECKED | ||||
|                 } else if (geoLoc.children.any { Data.visits.getVisited(it) != NO_GROUP }) { | ||||
|                     Data.visits.setVisited(geoLoc, AUTO_GROUP) | ||||
|                     MaterialCheckBox.STATE_INDETERMINATE | ||||
|                 } else { | ||||
|                     Data.visits.setVisited(geoLoc, NO_GROUP) | ||||
|                     if (Data.clearing_geoloc == geoLoc) { | ||||
|                         Data.clearing_geoloc = null | ||||
|                     } | ||||
|                     MaterialCheckBox.STATE_UNCHECKED | ||||
|                 } | ||||
|             Data.saveData() | ||||
|  | ||||
|             var col = Data.groups.getGroupFromKey(Data.visits.getVisited(geoLoc)).color | ||||
|             if (Data.visits.getVisited(geoLoc) == AUTO_GROUP) { | ||||
|                 col = colorWrapper(ctx, android.R.attr.colorPrimary) | ||||
|             } else if (col.color == Color.TRANSPARENT) { | ||||
|                 col = colorWrapper(ctx, android.R.attr.panelColorBackground) | ||||
|                 col.alpha = 64 | ||||
|             } | ||||
|             _binding.checkBox.buttonTintList = ColorStateList.valueOf(col.color) | ||||
|         } | ||||
|  | ||||
|         private fun refreshCount(geoLoc: GeoLoc) { | ||||
|             val numerator = | ||||
|                 geoLoc.children.map { Data.visits.getVisited(it) != NO_GROUP }.count { it } | ||||
|             val denominator = geoLoc.children.size | ||||
|             _binding.count.text = Settings.getStats(ctx, numerator, denominator) | ||||
|         } | ||||
|  | ||||
|         private fun refresh(geoLoc: GeoLoc) { | ||||
|             // Refresh | ||||
|             refreshCheck(geoLoc) | ||||
|             refreshCount(geoLoc) | ||||
|  | ||||
|             // Recursively refresh parent | ||||
|             _parentHolder?.refresh(_parentGeoLoc) | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,78 @@ | ||||
| package net.helcel.beans.activity.adapter | ||||
|  | ||||
| import android.view.LayoutInflater | ||||
| import android.view.ViewGroup | ||||
| import androidx.fragment.app.DialogFragment | ||||
| import androidx.fragment.app.FragmentActivity | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import net.helcel.beans.activity.fragment.EditGroupAddFragment | ||||
| import net.helcel.beans.databinding.ItemListGroupBinding | ||||
| import net.helcel.beans.helper.Data | ||||
| import net.helcel.beans.helper.Groups | ||||
| import net.helcel.beans.helper.Theme.getContrastColor | ||||
|  | ||||
| class GroupListAdapter( | ||||
|     private val activity: FragmentActivity, | ||||
|     private val selectDialog: DialogFragment, | ||||
|     private val delete: Boolean = false | ||||
| ) : RecyclerView.Adapter<GroupListAdapter.GroupViewHolder>() { | ||||
|  | ||||
|     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GroupViewHolder { | ||||
|         val binding = | ||||
|             ItemListGroupBinding.inflate(LayoutInflater.from(parent.context), parent, false) | ||||
|         return GroupViewHolder(binding, activity, selectDialog) | ||||
|     } | ||||
|  | ||||
|     override fun onBindViewHolder(holder: GroupViewHolder, pos: Int) { | ||||
|         holder.bind(Data.groups.getGroupFromPos(pos)) | ||||
|     } | ||||
|  | ||||
|     override fun getItemCount(): Int { | ||||
|         return Data.groups.size() | ||||
|     } | ||||
|  | ||||
|     inner class GroupViewHolder( | ||||
|         private val _binding: ItemListGroupBinding, | ||||
|         private val activity: FragmentActivity, | ||||
|         private val selectDialog: DialogFragment | ||||
|     ) : RecyclerView.ViewHolder(_binding.root) { | ||||
|         private lateinit var dialogFragment: EditGroupAddFragment | ||||
|         fun bind(entry: Pair<Int, Groups.Group>) { | ||||
|             _binding.groupColor.text = entry.second.name | ||||
|             dialogFragment = EditGroupAddFragment(entry.first, { | ||||
|                 val newEntry = Data.groups.getGroupFromKey(entry.first) | ||||
|                 _binding.groupColor.text = newEntry.name | ||||
|                 val newEntryColor = newEntry.color.color | ||||
|                 val contrastNewEntryColor = | ||||
|                     getContrastColor(newEntryColor) | ||||
|                 _binding.groupColor.setBackgroundColor(newEntryColor) | ||||
|                 _binding.groupColor.setTextColor(contrastNewEntryColor) | ||||
|                 _binding.name.setTextColor(contrastNewEntryColor) | ||||
|                 _binding.name.text = "0" | ||||
|             }, { | ||||
|                 notifyItemRemoved(it) | ||||
|             }) | ||||
|  | ||||
|             val entryColor = entry.second.color.color | ||||
|             val contrastEntryColor = getContrastColor(entryColor) | ||||
|             _binding.groupColor.setBackgroundColor(entryColor) | ||||
|             _binding.groupColor.setTextColor(contrastEntryColor) | ||||
|             _binding.name.setTextColor(contrastEntryColor) | ||||
|             _binding.name.text = Data.visits.countVisited(entry.first).toString() | ||||
|  | ||||
|             _binding.groupColor.setOnClickListener { | ||||
|                 Data.selected_group = entry.second | ||||
|                 selectDialog.dismiss() | ||||
|             } | ||||
|             if (!delete) { | ||||
|                 _binding.groupColor.setOnLongClickListener { | ||||
|                     dialogFragment.show( | ||||
|                         activity.supportFragmentManager, | ||||
|                         "AddColorDialogFragment" | ||||
|                     ) | ||||
|                     true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,152 @@ | ||||
| package net.helcel.beans.activity.adapter | ||||
|  | ||||
| import android.content.Context | ||||
| import android.view.LayoutInflater | ||||
| import android.view.ViewGroup | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import com.google.android.material.textview.MaterialTextView | ||||
| import net.helcel.beans.R | ||||
| import net.helcel.beans.countries.GeoLoc | ||||
| import net.helcel.beans.countries.GeoLoc.LocType | ||||
| import net.helcel.beans.countries.World | ||||
| import net.helcel.beans.databinding.ItemListGroupBinding | ||||
| import net.helcel.beans.helper.AUTO_GROUP | ||||
| import net.helcel.beans.helper.Data | ||||
| import net.helcel.beans.helper.Groups | ||||
| import net.helcel.beans.helper.Settings | ||||
| import net.helcel.beans.helper.Theme.getContrastColor | ||||
|  | ||||
| class StatsListAdapter(private val stats: RecyclerView, private val total: MaterialTextView) : | ||||
|     RecyclerView.Adapter<StatsListAdapter.StatsViewHolder>() { | ||||
|     private val unit = "km²" | ||||
|  | ||||
|     private var locMode = LocType.WORLD | ||||
|     private lateinit var ctx: Context | ||||
|     private var countMode: Boolean = true | ||||
|     private var initialSum: Int = 0 | ||||
|  | ||||
|     private val wwwTotal: List<GeoLoc> = World.WWW.children.toList() | ||||
|     private val countryTotal: List<GeoLoc> = World.WWW.children.flatMap { it.children } | ||||
|     private val stateTotal: List<GeoLoc> = | ||||
|         World.WWW.children.flatMap { it.children.flatMap { itt -> itt.children } } | ||||
|  | ||||
|     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatsViewHolder { | ||||
|         ctx = parent.context | ||||
|         val binding = | ||||
|             ItemListGroupBinding.inflate(LayoutInflater.from(ctx), parent, false) | ||||
|  | ||||
|         return StatsViewHolder(binding) | ||||
|     } | ||||
|  | ||||
|     override fun onBindViewHolder(holder: StatsViewHolder, pos: Int) { | ||||
|         initialSum += if (pos == itemCount - 1) { | ||||
|             holder.bind( | ||||
|                 Pair( | ||||
|                     AUTO_GROUP, | ||||
|                     Groups.Group(AUTO_GROUP, ctx.getString(R.string.uncategorized)) | ||||
|                 ) | ||||
|             ) | ||||
|         } else { | ||||
|             holder.bind(Data.groups.getGroupFromPos(pos)) | ||||
|         } | ||||
|         val unitNow = if (!countMode) unit else "" | ||||
|         total.text = Settings.getStats(ctx, initialSum, getTotal(), unitNow) | ||||
|     } | ||||
|  | ||||
|     override fun getItemCount(): Int { | ||||
|         return Data.groups.size() + 1 | ||||
|     } | ||||
|  | ||||
|     private fun getTotal(): Int { | ||||
|         return if (countMode) { | ||||
|             when (locMode) { | ||||
|                 LocType.WORLD -> wwwTotal.size | ||||
|                 LocType.COUNTRY -> countryTotal.size | ||||
|                 LocType.STATE -> stateTotal.size | ||||
|                 else -> 0 | ||||
|             } | ||||
|         } else { | ||||
|             when (locMode) { | ||||
|                 LocType.WORLD -> wwwTotal.sumOf { it.area } | ||||
|                 LocType.COUNTRY -> countryTotal.sumOf { it.area } | ||||
|                 LocType.STATE -> stateTotal.sumOf { it.area } | ||||
|                 else -> 0 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun refreshMode(mode: LocType) { | ||||
|         val sum = (0 until itemCount).map { | ||||
|             val viewHolder = stats.findViewHolderForAdapterPosition(it) as? StatsViewHolder | ||||
|             viewHolder?.refresh(mode) | ||||
|         }.reduce { acc, i -> acc?.plus((i ?: 0)) } | ||||
|         val unitNow = if (!countMode) unit else "" | ||||
|         total.text = Settings.getStats(ctx, sum, getTotal(), unitNow) | ||||
|     } | ||||
|  | ||||
|     fun invertCountMode() { | ||||
|         countMode = !countMode | ||||
|         refreshMode(locMode) | ||||
|     } | ||||
|  | ||||
|     inner class StatsViewHolder( | ||||
|         private val _binding: ItemListGroupBinding | ||||
|     ) : RecyclerView.ViewHolder(_binding.root) { | ||||
|  | ||||
|         private lateinit var data: Pair<Int, Groups.Group> | ||||
|  | ||||
|         private lateinit var wwwCount: List<GeoLoc> | ||||
|         private lateinit var countryCount: List<GeoLoc> | ||||
|         private lateinit var stateCount: List<GeoLoc> | ||||
|  | ||||
|         fun bind(entry: Pair<Int, Groups.Group>): Int { | ||||
|             data = entry | ||||
|             _binding.groupColor.text = entry.second.name | ||||
|  | ||||
|             val entryColor = data.second.color.color | ||||
|             val contrastEntryColor = getContrastColor(entryColor) | ||||
|             _binding.groupColor.setBackgroundColor(entryColor) | ||||
|             _binding.groupColor.setTextColor(contrastEntryColor) | ||||
|             _binding.name.setTextColor(contrastEntryColor) | ||||
|  | ||||
|             _binding.groupColor.setOnClickListener { invertCountMode() } | ||||
|             compute() | ||||
|             return refresh(locMode) | ||||
|         } | ||||
|  | ||||
|         private fun compute() { | ||||
|             val visited = Data.visits.getVisitedByValue(data.first) | ||||
|             wwwCount = World.WWW.children.filter { it.code in visited } | ||||
|             countryCount = | ||||
|                 World.WWW.children.map { it.children.filter { itt -> itt.code in visited } } | ||||
|                     .flatten() | ||||
|             stateCount = | ||||
|                 World.WWW.children.map { it.children.map { itt -> itt.children.filter { ittt -> ittt.code in visited } } } | ||||
|                     .flatten().flatten() | ||||
|         } | ||||
|  | ||||
|         fun refresh(mode: LocType): Int { | ||||
|             locMode = mode | ||||
|             return if (countMode) { | ||||
|                 val count = when (locMode) { | ||||
|                     LocType.WORLD -> wwwCount.size | ||||
|                     LocType.COUNTRY -> countryCount.size | ||||
|                     LocType.STATE -> stateCount.size | ||||
|                     else -> -1 | ||||
|                 } | ||||
|                 _binding.name.text = count.toString() | ||||
|                 count | ||||
|             } else { | ||||
|                 val area = when (locMode) { | ||||
|                     LocType.WORLD -> wwwCount.sumOf { it.area } | ||||
|                     LocType.COUNTRY -> countryCount.sumOf { it.area } | ||||
|                     LocType.STATE -> stateCount.sumOf { it.area } | ||||
|                     else -> -1 | ||||
|                 } | ||||
|                 _binding.name.text = ctx.getString(R.string.number_with_unit, area, unit) | ||||
|                 area | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,61 @@ | ||||
| package net.helcel.beans.activity.adapter | ||||
|  | ||||
| import android.graphics.drawable.ColorDrawable | ||||
| import androidx.fragment.app.Fragment | ||||
| import androidx.fragment.app.FragmentManager | ||||
| import androidx.lifecycle.Lifecycle | ||||
| import androidx.viewpager2.adapter.FragmentStateAdapter | ||||
| import androidx.viewpager2.widget.ViewPager2 | ||||
| import net.helcel.beans.activity.fragment.EditPlaceFragment | ||||
| import kotlin.math.max | ||||
|  | ||||
| class ViewPagerAdapter( | ||||
|     fragmentManager: FragmentManager, | ||||
|     lifecycle: Lifecycle, | ||||
|     private val viewPager: ViewPager2 | ||||
| ) : | ||||
|     FragmentStateAdapter(fragmentManager, lifecycle) { | ||||
|  | ||||
|     private val fragmentList: MutableList<EditPlaceFragment> = ArrayList() | ||||
|  | ||||
|     fun addFragment(src: EditPlaceFragment?, target: EditPlaceFragment) { | ||||
|         val idx = fragmentList.indexOf(src) | ||||
|         viewPager.currentItem = max(0, idx) | ||||
|         if (src != null && idx >= 0) { | ||||
|             fragmentList.subList(idx + 1, fragmentList.size).clear() | ||||
|         } | ||||
|         fragmentList.add(target) | ||||
|         notifyItemRangeChanged(max(0, idx), fragmentList.size) | ||||
|         viewPager.currentItem = fragmentList.size - 1 | ||||
|     } | ||||
|  | ||||
|     override fun getItemCount(): Int { | ||||
|         return fragmentList.size | ||||
|     } | ||||
|  | ||||
|     fun backPressed(): Boolean { | ||||
|         if (viewPager.currentItem == 0) { | ||||
|             return false | ||||
|         } | ||||
|         val target = viewPager.currentItem | ||||
|         while (fragmentList.size > target) { | ||||
|             fragmentList.removeLast() | ||||
|             notifyItemRemoved(fragmentList.size) | ||||
|         } | ||||
|         return true | ||||
|     } | ||||
|  | ||||
|     fun getLabel(pos: Int): String { | ||||
|         return fragmentList[pos].loc.fullName | ||||
|     } | ||||
|  | ||||
|     override fun createFragment(position: Int): Fragment { | ||||
|         return fragmentList[position] | ||||
|     } | ||||
|  | ||||
|     fun refreshColors(colorDrawable: ColorDrawable) { | ||||
|         fragmentList.forEach{ it.refreshColors(colorDrawable)} | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -0,0 +1,21 @@ | ||||
| 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 | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,155 @@ | ||||
| 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 | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,60 @@ | ||||
| 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) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,38 @@ | ||||
| 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) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,33 @@ | ||||
| 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 | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,142 @@ | ||||
| package net.helcel.beans.activity.fragment | ||||
|  | ||||
| import android.content.Context | ||||
| import android.os.Bundle | ||||
| import androidx.appcompat.app.AppCompatDelegate | ||||
| import androidx.preference.Preference | ||||
| import androidx.preference.PreferenceFragmentCompat | ||||
| import androidx.preference.PreferenceManager | ||||
| import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||||
| import net.helcel.beans.R | ||||
| import net.helcel.beans.countries.GeoLocImporter | ||||
| import net.helcel.beans.helper.Data | ||||
| import net.helcel.beans.helper.DialogCloser | ||||
| import net.helcel.beans.helper.Settings | ||||
|  | ||||
|  | ||||
| class SettingsFragment : PreferenceFragmentCompat(), DialogCloser { | ||||
|     private var savedInstanceState: Bundle? = null | ||||
|     private var rootKey: String? = null | ||||
|  | ||||
|     override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { | ||||
|         this.savedInstanceState = savedInstanceState | ||||
|         this.rootKey = rootKey | ||||
|  | ||||
|         setPreferencesFromResource(R.xml.fragment_settings, rootKey) | ||||
|         val ctx = requireContext() | ||||
|  | ||||
|         // Select Light/Dark/System Mode | ||||
|         findPreference<Preference>(getString(R.string.key_theme))?.setOnPreferenceChangeListener { _, key -> | ||||
|             setTheme(ctx, key as String) | ||||
|         } | ||||
|  | ||||
|         // Select map projection | ||||
|         findPreference<Preference>(getString(R.string.key_projection))?.setOnPreferenceChangeListener { _, key -> | ||||
|             Settings.refreshProjection() | ||||
|         } | ||||
|  | ||||
|         // Toggle groups | ||||
|         findPreference<Preference>(getString(R.string.key_group))?.setOnPreferenceChangeListener { _, key -> | ||||
|             if (key as String == ctx.getString(R.string.off)) { | ||||
|                 val fragment = EditPlaceColorFragment(this, true) | ||||
|                 fragment.show( | ||||
|                     this.parentFragmentManager, | ||||
|                     "AddColorDialogFragment" | ||||
|                 ) | ||||
|                 false | ||||
|             } else { | ||||
|                 true | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Toggle regional geolocs | ||||
|         findPreference<Preference>(getString(R.string.key_regional))?.setOnPreferenceChangeListener { _, key -> | ||||
|             when (key as String) { | ||||
|                 ctx.getString(R.string.off) -> { | ||||
|                     MaterialAlertDialogBuilder(requireActivity()) | ||||
|                         .setMessage(R.string.delete_regions) | ||||
|                         .setPositiveButton(android.R.string.ok) { _, _ -> | ||||
|                             GeoLocImporter.clearStates() | ||||
|                             PreferenceManager.getDefaultSharedPreferences(ctx).edit().putString( | ||||
|                                 ctx.getString(R.string.key_regional), | ||||
|                                 ctx.getString(R.string.off) | ||||
|                             ).apply() | ||||
|                             refreshPreferences() | ||||
|                         } | ||||
|                         .setNegativeButton(android.R.string.cancel) { _, _ -> } | ||||
|                         .show() | ||||
|                     false | ||||
|                 } | ||||
|  | ||||
|                 ctx.getString(R.string.on) -> { | ||||
|                     GeoLocImporter.importStates(ctx, true) | ||||
|                     true | ||||
|                 } | ||||
|  | ||||
|                 else -> false | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // Open license fragment | ||||
|         findPreference<Preference>(getString(R.string.licenses))?.setOnPreferenceClickListener { | ||||
|             requireActivity().supportFragmentManager.beginTransaction() | ||||
|                 .replace(R.id.fragment_view, LicenseFragment(), getString(R.string.licenses)) | ||||
|                 .commit() | ||||
|             true | ||||
|         } | ||||
|  | ||||
|         // Open about fragment | ||||
|         findPreference<Preference>(getString(R.string.about))?.setOnPreferenceClickListener { | ||||
|             requireActivity().supportFragmentManager.beginTransaction() | ||||
|                 .replace(R.id.fragment_view, AboutFragment(), getString(R.string.about)) | ||||
|                 .commit() | ||||
|             true | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|         fun setTheme(ctx: Context, key: String?): Boolean { | ||||
|             AppCompatDelegate.setDefaultNightMode( | ||||
|                 when (key) { | ||||
|                     ctx.getString(R.string.system) -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM | ||||
|                     ctx.getString(R.string.light) -> AppCompatDelegate.MODE_NIGHT_NO | ||||
|                     ctx.getString(R.string.dark) -> AppCompatDelegate.MODE_NIGHT_YES | ||||
|                     else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM | ||||
|                 } | ||||
|             ) | ||||
|             return true | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onDialogDismiss(clear: Boolean) { | ||||
|         // When turning groups off, select one group to keep and reassign everything | ||||
|         Data.selected_group?.let { selectedGroup -> | ||||
|             // Reassign all visited that are not to selectedGroup to selectedGroup | ||||
|             Data.visits.reassignAllVisitedToGroup(selectedGroup.key) | ||||
|  | ||||
|             // Delete all groups that are not selectedGroup | ||||
|             Data.groups.deleteAllExcept(selectedGroup.key) | ||||
|  | ||||
|             // Save and clear global variables | ||||
|             Data.saveData() | ||||
|             Data.selected_geoloc = null | ||||
|             Data.selected_group = null | ||||
|  | ||||
|             // Actually change preference | ||||
|             val ctx = requireContext() | ||||
|             val sp = PreferenceManager.getDefaultSharedPreferences(ctx) | ||||
|             sp.edit().putString(ctx.getString(R.string.key_group), ctx.getString(R.string.off)) | ||||
|                 .apply() | ||||
|  | ||||
|             // Refresh entire preference fragment to reflect changes | ||||
|             refreshPreferences() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun refreshPreferences() { | ||||
|         preferenceScreen.removeAll() | ||||
|         onCreatePreferences(savedInstanceState, rootKey) | ||||
|     } | ||||
| } | ||||
| @@ -1,78 +0,0 @@ | ||||
| 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) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @@ -1,222 +0,0 @@ | ||||
| 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, | ||||
|         ) | ||||
|     ) | ||||
| } | ||||
| @@ -1,193 +0,0 @@ | ||||
| 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) | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|     ) | ||||
| } | ||||
| @@ -1,218 +0,0 @@ | ||||
| package net.helcel.beans.activity.sub | ||||
|  | ||||
|  | ||||
| import androidx.activity.compose.BackHandler | ||||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.clickable | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.Spacer | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.height | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.layout.size | ||||
| import androidx.compose.foundation.lazy.LazyColumn | ||||
| import androidx.compose.foundation.lazy.items | ||||
| import androidx.compose.material.CheckboxDefaults | ||||
| import androidx.compose.material.MaterialTheme | ||||
| import androidx.compose.material.Tab | ||||
| import androidx.compose.material.TabRow | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.material.TriStateCheckbox | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.LaunchedEffect | ||||
| import androidx.compose.runtime.SideEffect | ||||
| import androidx.compose.runtime.collectAsState | ||||
| import androidx.compose.runtime.derivedStateOf | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.mutableIntStateOf | ||||
| import androidx.compose.runtime.mutableStateListOf | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.runtime.snapshots.SnapshotStateList | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import androidx.compose.ui.state.ToggleableState | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import androidx.compose.ui.unit.dp | ||||
| import net.helcel.beans.countries.GeoLoc | ||||
| import net.helcel.beans.countries.Group | ||||
| import net.helcel.beans.countries.World | ||||
| import net.helcel.beans.helper.AUTO_GROUP | ||||
| import net.helcel.beans.helper.Data | ||||
| import net.helcel.beans.helper.NO_GROUP | ||||
| import net.helcel.beans.helper.Settings | ||||
| import kotlin.math.min | ||||
|  | ||||
| @Preview | ||||
| @Composable | ||||
| fun EditPlaceScreenPreview(){ | ||||
|     EditPlaceScreen(Group.EEE) | ||||
| } | ||||
|  | ||||
| fun syncVisited(loc: GeoLoc?=World.WWW){ | ||||
|     loc?.children?.forEach { tt -> | ||||
|         tt.children.forEach {itc-> | ||||
|             if(Data.visits.getVisited(itc) in listOf(AUTO_GROUP,NO_GROUP)) { | ||||
|                 if(itc.children.any { itcc -> Data.visits.getVisited(itcc) != NO_GROUP }) | ||||
|                     Data.visits.setVisited(itc, AUTO_GROUP) | ||||
|                 else | ||||
|                     Data.visits.setVisited(itc, NO_GROUP) | ||||
|             } | ||||
|         } | ||||
|         if(Data.visits.getVisited(tt) in listOf(AUTO_GROUP,NO_GROUP)) { | ||||
|             if(tt.children.any { itc -> Data.visits.getVisited(itc) != NO_GROUP }) | ||||
|                 Data.visits.setVisited(tt, AUTO_GROUP) | ||||
|             else | ||||
|                 Data.visits.setVisited(tt, NO_GROUP) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| fun EditPlaceScreen(loc: GeoLoc, onExit:()->Unit={}) { | ||||
|     var showEdit by remember { mutableStateOf(false) } | ||||
|     val tabs : SnapshotStateList<GeoLoc> = remember { mutableStateListOf(loc) } | ||||
|     val ctx = LocalContext.current | ||||
|     var selectedTab by remember { mutableIntStateOf(0) } | ||||
|  | ||||
|     LaunchedEffect(tabs.size) { | ||||
|         selectedTab = tabs.lastIndex | ||||
|     } | ||||
|     SideEffect { | ||||
|         syncVisited() | ||||
|     } | ||||
|     BackHandler { | ||||
|         if (tabs.size > 1) tabs.removeAt(tabs.lastIndex) | ||||
|         else onExit() | ||||
|     } | ||||
|     if(showEdit) | ||||
|         EditPlaceDialog(false) { | ||||
|             showEdit = false | ||||
|             if (it) { | ||||
|                 Data.visits.setVisited(Data.selected_geoloc, NO_GROUP) | ||||
|                 Data.saveData() | ||||
|  | ||||
|                 if (Data.selected_geoloc!=null && Data.selected_geoloc!!.children.any { itc-> Data.visits.getVisited(itc) != NO_GROUP }) { | ||||
|                     Data.clearing_geoloc = Data.selected_geoloc | ||||
|                 } | ||||
|             } | ||||
|             if (Data.selected_group != null && Data.selected_geoloc != null) { | ||||
|                 Data.visits.setVisited(Data.selected_geoloc, Data.selected_group!!.key) | ||||
|                 Data.saveData() | ||||
|             } | ||||
|             Data.selected_geoloc = null | ||||
|             Data.selected_group = null | ||||
|         } | ||||
|  | ||||
|     Column { | ||||
|         val currentTab = tabs.getOrNull(selectedTab) ?: return@Column | ||||
|             TabRow( | ||||
|                 selectedTabIndex = min(tabs.lastIndex, selectedTab), | ||||
|             ) { | ||||
|                 tabs.forEachIndexed { index, tab -> | ||||
|                     Tab( | ||||
|                         selected = selectedTab == index, | ||||
|                         onClick = { | ||||
|                             while (tabs.size > index + 1) | ||||
|                                 tabs.removeAt(tabs.lastIndex) | ||||
|                         }, | ||||
|                         text = { Text(tab.fullName) } | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|         LazyColumn( | ||||
|             modifier = Modifier.fillMaxSize() | ||||
|         ) { | ||||
|             items(currentTab.children.toList(), key= {it.code}) { loc -> | ||||
|  | ||||
|                 GeoLocRow(loc, { | ||||
|                         if (loc.children.isNotEmpty()){ | ||||
|                             tabs.add(loc) | ||||
|                         } | ||||
|                     }, { | ||||
|                         Data.selected_geoloc = loc | ||||
|                         if (Data.groups.size() == 1 && Settings.isSingleGroup(ctx)) { | ||||
|                             Data.visits.setVisited(Data.selected_geoloc, | ||||
|                                 if (it != ToggleableState.On) Data.groups.getUniqueEntry()!!.key | ||||
|                                 else if(Data.selected_geoloc?.children?.any{ itc-> | ||||
|                                         Data.visits.getVisited(itc)!= NO_GROUP } == true) AUTO_GROUP | ||||
|                                 else NO_GROUP | ||||
|                             ) | ||||
|                             Data.selected_group = null | ||||
|                         } else { | ||||
|                             Data.selected_group = null | ||||
|                             showEdit=true | ||||
|                         } | ||||
|  | ||||
|                     }) | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| @Composable | ||||
| fun GeoLocRow( | ||||
|     loc: GeoLoc, | ||||
|     onClick: () -> Unit, | ||||
|     onCheckedChange: (ToggleableState) -> Unit | ||||
| ) { | ||||
|     val visits by Data.visits.visitsFlow.collectAsState() | ||||
|     val checked by remember(visits, loc) { | ||||
|         derivedStateOf { | ||||
|             when (visits.getOrElse(loc.code) { NO_GROUP }) { | ||||
|                 NO_GROUP -> ToggleableState.Off | ||||
|                 AUTO_GROUP -> ToggleableState.Indeterminate | ||||
|                 else -> ToggleableState.On | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     val color = if (Data.visits.getVisited(loc) !in listOf(NO_GROUP, AUTO_GROUP)) | ||||
|         Color(Data.groups.getGroupFromKey(Data.visits.getVisited(loc)).color.color) | ||||
|     else MaterialTheme.colors.onBackground | ||||
|     Row( | ||||
|         modifier = Modifier | ||||
|             .fillMaxWidth() | ||||
|             .height(50.dp) | ||||
|             .clickable(onClick = onClick) // whole row clickable | ||||
|             .padding(horizontal = 20.dp, vertical = 4.dp), | ||||
|         verticalAlignment = Alignment.CenterVertically | ||||
|     ) { | ||||
|         Text( | ||||
|             text = loc.fullName, | ||||
|             style = MaterialTheme.typography.body2, | ||||
|             color = MaterialTheme.colors.onBackground, | ||||
|             modifier = Modifier.weight(1f) | ||||
|         ) | ||||
|  | ||||
|         Text( | ||||
|             text = "",//loc.children.size.toString(), | ||||
|             style = MaterialTheme.typography.body2, | ||||
|             modifier = Modifier.padding(end = 16.dp) | ||||
|         ) | ||||
|  | ||||
|         TriStateCheckbox( | ||||
|             state = checked, | ||||
|             onClick= { onCheckedChange(checked) }, | ||||
|             colors = CheckboxDefaults.colors( | ||||
|                 checkedColor = color, | ||||
|             ), | ||||
|  | ||||
|             modifier = Modifier.size(24.dp) | ||||
|         ) | ||||
|     } | ||||
|     Spacer(modifier = Modifier | ||||
|         .height(2.dp) | ||||
|         .fillMaxWidth() | ||||
|         .background(MaterialTheme.colors.onBackground)) | ||||
| } | ||||
| @@ -1,43 +0,0 @@ | ||||
| package net.helcel.beans.activity.sub | ||||
|  | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.material.MaterialTheme | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import com.mikepenz.aboutlibraries.ui.compose.DefaultChipColors | ||||
| import com.mikepenz.aboutlibraries.ui.compose.DefaultLibraryColors | ||||
| import com.mikepenz.aboutlibraries.ui.compose.android.rememberLibraries | ||||
| import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer | ||||
| import net.helcel.beans.R | ||||
| import net.helcel.beans.activity.SysTheme | ||||
|  | ||||
|  | ||||
| @Preview | ||||
| @Composable | ||||
| fun LicenseScreen() { | ||||
|     val libraries = rememberLibraries(R.raw.aboutlibraries) | ||||
|     SysTheme { | ||||
|         LibrariesContainer( | ||||
|             libraries = libraries.value, | ||||
|             modifier = Modifier.fillMaxSize(), | ||||
|             colors = DefaultLibraryColors( | ||||
|                 backgroundColor = MaterialTheme.colors.background, | ||||
|                 contentColor = MaterialTheme.colors.onBackground, | ||||
|                 licenseChipColors = DefaultChipColors( | ||||
|                     containerColor = MaterialTheme.colors.primary, | ||||
|                     contentColor = MaterialTheme.colors.onPrimary, | ||||
|                 ), | ||||
|                 versionChipColors = DefaultChipColors( | ||||
|                     containerColor = MaterialTheme.colors.secondary, | ||||
|                     contentColor = MaterialTheme.colors.onSecondary, | ||||
|                 ), | ||||
|                 fundingChipColors = DefaultChipColors( | ||||
|                     containerColor = MaterialTheme.colors.secondary, | ||||
|                     contentColor = MaterialTheme.colors.onSecondary, | ||||
|                 ), | ||||
|                 dialogConfirmButtonColor = MaterialTheme.colors.primary, | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @@ -7,8 +7,8 @@ enum class Country( | ||||
|     ATA("Antarctica", 14000000), | ||||
|  | ||||
|     //    HKG("Hong Kong", 1104), | ||||
|     //    MAC("Macao", 32), | ||||
|     //    ANT("Netherlands Antilles", 800), | ||||
| //    MAC("Macao", 32), | ||||
| //    ANT("Netherlands Antilles", 800), | ||||
|     AFG("Afghanistan", 645487), | ||||
|     XAD("Akrotiri and Dhekelia", 234), | ||||
|     ALA("Åland", 1483), | ||||
|   | ||||
| @@ -1,21 +1,13 @@ | ||||
| package net.helcel.beans.helper | ||||
|  | ||||
| import android.content.ContentValues | ||||
| import android.content.Context | ||||
| import android.content.SharedPreferences | ||||
| import android.net.Uri | ||||
| import android.os.Build | ||||
| import android.os.Environment | ||||
| import android.provider.MediaStore | ||||
| import android.graphics.drawable.ColorDrawable | ||||
| 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()) | ||||
| @@ -41,8 +33,7 @@ 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", | ||||
|                 ContextCompat.getColor(ctx, R.color.blue).toDrawable()) | ||||
|             groups.setGroup(DEFAULT_GROUP, "Visited", ColorDrawable(ContextCompat.getColor(ctx, R.color.blue))) | ||||
|             saveData() | ||||
|         } | ||||
|     } | ||||
| @@ -50,73 +41,9 @@ object Data { | ||||
|     fun saveData() { | ||||
|         if(groups.id != visits.id) return | ||||
|         val id = groups.id | ||||
|         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) | ||||
|         val editor = sharedPreferences.edit() | ||||
|         editor.putString("groups_$id", groupsSerial.writeTo(groups)) | ||||
|         editor.putString("visits_$id", visitsSerial.writeTo(visits)) | ||||
|         editor.apply() | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,5 @@ | ||||
| package net.helcel.beans.helper | ||||
|  | ||||
| interface DialogCloser { | ||||
|     fun onDialogDismiss(clear: Boolean) | ||||
| } | ||||
| @@ -2,16 +2,15 @@ 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 | ||||
| @@ -21,23 +20,20 @@ const val NO_GROUP = 0 | ||||
| const val DEFAULT_GROUP = 1 | ||||
| const val AUTO_GROUP = -1 | ||||
|  | ||||
|  | ||||
|  | ||||
| @Serializable | ||||
| class Groups(val id: Int, private val grps: HashMap<Int, Group>) { | ||||
|     @kotlinx.serialization.Transient | ||||
|     private val _groupsFlow = MutableStateFlow<List<Group>>(grps.values.toList()) | ||||
|     @kotlinx.serialization.Transient | ||||
|     val groupsFlow: StateFlow<List<Group>> = _groupsFlow.asStateFlow() | ||||
|  | ||||
|     fun setGroup(key: Int, name: String, col: ColorDrawable) { | ||||
|         grps[key] = Group(key, name, col) | ||||
|         _groupsFlow.value = grps.values.toList() | ||||
|     } | ||||
|  | ||||
|     fun deleteGroup(key: Int) { | ||||
|         grps.remove(key) | ||||
|         _groupsFlow.value = grps.values.toList() | ||||
|     } | ||||
|  | ||||
|     fun deleteAllExcept(grp: Int) { | ||||
|         val keysToDelete = grps.keys.filter { it != grp } | ||||
|         keysToDelete.forEach { grps.remove(it) } | ||||
|     } | ||||
|  | ||||
|     fun getGroupFromKey(key: Int): Group { | ||||
| @@ -64,7 +60,6 @@ class Groups(val id: Int, private val grps: HashMap<Int, Group>) { | ||||
|     } | ||||
|  | ||||
|     fun getGroupFromPos(pos: Int): Pair<Int, Group> { | ||||
|         if(grps.keys.isEmpty()) return Pair(NO_GROUP,Group(NO_GROUP,"-")) | ||||
|         val key = grps.keys.toList()[pos] | ||||
|         return Pair(key, getGroupFromKey(key)) | ||||
|     } | ||||
| @@ -79,7 +74,9 @@ class Groups(val id: Int, private val grps: HashMap<Int, Group>) { | ||||
|     open class Group( | ||||
|         val key: Int, | ||||
|         val name: String, | ||||
|         @Serializable(with = Theme.ColorDrawableSerializer::class) val color: ColorDrawable = Color.GRAY.toDrawable() | ||||
|         @Serializable(with = Theme.ColorDrawableSerializer::class) val color: ColorDrawable = ColorDrawable( | ||||
|             Color.GRAY | ||||
|         ) | ||||
|     ) | ||||
|  | ||||
|     @OptIn(ExperimentalSerializationApi::class) | ||||
|   | ||||
| @@ -4,15 +4,19 @@ import android.content.Context | ||||
| import android.content.SharedPreferences | ||||
| import androidx.preference.PreferenceManager | ||||
| import net.helcel.beans.R | ||||
| import net.helcel.beans.activity.MainScreen | ||||
| import net.helcel.beans.activity.MainActivity | ||||
| import net.helcel.beans.activity.fragment.SettingsFragment | ||||
|  | ||||
| object Settings { | ||||
|  | ||||
|     private lateinit var sp: SharedPreferences | ||||
|     private lateinit var mainActivity: MainScreen | ||||
|     fun start(ctx: MainScreen) { | ||||
|     private lateinit var mainActivity: MainActivity | ||||
|     fun start(ctx: MainActivity) { | ||||
|         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 { | ||||
| @@ -37,7 +41,7 @@ object Settings { | ||||
|     } | ||||
|  | ||||
|     fun refreshProjection(): Boolean { | ||||
|         (mainActivity).refreshProjection() | ||||
|         mainActivity.refreshProjection() | ||||
|         return true | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,16 +1,23 @@ | ||||
| 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) | ||||
| @@ -21,6 +28,11 @@ 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) | ||||
| @@ -31,7 +43,7 @@ object Theme { | ||||
|         override val descriptor = PrimitiveSerialDescriptor("ColorDrawable", PrimitiveKind.INT) | ||||
|  | ||||
|         override fun deserialize(decoder: Decoder): ColorDrawable { | ||||
|             return decoder.decodeInt().toDrawable() | ||||
|             return ColorDrawable(decoder.decodeInt()) | ||||
|         } | ||||
|  | ||||
|         override fun serialize(encoder: Encoder, value: ColorDrawable) { | ||||
|   | ||||
| @@ -1,7 +1,5 @@ | ||||
| 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 | ||||
| @@ -13,17 +11,9 @@ import java.io.InputStream | ||||
| @Serializable | ||||
| class Visits(val id: Int, private val locs: HashMap<String, Int>) { | ||||
|  | ||||
|     @kotlinx.serialization.Transient | ||||
|     private val _visitsFlow = MutableStateFlow<Map<String,Int>>(locs.toMutableMap()) | ||||
|     @kotlinx.serialization.Transient | ||||
|     val visitsFlow: StateFlow<Map<String,Int>> = _visitsFlow | ||||
|  | ||||
|     fun setVisited(key: GeoLoc?, b: Int) { | ||||
|         if (key == null) | ||||
|             return | ||||
|         _visitsFlow.value = _visitsFlow.value.toMutableMap().apply { | ||||
|             this[key.code] = b | ||||
|         } | ||||
|         locs[key.code] = b | ||||
|     } | ||||
|  | ||||
| @@ -31,9 +21,6 @@ class Visits(val id: Int, private val locs: HashMap<String, Int>) { | ||||
|         val keysToDelete = locs | ||||
|             .filter { it.value == key } | ||||
|             .map { it.key } | ||||
|         _visitsFlow.value = _visitsFlow.value.toMutableMap().apply { | ||||
|             keysToDelete.forEach { this.remove(it)} | ||||
|         } | ||||
|         keysToDelete.forEach { | ||||
|             locs.remove(it) | ||||
|         } | ||||
| @@ -66,7 +53,6 @@ class Visits(val id: Int, private val locs: HashMap<String, Int>) { | ||||
|         keys.forEach { | ||||
|             locs[it] = group | ||||
|         } | ||||
|         _visitsFlow.value = locs | ||||
|     } | ||||
|  | ||||
|     @OptIn(ExperimentalSerializationApi::class) | ||||
|   | ||||
| @@ -1,10 +1,6 @@ | ||||
| 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 | ||||
| @@ -12,10 +8,15 @@ 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" } | ||||
| @@ -23,22 +24,18 @@ class CSSWrapper(private val ctx: Context) { | ||||
|     private val regional: String = World.WWW.children.joinToString(",") { itt -> | ||||
|         itt.children.joinToString(",") { "#${it.code}1" } | ||||
|     } | ||||
|  | ||||
|     @Composable | ||||
|     fun getBaseColors() : Pair<String, String> { | ||||
|         val colorForeground = colorToHex6(MaterialTheme.colors.onBackground.toArgb().toDrawable()) | ||||
|         val colorBackground = colorToHex6(MaterialTheme.colors.background.toArgb().toDrawable()) | ||||
|  | ||||
|         return Pair(colorForeground, colorBackground) | ||||
|     } | ||||
|  | ||||
|     private 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;}" | ||||
|     private var customCSS: String = "" | ||||
|  | ||||
|     init { | ||||
|         refresh() | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private fun refresh() { | ||||
|         val id = if (Settings.isRegional(ctx)) "1" else "2" | ||||
|         customCSS = visits.getVisitedByValue().map { (k, v) -> | ||||
| @@ -50,24 +47,20 @@ class CSSWrapper(private val ctx: Context) { | ||||
|                 emptyList() | ||||
|             }).takeIf { it.isNotEmpty() } | ||||
|                 ?.joinToString(",") { "#${it}$id,#${it}" } + "{fill:${ | ||||
|                 if (k == AUTO_GROUP) colorToHex6(groups.getGroupFromPos(0).second.color)  | ||||
|                 else colorToHex6(groups.getGroupFromKey(k).color) | ||||
|                 colorToHex6( | ||||
|                     if (k == AUTO_GROUP) | ||||
|                         colorWrapper(ctx, android.R.attr.colorPrimary) | ||||
|                     else 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 | ||||
|         } | ||||
|     } | ||||
|   | ||||
							
								
								
									
										13
									
								
								app/src/main/res/drawable/about.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app/src/main/res/drawable/about.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| android:width="24dp" | ||||
| android:height="24dp" | ||||
| android:viewportWidth="960" | ||||
| android:viewportHeight="960"> | ||||
| <path | ||||
|     android:fillColor="?attr/colorOnBackground" | ||||
|     android:pathData="M80,160Q80,127 103.5,103.5Q127,80 160,80L800,80Q833,80 856.5,103.5Q880,127 880,160L880,640Q880,673 856.5,696.5Q833,720 800,720L240,720L80,880ZM206,640L800,640Q800,640 800,640Q800,640 800,640L800,160Q800,160 800,160Q800,160 800,160L160,160Q160,160 160,160Q160,160 160,160L160,685L206,640ZM160,640L160,640L160,160Q160,160 160,160Q160,160 160,160L160,160Q160,160 160,160Q160,160 160,160L160,640Q160,640 160,640Q160,640 160,640L160,640Z" /> | ||||
|  | ||||
| <path | ||||
|     android:fillColor="?attr/colorPrimary" | ||||
|     android:pathData="M480,280Q497,280 508.5,268.5Q520,257 520,240Q520,223 508.5,211.5Q497,200 480,200Q463,200 451.5,211.5Q440,223 440,240Q440,257 451.5,268.5Q463,280 480,280ZM440,600L520,600L520,360L440,360L440,600ZM80,880L80,160" /> | ||||
| </vector> | ||||
							
								
								
									
										9
									
								
								app/src/main/res/drawable/add.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/src/main/res/drawable/add.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| android:width="24dp" | ||||
| android:height="24dp" | ||||
| android:viewportWidth="24" | ||||
| android:viewportHeight="24"> | ||||
| <path | ||||
|     android:fillColor="?attr/colorOnBackground" | ||||
|     android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" /> | ||||
| </vector> | ||||
							
								
								
									
										5
									
								
								app/src/main/res/drawable/back.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/res/drawable/back.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/> | ||||
|      | ||||
| </vector> | ||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable/color.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/drawable/color.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| <vector | ||||
|     android:height="24dp" | ||||
|     android:viewportHeight="960" | ||||
|     android:viewportWidth="960" | ||||
|     android:width="24dp" | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
| <path | ||||
|     android:fillColor="@color/white" | ||||
|     android:pathData="M346,820L100,574Q90,564 85,552Q80,540 80,527Q80,514 85,502Q90,490 100,480L330,251L224,145L286,80L686,480Q696,490 700.5,502Q705,514 705,527Q705,540 700.5,552Q696,564 686,574L440,820Q430,830 418,835Q406,840 393,840Q380,840 368,835Q356,830 346,820ZM393,314L179,528Q179,528 179,528Q179,528 179,528L607,528Q607,528 607,528Q607,528 607,528L393,314ZM792,840Q756,840 731,814.5Q706,789 706,752Q706,725 719.5,701Q733,677 750,654L792,600L836,654Q852,677 866,701Q880,725 880,752Q880,789 854,814.5Q828,840 792,840Z"/> | ||||
| </vector> | ||||
							
								
								
									
										41
									
								
								app/src/main/res/drawable/delete.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								app/src/main/res/drawable/delete.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:width="72dp" | ||||
|     android:height="72dp" | ||||
|     android:viewportWidth="72" | ||||
|     android:viewportHeight="72"> | ||||
|   <path | ||||
|       android:pathData="M31,16l0,-4l10,0l0,4" | ||||
|       android:strokeLineJoin="round" | ||||
|       android:strokeWidth="4" | ||||
|       android:fillColor="#00000000" | ||||
|       android:strokeColor="?attr/colorOnBackground" | ||||
|       android:strokeLineCap="round"/> | ||||
|   <path | ||||
|       android:pathData="M51,25v31c0,2.209 -1.791,4 -4,4H25c-2.209,0 -4,-1.791 -4,-4V25" | ||||
|       android:strokeLineJoin="round" | ||||
|       android:strokeWidth="4" | ||||
|       android:fillColor="#00000000" | ||||
|       android:strokeColor="?attr/colorOnBackground" | ||||
|       android:strokeLineCap="round"/> | ||||
|   <path | ||||
|       android:pathData="M17,16h38v4h-38z" | ||||
|       android:strokeLineJoin="round" | ||||
|       android:strokeWidth="4" | ||||
|       android:fillColor="#00000000" | ||||
|       android:strokeColor="?attr/colorOnBackground" | ||||
|       android:strokeLineCap="round"/> | ||||
|   <path | ||||
|       android:pathData="M41,28.25L41,55" | ||||
|       android:strokeLineJoin="round" | ||||
|       android:strokeWidth="4" | ||||
|       android:fillColor="#00000000" | ||||
|       android:strokeColor="?attr/colorOnBackground" | ||||
|       android:strokeLineCap="round"/> | ||||
|   <path | ||||
|       android:pathData="M31,28.25L31,55" | ||||
|       android:strokeLineJoin="round" | ||||
|       android:strokeWidth="4" | ||||
|       android:fillColor="#00000000" | ||||
|       android:strokeColor="?attr/colorOnBackground" | ||||
|       android:strokeLineCap="round"/> | ||||
| </vector> | ||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable/edit.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/drawable/edit.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| <vector | ||||
|     android:height="24dp" | ||||
|     android:viewportHeight="24" | ||||
|     android:viewportWidth="24" | ||||
|     android:width="24dp" | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <path | ||||
|         android:fillColor="@color/white" | ||||
|         android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/> | ||||
| </vector> | ||||
							
								
								
									
										16
									
								
								app/src/main/res/drawable/group.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/src/main/res/drawable/group.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <vector | ||||
| android:height="24dp" | ||||
| android:viewportHeight="24" | ||||
| android:viewportWidth="24" | ||||
| android:width="24dp" | ||||
| xmlns:android="http://schemas.android.com/apk/res/android" > | ||||
|  | ||||
| <path android:fillColor="?attr/colorOnBackground" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/> | ||||
|  | ||||
| <path android:fillColor="?attr/colorPrimary" android:pathData="M8,14m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/> | ||||
|  | ||||
| <path android:fillColor="?attr/colorPrimary" android:pathData="M12,8m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/> | ||||
|  | ||||
| <path android:fillColor="?attr/colorPrimary" android:pathData="M16,14m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/> | ||||
|  | ||||
| </vector> | ||||
							
								
								
									
										18
									
								
								app/src/main/res/drawable/licenses.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								app/src/main/res/drawable/licenses.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| android:width="24dp" | ||||
| android:height="24dp" | ||||
| android:viewportWidth="24.0" | ||||
| android:viewportHeight="24.0"> | ||||
|  | ||||
| <path | ||||
|     android:fillColor="?attr/colorOnBackground" | ||||
|     android:pathData="M22,7h-9v2h9V7zM22,15h-9v2h9V15z"/> | ||||
|  | ||||
| <path | ||||
|     android:fillColor="?attr/colorPrimary" | ||||
|     android:pathData="M5.54,11L2,7.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,11z"/> | ||||
|  | ||||
| <path | ||||
|     android:fillColor="?attr/colorPrimary" | ||||
|     android:pathData="M5.54,19L2,15.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,19z"/> | ||||
| </vector> | ||||
							
								
								
									
										16
									
								
								app/src/main/res/drawable/map.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/src/main/res/drawable/map.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <vector | ||||
|     android:height="24dp" | ||||
|     android:viewportHeight="24" | ||||
|     android:viewportWidth="24" | ||||
|     android:width="24dp" | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" > | ||||
|  | ||||
|     <path | ||||
|         android:fillColor="?attr/colorOnBackground" | ||||
|         android:pathData="M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48L3,20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48L21,3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM10,5.47l4,1.4v11.66l-4,-1.4L10,5.47zM5,6.46l3,-1.01v11.7l-3,1.16L5,6.46zM19,17.54l-3,1.01L16,6.86l3,-1.16v11.84z" /> | ||||
|  | ||||
|     <path | ||||
|         android:fillColor="?attr/colorPrimary" | ||||
|         android:pathData="M15,18.89l-6,-2.11L9,5.11l6,2.11v11.67z"/> | ||||
|  | ||||
| </vector> | ||||
							
								
								
									
										14
									
								
								app/src/main/res/drawable/palette.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/src/main/res/drawable/palette.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| <vector | ||||
|     android:height="24dp" | ||||
|     android:viewportHeight="24" | ||||
|     android:viewportWidth="24" | ||||
|     android:width="24dp" | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" > | ||||
|  | ||||
|     <path | ||||
|         android:fillColor="?attr/colorOnBackground" | ||||
|         android:pathData="M12,22C6.49,22 2,17.51 2,12S6.49,2 12,2s10,4.04 10,9c0,3.31 -2.69,6 -6,6h-1.77c-0.28,0 -0.5,0.22 -0.5,0.5c0,0.12 0.05,0.23 0.13,0.33c0.41,0.47 0.64,1.06 0.64,1.67C14.5,20.88 13.38,22 12,22zM12,4c-4.41,0 -8,3.59 -8,8s3.59,8 8,8c0.28,0 0.5,-0.22 0.5,-0.5c0,-0.16 -0.08,-0.28 -0.14,-0.35c-0.41,-0.46 -0.63,-1.05 -0.63,-1.65c0,-1.38 1.12,-2.5 2.5,-2.5H16c2.21,0 4,-1.79 4,-4C20,7.14 16.41,4 12,4z" /> | ||||
|     <path | ||||
|         android:fillColor="?attr/colorPrimary" | ||||
|         android:pathData="M17.5,13c-0.83,0 -1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5C19,12.33 18.33,13 17.5,13zM14.5,9C13.67,9 13,8.33 13,7.5C13,6.67 13.67,6 14.5,6S16,6.67 16,7.5C16,8.33 15.33,9 14.5,9zM5,11.5C5,10.67 5.67,10 6.5,10S8,10.67 8,11.5C8,12.33 7.33,13 6.5,13S5,12.33 5,11.5zM11,7.5C11,8.33 10.33,9 9.5,9S8,8.33 8,7.5C8,6.67 8.67,6 9.5,6S11,6.67 11,7.5z" /> | ||||
| </vector> | ||||
							
								
								
									
										14
									
								
								app/src/main/res/drawable/stats.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/src/main/res/drawable/stats.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| <vector | ||||
| android:height="24dp" | ||||
| android:viewportHeight="960" | ||||
| android:viewportWidth="960" | ||||
| android:width="24dp" | ||||
| xmlns:android="http://schemas.android.com/apk/res/android" > | ||||
|  | ||||
| <path | ||||
|     android:fillColor="?attr/colorOnBackground" | ||||
|     android:pathData="M105,727L40,680L240,360L360,500L520,240L629,403Q606,404 585.5,408.5Q565,413 545,421L523,388L371,635L250,494L105,727ZM732,423Q713,415 692.5,410Q672,405 650,404L855,80L920,127L732,423Z" /> | ||||
| <path | ||||
|     android:fillColor="?attr/colorPrimary" | ||||
|     android:pathData="M863,920L738,795Q718,809 693.5,816Q669,823 643,823Q568,823 515.5,770.5Q463,718 463,643Q463,568 515.5,515.5Q568,463 643,463Q718,463 770.5,515.5Q823,568 823,643Q823,669 816,693.5Q809,718 795,739L920,863L863,920ZM643,743Q685,743 714,714Q743,685 743,643Q743,601 714,572Q685,543 643,543Q601,543 572,572Q543,601 543,643Q543,685 572,714Q601,743 643,743Z" /> | ||||
| </vector> | ||||
							
								
								
									
										12
									
								
								app/src/main/res/drawable/zoomin.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/src/main/res/drawable/zoomin.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| <vector | ||||
| android:height="24dp" | ||||
| android:viewportHeight="24" | ||||
| android:viewportWidth="24" | ||||
| android:width="24dp" | ||||
| xmlns:android="http://schemas.android.com/apk/res/android" > | ||||
|  | ||||
| <path android:fillColor="?attr/colorOnBackground" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/> | ||||
|  | ||||
| <path android:fillColor="?attr/colorPrimary" android:pathData="M12,10h-2v2H9v-2H7V9h2V7h1v2h2v1z"/> | ||||
|  | ||||
| </vector> | ||||
							
								
								
									
										21
									
								
								app/src/main/res/layout/activity_edit.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								app/src/main/res/layout/activity_edit.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     android:orientation="vertical" | ||||
|     android:theme="@style/Theme.Beans" | ||||
|     tools:context=".activity.EditActivity"> | ||||
|  | ||||
|     <com.google.android.material.tabs.TabLayout | ||||
|         android:id="@+id/tab" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" /> | ||||
|  | ||||
|     <androidx.viewpager2.widget.ViewPager2 | ||||
|         android:id="@+id/pager" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="0dp" | ||||
|         android:layout_weight="1" /> | ||||
|  | ||||
| </LinearLayout> | ||||
							
								
								
									
										14
									
								
								app/src/main/res/layout/activity_main.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/src/main/res/layout/activity_main.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     android:orientation="vertical" | ||||
|     android:theme="@style/Theme.Beans" | ||||
|     tools:context=".activity.MainActivity"> | ||||
|      | ||||
|     <com.github.chrisbanes.photoview.PhotoView | ||||
|         android:id="@+id/photo_view" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" /> | ||||
| </LinearLayout> | ||||
							
								
								
									
										15
									
								
								app/src/main/res/layout/activity_settings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								app/src/main/res/layout/activity_settings.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:orientation="vertical" | ||||
|     android:theme="@style/Theme.Beans" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     tools:context=".activity.SettingsActivity"> | ||||
|  | ||||
| <androidx.fragment.app.FragmentContainerView | ||||
|     android:id="@+id/fragment_view" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" /> | ||||
|  | ||||
| </LinearLayout> | ||||
							
								
								
									
										66
									
								
								app/src/main/res/layout/activity_stat.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								app/src/main/res/layout/activity_stat.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     android:orientation="vertical" | ||||
|     android:theme="@style/Theme.Beans" | ||||
|     tools:context=".activity.StatsActivity"> | ||||
|  | ||||
|     <com.google.android.material.tabs.TabLayout | ||||
|         android:id="@+id/tab" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" /> | ||||
|  | ||||
|     <androidx.viewpager2.widget.ViewPager2 | ||||
|         android:id="@+id/pager" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="0dp" | ||||
|         android:layout_weight="1" | ||||
|         android:visibility="gone" /> | ||||
|  | ||||
|     <androidx.constraintlayout.widget.ConstraintLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:paddingBottom="10dp"> | ||||
|  | ||||
|         <com.google.android.material.button.MaterialButton | ||||
|             android:id="@+id/group_color" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_marginTop="2dp" | ||||
|             android:layout_marginBottom="2dp" | ||||
|             android:paddingStart="56dp" | ||||
|             android:text="@string/total" | ||||
|             android:textAlignment="textStart" | ||||
|             android:textColor="?attr/colorOnPrimary" | ||||
|             app:cornerRadius="0dp" | ||||
|             app:layout_constraintBottom_toBottomOf="parent" | ||||
|             app:layout_constraintEnd_toEndOf="parent" | ||||
|             app:layout_constraintStart_toStartOf="parent" | ||||
|             app:layout_constraintTop_toTopOf="parent" | ||||
|             tools:ignore="RtlSymmetry" /> | ||||
|  | ||||
|         <com.google.android.material.textview.MaterialTextView | ||||
|             android:id="@+id/name" | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="50dp" | ||||
|             android:gravity="start|center_vertical" | ||||
|             android:paddingStart="20dp" | ||||
|             android:paddingEnd="52dp" | ||||
|             android:text="" | ||||
|             android:textColor="?attr/colorOnPrimary" | ||||
|             app:layout_constraintBottom_toBottomOf="@id/group_color" | ||||
|             app:layout_constraintEnd_toEndOf="@id/group_color" | ||||
|             app:layout_constraintTop_toTopOf="@id/group_color" /> | ||||
|  | ||||
|     </androidx.constraintlayout.widget.ConstraintLayout> | ||||
|  | ||||
|  | ||||
|     <androidx.recyclerview.widget.RecyclerView | ||||
|         android:id="@+id/stats" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" /> | ||||
|  | ||||
| </LinearLayout> | ||||
							
								
								
									
										67
									
								
								app/src/main/res/layout/fragment_about.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								app/src/main/res/layout/fragment_about.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     tools:context=".activity.fragment.AboutFragment"> | ||||
|  | ||||
|     <LinearLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:gravity="center_horizontal" | ||||
|         android:orientation="vertical"> | ||||
|  | ||||
|         <com.google.android.material.imageview.ShapeableImageView | ||||
|             android:layout_width="300dp" | ||||
|             android:layout_height="300dp" | ||||
|             android:layout_marginTop="20dp" | ||||
|             android:contentDescription="@string/logo" | ||||
|             android:src="@drawable/ic_launcher_foreground" /> | ||||
|  | ||||
|         <com.google.android.material.textview.MaterialTextView | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_marginStart="10dp" | ||||
|             android:layout_marginTop="15dp" | ||||
|             android:layout_marginEnd="10dp" | ||||
|             android:layout_marginBottom="10dp" | ||||
|             android:text="@string/app_name" | ||||
|             android:textAlignment="center" | ||||
|             android:textSize="30sp" | ||||
|             android:textStyle="bold" /> | ||||
|  | ||||
|         <com.google.android.material.textview.MaterialTextView | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_marginStart="10dp" | ||||
|             android:layout_marginTop="0dp" | ||||
|             android:layout_marginEnd="10dp" | ||||
|             android:layout_marginBottom="15dp" | ||||
|             android:text="@string/app_version" | ||||
|             android:textAlignment="center" | ||||
|             android:textSize="25sp" /> | ||||
|  | ||||
|         <com.google.android.material.textview.MaterialTextView | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_marginStart="10dp" | ||||
|             android:layout_marginTop="15dp" | ||||
|             android:layout_marginEnd="10dp" | ||||
|             android:layout_marginBottom="15dp" | ||||
|             android:text="@string/beans_is_foss" | ||||
|             android:textAlignment="center" /> | ||||
|  | ||||
|         <com.google.android.material.textview.MaterialTextView | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_marginStart="10dp" | ||||
|             android:layout_marginTop="15dp" | ||||
|             android:layout_marginEnd="10dp" | ||||
|             android:layout_marginBottom="15dp" | ||||
|             android:autoLink="web" | ||||
|             android:text="@string/beans_repo" | ||||
|             android:textAlignment="center" /> | ||||
|  | ||||
|     </LinearLayout> | ||||
|  | ||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||
							
								
								
									
										137
									
								
								app/src/main/res/layout/fragment_edit_groups_add.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								app/src/main/res/layout/fragment_edit_groups_add.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:orientation="vertical" | ||||
|     android:padding="16dp" | ||||
|     tools:context=".activity.fragment.EditGroupAddFragment"> | ||||
|  | ||||
|  | ||||
|     <com.google.android.material.textfield.TextInputEditText | ||||
|         android:id="@+id/group_name" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="48dp" | ||||
|         android:autofillHints="" | ||||
|         android:hint="@string/name" | ||||
|         android:inputType="text" /> | ||||
|  | ||||
|     <androidx.constraintlayout.widget.ConstraintLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent"> | ||||
|  | ||||
|         <View | ||||
|             android:id="@+id/colorView" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="0dp" | ||||
|             android:background="@color/black" | ||||
|             app:layout_constraintBottom_toBottomOf="parent" | ||||
|             app:layout_constraintEnd_toStartOf="@+id/colorR" | ||||
|             app:layout_constraintStart_toStartOf="parent" | ||||
|             app:layout_constraintTop_toTopOf="parent" /> | ||||
|  | ||||
|  | ||||
|         <com.google.android.material.slider.Slider | ||||
|             android:id="@+id/colorR" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             app:layout_constraintBottom_toTopOf="@id/colorG" | ||||
|             app:layout_constraintEnd_toEndOf="parent" | ||||
|             app:layout_constraintStart_toEndOf="@id/colorView" | ||||
|             app:layout_constraintTop_toTopOf="parent" | ||||
|             app:thumbColor="@color/red" | ||||
|             app:trackColorActive="@color/red" /> | ||||
|  | ||||
|         <com.google.android.material.slider.Slider | ||||
|             android:id="@+id/colorG" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:foregroundTint="#FF0000" | ||||
|             app:layout_constraintBottom_toTopOf="@id/colorB" | ||||
|             app:layout_constraintEnd_toEndOf="parent" | ||||
|             app:layout_constraintStart_toEndOf="@id/colorView" | ||||
|             app:layout_constraintTop_toBottomOf="@id/colorR" | ||||
|             app:thumbColor="@color/green" | ||||
|             app:trackColorActive="@color/green" /> | ||||
|  | ||||
|         <com.google.android.material.slider.Slider | ||||
|             android:id="@+id/colorB" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             app:layout_constraintBottom_toBottomOf="parent" | ||||
|             app:layout_constraintEnd_toEndOf="parent" | ||||
|             app:layout_constraintStart_toEndOf="@id/colorView" | ||||
|             app:layout_constraintTop_toBottomOf="@id/colorG" | ||||
|             app:thumbColor="@color/blue" | ||||
|             app:trackColorActive="@color/blue" /> | ||||
|  | ||||
|     </androidx.constraintlayout.widget.ConstraintLayout> | ||||
|  | ||||
|     <LinearLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent"> | ||||
|  | ||||
|         <com.google.android.material.textview.MaterialTextView | ||||
|             android:id="@+id/textView" | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:labelFor="@id/group_color" | ||||
|             android:text="@string/hashtag" /> | ||||
|  | ||||
|         <com.google.android.material.textfield.TextInputEditText | ||||
|             android:id="@+id/group_color" | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="48dp" | ||||
|             android:autofillHints="" | ||||
|             android:hint="@string/color_rrggbb" | ||||
|             android:inputType="text" | ||||
|             android:maxLength="6" /> | ||||
|     </LinearLayout> | ||||
|  | ||||
|     <androidx.constraintlayout.widget.ConstraintLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:layout_marginTop="10dp"> | ||||
|  | ||||
|         <com.google.android.material.button.MaterialButton | ||||
|             android:id="@+id/btnDelete" | ||||
|             android:layout_width="52dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:paddingLeft="6dp" | ||||
|             android:paddingRight="6dp" | ||||
|             android:tooltipText="@string/delete" | ||||
|             app:icon="@drawable/delete" | ||||
|             app:iconGravity="textStart" | ||||
|             app:iconPadding="0dp" | ||||
|             app:layout_constraintBottom_toBottomOf="parent" | ||||
|             app:layout_constraintStart_toStartOf="parent" | ||||
|             app:layout_constraintTop_toTopOf="parent" /> | ||||
|  | ||||
|  | ||||
|         <com.google.android.material.button.MaterialButton | ||||
|             android:id="@+id/btnCancel" | ||||
|             android:layout_width="80dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_marginEnd="6dp" | ||||
|             android:paddingLeft="6dp" | ||||
|             android:paddingRight="6dp" | ||||
|             android:text="@string/cancel" | ||||
|             app:layout_constraintBottom_toBottomOf="parent" | ||||
|             app:layout_constraintEnd_toStartOf="@+id/btnOk" | ||||
|             app:layout_constraintTop_toTopOf="parent" /> | ||||
|  | ||||
|         <com.google.android.material.button.MaterialButton | ||||
|             android:id="@+id/btnOk" | ||||
|             android:layout_width="52dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:paddingLeft="6dp" | ||||
|             android:paddingRight="6dp" | ||||
|             android:text="@string/ok" | ||||
|             app:layout_constraintBottom_toBottomOf="parent" | ||||
|             app:layout_constraintEnd_toEndOf="parent" | ||||
|             app:layout_constraintTop_toTopOf="parent" /> | ||||
|  | ||||
|     </androidx.constraintlayout.widget.ConstraintLayout> | ||||
|  | ||||
| </LinearLayout> | ||||
							
								
								
									
										26
									
								
								app/src/main/res/layout/fragment_edit_places.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/src/main/res/layout/fragment_edit_places.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     tools:context=".activity.fragment.EditPlaceFragment"> | ||||
|  | ||||
|  | ||||
|     <androidx.core.widget.NestedScrollView | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:scrollbars="vertical" | ||||
|         app:layout_constraintBottom_toBottomOf="parent" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toTopOf="parent"> | ||||
|  | ||||
|         <androidx.recyclerview.widget.RecyclerView | ||||
|             android:id="@+id/list" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             android:nestedScrollingEnabled="false" | ||||
|             android:scrollbars="vertical" /> | ||||
|     </androidx.core.widget.NestedScrollView> | ||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||
							
								
								
									
										59
									
								
								app/src/main/res/layout/fragment_edit_places_colors.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								app/src/main/res/layout/fragment_edit_places_colors.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:orientation="vertical" | ||||
|     android:padding="16dp"> | ||||
|  | ||||
|     <TextView | ||||
|         android:id="@+id/warning_text" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_marginBottom="10dp" /> | ||||
|  | ||||
|     <ScrollView | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:orientation="vertical" | ||||
|         android:padding="4dp"> | ||||
|  | ||||
|         <androidx.recyclerview.widget.RecyclerView | ||||
|             android:id="@+id/groups_color" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" /> | ||||
|     </ScrollView> | ||||
|  | ||||
|  | ||||
|     <androidx.constraintlayout.widget.ConstraintLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_marginTop="10dp"> | ||||
|  | ||||
|         <com.google.android.material.button.MaterialButton | ||||
|             android:id="@+id/btnAdd" | ||||
|             android:layout_width="64dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:paddingLeft="6dp" | ||||
|             android:paddingRight="6dp" | ||||
|             android:text="@string/add" | ||||
|             app:layout_constraintBottom_toBottomOf="parent" | ||||
|             app:layout_constraintStart_toStartOf="parent" | ||||
|             app:layout_constraintTop_toTopOf="parent" /> | ||||
|  | ||||
|  | ||||
|         <com.google.android.material.button.MaterialButton | ||||
|             android:id="@+id/btnClear" | ||||
|             android:layout_width="64dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:paddingLeft="6dp" | ||||
|             android:paddingRight="6dp" | ||||
|             android:text="@string/clear" | ||||
|             app:layout_constraintBottom_toBottomOf="parent" | ||||
|             app:layout_constraintEnd_toEndOf="parent" | ||||
|             app:layout_constraintTop_toTopOf="parent" /> | ||||
|  | ||||
|     </androidx.constraintlayout.widget.ConstraintLayout> | ||||
|  | ||||
| </LinearLayout> | ||||
|  | ||||
							
								
								
									
										13
									
								
								app/src/main/res/layout/fragment_license.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app/src/main/res/layout/fragment_license.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     tools:context=".activity.fragment.LicenseFragment"> | ||||
|  | ||||
|     <androidx.fragment.app.FragmentContainerView | ||||
|         android:id="@+id/license_fragment_view" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" /> | ||||
|  | ||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||
							
								
								
									
										52
									
								
								app/src/main/res/layout/item_list_geoloc.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								app/src/main/res/layout/item_list_geoloc.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
|  | ||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content"> | ||||
|  | ||||
|     <com.google.android.material.button.MaterialButton | ||||
|         android:id="@+id/textView" | ||||
|         android:layout_width="0dp" | ||||
|         android:layout_height="50dp" | ||||
|         android:clickable="true" | ||||
|         android:focusable="true" | ||||
|         android:gravity="start|center_vertical" | ||||
|         android:insetTop="4dp" | ||||
|         android:insetBottom="4dp" | ||||
|         android:paddingStart="20dp" | ||||
|         android:paddingEnd="20dp" | ||||
|         android:textAllCaps="false" | ||||
|         android:textAppearance="?attr/textAppearanceBody2" | ||||
|         android:textColor="?attr/colorOnBackground" | ||||
|  | ||||
|         app:cornerRadius="4dp" | ||||
|         app:layout_constraintBottom_toBottomOf="@id/checkBox" | ||||
|         app:layout_constraintEnd_toStartOf="@id/checkBox" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toTopOf="parent" /> | ||||
|  | ||||
|     <com.google.android.material.textview.MaterialTextView | ||||
|         android:id="@+id/count" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="50dp" | ||||
|         android:gravity="start|center_vertical" | ||||
|         android:paddingStart="20dp" | ||||
|         android:paddingEnd="20dp" | ||||
|         app:layout_constraintBottom_toBottomOf="@id/checkBox" | ||||
|         app:layout_constraintEnd_toStartOf="@id/checkBox" | ||||
|         app:layout_constraintTop_toTopOf="parent" /> | ||||
|  | ||||
|     <com.google.android.material.checkbox.MaterialCheckBox | ||||
|         android:id="@+id/checkBox" | ||||
|         android:layout_width="50dp" | ||||
|         android:layout_height="50dp" | ||||
|         app:checkedState="indeterminate" | ||||
|         app:layout_constraintBottom_toBottomOf="@id/textView" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintHorizontal_bias="1" | ||||
|         app:layout_constraintStart_toEndOf="@id/textView" | ||||
|         app:layout_constraintTop_toTopOf="@id/textView" | ||||
|         app:layout_constraintVertical_bias="0.5" /> | ||||
|  | ||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||
							
								
								
									
										34
									
								
								app/src/main/res/layout/item_list_group.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								app/src/main/res/layout/item_list_group.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content"> | ||||
|  | ||||
|     <com.google.android.material.button.MaterialButton | ||||
|         android:id="@+id/group_color" | ||||
|         android:layout_width="0dp" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_marginStart="32dp" | ||||
|         android:layout_marginTop="2dp" | ||||
|         android:layout_marginEnd="32dp" | ||||
|         android:layout_marginBottom="2dp" | ||||
|         android:textAlignment="textStart" | ||||
|         android:textColor="?attr/colorOnPrimary" | ||||
|         app:layout_constraintBottom_toBottomOf="parent" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toTopOf="parent" /> | ||||
|  | ||||
|     <com.google.android.material.textview.MaterialTextView | ||||
|         android:id="@+id/name" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="50dp" | ||||
|         android:gravity="start|center_vertical" | ||||
|         android:paddingStart="20dp" | ||||
|         android:paddingEnd="20dp" | ||||
|         android:textColor="?attr/colorOnPrimary" | ||||
|         app:layout_constraintBottom_toBottomOf="@id/group_color" | ||||
|         app:layout_constraintEnd_toEndOf="@id/group_color" | ||||
|         app:layout_constraintTop_toTopOf="@id/group_color" /> | ||||
|  | ||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||
							
								
								
									
										13
									
								
								app/src/main/res/menu/menu_edit.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app/src/main/res/menu/menu_edit.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <menu xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     tools:context="net.helcel.beans.activity.EditActivity" > | ||||
|  | ||||
|     <item | ||||
|         android:id="@+id/action_color" | ||||
|         android:orderInCategory="100" | ||||
|         android:icon="@drawable/color" | ||||
|         android:title="@string/action_color" | ||||
|         app:showAsAction="ifRoom" /> | ||||
|  | ||||
| </menu> | ||||
							
								
								
									
										25
									
								
								app/src/main/res/menu/menu_main.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/src/main/res/menu/menu_main.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| <menu xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     tools:context="net.helcel.beans.activity.MainActivity" > | ||||
|  | ||||
|     <item | ||||
|         android:id="@+id/action_edit" | ||||
|         android:orderInCategory="100" | ||||
|         android:icon="@drawable/edit" | ||||
|         android:title="@string/action_edit" | ||||
|         app:showAsAction="ifRoom" /> | ||||
|     <item | ||||
|         android:id="@+id/action_stats" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:orderInCategory="100" | ||||
|         android:title="@string/action_stat" | ||||
|         app:showAsAction="never" /> | ||||
|     <item | ||||
|         android:id="@+id/action_settings" | ||||
|         android:orderInCategory="100" | ||||
|         android:title="@string/action_settings" | ||||
|         app:showAsAction="never" /> | ||||
|  | ||||
| </menu> | ||||
							
								
								
									
										25
									
								
								app/src/main/res/values/arrays.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/src/main/res/values/arrays.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <string-array name="entries_theme"> | ||||
|         <item>@string/system</item> | ||||
|         <item>@string/light</item> | ||||
|         <item>@string/dark</item> | ||||
|     </string-array> | ||||
|  | ||||
|     <string-array name="entries_stats"> | ||||
|         <item>@string/counters</item> | ||||
|         <item>@string/percentages</item> | ||||
|     </string-array> | ||||
|  | ||||
|     <string-array name="entries_onoff"> | ||||
|         <item>@string/on</item> | ||||
|         <item>@string/off</item> | ||||
|     </string-array> | ||||
|  | ||||
|     <string-array name="map_projection"> | ||||
|         <item>@string/azimuthalequidistant</item> | ||||
|         <item>@string/loximuthal</item> | ||||
|         <item>@string/mercator</item> | ||||
|  | ||||
|     </string-array> | ||||
| </resources> | ||||
| @@ -1,10 +1,10 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <string name="app_name">Beans</string> | ||||
|     <string name="app_version">1.0a</string> | ||||
|     <string name="action_settings">Settings</string> | ||||
|     <string name="action_stat">Stats</string> | ||||
|     <string name="action_edit">Edit</string> | ||||
|     <string name="action_add">Add</string> | ||||
|     <string name="action_clear">Clear</string> | ||||
|     <string name="action_color">Color</string> | ||||
|     <string name="key_theme">App theme</string> | ||||
|     <string name="system">System</string> | ||||
| @@ -18,17 +18,16 @@ | ||||
|     <string name="key_regional">Regional</string> | ||||
|     <string name="about">About</string> | ||||
|     <string name="beans_is_foss">Beans is free and open source software, licensed under the GNU General Public License (version 3 or later)</string> | ||||
|     <string name="beans_repo_uri">https://github.com/helcel-net/beans</string> | ||||
|     <string name="beans_repo">Project repository: %1$s\n Feel free to report issues or contribute.</string> | ||||
|     <string name="beans_repo">Project repository: https://github.com/helcel-net/beans\n Feel free to report issues or contribute to the project.</string> | ||||
|     <string name="foss_licenses">Free and open source dependencies and licenses</string> | ||||
|     <string name="about_beans">About the Beans application</string> | ||||
|     <string name="edit_group">Select the group to assign.</string> | ||||
|     <string name="edit_group_sub">Long press on a group to edit its name and color.</string> | ||||
|     <string name="select_group">Select the group to keep.</string> | ||||
|     <string name="select_group_sub">All others will be deleted and its mappings reassigned to the group you choose here.</string> | ||||
|     <string name="edit_group">Select the group to assign. Long press on a group to edit its name and color.</string> | ||||
|     <string name="delete_group">Are your sure you want to delete this group and remove all its country mappings?</string> | ||||
|     <string name="select_group">Select one group you want to keep. All others will be deleted and its mappings reassigned to the group you choose here.</string> | ||||
|     <string name="delete_regions">Are you sure you want to disable regions and reassign all regional mappings to the corresponding countries?</string> | ||||
|  | ||||
|     <string name="add">Add</string> | ||||
|     <string name="clear">Clear</string> | ||||
|     <string name="logo">Logo</string> | ||||
|     <string name="name">Name</string> | ||||
|     <string name="rate">%1$d/%2$d</string> | ||||
|     <string name="rate_with_unit">%1$d / %2$d %3$s</string> | ||||
| @@ -40,10 +39,9 @@ | ||||
|     <string name="off">Off</string> | ||||
|     <string name="delete">Delete</string> | ||||
|     <string name="cancel">Cancel</string> | ||||
|     <string name="ok">OK</string> | ||||
|     <string name="ok">Ok</string> | ||||
|     <string name="total">Total</string> | ||||
|     <string name="uncategorized">Uncategorized</string> | ||||
|  | ||||
|     <string name="azimuthalequidistant">Azimuthal Equidistant</string> | ||||
|     <string name="mercator">Mercator</string> | ||||
|     <string name="loximuthal">Loximuthal</string> | ||||
|   | ||||
							
								
								
									
										32
									
								
								app/src/main/res/values/themes.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								app/src/main/res/values/themes.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| <resources> | ||||
|     <style name="Theme.Beans" parent="Theme.Material3.DayNight"> | ||||
|         <item name="colorPrimary">@color/blue</item> | ||||
|         <item name="background">@color/darkgray</item> | ||||
|         <item name="android:colorPrimary">?attr/colorPrimary</item> | ||||
|         <item name="android:panelColorBackground">@color/lightgray</item> | ||||
|         <item name="android:statusBarColor">?attr/colorPrimary</item> | ||||
|  | ||||
|         <item name="checkboxStyle">@style/Theme.Beans.CheckBox</item> | ||||
|         <item name="actionBarStyle">@style/Theme.Beans.ActionBar</item> | ||||
|         <item name="android:actionOverflowButtonStyle">@style/Theme.Beans.ActionBar.ButtonOverflow</item> | ||||
|     </style> | ||||
|  | ||||
|     <style name="Theme.Beans.CheckBox" parent="Widget.Material3.CompoundButton.CheckBox"> | ||||
|     </style> | ||||
|  | ||||
|     <style name="Theme.Beans.ActionBar" parent="Widget.Material3.ActionBar.Solid"> | ||||
|         <item name="background">?attr/colorPrimary</item> | ||||
|         <item name="titleTextStyle">@style/Theme.Beans.ActionBar.Text</item> | ||||
|         <item name="android:tint">@color/white</item> | ||||
|         <item name="actionMenuTextColor">@color/white</item> | ||||
|         <item name="homeAsUpIndicator">@drawable/back</item> | ||||
|     </style> | ||||
|  | ||||
|     <style name="Theme.Beans.ActionBar.Text" parent="TextAppearance.Material3.ActionBar.Title"> | ||||
|         <item name="android:textColor">@color/white</item> | ||||
|     </style> | ||||
|  | ||||
|     <style name="Theme.Beans.ActionBar.ButtonOverflow" parent="Widget.Material3.Search.ActionButton.Overflow"> | ||||
|         <item name="android:tint">@color/white</item> | ||||
|     </style> | ||||
| </resources> | ||||
							
								
								
									
										73
									
								
								app/src/main/res/xml/fragment_settings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								app/src/main/res/xml/fragment_settings.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:theme="@style/Theme.Beans"> | ||||
|  | ||||
|     <ListPreference | ||||
|         app:defaultValue="@string/system" | ||||
|         app:enabled="true" | ||||
|         app:entries="@array/entries_theme" | ||||
|         app:entryValues="@array/entries_theme" | ||||
|         app:icon="@drawable/palette" | ||||
|         app:key="@string/key_theme" | ||||
|         app:title="@string/key_theme" | ||||
|         app:useSimpleSummaryProvider="true" /> | ||||
|  | ||||
|     <ListPreference | ||||
|         app:defaultValue="@string/mercator" | ||||
|         app:enabled="true" | ||||
|         app:entries="@array/map_projection" | ||||
|         app:entryValues="@array/map_projection" | ||||
|         app:icon="@drawable/map" | ||||
|         app:key="@string/key_projection" | ||||
|         app:title="@string/key_projection" | ||||
|         app:useSimpleSummaryProvider="true" /> | ||||
|  | ||||
|     <ListPreference | ||||
|         app:defaultValue="@string/counters" | ||||
|         app:enabled="true" | ||||
|         app:entries="@array/entries_stats" | ||||
|         app:entryValues="@array/entries_stats" | ||||
|         app:icon="@drawable/stats" | ||||
|         app:key="@string/key_stats" | ||||
|         app:title="@string/key_stats" | ||||
|         app:useSimpleSummaryProvider="true" /> | ||||
|  | ||||
|     <ListPreference | ||||
|         app:defaultValue="@string/off" | ||||
|         app:enabled="true" | ||||
|         app:allowDividerAbove="true" | ||||
|         app:entries="@array/entries_onoff" | ||||
|         app:entryValues="@array/entries_onoff" | ||||
|         app:icon="@drawable/group" | ||||
|         app:key="@string/key_group" | ||||
|         app:title="@string/key_group" | ||||
|         app:useSimpleSummaryProvider="true" /> | ||||
|  | ||||
|     <ListPreference | ||||
|         app:defaultValue="@string/off" | ||||
|         app:enabled="true" | ||||
|         app:entries="@array/entries_onoff" | ||||
|         app:entryValues="@array/entries_onoff" | ||||
|         app:icon="@drawable/zoomin" | ||||
|         app:key="@string/key_regional" | ||||
|         app:title="@string/key_regional" | ||||
|         app:useSimpleSummaryProvider="true" /> | ||||
|  | ||||
|     <Preference | ||||
|         android:summary="@string/foss_licenses" | ||||
|         app:enabled="true" | ||||
|         app:allowDividerAbove="true" | ||||
|         app:icon="@drawable/licenses" | ||||
|         app:key="@string/licenses" | ||||
|         app:title="@string/licenses" /> | ||||
|  | ||||
|     <Preference | ||||
|         android:summary="@string/about_beans" | ||||
|         app:enabled="true" | ||||
|         app:icon="@drawable/about" | ||||
|         app:key="@string/about" | ||||
|         app:title="@string/about" /> | ||||
|  | ||||
|  | ||||
| </PreferenceScreen> | ||||
| @@ -1,6 +1,6 @@ | ||||
| // Top-level build file where you can add configuration options common to all sub-projects/modules. | ||||
| plugins { | ||||
|     id 'com.android.application' version '8.13.0' apply false | ||||
|     id 'com.android.library' version '8.13.0' apply false | ||||
|     id 'org.jetbrains.kotlin.android' version '2.2.21' apply false | ||||
|     id 'com.android.application' version '8.10.1' apply false | ||||
|     id 'com.android.library' version '8.10.1' apply false | ||||
|     id 'org.jetbrains.kotlin.android' version '2.1.21' apply false | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										4
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| distributionBase=GRADLE_USER_HOME | ||||
| distributionPath=wrapper/dists | ||||
| distributionSha256Sum=df67a32e86e3276d011735facb1535f64d0d88df84fa87521e90becc2d735444 | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip | ||||
| distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip | ||||
| networkTimeout=10000 | ||||
| validateDistributionUrl=true | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
|   | ||||
							
								
								
									
										5
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| #!/bin/sh | ||||
|  | ||||
| # | ||||
| # Copyright © 2015 the original authors. | ||||
| # Copyright © 2015-2021 the original authors. | ||||
| # | ||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| # you may not use this file except in compliance with the License. | ||||
| @@ -114,6 +114,7 @@ case "$( uname )" in                #( | ||||
|   NONSTOP* )        nonstop=true ;; | ||||
| esac | ||||
|  | ||||
| CLASSPATH="\\\"\\\"" | ||||
|  | ||||
|  | ||||
| # Determine the Java command to use to start the JVM. | ||||
| @@ -171,6 +172,7 @@ fi | ||||
| # For Cygwin or MSYS, switch paths to Windows format before running java | ||||
| if "$cygwin" || "$msys" ; then | ||||
|     APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) | ||||
|     CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) | ||||
|  | ||||
|     JAVACMD=$( cygpath --unix "$JAVACMD" ) | ||||
|  | ||||
| @@ -210,6 +212,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | ||||
|  | ||||
| set -- \ | ||||
|         "-Dorg.gradle.appname=$APP_BASE_NAME" \ | ||||
|         -classpath "$CLASSPATH" \ | ||||
|         -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ | ||||
|         "$@" | ||||
|  | ||||
|   | ||||
							
								
								
									
										3
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							| @@ -70,10 +70,11 @@ goto fail | ||||
| :execute | ||||
| @rem Setup the command line | ||||
|  | ||||
| set CLASSPATH= | ||||
|  | ||||
|  | ||||
| @rem Execute Gradle | ||||
| "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* | ||||
| "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* | ||||
|  | ||||
| :end | ||||
| @rem End local scope for the variables with windows NT shell | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|   "dependencies": { | ||||
|     "@turf/area": "^7.0.0", | ||||
|     "@turf/turf": "^7.0.0", | ||||
|     "jsdom": "^27.0.0", | ||||
|     "jsdom": "^26.0.0", | ||||
|     "mapshaper": "^0.6.79" | ||||
|   }, | ||||
|   "type": "module" | ||||
|   | ||||
							
								
								
									
										378
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										378
									
								
								yarn.lock
									
									
									
									
									
								
							| @@ -2,62 +2,41 @@ | ||||
| # yarn lockfile v1 | ||||
|  | ||||
|  | ||||
| "@asamuzakjp/css-color@^4.0.3": | ||||
|   version "4.0.5" | ||||
|   resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-4.0.5.tgz#cc533095241d8a56c49614591955280ab8c4bb02" | ||||
|   integrity sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ== | ||||
| "@asamuzakjp/css-color@^3.2.0": | ||||
|   version "3.2.0" | ||||
|   resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-3.2.0.tgz#cc42f5b85c593f79f1fa4f25d2b9b321e61d1794" | ||||
|   integrity sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw== | ||||
|   dependencies: | ||||
|     "@csstools/css-calc" "^2.1.4" | ||||
|     "@csstools/css-color-parser" "^3.1.0" | ||||
|     "@csstools/css-parser-algorithms" "^3.0.5" | ||||
|     "@csstools/css-tokenizer" "^3.0.4" | ||||
|     lru-cache "^11.2.1" | ||||
|     "@csstools/css-calc" "^2.1.3" | ||||
|     "@csstools/css-color-parser" "^3.0.9" | ||||
|     "@csstools/css-parser-algorithms" "^3.0.4" | ||||
|     "@csstools/css-tokenizer" "^3.0.3" | ||||
|     lru-cache "^10.4.3" | ||||
|  | ||||
| "@asamuzakjp/dom-selector@^6.7.2": | ||||
|   version "6.7.3" | ||||
|   resolved "https://registry.yarnpkg.com/@asamuzakjp/dom-selector/-/dom-selector-6.7.3.tgz#c6b1dadf8f4365de5db4435bba53328265c83f6c" | ||||
|   integrity sha512-kiGFeY+Hxf5KbPpjRLf+ffWbkos1aGo8MBfd91oxS3O57RgU3XhZrt/6UzoVF9VMpWbC3v87SRc9jxGrc9qHtQ== | ||||
|   dependencies: | ||||
|     "@asamuzakjp/nwsapi" "^2.3.9" | ||||
|     bidi-js "^1.0.3" | ||||
|     css-tree "^3.1.0" | ||||
|     is-potential-custom-element-name "^1.0.1" | ||||
|     lru-cache "^11.2.2" | ||||
| "@csstools/color-helpers@^5.0.2": | ||||
|   version "5.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.2.tgz#82592c9a7c2b83c293d9161894e2a6471feb97b8" | ||||
|   integrity sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA== | ||||
|  | ||||
| "@asamuzakjp/nwsapi@^2.3.9": | ||||
|   version "2.3.9" | ||||
|   resolved "https://registry.yarnpkg.com/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz#ad5549322dfe9d153d4b4dd6f7ff2ae234b06e24" | ||||
|   integrity sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q== | ||||
|  | ||||
| "@csstools/color-helpers@^5.1.0": | ||||
|   version "5.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.1.0.tgz#106c54c808cabfd1ab4c602d8505ee584c2996ef" | ||||
|   integrity sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA== | ||||
|  | ||||
| "@csstools/css-calc@^2.1.4": | ||||
| "@csstools/css-calc@^2.1.3", "@csstools/css-calc@^2.1.4": | ||||
|   version "2.1.4" | ||||
|   resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.4.tgz#8473f63e2fcd6e459838dd412401d5948f224c65" | ||||
|   integrity sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ== | ||||
|  | ||||
| "@csstools/css-color-parser@^3.1.0": | ||||
|   version "3.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz#4e386af3a99dd36c46fef013cfe4c1c341eed6f0" | ||||
|   integrity sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA== | ||||
| "@csstools/css-color-parser@^3.0.9": | ||||
|   version "3.0.10" | ||||
|   resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz#79fc68864dd43c3b6782d2b3828bc0fa9d085c10" | ||||
|   integrity sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg== | ||||
|   dependencies: | ||||
|     "@csstools/color-helpers" "^5.1.0" | ||||
|     "@csstools/color-helpers" "^5.0.2" | ||||
|     "@csstools/css-calc" "^2.1.4" | ||||
|  | ||||
| "@csstools/css-parser-algorithms@^3.0.5": | ||||
| "@csstools/css-parser-algorithms@^3.0.4": | ||||
|   version "3.0.5" | ||||
|   resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz#5755370a9a29abaec5515b43c8b3f2cf9c2e3076" | ||||
|   integrity sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ== | ||||
|  | ||||
| "@csstools/css-syntax-patches-for-csstree@^1.0.14": | ||||
|   version "1.0.14" | ||||
|   resolved "https://registry.yarnpkg.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.14.tgz#96e8bd829dea29da6460ac7568ee922f48ecc382" | ||||
|   integrity sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q== | ||||
|  | ||||
| "@csstools/css-tokenizer@^3.0.4": | ||||
| "@csstools/css-tokenizer@^3.0.3": | ||||
|   version "3.0.4" | ||||
|   resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz#333fedabc3fd1a8e5d0100013731cf19e6a8c5d3" | ||||
|   integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== | ||||
| @@ -97,11 +76,6 @@ | ||||
|   resolved "https://registry.yarnpkg.com/@placemarkio/tokml/-/tokml-0.3.7.tgz#c4d38a184028a7bbcedfd61987a45928b58c2c43" | ||||
|   integrity sha512-pQaoQTBvDf7p7d/3ZHDaxWaU62guSYB9KQ6vvecshELunzpdN5tbgw0d+SVO1eYaTlrxX3Nvi7F9DI8FcoJePg== | ||||
|  | ||||
| "@rollup/rollup-linux-x64-gnu@^4.44.1": | ||||
|   version "4.52.5" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz#a151cb1234cc9b2cf5e8cfc02aa91436b8f9e278" | ||||
|   integrity sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q== | ||||
|  | ||||
| "@tmcw/togeojson@^5.6.0": | ||||
|   version "5.8.1" | ||||
|   resolved "https://registry.yarnpkg.com/@tmcw/togeojson/-/togeojson-5.8.1.tgz#6cbcc9b1484ed28e71bdd4b5f96ceae540a4533f" | ||||
| @@ -1587,17 +1561,22 @@ | ||||
|   integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== | ||||
|  | ||||
| "@types/node@*": | ||||
|   version "24.9.1" | ||||
|   resolved "https://registry.yarnpkg.com/@types/node/-/node-24.9.1.tgz#b7360b3c789089e57e192695a855aa4f6981a53c" | ||||
|   integrity sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg== | ||||
|   version "24.0.3" | ||||
|   resolved "https://registry.yarnpkg.com/@types/node/-/node-24.0.3.tgz#f935910f3eece3a3a2f8be86b96ba833dc286cab" | ||||
|   integrity sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg== | ||||
|   dependencies: | ||||
|     undici-types "~7.16.0" | ||||
|     undici-types "~7.8.0" | ||||
|  | ||||
| "@types/node@^10.0.3": | ||||
|   version "10.17.60" | ||||
|   resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" | ||||
|   integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== | ||||
|  | ||||
| "@types/node@^7.0.31": | ||||
|   version "7.10.14" | ||||
|   resolved "https://registry.yarnpkg.com/@types/node/-/node-7.10.14.tgz#06fa7319b8131b969a8da4a14c487e6f28abacf7" | ||||
|   integrity sha512-29GS75BE8asnTno3yB6ubOJOO0FboExEqNJy4bpz0GSmW/8wPTNL4h9h63c6s1uTrOopCmJYe/4yJLh5r92ZUA== | ||||
|  | ||||
| "@types/node@^8.0.0": | ||||
|   version "8.10.66" | ||||
|   resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" | ||||
| @@ -1609,9 +1588,9 @@ | ||||
|   integrity sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ== | ||||
|  | ||||
| "@xmldom/xmldom@^0.8.6": | ||||
|   version "0.8.11" | ||||
|   resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.11.tgz#b79de2d67389734c57c52595f7a7305e30c2d608" | ||||
|   integrity sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw== | ||||
|   version "0.8.10" | ||||
|   resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99" | ||||
|   integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== | ||||
|  | ||||
| adm-zip@^0.5.9: | ||||
|   version "0.5.16" | ||||
| @@ -1619,9 +1598,9 @@ adm-zip@^0.5.9: | ||||
|   integrity sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ== | ||||
|  | ||||
| agent-base@^7.1.0, agent-base@^7.1.2: | ||||
|   version "7.1.4" | ||||
|   resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" | ||||
|   integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== | ||||
|   version "7.1.3" | ||||
|   resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" | ||||
|   integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== | ||||
|  | ||||
| asap@~2.0.6: | ||||
|   version "2.0.6" | ||||
| @@ -1633,22 +1612,10 @@ asynckit@^0.4.0: | ||||
|   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" | ||||
|   integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== | ||||
|  | ||||
| bidi-js@^1.0.3: | ||||
|   version "1.0.3" | ||||
|   resolved "https://registry.yarnpkg.com/bidi-js/-/bidi-js-1.0.3.tgz#6f8bcf3c877c4d9220ddf49b9bb6930c88f877d2" | ||||
|   integrity sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw== | ||||
|   dependencies: | ||||
|     require-from-string "^2.0.2" | ||||
|  | ||||
| big.js@^7.0.1: | ||||
|   version "7.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/big.js/-/big.js-7.0.1.tgz#c537c649ec6ea11d1306723d13c096ba199aadc4" | ||||
|   integrity sha512-iFgV784tD8kq4ccF1xtNMZnXeZzVuXWWM+ERFzKQjv+A5G9HC8CY3DuV45vgzFFcW+u2tIvmF95+AzWgs6BjCg== | ||||
|  | ||||
| bignumber.js@^9.1.0: | ||||
|   version "9.3.1" | ||||
|   resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.3.1.tgz#759c5aaddf2ffdc4f154f7b493e1c8770f88c4d7" | ||||
|   integrity sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ== | ||||
|   version "9.3.0" | ||||
|   resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.3.0.tgz#bdba7e2a4c1a2eba08290e8dcad4f36393c92acd" | ||||
|   integrity sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA== | ||||
|  | ||||
| buffer-from@^1.0.0: | ||||
|   version "1.1.2" | ||||
| @@ -1671,7 +1638,7 @@ call-bound@^1.0.2: | ||||
|     call-bind-apply-helpers "^1.0.2" | ||||
|     get-intrinsic "^1.3.0" | ||||
|  | ||||
| caseless@^0.12.0, caseless@~0.12.0: | ||||
| caseless@~0.12.0: | ||||
|   version "0.12.0" | ||||
|   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" | ||||
|   integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== | ||||
| @@ -1693,7 +1660,7 @@ commander@7.0.0: | ||||
|   resolved "https://registry.yarnpkg.com/commander/-/commander-7.0.0.tgz#3e2bbfd8bb6724760980988fb5b22b7ee6b71ab2" | ||||
|   integrity sha512-ovx/7NkTrnPuIV8sqk/GjUIIM1+iUQeqA3ye2VNpq9sVoiZsooObWlQy+OPWGI17GDaEoybuAGJm6U8yC077BA== | ||||
|  | ||||
| concat-stream@^1.6.0, concat-stream@^1.6.2: | ||||
| concat-stream@^1.4.6, concat-stream@^1.6.0: | ||||
|   version "1.6.2" | ||||
|   resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" | ||||
|   integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== | ||||
| @@ -1726,22 +1693,13 @@ core-util-is@~1.0.0: | ||||
|   resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" | ||||
|   integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== | ||||
|  | ||||
| css-tree@^3.1.0: | ||||
|   version "3.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-3.1.0.tgz#7aabc035f4e66b5c86f54570d55e05b1346eb0fd" | ||||
|   integrity sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w== | ||||
| cssstyle@^4.2.1: | ||||
|   version "4.5.0" | ||||
|   resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.5.0.tgz#b2d6f06884db0de8315443fb75b0a48d60931cac" | ||||
|   integrity sha512-/7gw8TGrvH/0g564EnhgFZogTMVe+lifpB7LWU+PEsiq5o83TUXR3fDbzTRXOJhoJwck5IS9ez3Em5LNMMO2aw== | ||||
|   dependencies: | ||||
|     mdn-data "2.12.2" | ||||
|     source-map-js "^1.0.1" | ||||
|  | ||||
| cssstyle@^5.3.1: | ||||
|   version "5.3.1" | ||||
|   resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-5.3.1.tgz#f55a9cc73d12705da8a341261d5e85003fe3a441" | ||||
|   integrity sha512-g5PC9Aiph9eiczFpcgUhd9S4UUO3F+LHGRIi5NUMZ+4xtoIYbHNZwZnWA2JsFGe8OU8nl4WyaEFiZuGuxlutJQ== | ||||
|   dependencies: | ||||
|     "@asamuzakjp/css-color" "^4.0.3" | ||||
|     "@csstools/css-syntax-patches-for-csstree" "^1.0.14" | ||||
|     css-tree "^3.1.0" | ||||
|     "@asamuzakjp/css-color" "^3.2.0" | ||||
|     rrweb-cssom "^0.8.0" | ||||
|  | ||||
| d3-array@1: | ||||
|   version "1.2.4" | ||||
| @@ -1780,25 +1738,25 @@ d3-voronoi@1.1.2: | ||||
|   resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.2.tgz#1687667e8f13a2d158c80c1480c5a29cb0d8973c" | ||||
|   integrity sha512-RhGS1u2vavcO7ay7ZNAPo4xeDh/VYeGof3x5ZLJBQgYhLegxr3s5IykvWmJ94FTU6mcbtp4sloqZ54mP6R4Utw== | ||||
|  | ||||
| data-urls@^6.0.0: | ||||
|   version "6.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-6.0.0.tgz#95a7943c8ac14c1d563b771f2621cc50e8ec7744" | ||||
|   integrity sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA== | ||||
| data-urls@^5.0.0: | ||||
|   version "5.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" | ||||
|   integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== | ||||
|   dependencies: | ||||
|     whatwg-mimetype "^4.0.0" | ||||
|     whatwg-url "^15.0.0" | ||||
|     whatwg-url "^14.0.0" | ||||
|  | ||||
| debug@4, debug@^4.3.4: | ||||
|   version "4.4.3" | ||||
|   resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" | ||||
|   integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== | ||||
|   version "4.4.1" | ||||
|   resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" | ||||
|   integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== | ||||
|   dependencies: | ||||
|     ms "^2.1.3" | ||||
|  | ||||
| decimal.js@^10.6.0: | ||||
|   version "10.6.0" | ||||
|   resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a" | ||||
|   integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== | ||||
| decimal.js@^10.5.0: | ||||
|   version "10.5.0" | ||||
|   resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" | ||||
|   integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== | ||||
|  | ||||
| delaunator@^5.0.0: | ||||
|   version "5.0.1" | ||||
| @@ -1818,9 +1776,9 @@ depd@~2.0.0: | ||||
|   integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== | ||||
|  | ||||
| detect-libc@^2.0.1: | ||||
|   version "2.1.2" | ||||
|   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" | ||||
|   integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== | ||||
|   version "2.0.4" | ||||
|   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.4.tgz#f04715b8ba815e53b4d8109655b6508a6865a7e8" | ||||
|   integrity sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA== | ||||
|  | ||||
| dunder-proto@^1.0.1: | ||||
|   version "1.0.1" | ||||
| @@ -1873,10 +1831,10 @@ fast-deep-equal@^3.1.3: | ||||
|   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" | ||||
|   integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== | ||||
|  | ||||
| fflate@0.8.2: | ||||
|   version "0.8.2" | ||||
|   resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" | ||||
|   integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== | ||||
| fflate@^0.7.4: | ||||
|   version "0.7.4" | ||||
|   resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.7.4.tgz#61587e5d958fdabb5a9368a302c25363f4f69f50" | ||||
|   integrity sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw== | ||||
|  | ||||
| flatbush@^3.2.1: | ||||
|   version "3.3.1" | ||||
| @@ -1891,14 +1849,13 @@ flatqueue@^1.2.1: | ||||
|   integrity sha512-X86TpWS1rGuY7m382HuA9vngLeDuWA9lJvhEG+GfgKMV5onSvx5a71cl7GMbXzhWtlN9dGfqOBrpfqeOtUfGYQ== | ||||
|  | ||||
| form-data@^2.2.0: | ||||
|   version "2.5.5" | ||||
|   resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.5.tgz#a5f6364ad7e4e67e95b4a07e2d8c6f711c74f624" | ||||
|   integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== | ||||
|   version "2.5.3" | ||||
|   resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.3.tgz#f9bcf87418ce748513c0c3494bb48ec270c97acc" | ||||
|   integrity sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ== | ||||
|   dependencies: | ||||
|     asynckit "^0.4.0" | ||||
|     combined-stream "^1.0.8" | ||||
|     es-set-tostringtag "^2.1.0" | ||||
|     hasown "^2.0.2" | ||||
|     mime-types "^2.1.35" | ||||
|     safe-buffer "^5.2.1" | ||||
|  | ||||
| @@ -1907,11 +1864,6 @@ function-bind@^1.1.2: | ||||
|   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" | ||||
|   integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== | ||||
|  | ||||
| geographiclib-geodesic@^2.2.0: | ||||
|   version "2.2.0" | ||||
|   resolved "https://registry.yarnpkg.com/geographiclib-geodesic/-/geographiclib-geodesic-2.2.0.tgz#aa4eebfa798d4c80f8a2c3e28cbb76ecc3295622" | ||||
|   integrity sha512-cIedo9VTYb0DFufodgibDmVfsWe9EASqb/kUByl09xc6PZYvLvlc89BHCThtGTPf2OII/zWJGxsR3Uz6O7QOVw== | ||||
|  | ||||
| geographiclib@1.48.0: | ||||
|   version "1.48.0" | ||||
|   resolved "https://registry.yarnpkg.com/geographiclib/-/geographiclib-1.48.0.tgz#8ff2ae185ad380f675db6a243935fadd147def82" | ||||
| @@ -1998,13 +1950,15 @@ html-encoding-sniffer@^4.0.0: | ||||
|   dependencies: | ||||
|     whatwg-encoding "^3.1.1" | ||||
|  | ||||
| http-basic@^8.1.1: | ||||
|   version "8.1.3" | ||||
|   resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf" | ||||
|   integrity sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw== | ||||
| http-basic@^6.0.0: | ||||
|   version "6.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-6.0.0.tgz#1d63df8f891e1e25e0c2c84d7a2d94702d6ae229" | ||||
|   integrity sha512-7ScbVjuiReYe8S+OZOpNjoKGXrbhJHIrQQe7eq1TpLTJkxH8MPKvnTUzq/TNLjww1hdFQy8yUIC42wuLhCjYcQ== | ||||
|   dependencies: | ||||
|     caseless "^0.12.0" | ||||
|     concat-stream "^1.6.2" | ||||
|     "@types/concat-stream" "^1.6.0" | ||||
|     "@types/node" "^7.0.31" | ||||
|     caseless "~0.12.0" | ||||
|     concat-stream "^1.4.6" | ||||
|     http-response-object "^3.0.1" | ||||
|     parse-cache-control "^1.0.1" | ||||
|  | ||||
| @@ -2063,30 +2017,30 @@ isarray@~1.0.0: | ||||
|   resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" | ||||
|   integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== | ||||
|  | ||||
| jsdom@^27.0.0: | ||||
|   version "27.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-27.0.1.tgz#72f3ff263ccc128da40d8526943a7df253eeea93" | ||||
|   integrity sha512-SNSQteBL1IlV2zqhwwolaG9CwhIhTvVHWg3kTss/cLE7H/X4644mtPQqYvCfsSrGQWt9hSZcgOXX8bOZaMN+kA== | ||||
| jsdom@^26.0.0: | ||||
|   version "26.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-26.1.0.tgz#ab5f1c1cafc04bd878725490974ea5e8bf0c72b3" | ||||
|   integrity sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg== | ||||
|   dependencies: | ||||
|     "@asamuzakjp/dom-selector" "^6.7.2" | ||||
|     cssstyle "^5.3.1" | ||||
|     data-urls "^6.0.0" | ||||
|     decimal.js "^10.6.0" | ||||
|     cssstyle "^4.2.1" | ||||
|     data-urls "^5.0.0" | ||||
|     decimal.js "^10.5.0" | ||||
|     html-encoding-sniffer "^4.0.0" | ||||
|     http-proxy-agent "^7.0.2" | ||||
|     https-proxy-agent "^7.0.6" | ||||
|     is-potential-custom-element-name "^1.0.1" | ||||
|     parse5 "^8.0.0" | ||||
|     nwsapi "^2.2.16" | ||||
|     parse5 "^7.2.1" | ||||
|     rrweb-cssom "^0.8.0" | ||||
|     saxes "^6.0.0" | ||||
|     symbol-tree "^3.2.4" | ||||
|     tough-cookie "^6.0.0" | ||||
|     tough-cookie "^5.1.1" | ||||
|     w3c-xmlserializer "^5.0.0" | ||||
|     webidl-conversions "^8.0.0" | ||||
|     webidl-conversions "^7.0.0" | ||||
|     whatwg-encoding "^3.1.1" | ||||
|     whatwg-mimetype "^4.0.0" | ||||
|     whatwg-url "^15.1.0" | ||||
|     ws "^8.18.3" | ||||
|     whatwg-url "^14.1.1" | ||||
|     ws "^8.18.0" | ||||
|     xml-name-validator "^5.0.0" | ||||
|  | ||||
| jsts@2.7.1: | ||||
| @@ -2106,30 +2060,28 @@ keygrip@~1.1.0: | ||||
|   dependencies: | ||||
|     tsscmp "1.0.6" | ||||
|  | ||||
| lru-cache@^11.2.1, lru-cache@^11.2.2: | ||||
|   version "11.2.2" | ||||
|   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.2.tgz#40fd37edffcfae4b2940379c0722dc6eeaa75f24" | ||||
|   integrity sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg== | ||||
| lru-cache@^10.4.3: | ||||
|   version "10.4.3" | ||||
|   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" | ||||
|   integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== | ||||
|  | ||||
| mapshaper@^0.6.79: | ||||
|   version "0.6.113" | ||||
|   resolved "https://registry.yarnpkg.com/mapshaper/-/mapshaper-0.6.113.tgz#23fadfd247c4b53c892655aa796f470504e77cf1" | ||||
|   integrity sha512-eN37+sb5pE084b6pVgRUsM5s5aFwwilYZQm8AN/FN8jD7hzoQ48aBwvd+QNzrIYSf/ZknbT9tYa4QuaoR1KRWg== | ||||
|   version "0.6.102" | ||||
|   resolved "https://registry.yarnpkg.com/mapshaper/-/mapshaper-0.6.102.tgz#ca5108611ba07c46c788fbfbcf49fdf467f9fc33" | ||||
|   integrity sha512-LYflAzywcFskJ4aQVdctuymmOzB15MEjRY2nU+tkEH2hXNtsNSsM/IhOTF+NuoQziKxx7tHZObxnQSpcH5yaAA== | ||||
|   dependencies: | ||||
|     "@placemarkio/tokml" "^0.3.3" | ||||
|     "@tmcw/togeojson" "^5.6.0" | ||||
|     "@xmldom/xmldom" "^0.8.6" | ||||
|     adm-zip "^0.5.9" | ||||
|     big.js "^7.0.1" | ||||
|     commander "7.0.0" | ||||
|     cookies "^0.8.0" | ||||
|     d3-color "3.1.0" | ||||
|     d3-interpolate "^3.0.1" | ||||
|     d3-scale-chromatic "3.0.0" | ||||
|     delaunator "^5.0.0" | ||||
|     fflate "0.8.2" | ||||
|     fflate "^0.7.4" | ||||
|     flatbush "^3.2.1" | ||||
|     geographiclib-geodesic "^2.2.0" | ||||
|     geokdbush "^1.1.0" | ||||
|     iconv-lite "^0.6.3" | ||||
|     idb-keyval "^6.2.0" | ||||
| @@ -2138,10 +2090,8 @@ mapshaper@^0.6.79: | ||||
|     msgpackr "^1.10.1" | ||||
|     opn "^5.3.0" | ||||
|     rw "~1.3.3" | ||||
|     sync-request "6.1.0" | ||||
|     sync-request "5.0.0" | ||||
|     tinyqueue "^2.0.3" | ||||
|   optionalDependencies: | ||||
|     "@rollup/rollup-linux-x64-gnu" "^4.44.1" | ||||
|  | ||||
| marchingsquares@^1.3.3: | ||||
|   version "1.3.3" | ||||
| @@ -2153,11 +2103,6 @@ math-intrinsics@^1.1.0: | ||||
|   resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" | ||||
|   integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== | ||||
|  | ||||
| mdn-data@2.12.2: | ||||
|   version "2.12.2" | ||||
|   resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.12.2.tgz#9ae6c41a9e65adf61318b32bff7b64fbfb13f8cf" | ||||
|   integrity sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA== | ||||
|  | ||||
| mime-db@1.52.0: | ||||
|   version "1.52.0" | ||||
|   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" | ||||
| @@ -2198,9 +2143,9 @@ msgpackr-extract@^3.0.2: | ||||
|     "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.3" | ||||
|  | ||||
| msgpackr@^1.10.1: | ||||
|   version "1.11.5" | ||||
|   resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.11.5.tgz#edf0b9d9cb7d8ed6897dd0e42cfb865a2f4b602e" | ||||
|   integrity sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA== | ||||
|   version "1.11.4" | ||||
|   resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.11.4.tgz#14703caead8ee0c2e7c89417de5a3ec94adf5d3e" | ||||
|   integrity sha512-uaff7RG9VIC4jacFW9xzL3jc0iM32DNHe4jYVycBcjUePT/Klnfj7pqtWJt9khvDFizmjN2TlYniYmSS2LIaZg== | ||||
|   optionalDependencies: | ||||
|     msgpackr-extract "^3.0.2" | ||||
|  | ||||
| @@ -2211,6 +2156,11 @@ node-gyp-build-optional-packages@5.2.2: | ||||
|   dependencies: | ||||
|     detect-libc "^2.0.1" | ||||
|  | ||||
| nwsapi@^2.2.16: | ||||
|   version "2.2.20" | ||||
|   resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.20.tgz#22e53253c61e7b0e7e93cef42c891154bcca11ef" | ||||
|   integrity sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA== | ||||
|  | ||||
| object-inspect@^1.13.3: | ||||
|   version "1.13.4" | ||||
|   resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" | ||||
| @@ -2228,10 +2178,10 @@ parse-cache-control@^1.0.1: | ||||
|   resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e" | ||||
|   integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg== | ||||
|  | ||||
| parse5@^8.0.0: | ||||
|   version "8.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/parse5/-/parse5-8.0.0.tgz#aceb267f6b15f9b6e6ba9e35bfdd481fc2167b12" | ||||
|   integrity sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA== | ||||
| parse5@^7.2.1: | ||||
|   version "7.3.0" | ||||
|   resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05" | ||||
|   integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw== | ||||
|   dependencies: | ||||
|     entities "^6.0.0" | ||||
|  | ||||
| @@ -2316,11 +2266,6 @@ readable-stream@^2.2.2: | ||||
|     string_decoder "~1.1.1" | ||||
|     util-deprecate "~1.0.1" | ||||
|  | ||||
| require-from-string@^2.0.2: | ||||
|   version "2.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" | ||||
|   integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== | ||||
|  | ||||
| robust-predicates@^2.0.4: | ||||
|   version "2.0.4" | ||||
|   resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-2.0.4.tgz#0a2367a93abd99676d075981707f29cfb402248b" | ||||
| @@ -2408,11 +2353,6 @@ skmeans@0.9.7: | ||||
|   resolved "https://registry.yarnpkg.com/skmeans/-/skmeans-0.9.7.tgz#72670cebb728508f56e29c0e10d11e623529ce5d" | ||||
|   integrity sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg== | ||||
|  | ||||
| source-map-js@^1.0.1: | ||||
|   version "1.2.1" | ||||
|   resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" | ||||
|   integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== | ||||
|  | ||||
| splaytree-ts@^1.0.2: | ||||
|   version "1.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/splaytree-ts/-/splaytree-ts-1.0.2.tgz#34963704587aff45eaa09c24713f552bbf56e8f0" | ||||
| @@ -2437,26 +2377,26 @@ symbol-tree@^3.2.4: | ||||
|   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" | ||||
|   integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== | ||||
|  | ||||
| sync-request@6.1.0: | ||||
|   version "6.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-6.1.0.tgz#e96217565b5e50bbffe179868ba75532fb597e68" | ||||
|   integrity sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw== | ||||
| sync-request@5.0.0: | ||||
|   version "5.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-5.0.0.tgz#b815d5c6e16193392ec4139a4881db5e90e88676" | ||||
|   integrity sha512-NKhEA4WacR3mRBIFz1niXrIUTrUVFtP2spzrLMINangebvJ/EFyVv+LMJKvVl6UIrJM4Fburnnj91lRnqb4WkA== | ||||
|   dependencies: | ||||
|     http-response-object "^3.0.1" | ||||
|     sync-rpc "^1.2.1" | ||||
|     then-request "^6.0.0" | ||||
|     sync-rpc "^1.2.0" | ||||
|     then-request "^5.0.0" | ||||
|  | ||||
| sync-rpc@^1.2.1: | ||||
| sync-rpc@^1.2.0: | ||||
|   version "1.3.6" | ||||
|   resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7" | ||||
|   integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw== | ||||
|   dependencies: | ||||
|     get-port "^3.1.0" | ||||
|  | ||||
| then-request@^6.0.0: | ||||
|   version "6.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/then-request/-/then-request-6.0.2.tgz#ec18dd8b5ca43aaee5cb92f7e4c1630e950d4f0c" | ||||
|   integrity sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA== | ||||
| then-request@^5.0.0: | ||||
|   version "5.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/then-request/-/then-request-5.0.0.tgz#7a23f616799597621de8cfebc77c61fd28c00d64" | ||||
|   integrity sha512-A3uIVLD33SAvB10PfsxLuQBMV8GVC/6xKBMPOvkJchi6251e5AMJ+Yy+RVKsVsnj0iYNhN2E5SkNSi58H19wsw== | ||||
|   dependencies: | ||||
|     "@types/concat-stream" "^1.6.0" | ||||
|     "@types/form-data" "0.0.33" | ||||
| @@ -2465,7 +2405,7 @@ then-request@^6.0.0: | ||||
|     caseless "~0.12.0" | ||||
|     concat-stream "^1.6.0" | ||||
|     form-data "^2.2.0" | ||||
|     http-basic "^8.1.1" | ||||
|     http-basic "^6.0.0" | ||||
|     http-response-object "^3.0.1" | ||||
|     promise "^8.0.0" | ||||
|     qs "^6.4.0" | ||||
| @@ -2480,17 +2420,17 @@ tinyqueue@^2.0.0, tinyqueue@^2.0.3: | ||||
|   resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08" | ||||
|   integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA== | ||||
|  | ||||
| tldts-core@^7.0.17: | ||||
|   version "7.0.17" | ||||
|   resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.0.17.tgz#dadfee3750dd272ed219d7367beb7cbb2ff29eb8" | ||||
|   integrity sha512-DieYoGrP78PWKsrXr8MZwtQ7GLCUeLxihtjC1jZsW1DnvSMdKPitJSe8OSYDM2u5H6g3kWJZpePqkp43TfLh0g== | ||||
| tldts-core@^6.1.86: | ||||
|   version "6.1.86" | ||||
|   resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.86.tgz#a93e6ed9d505cb54c542ce43feb14c73913265d8" | ||||
|   integrity sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA== | ||||
|  | ||||
| tldts@^7.0.5: | ||||
|   version "7.0.17" | ||||
|   resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.0.17.tgz#a6cdc067b9e80ea05f3be471c0ea410688cc78b2" | ||||
|   integrity sha512-Y1KQBgDd/NUc+LfOtKS6mNsC9CCaH+m2P1RoIZy7RAPo3C3/t8X45+zgut31cRZtZ3xKPjfn3TkGTrctC2TQIQ== | ||||
| tldts@^6.1.32: | ||||
|   version "6.1.86" | ||||
|   resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.86.tgz#087e0555b31b9725ee48ca7e77edc56115cd82f7" | ||||
|   integrity sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ== | ||||
|   dependencies: | ||||
|     tldts-core "^7.0.17" | ||||
|     tldts-core "^6.1.86" | ||||
|  | ||||
| topojson-client@3.x: | ||||
|   version "3.1.0" | ||||
| @@ -2506,17 +2446,17 @@ topojson-server@3.x: | ||||
|   dependencies: | ||||
|     commander "2" | ||||
|  | ||||
| tough-cookie@^6.0.0: | ||||
|   version "6.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-6.0.0.tgz#11e418b7864a2c0d874702bc8ce0f011261940e5" | ||||
|   integrity sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w== | ||||
| tough-cookie@^5.1.1: | ||||
|   version "5.1.2" | ||||
|   resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.2.tgz#66d774b4a1d9e12dc75089725af3ac75ec31bed7" | ||||
|   integrity sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A== | ||||
|   dependencies: | ||||
|     tldts "^7.0.5" | ||||
|     tldts "^6.1.32" | ||||
|  | ||||
| tr46@^6.0.0: | ||||
|   version "6.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/tr46/-/tr46-6.0.0.tgz#f5a1ae546a0adb32a277a2278d0d17fa2f9093e6" | ||||
|   integrity sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw== | ||||
| tr46@^5.1.0: | ||||
|   version "5.1.1" | ||||
|   resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.1.1.tgz#96ae867cddb8fdb64a49cc3059a8d428bcf238ca" | ||||
|   integrity sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw== | ||||
|   dependencies: | ||||
|     punycode "^2.3.1" | ||||
|  | ||||
| @@ -2535,10 +2475,10 @@ typedarray@^0.0.6: | ||||
|   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" | ||||
|   integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== | ||||
|  | ||||
| undici-types@~7.16.0: | ||||
|   version "7.16.0" | ||||
|   resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" | ||||
|   integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== | ||||
| undici-types@~7.8.0: | ||||
|   version "7.8.0" | ||||
|   resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.8.0.tgz#de00b85b710c54122e44fbfd911f8d70174cd294" | ||||
|   integrity sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw== | ||||
|  | ||||
| util-deprecate@~1.0.1: | ||||
|   version "1.0.2" | ||||
| @@ -2552,10 +2492,10 @@ w3c-xmlserializer@^5.0.0: | ||||
|   dependencies: | ||||
|     xml-name-validator "^5.0.0" | ||||
|  | ||||
| webidl-conversions@^8.0.0: | ||||
|   version "8.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-8.0.0.tgz#821c92aa4f88d88a31264d887e244cb9655690c6" | ||||
|   integrity sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA== | ||||
| webidl-conversions@^7.0.0: | ||||
|   version "7.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" | ||||
|   integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== | ||||
|  | ||||
| whatwg-encoding@^3.1.1: | ||||
|   version "3.1.1" | ||||
| @@ -2569,18 +2509,18 @@ whatwg-mimetype@^4.0.0: | ||||
|   resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" | ||||
|   integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== | ||||
|  | ||||
| whatwg-url@^15.0.0, whatwg-url@^15.1.0: | ||||
|   version "15.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-15.1.0.tgz#5c433439b9a5789eeb3806bbd0da89a8bd40b8d7" | ||||
|   integrity sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g== | ||||
| whatwg-url@^14.0.0, whatwg-url@^14.1.1: | ||||
|   version "14.2.0" | ||||
|   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.2.0.tgz#4ee02d5d725155dae004f6ae95c73e7ef5d95663" | ||||
|   integrity sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw== | ||||
|   dependencies: | ||||
|     tr46 "^6.0.0" | ||||
|     webidl-conversions "^8.0.0" | ||||
|     tr46 "^5.1.0" | ||||
|     webidl-conversions "^7.0.0" | ||||
|  | ||||
| ws@^8.18.3: | ||||
|   version "8.18.3" | ||||
|   resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" | ||||
|   integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== | ||||
| ws@^8.18.0: | ||||
|   version "8.18.2" | ||||
|   resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.2.tgz#42738b2be57ced85f46154320aabb51ab003705a" | ||||
|   integrity sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ== | ||||
|  | ||||
| xml-name-validator@^5.0.0: | ||||
|   version "5.0.0" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user