Compare commits
	
		
			138 Commits
		
	
	
		
			1.0c
			...
			86aa888be6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 86aa888be6 | ||
|  | 9c1374c99f | ||
|  | 6b2f786afe | ||
|  | 2ded750c46 | ||
|  | 686be17fd2 | ||
|  | 7339e16e61 | ||
|  | e43264b327 | ||
|  | 532a34cc7f | ||
|  | 59a02332d0 | ||
|  | 02a83a491f | ||
|  | 0a01aa95f1 | ||
|  | 438eae23b0 | ||
|  | 796faf393e | ||
|  | 9643d0ebb8 | ||
|  | 0999a0c512 | ||
|  | 7a3eb6c8f3 | ||
|  | 5a01c846fa | ||
|  | 4533f36bd2 | ||
|  | ec45f36ec4 | ||
|  | 5396b570ec | ||
|  | d0c656e3d0 | ||
|  | adc416a5e3 | ||
|  | b30430ec7d | ||
|  | 767d018d45 | ||
|  | cbfc484ed8 | ||
|  | 39418809f6 | ||
|  | 40ab056bc0 | ||
|  | ad64b289d4 | ||
|  | 5bdb16b48b | ||
|  | 89ca3200b4 | ||
|  | 54dbf74721 | ||
|  | 2cc7a69b0f | ||
|  | 37026870c7 | ||
|  | 8a9e389a53 | ||
|  | 7a2bae0e6b | ||
|  | 5b199aef8e | ||
|  | 99ff29af88 | ||
|  | 1d36d282f4 | ||
|  | 122dfe8981 | ||
|  | 0f9f428eed | ||
|  | ff4a2513c3 | ||
|  | 14a3fb9ffd | ||
|  | 952bddf9e2 | ||
|  | f346a93b1f | ||
|  | 7ac9dee6d8 | ||
|  | 767b98468b | ||
|  | 84c20349c7 | ||
|  | 097b86b6a5 | ||
|  | 30f6853d98 | ||
|  | fe201a139d | ||
|  | 4da09b509f | ||
|  | c009e750bb | ||
|  | 69ea4cf7c0 | ||
|  | 2f3c3ee84c | ||
|  | dbd84a91bf | ||
|  | 34560b2da2 | ||
|  | c8bb846d0f | ||
|  | 5bd6920857 | ||
|  | 5080498b96 | ||
|  | 3cc522aaa5 | ||
|  | 832b06f424 | ||
|  | 19dfd83351 | ||
|  | 75058f0705 | ||
|  | da2d72246e | ||
|  | e13517317f | ||
|  | ca6d74b581 | ||
|  | 23b47df111 | ||
|  | 38422cc30a | ||
|  | 2f314e5082 | ||
|  | 27befa1790 | ||
|  | 86aaa3899b | ||
|  | 205f81bdab | ||
|  | 9899f8038b | ||
|  | 62b3ff8500 | ||
|  | 2dbb4a2d77 | ||
|  | f4f56e0711 | ||
|  | 8b4ceae602 | ||
|  | 6619e7c891 | ||
|  | 0aac38c05e | ||
|  | cb67901e41 | ||
|  | 4b418942da | ||
|  | 5e03bb33e3 | ||
|  | 22a75e9bc7 | ||
|  | 8ef3702f89 | ||
|  | a89c916e1a | ||
|  | 6eeab1103a | ||
|  | 89c10a67fd | ||
|  | aa2b503732 | ||
|  | f01fdbad00 | ||
|  | 0fc5da8693 | ||
|  | 891a15c1f9 | ||
|  | ff7e05a747 | ||
|  | 9e741fe04e | ||
|  | e8307d79f5 | ||
|  | ac694850da | ||
|  | 2aaf3661e5 | ||
|  | b661a7da47 | ||
|  | c38ca8d8c4 | ||
|  | 80a8c21cb0 | ||
|  | 944ff3529b | ||
|  | fe4ae26428 | ||
|  | bee13854f5 | ||
|  | c02cfa4344 | ||
|  | a4376e5513 | ||
|  | 731ef956d2 | ||
|  | 714abc2a96 | ||
|  | 542fc9b01f | ||
|  | 0e436cb1fc | ||
|  | 324e887d68 | ||
|  | c6c4c072a8 | ||
|  | 02f289d4d0 | ||
|  | fb89deabdf | ||
|  | 5ef6f76355 | ||
|  | 40edcff528 | ||
|  | 370fa6f15f | ||
|  | 1b2a2f5ff4 | ||
|  | 0c7490dea4 | ||
|  | 5ffc1cce50 | ||
|  | bebfaf0921 | ||
|  | 21db9c6d23 | ||
|  | 5cb12372e1 | ||
|  | 1c45957cc3 | ||
|  | 86e233e93b | ||
|  | 2575e0f159 | ||
|  | 4442b94211 | ||
|  | 9a88c4a943 | ||
|  | 6bf9570916 | ||
|  | 1c7aca98c1 | ||
|  | 4982e168ff | ||
|  | 0b9e42ac6c | ||
|  | 05d44d274e | ||
|  | 25c06cd390 | ||
|  | 0e7a939172 | ||
|  | 0b8d699d68 | ||
|  | 34f9fb53bc | ||
|  | ae782b1c32 | ||
|  | bc78898f8e | ||
|  | 8ae862e292 | 
							
								
								
									
										4
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -23,7 +23,7 @@ jobs: | |||||||
|       contents: write |       contents: write | ||||||
|  |  | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4 |       - uses: actions/checkout@v5 | ||||||
|  |  | ||||||
|       - name: set up secrets |       - name: set up secrets | ||||||
|         run: | |         run: | | ||||||
| @@ -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.0.21' |     id 'org.jetbrains.kotlin.plugin.serialization' version '2.2.20' | ||||||
|     id 'com.mikepenz.aboutlibraries.plugin' version '11.2.3' |     id 'org.jetbrains.kotlin.plugin.compose' version '2.2.20' | ||||||
|  |     id 'com.mikepenz.aboutlibraries.plugin' version '12.2.4' | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| 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.0d" | ||||||
|     } |     } | ||||||
|     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,47 @@ 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 { | ||||||
|     coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.1.3' |     implementation 'androidx.compose.material3:material3:1.3.2' | ||||||
|  |     implementation 'androidx.navigation:navigation-compose:2.9.4' | ||||||
|  |     coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.1.5' | ||||||
|     implementation 'androidx.preference:preference-ktx:1.2.1' |     implementation 'androidx.preference:preference-ktx:1.2.1' | ||||||
|     implementation 'com.google.android.material:material:1.12.0' |     implementation 'androidx.compose.ui:ui' | ||||||
|     implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3' |     implementation "androidx.compose.material:material:1.9.1" | ||||||
|  |     implementation "androidx.activity:activity-ktx:1.11.0" | ||||||
|  |  | ||||||
|  |     implementation 'androidx.compose.ui:ui-tooling-preview' | ||||||
|  |     implementation 'com.google.android.material:material:1.13.0' | ||||||
|  |     implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0' | ||||||
|  |  | ||||||
|     implementation 'com.caverock:androidsvg-aar:1.4' |     implementation 'com.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:11.2.3' |  | ||||||
|  |     implementation 'com.mikepenz:aboutlibraries:12.2.4' | ||||||
|  |     implementation 'com.mikepenz:aboutlibraries-compose-m3:12.2.4' | ||||||
|  |     implementation 'com.mikepenz:aboutlibraries-core:12.2.4' | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     implementation platform('androidx.compose:compose-bom:2025.09.00') | ||||||
|  |     debugImplementation 'androidx.compose.ui:ui-tooling:1.9.1' | ||||||
| } | } | ||||||
| @@ -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) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } |  | ||||||
							
								
								
									
										64
									
								
								app/src/main/java/net/helcel/beans/activity/EditScreen.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								app/src/main/java/net/helcel/beans/activity/EditScreen.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | |||||||
