Compare commits
	
		
			68 Commits
		
	
	
		
			9643d0ebb8
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 14bfaabb11 | |||
|  | d77c5ec391 | ||
| b793ac7a6e | |||
|  | d04e78694c | ||
| d6dd4e6b96 | |||
|  | 44e5c54c77 | ||
| bdb49cd085 | |||
|  | e76aca71eb | ||
| 98321b4c3b | |||
| b082fc44dc | |||
|  | 0776814283 | ||
|  | 3a9718d1a7 | ||
| d9ef921a43 | |||
| c415e8a829 | |||
|  | daa291d5de | ||
|  | d2987ebe1e | ||
| ed32fd1183 | |||
|  | 83b1ec9c92 | ||
| 5e982e93ee | |||
|  | 846081536d | ||
| 9f2f7844f9 | |||
|  | 9966bba33c | ||
| ae59b054c9 | |||
|  | 8bed6b040b | ||
| d1933ec661 | |||
|  | b40932624a | ||
| 6c5e727c67 | |||
|  | 6420ae2f13 | ||
| 91b234777d | |||
|  | b1a646f1f2 | ||
| 89134681f3 | |||
|  | 3b670b8ce2 | ||
| b0ba07a7fb | |||
|  | 07adda1d74 | ||
| 6a86d05bad | |||
|  | dc94c465d6 | ||
| 950bfbb42e | |||
|  | 927ac05f03 | ||
| d6d84c6892 | |||
|  | a34c1dbb11 | ||
| 6df13a044b | |||
| 68fb0023da | |||
|  | 686982c096 | ||
|  | 84898895df | ||
|  | 472237ba8f | ||
|  | 2ae7e1fb8c | ||
|  | ed4d751dce | ||
|  | b5274d2113 | ||
|  | d7ad8ebd74 | ||
|  | 43a521de16 | ||
|  | 4cf8c497a8 | ||
|  | fd57b5dee4 | ||
|  | 49b80dc3b9 | ||
|  | c0e3943700 | ||
| f1608179f8 | |||
|  | 86aa888be6 | ||
|  | 9c1374c99f | ||
|  | 6b2f786afe | ||
|  | 2ded750c46 | ||
|  | 686be17fd2 | ||
|  | 7339e16e61 | ||
|  | e43264b327 | ||
|  | 532a34cc7f | ||
|  | 59a02332d0 | ||
|  | 02a83a491f | ||
|  | 0a01aa95f1 | ||
|  | 438eae23b0 | ||
|  | 796faf393e | 
							
								
								
									
										2
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -41,7 +41,7 @@ jobs: | |||||||
|         run: git checkout -B "$BRANCH" |         run: git checkout -B "$BRANCH" | ||||||
|  |  | ||||||
|       - name: set up JDK |       - name: set up JDK | ||||||
|         uses: actions/setup-java@v4 |         uses: actions/setup-java@v5 | ||||||
|         with: |         with: | ||||||
|           java-version: 17 |           java-version: 17 | ||||||
|           distribution: "temurin" |           distribution: "temurin" | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -20,8 +20,6 @@ app/build/ | |||||||
| app/debug/ | app/debug/ | ||||||
| app/release/ | app/release/ | ||||||
| captures/ | captures/ | ||||||
| .externalNativeBuild |  | ||||||
| .cxx |  | ||||||
| local.properties | local.properties | ||||||
| keystore.properties | keystore.properties | ||||||
| key.jks | key.jks | ||||||
| @@ -1,21 +1,24 @@ | |||||||
| plugins { | plugins { | ||||||
|     id 'com.android.application' |     id 'com.android.application' | ||||||
|     id 'org.jetbrains.kotlin.android' |     id 'org.jetbrains.kotlin.android' | ||||||
|     id 'org.jetbrains.kotlin.plugin.serialization' version '2.2.10' |     id 'org.jetbrains.kotlin.plugin.serialization' version '2.2.21' | ||||||
|     id 'com.mikepenz.aboutlibraries.plugin' version '12.2.4' |     id 'org.jetbrains.kotlin.plugin.compose' version '2.2.21' | ||||||
|  |     id 'com.mikepenz.aboutlibraries.plugin' version '13.1.0' | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| android { | android { | ||||||
|     namespace 'net.helcel.beans' |     namespace 'net.helcel.beans' | ||||||
|     compileSdk 34 |     compileSdk 36 | ||||||
|  |  | ||||||
|     defaultConfig { |     defaultConfig { | ||||||
|  |         buildConfigField("String", "APP_NAME", "\"Beans\"") | ||||||
|  |         manifestPlaceholders["APP_NAME"] = "Beans" | ||||||
|         applicationId 'net.helcel.beans' |         applicationId 'net.helcel.beans' | ||||||
|         minSdk 28 |         minSdk 28 | ||||||
|         targetSdk 34 |         targetSdk 36 | ||||||
|         versionCode 2 |         versionCode 4 | ||||||
|         versionName "1.0b" |         versionName "1.1a" | ||||||
|     } |     } | ||||||
|     signingConfigs { |     signingConfigs { | ||||||
|         create("release") { |         create("release") { | ||||||
| @@ -54,17 +57,15 @@ android { | |||||||
|     compileOptions { |     compileOptions { | ||||||
|         coreLibraryDesugaringEnabled true |         coreLibraryDesugaringEnabled true | ||||||
|  |  | ||||||
|         sourceCompatibility JavaVersion.VERSION_17 |         sourceCompatibility JavaVersion.VERSION_21 | ||||||
|         targetCompatibility JavaVersion.VERSION_17 |         targetCompatibility JavaVersion.VERSION_21 | ||||||
|         encoding 'utf-8' |         encoding 'utf-8' | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     kotlinOptions { |  | ||||||
|         jvmTarget = JavaVersion.VERSION_17 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     buildFeatures { |     buildFeatures { | ||||||
|         viewBinding true |         viewBinding true | ||||||
|  |         compose true | ||||||
|  |         buildConfig true | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     dependenciesInfo { |     dependenciesInfo { | ||||||
| @@ -73,21 +74,51 @@ android { | |||||||
|         // Disables dependency metadata when building Android App Bundles. |         // Disables dependency metadata when building Android App Bundles. | ||||||
|         includeInBundle = false |         includeInBundle = false | ||||||
|     } |     } | ||||||
|  |     composeOptions { | ||||||
|  |         kotlinCompilerExtensionVersion = "2.2.20" | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     kotlin { | ||||||
|  |         jvmToolchain(21) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lint { | ||||||
|  |         disable 'UsingMaterialAndMaterial3Libraries' | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
| aboutLibraries { | aboutLibraries { | ||||||
|     exclusionPatterns = [~"androidx.*", ~"com.google.android.*", ~"org.jetbrains.*"] |     library { | ||||||
|     configPath = "config" |         exclusionPatterns = [~"androidx.*", ~"com.google.android.*", ~"org.jetbrains.*"] | ||||||
|  |     } | ||||||
|     excludeFields = ["generated"] |     excludeFields = ["generated"] | ||||||
| } | } | ||||||
|  |  | ||||||
| dependencies { | 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' |     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.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 'com.google.android.material:material:1.13.0' | ||||||
|     implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0' |     implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0' | ||||||
|  |  | ||||||
|     implementation 'com.caverock:androidsvg-aar:1.4' |     implementation 'com.caverock:androidsvg-aar:1.4' | ||||||
|     implementation 'com.github.chrisbanes:PhotoView:2.3.0' |     implementation 'com.github.chrisbanes:PhotoView:2.3.0' | ||||||
|     implementation 'com.mikepenz:aboutlibraries:12.2.4' |  | ||||||
|  |     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' | ||||||
| } | } | ||||||
| @@ -1,52 +1,22 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | <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 |     <application | ||||||
|         android:allowBackup="true" |         android:allowBackup="true" | ||||||
|         android:dataExtractionRules="@xml/data_extraction_rules" |         android:dataExtractionRules="@xml/data_extraction_rules" | ||||||
|         android:fullBackupContent="@xml/backup_rules" |         android:fullBackupContent="@xml/backup_rules" | ||||||
|         android:hardwareAccelerated="false" |         android:hardwareAccelerated="false" | ||||||
|         android:icon="@mipmap/ic_launcher_round" |         android:icon="@mipmap/ic_launcher_round" | ||||||
|         android:label="@string/app_name" |         android:label="${APP_NAME}" | ||||||
|         android:supportsRtl="true" |         android:supportsRtl="true" | ||||||
|         android:theme="@style/Theme.Beans" |         tools:replace="android:allowBackup"> | ||||||
|         tools:replace="android:allowBackup" |  | ||||||
|         tools:targetApi="31"> |  | ||||||
|         <profileable android:shell="true" /> |  | ||||||
|  |  | ||||||
|         <activity |         <activity | ||||||
|             android:name=".activity.MainActivity" |             android:name=".activity.MainScreen" | ||||||
|             android:exported="true"> |             android:exported="true"> | ||||||
|             <intent-filter> |             <intent-filter> | ||||||
|                 <action android:name="android.intent.action.MAIN" /> |                 <action android:name="android.intent.action.MAIN" /> | ||||||
|                 <category android:name="android.intent.category.LAUNCHER" /> |                 <category android:name="android.intent.category.LAUNCHER" /> | ||||||
|             </intent-filter> |             </intent-filter> | ||||||
|         </activity> |         </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> |     </application> | ||||||
|  |  | ||||||
| </manifest> | </manifest> | ||||||
| @@ -1,69 +0,0 @@ | |||||||
| package net.helcel.beans.activity |  | ||||||
|  |  | ||||||
| import android.os.Bundle |  | ||||||
| import android.view.Menu |  | ||||||
| import android.view.MenuItem |  | ||||||
| import androidx.activity.addCallback |  | ||||||
| import androidx.appcompat.app.AppCompatActivity |  | ||||||
| import com.google.android.material.tabs.TabLayoutMediator |  | ||||||
| import net.helcel.beans.R |  | ||||||
| import net.helcel.beans.activity.adapter.ViewPagerAdapter |  | ||||||
| import net.helcel.beans.activity.fragment.EditGroupAddFragment |  | ||||||
| import net.helcel.beans.activity.fragment.EditPlaceFragment |  | ||||||
| import net.helcel.beans.countries.World |  | ||||||
| import net.helcel.beans.databinding.ActivityEditBinding |  | ||||||
| import net.helcel.beans.helper.Data |  | ||||||
| import net.helcel.beans.helper.Settings |  | ||||||
| import net.helcel.beans.helper.Theme.createActionBar |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class EditActivity : AppCompatActivity() { |  | ||||||
|  |  | ||||||
|     private lateinit var _binding: ActivityEditBinding |  | ||||||
|  |  | ||||||
|     override fun onCreate(savedInstanceState: Bundle?) { |  | ||||||
|         super.onCreate(savedInstanceState) |  | ||||||
|         _binding = ActivityEditBinding.inflate(layoutInflater) |  | ||||||
|  |  | ||||||
|         setContentView(_binding.root) |  | ||||||
|         createActionBar(this, getString(R.string.action_edit)) |  | ||||||
|  |  | ||||||
|         val adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, _binding.pager) |  | ||||||
|         _binding.pager.adapter = adapter |  | ||||||
|         adapter.addFragment(null, EditPlaceFragment(World.WWW, adapter)) |  | ||||||
|  |  | ||||||
|         TabLayoutMediator(_binding.tab, _binding.pager) { tab, position -> |  | ||||||
|             tab.text = adapter.getLabel(position) |  | ||||||
|         }.attach() |  | ||||||
|  |  | ||||||
|         onBackPressedDispatcher.addCallback { |  | ||||||
|             if (!adapter.backPressed()) { |  | ||||||
|                 finish() |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun onCreateOptionsMenu(menu: Menu): Boolean { |  | ||||||
|         if (Settings.isSingleGroup(this)) { |  | ||||||
|             menuInflater.inflate(R.menu.menu_edit, menu) |  | ||||||
|         } |  | ||||||
|         return true |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun onOptionsItemSelected(item: MenuItem): Boolean { |  | ||||||
|         when (item.itemId) { |  | ||||||
|             R.id.action_color -> { |  | ||||||
|                 Data.groups.getUniqueEntry()?.let { group -> |  | ||||||
|                     EditGroupAddFragment(group.key, { |  | ||||||
|                         (_binding.pager.adapter as ViewPagerAdapter?)?.refreshColors(group.color) |  | ||||||
|                     }, {}, false).show(supportFragmentManager, "AddColorDialogFragment") |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             else -> finish() |  | ||||||
|         } |  | ||||||
|         return super.onOptionsItemSelected(item) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } |  | ||||||
							
								
								
									
										66
									
								
								app/src/main/java/net/helcel/beans/activity/EditScreen.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								app/src/main/java/net/helcel/beans/activity/EditScreen.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | package net.helcel.beans.activity | ||||||
|  |  | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.layout.Box | ||||||
|  | import androidx.compose.foundation.layout.Column | ||||||
|  | import androidx.compose.foundation.layout.Spacer | ||||||
|  | import androidx.compose.foundation.layout.fillMaxSize | ||||||
|  | import androidx.compose.foundation.layout.fillMaxWidth | ||||||
|  | import androidx.compose.foundation.layout.height | ||||||
|  | import androidx.compose.foundation.layout.padding | ||||||
|  | import androidx.compose.material.Icon | ||||||
|  | import androidx.compose.material.IconButton | ||||||
|  | import androidx.compose.material.MaterialTheme | ||||||
|  | import androidx.compose.material.Scaffold | ||||||
|  | import androidx.compose.material.Text | ||||||
|  | import androidx.compose.material.TopAppBar | ||||||
|  | import androidx.compose.material.icons.Icons | ||||||
|  | import androidx.compose.material.icons.automirrored.filled.ArrowBack | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
|  | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
|  | import net.helcel.beans.R | ||||||
|  | import net.helcel.beans.activity.sub.EditPlaceScreen | ||||||
|  | import net.helcel.beans.countries.World | ||||||
|  |  | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun EditScreen(onExit:()->Unit = {}) { | ||||||
|  |     SysTheme { | ||||||
|  |         Scaffold( | ||||||
|  |             topBar = { | ||||||
|  |                 TopAppBar( | ||||||
|  |                     title = { Text(stringResource(R.string.action_edit)) }, | ||||||
|  |                     navigationIcon = { | ||||||
|  |                         IconButton(onClick = onExit) { | ||||||
|  |                             Icon( | ||||||
|  |                                 Icons.AutoMirrored.Filled.ArrowBack, | ||||||
|  |                                 contentDescription = null | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 ) | ||||||
|  |             }, | ||||||
|  |  | ||||||
|  |             ) { innerPadding -> | ||||||
|  |             Box( | ||||||
|  |                 modifier = Modifier | ||||||
|  |                     .fillMaxSize() | ||||||
|  |                     .background(MaterialTheme.colors.background) | ||||||
|  |                     .padding(innerPadding) // ensures content is below the app bar | ||||||
|  |             ) { | ||||||
|  |                 Column { | ||||||
|  |                     Spacer( | ||||||
|  |                         modifier = Modifier.height(2.dp).fillMaxWidth() | ||||||
|  |                             .background(MaterialTheme.colors.background) | ||||||
|  |                     ) | ||||||
|  |                     EditPlaceScreen(World.WWW, onExit) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -1,75 +0,0 @@ | |||||||
| package net.helcel.beans.activity |  | ||||||
|  |  | ||||||
| import android.content.Intent |  | ||||||
| import android.graphics.drawable.PictureDrawable |  | ||||||
| import android.os.Bundle |  | ||||||
| import android.view.Menu |  | ||||||
| import android.view.MenuItem |  | ||||||
| import androidx.appcompat.app.AppCompatActivity |  | ||||||
| import com.caverock.androidsvg.RenderOptions |  | ||||||
| import net.helcel.beans.R |  | ||||||
| import net.helcel.beans.countries.GeoLocImporter |  | ||||||
| import net.helcel.beans.databinding.ActivityMainBinding |  | ||||||
| import net.helcel.beans.helper.Data |  | ||||||
| import net.helcel.beans.helper.Settings |  | ||||||
| import net.helcel.beans.svg.CSSWrapper |  | ||||||
| import net.helcel.beans.svg.SVGWrapper |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class MainActivity : AppCompatActivity() { |  | ||||||
|     private lateinit var _binding: ActivityMainBinding |  | ||||||
|  |  | ||||||
|     private lateinit var psvg: SVGWrapper |  | ||||||
|     private lateinit var css: CSSWrapper |  | ||||||
|  |  | ||||||
|     override fun onRestart() { |  | ||||||
|         refreshProjection() |  | ||||||
|         refreshMap() |  | ||||||
|         super.onRestart() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun onCreateOptionsMenu(menu: Menu): Boolean { |  | ||||||
|         menuInflater.inflate(R.menu.menu_main, menu) |  | ||||||
|         return true |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun onOptionsItemSelected(item: MenuItem): Boolean { |  | ||||||
|         val d = when (item.itemId) { |  | ||||||
|             R.id.action_edit -> EditActivity::class.java |  | ||||||
|             R.id.action_stats -> StatsActivity::class.java |  | ||||||
|             R.id.action_settings -> SettingsActivity::class.java |  | ||||||
|             else -> throw Exception("Non Existent Menu Item") |  | ||||||
|         } |  | ||||||
|         startActivity(Intent(this@MainActivity, d)) |  | ||||||
|         return true |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun onCreate(savedInstanceState: Bundle?) { |  | ||||||
|         super.onCreate(savedInstanceState) |  | ||||||
|         _binding = ActivityMainBinding.inflate(layoutInflater) |  | ||||||
|         Settings.start(this) |  | ||||||
|  |  | ||||||
|         setContentView(_binding.root) |  | ||||||
|  |  | ||||||
|         _binding.photoView.minimumScale = 1f |  | ||||||
|         _binding.photoView.maximumScale = 40f |  | ||||||
|  |  | ||||||
|         GeoLocImporter.importStates(this) |  | ||||||
|         Data.loadData(this, Int.MIN_VALUE) |  | ||||||
|  |  | ||||||
|         refreshProjection() |  | ||||||
|         refreshMap() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private fun refreshMap() { |  | ||||||
|         val opt: RenderOptions = RenderOptions.create() |  | ||||||
|         opt.css(css.get()) |  | ||||||
|         _binding.photoView.setImageDrawable(PictureDrawable(psvg.get()?.renderToPicture(opt))) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun refreshProjection() { |  | ||||||
|         psvg = SVGWrapper(this) |  | ||||||
|         css = CSSWrapper(this) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
							
								
								
									
										124
									
								
								app/src/main/java/net/helcel/beans/activity/MainScreen.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								app/src/main/java/net/helcel/beans/activity/MainScreen.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | |||||||
|  | package net.helcel.beans.activity | ||||||
|  |  | ||||||
|  | import android.graphics.drawable.PictureDrawable | ||||||
|  | import android.os.Bundle | ||||||
|  | import android.widget.ImageView | ||||||
|  | import androidx.activity.ComponentActivity | ||||||
|  | import androidx.activity.compose.setContent | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.layout.Box | ||||||
|  | import androidx.compose.foundation.layout.fillMaxSize | ||||||
|  | import androidx.compose.foundation.layout.padding | ||||||
|  | import androidx.compose.material.Icon | ||||||
|  | import androidx.compose.material.IconButton | ||||||
|  | import androidx.compose.material.MaterialTheme | ||||||
|  | import androidx.compose.material.Scaffold | ||||||
|  | import androidx.compose.material.Text | ||||||
|  | import androidx.compose.material.TopAppBar | ||||||
|  | import androidx.compose.material.icons.Icons | ||||||
|  | import androidx.compose.material.icons.filled.DateRange | ||||||
|  | import androidx.compose.material.icons.filled.Edit | ||||||
|  | import androidx.compose.material.icons.filled.Percent | ||||||
|  | import androidx.compose.material.icons.filled.Settings | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.runtime.remember | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.viewinterop.AndroidView | ||||||
|  | import androidx.navigation.NavHostController | ||||||
|  | import androidx.navigation.compose.NavHost | ||||||
|  | import androidx.navigation.compose.composable | ||||||
|  | import androidx.navigation.compose.rememberNavController | ||||||
|  | import com.caverock.androidsvg.RenderOptions | ||||||
|  | import com.github.chrisbanes.photoview.PhotoView | ||||||
|  | import net.helcel.beans.BuildConfig | ||||||
|  | import net.helcel.beans.countries.GeoLocImporter | ||||||
|  | import net.helcel.beans.helper.Data | ||||||
|  | import net.helcel.beans.helper.Settings | ||||||
|  | import net.helcel.beans.svg.CSSWrapper | ||||||
|  | import net.helcel.beans.svg.SVGWrapper | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class MainScreen : ComponentActivity() { | ||||||
|  |  | ||||||
|  |     private lateinit var psvg: SVGWrapper | ||||||
|  |     private lateinit var css: CSSWrapper | ||||||
|  |  | ||||||
|  |     override fun onCreate(savedInstanceState: Bundle?) { | ||||||
|  |         super.onCreate(savedInstanceState) | ||||||
|  |         actionBar?.hide() | ||||||
|  |         Settings.start(this) | ||||||
|  |         GeoLocImporter.importStates(this) | ||||||
|  |         Data.loadData(this, Int.MIN_VALUE) | ||||||
|  |  | ||||||
|  |         setContent { | ||||||
|  |             SysTheme { | ||||||
|  |                 Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.background)) { | ||||||
|  |                     AppNavHost(psvg, css) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         refreshProjection() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Composable | ||||||
|  |     fun AppNavHost(psvg: SVGWrapper, css: CSSWrapper) { | ||||||
|  |         val navController = rememberNavController() | ||||||
|  |         NavHost(navController, startDestination = "main") { | ||||||
|  |             composable("main") { MainScreenC(psvg,css, navController) } | ||||||
|  |             composable("settings") { SettingsMainScreen { navController.navigate("main")} } | ||||||
|  |             composable("edit") { EditScreen { navController.navigate("main") } } | ||||||
|  |             composable("stats") { StatsScreen { navController.navigate("main") } } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Composable | ||||||
|  |     fun MainScreenC(psvg: SVGWrapper,css: CSSWrapper, nav: NavHostController){ | ||||||
|  |         SysTheme { | ||||||
|  |             Scaffold( | ||||||
|  |                 topBar = { | ||||||
|  |                     TopAppBar( | ||||||
|  |                         title = { Text(BuildConfig.APP_NAME) }, | ||||||
|  |                         actions = { | ||||||
|  |                             IconButton(onClick = { nav.navigate("edit") }) { | ||||||
|  |                                 Icon(Icons.Default.Edit, contentDescription = "Edit") | ||||||
|  |                             } | ||||||
|  |                             IconButton(onClick = {  nav.navigate("stats") }){ | ||||||
|  |                                 Icon(Icons.Default.Percent, contentDescription = "Stats") | ||||||
|  |                             } | ||||||
|  |                             IconButton(onClick = { nav.navigate("settings") }) { | ||||||
|  |                                 Icon(Icons.Default.Settings, contentDescription = "Settings") | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |             ) { innerPadding -> | ||||||
|  |                 Box(modifier = Modifier.padding(innerPadding)) { | ||||||
|  |                     MapScreen(psvg, css) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Composable | ||||||
|  |     fun MapScreen(psvg: SVGWrapper, css: CSSWrapper) { | ||||||
|  |         Box { | ||||||
|  |             val opt: RenderOptions = RenderOptions.create() | ||||||
|  |             opt.css(css.get()) | ||||||
|  |             val drawable = remember(psvg, css) { | ||||||
|  |                 PictureDrawable(psvg.get()?.renderToPicture(opt)) | ||||||
|  |             } | ||||||
|  |             AndroidView(factory = { ctx -> | ||||||
|  |                 PhotoView(ctx).apply { | ||||||
|  |                     setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null) | ||||||
|  |                     setImageDrawable(drawable) | ||||||
|  |                     scaleType = ImageView.ScaleType.FIT_CENTER | ||||||
|  |                 } | ||||||
|  |             }, modifier = Modifier.fillMaxSize()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun refreshProjection() { | ||||||
|  |         psvg = SVGWrapper(this) | ||||||
|  |         css = CSSWrapper(this) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,63 +0,0 @@ | |||||||
| package net.helcel.beans.activity |  | ||||||
|  |  | ||||||
| import android.os.Bundle |  | ||||||
| import android.view.MenuItem |  | ||||||
| import androidx.appcompat.app.AppCompatActivity |  | ||||||
| import net.helcel.beans.R |  | ||||||
| import net.helcel.beans.activity.fragment.AboutFragment |  | ||||||
| import net.helcel.beans.activity.fragment.LicenseFragment |  | ||||||
| import net.helcel.beans.activity.fragment.SettingsFragment |  | ||||||
| import net.helcel.beans.helper.Theme.createActionBar |  | ||||||
|  |  | ||||||
| class SettingsActivity : AppCompatActivity() { |  | ||||||
|  |  | ||||||
|     override fun onCreate(savedInstanceState: Bundle?) { |  | ||||||
|         super.onCreate(savedInstanceState) |  | ||||||
|  |  | ||||||
|         setContentView(R.layout.activity_settings) |  | ||||||
|         createActionBar(this, getString(R.string.action_settings)) |  | ||||||
|  |  | ||||||
|         // Populate activity with settings fragment |  | ||||||
|         supportFragmentManager.beginTransaction() |  | ||||||
|             .replace(R.id.fragment_view, SettingsFragment(), getString(R.string.action_settings)) |  | ||||||
|             .commit() |  | ||||||
|  |  | ||||||
|         // Change title in action bar according to current fragment |  | ||||||
|         supportFragmentManager.addFragmentOnAttachListener { _, _ -> |  | ||||||
|             supportActionBar?.title = |  | ||||||
|                 supportFragmentManager.findFragmentById(R.id.fragment_view).let { |  | ||||||
|                     when (it) { |  | ||||||
|                         is LicenseFragment -> getString(R.string.licenses) |  | ||||||
|                         is AboutFragment -> getString(R.string.about) |  | ||||||
|                         else -> getString(R.string.action_settings) |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun onOptionsItemSelected(item: MenuItem): Boolean { |  | ||||||
|         // Configure on back pressed |  | ||||||
|         supportFragmentManager.findFragmentById(R.id.fragment_view).let { |  | ||||||
|             when (it) { |  | ||||||
|                 is LicenseFragment, is AboutFragment -> { |  | ||||||
|                     supportFragmentManager.beginTransaction() |  | ||||||
|                         .remove(it) |  | ||||||
|                         .commit() |  | ||||||
|                     supportFragmentManager.beginTransaction() |  | ||||||
|                         .replace( |  | ||||||
|                             R.id.fragment_view, |  | ||||||
|                             SettingsFragment(), |  | ||||||
|                             getString(R.string.action_settings) |  | ||||||
|                         ) |  | ||||||
|                         .commit() |  | ||||||
|                     supportActionBar?.title = getString(R.string.action_settings) |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 else -> { |  | ||||||
|                     finish() |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return super.onOptionsItemSelected(item) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										381
									
								
								app/src/main/java/net/helcel/beans/activity/SettingsScreen.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										381
									
								
								app/src/main/java/net/helcel/beans/activity/SettingsScreen.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,381 @@ | |||||||
|  | package net.helcel.beans.activity | ||||||
|  |  | ||||||
|  | import android.os.Build | ||||||
|  | import androidx.activity.compose.rememberLauncherForActivityResult | ||||||
|  | import androidx.activity.result.contract.ActivityResultContracts | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.clickable | ||||||
|  | import androidx.compose.foundation.isSystemInDarkTheme | ||||||
|  | import androidx.compose.foundation.layout.Box | ||||||
|  | import androidx.compose.foundation.layout.Column | ||||||
|  | import androidx.compose.foundation.layout.Row | ||||||
|  | import androidx.compose.foundation.layout.Spacer | ||||||
|  | import androidx.compose.foundation.layout.fillMaxSize | ||||||
|  | import androidx.compose.foundation.layout.fillMaxWidth | ||||||
|  | import androidx.compose.foundation.layout.height | ||||||
|  | import androidx.compose.foundation.layout.padding | ||||||
|  | import androidx.compose.foundation.layout.size | ||||||
|  | import androidx.compose.foundation.lazy.LazyColumn | ||||||
|  | import androidx.compose.foundation.shape.CornerSize | ||||||
|  | import androidx.compose.foundation.shape.RoundedCornerShape | ||||||
|  | import androidx.compose.material.Button | ||||||
|  | import androidx.compose.material.CircularProgressIndicator | ||||||
|  | import androidx.compose.material.Colors | ||||||
|  | import androidx.compose.material.Icon | ||||||
|  | import androidx.compose.material.IconButton | ||||||
|  | import androidx.compose.material.MaterialTheme | ||||||
|  | import androidx.compose.material.RadioButton | ||||||
|  | import androidx.compose.material.Scaffold | ||||||
|  | import androidx.compose.material.Text | ||||||
|  | import androidx.compose.material.TopAppBar | ||||||
|  | import androidx.compose.material.icons.Icons | ||||||
|  | import androidx.compose.material.icons.automirrored.filled.ArrowBack | ||||||
|  | import androidx.compose.material3.HorizontalDivider | ||||||
|  | import androidx.compose.material3.darkColorScheme | ||||||
|  | import androidx.compose.material3.dynamicDarkColorScheme | ||||||
|  | import androidx.compose.material3.dynamicLightColorScheme | ||||||
|  | import androidx.compose.material3.lightColorScheme | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.runtime.getValue | ||||||
|  | import androidx.compose.runtime.mutableStateOf | ||||||
|  | import androidx.compose.runtime.remember | ||||||
|  | import androidx.compose.runtime.rememberCoroutineScope | ||||||
|  | import androidx.compose.runtime.setValue | ||||||
|  | import androidx.compose.ui.Alignment | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.platform.LocalContext | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
|  | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
|  | import androidx.compose.ui.window.Dialog | ||||||
|  | import androidx.preference.PreferenceManager | ||||||
|  | import net.helcel.beans.R | ||||||
|  | import net.helcel.beans.countries.GeoLocImporter | ||||||
|  | import net.helcel.beans.helper.Settings | ||||||
|  | import androidx.core.content.edit | ||||||
|  | import androidx.navigation.NavHostController | ||||||
|  | import androidx.navigation.compose.NavHost | ||||||
|  | import androidx.navigation.compose.composable | ||||||
|  | import androidx.navigation.compose.rememberNavController | ||||||
|  | import kotlinx.coroutines.Dispatchers | ||||||
|  | import kotlinx.coroutines.launch | ||||||
|  | import kotlinx.coroutines.withContext | ||||||
|  | import net.helcel.beans.activity.sub.AboutScreen | ||||||
|  | import net.helcel.beans.activity.sub.EditPlaceColorDialog | ||||||
|  | import net.helcel.beans.activity.sub.EditPlaceDialog | ||||||
|  | import net.helcel.beans.activity.sub.LicenseScreen | ||||||
|  | import net.helcel.beans.helper.Data | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun SysTheme( | ||||||
|  |     content: @Composable () -> Unit | ||||||
|  | ) { | ||||||
|  |     val context = LocalContext.current | ||||||
|  |     val prefs = PreferenceManager.getDefaultSharedPreferences(context) | ||||||
|  |     val themeKey = prefs.getString(stringResource(R.string.key_theme), stringResource(R.string.system)) | ||||||
|  |     val darkTheme = when (themeKey) { | ||||||
|  |         stringResource(R.string.system) -> isSystemInDarkTheme() | ||||||
|  |         stringResource(R.string.light) -> false | ||||||
|  |         stringResource(R.string.dark) -> true | ||||||
|  |         else -> isSystemInDarkTheme() | ||||||
|  |     } | ||||||
|  |     val colorScheme = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { | ||||||
|  |         if(darkTheme) dynamicDarkColorScheme(LocalContext.current ) else dynamicLightColorScheme(LocalContext.current ) | ||||||
|  |     } else { | ||||||
|  |         if(darkTheme) darkColorScheme() else lightColorScheme() | ||||||
|  |     } | ||||||
|  |     val m2colors = Colors( | ||||||
|  |         primary = colorScheme.primary, | ||||||
|  |         primaryVariant = colorScheme.primaryContainer, | ||||||
|  |         secondary = colorScheme.secondary, | ||||||
|  |         background = colorScheme.background, | ||||||
|  |         surface = colorScheme.surface, | ||||||
|  |         onPrimary = colorScheme.onPrimary, | ||||||
|  |         onSecondary = colorScheme.onSecondary, | ||||||
|  |         onBackground = colorScheme.onBackground, | ||||||
|  |         onSurface = colorScheme.onSurface, | ||||||
|  |         secondaryVariant = colorScheme.secondary, | ||||||
|  |         error = colorScheme.error, | ||||||
|  |         onError = colorScheme.onError, | ||||||
|  |         isLight = !darkTheme, | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     MaterialTheme( | ||||||
|  |         colors = m2colors, | ||||||
|  |         content = content | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun settingsNav(): NavHostController { | ||||||
|  |     val navController = rememberNavController() | ||||||
|  |     NavHost(navController, startDestination= "settings"){ | ||||||
|  |         composable("settings"){SettingsScreen(navController)} | ||||||
|  |         composable("licenses"){ LicenseScreen() } | ||||||
|  |         composable("about"){ AboutScreen() } | ||||||
|  |     } | ||||||
|  |     return navController | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun SettingsMainScreen(onExit: ()->Unit = {}) { | ||||||
|  |     val nav: NavHostController = settingsNav() | ||||||
|  |     SysTheme { | ||||||
|  |         Scaffold( | ||||||
|  |             topBar = { | ||||||
|  |                 TopAppBar( | ||||||
|  |                     title = { Text(stringResource(R.string.action_settings)) }, | ||||||
|  |                     navigationIcon = { | ||||||
|  |                         IconButton(onClick = { | ||||||
|  |                             if(!nav.popBackStack()) | ||||||
|  |                                 onExit() | ||||||
|  |                         }) { | ||||||
|  |                             Icon( | ||||||
|  |                                 Icons.AutoMirrored.Filled.ArrowBack, | ||||||
|  |                                 contentDescription = null | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         ) { innerPadding -> | ||||||
|  |             Box(modifier = Modifier.padding(innerPadding)) { | ||||||
|  |                 SettingsScreen() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun SettingsScreen(navController: NavHostController = settingsNav()) { | ||||||
|  |     val context = LocalContext.current | ||||||
|  |     val prefs = PreferenceManager.getDefaultSharedPreferences(context) | ||||||
|  |     var showEdit by remember { mutableStateOf(false) } | ||||||
|  |  | ||||||
|  |     var theme by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_theme), context.getString(R.string.system))!!) } | ||||||
|  |     var projection by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_projection), "default")!!) } | ||||||
|  |     var groups by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_group), context.getString(R.string.off))!!) } | ||||||
|  |  | ||||||
|  |         if(showEdit) | ||||||
|  |             EditPlaceDialog(true) { | ||||||
|  |                 showEdit = false | ||||||
|  |                 val g = Data.selected_group | ||||||
|  |                 if (it && g != null) | ||||||
|  |                     Data.visits.reassignAllVisitedToGroup(g.key) | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         LazyColumn( | ||||||
|  |             modifier = Modifier | ||||||
|  |                 .fillMaxSize() | ||||||
|  |                 .padding(16.dp) | ||||||
|  |                 .background(MaterialTheme.colors.background) | ||||||
|  |         ) { | ||||||
|  |             item { | ||||||
|  |                 Text( | ||||||
|  |                     "Theme", style = MaterialTheme.typography.h6, | ||||||
|  |                     color = MaterialTheme.colors.onBackground, | ||||||
|  |                 ) | ||||||
|  |                 MultiPreference(arrayOf(stringResource(R.string.system),stringResource(R.string.light),stringResource(R.string.dark)), theme) { newTheme -> | ||||||
|  |                     theme = newTheme | ||||||
|  |                     prefs.edit { putString(context.getString(R.string.key_theme), newTheme) } | ||||||
|  |                 } | ||||||
|  |                 HorizontalDivider() | ||||||
|  |             } | ||||||
|  |             item { | ||||||
|  |                 Text( | ||||||
|  |                     "Map Projection", | ||||||
|  |                     style = MaterialTheme.typography.h6, | ||||||
|  |                     color = MaterialTheme.colors.onBackground, | ||||||
|  |                     modifier = Modifier.padding(top = 16.dp) | ||||||
|  |                 ) | ||||||
|  |                 MultiPreference(arrayOf(stringResource(R.string.mercator), stringResource(R.string.azimuthalequidistant)), projection) { newProj -> | ||||||
|  |                     projection = newProj | ||||||
|  |                     prefs.edit { putString(context.getString(R.string.key_projection), newProj) } | ||||||
|  |                     Settings.refreshProjection() | ||||||
|  |                 } | ||||||
|  |                 HorizontalDivider() | ||||||
|  |             } | ||||||
|  |             item { | ||||||
|  |                 Text( | ||||||
|  |                     "Groups", | ||||||
|  |                     style = MaterialTheme.typography.h6, | ||||||
|  |                     color = MaterialTheme.colors.onBackground, | ||||||
|  |                     modifier = Modifier.padding(top = 16.dp) | ||||||
|  |                 ) | ||||||
|  |                 var showDialog by remember{mutableStateOf(false)} | ||||||
|  |                 if(showDialog){ | ||||||
|  |                     EditPlaceColorDialog( | ||||||
|  |                         deleteMode = true, | ||||||
|  |                         onDismiss = { | ||||||
|  |                             val g = Data.selected_group | ||||||
|  |                             if (g != null) | ||||||
|  |                                 Data.visits.reassignAllVisitedToGroup(g.key) | ||||||
|  |                             showDialog = false | ||||||
|  |                         }) | ||||||
|  |                 } | ||||||
|  |                 MultiPreference( | ||||||
|  |                     arrayOf(stringResource(R.string.on), stringResource(R.string.off)), | ||||||
|  |                     groups | ||||||
|  |                 ) { key -> | ||||||
|  |                     if (key == context.getString(R.string.off)) { | ||||||
|  |                         showDialog=true | ||||||
|  |                     } | ||||||
|  |                     groups = key | ||||||
|  |                     prefs.edit { putString(context.getString(R.string.key_group), key) } | ||||||
|  |                 } | ||||||
|  |                 HorizontalDivider() | ||||||
|  |             } | ||||||
|  |             item { | ||||||
|  |                 Text( | ||||||
|  |                     text = "Regional", | ||||||
|  |                     style = MaterialTheme.typography.h6, | ||||||
|  |                     color = MaterialTheme.colors.onBackground, | ||||||
|  |                     modifier = Modifier | ||||||
|  |                         .padding(top = 16.dp) | ||||||
|  |                         .clickable(onClick = {}), | ||||||
|  |                 ) | ||||||
|  |                 RegionalScreen() | ||||||
|  |                 HorizontalDivider() | ||||||
|  |             } | ||||||
|  |             item{ | ||||||
|  |                 val launcher = rememberLauncherForActivityResult( | ||||||
|  |                 contract = ActivityResultContracts.OpenDocument(), | ||||||
|  |                 onResult = { uri -> Data.doImport(context, uri)   } | ||||||
|  |                 ) | ||||||
|  |                 Row( | ||||||
|  |                     modifier = Modifier.fillMaxWidth() | ||||||
|  |                 ) { | ||||||
|  |                     Button(onClick = { | ||||||
|  |                         launcher.launch(arrayOf("*/*")) | ||||||
|  |                     }, modifier = Modifier | ||||||
|  |                         .fillMaxWidth(fraction = 0.4f) | ||||||
|  |                         .padding(vertical = 8.dp)) { | ||||||
|  |                         Text("Import") | ||||||
|  |                     } | ||||||
|  |                     Spacer( | ||||||
|  |                         modifier = Modifier.fillMaxWidth(0.4f) | ||||||
|  |                     ) | ||||||
|  |                     Button(onClick = { | ||||||
|  |                         Data.doExport(context) | ||||||
|  |                     }, modifier = Modifier | ||||||
|  |                         .fillMaxWidth(fraction = 1f) | ||||||
|  |                         .padding(vertical = 8.dp)) { | ||||||
|  |                         Text("Export") | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 HorizontalDivider() | ||||||
|  |             } | ||||||
|  |             item { | ||||||
|  |                 PreferenceButton("Licenses") { | ||||||
|  |                     if (navController.currentDestination?.route != "licenses") | ||||||
|  |                         navController.navigate("licenses") | ||||||
|  |                 } | ||||||
|  |                 PreferenceButton("About") { | ||||||
|  |                     if (navController.currentDestination?.route != "about") | ||||||
|  |                         navController.navigate("about") | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun RegionalScreen() { | ||||||
|  |     val context = LocalContext.current | ||||||
|  |     val prefs = PreferenceManager.getDefaultSharedPreferences(context) | ||||||
|  |     var selected by remember {  mutableStateOf(prefs.getString(context.getString(R.string.key_regional),context.getString(R.string.off))!!)} | ||||||
|  |     var regional by remember{ mutableStateOf(prefs.getString(context.getString(R.string.key_regional), context.getString(R.string.off))!!)} | ||||||
|  |     var showDialog by remember{mutableStateOf(false)} | ||||||
|  |     var showLoad by remember{mutableStateOf(false)} | ||||||
|  |  | ||||||
|  |     if(showDialog) | ||||||
|  |         Dialog( | ||||||
|  |             content = { | ||||||
|  |                 Column( | ||||||
|  |                     modifier = Modifier | ||||||
|  |                         .background( | ||||||
|  |                             MaterialTheme.colors.background, | ||||||
|  |                             RoundedCornerShape(corner = CornerSize(16.dp)) | ||||||
|  |                         ) | ||||||
|  |                         .padding(16.dp),){ | ||||||
|  |                 Text(style=MaterialTheme.typography.caption, text=  stringResource(R.string.delete_regions)) | ||||||
|  |                     Button(onClick = { | ||||||
|  |                         GeoLocImporter.clearStates() | ||||||
|  |                         regional= selected | ||||||
|  |                         prefs.edit { | ||||||
|  |                             putString( | ||||||
|  |                                 context.getString(R.string.key_regional), | ||||||
|  |                                 regional | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                         showDialog=false | ||||||
|  |                     }){ | ||||||
|  |                        Text(stringResource(R.string.ok)) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             onDismissRequest = { showDialog=false } | ||||||
|  |         ) | ||||||
|  |     if(showLoad){ | ||||||
|  |         Dialog( | ||||||
|  |             content = { | ||||||
|  |                 CircularProgressIndicator( | ||||||
|  |                     color = MaterialTheme.colors.primary, | ||||||
|  |                     strokeWidth = 4.dp, | ||||||
|  |                     modifier = Modifier.size(50.dp) | ||||||
|  |                 ) | ||||||
|  |             }, | ||||||
|  |             onDismissRequest = {} | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |     val scope = rememberCoroutineScope() | ||||||
|  |     MultiPreference(arrayOf(stringResource(R.string.on),stringResource(R.string.off)),regional) { key -> | ||||||
|  |                 when (key) { | ||||||
|  |                     context.getString(R.string.off) -> { showDialog=true | ||||||
|  |                         selected=key | ||||||
|  |                     } | ||||||
|  |                     context.getString(R.string.on) -> { | ||||||
|  |                         regional = key | ||||||
|  |                         prefs.edit { putString(context.getString(R.string.key_regional), key) } | ||||||
|  |                         showLoad=true | ||||||
|  |                         scope.launch { | ||||||
|  |                             withContext(Dispatchers.IO) { | ||||||
|  |                                 GeoLocImporter.importStates(context, true) | ||||||
|  |                             } | ||||||
|  |                             showLoad = false | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun MultiPreference(list: Array<String>, selected: String, onSelected: (String) -> Unit) { | ||||||
|  |     Column(Modifier.padding(2.dp)) { | ||||||
|  |         list.map { value -> | ||||||
|  |             Row( | ||||||
|  |                 verticalAlignment = Alignment.CenterVertically, | ||||||
|  |                 modifier = Modifier | ||||||
|  |                     .fillMaxWidth() | ||||||
|  |                     .height(36.dp) | ||||||
|  |                     .clickable { onSelected(value) }) { | ||||||
|  |                 RadioButton(selected = selected == value, onClick = { onSelected(value) }) | ||||||
|  |                 Text( | ||||||
|  |                     value, modifier = Modifier.padding(start = 8.dp), | ||||||
|  |                     color = MaterialTheme.colors.onBackground, | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun PreferenceButton(text: String, onClick: () -> Unit) { | ||||||
|  |     Button(onClick = onClick, modifier = Modifier | ||||||
|  |         .fillMaxWidth() | ||||||
|  |         .padding(vertical = 8.dp)) { | ||||||
|  |         Text(text) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,57 +1,159 @@ | |||||||
| package net.helcel.beans.activity | package net.helcel.beans.activity | ||||||
|  |  | ||||||
| import android.os.Bundle | import androidx.compose.foundation.background | ||||||
| import android.view.MenuItem | import androidx.compose.foundation.layout.Arrangement | ||||||
| import androidx.appcompat.app.AppCompatActivity | import androidx.compose.foundation.layout.Column | ||||||
| import androidx.fragment.app.Fragment | import androidx.compose.foundation.layout.Row | ||||||
| import androidx.recyclerview.widget.LinearLayoutManager | import androidx.compose.foundation.layout.fillMaxSize | ||||||
| import androidx.recyclerview.widget.RecyclerView | import androidx.compose.foundation.layout.fillMaxWidth | ||||||
| import androidx.viewpager2.adapter.FragmentStateAdapter | import androidx.compose.foundation.layout.padding | ||||||
| import androidx.viewpager2.widget.ViewPager2 | import androidx.compose.foundation.lazy.LazyColumn | ||||||
| import com.google.android.material.tabs.TabLayoutMediator | import androidx.compose.foundation.lazy.items | ||||||
|  | import androidx.compose.material.Button | ||||||
|  | import androidx.compose.material.Icon | ||||||
|  | import androidx.compose.material.IconButton | ||||||
|  | import androidx.compose.material.Scaffold | ||||||
|  | import androidx.compose.material.Tab | ||||||
|  | import androidx.compose.material.TabRow | ||||||
|  | import androidx.compose.material.Text | ||||||
|  | import androidx.compose.material.TopAppBar | ||||||
|  | import androidx.compose.material.icons.Icons | ||||||
|  | import androidx.compose.material.icons.automirrored.filled.ArrowBack | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.runtime.getValue | ||||||
|  | import androidx.compose.runtime.mutableIntStateOf | ||||||
|  | import androidx.compose.runtime.mutableStateOf | ||||||
|  | import androidx.compose.runtime.remember | ||||||
|  | import androidx.compose.runtime.setValue | ||||||
|  | import androidx.compose.ui.Alignment | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.graphics.Color | ||||||
|  | import androidx.compose.ui.platform.LocalContext | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
| import net.helcel.beans.R | import net.helcel.beans.R | ||||||
| import net.helcel.beans.activity.adapter.StatsListAdapter |  | ||||||
| import net.helcel.beans.countries.GeoLoc.LocType | import net.helcel.beans.countries.GeoLoc.LocType | ||||||
| import net.helcel.beans.databinding.ActivityStatBinding | import net.helcel.beans.countries.World | ||||||
| import net.helcel.beans.helper.Settings | import net.helcel.beans.helper.AUTO_GROUP | ||||||
| import net.helcel.beans.helper.Theme.createActionBar | import net.helcel.beans.helper.Data | ||||||
|  | import net.helcel.beans.helper.Groups | ||||||
|  | import net.helcel.beans.helper.Settings.isRegional | ||||||
|  | import net.helcel.beans.helper.Theme.getContrastColor | ||||||
|  |  | ||||||
| private val MODE_LIST = listOf(LocType.WORLD, LocType.COUNTRY, LocType.STATE) | private val MODE_LIST = listOf(LocType.WORLD, LocType.COUNTRY, LocType.STATE) | ||||||
|  |  | ||||||
| class StatsActivity : AppCompatActivity() { | @Composable | ||||||
|     private lateinit var _binding: ActivityStatBinding | fun StatsScreen( | ||||||
|     private var activeMode = LocType.WORLD |     onExit: ()-> Unit | ||||||
|  | ) { | ||||||
|  |     val modes = if (isRegional(LocalContext.current)) MODE_LIST else MODE_LIST.take(2) | ||||||
|  |     var selectedTab by remember { mutableIntStateOf(0) } | ||||||
|  |     var countMode by remember { mutableStateOf(true) } | ||||||
|  |  | ||||||
|     override fun onCreate(savedInstanceState: Bundle?) { |  | ||||||
|         super.onCreate(savedInstanceState) |  | ||||||
|         _binding = ActivityStatBinding.inflate(layoutInflater) |  | ||||||
|         setContentView(_binding.root) |  | ||||||
|         createActionBar(this, getString(R.string.action_stat)) |  | ||||||
|  |  | ||||||
|         _binding.stats.layoutManager = |     SysTheme { | ||||||
|             LinearLayoutManager(this, RecyclerView.VERTICAL, false) |         Scaffold( | ||||||
|         val adapter = StatsListAdapter(_binding.stats, _binding.name) |             topBar = { | ||||||
|         _binding.groupColor.setOnClickListener { adapter.invertCountMode() } |                 TopAppBar( | ||||||
|         _binding.stats.adapter = adapter |                     title = { | ||||||
|  |                         Row(verticalAlignment = Alignment.CenterVertically){ | ||||||
|  |                             Text(text=stringResource(R.string.action_edit), modifier = Modifier.weight(1f)) | ||||||
|  |                             Button(onClick = { countMode = !countMode }) { | ||||||
|  |                                 Text(if (countMode) "Count" else "Area") | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     }, | ||||||
|  |                     navigationIcon = { | ||||||
|  |                         IconButton(onClick = onExit) { | ||||||
|  |                             Icon( | ||||||
|  |                                 Icons.AutoMirrored.Filled.ArrowBack, | ||||||
|  |                                 contentDescription = null | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                     }, | ||||||
|  |  | ||||||
|         _binding.pager.adapter = object : FragmentStateAdapter(supportFragmentManager, lifecycle) { |                 ) | ||||||
|             override fun getItemCount(): Int = if (Settings.isRegional(applicationContext)) 3 else 2 |             }, | ||||||
|             override fun createFragment(position: Int): Fragment = Fragment() |         ) { padding -> | ||||||
|         } |             Column(Modifier.padding(padding)) { | ||||||
|         TabLayoutMediator(_binding.tab, _binding.pager) { tab, position -> |                 TabRow(selectedTabIndex = selectedTab) { | ||||||
|             tab.text = MODE_LIST[position].txt |                     modes.forEachIndexed { index, mode -> | ||||||
|         }.attach() |                         Tab( | ||||||
|  |                             selected = selectedTab == index, | ||||||
|  |                             onClick = { selectedTab = index }, | ||||||
|  |                             text = { Text(mode.txt) } | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|         _binding.pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { |                 Row( | ||||||
|             override fun onPageSelected(position: Int) { |                     modifier = Modifier | ||||||
|                 activeMode = MODE_LIST[position] |                         .fillMaxWidth() | ||||||
|                 adapter.refreshMode(activeMode) |                         .padding(8.dp), | ||||||
|  |                     horizontalArrangement = Arrangement.End | ||||||
|  |                 ) { | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 val activeMode = modes.getOrNull(selectedTab) ?: LocType.WORLD | ||||||
|  |                 StatsList(activeMode, countMode) | ||||||
|             } |             } | ||||||
|         }) |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun StatsList(activeMode: LocType, countMode: Boolean) { | ||||||
|  |     val groups = remember { Data.groups.groupsFlow } | ||||||
|  |     val unCat = stringResource(R.string.uncategorized) | ||||||
|  |  | ||||||
|  |     LazyColumn(modifier = Modifier.fillMaxSize()) { | ||||||
|  |         items(groups.value + listOf(Groups.Group(AUTO_GROUP, unCat))) { group -> | ||||||
|  |             StatsRow(group, activeMode, countMode) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun StatsRow(group: Groups.Group, activeMode: LocType, countMode: Boolean) { | ||||||
|  |     val context = LocalContext.current | ||||||
|  |  | ||||||
|  |     val visited = remember(group, activeMode) { | ||||||
|  |         Data.visits.getVisitedByValue(group.key) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     override fun onOptionsItemSelected(item: MenuItem): Boolean { |     val count = when (activeMode) { | ||||||
|         finish() |         LocType.WORLD -> World.WWW.children.filter { it.code in visited }.size | ||||||
|         return super.onOptionsItemSelected(item) |         LocType.COUNTRY -> World.WWW.children.flatMap { it.children.filter { c -> c.code in visited } }.size | ||||||
|  |         LocType.STATE -> World.WWW.children.flatMap { itc->itc.children.flatMap { it.children.filter { it.code in visited } } }.size | ||||||
|  |         else -> 0 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val area = when (activeMode) { | ||||||
|  |         LocType.WORLD -> World.WWW.children.filter { it.code in visited }.sumOf { it.area } | ||||||
|  |         LocType.COUNTRY -> World.WWW.children.flatMap { it.children.filter { c -> c.code in visited } }.sumOf { it.area } | ||||||
|  |         LocType.STATE -> World.WWW.children.flatMap { it.children.flatMap { it.children.filter { it.code in visited } } }.sumOf { it.area } | ||||||
|  |         else -> 0 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val displayValue = if (countMode) count.toString() else context.getString(R.string.number_with_unit, area, "km²") | ||||||
|  |  | ||||||
|  |     val backgroundColor = group.color.color | ||||||
|  |     val textColor = getContrastColor(backgroundColor) | ||||||
|  |  | ||||||
|  |     Row( | ||||||
|  |         modifier = Modifier | ||||||
|  |             .fillMaxWidth() | ||||||
|  |             .background(Color(backgroundColor)) | ||||||
|  |             .padding(16.dp), | ||||||
|  |         verticalAlignment = Alignment.CenterVertically | ||||||
|  |     ) { | ||||||
|  |         Text( | ||||||
|  |             text=group.name, | ||||||
|  |             modifier= Modifier.weight(1f), | ||||||
|  |             color = Color(textColor) | ||||||
|  |         ) | ||||||
|  |         Text(text=displayValue, | ||||||
|  |             color = Color(textColor) | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1,193 +0,0 @@ | |||||||
| package net.helcel.beans.activity.adapter |  | ||||||
|  |  | ||||||
| import android.content.res.ColorStateList |  | ||||||
| import android.graphics.Color |  | ||||||
| import android.graphics.Typeface |  | ||||||
| import android.graphics.drawable.ColorDrawable |  | ||||||
| import android.view.LayoutInflater |  | ||||||
| import android.view.ViewGroup |  | ||||||
| import androidx.fragment.app.FragmentActivity |  | ||||||
| import androidx.recyclerview.widget.RecyclerView |  | ||||||
| import com.google.android.material.checkbox.MaterialCheckBox |  | ||||||
| import net.helcel.beans.activity.fragment.EditPlaceColorFragment |  | ||||||
| import net.helcel.beans.activity.fragment.EditPlaceFragment |  | ||||||
| import net.helcel.beans.countries.GeoLoc |  | ||||||
| import net.helcel.beans.databinding.ItemListGeolocBinding |  | ||||||
| import net.helcel.beans.helper.* |  | ||||||
| import net.helcel.beans.helper.Theme.colorWrapper |  | ||||||
|  |  | ||||||
| class GeolocListAdapter( |  | ||||||
|     private val ctx: EditPlaceFragment, private val l: GeoLoc, private val pager: ViewPagerAdapter, |  | ||||||
|     private val parentHolder: FoldingListViewHolder? |  | ||||||
| ) : RecyclerView.Adapter<GeolocListAdapter.FoldingListViewHolder>() { |  | ||||||
|  |  | ||||||
|     private val sortedList = l.children.toList().sortedBy { it.fullName } |  | ||||||
|     private val holders: MutableSet<FoldingListViewHolder> = mutableSetOf() |  | ||||||
|  |  | ||||||
|     override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): FoldingListViewHolder { |  | ||||||
|         val binding = ItemListGeolocBinding.inflate( |  | ||||||
|             LayoutInflater.from(viewGroup.context), |  | ||||||
|             viewGroup, |  | ||||||
|             false |  | ||||||
|         ) |  | ||||||
|         val holder = FoldingListViewHolder(ctx.requireActivity(), binding, parentHolder, l) |  | ||||||
|         holders.add(holder) |  | ||||||
|         return holder |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun onBindViewHolder(holder: FoldingListViewHolder, position: Int) { |  | ||||||
|         val el = sortedList[position] |  | ||||||
|         holder.bind(el) |  | ||||||
|         holder.addListeners(el) { |  | ||||||
|             if (el.children.isNotEmpty()) |  | ||||||
|                 pager.addFragment(ctx, EditPlaceFragment(el, pager, holder)) |  | ||||||
|             true |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun getItemCount(): Int { |  | ||||||
|         return l.children.size |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun refreshColors(colorDrawable: ColorDrawable) { |  | ||||||
|         holders.forEach { it.refreshColor(colorDrawable) } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     class FoldingListViewHolder( |  | ||||||
|         private val ctx: FragmentActivity, |  | ||||||
|         private val _binding: ItemListGeolocBinding, |  | ||||||
|         private val _parentHolder: FoldingListViewHolder? = null, |  | ||||||
|         private val _parentGeoLoc: GeoLoc, |  | ||||||
|     ) : RecyclerView.ViewHolder(_binding.root), DialogCloser { |  | ||||||
|         private lateinit var el: GeoLoc |  | ||||||
|  |  | ||||||
|         private fun bindGroup(el: GeoLoc) { |  | ||||||
|             refreshCount(el) |  | ||||||
|             _binding.textView.setTypeface(null, Typeface.BOLD) |  | ||||||
|             _binding.textView.backgroundTintList = ColorStateList.valueOf( |  | ||||||
|                 colorWrapper( |  | ||||||
|                     ctx, |  | ||||||
|                     android.R.attr.panelColorBackground |  | ||||||
|                 ).color |  | ||||||
|             ).withAlpha(64) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         fun bind(el: GeoLoc) { |  | ||||||
|             this.el = el |  | ||||||
|             _binding.textView.text = el.fullName |  | ||||||
|             _binding.textView.backgroundTintList = |  | ||||||
|                 ColorStateList.valueOf(colorWrapper(ctx, android.R.attr.colorBackground).color) |  | ||||||
|  |  | ||||||
|             if (el.children.isNotEmpty()) |  | ||||||
|                 bindGroup(el) |  | ||||||
|  |  | ||||||
|             refreshCheck(el) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         fun refreshColor(colorDrawable: ColorDrawable) { |  | ||||||
|             if (Data.visits.getVisited(el) !in listOf(NO_GROUP, AUTO_GROUP)) { |  | ||||||
|                 _binding.checkBox.buttonTintList = |  | ||||||
|                     ColorStateList.valueOf(colorDrawable.color) |  | ||||||
|                 refreshCheck(el) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         fun addListeners(el: GeoLoc, expandLambda: () -> Boolean) { |  | ||||||
|             if (el.children.isNotEmpty()) { |  | ||||||
|                 _binding.textView.setOnClickListener { expandLambda() } |  | ||||||
|             } |  | ||||||
|             _binding.checkBox.setOnClickListener { |  | ||||||
|                 Data.selected_geoloc = el |  | ||||||
|                 if (Data.groups.size() == 1 && Settings.isSingleGroup(ctx)) { |  | ||||||
|                     if (_binding.checkBox.isChecked) { |  | ||||||
|                         // If one has just checked the box (assign unique group) |  | ||||||
|                         Data.selected_group = Data.groups.getUniqueEntry() |  | ||||||
|                         onDialogDismiss(false) |  | ||||||
|                     } else { |  | ||||||
|                         // If one has just unchecked the box (unassign unique group) |  | ||||||
|                         Data.selected_group = null |  | ||||||
|                         onDialogDismiss(true) |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     Data.selected_group = null |  | ||||||
|                     EditPlaceColorFragment(this).show( |  | ||||||
|                         ctx.supportFragmentManager, |  | ||||||
|                         "AddColorDialogFragment" |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
|                 _parentHolder?.refresh(_parentGeoLoc) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         override fun onDialogDismiss(clear: Boolean) { |  | ||||||
|             if (clear) { |  | ||||||
|                 Data.visits.setVisited(Data.selected_geoloc, NO_GROUP) |  | ||||||
|                 Data.saveData() |  | ||||||
|  |  | ||||||
|                 if (_parentGeoLoc.children.all { Data.visits.getVisited(it) == NO_GROUP }) { |  | ||||||
|                     Data.clearing_geoloc = _parentGeoLoc |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             if (Data.selected_group != null && Data.selected_geoloc != null) { |  | ||||||
|                 Data.visits.setVisited(Data.selected_geoloc, Data.selected_group?.key ?: NO_GROUP) |  | ||||||
|                 Data.saveData() |  | ||||||
|             } |  | ||||||
|             Data.selected_geoloc?.let { refreshCheck(it) } |  | ||||||
|             Data.selected_geoloc = null |  | ||||||
|             Data.selected_group = null |  | ||||||
|             _parentHolder?.refresh(_parentGeoLoc) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private fun refreshCheck(geoLoc: GeoLoc) { |  | ||||||
|             _binding.checkBox.checkedState = |  | ||||||
|                 if (Data.visits.getVisited(geoLoc) !in listOf(NO_GROUP, AUTO_GROUP)) { |  | ||||||
|                     MaterialCheckBox.STATE_CHECKED |  | ||||||
|                 } else if (geoLoc.children.isNotEmpty() && |  | ||||||
|                     geoLoc.children.all { |  | ||||||
|                         Data.visits.getVisited(it) !in listOf(NO_GROUP, AUTO_GROUP) |  | ||||||
|                     } |  | ||||||
|                 ) { |  | ||||||
|                     Data.visits.setVisited(geoLoc, AUTO_GROUP) |  | ||||||
|                     MaterialCheckBox.STATE_CHECKED |  | ||||||
|                 } else if (geoLoc.children.isEmpty() && Data.visits.getVisited(geoLoc) == AUTO_GROUP) { |  | ||||||
|                     MaterialCheckBox.STATE_CHECKED |  | ||||||
|                 } else if (geoLoc.children.any { Data.visits.getVisited(it) != NO_GROUP }) { |  | ||||||
|                     Data.visits.setVisited(geoLoc, AUTO_GROUP) |  | ||||||
|                     MaterialCheckBox.STATE_INDETERMINATE |  | ||||||
|                 } else { |  | ||||||
|                     Data.visits.setVisited(geoLoc, NO_GROUP) |  | ||||||
|                     if (Data.clearing_geoloc == geoLoc) { |  | ||||||
|                         Data.clearing_geoloc = null |  | ||||||
|                     } |  | ||||||
|                     MaterialCheckBox.STATE_UNCHECKED |  | ||||||
|                 } |  | ||||||
|             Data.saveData() |  | ||||||
|  |  | ||||||
|             var col = Data.groups.getGroupFromKey(Data.visits.getVisited(geoLoc)).color |  | ||||||
|             if (Data.visits.getVisited(geoLoc) == AUTO_GROUP) { |  | ||||||
|                 col = colorWrapper(ctx, android.R.attr.colorPrimary) |  | ||||||
|             } else if (col.color == Color.TRANSPARENT) { |  | ||||||
|                 col = colorWrapper(ctx, android.R.attr.panelColorBackground) |  | ||||||
|                 col.alpha = 64 |  | ||||||
|             } |  | ||||||
|             _binding.checkBox.buttonTintList = ColorStateList.valueOf(col.color) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private fun refreshCount(geoLoc: GeoLoc) { |  | ||||||
|             val numerator = |  | ||||||
|                 geoLoc.children.map { Data.visits.getVisited(it) != NO_GROUP }.count { it } |  | ||||||
|             val denominator = geoLoc.children.size |  | ||||||
|             _binding.count.text = Settings.getStats(ctx, numerator, denominator) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private fun refresh(geoLoc: GeoLoc) { |  | ||||||
|             // Refresh |  | ||||||
|             refreshCheck(geoLoc) |  | ||||||
|             refreshCount(geoLoc) |  | ||||||
|  |  | ||||||
|             // Recursively refresh parent |  | ||||||
|             _parentHolder?.refresh(_parentGeoLoc) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,78 +0,0 @@ | |||||||
| package net.helcel.beans.activity.adapter |  | ||||||
|  |  | ||||||
| import android.view.LayoutInflater |  | ||||||
| import android.view.ViewGroup |  | ||||||
| import androidx.fragment.app.DialogFragment |  | ||||||
| import androidx.fragment.app.FragmentActivity |  | ||||||
| import androidx.recyclerview.widget.RecyclerView |  | ||||||
| import net.helcel.beans.activity.fragment.EditGroupAddFragment |  | ||||||
| import net.helcel.beans.databinding.ItemListGroupBinding |  | ||||||
| import net.helcel.beans.helper.Data |  | ||||||
| import net.helcel.beans.helper.Groups |  | ||||||
| import net.helcel.beans.helper.Theme.getContrastColor |  | ||||||
|  |  | ||||||
| class GroupListAdapter( |  | ||||||
|     private val activity: FragmentActivity, |  | ||||||
|     private val selectDialog: DialogFragment, |  | ||||||
|     private val delete: Boolean = false |  | ||||||
| ) : RecyclerView.Adapter<GroupListAdapter.GroupViewHolder>() { |  | ||||||
|  |  | ||||||
|     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GroupViewHolder { |  | ||||||
|         val binding = |  | ||||||
|             ItemListGroupBinding.inflate(LayoutInflater.from(parent.context), parent, false) |  | ||||||
|         return GroupViewHolder(binding, activity, selectDialog) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun onBindViewHolder(holder: GroupViewHolder, pos: Int) { |  | ||||||
|         holder.bind(Data.groups.getGroupFromPos(pos)) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun getItemCount(): Int { |  | ||||||
|         return Data.groups.size() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     inner class GroupViewHolder( |  | ||||||
|         private val _binding: ItemListGroupBinding, |  | ||||||
|         private val activity: FragmentActivity, |  | ||||||
|         private val selectDialog: DialogFragment |  | ||||||
|     ) : RecyclerView.ViewHolder(_binding.root) { |  | ||||||
|         private lateinit var dialogFragment: EditGroupAddFragment |  | ||||||
|         fun bind(entry: Pair<Int, Groups.Group>) { |  | ||||||
|             _binding.groupColor.text = entry.second.name |  | ||||||
|             dialogFragment = EditGroupAddFragment(entry.first, { |  | ||||||
|                 val newEntry = Data.groups.getGroupFromKey(entry.first) |  | ||||||
|                 _binding.groupColor.text = newEntry.name |  | ||||||
|                 val newEntryColor = newEntry.color.color |  | ||||||
|                 val contrastNewEntryColor = |  | ||||||
|                     getContrastColor(newEntryColor) |  | ||||||
|                 _binding.groupColor.setBackgroundColor(newEntryColor) |  | ||||||
|                 _binding.groupColor.setTextColor(contrastNewEntryColor) |  | ||||||
|                 _binding.name.setTextColor(contrastNewEntryColor) |  | ||||||
|                 _binding.name.text = "0" |  | ||||||
|             }, { |  | ||||||
|                 notifyItemRemoved(it) |  | ||||||
|             }) |  | ||||||
|  |  | ||||||
|             val entryColor = entry.second.color.color |  | ||||||
|             val contrastEntryColor = getContrastColor(entryColor) |  | ||||||
|             _binding.groupColor.setBackgroundColor(entryColor) |  | ||||||
|             _binding.groupColor.setTextColor(contrastEntryColor) |  | ||||||
|             _binding.name.setTextColor(contrastEntryColor) |  | ||||||
|             _binding.name.text = Data.visits.countVisited(entry.first).toString() |  | ||||||
|  |  | ||||||
|             _binding.groupColor.setOnClickListener { |  | ||||||
|                 Data.selected_group = entry.second |  | ||||||
|                 selectDialog.dismiss() |  | ||||||
|             } |  | ||||||
|             if (!delete) { |  | ||||||
|                 _binding.groupColor.setOnLongClickListener { |  | ||||||
|                     dialogFragment.show( |  | ||||||
|                         activity.supportFragmentManager, |  | ||||||
|                         "AddColorDialogFragment" |  | ||||||
|                     ) |  | ||||||
|                     true |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,152 +0,0 @@ | |||||||
| package net.helcel.beans.activity.adapter |  | ||||||
|  |  | ||||||
| import android.content.Context |  | ||||||
| import android.view.LayoutInflater |  | ||||||
| import android.view.ViewGroup |  | ||||||
| import androidx.recyclerview.widget.RecyclerView |  | ||||||
| import com.google.android.material.textview.MaterialTextView |  | ||||||
| import net.helcel.beans.R |  | ||||||
| import net.helcel.beans.countries.GeoLoc |  | ||||||
| import net.helcel.beans.countries.GeoLoc.LocType |  | ||||||
| import net.helcel.beans.countries.World |  | ||||||
| import net.helcel.beans.databinding.ItemListGroupBinding |  | ||||||
| import net.helcel.beans.helper.AUTO_GROUP |  | ||||||
| import net.helcel.beans.helper.Data |  | ||||||
| import net.helcel.beans.helper.Groups |  | ||||||
| import net.helcel.beans.helper.Settings |  | ||||||
| import net.helcel.beans.helper.Theme.getContrastColor |  | ||||||
|  |  | ||||||
| class StatsListAdapter(private val stats: RecyclerView, private val total: MaterialTextView) : |  | ||||||
|     RecyclerView.Adapter<StatsListAdapter.StatsViewHolder>() { |  | ||||||
|     private val unit = "km²" |  | ||||||
|  |  | ||||||
|     private var locMode = LocType.WORLD |  | ||||||
|     private lateinit var ctx: Context |  | ||||||
|     private var countMode: Boolean = true |  | ||||||
|     private var initialSum: Int = 0 |  | ||||||
|  |  | ||||||
|     private val wwwTotal: List<GeoLoc> = World.WWW.children.toList() |  | ||||||
|     private val countryTotal: List<GeoLoc> = World.WWW.children.flatMap { it.children } |  | ||||||
|     private val stateTotal: List<GeoLoc> = |  | ||||||
|         World.WWW.children.flatMap { it.children.flatMap { itt -> itt.children } } |  | ||||||
|  |  | ||||||
|     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatsViewHolder { |  | ||||||
|         ctx = parent.context |  | ||||||
|         val binding = |  | ||||||
|             ItemListGroupBinding.inflate(LayoutInflater.from(ctx), parent, false) |  | ||||||
|  |  | ||||||
|         return StatsViewHolder(binding) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun onBindViewHolder(holder: StatsViewHolder, pos: Int) { |  | ||||||
|         initialSum += if (pos == itemCount - 1) { |  | ||||||
|             holder.bind( |  | ||||||
|                 Pair( |  | ||||||
|                     AUTO_GROUP, |  | ||||||
|                     Groups.Group(AUTO_GROUP, ctx.getString(R.string.uncategorized)) |  | ||||||
|                 ) |  | ||||||
|             ) |  | ||||||
|         } else { |  | ||||||
|             holder.bind(Data.groups.getGroupFromPos(pos)) |  | ||||||
|         } |  | ||||||
|         val unitNow = if (!countMode) unit else "" |  | ||||||
|         total.text = Settings.getStats(ctx, initialSum, getTotal(), unitNow) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun getItemCount(): Int { |  | ||||||
|         return Data.groups.size() + 1 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private fun getTotal(): Int { |  | ||||||
|         return if (countMode) { |  | ||||||
|             when (locMode) { |  | ||||||
|                 LocType.WORLD -> wwwTotal.size |  | ||||||
|                 LocType.COUNTRY -> countryTotal.size |  | ||||||
|                 LocType.STATE -> stateTotal.size |  | ||||||
|                 else -> 0 |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             when (locMode) { |  | ||||||
|                 LocType.WORLD -> wwwTotal.sumOf { it.area } |  | ||||||
|                 LocType.COUNTRY -> countryTotal.sumOf { it.area } |  | ||||||
|                 LocType.STATE -> stateTotal.sumOf { it.area } |  | ||||||
|                 else -> 0 |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun refreshMode(mode: LocType) { |  | ||||||
|         val sum = (0 until itemCount).map { |  | ||||||
|             val viewHolder = stats.findViewHolderForAdapterPosition(it) as? StatsViewHolder |  | ||||||
|             viewHolder?.refresh(mode) |  | ||||||
|         }.reduce { acc, i -> acc?.plus((i ?: 0)) } |  | ||||||
|         val unitNow = if (!countMode) unit else "" |  | ||||||
|         total.text = Settings.getStats(ctx, sum, getTotal(), unitNow) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun invertCountMode() { |  | ||||||
|         countMode = !countMode |  | ||||||
|         refreshMode(locMode) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     inner class StatsViewHolder( |  | ||||||
|         private val _binding: ItemListGroupBinding |  | ||||||
|     ) : RecyclerView.ViewHolder(_binding.root) { |  | ||||||
|  |  | ||||||
|         private lateinit var data: Pair<Int, Groups.Group> |  | ||||||
|  |  | ||||||
|         private lateinit var wwwCount: List<GeoLoc> |  | ||||||
|         private lateinit var countryCount: List<GeoLoc> |  | ||||||
|         private lateinit var stateCount: List<GeoLoc> |  | ||||||
|  |  | ||||||
|         fun bind(entry: Pair<Int, Groups.Group>): Int { |  | ||||||
|             data = entry |  | ||||||
|             _binding.groupColor.text = entry.second.name |  | ||||||
|  |  | ||||||
|             val entryColor = data.second.color.color |  | ||||||
|             val contrastEntryColor = getContrastColor(entryColor) |  | ||||||
|             _binding.groupColor.setBackgroundColor(entryColor) |  | ||||||
|             _binding.groupColor.setTextColor(contrastEntryColor) |  | ||||||
|             _binding.name.setTextColor(contrastEntryColor) |  | ||||||
|  |  | ||||||
|             _binding.groupColor.setOnClickListener { invertCountMode() } |  | ||||||
|             compute() |  | ||||||
|             return refresh(locMode) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private fun compute() { |  | ||||||
|             val visited = Data.visits.getVisitedByValue(data.first) |  | ||||||
|             wwwCount = World.WWW.children.filter { it.code in visited } |  | ||||||
|             countryCount = |  | ||||||
|                 World.WWW.children.map { it.children.filter { itt -> itt.code in visited } } |  | ||||||
|                     .flatten() |  | ||||||
|             stateCount = |  | ||||||
|                 World.WWW.children.map { it.children.map { itt -> itt.children.filter { ittt -> ittt.code in visited } } } |  | ||||||
|                     .flatten().flatten() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         fun refresh(mode: LocType): Int { |  | ||||||
|             locMode = mode |  | ||||||
|             return if (countMode) { |  | ||||||
|                 val count = when (locMode) { |  | ||||||
|                     LocType.WORLD -> wwwCount.size |  | ||||||
|                     LocType.COUNTRY -> countryCount.size |  | ||||||
|                     LocType.STATE -> stateCount.size |  | ||||||
|                     else -> -1 |  | ||||||
|                 } |  | ||||||
|                 _binding.name.text = count.toString() |  | ||||||
|                 count |  | ||||||
|             } else { |  | ||||||
|                 val area = when (locMode) { |  | ||||||
|                     LocType.WORLD -> wwwCount.sumOf { it.area } |  | ||||||
|                     LocType.COUNTRY -> countryCount.sumOf { it.area } |  | ||||||
|                     LocType.STATE -> stateCount.sumOf { it.area } |  | ||||||
|                     else -> -1 |  | ||||||
|                 } |  | ||||||
|                 _binding.name.text = ctx.getString(R.string.number_with_unit, area, unit) |  | ||||||
|                 area |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,61 +0,0 @@ | |||||||
| package net.helcel.beans.activity.adapter |  | ||||||
|  |  | ||||||
| import android.graphics.drawable.ColorDrawable |  | ||||||
| import androidx.fragment.app.Fragment |  | ||||||
| import androidx.fragment.app.FragmentManager |  | ||||||
| import androidx.lifecycle.Lifecycle |  | ||||||
| import androidx.viewpager2.adapter.FragmentStateAdapter |  | ||||||
| import androidx.viewpager2.widget.ViewPager2 |  | ||||||
| import net.helcel.beans.activity.fragment.EditPlaceFragment |  | ||||||
| import kotlin.math.max |  | ||||||
|  |  | ||||||
| class ViewPagerAdapter( |  | ||||||
|     fragmentManager: FragmentManager, |  | ||||||
|     lifecycle: Lifecycle, |  | ||||||
|     private val viewPager: ViewPager2 |  | ||||||
| ) : |  | ||||||
|     FragmentStateAdapter(fragmentManager, lifecycle) { |  | ||||||
|  |  | ||||||
|     private val fragmentList: MutableList<EditPlaceFragment> = ArrayList() |  | ||||||
|  |  | ||||||
|     fun addFragment(src: EditPlaceFragment?, target: EditPlaceFragment) { |  | ||||||
|         val idx = fragmentList.indexOf(src) |  | ||||||
|         viewPager.currentItem = max(0, idx) |  | ||||||
|         if (src != null && idx >= 0) { |  | ||||||
|             fragmentList.subList(idx + 1, fragmentList.size).clear() |  | ||||||
|         } |  | ||||||
|         fragmentList.add(target) |  | ||||||
|         notifyItemRangeChanged(max(0, idx), fragmentList.size) |  | ||||||
|         viewPager.currentItem = fragmentList.size - 1 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun getItemCount(): Int { |  | ||||||
|         return fragmentList.size |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun backPressed(): Boolean { |  | ||||||
|         if (viewPager.currentItem == 0) { |  | ||||||
|             return false |  | ||||||
|         } |  | ||||||
|         val target = viewPager.currentItem |  | ||||||
|         while (fragmentList.size > target) { |  | ||||||
|             fragmentList.removeLast() |  | ||||||
|             notifyItemRemoved(fragmentList.size) |  | ||||||
|         } |  | ||||||
|         return true |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun getLabel(pos: Int): String { |  | ||||||
|         return fragmentList[pos].loc.fullName |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun createFragment(position: Int): Fragment { |  | ||||||
|         return fragmentList[position] |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun refreshColors(colorDrawable: ColorDrawable) { |  | ||||||
|         fragmentList.forEach{ it.refreshColors(colorDrawable)} |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -1,21 +0,0 @@ | |||||||
| package net.helcel.beans.activity.fragment |  | ||||||
|  |  | ||||||
| import android.os.Bundle |  | ||||||
| import android.view.LayoutInflater |  | ||||||
| import android.view.View |  | ||||||
| import android.view.ViewGroup |  | ||||||
| import androidx.fragment.app.Fragment |  | ||||||
| import net.helcel.beans.databinding.FragmentAboutBinding |  | ||||||
|  |  | ||||||
| class AboutFragment : Fragment() { |  | ||||||
|     private lateinit var _binding: FragmentAboutBinding |  | ||||||
|  |  | ||||||
|     override fun onCreateView( |  | ||||||
|         inflater: LayoutInflater, |  | ||||||
|         container: ViewGroup?, |  | ||||||
|         savedInstanceState: Bundle? |  | ||||||
|     ): View { |  | ||||||
|         _binding = FragmentAboutBinding.inflate(inflater, container, false) |  | ||||||
|         return _binding.root |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,155 +0,0 @@ | |||||||
| package net.helcel.beans.activity.fragment |  | ||||||
|  |  | ||||||
| import android.app.Dialog |  | ||||||
| import android.graphics.Color |  | ||||||
| import android.graphics.drawable.ColorDrawable |  | ||||||
| import android.os.Bundle |  | ||||||
| import android.text.Editable |  | ||||||
| import android.text.TextWatcher |  | ||||||
| import android.view.View |  | ||||||
| import androidx.core.graphics.blue |  | ||||||
| import androidx.core.graphics.green |  | ||||||
| import androidx.core.graphics.red |  | ||||||
| import androidx.fragment.app.DialogFragment |  | ||||||
| import com.google.android.material.dialog.MaterialAlertDialogBuilder |  | ||||||
| import com.google.android.material.slider.Slider |  | ||||||
| import com.google.android.material.textfield.TextInputEditText |  | ||||||
| import net.helcel.beans.R |  | ||||||
| import net.helcel.beans.databinding.FragmentEditGroupsAddBinding |  | ||||||
| import net.helcel.beans.helper.Data |  | ||||||
| import net.helcel.beans.helper.Groups |  | ||||||
| import net.helcel.beans.helper.Theme.colorToHex6 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class EditGroupAddFragment( |  | ||||||
|     private val key: Int = 0, |  | ||||||
|     val onAddCb: (Int) -> Unit, |  | ||||||
|     val onDelCb: (Int) -> Unit, |  | ||||||
|     private val deleteEnabled: Boolean = true |  | ||||||
| ) : DialogFragment() { |  | ||||||
|  |  | ||||||
|     private lateinit var _binding: FragmentEditGroupsAddBinding |  | ||||||
|     private val grp = Data.groups.getGroupFromKey(key) |  | ||||||
|  |  | ||||||
|     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { |  | ||||||
|         val builder = MaterialAlertDialogBuilder(requireContext()) |  | ||||||
|         _binding = FragmentEditGroupsAddBinding.inflate(layoutInflater) |  | ||||||
|  |  | ||||||
|         setupSlider(_binding.colorR, grp.color.color.red / 255F) |  | ||||||
|         setupSlider(_binding.colorG, grp.color.color.green / 255F) |  | ||||||
|         setupSlider(_binding.colorB, grp.color.color.blue / 255F) |  | ||||||
|         setupText(_binding.groupColor, grp) |  | ||||||
|  |  | ||||||
|         _binding.colorView.background = ColorDrawable(grp.color.color) |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         if (key == 0 || !deleteEnabled) { |  | ||||||
|             _binding.btnDelete.visibility = View.INVISIBLE |  | ||||||
|             _binding.btnDelete.isEnabled = false |  | ||||||
|         } |  | ||||||
|         _binding.btnDelete.setOnClickListener { |  | ||||||
|             MaterialAlertDialogBuilder(requireActivity()) |  | ||||||
|                 .setMessage(R.string.delete_group) |  | ||||||
|                 .setPositiveButton(android.R.string.ok) { _, _ -> |  | ||||||
|                     val pos = Data.groups.findGroupPos(key) |  | ||||||
|                     // Remove all countries belonging to that group |  | ||||||
|                     Data.visits.deleteVisited(key) |  | ||||||
|                     // Delete the group |  | ||||||
|                     Data.groups.deleteGroup(key) |  | ||||||
|                     Data.saveData() |  | ||||||
|                     onDelCb(pos) |  | ||||||
|                     dialog?.dismiss() |  | ||||||
|                 } |  | ||||||
|                 .setNegativeButton(android.R.string.cancel) { _, _ -> } |  | ||||||
|                 .show() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         _binding.btnOk.setOnClickListener { |  | ||||||
|             val name = _binding.groupName.text.toString() |  | ||||||
|             val color = _binding.groupColor.text.toString() |  | ||||||
|             val key = (if (key != 0) key else Data.groups.genKey()) |  | ||||||
|             Data.groups.setGroup(key, name, ColorDrawable(Color.parseColor("#$color"))) |  | ||||||
|             Data.saveData() |  | ||||||
|             onAddCb(key) |  | ||||||
|             dialog?.dismiss() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         _binding.btnCancel.setOnClickListener { |  | ||||||
|             dialog?.cancel() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         _binding.groupName.setText(grp.name) |  | ||||||
|         builder.setView(_binding.root) |  | ||||||
|         return builder.create() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private fun setupText(s: TextInputEditText, grp: Groups.Group?) { |  | ||||||
|         s.setText(colorToHex6(ColorDrawable(grp?.color?.color ?: 0)).substring(1)) |  | ||||||
|         s.addTextChangedListener( |  | ||||||
|             EditTextListener( |  | ||||||
|                 _binding.colorR, |  | ||||||
|                 _binding.colorG, |  | ||||||
|                 _binding.colorB, |  | ||||||
|                 _binding.groupColor, |  | ||||||
|                 _binding.colorView |  | ||||||
|             ) |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private fun setupSlider(s: Slider, v: Float) { |  | ||||||
|         s.valueFrom = 0F |  | ||||||
|         s.valueTo = 1F |  | ||||||
|         s.value = v |  | ||||||
|         s.addOnChangeListener( |  | ||||||
|             SliderOnChangeListener( |  | ||||||
|                 _binding.colorR, |  | ||||||
|                 _binding.colorG, |  | ||||||
|                 _binding.colorB, |  | ||||||
|                 _binding.groupColor, |  | ||||||
|                 _binding.colorView |  | ||||||
|             ) |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| private class EditTextListener( |  | ||||||
|     private val colorEditR: Slider, |  | ||||||
|     private val colorEditG: Slider, |  | ||||||
|     private val colorEditB: Slider, |  | ||||||
|     private val colorEditText: TextInputEditText, |  | ||||||
|     private val colorView: View |  | ||||||
| ) : TextWatcher { |  | ||||||
|  |  | ||||||
|     override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} |  | ||||||
|     override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} |  | ||||||
|     override fun afterTextChanged(s: Editable?) { |  | ||||||
|         val col: Color |  | ||||||
|         try { |  | ||||||
|             col = Color.valueOf(Color.parseColor("#${colorEditText.text}")) |  | ||||||
|         } catch (e: Exception) { |  | ||||||
|             return |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         colorEditR.value = col.red() |  | ||||||
|         colorEditG.value = col.green() |  | ||||||
|         colorEditB.value = col.blue() |  | ||||||
|         colorView.background = ColorDrawable(col.toArgb()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| private class SliderOnChangeListener( |  | ||||||
|     private val colorEditR: Slider, |  | ||||||
|     private val colorEditG: Slider, |  | ||||||
|     private val colorEditB: Slider, |  | ||||||
|     private val colorEditText: TextInputEditText, |  | ||||||
|     private val colorView: View |  | ||||||
| ) : Slider.OnChangeListener { |  | ||||||
|     override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) { |  | ||||||
|         val rgb = |  | ||||||
|             ColorDrawable(Color.argb(1F, colorEditR.value, colorEditG.value, colorEditB.value)) |  | ||||||
|         colorEditText.setText(colorToHex6(rgb).substring(1)) |  | ||||||
|         colorView.background = rgb |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,60 +0,0 @@ | |||||||
| package net.helcel.beans.activity.fragment |  | ||||||
|  |  | ||||||
| import android.app.Dialog |  | ||||||
| import android.content.DialogInterface |  | ||||||
| import android.os.Bundle |  | ||||||
| import android.view.View |  | ||||||
| import androidx.fragment.app.DialogFragment |  | ||||||
| import androidx.recyclerview.widget.LinearLayoutManager |  | ||||||
| import androidx.recyclerview.widget.RecyclerView |  | ||||||
| import com.google.android.material.dialog.MaterialAlertDialogBuilder |  | ||||||
| import net.helcel.beans.R |  | ||||||
| import net.helcel.beans.activity.adapter.GroupListAdapter |  | ||||||
| import net.helcel.beans.databinding.FragmentEditPlacesColorsBinding |  | ||||||
| import net.helcel.beans.helper.Data |  | ||||||
| import net.helcel.beans.helper.DialogCloser |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class EditPlaceColorFragment(private val parent: DialogCloser, private val delete: Boolean = false) : |  | ||||||
|     DialogFragment() { |  | ||||||
|  |  | ||||||
|     private lateinit var _binding: FragmentEditPlacesColorsBinding |  | ||||||
|     private lateinit var listAdapt: GroupListAdapter |  | ||||||
|     private var clear: Boolean = false |  | ||||||
|  |  | ||||||
|     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { |  | ||||||
|         val ctx = requireContext() |  | ||||||
|         val builder = MaterialAlertDialogBuilder(ctx) |  | ||||||
|         _binding = FragmentEditPlacesColorsBinding.inflate(layoutInflater) |  | ||||||
|         _binding.btnAdd.setOnClickListener { |  | ||||||
|             EditGroupAddFragment(0, { |  | ||||||
|                 listAdapt.notifyItemInserted(Data.groups.findGroupPos(it)) |  | ||||||
|             }, {}).show(requireActivity().supportFragmentManager, "AddColorDialogFragment") |  | ||||||
|         } |  | ||||||
|         _binding.btnClear.setOnClickListener { |  | ||||||
|             clear = true |  | ||||||
|             dialog?.dismiss() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         val dialog = builder.setView(_binding.root).create() |  | ||||||
|         listAdapt = GroupListAdapter(requireActivity(), this, delete) |  | ||||||
|         _binding.groupsColor.layoutManager = |  | ||||||
|             LinearLayoutManager(ctx, RecyclerView.VERTICAL, false) |  | ||||||
|         _binding.groupsColor.adapter = listAdapt |  | ||||||
|  |  | ||||||
|         if (delete) { |  | ||||||
|             _binding.btnAdd.visibility = View.GONE |  | ||||||
|             _binding.btnClear.text = ctx.getString(R.string.cancel) |  | ||||||
|             _binding.warningText.text = ctx.getString(R.string.select_group) |  | ||||||
|         } else { |  | ||||||
|             _binding.warningText.text = ctx.getString(R.string.edit_group) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return dialog |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun onDismiss(dialog: DialogInterface) { |  | ||||||
|         super.onDismiss(dialog) |  | ||||||
|         parent.onDialogDismiss(clear) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,38 +0,0 @@ | |||||||
| package net.helcel.beans.activity.fragment |  | ||||||
|  |  | ||||||
| import android.graphics.drawable.ColorDrawable |  | ||||||
| import android.os.Bundle |  | ||||||
| import android.view.LayoutInflater |  | ||||||
| import android.view.View |  | ||||||
| import android.view.ViewGroup |  | ||||||
| import androidx.fragment.app.Fragment |  | ||||||
| import androidx.recyclerview.widget.LinearLayoutManager |  | ||||||
| import androidx.recyclerview.widget.RecyclerView |  | ||||||
| import net.helcel.beans.activity.adapter.GeolocListAdapter |  | ||||||
| import net.helcel.beans.activity.adapter.GeolocListAdapter.FoldingListViewHolder |  | ||||||
| import net.helcel.beans.activity.adapter.ViewPagerAdapter |  | ||||||
| import net.helcel.beans.countries.GeoLoc |  | ||||||
| import net.helcel.beans.databinding.FragmentEditPlacesBinding |  | ||||||
|  |  | ||||||
| class EditPlaceFragment(val loc: GeoLoc, private val pager: ViewPagerAdapter, private val holder: FoldingListViewHolder? = null) : Fragment() { |  | ||||||
|     private lateinit var _binding: FragmentEditPlacesBinding |  | ||||||
|  |  | ||||||
|     override fun onCreateView( |  | ||||||
|         inflater: LayoutInflater, |  | ||||||
|         container: ViewGroup?, |  | ||||||
|         savedInstanceState: Bundle? |  | ||||||
|     ): View { |  | ||||||
|         _binding = FragmentEditPlacesBinding.inflate(inflater, container, false) |  | ||||||
|  |  | ||||||
|         _binding.list.setItemViewCacheSize(5) |  | ||||||
|         _binding.list.setHasFixedSize(true) |  | ||||||
|         _binding.list.layoutManager = |  | ||||||
|             LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) |  | ||||||
|         _binding.list.adapter = GeolocListAdapter(this, loc, pager, holder) |  | ||||||
|         return _binding.root |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun refreshColors(colorDrawable: ColorDrawable) { |  | ||||||
|         (_binding.list.adapter as GeolocListAdapter?)?.refreshColors(colorDrawable) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,33 +0,0 @@ | |||||||
| package net.helcel.beans.activity.fragment |  | ||||||
|  |  | ||||||
| import android.os.Bundle |  | ||||||
| import android.view.LayoutInflater |  | ||||||
| import android.view.View |  | ||||||
| import android.view.ViewGroup |  | ||||||
| import androidx.fragment.app.Fragment |  | ||||||
| import com.mikepenz.aboutlibraries.LibsBuilder |  | ||||||
| import net.helcel.beans.R |  | ||||||
| import net.helcel.beans.databinding.FragmentLicenseBinding |  | ||||||
|  |  | ||||||
| class LicenseFragment : Fragment() { |  | ||||||
|     private lateinit var _binding: FragmentLicenseBinding |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     override fun onCreateView( |  | ||||||
|         inflater: LayoutInflater, |  | ||||||
|         container: ViewGroup?, |  | ||||||
|         savedInstanceState: Bundle? |  | ||||||
|     ): View { |  | ||||||
|         _binding = FragmentLicenseBinding.inflate(inflater, container, false) |  | ||||||
|  |  | ||||||
|         val librariesFragment = LibsBuilder() |  | ||||||
|             .withLicenseShown(true) |  | ||||||
|             .supportFragment() |  | ||||||
|  |  | ||||||
|         requireActivity().supportFragmentManager.beginTransaction() |  | ||||||
|             .replace(R.id.license_fragment_view, librariesFragment) |  | ||||||
|             .commit() |  | ||||||
|  |  | ||||||
|         return _binding.root |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,142 +0,0 @@ | |||||||
| package net.helcel.beans.activity.fragment |  | ||||||
|  |  | ||||||
| import android.content.Context |  | ||||||
| import android.os.Bundle |  | ||||||
| import androidx.appcompat.app.AppCompatDelegate |  | ||||||
| import androidx.preference.Preference |  | ||||||
| import androidx.preference.PreferenceFragmentCompat |  | ||||||
| import androidx.preference.PreferenceManager |  | ||||||
| import com.google.android.material.dialog.MaterialAlertDialogBuilder |  | ||||||
| import net.helcel.beans.R |  | ||||||
| import net.helcel.beans.countries.GeoLocImporter |  | ||||||
| import net.helcel.beans.helper.Data |  | ||||||
| import net.helcel.beans.helper.DialogCloser |  | ||||||
| import net.helcel.beans.helper.Settings |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class SettingsFragment : PreferenceFragmentCompat(), DialogCloser { |  | ||||||
|     private var savedInstanceState: Bundle? = null |  | ||||||
|     private var rootKey: String? = null |  | ||||||
|  |  | ||||||
|     override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { |  | ||||||
|         this.savedInstanceState = savedInstanceState |  | ||||||
|         this.rootKey = rootKey |  | ||||||
|  |  | ||||||
|         setPreferencesFromResource(R.xml.fragment_settings, rootKey) |  | ||||||
|         val ctx = requireContext() |  | ||||||
|  |  | ||||||
|         // Select Light/Dark/System Mode |  | ||||||
|         findPreference<Preference>(getString(R.string.key_theme))?.setOnPreferenceChangeListener { _, key -> |  | ||||||
|             setTheme(ctx, key as String) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Select map projection |  | ||||||
|         findPreference<Preference>(getString(R.string.key_projection))?.setOnPreferenceChangeListener { _, key -> |  | ||||||
|             Settings.refreshProjection() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Toggle groups |  | ||||||
|         findPreference<Preference>(getString(R.string.key_group))?.setOnPreferenceChangeListener { _, key -> |  | ||||||
|             if (key as String == ctx.getString(R.string.off)) { |  | ||||||
|                 val fragment = EditPlaceColorFragment(this, true) |  | ||||||
|                 fragment.show( |  | ||||||
|                     this.parentFragmentManager, |  | ||||||
|                     "AddColorDialogFragment" |  | ||||||
|                 ) |  | ||||||
|                 false |  | ||||||
|             } else { |  | ||||||
|                 true |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Toggle regional geolocs |  | ||||||
|         findPreference<Preference>(getString(R.string.key_regional))?.setOnPreferenceChangeListener { _, key -> |  | ||||||
|             when (key as String) { |  | ||||||
|                 ctx.getString(R.string.off) -> { |  | ||||||
|                     MaterialAlertDialogBuilder(requireActivity()) |  | ||||||
|                         .setMessage(R.string.delete_regions) |  | ||||||
|                         .setPositiveButton(android.R.string.ok) { _, _ -> |  | ||||||
|                             GeoLocImporter.clearStates() |  | ||||||
|                             PreferenceManager.getDefaultSharedPreferences(ctx).edit().putString( |  | ||||||
|                                 ctx.getString(R.string.key_regional), |  | ||||||
|                                 ctx.getString(R.string.off) |  | ||||||
|                             ).apply() |  | ||||||
|                             refreshPreferences() |  | ||||||
|                         } |  | ||||||
|                         .setNegativeButton(android.R.string.cancel) { _, _ -> } |  | ||||||
|                         .show() |  | ||||||
|                     false |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 ctx.getString(R.string.on) -> { |  | ||||||
|                     GeoLocImporter.importStates(ctx, true) |  | ||||||
|                     true |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 else -> false |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         // Open license fragment |  | ||||||
|         findPreference<Preference>(getString(R.string.licenses))?.setOnPreferenceClickListener { |  | ||||||
|             requireActivity().supportFragmentManager.beginTransaction() |  | ||||||
|                 .replace(R.id.fragment_view, LicenseFragment(), getString(R.string.licenses)) |  | ||||||
|                 .commit() |  | ||||||
|             true |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Open about fragment |  | ||||||
|         findPreference<Preference>(getString(R.string.about))?.setOnPreferenceClickListener { |  | ||||||
|             requireActivity().supportFragmentManager.beginTransaction() |  | ||||||
|                 .replace(R.id.fragment_view, AboutFragment(), getString(R.string.about)) |  | ||||||
|                 .commit() |  | ||||||
|             true |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     companion object { |  | ||||||
|         fun setTheme(ctx: Context, key: String?): Boolean { |  | ||||||
|             AppCompatDelegate.setDefaultNightMode( |  | ||||||
|                 when (key) { |  | ||||||
|                     ctx.getString(R.string.system) -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM |  | ||||||
|                     ctx.getString(R.string.light) -> AppCompatDelegate.MODE_NIGHT_NO |  | ||||||
|                     ctx.getString(R.string.dark) -> AppCompatDelegate.MODE_NIGHT_YES |  | ||||||
|                     else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM |  | ||||||
|                 } |  | ||||||
|             ) |  | ||||||
|             return true |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun onDialogDismiss(clear: Boolean) { |  | ||||||
|         // When turning groups off, select one group to keep and reassign everything |  | ||||||
|         Data.selected_group?.let { selectedGroup -> |  | ||||||
|             // Reassign all visited that are not to selectedGroup to selectedGroup |  | ||||||
|             Data.visits.reassignAllVisitedToGroup(selectedGroup.key) |  | ||||||
|  |  | ||||||
|             // Delete all groups that are not selectedGroup |  | ||||||
|             Data.groups.deleteAllExcept(selectedGroup.key) |  | ||||||
|  |  | ||||||
|             // Save and clear global variables |  | ||||||
|             Data.saveData() |  | ||||||
|             Data.selected_geoloc = null |  | ||||||
|             Data.selected_group = null |  | ||||||
|  |  | ||||||
|             // Actually change preference |  | ||||||
|             val ctx = requireContext() |  | ||||||
|             val sp = PreferenceManager.getDefaultSharedPreferences(ctx) |  | ||||||
|             sp.edit().putString(ctx.getString(R.string.key_group), ctx.getString(R.string.off)) |  | ||||||
|                 .apply() |  | ||||||
|  |  | ||||||
|             // Refresh entire preference fragment to reflect changes |  | ||||||
|             refreshPreferences() |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private fun refreshPreferences() { |  | ||||||
|         preferenceScreen.removeAll() |  | ||||||
|         onCreatePreferences(savedInstanceState, rootKey) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,78 @@ | |||||||
|  | package net.helcel.beans.activity.sub | ||||||
|  |  | ||||||
|  | import androidx.compose.foundation.Image | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.clickable | ||||||
|  | import androidx.compose.foundation.layout.* | ||||||
|  | import androidx.compose.material.MaterialTheme | ||||||
|  | import androidx.compose.material.Text | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.ui.Alignment | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.platform.LocalUriHandler | ||||||
|  | import androidx.compose.ui.res.painterResource | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
|  | import androidx.compose.ui.text.font.FontWeight | ||||||
|  | import androidx.compose.ui.text.style.TextAlign | ||||||
|  | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
|  | import androidx.compose.ui.unit.sp | ||||||
|  | import net.helcel.beans.R | ||||||
|  | import net.helcel.beans.BuildConfig | ||||||
|  |  | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun AboutScreen( | ||||||
|  |     modifier: Modifier = Modifier | ||||||
|  | ) { | ||||||
|  |     Column( | ||||||
|  |         modifier = modifier | ||||||
|  |             .fillMaxSize() | ||||||
|  |             .padding(top = 20.dp).background(MaterialTheme.colors.background), | ||||||
|  |         horizontalAlignment = Alignment.CenterHorizontally, | ||||||
|  |     ) { | ||||||
|  |         Image( | ||||||
|  |             painter = painterResource(id = R.drawable.ic_launcher_foreground), | ||||||
|  |             contentDescription = "Logo", | ||||||
|  |             modifier = Modifier | ||||||
|  |                 .size(300.dp) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         Text( | ||||||
|  |             text = BuildConfig.APP_NAME, | ||||||
|  |             fontSize = 30.sp, | ||||||
|  |             color = MaterialTheme.colors.onBackground, | ||||||
|  |             fontWeight = FontWeight.Bold, | ||||||
|  |             textAlign = TextAlign.Center, | ||||||
|  |             modifier = Modifier.padding(vertical = 15.dp, horizontal = 10.dp) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         Text( | ||||||
|  |             text = BuildConfig.VERSION_NAME, | ||||||
|  |             fontSize = 25.sp, | ||||||
|  |             color = MaterialTheme.colors.onBackground, | ||||||
|  |             textAlign = TextAlign.Center, | ||||||
|  |             modifier = Modifier.padding(vertical = 15.dp, horizontal = 10.dp) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         Text( | ||||||
|  |             text = stringResource(R.string.beans_is_foss), | ||||||
|  |             textAlign = TextAlign.Center, | ||||||
|  |             color = MaterialTheme.colors.onBackground, | ||||||
|  |             modifier = Modifier.padding(vertical = 15.dp, horizontal = 10.dp) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         val uriHandler = LocalUriHandler.current | ||||||
|  |         val uri = stringResource(R.string.beans_repo_uri) | ||||||
|  |         Text( | ||||||
|  |             text = stringResource(id = R.string.beans_repo,uri), | ||||||
|  |             textAlign = TextAlign.Center, | ||||||
|  |             color = MaterialTheme.colors.onBackground, | ||||||
|  |             modifier = Modifier | ||||||
|  |                 .clickable { | ||||||
|  |                     uriHandler.openUri(uri) | ||||||
|  |                 } | ||||||
|  |                 .padding(vertical = 15.dp, horizontal = 10.dp) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,222 @@ | |||||||
|  | package net.helcel.beans.activity.sub | ||||||
|  |  | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.layout.Arrangement | ||||||
|  | import androidx.compose.foundation.layout.Box | ||||||
|  | import androidx.compose.foundation.layout.Column | ||||||
|  | import androidx.compose.foundation.layout.Row | ||||||
|  | import androidx.compose.foundation.layout.Spacer | ||||||
|  | import androidx.compose.foundation.layout.fillMaxWidth | ||||||
|  | import androidx.compose.foundation.layout.height | ||||||
|  | import androidx.compose.foundation.layout.padding | ||||||
|  | import androidx.compose.foundation.layout.size | ||||||
|  | import androidx.compose.foundation.shape.CornerSize | ||||||
|  | import androidx.compose.foundation.shape.RoundedCornerShape | ||||||
|  | import androidx.compose.material.Button | ||||||
|  | import androidx.compose.material.MaterialTheme | ||||||
|  | import androidx.compose.material.Slider | ||||||
|  | import androidx.compose.material.SliderDefaults | ||||||
|  | import androidx.compose.material.Text | ||||||
|  | import androidx.compose.material.TextButton | ||||||
|  | import androidx.compose.material3.OutlinedTextField | ||||||
|  | import androidx.compose.material3.OutlinedTextFieldDefaults | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.runtime.getValue | ||||||
|  | import androidx.compose.runtime.mutableIntStateOf | ||||||
|  | import androidx.compose.runtime.mutableStateOf | ||||||
|  | import androidx.compose.runtime.remember | ||||||
|  | import androidx.compose.runtime.setValue | ||||||
|  | import androidx.compose.ui.Alignment | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.draw.clip | ||||||
|  | import androidx.compose.ui.graphics.Color | ||||||
|  | import androidx.compose.ui.graphics.toArgb | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
|  | import androidx.compose.ui.text.TextStyle | ||||||
|  | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
|  | import androidx.compose.ui.unit.sp | ||||||
|  | import androidx.compose.ui.window.Dialog | ||||||
|  | import net.helcel.beans.helper.Data | ||||||
|  | import net.helcel.beans.helper.Theme.colorToHex6 | ||||||
|  | import androidx.core.graphics.drawable.toDrawable | ||||||
|  | import androidx.core.graphics.toColorInt | ||||||
|  | import net.helcel.beans.R | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun EditGroupPreview(){ | ||||||
|  |     EditGroupDialog(0,true,{},{},{}) | ||||||
|  | } | ||||||
|  | @Composable | ||||||
|  | fun EditGroupDialog( | ||||||
|  |     key: Int = 0, | ||||||
|  |     deleteEnabled: Boolean = true, | ||||||
|  |     onAddCb: (Int) -> Unit, | ||||||
|  |     onDelCb: (Int) -> Unit, | ||||||
|  |     onDismiss: () -> Unit | ||||||
|  | ) { | ||||||
|  |     val group by remember { mutableStateOf(Data.groups.getGroupFromKey(key)) } | ||||||
|  |     var name by remember { mutableStateOf(group.name) } | ||||||
|  |     var colorHex by remember { | ||||||
|  |         mutableStateOf(colorToHex6(group.color.color.toDrawable()).substring(1)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Convert hex to Color safely | ||||||
|  |     var color = remember {try { | ||||||
|  |         Color("#$colorHex".toColorInt()) | ||||||
|  |     } catch (_: Exception) { | ||||||
|  |         Color.Gray | ||||||
|  |     }} | ||||||
|  |     var r by remember { mutableIntStateOf((color.red *255).toInt()) } | ||||||
|  |     var g by remember { mutableIntStateOf((color.green*255).toInt()) } | ||||||
|  |     var b by remember { mutableIntStateOf((color.blue*255).toInt()) } | ||||||
|  |  | ||||||
|  |     fun updateHexFromSliders() { | ||||||
|  |         val newColor = Color(r, g, b) | ||||||
|  |         colorHex = colorToHex6(newColor.toArgb().toDrawable()).substring(1) | ||||||
|  |         color = newColor | ||||||
|  |     } | ||||||
|  |         Dialog( | ||||||
|  |             onDismissRequest = onDismiss, | ||||||
|  |             content = { | ||||||
|  |                 Column( | ||||||
|  |                     modifier = Modifier | ||||||
|  |                         .background( | ||||||
|  |                             MaterialTheme.colors.background, | ||||||
|  |                             RoundedCornerShape(corner = CornerSize(16.dp)) | ||||||
|  |                         ) | ||||||
|  |                         .padding(16.dp), | ||||||
|  |  | ||||||
|  |                     ) { | ||||||
|  |                     Text( | ||||||
|  |                         color = MaterialTheme.colors.onBackground, | ||||||
|  |                         style = MaterialTheme.typography.h6, | ||||||
|  |                         text = if (key == 0) stringResource(R.string.action_add) | ||||||
|  |                         else stringResource(R.string.action_edit), | ||||||
|  |                     ) | ||||||
|  |  | ||||||
|  |                     Spacer(modifier = Modifier.height(16.dp)) | ||||||
|  |                     Column( | ||||||
|  |                         verticalArrangement = Arrangement.spacedBy(12.dp) | ||||||
|  |                     ) { | ||||||
|  |                         // Group name | ||||||
|  |                         OutlinedTextField( | ||||||
|  |                             value = name, | ||||||
|  |                             onValueChange = { it: String -> name = it }, | ||||||
|  |                             modifier = Modifier.fillMaxWidth(), | ||||||
|  |                             placeholder = { Text(stringResource(R.string.name)) }, | ||||||
|  |                             colors = OutlinedTextFieldDefaults.colors( | ||||||
|  |                                 unfocusedTextColor = MaterialTheme.colors.onBackground, | ||||||
|  |                                 focusedTextColor = MaterialTheme.colors.onBackground, | ||||||
|  |                             ), | ||||||
|  |                         ) | ||||||
|  |                         Row( | ||||||
|  |                             horizontalArrangement = Arrangement.spacedBy(16.dp), | ||||||
|  |                             verticalAlignment = Alignment.CenterVertically | ||||||
|  |                         ) { | ||||||
|  |  | ||||||
|  |                             // Color preview | ||||||
|  |                             Box( | ||||||
|  |                                 modifier = Modifier | ||||||
|  |                                     .size(96.dp, (96).dp) | ||||||
|  |                                     .clip(RoundedCornerShape(8.dp)) | ||||||
|  |                                     .background(color), | ||||||
|  |                                 propagateMinConstraints = true, | ||||||
|  |  | ||||||
|  |                                 content = {} | ||||||
|  |                             ) | ||||||
|  |                             Column { | ||||||
|  |                                 ColorSlider( | ||||||
|  |                                     r.toFloat(), | ||||||
|  |                                     { r = it.toInt(); updateHexFromSliders() }, | ||||||
|  |                                     Color(255, 0, 0) | ||||||
|  |                                 ) | ||||||
|  |                                 ColorSlider( | ||||||
|  |                                     g.toFloat(), | ||||||
|  |                                     { g = it.toInt(); updateHexFromSliders() }, | ||||||
|  |                                     Color(0, 255, 0) | ||||||
|  |                                 ) | ||||||
|  |                                 ColorSlider( | ||||||
|  |                                     b.toFloat(), | ||||||
|  |                                     { b = it.toInt(); updateHexFromSliders() }, | ||||||
|  |                                     Color(0, 0, 255) | ||||||
|  |                                 ) | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         // Hex input | ||||||
|  |                         OutlinedTextField( | ||||||
|  |                             value = colorHex, | ||||||
|  |                             onValueChange = { n:String-> | ||||||
|  |                                 colorHex = n.filter { it.isLetterOrDigit() } | ||||||
|  |                             }, | ||||||
|  |                             label = { Text(text="Color (hex)", color=MaterialTheme.colors.onBackground) }, | ||||||
|  |                             singleLine = true, | ||||||
|  |                             textStyle = TextStyle( | ||||||
|  |                                 fontSize = 12.sp | ||||||
|  |                             ), | ||||||
|  |                             modifier = Modifier.fillMaxWidth(), | ||||||
|  |                             colors = OutlinedTextFieldDefaults.colors( | ||||||
|  |                                 unfocusedTextColor =MaterialTheme.colors.onBackground, | ||||||
|  |                                 focusedTextColor = MaterialTheme.colors.onBackground, | ||||||
|  |                             ), | ||||||
|  |  | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                     Spacer(modifier = Modifier.height(8.dp)) | ||||||
|  |  | ||||||
|  |                     Row( | ||||||
|  |                         modifier = Modifier.fillMaxWidth(), | ||||||
|  |                         horizontalArrangement = Arrangement.End | ||||||
|  |                     ) { | ||||||
|  |                         Button(onClick = { | ||||||
|  |                             val newKey = if (key != 0) key else Data.groups.genKey() | ||||||
|  |                             Data.groups.setGroup( | ||||||
|  |                                 newKey, | ||||||
|  |                                 name, | ||||||
|  |                                 "#$colorHex".toColorInt().toDrawable() | ||||||
|  |                             ) | ||||||
|  |                             Data.saveData() | ||||||
|  |                             onAddCb(newKey) | ||||||
|  |                             onDismiss() | ||||||
|  |                         }, ) { | ||||||
|  |                             Text("OK") | ||||||
|  |                         } | ||||||
|  |                         if (key != 0 && deleteEnabled) { | ||||||
|  |                             TextButton(onClick = { | ||||||
|  |                                 val pos = Data.groups.findGroupPos(key) | ||||||
|  |                                 Data.visits.deleteVisited(key) | ||||||
|  |                                 Data.groups.deleteGroup(key) | ||||||
|  |                                 Data.saveData() | ||||||
|  |                                 onDelCb(pos) | ||||||
|  |                                 onDismiss() | ||||||
|  |                             }) { | ||||||
|  |                                 Text("Delete") | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         TextButton(onClick = { onDismiss() }) { | ||||||
|  |                             Text("Cancel") | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun ColorSlider(v: Float, onChange:(Float)->Unit, c:Color ){ | ||||||
|  |     Slider( | ||||||
|  |         value = v, | ||||||
|  |         onValueChange = onChange, | ||||||
|  |         valueRange = 0f..255f, | ||||||
|  |         steps = 255, | ||||||
|  |         modifier = Modifier.height(32.dp), | ||||||
|  |         colors = SliderDefaults.colors( | ||||||
|  |             thumbColor = c, | ||||||
|  |             activeTickColor = c, | ||||||
|  |             inactiveTickColor = MaterialTheme.colors.onBackground, | ||||||
|  |         ) | ||||||
|  |     ) | ||||||
|  | } | ||||||
| @@ -0,0 +1,193 @@ | |||||||
|  | package net.helcel.beans.activity.sub | ||||||
|  |  | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.combinedClickable | ||||||
|  | import androidx.compose.foundation.layout.Arrangement | ||||||
|  | import androidx.compose.foundation.layout.Box | ||||||
|  | import androidx.compose.foundation.layout.Column | ||||||
|  | import androidx.compose.foundation.layout.Row | ||||||
|  | import androidx.compose.foundation.layout.Spacer | ||||||
|  | import androidx.compose.foundation.layout.fillMaxWidth | ||||||
|  | import androidx.compose.foundation.layout.height | ||||||
|  | import androidx.compose.foundation.layout.heightIn | ||||||
|  | import androidx.compose.foundation.layout.padding | ||||||
|  | import androidx.compose.foundation.layout.size | ||||||
|  | import androidx.compose.foundation.layout.width | ||||||
|  | import androidx.compose.foundation.lazy.LazyColumn | ||||||
|  | import androidx.compose.foundation.lazy.items | ||||||
|  | import androidx.compose.foundation.shape.CircleShape | ||||||
|  | import androidx.compose.foundation.shape.CornerSize | ||||||
|  | import androidx.compose.foundation.shape.RoundedCornerShape | ||||||
|  | import androidx.compose.material.Button | ||||||
|  | import androidx.compose.material.MaterialTheme | ||||||
|  | import androidx.compose.material.Text | ||||||
|  | import androidx.compose.material.TextButton | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.runtime.collectAsState | ||||||
|  | import androidx.compose.runtime.getValue | ||||||
|  | import androidx.compose.runtime.mutableIntStateOf | ||||||
|  | import androidx.compose.runtime.mutableStateOf | ||||||
|  | import androidx.compose.runtime.remember | ||||||
|  | import androidx.compose.runtime.setValue | ||||||
|  | import androidx.compose.ui.Alignment | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.graphics.Color | ||||||
|  | import androidx.compose.ui.graphics.toArgb | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
|  | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
|  | import androidx.compose.ui.window.Dialog | ||||||
|  | import net.helcel.beans.R | ||||||
|  | import net.helcel.beans.helper.Data | ||||||
|  | import net.helcel.beans.helper.Groups | ||||||
|  | import androidx.core.graphics.drawable.toDrawable | ||||||
|  | import net.helcel.beans.activity.SysTheme | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun EditPlaceDialog(delete: Boolean, onDialogDismiss: (Boolean)->Unit){ | ||||||
|  |     SysTheme { | ||||||
|  |         var showEditGroupDialog by remember { mutableStateOf(false) } | ||||||
|  |         var showEditPlaceColorDialog by remember { mutableStateOf(true) } | ||||||
|  |         var showSelectedKey by remember { mutableIntStateOf(-1) } | ||||||
|  |         var showDelete by remember { mutableStateOf(false) } | ||||||
|  |         if (showEditGroupDialog) | ||||||
|  |             EditGroupDialog( | ||||||
|  |                 key = showSelectedKey, | ||||||
|  |                 deleteEnabled = showDelete, | ||||||
|  |                 onAddCb = { }, | ||||||
|  |                 onDelCb = { | ||||||
|  |  | ||||||
|  |                 }, | ||||||
|  |                 onDismiss = { | ||||||
|  |                     showEditGroupDialog = false | ||||||
|  |                 }, | ||||||
|  |             ) | ||||||
|  |         if (showEditPlaceColorDialog) | ||||||
|  |             EditPlaceColorDialog( | ||||||
|  |                 delete, | ||||||
|  |                 onAdd = { | ||||||
|  |                     showSelectedKey = it | ||||||
|  |                     showDelete = false | ||||||
|  |                     showEditGroupDialog = true | ||||||
|  |                 }, | ||||||
|  |                 onDelete = { | ||||||
|  |                     showSelectedKey = it | ||||||
|  |                     showDelete = true | ||||||
|  |                     showEditGroupDialog = true | ||||||
|  |                 }, | ||||||
|  |                 onClear = { | ||||||
|  |                     showEditPlaceColorDialog = false | ||||||
|  |                     onDialogDismiss(true) | ||||||
|  |                 }, | ||||||
|  |                 onDismiss = { | ||||||
|  |                     showEditPlaceColorDialog = false | ||||||
|  |                     onDialogDismiss(false) | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun GroupListPreview() { | ||||||
|  |     Data.groups = Groups(0, HashMap()) | ||||||
|  |     Data.groups.setGroup(0, "Testing", Color.Red.toArgb().toDrawable()) | ||||||
|  |     Data.groups.setGroup(1, "Testing", Color.Blue.toArgb().toDrawable()) | ||||||
|  |     EditPlaceColorDialog(false,{},{},{},{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun EditPlaceColorDialog( | ||||||
|  |     deleteMode: Boolean = false, | ||||||
|  |     onAdd: (Int) -> Unit = {}, | ||||||
|  |     onDelete: (Int) -> Unit= {}, | ||||||
|  |     onClear: () -> Unit= {}, | ||||||
|  |     onDismiss: () -> Unit= {}, | ||||||
|  | ) { | ||||||
|  |     val groups by Data.groups.groupsFlow.collectAsState() | ||||||
|  |  | ||||||
|  |     Dialog( | ||||||
|  |         onDismissRequest = onDismiss, | ||||||
|  |         content = { | ||||||
|  |             Column( | ||||||
|  |                 modifier = Modifier | ||||||
|  |                     .background( | ||||||
|  |                         MaterialTheme.colors.background, | ||||||
|  |                         RoundedCornerShape(corner = CornerSize(16.dp))) | ||||||
|  |                     .padding(16.dp) | ||||||
|  |                 , | ||||||
|  |  | ||||||
|  |                 ) { | ||||||
|  |                 Text( | ||||||
|  |                     style = MaterialTheme.typography.h6, | ||||||
|  |                     color=MaterialTheme.colors.onBackground, | ||||||
|  |                     text = if (deleteMode) stringResource(R.string.select_group) | ||||||
|  |                     else stringResource(R.string.edit_group) | ||||||
|  |                 ) | ||||||
|  |                 Text( | ||||||
|  |                     style = MaterialTheme.typography.caption, | ||||||
|  |                     color=MaterialTheme.colors.onBackground, | ||||||
|  |                     text = if (deleteMode) stringResource(R.string.select_group_sub) | ||||||
|  |                     else stringResource(R.string.edit_group_sub) | ||||||
|  |                 ) | ||||||
|  |                 Spacer(modifier = Modifier.height(16.dp)) | ||||||
|  |                 Box( | ||||||
|  |                     modifier = Modifier | ||||||
|  |                         .fillMaxWidth() | ||||||
|  |                         .heightIn(max = 300.dp) // cap dialog growth | ||||||
|  |                 ) { | ||||||
|  |                     LazyColumn( | ||||||
|  |                         modifier = Modifier | ||||||
|  |                             .fillMaxWidth() | ||||||
|  |                         //.weight(1f) | ||||||
|  |                     ) { | ||||||
|  |                         items(groups, key = { it.key }) { group -> | ||||||
|  |                             Row( | ||||||
|  |                                 modifier = Modifier | ||||||
|  |                                     .fillMaxWidth() | ||||||
|  |                                     .combinedClickable( | ||||||
|  |                                         onClick = { Data.selected_group = group; onDismiss() }, | ||||||
|  |                                         onLongClick = { onDelete(group.key) }) | ||||||
|  |                                     .background( | ||||||
|  |                                         Color(88, 88, 88, 88), | ||||||
|  |                                         RoundedCornerShape(corner = CornerSize(16.dp)) | ||||||
|  |                                     ) | ||||||
|  |                                     .padding(8.dp), | ||||||
|  |                                 verticalAlignment = Alignment.CenterVertically, | ||||||
|  |  | ||||||
|  |                                 ) { | ||||||
|  |                                 Box( | ||||||
|  |                                     modifier = Modifier | ||||||
|  |                                         .size(24.dp) | ||||||
|  |                                         .background(Color(group.color.color), CircleShape) | ||||||
|  |                                 ) | ||||||
|  |                                 Spacer(modifier = Modifier.width(8.dp)) | ||||||
|  |                                 Text(color=MaterialTheme.colors.onBackground,text=group.name) | ||||||
|  |                             } | ||||||
|  |                             Spacer(modifier = Modifier.height(8.dp)) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Spacer(modifier = Modifier.height(8.dp)) | ||||||
|  |  | ||||||
|  |                 Row( | ||||||
|  |                     modifier = Modifier.fillMaxWidth(), | ||||||
|  |                     horizontalArrangement = Arrangement.End) { | ||||||
|  |                     if (!deleteMode) { | ||||||
|  |                         Button(onClick = { onAdd(0) }) { | ||||||
|  |                             Text(stringResource(R.string.action_add)) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     TextButton(onClick = { | ||||||
|  |                         if (deleteMode) onDismiss() else onClear() | ||||||
|  |                     }) { | ||||||
|  |                         Text( | ||||||
|  |                             text = if (deleteMode) stringResource(R.string.cancel) | ||||||
|  |                             else stringResource(R.string.action_clear) | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |     ) | ||||||
|  | } | ||||||
| @@ -0,0 +1,218 @@ | |||||||
|  | package net.helcel.beans.activity.sub | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import androidx.activity.compose.BackHandler | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.clickable | ||||||
|  | import androidx.compose.foundation.layout.Column | ||||||
|  | import androidx.compose.foundation.layout.Row | ||||||
|  | import androidx.compose.foundation.layout.Spacer | ||||||
|  | import androidx.compose.foundation.layout.fillMaxSize | ||||||
|  | import androidx.compose.foundation.layout.fillMaxWidth | ||||||
|  | import androidx.compose.foundation.layout.height | ||||||
|  | import androidx.compose.foundation.layout.padding | ||||||
|  | import androidx.compose.foundation.layout.size | ||||||
|  | import androidx.compose.foundation.lazy.LazyColumn | ||||||
|  | import androidx.compose.foundation.lazy.items | ||||||
|  | import androidx.compose.material.CheckboxDefaults | ||||||
|  | import androidx.compose.material.MaterialTheme | ||||||
|  | import androidx.compose.material.Tab | ||||||
|  | import androidx.compose.material.TabRow | ||||||
|  | import androidx.compose.material.Text | ||||||
|  | import androidx.compose.material.TriStateCheckbox | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.runtime.LaunchedEffect | ||||||
|  | import androidx.compose.runtime.SideEffect | ||||||
|  | import androidx.compose.runtime.collectAsState | ||||||
|  | import androidx.compose.runtime.derivedStateOf | ||||||
|  | import androidx.compose.runtime.getValue | ||||||
|  | import androidx.compose.runtime.mutableIntStateOf | ||||||
|  | import androidx.compose.runtime.mutableStateListOf | ||||||
|  | import androidx.compose.runtime.mutableStateOf | ||||||
|  | import androidx.compose.runtime.remember | ||||||
|  | import androidx.compose.runtime.setValue | ||||||
|  | import androidx.compose.runtime.snapshots.SnapshotStateList | ||||||
|  | import androidx.compose.ui.Alignment | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.graphics.Color | ||||||
|  | import androidx.compose.ui.platform.LocalContext | ||||||
|  | import androidx.compose.ui.state.ToggleableState | ||||||
|  | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
|  | import net.helcel.beans.countries.GeoLoc | ||||||
|  | import net.helcel.beans.countries.Group | ||||||
|  | import net.helcel.beans.countries.World | ||||||
|  | import net.helcel.beans.helper.AUTO_GROUP | ||||||
|  | import net.helcel.beans.helper.Data | ||||||
|  | import net.helcel.beans.helper.NO_GROUP | ||||||
|  | import net.helcel.beans.helper.Settings | ||||||
|  | import kotlin.math.min | ||||||
|  |  | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun EditPlaceScreenPreview(){ | ||||||
|  |     EditPlaceScreen(Group.EEE) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun syncVisited(loc: GeoLoc?=World.WWW){ | ||||||
|  |     loc?.children?.forEach { tt -> | ||||||
|  |         tt.children.forEach {itc-> | ||||||
|  |             if(Data.visits.getVisited(itc) in listOf(AUTO_GROUP,NO_GROUP)) { | ||||||
|  |                 if(itc.children.any { itcc -> Data.visits.getVisited(itcc) != NO_GROUP }) | ||||||
|  |                     Data.visits.setVisited(itc, AUTO_GROUP) | ||||||
|  |                 else | ||||||
|  |                     Data.visits.setVisited(itc, NO_GROUP) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(Data.visits.getVisited(tt) in listOf(AUTO_GROUP,NO_GROUP)) { | ||||||
|  |             if(tt.children.any { itc -> Data.visits.getVisited(itc) != NO_GROUP }) | ||||||
|  |                 Data.visits.setVisited(tt, AUTO_GROUP) | ||||||
|  |             else | ||||||
|  |                 Data.visits.setVisited(tt, NO_GROUP) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun EditPlaceScreen(loc: GeoLoc, onExit:()->Unit={}) { | ||||||
|  |     var showEdit by remember { mutableStateOf(false) } | ||||||
|  |     val tabs : SnapshotStateList<GeoLoc> = remember { mutableStateListOf(loc) } | ||||||
|  |     val ctx = LocalContext.current | ||||||
|  |     var selectedTab by remember { mutableIntStateOf(0) } | ||||||
|  |  | ||||||
|  |     LaunchedEffect(tabs.size) { | ||||||
|  |         selectedTab = tabs.lastIndex | ||||||
|  |     } | ||||||
|  |     SideEffect { | ||||||
|  |         syncVisited() | ||||||
|  |     } | ||||||
|  |     BackHandler { | ||||||
|  |         if (tabs.size > 1) tabs.removeAt(tabs.lastIndex) | ||||||
|  |         else onExit() | ||||||
|  |     } | ||||||
|  |     if(showEdit) | ||||||
|  |         EditPlaceDialog(false) { | ||||||
|  |             showEdit = false | ||||||
|  |             if (it) { | ||||||
|  |                 Data.visits.setVisited(Data.selected_geoloc, NO_GROUP) | ||||||
|  |                 Data.saveData() | ||||||
|  |  | ||||||
|  |                 if (Data.selected_geoloc!=null && Data.selected_geoloc!!.children.any { itc-> Data.visits.getVisited(itc) != NO_GROUP }) { | ||||||
|  |                     Data.clearing_geoloc = Data.selected_geoloc | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (Data.selected_group != null && Data.selected_geoloc != null) { | ||||||
|  |                 Data.visits.setVisited(Data.selected_geoloc, Data.selected_group!!.key) | ||||||
|  |                 Data.saveData() | ||||||
|  |             } | ||||||
|  |             Data.selected_geoloc = null | ||||||
|  |             Data.selected_group = null | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     Column { | ||||||
|  |         val currentTab = tabs.getOrNull(selectedTab) ?: return@Column | ||||||
|  |             TabRow( | ||||||
|  |                 selectedTabIndex = min(tabs.lastIndex, selectedTab), | ||||||
|  |             ) { | ||||||
|  |                 tabs.forEachIndexed { index, tab -> | ||||||
|  |                     Tab( | ||||||
|  |                         selected = selectedTab == index, | ||||||
|  |                         onClick = { | ||||||
|  |                             while (tabs.size > index + 1) | ||||||
|  |                                 tabs.removeAt(tabs.lastIndex) | ||||||
|  |                         }, | ||||||
|  |                         text = { Text(tab.fullName) } | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         LazyColumn( | ||||||
|  |             modifier = Modifier.fillMaxSize() | ||||||
|  |         ) { | ||||||
|  |             items(currentTab.children.toList(), key= {it.code}) { loc -> | ||||||
|  |  | ||||||
|  |                 GeoLocRow(loc, { | ||||||
|  |                         if (loc.children.isNotEmpty()){ | ||||||
|  |                             tabs.add(loc) | ||||||
|  |                         } | ||||||
|  |                     }, { | ||||||
|  |                         Data.selected_geoloc = loc | ||||||
|  |                         if (Data.groups.size() == 1 && Settings.isSingleGroup(ctx)) { | ||||||
|  |                             Data.visits.setVisited(Data.selected_geoloc, | ||||||
|  |                                 if (it != ToggleableState.On) Data.groups.getUniqueEntry()!!.key | ||||||
|  |                                 else if(Data.selected_geoloc?.children?.any{ itc-> | ||||||
|  |                                         Data.visits.getVisited(itc)!= NO_GROUP } == true) AUTO_GROUP | ||||||
|  |                                 else NO_GROUP | ||||||
|  |                             ) | ||||||
|  |                             Data.selected_group = null | ||||||
|  |                         } else { | ||||||
|  |                             Data.selected_group = null | ||||||
|  |                             showEdit=true | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                     }) | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun GeoLocRow( | ||||||
|  |     loc: GeoLoc, | ||||||
|  |     onClick: () -> Unit, | ||||||
|  |     onCheckedChange: (ToggleableState) -> Unit | ||||||
|  | ) { | ||||||
|  |     val visits by Data.visits.visitsFlow.collectAsState() | ||||||
|  |     val checked by remember(visits, loc) { | ||||||
|  |         derivedStateOf { | ||||||
|  |             when (visits.getOrElse(loc.code) { NO_GROUP }) { | ||||||
|  |                 NO_GROUP -> ToggleableState.Off | ||||||
|  |                 AUTO_GROUP -> ToggleableState.Indeterminate | ||||||
|  |                 else -> ToggleableState.On | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val color = if (Data.visits.getVisited(loc) !in listOf(NO_GROUP, AUTO_GROUP)) | ||||||
|  |         Color(Data.groups.getGroupFromKey(Data.visits.getVisited(loc)).color.color) | ||||||
|  |     else MaterialTheme.colors.onBackground | ||||||
|  |     Row( | ||||||
|  |         modifier = Modifier | ||||||
|  |             .fillMaxWidth() | ||||||
|  |             .height(50.dp) | ||||||
|  |             .clickable(onClick = onClick) // whole row clickable | ||||||
|  |             .padding(horizontal = 20.dp, vertical = 4.dp), | ||||||
|  |         verticalAlignment = Alignment.CenterVertically | ||||||
|  |     ) { | ||||||
|  |         Text( | ||||||
|  |             text = loc.fullName, | ||||||
|  |             style = MaterialTheme.typography.body2, | ||||||
|  |             color = MaterialTheme.colors.onBackground, | ||||||
|  |             modifier = Modifier.weight(1f) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         Text( | ||||||
|  |             text = "",//loc.children.size.toString(), | ||||||
|  |             style = MaterialTheme.typography.body2, | ||||||
|  |             modifier = Modifier.padding(end = 16.dp) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         TriStateCheckbox( | ||||||
|  |             state = checked, | ||||||
|  |             onClick= { onCheckedChange(checked) }, | ||||||
|  |             colors = CheckboxDefaults.colors( | ||||||
|  |                 checkedColor = color, | ||||||
|  |             ), | ||||||
|  |  | ||||||
|  |             modifier = Modifier.size(24.dp) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |     Spacer(modifier = Modifier | ||||||
|  |         .height(2.dp) | ||||||
|  |         .fillMaxWidth() | ||||||
|  |         .background(MaterialTheme.colors.onBackground)) | ||||||
|  | } | ||||||
| @@ -0,0 +1,43 @@ | |||||||
|  | package net.helcel.beans.activity.sub | ||||||
|  |  | ||||||
|  | import androidx.compose.foundation.layout.fillMaxSize | ||||||
|  | import androidx.compose.material.MaterialTheme | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | import com.mikepenz.aboutlibraries.ui.compose.DefaultChipColors | ||||||
|  | import com.mikepenz.aboutlibraries.ui.compose.DefaultLibraryColors | ||||||
|  | import com.mikepenz.aboutlibraries.ui.compose.android.rememberLibraries | ||||||
|  | import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer | ||||||
|  | import net.helcel.beans.R | ||||||
|  | import net.helcel.beans.activity.SysTheme | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun LicenseScreen() { | ||||||
|  |     val libraries = rememberLibraries(R.raw.aboutlibraries) | ||||||
|  |     SysTheme { | ||||||
|  |         LibrariesContainer( | ||||||
|  |             libraries = libraries.value, | ||||||
|  |             modifier = Modifier.fillMaxSize(), | ||||||
|  |             colors = DefaultLibraryColors( | ||||||
|  |                 backgroundColor = MaterialTheme.colors.background, | ||||||
|  |                 contentColor = MaterialTheme.colors.onBackground, | ||||||
|  |                 licenseChipColors = DefaultChipColors( | ||||||
|  |                     containerColor = MaterialTheme.colors.primary, | ||||||
|  |                     contentColor = MaterialTheme.colors.onPrimary, | ||||||
|  |                 ), | ||||||
|  |                 versionChipColors = DefaultChipColors( | ||||||
|  |                     containerColor = MaterialTheme.colors.secondary, | ||||||
|  |                     contentColor = MaterialTheme.colors.onSecondary, | ||||||
|  |                 ), | ||||||
|  |                 fundingChipColors = DefaultChipColors( | ||||||
|  |                     containerColor = MaterialTheme.colors.secondary, | ||||||
|  |                     contentColor = MaterialTheme.colors.onSecondary, | ||||||
|  |                 ), | ||||||
|  |                 dialogConfirmButtonColor = MaterialTheme.colors.primary, | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -7,8 +7,8 @@ enum class Country( | |||||||
|     ATA("Antarctica", 14000000), |     ATA("Antarctica", 14000000), | ||||||
|  |  | ||||||
|     //    HKG("Hong Kong", 1104), |     //    HKG("Hong Kong", 1104), | ||||||
| //    MAC("Macao", 32), |     //    MAC("Macao", 32), | ||||||
| //    ANT("Netherlands Antilles", 800), |     //    ANT("Netherlands Antilles", 800), | ||||||
|     AFG("Afghanistan", 645487), |     AFG("Afghanistan", 645487), | ||||||
|     XAD("Akrotiri and Dhekelia", 234), |     XAD("Akrotiri and Dhekelia", 234), | ||||||
|     ALA("Åland", 1483), |     ALA("Åland", 1483), | ||||||
|   | |||||||
| @@ -12,4 +12,4 @@ interface GeoLoc { | |||||||
|  |  | ||||||
|     val type: LocType |     val type: LocType | ||||||
|     val children: Set<GeoLoc> |     val children: Set<GeoLoc> | ||||||
| } | } | ||||||
| @@ -1,13 +1,21 @@ | |||||||
| package net.helcel.beans.helper | package net.helcel.beans.helper | ||||||
|  |  | ||||||
|  | import android.content.ContentValues | ||||||
| import android.content.Context | import android.content.Context | ||||||
| import android.content.SharedPreferences | import android.content.SharedPreferences | ||||||
| import android.graphics.drawable.ColorDrawable | import android.net.Uri | ||||||
|  | import android.os.Build | ||||||
|  | import android.os.Environment | ||||||
|  | import android.provider.MediaStore | ||||||
| import androidx.core.content.ContextCompat | import androidx.core.content.ContextCompat | ||||||
| import androidx.preference.PreferenceManager | import androidx.preference.PreferenceManager | ||||||
| import net.helcel.beans.R | import net.helcel.beans.R | ||||||
| import net.helcel.beans.countries.GeoLoc | import net.helcel.beans.countries.GeoLoc | ||||||
| import java.util.HashMap | 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 { | object Data { | ||||||
|     var visits : Visits = Visits(0, HashMap()) |     var visits : Visits = Visits(0, HashMap()) | ||||||
| @@ -33,7 +41,8 @@ object Data { | |||||||
|  |  | ||||||
|         // Add default group "Visited" with app's color if there is no group already |         // Add default group "Visited" with app's color if there is no group already | ||||||
|         if (groups.size() == 0) { |         if (groups.size() == 0) { | ||||||
|             groups.setGroup(DEFAULT_GROUP, "Visited", ColorDrawable(ContextCompat.getColor(ctx, R.color.blue))) |             groups.setGroup(DEFAULT_GROUP, "Visited", | ||||||
|  |                 ContextCompat.getColor(ctx, R.color.blue).toDrawable()) | ||||||
|             saveData() |             saveData() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -41,9 +50,73 @@ object Data { | |||||||
|     fun saveData() { |     fun saveData() { | ||||||
|         if(groups.id != visits.id) return |         if(groups.id != visits.id) return | ||||||
|         val id = groups.id |         val id = groups.id | ||||||
|         val editor = sharedPreferences.edit() |         sharedPreferences.edit { | ||||||
|         editor.putString("groups_$id", groupsSerial.writeTo(groups)) |             putString("groups_$id", groupsSerial.writeTo(groups)) | ||||||
|         editor.putString("visits_$id", visitsSerial.writeTo(visits)) |             putString("visits_$id", visitsSerial.writeTo(visits)) | ||||||
|         editor.apply() |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun exportData(ctx: Context, filepath: Uri){ | ||||||
|  |         val groupsJson = groupsSerial.writeTo(groups) | ||||||
|  |         val visitsJson = visitsSerial.writeTo(visits) | ||||||
|  |         val outputStream = ctx.contentResolver.openOutputStream(filepath) | ||||||
|  |         outputStream?.write( | ||||||
|  |             buildString { | ||||||
|  |                 append(groupsJson) | ||||||
|  |                 append("\n---\n") // optional separator | ||||||
|  |                 append(visitsJson) | ||||||
|  |             }.toByteArray()) | ||||||
|  |         outputStream?.flush() | ||||||
|  |         outputStream?.close() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     fun importData(ctx: Context, filePath: Uri) { | ||||||
|  |         val inputStream = ctx.contentResolver.openInputStream(filePath) | ||||||
|  |         val data = inputStream?.bufferedReader().use { it?.readText() } | ||||||
|  |         if(data==null) return | ||||||
|  |         val lines = data.split("\n---\n") | ||||||
|  |         val groupsJson = lines[0] | ||||||
|  |         val visitsJson = lines[1] | ||||||
|  |  | ||||||
|  |         groups = if(groupsJson.isNotEmpty()) groupsSerial.readFrom(groupsJson.byteInputStream()) else groupsSerial.defaultValue | ||||||
|  |         visits = if(visitsJson.isNotEmpty()) visitsSerial.readFrom(visitsJson.byteInputStream()) else visitsSerial.defaultValue | ||||||
|  |  | ||||||
|  |         // Add default group "Visited" with app's color if there is no group already | ||||||
|  |         if (groups.size() == 0) { | ||||||
|  |             groups.setGroup(DEFAULT_GROUP, "Visited", | ||||||
|  |                 ContextCompat.getColor(ctx, R.color.blue).toDrawable()) | ||||||
|  |         } | ||||||
|  |         saveData() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun doImport(ctx: Context, file: Uri?){ | ||||||
|  |         if(file!=null) { | ||||||
|  |             importData(ctx, file) | ||||||
|  |             val intent = ctx.packageManager | ||||||
|  |                 .getLaunchIntentForPackage(ctx.packageName)?.apply { | ||||||
|  |                     addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) | ||||||
|  |                 } | ||||||
|  |             ctx.startActivity(intent) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun doExport(ctx: Context){ | ||||||
|  |         val fileName = "beans_backup.json" | ||||||
|  |         val resolver = ctx.contentResolver | ||||||
|  |         val contentValues = ContentValues().apply { | ||||||
|  |             put(MediaStore.Downloads.DISPLAY_NAME, fileName) // "backup.json" | ||||||
|  |             put(MediaStore.Downloads.MIME_TYPE, "text/*") | ||||||
|  |             put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) | ||||||
|  |         } | ||||||
|  |         val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { | ||||||
|  |             resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) | ||||||
|  |         } else { | ||||||
|  |             val downloadsDir = | ||||||
|  |                 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) | ||||||
|  |             val file = File(downloadsDir, fileName) | ||||||
|  |             Uri.fromFile(file) | ||||||
|  |         } | ||||||
|  |         if(uri!=null) exportData(ctx, uri) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| package net.helcel.beans.helper |  | ||||||
|  |  | ||||||
| interface DialogCloser { |  | ||||||
|     fun onDialogDismiss(clear: Boolean) |  | ||||||
| } |  | ||||||
| @@ -2,15 +2,16 @@ package net.helcel.beans.helper | |||||||
|  |  | ||||||
| import android.graphics.Color | import android.graphics.Color | ||||||
| import android.graphics.drawable.ColorDrawable | import android.graphics.drawable.ColorDrawable | ||||||
| import androidx.core.content.ContextCompat |  | ||||||
| import kotlinx.serialization.ExperimentalSerializationApi | import kotlinx.serialization.ExperimentalSerializationApi | ||||||
| import kotlinx.serialization.Serializable | import kotlinx.serialization.Serializable | ||||||
| import kotlinx.serialization.Serializer | import kotlinx.serialization.Serializer | ||||||
| import kotlinx.serialization.json.Json | import kotlinx.serialization.json.Json | ||||||
| import net.helcel.beans.R |  | ||||||
| import java.io.InputStream | import java.io.InputStream | ||||||
| import kotlin.coroutines.coroutineContext |  | ||||||
| import kotlin.random.Random | 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 | private const val randSeed = 0 | ||||||
| @@ -20,20 +21,23 @@ const val NO_GROUP = 0 | |||||||
| const val DEFAULT_GROUP = 1 | const val DEFAULT_GROUP = 1 | ||||||
| const val AUTO_GROUP = -1 | const val AUTO_GROUP = -1 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @Serializable | @Serializable | ||||||
| class Groups(val id: Int, private val grps: HashMap<Int, Group>) { | 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) { |     fun setGroup(key: Int, name: String, col: ColorDrawable) { | ||||||
|         grps[key] = Group(key, name, col) |         grps[key] = Group(key, name, col) | ||||||
|  |         _groupsFlow.value = grps.values.toList() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun deleteGroup(key: Int) { |     fun deleteGroup(key: Int) { | ||||||
|         grps.remove(key) |         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 { |     fun getGroupFromKey(key: Int): Group { | ||||||
| @@ -60,6 +64,7 @@ class Groups(val id: Int, private val grps: HashMap<Int, Group>) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun getGroupFromPos(pos: Int): Pair<Int, Group> { |     fun getGroupFromPos(pos: Int): Pair<Int, Group> { | ||||||
|  |         if(grps.keys.isEmpty()) return Pair(NO_GROUP,Group(NO_GROUP,"-")) | ||||||
|         val key = grps.keys.toList()[pos] |         val key = grps.keys.toList()[pos] | ||||||
|         return Pair(key, getGroupFromKey(key)) |         return Pair(key, getGroupFromKey(key)) | ||||||
|     } |     } | ||||||
| @@ -74,9 +79,7 @@ class Groups(val id: Int, private val grps: HashMap<Int, Group>) { | |||||||
|     open class Group( |     open class Group( | ||||||
|         val key: Int, |         val key: Int, | ||||||
|         val name: String, |         val name: String, | ||||||
|         @Serializable(with = Theme.ColorDrawableSerializer::class) val color: ColorDrawable = ColorDrawable( |         @Serializable(with = Theme.ColorDrawableSerializer::class) val color: ColorDrawable = Color.GRAY.toDrawable() | ||||||
|             Color.GRAY |  | ||||||
|         ) |  | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     @OptIn(ExperimentalSerializationApi::class) |     @OptIn(ExperimentalSerializationApi::class) | ||||||
|   | |||||||
| @@ -4,19 +4,15 @@ import android.content.Context | |||||||
| import android.content.SharedPreferences | import android.content.SharedPreferences | ||||||
| import androidx.preference.PreferenceManager | import androidx.preference.PreferenceManager | ||||||
| import net.helcel.beans.R | import net.helcel.beans.R | ||||||
| import net.helcel.beans.activity.MainActivity | import net.helcel.beans.activity.MainScreen | ||||||
| import net.helcel.beans.activity.fragment.SettingsFragment |  | ||||||
|  |  | ||||||
| object Settings { | object Settings { | ||||||
|  |  | ||||||
|     private lateinit var sp: SharedPreferences |     private lateinit var sp: SharedPreferences | ||||||
|     private lateinit var mainActivity: MainActivity |     private lateinit var mainActivity: MainScreen | ||||||
|     fun start(ctx: MainActivity) { |     fun start(ctx: MainScreen) { | ||||||
|         mainActivity = ctx |         mainActivity = ctx | ||||||
|         sp = PreferenceManager.getDefaultSharedPreferences(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 { |     fun isSingleGroup(ctx: Context): Boolean { | ||||||
| @@ -41,7 +37,7 @@ object Settings { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun refreshProjection(): Boolean { |     fun refreshProjection(): Boolean { | ||||||
|         mainActivity.refreshProjection() |         (mainActivity).refreshProjection() | ||||||
|         return true |         return true | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,23 +1,16 @@ | |||||||
| package net.helcel.beans.helper | package net.helcel.beans.helper | ||||||
|  |  | ||||||
| import android.content.Context |  | ||||||
| import android.graphics.Color | import android.graphics.Color | ||||||
| import android.graphics.drawable.ColorDrawable | import android.graphics.drawable.ColorDrawable | ||||||
| import android.util.TypedValue |  | ||||||
| import androidx.appcompat.app.AppCompatActivity |  | ||||||
| import androidx.core.graphics.ColorUtils | import androidx.core.graphics.ColorUtils | ||||||
| import kotlinx.serialization.KSerializer | import kotlinx.serialization.KSerializer | ||||||
| import kotlinx.serialization.descriptors.PrimitiveKind | import kotlinx.serialization.descriptors.PrimitiveKind | ||||||
| import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor | import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor | ||||||
| import kotlinx.serialization.encoding.Decoder | import kotlinx.serialization.encoding.Decoder | ||||||
| import kotlinx.serialization.encoding.Encoder | import kotlinx.serialization.encoding.Encoder | ||||||
|  | import androidx.core.graphics.drawable.toDrawable | ||||||
|  |  | ||||||
| object Theme { | 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 { |     fun colorToHex6(c: ColorDrawable): String { | ||||||
|         return '#' + colorToHex8(c).substring(3) |         return '#' + colorToHex8(c).substring(3) | ||||||
| @@ -28,11 +21,6 @@ object Theme { | |||||||
|         return '#' + c.color.toHexString() |         return '#' + c.color.toHexString() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun createActionBar(ctx: AppCompatActivity, title: String) { |  | ||||||
|         ctx.supportActionBar?.title = title |  | ||||||
|         ctx.supportActionBar?.setDisplayHomeAsUpEnabled(true) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun getContrastColor(color: Int): Int { |     fun getContrastColor(color: Int): Int { | ||||||
|         val whiteContrast = ColorUtils.calculateContrast(Color.WHITE, color) |         val whiteContrast = ColorUtils.calculateContrast(Color.WHITE, color) | ||||||
|         val blackContrast = ColorUtils.calculateContrast(Color.BLACK, color) |         val blackContrast = ColorUtils.calculateContrast(Color.BLACK, color) | ||||||
| @@ -43,7 +31,7 @@ object Theme { | |||||||
|         override val descriptor = PrimitiveSerialDescriptor("ColorDrawable", PrimitiveKind.INT) |         override val descriptor = PrimitiveSerialDescriptor("ColorDrawable", PrimitiveKind.INT) | ||||||
|  |  | ||||||
|         override fun deserialize(decoder: Decoder): ColorDrawable { |         override fun deserialize(decoder: Decoder): ColorDrawable { | ||||||
|             return ColorDrawable(decoder.decodeInt()) |             return decoder.decodeInt().toDrawable() | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         override fun serialize(encoder: Encoder, value: ColorDrawable) { |         override fun serialize(encoder: Encoder, value: ColorDrawable) { | ||||||
|   | |||||||
| @@ -1,5 +1,7 @@ | |||||||
| package net.helcel.beans.helper | package net.helcel.beans.helper | ||||||
|  |  | ||||||
|  | import kotlinx.coroutines.flow.MutableStateFlow | ||||||
|  | import kotlinx.coroutines.flow.StateFlow | ||||||
| import kotlinx.serialization.ExperimentalSerializationApi | import kotlinx.serialization.ExperimentalSerializationApi | ||||||
| import kotlinx.serialization.Serializable | import kotlinx.serialization.Serializable | ||||||
| import kotlinx.serialization.Serializer | import kotlinx.serialization.Serializer | ||||||
| @@ -11,9 +13,17 @@ import java.io.InputStream | |||||||
| @Serializable | @Serializable | ||||||
| class Visits(val id: Int, private val locs: HashMap<String, Int>) { | 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) { |     fun setVisited(key: GeoLoc?, b: Int) { | ||||||
|         if (key == null) |         if (key == null) | ||||||
|             return |             return | ||||||
|  |         _visitsFlow.value = _visitsFlow.value.toMutableMap().apply { | ||||||
|  |             this[key.code] = b | ||||||
|  |         } | ||||||
|         locs[key.code] = b |         locs[key.code] = b | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -21,6 +31,9 @@ class Visits(val id: Int, private val locs: HashMap<String, Int>) { | |||||||
|         val keysToDelete = locs |         val keysToDelete = locs | ||||||
|             .filter { it.value == key } |             .filter { it.value == key } | ||||||
|             .map { it.key } |             .map { it.key } | ||||||
|  |         _visitsFlow.value = _visitsFlow.value.toMutableMap().apply { | ||||||
|  |             keysToDelete.forEach { this.remove(it)} | ||||||
|  |         } | ||||||
|         keysToDelete.forEach { |         keysToDelete.forEach { | ||||||
|             locs.remove(it) |             locs.remove(it) | ||||||
|         } |         } | ||||||
| @@ -53,6 +66,7 @@ class Visits(val id: Int, private val locs: HashMap<String, Int>) { | |||||||
|         keys.forEach { |         keys.forEach { | ||||||
|             locs[it] = group |             locs[it] = group | ||||||
|         } |         } | ||||||
|  |         _visitsFlow.value = locs | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @OptIn(ExperimentalSerializationApi::class) |     @OptIn(ExperimentalSerializationApi::class) | ||||||
|   | |||||||
| @@ -1,6 +1,10 @@ | |||||||
| package net.helcel.beans.svg | package net.helcel.beans.svg | ||||||
|  |  | ||||||
| import android.content.Context | 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.countries.World | ||||||
| import net.helcel.beans.helper.AUTO_GROUP | import net.helcel.beans.helper.AUTO_GROUP | ||||||
| import net.helcel.beans.helper.Data.groups | import net.helcel.beans.helper.Data.groups | ||||||
| @@ -8,15 +12,10 @@ import net.helcel.beans.helper.Data.visits | |||||||
| import net.helcel.beans.helper.NO_GROUP | import net.helcel.beans.helper.NO_GROUP | ||||||
| import net.helcel.beans.helper.Settings | import net.helcel.beans.helper.Settings | ||||||
| import net.helcel.beans.helper.Theme.colorToHex6 | import net.helcel.beans.helper.Theme.colorToHex6 | ||||||
| import net.helcel.beans.helper.Theme.colorWrapper |  | ||||||
|  |  | ||||||
| class CSSWrapper(private val ctx: Context) { | 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 continents: String = World.WWW.children.joinToString(",") { "#${it.code}2" } | ||||||
|     private val countries: String = World.WWW.children.joinToString(",") { itt -> |     private val countries: String = World.WWW.children.joinToString(",") { itt -> | ||||||
|         itt.children.joinToString(",") { "#${it.code}2" } |         itt.children.joinToString(",") { "#${it.code}2" } | ||||||
| @@ -24,18 +23,22 @@ class CSSWrapper(private val ctx: Context) { | |||||||
|     private val regional: String = World.WWW.children.joinToString(",") { itt -> |     private val regional: String = World.WWW.children.joinToString(",") { itt -> | ||||||
|         itt.children.joinToString(",") { "#${it.code}1" } |         itt.children.joinToString(",") { "#${it.code}1" } | ||||||
|     } |     } | ||||||
|     private val countryOnlyCSS: String = |  | ||||||
|         "svg{fill:$colorForeground;stroke:$colorBackground;stroke-width:0.1;}" + |     @Composable | ||||||
|                 "${regional}{display:none;}" |     fun getBaseColors() : Pair<String, String> { | ||||||
|     private val countryRegionalCSS: String = |         val colorForeground = colorToHex6(MaterialTheme.colors.onBackground.toArgb().toDrawable()) | ||||||
|         "svg{fill:$colorForeground;stroke:$colorBackground;stroke-width:0.01;}" + |         val colorBackground = colorToHex6(MaterialTheme.colors.background.toArgb().toDrawable()) | ||||||
|                 "$continents,$countries{fill:none;stroke:$colorBackground;stroke-width:0.1;}" |  | ||||||
|  |         return Pair(colorForeground, colorBackground) | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private var customCSS: String = "" |     private var customCSS: String = "" | ||||||
|  |  | ||||||
|     init { |     init { | ||||||
|         refresh() |         refresh() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     private fun refresh() { |     private fun refresh() { | ||||||
|         val id = if (Settings.isRegional(ctx)) "1" else "2" |         val id = if (Settings.isRegional(ctx)) "1" else "2" | ||||||
|         customCSS = visits.getVisitedByValue().map { (k, v) -> |         customCSS = visits.getVisitedByValue().map { (k, v) -> | ||||||
| @@ -47,20 +50,24 @@ class CSSWrapper(private val ctx: Context) { | |||||||
|                 emptyList() |                 emptyList() | ||||||
|             }).takeIf { it.isNotEmpty() } |             }).takeIf { it.isNotEmpty() } | ||||||
|                 ?.joinToString(",") { "#${it}$id,#${it}" } + "{fill:${ |                 ?.joinToString(",") { "#${it}$id,#${it}" } + "{fill:${ | ||||||
|                 colorToHex6( |                 if (k == AUTO_GROUP) colorToHex6(groups.getGroupFromPos(0).second.color)  | ||||||
|                     if (k == AUTO_GROUP) |                 else colorToHex6(groups.getGroupFromKey(k).color) | ||||||
|                         colorWrapper(ctx, android.R.attr.colorPrimary) |  | ||||||
|                     else groups.getGroupFromKey(k).color |  | ||||||
|                 ) |  | ||||||
|             };}" |             };}" | ||||||
|         }.joinToString("") |         }.joinToString("") | ||||||
|     } |     } | ||||||
|  |     @Composable | ||||||
|     fun get(): String { |     fun get(): String { | ||||||
|  |         val (colorForeground,colorBackground) = getBaseColors() | ||||||
|         refresh() |         refresh() | ||||||
|         return if (Settings.isRegional(ctx)) { |         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 |             countryRegionalCSS + customCSS | ||||||
|         } else { |         } else { | ||||||
|  |             val countryOnlyCSS: String = | ||||||
|  |                 "svg{fill:$colorForeground;stroke:$colorBackground;stroke-width:0.1;}" + | ||||||
|  |                         "${regional}{display:none;}" | ||||||
|             countryOnlyCSS + customCSS |             countryOnlyCSS + customCSS | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,13 +0,0 @@ | |||||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
| android:width="24dp" |  | ||||||
| android:height="24dp" |  | ||||||
| android:viewportWidth="960" |  | ||||||
| android:viewportHeight="960"> |  | ||||||
| <path |  | ||||||
|     android:fillColor="?attr/colorOnBackground" |  | ||||||
|     android:pathData="M80,160Q80,127 103.5,103.5Q127,80 160,80L800,80Q833,80 856.5,103.5Q880,127 880,160L880,640Q880,673 856.5,696.5Q833,720 800,720L240,720L80,880ZM206,640L800,640Q800,640 800,640Q800,640 800,640L800,160Q800,160 800,160Q800,160 800,160L160,160Q160,160 160,160Q160,160 160,160L160,685L206,640ZM160,640L160,640L160,160Q160,160 160,160Q160,160 160,160L160,160Q160,160 160,160Q160,160 160,160L160,640Q160,640 160,640Q160,640 160,640L160,640Z" /> |  | ||||||
|  |  | ||||||
| <path |  | ||||||
|     android:fillColor="?attr/colorPrimary" |  | ||||||
|     android:pathData="M480,280Q497,280 508.5,268.5Q520,257 520,240Q520,223 508.5,211.5Q497,200 480,200Q463,200 451.5,211.5Q440,223 440,240Q440,257 451.5,268.5Q463,280 480,280ZM440,600L520,600L520,360L440,360L440,600ZM80,880L80,160" /> |  | ||||||
| </vector> |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
| android:width="24dp" |  | ||||||
| android:height="24dp" |  | ||||||
| android:viewportWidth="24" |  | ||||||
| android:viewportHeight="24"> |  | ||||||
| <path |  | ||||||
|     android:fillColor="?attr/colorOnBackground" |  | ||||||
|     android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" /> |  | ||||||
| </vector> |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> |  | ||||||
|        |  | ||||||
|     <path android:fillColor="@android:color/white" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/> |  | ||||||
|      |  | ||||||
| </vector> |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| <vector |  | ||||||
|     android:height="24dp" |  | ||||||
|     android:viewportHeight="960" |  | ||||||
|     android:viewportWidth="960" |  | ||||||
|     android:width="24dp" |  | ||||||
|     xmlns:android="http://schemas.android.com/apk/res/android"> |  | ||||||
| <path |  | ||||||
|     android:fillColor="@color/white" |  | ||||||
|     android:pathData="M346,820L100,574Q90,564 85,552Q80,540 80,527Q80,514 85,502Q90,490 100,480L330,251L224,145L286,80L686,480Q696,490 700.5,502Q705,514 705,527Q705,540 700.5,552Q696,564 686,574L440,820Q430,830 418,835Q406,840 393,840Q380,840 368,835Q356,830 346,820ZM393,314L179,528Q179,528 179,528Q179,528 179,528L607,528Q607,528 607,528Q607,528 607,528L393,314ZM792,840Q756,840 731,814.5Q706,789 706,752Q706,725 719.5,701Q733,677 750,654L792,600L836,654Q852,677 866,701Q880,725 880,752Q880,789 854,814.5Q828,840 792,840Z"/> |  | ||||||
| </vector> |  | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     android:width="72dp" |  | ||||||
|     android:height="72dp" |  | ||||||
|     android:viewportWidth="72" |  | ||||||
|     android:viewportHeight="72"> |  | ||||||
|   <path |  | ||||||
|       android:pathData="M31,16l0,-4l10,0l0,4" |  | ||||||
|       android:strokeLineJoin="round" |  | ||||||
|       android:strokeWidth="4" |  | ||||||
|       android:fillColor="#00000000" |  | ||||||
|       android:strokeColor="?attr/colorOnBackground" |  | ||||||
|       android:strokeLineCap="round"/> |  | ||||||
|   <path |  | ||||||
|       android:pathData="M51,25v31c0,2.209 -1.791,4 -4,4H25c-2.209,0 -4,-1.791 -4,-4V25" |  | ||||||
|       android:strokeLineJoin="round" |  | ||||||
|       android:strokeWidth="4" |  | ||||||
|       android:fillColor="#00000000" |  | ||||||
|       android:strokeColor="?attr/colorOnBackground" |  | ||||||
|       android:strokeLineCap="round"/> |  | ||||||
|   <path |  | ||||||
|       android:pathData="M17,16h38v4h-38z" |  | ||||||
|       android:strokeLineJoin="round" |  | ||||||
|       android:strokeWidth="4" |  | ||||||
|       android:fillColor="#00000000" |  | ||||||
|       android:strokeColor="?attr/colorOnBackground" |  | ||||||
|       android:strokeLineCap="round"/> |  | ||||||
|   <path |  | ||||||
|       android:pathData="M41,28.25L41,55" |  | ||||||
|       android:strokeLineJoin="round" |  | ||||||
|       android:strokeWidth="4" |  | ||||||
|       android:fillColor="#00000000" |  | ||||||
|       android:strokeColor="?attr/colorOnBackground" |  | ||||||
|       android:strokeLineCap="round"/> |  | ||||||
|   <path |  | ||||||
|       android:pathData="M31,28.25L31,55" |  | ||||||
|       android:strokeLineJoin="round" |  | ||||||
|       android:strokeWidth="4" |  | ||||||
|       android:fillColor="#00000000" |  | ||||||
|       android:strokeColor="?attr/colorOnBackground" |  | ||||||
|       android:strokeLineCap="round"/> |  | ||||||
| </vector> |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| <vector |  | ||||||
|     android:height="24dp" |  | ||||||
|     android:viewportHeight="24" |  | ||||||
|     android:viewportWidth="24" |  | ||||||
|     android:width="24dp" |  | ||||||
|     xmlns:android="http://schemas.android.com/apk/res/android"> |  | ||||||
|     <path |  | ||||||
|         android:fillColor="@color/white" |  | ||||||
|         android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/> |  | ||||||
| </vector> |  | ||||||
| @@ -1,16 +0,0 @@ | |||||||
| <vector |  | ||||||
| android:height="24dp" |  | ||||||
| android:viewportHeight="24" |  | ||||||
| android:viewportWidth="24" |  | ||||||
| android:width="24dp" |  | ||||||
| xmlns:android="http://schemas.android.com/apk/res/android" > |  | ||||||
|  |  | ||||||
| <path android:fillColor="?attr/colorOnBackground" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/> |  | ||||||
|  |  | ||||||
| <path android:fillColor="?attr/colorPrimary" android:pathData="M8,14m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/> |  | ||||||
|  |  | ||||||
| <path android:fillColor="?attr/colorPrimary" android:pathData="M12,8m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/> |  | ||||||
|  |  | ||||||
| <path android:fillColor="?attr/colorPrimary" android:pathData="M16,14m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/> |  | ||||||
|  |  | ||||||
| </vector> |  | ||||||
| @@ -1,18 +0,0 @@ | |||||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
| android:width="24dp" |  | ||||||
| android:height="24dp" |  | ||||||
| android:viewportWidth="24.0" |  | ||||||
| android:viewportHeight="24.0"> |  | ||||||
|  |  | ||||||
| <path |  | ||||||
|     android:fillColor="?attr/colorOnBackground" |  | ||||||
|     android:pathData="M22,7h-9v2h9V7zM22,15h-9v2h9V15z"/> |  | ||||||
|  |  | ||||||
| <path |  | ||||||
|     android:fillColor="?attr/colorPrimary" |  | ||||||
|     android:pathData="M5.54,11L2,7.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,11z"/> |  | ||||||
|  |  | ||||||
| <path |  | ||||||
|     android:fillColor="?attr/colorPrimary" |  | ||||||
|     android:pathData="M5.54,19L2,15.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,19z"/> |  | ||||||
| </vector> |  | ||||||
| @@ -1,16 +0,0 @@ | |||||||
| <vector |  | ||||||
|     android:height="24dp" |  | ||||||
|     android:viewportHeight="24" |  | ||||||
|     android:viewportWidth="24" |  | ||||||
|     android:width="24dp" |  | ||||||
|     xmlns:android="http://schemas.android.com/apk/res/android" > |  | ||||||
|  |  | ||||||
|     <path |  | ||||||
|         android:fillColor="?attr/colorOnBackground" |  | ||||||
|         android:pathData="M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48L3,20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48L21,3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM10,5.47l4,1.4v11.66l-4,-1.4L10,5.47zM5,6.46l3,-1.01v11.7l-3,1.16L5,6.46zM19,17.54l-3,1.01L16,6.86l3,-1.16v11.84z" /> |  | ||||||
|  |  | ||||||
|     <path |  | ||||||
|         android:fillColor="?attr/colorPrimary" |  | ||||||
|         android:pathData="M15,18.89l-6,-2.11L9,5.11l6,2.11v11.67z"/> |  | ||||||
|  |  | ||||||
| </vector> |  | ||||||
| @@ -1,14 +0,0 @@ | |||||||
| <vector |  | ||||||
|     android:height="24dp" |  | ||||||
|     android:viewportHeight="24" |  | ||||||
|     android:viewportWidth="24" |  | ||||||
|     android:width="24dp" |  | ||||||
|     xmlns:android="http://schemas.android.com/apk/res/android" > |  | ||||||
|  |  | ||||||
|     <path |  | ||||||
|         android:fillColor="?attr/colorOnBackground" |  | ||||||
|         android:pathData="M12,22C6.49,22 2,17.51 2,12S6.49,2 12,2s10,4.04 10,9c0,3.31 -2.69,6 -6,6h-1.77c-0.28,0 -0.5,0.22 -0.5,0.5c0,0.12 0.05,0.23 0.13,0.33c0.41,0.47 0.64,1.06 0.64,1.67C14.5,20.88 13.38,22 12,22zM12,4c-4.41,0 -8,3.59 -8,8s3.59,8 8,8c0.28,0 0.5,-0.22 0.5,-0.5c0,-0.16 -0.08,-0.28 -0.14,-0.35c-0.41,-0.46 -0.63,-1.05 -0.63,-1.65c0,-1.38 1.12,-2.5 2.5,-2.5H16c2.21,0 4,-1.79 4,-4C20,7.14 16.41,4 12,4z" /> |  | ||||||
|     <path |  | ||||||
|         android:fillColor="?attr/colorPrimary" |  | ||||||
|         android:pathData="M17.5,13c-0.83,0 -1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5C19,12.33 18.33,13 17.5,13zM14.5,9C13.67,9 13,8.33 13,7.5C13,6.67 13.67,6 14.5,6S16,6.67 16,7.5C16,8.33 15.33,9 14.5,9zM5,11.5C5,10.67 5.67,10 6.5,10S8,10.67 8,11.5C8,12.33 7.33,13 6.5,13S5,12.33 5,11.5zM11,7.5C11,8.33 10.33,9 9.5,9S8,8.33 8,7.5C8,6.67 8.67,6 9.5,6S11,6.67 11,7.5z" /> |  | ||||||
| </vector> |  | ||||||
| @@ -1,14 +0,0 @@ | |||||||
| <vector |  | ||||||
| android:height="24dp" |  | ||||||
| android:viewportHeight="960" |  | ||||||
| android:viewportWidth="960" |  | ||||||
| android:width="24dp" |  | ||||||
| xmlns:android="http://schemas.android.com/apk/res/android" > |  | ||||||
|  |  | ||||||
| <path |  | ||||||
|     android:fillColor="?attr/colorOnBackground" |  | ||||||
|     android:pathData="M105,727L40,680L240,360L360,500L520,240L629,403Q606,404 585.5,408.5Q565,413 545,421L523,388L371,635L250,494L105,727ZM732,423Q713,415 692.5,410Q672,405 650,404L855,80L920,127L732,423Z" /> |  | ||||||
| <path |  | ||||||
|     android:fillColor="?attr/colorPrimary" |  | ||||||
|     android:pathData="M863,920L738,795Q718,809 693.5,816Q669,823 643,823Q568,823 515.5,770.5Q463,718 463,643Q463,568 515.5,515.5Q568,463 643,463Q718,463 770.5,515.5Q823,568 823,643Q823,669 816,693.5Q809,718 795,739L920,863L863,920ZM643,743Q685,743 714,714Q743,685 743,643Q743,601 714,572Q685,543 643,543Q601,543 572,572Q543,601 543,643Q543,685 572,714Q601,743 643,743Z" /> |  | ||||||
| </vector> |  | ||||||
| @@ -1,12 +0,0 @@ | |||||||
| <vector |  | ||||||
| android:height="24dp" |  | ||||||
| android:viewportHeight="24" |  | ||||||
| android:viewportWidth="24" |  | ||||||
| android:width="24dp" |  | ||||||
| xmlns:android="http://schemas.android.com/apk/res/android" > |  | ||||||
|  |  | ||||||
| <path android:fillColor="?attr/colorOnBackground" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/> |  | ||||||
|  |  | ||||||
| <path android:fillColor="?attr/colorPrimary" android:pathData="M12,10h-2v2H9v-2H7V9h2V7h1v2h2v1z"/> |  | ||||||
|  |  | ||||||
| </vector> |  | ||||||
| @@ -1,21 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="match_parent" |  | ||||||
|     android:orientation="vertical" |  | ||||||
|     android:theme="@style/Theme.Beans" |  | ||||||
|     tools:context=".activity.EditActivity"> |  | ||||||
|  |  | ||||||
|     <com.google.android.material.tabs.TabLayout |  | ||||||
|         android:id="@+id/tab" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" /> |  | ||||||
|  |  | ||||||
|     <androidx.viewpager2.widget.ViewPager2 |  | ||||||
|         android:id="@+id/pager" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="0dp" |  | ||||||
|         android:layout_weight="1" /> |  | ||||||
|  |  | ||||||
| </LinearLayout> |  | ||||||
| @@ -1,14 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="match_parent" |  | ||||||
|     android:orientation="vertical" |  | ||||||
|     android:theme="@style/Theme.Beans" |  | ||||||
|     tools:context=".activity.MainActivity"> |  | ||||||
|      |  | ||||||
|     <com.github.chrisbanes.photoview.PhotoView |  | ||||||
|         android:id="@+id/photo_view" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="match_parent" /> |  | ||||||
| </LinearLayout> |  | ||||||
| @@ -1,15 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     android:orientation="vertical" |  | ||||||
|     android:theme="@style/Theme.Beans" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="match_parent" |  | ||||||
|     tools:context=".activity.SettingsActivity"> |  | ||||||
|  |  | ||||||
| <androidx.fragment.app.FragmentContainerView |  | ||||||
|     android:id="@+id/fragment_view" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="match_parent" /> |  | ||||||
|  |  | ||||||
| </LinearLayout> |  | ||||||
| @@ -1,66 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="match_parent" |  | ||||||
|     android:orientation="vertical" |  | ||||||
|     android:theme="@style/Theme.Beans" |  | ||||||
|     tools:context=".activity.StatsActivity"> |  | ||||||
|  |  | ||||||
|     <com.google.android.material.tabs.TabLayout |  | ||||||
|         android:id="@+id/tab" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" /> |  | ||||||
|  |  | ||||||
|     <androidx.viewpager2.widget.ViewPager2 |  | ||||||
|         android:id="@+id/pager" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="0dp" |  | ||||||
|         android:layout_weight="1" |  | ||||||
|         android:visibility="gone" /> |  | ||||||
|  |  | ||||||
|     <androidx.constraintlayout.widget.ConstraintLayout |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:paddingBottom="10dp"> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.button.MaterialButton |  | ||||||
|             android:id="@+id/group_color" |  | ||||||
|             android:layout_width="0dp" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:layout_marginTop="2dp" |  | ||||||
|             android:layout_marginBottom="2dp" |  | ||||||
|             android:paddingStart="56dp" |  | ||||||
|             android:text="@string/total" |  | ||||||
|             android:textAlignment="textStart" |  | ||||||
|             android:textColor="?attr/colorOnPrimary" |  | ||||||
|             app:cornerRadius="0dp" |  | ||||||
|             app:layout_constraintBottom_toBottomOf="parent" |  | ||||||
|             app:layout_constraintEnd_toEndOf="parent" |  | ||||||
|             app:layout_constraintStart_toStartOf="parent" |  | ||||||
|             app:layout_constraintTop_toTopOf="parent" |  | ||||||
|             tools:ignore="RtlSymmetry" /> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.textview.MaterialTextView |  | ||||||
|             android:id="@+id/name" |  | ||||||
|             android:layout_width="wrap_content" |  | ||||||
|             android:layout_height="50dp" |  | ||||||
|             android:gravity="start|center_vertical" |  | ||||||
|             android:paddingStart="20dp" |  | ||||||
|             android:paddingEnd="52dp" |  | ||||||
|             android:text="" |  | ||||||
|             android:textColor="?attr/colorOnPrimary" |  | ||||||
|             app:layout_constraintBottom_toBottomOf="@id/group_color" |  | ||||||
|             app:layout_constraintEnd_toEndOf="@id/group_color" |  | ||||||
|             app:layout_constraintTop_toTopOf="@id/group_color" /> |  | ||||||
|  |  | ||||||
|     </androidx.constraintlayout.widget.ConstraintLayout> |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     <androidx.recyclerview.widget.RecyclerView |  | ||||||
|         android:id="@+id/stats" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" /> |  | ||||||
|  |  | ||||||
| </LinearLayout> |  | ||||||
| @@ -1,67 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="match_parent" |  | ||||||
|     tools:context=".activity.fragment.AboutFragment"> |  | ||||||
|  |  | ||||||
|     <LinearLayout |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="match_parent" |  | ||||||
|         android:gravity="center_horizontal" |  | ||||||
|         android:orientation="vertical"> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.imageview.ShapeableImageView |  | ||||||
|             android:layout_width="300dp" |  | ||||||
|             android:layout_height="300dp" |  | ||||||
|             android:layout_marginTop="20dp" |  | ||||||
|             android:contentDescription="@string/logo" |  | ||||||
|             android:src="@drawable/ic_launcher_foreground" /> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.textview.MaterialTextView |  | ||||||
|             android:layout_width="match_parent" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:layout_marginStart="10dp" |  | ||||||
|             android:layout_marginTop="15dp" |  | ||||||
|             android:layout_marginEnd="10dp" |  | ||||||
|             android:layout_marginBottom="10dp" |  | ||||||
|             android:text="@string/app_name" |  | ||||||
|             android:textAlignment="center" |  | ||||||
|             android:textSize="30sp" |  | ||||||
|             android:textStyle="bold" /> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.textview.MaterialTextView |  | ||||||
|             android:layout_width="match_parent" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:layout_marginStart="10dp" |  | ||||||
|             android:layout_marginTop="0dp" |  | ||||||
|             android:layout_marginEnd="10dp" |  | ||||||
|             android:layout_marginBottom="15dp" |  | ||||||
|             android:text="@string/app_version" |  | ||||||
|             android:textAlignment="center" |  | ||||||
|             android:textSize="25sp" /> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.textview.MaterialTextView |  | ||||||
|             android:layout_width="match_parent" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:layout_marginStart="10dp" |  | ||||||
|             android:layout_marginTop="15dp" |  | ||||||
|             android:layout_marginEnd="10dp" |  | ||||||
|             android:layout_marginBottom="15dp" |  | ||||||
|             android:text="@string/beans_is_foss" |  | ||||||
|             android:textAlignment="center" /> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.textview.MaterialTextView |  | ||||||
|             android:layout_width="match_parent" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:layout_marginStart="10dp" |  | ||||||
|             android:layout_marginTop="15dp" |  | ||||||
|             android:layout_marginEnd="10dp" |  | ||||||
|             android:layout_marginBottom="15dp" |  | ||||||
|             android:autoLink="web" |  | ||||||
|             android:text="@string/beans_repo" |  | ||||||
|             android:textAlignment="center" /> |  | ||||||
|  |  | ||||||
|     </LinearLayout> |  | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> |  | ||||||
| @@ -1,137 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="wrap_content" |  | ||||||
|     android:orientation="vertical" |  | ||||||
|     android:padding="16dp" |  | ||||||
|     tools:context=".activity.fragment.EditGroupAddFragment"> |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     <com.google.android.material.textfield.TextInputEditText |  | ||||||
|         android:id="@+id/group_name" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="48dp" |  | ||||||
|         android:autofillHints="" |  | ||||||
|         android:hint="@string/name" |  | ||||||
|         android:inputType="text" /> |  | ||||||
|  |  | ||||||
|     <androidx.constraintlayout.widget.ConstraintLayout |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="match_parent"> |  | ||||||
|  |  | ||||||
|         <View |  | ||||||
|             android:id="@+id/colorView" |  | ||||||
|             android:layout_width="0dp" |  | ||||||
|             android:layout_height="0dp" |  | ||||||
|             android:background="@color/black" |  | ||||||
|             app:layout_constraintBottom_toBottomOf="parent" |  | ||||||
|             app:layout_constraintEnd_toStartOf="@+id/colorR" |  | ||||||
|             app:layout_constraintStart_toStartOf="parent" |  | ||||||
|             app:layout_constraintTop_toTopOf="parent" /> |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         <com.google.android.material.slider.Slider |  | ||||||
|             android:id="@+id/colorR" |  | ||||||
|             android:layout_width="0dp" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             app:layout_constraintBottom_toTopOf="@id/colorG" |  | ||||||
|             app:layout_constraintEnd_toEndOf="parent" |  | ||||||
|             app:layout_constraintStart_toEndOf="@id/colorView" |  | ||||||
|             app:layout_constraintTop_toTopOf="parent" |  | ||||||
|             app:thumbColor="@color/red" |  | ||||||
|             app:trackColorActive="@color/red" /> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.slider.Slider |  | ||||||
|             android:id="@+id/colorG" |  | ||||||
|             android:layout_width="0dp" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:foregroundTint="#FF0000" |  | ||||||
|             app:layout_constraintBottom_toTopOf="@id/colorB" |  | ||||||
|             app:layout_constraintEnd_toEndOf="parent" |  | ||||||
|             app:layout_constraintStart_toEndOf="@id/colorView" |  | ||||||
|             app:layout_constraintTop_toBottomOf="@id/colorR" |  | ||||||
|             app:thumbColor="@color/green" |  | ||||||
|             app:trackColorActive="@color/green" /> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.slider.Slider |  | ||||||
|             android:id="@+id/colorB" |  | ||||||
|             android:layout_width="0dp" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             app:layout_constraintBottom_toBottomOf="parent" |  | ||||||
|             app:layout_constraintEnd_toEndOf="parent" |  | ||||||
|             app:layout_constraintStart_toEndOf="@id/colorView" |  | ||||||
|             app:layout_constraintTop_toBottomOf="@id/colorG" |  | ||||||
|             app:thumbColor="@color/blue" |  | ||||||
|             app:trackColorActive="@color/blue" /> |  | ||||||
|  |  | ||||||
|     </androidx.constraintlayout.widget.ConstraintLayout> |  | ||||||
|  |  | ||||||
|     <LinearLayout |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="match_parent"> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.textview.MaterialTextView |  | ||||||
|             android:id="@+id/textView" |  | ||||||
|             android:layout_width="wrap_content" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:labelFor="@id/group_color" |  | ||||||
|             android:text="@string/hashtag" /> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.textfield.TextInputEditText |  | ||||||
|             android:id="@+id/group_color" |  | ||||||
|             android:layout_width="wrap_content" |  | ||||||
|             android:layout_height="48dp" |  | ||||||
|             android:autofillHints="" |  | ||||||
|             android:hint="@string/color_rrggbb" |  | ||||||
|             android:inputType="text" |  | ||||||
|             android:maxLength="6" /> |  | ||||||
|     </LinearLayout> |  | ||||||
|  |  | ||||||
|     <androidx.constraintlayout.widget.ConstraintLayout |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="match_parent" |  | ||||||
|         android:layout_marginTop="10dp"> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.button.MaterialButton |  | ||||||
|             android:id="@+id/btnDelete" |  | ||||||
|             android:layout_width="52dp" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:paddingLeft="6dp" |  | ||||||
|             android:paddingRight="6dp" |  | ||||||
|             android:tooltipText="@string/delete" |  | ||||||
|             app:icon="@drawable/delete" |  | ||||||
|             app:iconGravity="textStart" |  | ||||||
|             app:iconPadding="0dp" |  | ||||||
|             app:layout_constraintBottom_toBottomOf="parent" |  | ||||||
|             app:layout_constraintStart_toStartOf="parent" |  | ||||||
|             app:layout_constraintTop_toTopOf="parent" /> |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         <com.google.android.material.button.MaterialButton |  | ||||||
|             android:id="@+id/btnCancel" |  | ||||||
|             android:layout_width="80dp" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:layout_marginEnd="6dp" |  | ||||||
|             android:paddingLeft="6dp" |  | ||||||
|             android:paddingRight="6dp" |  | ||||||
|             android:text="@string/cancel" |  | ||||||
|             app:layout_constraintBottom_toBottomOf="parent" |  | ||||||
|             app:layout_constraintEnd_toStartOf="@+id/btnOk" |  | ||||||
|             app:layout_constraintTop_toTopOf="parent" /> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.button.MaterialButton |  | ||||||
|             android:id="@+id/btnOk" |  | ||||||
|             android:layout_width="52dp" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:paddingLeft="6dp" |  | ||||||
|             android:paddingRight="6dp" |  | ||||||
|             android:text="@string/ok" |  | ||||||
|             app:layout_constraintBottom_toBottomOf="parent" |  | ||||||
|             app:layout_constraintEnd_toEndOf="parent" |  | ||||||
|             app:layout_constraintTop_toTopOf="parent" /> |  | ||||||
|  |  | ||||||
|     </androidx.constraintlayout.widget.ConstraintLayout> |  | ||||||
|  |  | ||||||
| </LinearLayout> |  | ||||||
| @@ -1,26 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="match_parent" |  | ||||||
|     tools:context=".activity.fragment.EditPlaceFragment"> |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     <androidx.core.widget.NestedScrollView |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="match_parent" |  | ||||||
|         android:scrollbars="vertical" |  | ||||||
|         app:layout_constraintBottom_toBottomOf="parent" |  | ||||||
|         app:layout_constraintEnd_toEndOf="parent" |  | ||||||
|         app:layout_constraintStart_toStartOf="parent" |  | ||||||
|         app:layout_constraintTop_toTopOf="parent"> |  | ||||||
|  |  | ||||||
|         <androidx.recyclerview.widget.RecyclerView |  | ||||||
|             android:id="@+id/list" |  | ||||||
|             android:layout_width="match_parent" |  | ||||||
|             android:layout_height="match_parent" |  | ||||||
|             android:nestedScrollingEnabled="false" |  | ||||||
|             android:scrollbars="vertical" /> |  | ||||||
|     </androidx.core.widget.NestedScrollView> |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> |  | ||||||
| @@ -1,59 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="wrap_content" |  | ||||||
|     android:orientation="vertical" |  | ||||||
|     android:padding="16dp"> |  | ||||||
|  |  | ||||||
|     <TextView |  | ||||||
|         android:id="@+id/warning_text" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:layout_marginBottom="10dp" /> |  | ||||||
|  |  | ||||||
|     <ScrollView |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:orientation="vertical" |  | ||||||
|         android:padding="4dp"> |  | ||||||
|  |  | ||||||
|         <androidx.recyclerview.widget.RecyclerView |  | ||||||
|             android:id="@+id/groups_color" |  | ||||||
|             android:layout_width="match_parent" |  | ||||||
|             android:layout_height="wrap_content" /> |  | ||||||
|     </ScrollView> |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     <androidx.constraintlayout.widget.ConstraintLayout |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:layout_marginTop="10dp"> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.button.MaterialButton |  | ||||||
|             android:id="@+id/btnAdd" |  | ||||||
|             android:layout_width="64dp" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:paddingLeft="6dp" |  | ||||||
|             android:paddingRight="6dp" |  | ||||||
|             android:text="@string/add" |  | ||||||
|             app:layout_constraintBottom_toBottomOf="parent" |  | ||||||
|             app:layout_constraintStart_toStartOf="parent" |  | ||||||
|             app:layout_constraintTop_toTopOf="parent" /> |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         <com.google.android.material.button.MaterialButton |  | ||||||
|             android:id="@+id/btnClear" |  | ||||||
|             android:layout_width="64dp" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:paddingLeft="6dp" |  | ||||||
|             android:paddingRight="6dp" |  | ||||||
|             android:text="@string/clear" |  | ||||||
|             app:layout_constraintBottom_toBottomOf="parent" |  | ||||||
|             app:layout_constraintEnd_toEndOf="parent" |  | ||||||
|             app:layout_constraintTop_toTopOf="parent" /> |  | ||||||
|  |  | ||||||
|     </androidx.constraintlayout.widget.ConstraintLayout> |  | ||||||
|  |  | ||||||
| </LinearLayout> |  | ||||||
|  |  | ||||||
| @@ -1,13 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="match_parent" |  | ||||||
|     tools:context=".activity.fragment.LicenseFragment"> |  | ||||||
|  |  | ||||||
|     <androidx.fragment.app.FragmentContainerView |  | ||||||
|         android:id="@+id/license_fragment_view" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="match_parent" /> |  | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> |  | ||||||
| @@ -1,52 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
|  |  | ||||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="wrap_content"> |  | ||||||
|  |  | ||||||
|     <com.google.android.material.button.MaterialButton |  | ||||||
|         android:id="@+id/textView" |  | ||||||
|         android:layout_width="0dp" |  | ||||||
|         android:layout_height="50dp" |  | ||||||
|         android:clickable="true" |  | ||||||
|         android:focusable="true" |  | ||||||
|         android:gravity="start|center_vertical" |  | ||||||
|         android:insetTop="4dp" |  | ||||||
|         android:insetBottom="4dp" |  | ||||||
|         android:paddingStart="20dp" |  | ||||||
|         android:paddingEnd="20dp" |  | ||||||
|         android:textAllCaps="false" |  | ||||||
|         android:textAppearance="?attr/textAppearanceBody2" |  | ||||||
|         android:textColor="?attr/colorOnBackground" |  | ||||||
|  |  | ||||||
|         app:cornerRadius="4dp" |  | ||||||
|         app:layout_constraintBottom_toBottomOf="@id/checkBox" |  | ||||||
|         app:layout_constraintEnd_toStartOf="@id/checkBox" |  | ||||||
|         app:layout_constraintStart_toStartOf="parent" |  | ||||||
|         app:layout_constraintTop_toTopOf="parent" /> |  | ||||||
|  |  | ||||||
|     <com.google.android.material.textview.MaterialTextView |  | ||||||
|         android:id="@+id/count" |  | ||||||
|         android:layout_width="wrap_content" |  | ||||||
|         android:layout_height="50dp" |  | ||||||
|         android:gravity="start|center_vertical" |  | ||||||
|         android:paddingStart="20dp" |  | ||||||
|         android:paddingEnd="20dp" |  | ||||||
|         app:layout_constraintBottom_toBottomOf="@id/checkBox" |  | ||||||
|         app:layout_constraintEnd_toStartOf="@id/checkBox" |  | ||||||
|         app:layout_constraintTop_toTopOf="parent" /> |  | ||||||
|  |  | ||||||
|     <com.google.android.material.checkbox.MaterialCheckBox |  | ||||||
|         android:id="@+id/checkBox" |  | ||||||
|         android:layout_width="50dp" |  | ||||||
|         android:layout_height="50dp" |  | ||||||
|         app:checkedState="indeterminate" |  | ||||||
|         app:layout_constraintBottom_toBottomOf="@id/textView" |  | ||||||
|         app:layout_constraintEnd_toEndOf="parent" |  | ||||||
|         app:layout_constraintHorizontal_bias="1" |  | ||||||
|         app:layout_constraintStart_toEndOf="@id/textView" |  | ||||||
|         app:layout_constraintTop_toTopOf="@id/textView" |  | ||||||
|         app:layout_constraintVertical_bias="0.5" /> |  | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> |  | ||||||
| @@ -1,34 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="wrap_content"> |  | ||||||
|  |  | ||||||
|     <com.google.android.material.button.MaterialButton |  | ||||||
|         android:id="@+id/group_color" |  | ||||||
|         android:layout_width="0dp" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:layout_marginStart="32dp" |  | ||||||
|         android:layout_marginTop="2dp" |  | ||||||
|         android:layout_marginEnd="32dp" |  | ||||||
|         android:layout_marginBottom="2dp" |  | ||||||
|         android:textAlignment="textStart" |  | ||||||
|         android:textColor="?attr/colorOnPrimary" |  | ||||||
|         app:layout_constraintBottom_toBottomOf="parent" |  | ||||||
|         app:layout_constraintEnd_toEndOf="parent" |  | ||||||
|         app:layout_constraintStart_toStartOf="parent" |  | ||||||
|         app:layout_constraintTop_toTopOf="parent" /> |  | ||||||
|  |  | ||||||
|     <com.google.android.material.textview.MaterialTextView |  | ||||||
|         android:id="@+id/name" |  | ||||||
|         android:layout_width="wrap_content" |  | ||||||
|         android:layout_height="50dp" |  | ||||||
|         android:gravity="start|center_vertical" |  | ||||||
|         android:paddingStart="20dp" |  | ||||||
|         android:paddingEnd="20dp" |  | ||||||
|         android:textColor="?attr/colorOnPrimary" |  | ||||||
|         app:layout_constraintBottom_toBottomOf="@id/group_color" |  | ||||||
|         app:layout_constraintEnd_toEndOf="@id/group_color" |  | ||||||
|         app:layout_constraintTop_toTopOf="@id/group_color" /> |  | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> |  | ||||||
| @@ -1,13 +0,0 @@ | |||||||
| <menu xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     tools:context="net.helcel.beans.activity.EditActivity" > |  | ||||||
|  |  | ||||||
|     <item |  | ||||||
|         android:id="@+id/action_color" |  | ||||||
|         android:orderInCategory="100" |  | ||||||
|         android:icon="@drawable/color" |  | ||||||
|         android:title="@string/action_color" |  | ||||||
|         app:showAsAction="ifRoom" /> |  | ||||||
|  |  | ||||||
| </menu> |  | ||||||
| @@ -1,25 +0,0 @@ | |||||||
| <menu xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     tools:context="net.helcel.beans.activity.MainActivity" > |  | ||||||
|  |  | ||||||
|     <item |  | ||||||
|         android:id="@+id/action_edit" |  | ||||||
|         android:orderInCategory="100" |  | ||||||
|         android:icon="@drawable/edit" |  | ||||||
|         android:title="@string/action_edit" |  | ||||||
|         app:showAsAction="ifRoom" /> |  | ||||||
|     <item |  | ||||||
|         android:id="@+id/action_stats" |  | ||||||
|         android:layout_width="wrap_content" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:orderInCategory="100" |  | ||||||
|         android:title="@string/action_stat" |  | ||||||
|         app:showAsAction="never" /> |  | ||||||
|     <item |  | ||||||
|         android:id="@+id/action_settings" |  | ||||||
|         android:orderInCategory="100" |  | ||||||
|         android:title="@string/action_settings" |  | ||||||
|         app:showAsAction="never" /> |  | ||||||
|  |  | ||||||
| </menu> |  | ||||||
| @@ -1,25 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <resources> |  | ||||||
|     <string-array name="entries_theme"> |  | ||||||
|         <item>@string/system</item> |  | ||||||
|         <item>@string/light</item> |  | ||||||
|         <item>@string/dark</item> |  | ||||||
|     </string-array> |  | ||||||
|  |  | ||||||
|     <string-array name="entries_stats"> |  | ||||||
|         <item>@string/counters</item> |  | ||||||
|         <item>@string/percentages</item> |  | ||||||
|     </string-array> |  | ||||||
|  |  | ||||||
|     <string-array name="entries_onoff"> |  | ||||||
|         <item>@string/on</item> |  | ||||||
|         <item>@string/off</item> |  | ||||||
|     </string-array> |  | ||||||
|  |  | ||||||
|     <string-array name="map_projection"> |  | ||||||
|         <item>@string/azimuthalequidistant</item> |  | ||||||
|         <item>@string/loximuthal</item> |  | ||||||
|         <item>@string/mercator</item> |  | ||||||
|  |  | ||||||
|     </string-array> |  | ||||||
| </resources> |  | ||||||
| @@ -1,10 +1,10 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <resources> | <resources> | ||||||
|     <string name="app_name">Beans</string> |  | ||||||
|     <string name="app_version">1.0a</string> |  | ||||||
|     <string name="action_settings">Settings</string> |     <string name="action_settings">Settings</string> | ||||||
|     <string name="action_stat">Stats</string> |     <string name="action_stat">Stats</string> | ||||||
|     <string name="action_edit">Edit</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="action_color">Color</string> | ||||||
|     <string name="key_theme">App theme</string> |     <string name="key_theme">App theme</string> | ||||||
|     <string name="system">System</string> |     <string name="system">System</string> | ||||||
| @@ -18,16 +18,17 @@ | |||||||
|     <string name="key_regional">Regional</string> |     <string name="key_regional">Regional</string> | ||||||
|     <string name="about">About</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_is_foss">Beans is free and open source software, licensed under the GNU General Public License (version 3 or later)</string> | ||||||
|     <string name="beans_repo">Project repository: https://github.com/helcel-net/beans\n Feel free to report issues or contribute to the project.</string> |     <string name="beans_repo_uri">https://github.com/helcel-net/beans</string> | ||||||
|  |     <string name="beans_repo">Project repository: %1$s\n Feel free to report issues or contribute.</string> | ||||||
|     <string name="foss_licenses">Free and open source dependencies and licenses</string> |     <string name="foss_licenses">Free and open source dependencies and licenses</string> | ||||||
|     <string name="about_beans">About the Beans application</string> |     <string name="about_beans">About the Beans application</string> | ||||||
|     <string name="edit_group">Select the group to assign. Long press on a group to edit its name and color.</string> |     <string name="edit_group">Select the group to assign.</string> | ||||||
|  |     <string name="edit_group_sub">Long press on a group to edit its name and color.</string> | ||||||
|  |     <string name="select_group">Select the group to keep.</string> | ||||||
|  |     <string name="select_group_sub">All others will be deleted and its mappings reassigned to the group you choose here.</string> | ||||||
|     <string name="delete_group">Are your sure you want to delete this group and remove all its country mappings?</string> |     <string name="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="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="name">Name</string> | ||||||
|     <string name="rate">%1$d/%2$d</string> |     <string name="rate">%1$d/%2$d</string> | ||||||
|     <string name="rate_with_unit">%1$d / %2$d %3$s</string> |     <string name="rate_with_unit">%1$d / %2$d %3$s</string> | ||||||
| @@ -39,9 +40,10 @@ | |||||||
|     <string name="off">Off</string> |     <string name="off">Off</string> | ||||||
|     <string name="delete">Delete</string> |     <string name="delete">Delete</string> | ||||||
|     <string name="cancel">Cancel</string> |     <string name="cancel">Cancel</string> | ||||||
|     <string name="ok">Ok</string> |     <string name="ok">OK</string> | ||||||
|     <string name="total">Total</string> |     <string name="total">Total</string> | ||||||
|     <string name="uncategorized">Uncategorized</string> |     <string name="uncategorized">Uncategorized</string> | ||||||
|  |  | ||||||
|     <string name="azimuthalequidistant">Azimuthal Equidistant</string> |     <string name="azimuthalequidistant">Azimuthal Equidistant</string> | ||||||
|     <string name="mercator">Mercator</string> |     <string name="mercator">Mercator</string> | ||||||
|     <string name="loximuthal">Loximuthal</string> |     <string name="loximuthal">Loximuthal</string> | ||||||
|   | |||||||
| @@ -1,32 +0,0 @@ | |||||||
| <resources> |  | ||||||
|     <style name="Theme.Beans" parent="Theme.Material3.DayNight"> |  | ||||||
|         <item name="colorPrimary">@color/blue</item> |  | ||||||
|         <item name="background">@color/darkgray</item> |  | ||||||
|         <item name="android:colorPrimary">?attr/colorPrimary</item> |  | ||||||
|         <item name="android:panelColorBackground">@color/lightgray</item> |  | ||||||
|         <item name="android:statusBarColor">?attr/colorPrimary</item> |  | ||||||
|  |  | ||||||
|         <item name="checkboxStyle">@style/Theme.Beans.CheckBox</item> |  | ||||||
|         <item name="actionBarStyle">@style/Theme.Beans.ActionBar</item> |  | ||||||
|         <item name="android:actionOverflowButtonStyle">@style/Theme.Beans.ActionBar.ButtonOverflow</item> |  | ||||||
|     </style> |  | ||||||
|  |  | ||||||
|     <style name="Theme.Beans.CheckBox" parent="Widget.Material3.CompoundButton.CheckBox"> |  | ||||||
|     </style> |  | ||||||
|  |  | ||||||
|     <style name="Theme.Beans.ActionBar" parent="Widget.Material3.ActionBar.Solid"> |  | ||||||
|         <item name="background">?attr/colorPrimary</item> |  | ||||||
|         <item name="titleTextStyle">@style/Theme.Beans.ActionBar.Text</item> |  | ||||||
|         <item name="android:tint">@color/white</item> |  | ||||||
|         <item name="actionMenuTextColor">@color/white</item> |  | ||||||
|         <item name="homeAsUpIndicator">@drawable/back</item> |  | ||||||
|     </style> |  | ||||||
|  |  | ||||||
|     <style name="Theme.Beans.ActionBar.Text" parent="TextAppearance.Material3.ActionBar.Title"> |  | ||||||
|         <item name="android:textColor">@color/white</item> |  | ||||||
|     </style> |  | ||||||
|  |  | ||||||
|     <style name="Theme.Beans.ActionBar.ButtonOverflow" parent="Widget.Material3.Search.ActionButton.Overflow"> |  | ||||||
|         <item name="android:tint">@color/white</item> |  | ||||||
|     </style> |  | ||||||
| </resources> |  | ||||||
| @@ -1,73 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |  | ||||||
|     android:theme="@style/Theme.Beans"> |  | ||||||
|  |  | ||||||
|     <ListPreference |  | ||||||
|         app:defaultValue="@string/system" |  | ||||||
|         app:enabled="true" |  | ||||||
|         app:entries="@array/entries_theme" |  | ||||||
|         app:entryValues="@array/entries_theme" |  | ||||||
|         app:icon="@drawable/palette" |  | ||||||
|         app:key="@string/key_theme" |  | ||||||
|         app:title="@string/key_theme" |  | ||||||
|         app:useSimpleSummaryProvider="true" /> |  | ||||||
|  |  | ||||||
|     <ListPreference |  | ||||||
|         app:defaultValue="@string/mercator" |  | ||||||
|         app:enabled="true" |  | ||||||
|         app:entries="@array/map_projection" |  | ||||||
|         app:entryValues="@array/map_projection" |  | ||||||
|         app:icon="@drawable/map" |  | ||||||
|         app:key="@string/key_projection" |  | ||||||
|         app:title="@string/key_projection" |  | ||||||
|         app:useSimpleSummaryProvider="true" /> |  | ||||||
|  |  | ||||||
|     <ListPreference |  | ||||||
|         app:defaultValue="@string/counters" |  | ||||||
|         app:enabled="true" |  | ||||||
|         app:entries="@array/entries_stats" |  | ||||||
|         app:entryValues="@array/entries_stats" |  | ||||||
|         app:icon="@drawable/stats" |  | ||||||
|         app:key="@string/key_stats" |  | ||||||
|         app:title="@string/key_stats" |  | ||||||
|         app:useSimpleSummaryProvider="true" /> |  | ||||||
|  |  | ||||||
|     <ListPreference |  | ||||||
|         app:defaultValue="@string/off" |  | ||||||
|         app:enabled="true" |  | ||||||
|         app:allowDividerAbove="true" |  | ||||||
|         app:entries="@array/entries_onoff" |  | ||||||
|         app:entryValues="@array/entries_onoff" |  | ||||||
|         app:icon="@drawable/group" |  | ||||||
|         app:key="@string/key_group" |  | ||||||
|         app:title="@string/key_group" |  | ||||||
|         app:useSimpleSummaryProvider="true" /> |  | ||||||
|  |  | ||||||
|     <ListPreference |  | ||||||
|         app:defaultValue="@string/off" |  | ||||||
|         app:enabled="true" |  | ||||||
|         app:entries="@array/entries_onoff" |  | ||||||
|         app:entryValues="@array/entries_onoff" |  | ||||||
|         app:icon="@drawable/zoomin" |  | ||||||
|         app:key="@string/key_regional" |  | ||||||
|         app:title="@string/key_regional" |  | ||||||
|         app:useSimpleSummaryProvider="true" /> |  | ||||||
|  |  | ||||||
|     <Preference |  | ||||||
|         android:summary="@string/foss_licenses" |  | ||||||
|         app:enabled="true" |  | ||||||
|         app:allowDividerAbove="true" |  | ||||||
|         app:icon="@drawable/licenses" |  | ||||||
|         app:key="@string/licenses" |  | ||||||
|         app:title="@string/licenses" /> |  | ||||||
|  |  | ||||||
|     <Preference |  | ||||||
|         android:summary="@string/about_beans" |  | ||||||
|         app:enabled="true" |  | ||||||
|         app:icon="@drawable/about" |  | ||||||
|         app:key="@string/about" |  | ||||||
|         app:title="@string/about" /> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| </PreferenceScreen> |  | ||||||
| @@ -2,5 +2,5 @@ | |||||||
| plugins { | plugins { | ||||||
|     id 'com.android.application' version '8.13.0' apply false |     id 'com.android.application' version '8.13.0' apply false | ||||||
|     id 'com.android.library' version '8.13.0' apply false |     id 'com.android.library' version '8.13.0' apply false | ||||||
|     id 'org.jetbrains.kotlin.android' version '2.2.10' apply false |     id 'org.jetbrains.kotlin.android' version '2.2.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 | distributionBase=GRADLE_USER_HOME | ||||||
| distributionPath=wrapper/dists | distributionPath=wrapper/dists | ||||||
| distributionSha256Sum=8fad3d78296ca518113f3d29016617c7f9367dc005f932bd9d93bf45ba46072b | distributionSha256Sum=df67a32e86e3276d011735facb1535f64d0d88df84fa87521e90becc2d735444 | ||||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip | ||||||
| networkTimeout=10000 | networkTimeout=10000 | ||||||
| validateDistributionUrl=true | validateDistributionUrl=true | ||||||
| zipStoreBase=GRADLE_USER_HOME | zipStoreBase=GRADLE_USER_HOME | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							| @@ -114,7 +114,6 @@ case "$( uname )" in                #( | |||||||
|   NONSTOP* )        nonstop=true ;; |   NONSTOP* )        nonstop=true ;; | ||||||
| esac | esac | ||||||
|  |  | ||||||
| CLASSPATH="\\\"\\\"" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # Determine the Java command to use to start the JVM. | # Determine the Java command to use to start the JVM. | ||||||
| @@ -172,7 +171,6 @@ fi | |||||||
| # For Cygwin or MSYS, switch paths to Windows format before running java | # For Cygwin or MSYS, switch paths to Windows format before running java | ||||||
| if "$cygwin" || "$msys" ; then | if "$cygwin" || "$msys" ; then | ||||||
|     APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) |     APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) | ||||||
|     CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) |  | ||||||
|  |  | ||||||
|     JAVACMD=$( cygpath --unix "$JAVACMD" ) |     JAVACMD=$( cygpath --unix "$JAVACMD" ) | ||||||
|  |  | ||||||
| @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | |||||||
|  |  | ||||||
| set -- \ | set -- \ | ||||||
|         "-Dorg.gradle.appname=$APP_BASE_NAME" \ |         "-Dorg.gradle.appname=$APP_BASE_NAME" \ | ||||||
|         -classpath "$CLASSPATH" \ |  | ||||||
|         -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ |         -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ | ||||||
|         "$@" |         "$@" | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							| @@ -70,11 +70,10 @@ goto fail | |||||||
| :execute | :execute | ||||||
| @rem Setup the command line | @rem Setup the command line | ||||||
|  |  | ||||||
| set CLASSPATH= |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @rem Execute Gradle | @rem Execute Gradle | ||||||
| "%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" %* | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* | ||||||
|  |  | ||||||
| :end | :end | ||||||
| @rem End local scope for the variables with windows NT shell | @rem End local scope for the variables with windows NT shell | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@turf/area": "^7.0.0", |     "@turf/area": "^7.0.0", | ||||||
|     "@turf/turf": "^7.0.0", |     "@turf/turf": "^7.0.0", | ||||||
|     "jsdom": "^26.0.0", |     "jsdom": "^27.0.0", | ||||||
|     "mapshaper": "^0.6.79" |     "mapshaper": "^0.6.79" | ||||||
|   }, |   }, | ||||||
|   "type": "module" |   "type": "module" | ||||||
|   | |||||||
							
								
								
									
										318
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										318
									
								
								yarn.lock
									
									
									
									
									
								
							| @@ -2,41 +2,62 @@ | |||||||
| # yarn lockfile v1 | # yarn lockfile v1 | ||||||
|  |  | ||||||
|  |  | ||||||
| "@asamuzakjp/css-color@^3.2.0": | "@asamuzakjp/css-color@^4.0.3": | ||||||
|   version "3.2.0" |   version "4.0.5" | ||||||
|   resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-3.2.0.tgz#cc42f5b85c593f79f1fa4f25d2b9b321e61d1794" |   resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-4.0.5.tgz#cc533095241d8a56c49614591955280ab8c4bb02" | ||||||
|   integrity sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw== |   integrity sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ== | ||||||
|   dependencies: |   dependencies: | ||||||
|     "@csstools/css-calc" "^2.1.3" |     "@csstools/css-calc" "^2.1.4" | ||||||
|     "@csstools/css-color-parser" "^3.0.9" |     "@csstools/css-color-parser" "^3.1.0" | ||||||
|     "@csstools/css-parser-algorithms" "^3.0.4" |     "@csstools/css-parser-algorithms" "^3.0.5" | ||||||
|     "@csstools/css-tokenizer" "^3.0.3" |     "@csstools/css-tokenizer" "^3.0.4" | ||||||
|     lru-cache "^10.4.3" |     lru-cache "^11.2.1" | ||||||
|  |  | ||||||
| "@csstools/color-helpers@^5.0.2": | "@asamuzakjp/dom-selector@^6.7.2": | ||||||
|   version "5.0.2" |   version "6.7.3" | ||||||
|   resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.2.tgz#82592c9a7c2b83c293d9161894e2a6471feb97b8" |   resolved "https://registry.yarnpkg.com/@asamuzakjp/dom-selector/-/dom-selector-6.7.3.tgz#c6b1dadf8f4365de5db4435bba53328265c83f6c" | ||||||
|   integrity sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA== |   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/css-calc@^2.1.3", "@csstools/css-calc@^2.1.4": | "@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": | ||||||
|   version "2.1.4" |   version "2.1.4" | ||||||
|   resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.4.tgz#8473f63e2fcd6e459838dd412401d5948f224c65" |   resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.4.tgz#8473f63e2fcd6e459838dd412401d5948f224c65" | ||||||
|   integrity sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ== |   integrity sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ== | ||||||
|  |  | ||||||
| "@csstools/css-color-parser@^3.0.9": | "@csstools/css-color-parser@^3.1.0": | ||||||
|   version "3.0.10" |   version "3.1.0" | ||||||
|   resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz#79fc68864dd43c3b6782d2b3828bc0fa9d085c10" |   resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz#4e386af3a99dd36c46fef013cfe4c1c341eed6f0" | ||||||
|   integrity sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg== |   integrity sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA== | ||||||
|   dependencies: |   dependencies: | ||||||
|     "@csstools/color-helpers" "^5.0.2" |     "@csstools/color-helpers" "^5.1.0" | ||||||
|     "@csstools/css-calc" "^2.1.4" |     "@csstools/css-calc" "^2.1.4" | ||||||
|  |  | ||||||
| "@csstools/css-parser-algorithms@^3.0.4": | "@csstools/css-parser-algorithms@^3.0.5": | ||||||
|   version "3.0.5" |   version "3.0.5" | ||||||
|   resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz#5755370a9a29abaec5515b43c8b3f2cf9c2e3076" |   resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz#5755370a9a29abaec5515b43c8b3f2cf9c2e3076" | ||||||
|   integrity sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ== |   integrity sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ== | ||||||
|  |  | ||||||
| "@csstools/css-tokenizer@^3.0.3": | "@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": | ||||||
|   version "3.0.4" |   version "3.0.4" | ||||||
|   resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz#333fedabc3fd1a8e5d0100013731cf19e6a8c5d3" |   resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz#333fedabc3fd1a8e5d0100013731cf19e6a8c5d3" | ||||||
|   integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== |   integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== | ||||||
| @@ -77,9 +98,9 @@ | |||||||
|   integrity sha512-pQaoQTBvDf7p7d/3ZHDaxWaU62guSYB9KQ6vvecshELunzpdN5tbgw0d+SVO1eYaTlrxX3Nvi7F9DI8FcoJePg== |   integrity sha512-pQaoQTBvDf7p7d/3ZHDaxWaU62guSYB9KQ6vvecshELunzpdN5tbgw0d+SVO1eYaTlrxX3Nvi7F9DI8FcoJePg== | ||||||
|  |  | ||||||
| "@rollup/rollup-linux-x64-gnu@^4.44.1": | "@rollup/rollup-linux-x64-gnu@^4.44.1": | ||||||
|   version "4.50.0" |   version "4.52.5" | ||||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.0.tgz#8887c58bd51242754ae9c56947d6e883332dcc74" |   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz#a151cb1234cc9b2cf5e8cfc02aa91436b8f9e278" | ||||||
|   integrity sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA== |   integrity sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q== | ||||||
|  |  | ||||||
| "@tmcw/togeojson@^5.6.0": | "@tmcw/togeojson@^5.6.0": | ||||||
|   version "5.8.1" |   version "5.8.1" | ||||||
| @@ -1566,22 +1587,17 @@ | |||||||
|   integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== |   integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== | ||||||
|  |  | ||||||
| "@types/node@*": | "@types/node@*": | ||||||
|   version "24.3.1" |   version "24.9.1" | ||||||
|   resolved "https://registry.yarnpkg.com/@types/node/-/node-24.3.1.tgz#b0a3fb2afed0ef98e8d7f06d46ef6349047709f3" |   resolved "https://registry.yarnpkg.com/@types/node/-/node-24.9.1.tgz#b7360b3c789089e57e192695a855aa4f6981a53c" | ||||||
|   integrity sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g== |   integrity sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg== | ||||||
|   dependencies: |   dependencies: | ||||||
|     undici-types "~7.10.0" |     undici-types "~7.16.0" | ||||||
|  |  | ||||||
| "@types/node@^10.0.3": | "@types/node@^10.0.3": | ||||||
|   version "10.17.60" |   version "10.17.60" | ||||||
|   resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" |   resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" | ||||||
|   integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== |   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": | "@types/node@^8.0.0": | ||||||
|   version "8.10.66" |   version "8.10.66" | ||||||
|   resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" |   resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" | ||||||
| @@ -1617,6 +1633,13 @@ asynckit@^0.4.0: | |||||||
|   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" |   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" | ||||||
|   integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== |   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: | big.js@^7.0.1: | ||||||
|   version "7.0.1" |   version "7.0.1" | ||||||
|   resolved "https://registry.yarnpkg.com/big.js/-/big.js-7.0.1.tgz#c537c649ec6ea11d1306723d13c096ba199aadc4" |   resolved "https://registry.yarnpkg.com/big.js/-/big.js-7.0.1.tgz#c537c649ec6ea11d1306723d13c096ba199aadc4" | ||||||
| @@ -1648,7 +1671,7 @@ call-bound@^1.0.2: | |||||||
|     call-bind-apply-helpers "^1.0.2" |     call-bind-apply-helpers "^1.0.2" | ||||||
|     get-intrinsic "^1.3.0" |     get-intrinsic "^1.3.0" | ||||||
|  |  | ||||||
| caseless@~0.12.0: | caseless@^0.12.0, caseless@~0.12.0: | ||||||
|   version "0.12.0" |   version "0.12.0" | ||||||
|   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" |   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" | ||||||
|   integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== |   integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== | ||||||
| @@ -1670,7 +1693,7 @@ commander@7.0.0: | |||||||
|   resolved "https://registry.yarnpkg.com/commander/-/commander-7.0.0.tgz#3e2bbfd8bb6724760980988fb5b22b7ee6b71ab2" |   resolved "https://registry.yarnpkg.com/commander/-/commander-7.0.0.tgz#3e2bbfd8bb6724760980988fb5b22b7ee6b71ab2" | ||||||
|   integrity sha512-ovx/7NkTrnPuIV8sqk/GjUIIM1+iUQeqA3ye2VNpq9sVoiZsooObWlQy+OPWGI17GDaEoybuAGJm6U8yC077BA== |   integrity sha512-ovx/7NkTrnPuIV8sqk/GjUIIM1+iUQeqA3ye2VNpq9sVoiZsooObWlQy+OPWGI17GDaEoybuAGJm6U8yC077BA== | ||||||
|  |  | ||||||
| concat-stream@^1.4.6, concat-stream@^1.6.0: | concat-stream@^1.6.0, concat-stream@^1.6.2: | ||||||
|   version "1.6.2" |   version "1.6.2" | ||||||
|   resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" |   resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" | ||||||
|   integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== |   integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== | ||||||
| @@ -1703,13 +1726,22 @@ core-util-is@~1.0.0: | |||||||
|   resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" |   resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" | ||||||
|   integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== |   integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== | ||||||
|  |  | ||||||
| cssstyle@^4.2.1: | css-tree@^3.1.0: | ||||||
|   version "4.6.0" |   version "3.1.0" | ||||||
|   resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.6.0.tgz#ea18007024e3167f4f105315f3ec2d982bf48ed9" |   resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-3.1.0.tgz#7aabc035f4e66b5c86f54570d55e05b1346eb0fd" | ||||||
|   integrity sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg== |   integrity sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w== | ||||||
|   dependencies: |   dependencies: | ||||||
|     "@asamuzakjp/css-color" "^3.2.0" |     mdn-data "2.12.2" | ||||||
|     rrweb-cssom "^0.8.0" |     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" | ||||||
|  |  | ||||||
| d3-array@1: | d3-array@1: | ||||||
|   version "1.2.4" |   version "1.2.4" | ||||||
| @@ -1748,22 +1780,22 @@ d3-voronoi@1.1.2: | |||||||
|   resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.2.tgz#1687667e8f13a2d158c80c1480c5a29cb0d8973c" |   resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.2.tgz#1687667e8f13a2d158c80c1480c5a29cb0d8973c" | ||||||
|   integrity sha512-RhGS1u2vavcO7ay7ZNAPo4xeDh/VYeGof3x5ZLJBQgYhLegxr3s5IykvWmJ94FTU6mcbtp4sloqZ54mP6R4Utw== |   integrity sha512-RhGS1u2vavcO7ay7ZNAPo4xeDh/VYeGof3x5ZLJBQgYhLegxr3s5IykvWmJ94FTU6mcbtp4sloqZ54mP6R4Utw== | ||||||
|  |  | ||||||
| data-urls@^5.0.0: | data-urls@^6.0.0: | ||||||
|   version "5.0.0" |   version "6.0.0" | ||||||
|   resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" |   resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-6.0.0.tgz#95a7943c8ac14c1d563b771f2621cc50e8ec7744" | ||||||
|   integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== |   integrity sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA== | ||||||
|   dependencies: |   dependencies: | ||||||
|     whatwg-mimetype "^4.0.0" |     whatwg-mimetype "^4.0.0" | ||||||
|     whatwg-url "^14.0.0" |     whatwg-url "^15.0.0" | ||||||
|  |  | ||||||
| debug@4, debug@^4.3.4: | debug@4, debug@^4.3.4: | ||||||
|   version "4.4.1" |   version "4.4.3" | ||||||
|   resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" |   resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" | ||||||
|   integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== |   integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== | ||||||
|   dependencies: |   dependencies: | ||||||
|     ms "^2.1.3" |     ms "^2.1.3" | ||||||
|  |  | ||||||
| decimal.js@^10.5.0: | decimal.js@^10.6.0: | ||||||
|   version "10.6.0" |   version "10.6.0" | ||||||
|   resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a" |   resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a" | ||||||
|   integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== |   integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== | ||||||
| @@ -1786,9 +1818,9 @@ depd@~2.0.0: | |||||||
|   integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== |   integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== | ||||||
|  |  | ||||||
| detect-libc@^2.0.1: | detect-libc@^2.0.1: | ||||||
|   version "2.0.4" |   version "2.1.2" | ||||||
|   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.4.tgz#f04715b8ba815e53b4d8109655b6508a6865a7e8" |   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" | ||||||
|   integrity sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA== |   integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== | ||||||
|  |  | ||||||
| dunder-proto@^1.0.1: | dunder-proto@^1.0.1: | ||||||
|   version "1.0.1" |   version "1.0.1" | ||||||
| @@ -1875,6 +1907,11 @@ function-bind@^1.1.2: | |||||||
|   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" |   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" | ||||||
|   integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== |   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: | geographiclib@1.48.0: | ||||||
|   version "1.48.0" |   version "1.48.0" | ||||||
|   resolved "https://registry.yarnpkg.com/geographiclib/-/geographiclib-1.48.0.tgz#8ff2ae185ad380f675db6a243935fadd147def82" |   resolved "https://registry.yarnpkg.com/geographiclib/-/geographiclib-1.48.0.tgz#8ff2ae185ad380f675db6a243935fadd147def82" | ||||||
| @@ -1961,15 +1998,13 @@ html-encoding-sniffer@^4.0.0: | |||||||
|   dependencies: |   dependencies: | ||||||
|     whatwg-encoding "^3.1.1" |     whatwg-encoding "^3.1.1" | ||||||
|  |  | ||||||
| http-basic@^6.0.0: | http-basic@^8.1.1: | ||||||
|   version "6.0.0" |   version "8.1.3" | ||||||
|   resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-6.0.0.tgz#1d63df8f891e1e25e0c2c84d7a2d94702d6ae229" |   resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf" | ||||||
|   integrity sha512-7ScbVjuiReYe8S+OZOpNjoKGXrbhJHIrQQe7eq1TpLTJkxH8MPKvnTUzq/TNLjww1hdFQy8yUIC42wuLhCjYcQ== |   integrity sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw== | ||||||
|   dependencies: |   dependencies: | ||||||
|     "@types/concat-stream" "^1.6.0" |     caseless "^0.12.0" | ||||||
|     "@types/node" "^7.0.31" |     concat-stream "^1.6.2" | ||||||
|     caseless "~0.12.0" |  | ||||||
|     concat-stream "^1.4.6" |  | ||||||
|     http-response-object "^3.0.1" |     http-response-object "^3.0.1" | ||||||
|     parse-cache-control "^1.0.1" |     parse-cache-control "^1.0.1" | ||||||
|  |  | ||||||
| @@ -2028,30 +2063,30 @@ isarray@~1.0.0: | |||||||
|   resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" |   resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" | ||||||
|   integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== |   integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== | ||||||
|  |  | ||||||
| jsdom@^26.0.0: | jsdom@^27.0.0: | ||||||
|   version "26.1.0" |   version "27.0.1" | ||||||
|   resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-26.1.0.tgz#ab5f1c1cafc04bd878725490974ea5e8bf0c72b3" |   resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-27.0.1.tgz#72f3ff263ccc128da40d8526943a7df253eeea93" | ||||||
|   integrity sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg== |   integrity sha512-SNSQteBL1IlV2zqhwwolaG9CwhIhTvVHWg3kTss/cLE7H/X4644mtPQqYvCfsSrGQWt9hSZcgOXX8bOZaMN+kA== | ||||||
|   dependencies: |   dependencies: | ||||||
|     cssstyle "^4.2.1" |     "@asamuzakjp/dom-selector" "^6.7.2" | ||||||
|     data-urls "^5.0.0" |     cssstyle "^5.3.1" | ||||||
|     decimal.js "^10.5.0" |     data-urls "^6.0.0" | ||||||
|  |     decimal.js "^10.6.0" | ||||||
|     html-encoding-sniffer "^4.0.0" |     html-encoding-sniffer "^4.0.0" | ||||||
|     http-proxy-agent "^7.0.2" |     http-proxy-agent "^7.0.2" | ||||||
|     https-proxy-agent "^7.0.6" |     https-proxy-agent "^7.0.6" | ||||||
|     is-potential-custom-element-name "^1.0.1" |     is-potential-custom-element-name "^1.0.1" | ||||||
|     nwsapi "^2.2.16" |     parse5 "^8.0.0" | ||||||
|     parse5 "^7.2.1" |  | ||||||
|     rrweb-cssom "^0.8.0" |     rrweb-cssom "^0.8.0" | ||||||
|     saxes "^6.0.0" |     saxes "^6.0.0" | ||||||
|     symbol-tree "^3.2.4" |     symbol-tree "^3.2.4" | ||||||
|     tough-cookie "^5.1.1" |     tough-cookie "^6.0.0" | ||||||
|     w3c-xmlserializer "^5.0.0" |     w3c-xmlserializer "^5.0.0" | ||||||
|     webidl-conversions "^7.0.0" |     webidl-conversions "^8.0.0" | ||||||
|     whatwg-encoding "^3.1.1" |     whatwg-encoding "^3.1.1" | ||||||
|     whatwg-mimetype "^4.0.0" |     whatwg-mimetype "^4.0.0" | ||||||
|     whatwg-url "^14.1.1" |     whatwg-url "^15.1.0" | ||||||
|     ws "^8.18.0" |     ws "^8.18.3" | ||||||
|     xml-name-validator "^5.0.0" |     xml-name-validator "^5.0.0" | ||||||
|  |  | ||||||
| jsts@2.7.1: | jsts@2.7.1: | ||||||
| @@ -2071,15 +2106,15 @@ keygrip@~1.1.0: | |||||||
|   dependencies: |   dependencies: | ||||||
|     tsscmp "1.0.6" |     tsscmp "1.0.6" | ||||||
|  |  | ||||||
| lru-cache@^10.4.3: | lru-cache@^11.2.1, lru-cache@^11.2.2: | ||||||
|   version "10.4.3" |   version "11.2.2" | ||||||
|   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" |   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.2.tgz#40fd37edffcfae4b2940379c0722dc6eeaa75f24" | ||||||
|   integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== |   integrity sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg== | ||||||
|  |  | ||||||
| mapshaper@^0.6.79: | mapshaper@^0.6.79: | ||||||
|   version "0.6.111" |   version "0.6.113" | ||||||
|   resolved "https://registry.yarnpkg.com/mapshaper/-/mapshaper-0.6.111.tgz#4dc67e94844b3134ad05d6fedcd5678c1742dd62" |   resolved "https://registry.yarnpkg.com/mapshaper/-/mapshaper-0.6.113.tgz#23fadfd247c4b53c892655aa796f470504e77cf1" | ||||||
|   integrity sha512-17ywtn2q35U21O3OQOunGVQ2AAfjya0+gZPvPh3C/IcCJ0BEO6fTq3JK6UL5JN2sr4w33YrnKhIJiPkcqPwNVg== |   integrity sha512-eN37+sb5pE084b6pVgRUsM5s5aFwwilYZQm8AN/FN8jD7hzoQ48aBwvd+QNzrIYSf/ZknbT9tYa4QuaoR1KRWg== | ||||||
|   dependencies: |   dependencies: | ||||||
|     "@placemarkio/tokml" "^0.3.3" |     "@placemarkio/tokml" "^0.3.3" | ||||||
|     "@tmcw/togeojson" "^5.6.0" |     "@tmcw/togeojson" "^5.6.0" | ||||||
| @@ -2094,6 +2129,7 @@ mapshaper@^0.6.79: | |||||||
|     delaunator "^5.0.0" |     delaunator "^5.0.0" | ||||||
|     fflate "0.8.2" |     fflate "0.8.2" | ||||||
|     flatbush "^3.2.1" |     flatbush "^3.2.1" | ||||||
|  |     geographiclib-geodesic "^2.2.0" | ||||||
|     geokdbush "^1.1.0" |     geokdbush "^1.1.0" | ||||||
|     iconv-lite "^0.6.3" |     iconv-lite "^0.6.3" | ||||||
|     idb-keyval "^6.2.0" |     idb-keyval "^6.2.0" | ||||||
| @@ -2102,7 +2138,7 @@ mapshaper@^0.6.79: | |||||||
|     msgpackr "^1.10.1" |     msgpackr "^1.10.1" | ||||||
|     opn "^5.3.0" |     opn "^5.3.0" | ||||||
|     rw "~1.3.3" |     rw "~1.3.3" | ||||||
|     sync-request "5.0.0" |     sync-request "6.1.0" | ||||||
|     tinyqueue "^2.0.3" |     tinyqueue "^2.0.3" | ||||||
|   optionalDependencies: |   optionalDependencies: | ||||||
|     "@rollup/rollup-linux-x64-gnu" "^4.44.1" |     "@rollup/rollup-linux-x64-gnu" "^4.44.1" | ||||||
| @@ -2117,6 +2153,11 @@ math-intrinsics@^1.1.0: | |||||||
|   resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" |   resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" | ||||||
|   integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== |   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: | mime-db@1.52.0: | ||||||
|   version "1.52.0" |   version "1.52.0" | ||||||
|   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" |   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" | ||||||
| @@ -2170,11 +2211,6 @@ node-gyp-build-optional-packages@5.2.2: | |||||||
|   dependencies: |   dependencies: | ||||||
|     detect-libc "^2.0.1" |     detect-libc "^2.0.1" | ||||||
|  |  | ||||||
| nwsapi@^2.2.16: |  | ||||||
|   version "2.2.21" |  | ||||||
|   resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.21.tgz#8df7797079350adda208910d8c33fc4c2d7520c3" |  | ||||||
|   integrity sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA== |  | ||||||
|  |  | ||||||
| object-inspect@^1.13.3: | object-inspect@^1.13.3: | ||||||
|   version "1.13.4" |   version "1.13.4" | ||||||
|   resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" |   resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" | ||||||
| @@ -2192,10 +2228,10 @@ parse-cache-control@^1.0.1: | |||||||
|   resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e" |   resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e" | ||||||
|   integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg== |   integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg== | ||||||
|  |  | ||||||
| parse5@^7.2.1: | parse5@^8.0.0: | ||||||
|   version "7.3.0" |   version "8.0.0" | ||||||
|   resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05" |   resolved "https://registry.yarnpkg.com/parse5/-/parse5-8.0.0.tgz#aceb267f6b15f9b6e6ba9e35bfdd481fc2167b12" | ||||||
|   integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw== |   integrity sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA== | ||||||
|   dependencies: |   dependencies: | ||||||
|     entities "^6.0.0" |     entities "^6.0.0" | ||||||
|  |  | ||||||
| @@ -2280,6 +2316,11 @@ readable-stream@^2.2.2: | |||||||
|     string_decoder "~1.1.1" |     string_decoder "~1.1.1" | ||||||
|     util-deprecate "~1.0.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: | robust-predicates@^2.0.4: | ||||||
|   version "2.0.4" |   version "2.0.4" | ||||||
|   resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-2.0.4.tgz#0a2367a93abd99676d075981707f29cfb402248b" |   resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-2.0.4.tgz#0a2367a93abd99676d075981707f29cfb402248b" | ||||||
| @@ -2367,6 +2408,11 @@ skmeans@0.9.7: | |||||||
|   resolved "https://registry.yarnpkg.com/skmeans/-/skmeans-0.9.7.tgz#72670cebb728508f56e29c0e10d11e623529ce5d" |   resolved "https://registry.yarnpkg.com/skmeans/-/skmeans-0.9.7.tgz#72670cebb728508f56e29c0e10d11e623529ce5d" | ||||||
|   integrity sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg== |   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: | splaytree-ts@^1.0.2: | ||||||
|   version "1.0.2" |   version "1.0.2" | ||||||
|   resolved "https://registry.yarnpkg.com/splaytree-ts/-/splaytree-ts-1.0.2.tgz#34963704587aff45eaa09c24713f552bbf56e8f0" |   resolved "https://registry.yarnpkg.com/splaytree-ts/-/splaytree-ts-1.0.2.tgz#34963704587aff45eaa09c24713f552bbf56e8f0" | ||||||
| @@ -2391,26 +2437,26 @@ symbol-tree@^3.2.4: | |||||||
|   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" |   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" | ||||||
|   integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== |   integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== | ||||||
|  |  | ||||||
| sync-request@5.0.0: | sync-request@6.1.0: | ||||||
|   version "5.0.0" |   version "6.1.0" | ||||||
|   resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-5.0.0.tgz#b815d5c6e16193392ec4139a4881db5e90e88676" |   resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-6.1.0.tgz#e96217565b5e50bbffe179868ba75532fb597e68" | ||||||
|   integrity sha512-NKhEA4WacR3mRBIFz1niXrIUTrUVFtP2spzrLMINangebvJ/EFyVv+LMJKvVl6UIrJM4Fburnnj91lRnqb4WkA== |   integrity sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw== | ||||||
|   dependencies: |   dependencies: | ||||||
|     http-response-object "^3.0.1" |     http-response-object "^3.0.1" | ||||||
|     sync-rpc "^1.2.0" |     sync-rpc "^1.2.1" | ||||||
|     then-request "^5.0.0" |     then-request "^6.0.0" | ||||||
|  |  | ||||||
| sync-rpc@^1.2.0: | sync-rpc@^1.2.1: | ||||||
|   version "1.3.6" |   version "1.3.6" | ||||||
|   resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7" |   resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7" | ||||||
|   integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw== |   integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw== | ||||||
|   dependencies: |   dependencies: | ||||||
|     get-port "^3.1.0" |     get-port "^3.1.0" | ||||||
|  |  | ||||||
| then-request@^5.0.0: | then-request@^6.0.0: | ||||||
|   version "5.0.0" |   version "6.0.2" | ||||||
|   resolved "https://registry.yarnpkg.com/then-request/-/then-request-5.0.0.tgz#7a23f616799597621de8cfebc77c61fd28c00d64" |   resolved "https://registry.yarnpkg.com/then-request/-/then-request-6.0.2.tgz#ec18dd8b5ca43aaee5cb92f7e4c1630e950d4f0c" | ||||||
|   integrity sha512-A3uIVLD33SAvB10PfsxLuQBMV8GVC/6xKBMPOvkJchi6251e5AMJ+Yy+RVKsVsnj0iYNhN2E5SkNSi58H19wsw== |   integrity sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA== | ||||||
|   dependencies: |   dependencies: | ||||||
|     "@types/concat-stream" "^1.6.0" |     "@types/concat-stream" "^1.6.0" | ||||||
|     "@types/form-data" "0.0.33" |     "@types/form-data" "0.0.33" | ||||||
| @@ -2419,7 +2465,7 @@ then-request@^5.0.0: | |||||||
|     caseless "~0.12.0" |     caseless "~0.12.0" | ||||||
|     concat-stream "^1.6.0" |     concat-stream "^1.6.0" | ||||||
|     form-data "^2.2.0" |     form-data "^2.2.0" | ||||||
|     http-basic "^6.0.0" |     http-basic "^8.1.1" | ||||||
|     http-response-object "^3.0.1" |     http-response-object "^3.0.1" | ||||||
|     promise "^8.0.0" |     promise "^8.0.0" | ||||||
|     qs "^6.4.0" |     qs "^6.4.0" | ||||||
| @@ -2434,17 +2480,17 @@ tinyqueue@^2.0.0, tinyqueue@^2.0.3: | |||||||
|   resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08" |   resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08" | ||||||
|   integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA== |   integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA== | ||||||
|  |  | ||||||
| tldts-core@^6.1.86: | tldts-core@^7.0.17: | ||||||
|   version "6.1.86" |   version "7.0.17" | ||||||
|   resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.86.tgz#a93e6ed9d505cb54c542ce43feb14c73913265d8" |   resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.0.17.tgz#dadfee3750dd272ed219d7367beb7cbb2ff29eb8" | ||||||
|   integrity sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA== |   integrity sha512-DieYoGrP78PWKsrXr8MZwtQ7GLCUeLxihtjC1jZsW1DnvSMdKPitJSe8OSYDM2u5H6g3kWJZpePqkp43TfLh0g== | ||||||
|  |  | ||||||
| tldts@^6.1.32: | tldts@^7.0.5: | ||||||
|   version "6.1.86" |   version "7.0.17" | ||||||
|   resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.86.tgz#087e0555b31b9725ee48ca7e77edc56115cd82f7" |   resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.0.17.tgz#a6cdc067b9e80ea05f3be471c0ea410688cc78b2" | ||||||
|   integrity sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ== |   integrity sha512-Y1KQBgDd/NUc+LfOtKS6mNsC9CCaH+m2P1RoIZy7RAPo3C3/t8X45+zgut31cRZtZ3xKPjfn3TkGTrctC2TQIQ== | ||||||
|   dependencies: |   dependencies: | ||||||
|     tldts-core "^6.1.86" |     tldts-core "^7.0.17" | ||||||
|  |  | ||||||
| topojson-client@3.x: | topojson-client@3.x: | ||||||
|   version "3.1.0" |   version "3.1.0" | ||||||
| @@ -2460,17 +2506,17 @@ topojson-server@3.x: | |||||||
|   dependencies: |   dependencies: | ||||||
|     commander "2" |     commander "2" | ||||||
|  |  | ||||||
| tough-cookie@^5.1.1: | tough-cookie@^6.0.0: | ||||||
|   version "5.1.2" |   version "6.0.0" | ||||||
|   resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.2.tgz#66d774b4a1d9e12dc75089725af3ac75ec31bed7" |   resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-6.0.0.tgz#11e418b7864a2c0d874702bc8ce0f011261940e5" | ||||||
|   integrity sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A== |   integrity sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w== | ||||||
|   dependencies: |   dependencies: | ||||||
|     tldts "^6.1.32" |     tldts "^7.0.5" | ||||||
|  |  | ||||||
| tr46@^5.1.0: | tr46@^6.0.0: | ||||||
|   version "5.1.1" |   version "6.0.0" | ||||||
|   resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.1.1.tgz#96ae867cddb8fdb64a49cc3059a8d428bcf238ca" |   resolved "https://registry.yarnpkg.com/tr46/-/tr46-6.0.0.tgz#f5a1ae546a0adb32a277a2278d0d17fa2f9093e6" | ||||||
|   integrity sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw== |   integrity sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw== | ||||||
|   dependencies: |   dependencies: | ||||||
|     punycode "^2.3.1" |     punycode "^2.3.1" | ||||||
|  |  | ||||||
| @@ -2489,10 +2535,10 @@ typedarray@^0.0.6: | |||||||
|   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" |   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" | ||||||
|   integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== |   integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== | ||||||
|  |  | ||||||
| undici-types@~7.10.0: | undici-types@~7.16.0: | ||||||
|   version "7.10.0" |   version "7.16.0" | ||||||
|   resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.10.0.tgz#4ac2e058ce56b462b056e629cc6a02393d3ff350" |   resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" | ||||||
|   integrity sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag== |   integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== | ||||||
|  |  | ||||||
| util-deprecate@~1.0.1: | util-deprecate@~1.0.1: | ||||||
|   version "1.0.2" |   version "1.0.2" | ||||||
| @@ -2506,10 +2552,10 @@ w3c-xmlserializer@^5.0.0: | |||||||
|   dependencies: |   dependencies: | ||||||
|     xml-name-validator "^5.0.0" |     xml-name-validator "^5.0.0" | ||||||
|  |  | ||||||
| webidl-conversions@^7.0.0: | webidl-conversions@^8.0.0: | ||||||
|   version "7.0.0" |   version "8.0.0" | ||||||
|   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" |   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-8.0.0.tgz#821c92aa4f88d88a31264d887e244cb9655690c6" | ||||||
|   integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== |   integrity sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA== | ||||||
|  |  | ||||||
| whatwg-encoding@^3.1.1: | whatwg-encoding@^3.1.1: | ||||||
|   version "3.1.1" |   version "3.1.1" | ||||||
| @@ -2523,15 +2569,15 @@ whatwg-mimetype@^4.0.0: | |||||||
|   resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" |   resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" | ||||||
|   integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== |   integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== | ||||||
|  |  | ||||||
| whatwg-url@^14.0.0, whatwg-url@^14.1.1: | whatwg-url@^15.0.0, whatwg-url@^15.1.0: | ||||||
|   version "14.2.0" |   version "15.1.0" | ||||||
|   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.2.0.tgz#4ee02d5d725155dae004f6ae95c73e7ef5d95663" |   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-15.1.0.tgz#5c433439b9a5789eeb3806bbd0da89a8bd40b8d7" | ||||||
|   integrity sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw== |   integrity sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g== | ||||||
|   dependencies: |   dependencies: | ||||||
|     tr46 "^5.1.0" |     tr46 "^6.0.0" | ||||||
|     webidl-conversions "^7.0.0" |     webidl-conversions "^8.0.0" | ||||||
|  |  | ||||||
| ws@^8.18.0: | ws@^8.18.3: | ||||||
|   version "8.18.3" |   version "8.18.3" | ||||||
|   resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" |   resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" | ||||||
|   integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== |   integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user