|  | package net.helcel.beans.activity | ||||||
|  |  | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.layout.Box | ||||||
|  | import androidx.compose.foundation.layout.Column | ||||||
|  | import androidx.compose.foundation.layout.Spacer | ||||||
|  | import androidx.compose.foundation.layout.fillMaxSize | ||||||
|  | import androidx.compose.foundation.layout.fillMaxWidth | ||||||
|  | import androidx.compose.foundation.layout.height | ||||||
|  | import androidx.compose.foundation.layout.padding | ||||||
|  | import androidx.compose.material.Icon | ||||||
|  | import androidx.compose.material.IconButton | ||||||
|  | import androidx.compose.material.MaterialTheme | ||||||
|  | import androidx.compose.material.Scaffold | ||||||
|  | import androidx.compose.material.Text | ||||||
|  | import androidx.compose.material.TopAppBar | ||||||
|  | import androidx.compose.material.icons.Icons | ||||||
|  | import androidx.compose.material.icons.automirrored.filled.ArrowBack | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
|  | import net.helcel.beans.R | ||||||
|  | import net.helcel.beans.activity.sub.EditPlaceScreen | ||||||
|  | import net.helcel.beans.countries.World | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun EditScreen(onExit:()->Unit) { | ||||||
|  |     SysTheme { | ||||||
|  |         Scaffold( | ||||||
|  |             topBar = { | ||||||
|  |                 TopAppBar( | ||||||
|  |                     title = { Text(stringResource(R.string.action_edit)) }, | ||||||
|  |                     navigationIcon = { | ||||||
|  |                         IconButton(onClick = onExit) { | ||||||
|  |                             Icon( | ||||||
|  |                                 Icons.AutoMirrored.Filled.ArrowBack, | ||||||
|  |                                 contentDescription = null | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 ) | ||||||
|  |             }, | ||||||
|  |  | ||||||
|  |             ) { innerPadding -> | ||||||
|  |             Box( | ||||||
|  |                 modifier = Modifier | ||||||
|  |                     .fillMaxSize() | ||||||
|  |                     .background(MaterialTheme.colors.background) | ||||||
|  |                     .padding(innerPadding) // ensures content is below the app bar | ||||||
|  |             ) { | ||||||
|  |                 Column { | ||||||
|  |                     Spacer( | ||||||
|  |                         modifier = Modifier.height(2.dp).fillMaxWidth() | ||||||
|  |                             .background(MaterialTheme.colors.background) | ||||||
|  |                     ) | ||||||
|  |                     EditPlaceScreen(World.WWW, onExit) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -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) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
							
								
								
									
										123
									
								
								app/src/main/java/net/helcel/beans/activity/MainScreen.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								app/src/main/java/net/helcel/beans/activity/MainScreen.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | |||||||
|  | package net.helcel.beans.activity | ||||||
|  |  | ||||||
|  | import android.graphics.drawable.PictureDrawable | ||||||
|  | import android.os.Bundle | ||||||
|  | import android.widget.ImageView | ||||||
|  | import androidx.activity.ComponentActivity | ||||||
|  | import androidx.activity.compose.setContent | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.layout.Box | ||||||
|  | import androidx.compose.foundation.layout.fillMaxSize | ||||||
|  | import androidx.compose.foundation.layout.padding | ||||||
|  | import androidx.compose.material.Icon | ||||||
|  | import androidx.compose.material.IconButton | ||||||
|  | import androidx.compose.material.MaterialTheme | ||||||
|  | import androidx.compose.material.Scaffold | ||||||
|  | import androidx.compose.material.Text | ||||||
|  | import androidx.compose.material.TopAppBar | ||||||
|  | import androidx.compose.material.icons.Icons | ||||||
|  | import androidx.compose.material.icons.filled.DateRange | ||||||
|  | import androidx.compose.material.icons.filled.Edit | ||||||
|  | import androidx.compose.material.icons.filled.Settings | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.runtime.remember | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.viewinterop.AndroidView | ||||||
|  | import androidx.navigation.NavHostController | ||||||
|  | import androidx.navigation.compose.NavHost | ||||||
|  | import androidx.navigation.compose.composable | ||||||
|  | import androidx.navigation.compose.rememberNavController | ||||||
|  | import com.caverock.androidsvg.RenderOptions | ||||||
|  | import com.github.chrisbanes.photoview.PhotoView | ||||||
|  | import net.helcel.beans.BuildConfig | ||||||
|  | import net.helcel.beans.countries.GeoLocImporter | ||||||
|  | import net.helcel.beans.helper.Data | ||||||
|  | import net.helcel.beans.helper.Settings | ||||||
|  | import net.helcel.beans.svg.CSSWrapper | ||||||
|  | import net.helcel.beans.svg.SVGWrapper | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class MainScreen : ComponentActivity() { | ||||||
|  |  | ||||||
|  |     private lateinit var psvg: SVGWrapper | ||||||
|  |     private lateinit var css: CSSWrapper | ||||||
|  |  | ||||||
|  |     override fun onCreate(savedInstanceState: Bundle?) { | ||||||
|  |         super.onCreate(savedInstanceState) | ||||||
|  |         actionBar?.hide() | ||||||
|  |         Settings.start(this) | ||||||
|  |         GeoLocImporter.importStates(this) | ||||||
|  |         Data.loadData(this, Int.MIN_VALUE) | ||||||
|  |  | ||||||
|  |         setContent { | ||||||
|  |             SysTheme { | ||||||
|  |                 Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.background)) { | ||||||
|  |                     AppNavHost(psvg, css) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         refreshProjection() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Composable | ||||||
|  |     fun AppNavHost(psvg: SVGWrapper, css: CSSWrapper) { | ||||||
|  |         val navController = rememberNavController() | ||||||
|  |         NavHost(navController, startDestination = "main") { | ||||||
|  |             composable("main") { MainScreenC(psvg,css, navController) } | ||||||
|  |             composable("settings") { SettingsMainScreen { navController.navigate("main")} } | ||||||
|  |             composable("edit") { EditScreen { navController.navigate("main") } } | ||||||
|  |             composable("stats") { StatsScreen { navController.navigate("main") } } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Composable | ||||||
|  |     fun MainScreenC(psvg: SVGWrapper,css: CSSWrapper, nav: NavHostController){ | ||||||
|  |         SysTheme { | ||||||
|  |             Scaffold( | ||||||
|  |                 topBar = { | ||||||
|  |                     TopAppBar( | ||||||
|  |                         title = { Text(BuildConfig.APP_NAME) }, | ||||||
|  |                         actions = { | ||||||
|  |                             IconButton(onClick = { nav.navigate("edit") }) { | ||||||
|  |                                 Icon(Icons.Default.Edit, contentDescription = "Edit") | ||||||
|  |                             } | ||||||
|  |                             IconButton(onClick = {  nav.navigate("stats") }){ | ||||||
|  |                                 Icon(Icons.Default.DateRange, contentDescription = "Stats") | ||||||
|  |                             } | ||||||
|  |                             IconButton(onClick = { nav.navigate("settings") }) { | ||||||
|  |                                 Icon(Icons.Default.Settings, contentDescription = "Settings") | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |             ) { innerPadding -> | ||||||
|  |                 Box(modifier = Modifier.padding(innerPadding)) { | ||||||
|  |                     MapScreen(psvg, css) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Composable | ||||||
|  |     fun MapScreen(psvg: SVGWrapper, css: CSSWrapper) { | ||||||
|  |         Box { | ||||||
|  |             val opt: RenderOptions = RenderOptions.create() | ||||||
|  |             opt.css(css.get()) | ||||||
|  |             val drawable = remember(psvg, css) { | ||||||
|  |                 PictureDrawable(psvg.get()?.renderToPicture(opt)) | ||||||
|  |             } | ||||||
|  |             AndroidView(factory = { ctx -> | ||||||
|  |                 PhotoView(ctx).apply { | ||||||
|  |                     setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null) | ||||||
|  |                     setImageDrawable(drawable) | ||||||
|  |                     scaleType = ImageView.ScaleType.FIT_CENTER | ||||||
|  |                 } | ||||||
|  |             }, modifier = Modifier.fillMaxSize()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun refreshProjection() { | ||||||
|  |         psvg = SVGWrapper(this) | ||||||
|  |         css = CSSWrapper(this) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -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 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun SettingsMainScreen(onExit: ()->Unit = {}) { | ||||||
|  |     var nav: NavHostController? = null | ||||||
|  |     SysTheme { | ||||||
|  |         Scaffold( | ||||||
|  |             topBar = { | ||||||
|  |                 TopAppBar( | ||||||
|  |                     title = { Text(stringResource(R.string.action_settings)) }, | ||||||
|  |                     navigationIcon = { | ||||||
|  |                         IconButton(onClick = { | ||||||
|  |                             if(nav!=null && !nav!!.popBackStack()) | ||||||
|  |                                 onExit() | ||||||
|  |                         }) { | ||||||
|  |                             Icon( | ||||||
|  |                                 Icons.AutoMirrored.Filled.ArrowBack, | ||||||
|  |                                 contentDescription = null | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         ) { innerPadding -> | ||||||
|  |             Box(modifier = Modifier.padding(innerPadding)) { | ||||||
|  |                 nav = settingsNav() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun SettingsScreen(navController: NavHostController = settingsNav()) { | ||||||
|  |     val context = LocalContext.current | ||||||
|  |     val prefs = PreferenceManager.getDefaultSharedPreferences(context) | ||||||
|  |     var showEdit by remember { mutableStateOf(false) } | ||||||
|  |  | ||||||
|  |     var theme by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_theme), context.getString(R.string.system))!!) } | ||||||
|  |     var projection by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_projection), "default")!!) } | ||||||
|  |     var groups by remember { mutableStateOf(prefs.getString(context.getString(R.string.key_group), context.getString(R.string.off))!!) } | ||||||
|  |  | ||||||
|  |         if(showEdit) | ||||||
|  |             EditPlaceDialog(true) { | ||||||
|  |                 showEdit = false | ||||||
|  |                 val g = Data.selected_group | ||||||
|  |                 if (it && g != null) | ||||||
|  |                     Data.visits.reassignAllVisitedToGroup(g.key) | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         LazyColumn( | ||||||
|  |             modifier = Modifier | ||||||
|  |                 .fillMaxSize() | ||||||
|  |                 .padding(16.dp) | ||||||
|  |                 .background(MaterialTheme.colors.background) | ||||||
|  |         ) { | ||||||
|  |             item { | ||||||
|  |                 Text( | ||||||
|  |                     "Theme", style = MaterialTheme.typography.h6, | ||||||
|  |                     color = MaterialTheme.colors.onBackground, | ||||||
|  |                 ) | ||||||
|  |                 MultiPreference(arrayOf(stringResource(R.string.system),stringResource(R.string.light),stringResource(R.string.dark)), theme) { newTheme -> | ||||||
|  |                     theme = newTheme | ||||||
|  |                     prefs.edit { putString(context.getString(R.string.key_theme), newTheme) } | ||||||
|  |                 } | ||||||
|  |                 HorizontalDivider() | ||||||
|  |             } | ||||||
|  |             item { | ||||||
|  |                 Text( | ||||||
|  |                     "Map Projection", | ||||||
|  |                     style = MaterialTheme.typography.h6, | ||||||
|  |                     color = MaterialTheme.colors.onBackground, | ||||||
|  |                     modifier = Modifier.padding(top = 16.dp) | ||||||
|  |                 ) | ||||||
|  |                 MultiPreference(arrayOf(stringResource(R.string.mercator), stringResource(R.string.azimuthalequidistant)), projection) { newProj -> | ||||||
|  |                     projection = newProj | ||||||
|  |                     prefs.edit { putString(context.getString(R.string.key_projection), newProj) } | ||||||
|  |                     Settings.refreshProjection() | ||||||
|  |                 } | ||||||
|  |                 HorizontalDivider() | ||||||
|  |             } | ||||||
|  |             item { | ||||||
|  |                 Text( | ||||||
|  |                     "Groups", | ||||||
|  |                     style = MaterialTheme.typography.h6, | ||||||
|  |                     color = MaterialTheme.colors.onBackground, | ||||||
|  |                     modifier = Modifier.padding(top = 16.dp) | ||||||
|  |                 ) | ||||||
|  |                 var showDialog by remember{mutableStateOf(false)} | ||||||
|  |                 if(showDialog){ | ||||||
|  |                     EditPlaceColorDialog( | ||||||
|  |                         deleteMode = true, | ||||||
|  |                         onDismiss = { | ||||||
|  |                             val g = Data.selected_group | ||||||
|  |                             if (g != null) | ||||||
|  |                                 Data.visits.reassignAllVisitedToGroup(g.key) | ||||||
|  |                             showDialog = false | ||||||
|  |                         }) | ||||||
|  |                 } | ||||||
|  |                 MultiPreference( | ||||||
|  |                     arrayOf(stringResource(R.string.on), stringResource(R.string.off)), | ||||||
|  |                     groups | ||||||
|  |                 ) { key -> | ||||||
|  |                     if (key == context.getString(R.string.off)) { | ||||||
|  |                         showDialog=true | ||||||
|  |                     } | ||||||
|  |                     groups = key | ||||||
|  |                     prefs.edit { putString(context.getString(R.string.key_group), key) } | ||||||
|  |                 } | ||||||
|  |                 HorizontalDivider() | ||||||
|  |             } | ||||||
|  |             item { | ||||||
|  |                 Text( | ||||||
|  |                     text = "Regional", | ||||||
|  |                     style = MaterialTheme.typography.h6, | ||||||
|  |                     color = MaterialTheme.colors.onBackground, | ||||||
|  |                     modifier = Modifier | ||||||
|  |                         .padding(top = 16.dp) | ||||||
|  |                         .clickable(onClick = {}), | ||||||
|  |                 ) | ||||||
|  |                 RegionalScreen() | ||||||
|  |                 HorizontalDivider() | ||||||
|  |             } | ||||||
|  |             item{ | ||||||
|  |                 val launcher = rememberLauncherForActivityResult( | ||||||
|  |                 contract = ActivityResultContracts.OpenDocument(), | ||||||
|  |                 onResult = { uri -> Data.doImport(context, uri)   } | ||||||
|  |                 ) | ||||||
|  |                 Row( | ||||||
|  |                     modifier = Modifier.fillMaxWidth() | ||||||
|  |                 ) { | ||||||
|  |                     Button(onClick = { | ||||||
|  |                         launcher.launch(arrayOf("*/*")) | ||||||
|  |                     }, modifier = Modifier | ||||||
|  |                         .fillMaxWidth(fraction = 0.4f) | ||||||
|  |                         .padding(vertical = 8.dp)) { | ||||||
|  |                         Text("Import") | ||||||
|  |                     } | ||||||
|  |                     Spacer( | ||||||
|  |                         modifier = Modifier.fillMaxWidth(0.4f) | ||||||
|  |                     ) | ||||||
|  |                     Button(onClick = { | ||||||
|  |                         Data.doExport(context) | ||||||
|  |                     }, modifier = Modifier | ||||||
|  |                         .fillMaxWidth(fraction = 1f) | ||||||
|  |                         .padding(vertical = 8.dp)) { | ||||||
|  |                         Text("Export") | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 HorizontalDivider() | ||||||
|  |             } | ||||||
|  |             item { | ||||||
|  |                 PreferenceButton("Licenses") { | ||||||
|  |                     if (navController.currentDestination?.route != "licenses") | ||||||
|  |                         navController.navigate("licenses") | ||||||
|  |                 } | ||||||
|  |                 PreferenceButton("About") { | ||||||
|  |                     if (navController.currentDestination?.route != "about") | ||||||
|  |                         navController.navigate("about") | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun RegionalScreen() { | ||||||
|  |     val context = LocalContext.current | ||||||
|  |     val prefs = PreferenceManager.getDefaultSharedPreferences(context) | ||||||
|  |     var selected by remember {  mutableStateOf(prefs.getString(context.getString(R.string.key_regional),context.getString(R.string.off))!!)} | ||||||
|  |     var regional by remember{ mutableStateOf(prefs.getString(context.getString(R.string.key_regional), context.getString(R.string.off))!!)} | ||||||
|  |     var showDialog by remember{mutableStateOf(false)} | ||||||
|  |     var showLoad by remember{mutableStateOf(false)} | ||||||
|  |  | ||||||
|  |     if(showDialog) | ||||||
|  |         Dialog( | ||||||
|  |             content = { | ||||||
|  |                 Column( | ||||||
|  |                     modifier = Modifier | ||||||
|  |                         .background( | ||||||
|  |                             MaterialTheme.colors.background, | ||||||
|  |                             RoundedCornerShape(corner = CornerSize(16.dp)) | ||||||
|  |                         ) | ||||||
|  |                         .padding(16.dp),){ | ||||||
|  |                 Text(style=MaterialTheme.typography.caption, text=  stringResource(R.string.delete_regions)) | ||||||
|  |                     Button(onClick = { | ||||||
|  |                         GeoLocImporter.clearStates() | ||||||
|  |                         regional= selected | ||||||
|  |                         prefs.edit { | ||||||
|  |                             putString( | ||||||
|  |                                 context.getString(R.string.key_regional), | ||||||
|  |                                 regional | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                         showDialog=false | ||||||
|  |                     }){ | ||||||
|  |                        Text(stringResource(R.string.ok)) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             onDismissRequest = { showDialog=false } | ||||||
|  |         ) | ||||||
|  |     if(showLoad){ | ||||||
|  |         Dialog( | ||||||
|  |             content = { | ||||||
|  |                 CircularProgressIndicator( | ||||||
|  |                     color = MaterialTheme.colors.primary, | ||||||
|  |                     strokeWidth = 4.dp, | ||||||
|  |                     modifier = Modifier.size(50.dp) | ||||||
|  |                 ) | ||||||
|  |             }, | ||||||
|  |             onDismissRequest = {} | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |     val scope = rememberCoroutineScope() | ||||||
|  |     MultiPreference(arrayOf(stringResource(R.string.on),stringResource(R.string.off)),regional) { key -> | ||||||
|  |                 when (key) { | ||||||
|  |                     context.getString(R.string.off) -> { showDialog=true | ||||||
|  |                         selected=key | ||||||
|  |                     } | ||||||
|  |                     context.getString(R.string.on) -> { | ||||||
|  |                         regional = key | ||||||
|  |                         prefs.edit { putString(context.getString(R.string.key_regional), key) } | ||||||
|  |                         showLoad=true | ||||||
|  |                         scope.launch { | ||||||
|  |                             withContext(Dispatchers.IO) { | ||||||
|  |                                 GeoLocImporter.importStates(context, true) | ||||||
|  |                             } | ||||||
|  |                             showLoad = false | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun MultiPreference(list: Array<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> |  | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| // Top-level build file where you can add configuration options common to all sub-projects/modules. | // Top-level build file where you can add configuration options common to all sub-projects/modules. | ||||||
| plugins { | plugins { | ||||||
|     id 'com.android.application' version '8.7.2' apply false |     id 'com.android.application' version '8.13.0' apply false | ||||||
|     id 'com.android.library' version '8.7.2' apply false |     id 'com.android.library' version '8.13.0' apply false | ||||||
|     id 'org.jetbrains.kotlin.android' version '2.0.21' apply false |     id 'org.jetbrains.kotlin.android' version '2.2.20' 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=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26 | distributionSha256Sum=a17ddd85a26b6a7f5ddb71ff8b05fc5104c0202c6e64782429790c933686c806 | ||||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip | distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip | ||||||
| networkTimeout=10000 | networkTimeout=10000 | ||||||
| validateDistributionUrl=true | validateDistributionUrl=true | ||||||
| zipStoreBase=GRADLE_USER_HOME | zipStoreBase=GRADLE_USER_HOME | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
| #!/bin/sh | #!/bin/sh | ||||||
|  |  | ||||||
| # | # | ||||||
| # Copyright © 2015-2021 the original authors. | # Copyright © 2015 the original authors. | ||||||
| # | # | ||||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| # you may not use this file except in compliance with the License. | # you may not use this file except in compliance with the License. | ||||||
| @@ -86,8 +86,7 @@ done | |||||||
| # shellcheck disable=SC2034 | # shellcheck disable=SC2034 | ||||||
| APP_BASE_NAME=${0##*/} | APP_BASE_NAME=${0##*/} | ||||||
| # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) | ||||||
| APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit | ||||||
| ' "$PWD" ) || exit |  | ||||||
|  |  | ||||||
| # Use the maximum available, or set MAX_FD != -1 to use that value. | # Use the maximum available, or set MAX_FD != -1 to use that value. | ||||||
| MAX_FD=maximum | MAX_FD=maximum | ||||||
| @@ -115,7 +114,6 @@ case "$( uname )" in                #( | |||||||
|   NONSTOP* )        nonstop=true ;; |   NONSTOP* )        nonstop=true ;; | ||||||
| esac | esac | ||||||
|  |  | ||||||
| CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # Determine the Java command to use to start the JVM. | # Determine the Java command to use to start the JVM. | ||||||
| @@ -173,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" ) | ||||||
|  |  | ||||||
| @@ -206,15 +203,14 @@ fi | |||||||
| DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | ||||||
|  |  | ||||||
| # Collect all arguments for the java command: | # Collect all arguments for the java command: | ||||||
| #   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, | #   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, | ||||||
| #     and any embedded shellness will be escaped. | #     and any embedded shellness will be escaped. | ||||||
| #   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be | #   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be | ||||||
| #     treated as '${Hostname}' itself on the command line. | #     treated as '${Hostname}' itself on the command line. | ||||||
|  |  | ||||||
| set -- \ | set -- \ | ||||||
|         "-Dorg.gradle.appname=$APP_BASE_NAME" \ |         "-Dorg.gradle.appname=$APP_BASE_NAME" \ | ||||||
|         -classpath "$CLASSPATH" \ |         -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ | ||||||
|         org.gradle.wrapper.GradleWrapperMain \ |  | ||||||
|         "$@" |         "$@" | ||||||
|  |  | ||||||
| # Stop when "xargs" is not available. | # Stop when "xargs" is not available. | ||||||
|   | |||||||
							
								
								
									
										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=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @rem Execute Gradle | @rem Execute Gradle | ||||||
| "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* | ||||||
|  |  | ||||||
| :end | :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": "^25.0.0", |     "jsdom": "^27.0.0", | ||||||
|     "mapshaper": "^0.6.79" |     "mapshaper": "^0.6.79" | ||||||
|   }, |   }, | ||||||
|   "type": "module" |   "type": "module" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user