File upload
8
.gitignore
vendored
@ -4,6 +4,9 @@
|
|||||||
*.ap_
|
*.ap_
|
||||||
*.aab
|
*.aab
|
||||||
|
|
||||||
|
# Fonts
|
||||||
|
*.ttf
|
||||||
|
|
||||||
# Files for the ART/Dalvik VM
|
# Files for the ART/Dalvik VM
|
||||||
*.dex
|
*.dex
|
||||||
|
|
||||||
@ -20,6 +23,7 @@ out/
|
|||||||
# Gradle files
|
# Gradle files
|
||||||
.gradle/
|
.gradle/
|
||||||
build/
|
build/
|
||||||
|
release/
|
||||||
|
|
||||||
# Local configuration file (sdk path, etc)
|
# Local configuration file (sdk path, etc)
|
||||||
local.properties
|
local.properties
|
||||||
@ -60,7 +64,7 @@ captures/
|
|||||||
.cxx/
|
.cxx/
|
||||||
|
|
||||||
# Google Services (e.g. APIs or Firebase)
|
# Google Services (e.g. APIs or Firebase)
|
||||||
# google-services.json
|
google-services.json
|
||||||
|
|
||||||
# Freeline
|
# Freeline
|
||||||
freeline.py
|
freeline.py
|
||||||
@ -83,3 +87,5 @@ lint/generated/
|
|||||||
lint/outputs/
|
lint/outputs/
|
||||||
lint/tmp/
|
lint/tmp/
|
||||||
# lint/reports/
|
# lint/reports/
|
||||||
|
/.idea/
|
||||||
|
/app/google-services.json
|
||||||
|
17
README.md
@ -1,2 +1,19 @@
|
|||||||
# unitto
|
# unitto
|
||||||
Unit converter for Android
|
Unit converter for Android
|
||||||
|
|
||||||
|
This is Unitto, a free unit converter mobile app for Android devices. It uses Material Design 3 guidelines and supports all the latest Android features.
|
||||||
|
|
||||||
|
Key features:
|
||||||
|
- More than 150 units
|
||||||
|
- SI Standard
|
||||||
|
- No ads, obviously
|
||||||
|
- Supports Dynamic Theming
|
||||||
|
- Highly customizable
|
||||||
|
- Just looks good, I guess…
|
||||||
|
|
||||||
|
The app is in active development and has its own Trello board, where you can check out all the new stuff that will be added (including Currency conversion). You can also leave your feature requests.
|
||||||
|
|
||||||
|
Trello board link: https://trello.com/b/cxAbRlvu/unitto
|
||||||
|
|
||||||
|
Also, this app has a cool open source page, where you can find Terms and Conditions, Privacy Policy, Press Kit and contact links:
|
||||||
|
https://sadellie.github.io/unitto/
|
130
app/build.gradle
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
plugins {
|
||||||
|
// Basic stuff
|
||||||
|
id 'com.android.application'
|
||||||
|
id 'kotlin-android'
|
||||||
|
|
||||||
|
// Hilt
|
||||||
|
id 'kotlin-kapt'
|
||||||
|
id 'dagger.hilt.android.plugin'
|
||||||
|
|
||||||
|
// Google Services
|
||||||
|
id 'com.google.gms.google-services'
|
||||||
|
|
||||||
|
// Firebase Crashlytics
|
||||||
|
id 'com.google.firebase.crashlytics'
|
||||||
|
}
|
||||||
|
|
||||||
|
kapt {
|
||||||
|
correctErrorTypes = true
|
||||||
|
useBuildCache = true
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 32
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "com.sadellie.unitto"
|
||||||
|
minSdkVersion 21
|
||||||
|
targetSdkVersion 32
|
||||||
|
versionCode 3
|
||||||
|
versionName "Aero blue"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
debug {
|
||||||
|
// We don't need analytics for debug builds
|
||||||
|
resValue("bool", "FIREBASE_ANALYTICS_DEACTIVATED", "true")
|
||||||
|
resValue("bool", "FIREBASE_CRASHLYTICS_ENABLED", "false")
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseNoAnal {
|
||||||
|
// Same as release
|
||||||
|
// Used for on device testing. Same performance, but no analytics
|
||||||
|
resValue("bool", "FIREBASE_ANALYTICS_DEACTIVATED", "true")
|
||||||
|
resValue("bool", "FIREBASE_CRASHLYTICS_ENABLED", "false")
|
||||||
|
shrinkResources true
|
||||||
|
minifyEnabled true
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
|
||||||
|
release {
|
||||||
|
resValue("bool", "FIREBASE_ANALYTICS_DEACTIVATED", "false")
|
||||||
|
resValue("bool", "FIREBASE_CRASHLYTICS_ENABLED", "true")
|
||||||
|
shrinkResources true
|
||||||
|
minifyEnabled true
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
compose true
|
||||||
|
|
||||||
|
// These are unused features
|
||||||
|
buildConfig false
|
||||||
|
aidl false
|
||||||
|
renderScript false
|
||||||
|
shaders false
|
||||||
|
}
|
||||||
|
composeOptions {
|
||||||
|
kotlinCompilerExtensionVersion compose_version
|
||||||
|
}
|
||||||
|
packagingOptions {
|
||||||
|
jniLibs {
|
||||||
|
excludes += ['META-INF/licenses/**']
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
excludes += ['META-INF/licenses/**', 'META-INF/AL2.0', 'META-INF/LGPL2.1']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'androidx.core:core-ktx:1.7.0'
|
||||||
|
|
||||||
|
// Compose and navigation
|
||||||
|
implementation "androidx.compose.ui:ui:$compose_version"
|
||||||
|
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
|
||||||
|
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
|
||||||
|
implementation "androidx.navigation:navigation-compose:2.5.0-alpha03"
|
||||||
|
|
||||||
|
// Material Design 3
|
||||||
|
implementation "androidx.compose.material3:material3:1.0.0-alpha09"
|
||||||
|
|
||||||
|
// Hilt and navigation
|
||||||
|
implementation 'androidx.hilt:hilt-navigation-compose:1.0.0'
|
||||||
|
kapt 'com.google.dagger:dagger-android-processor:2.41'
|
||||||
|
implementation 'com.google.dagger:hilt-android:2.41'
|
||||||
|
kapt 'com.google.dagger:hilt-compiler:2.41'
|
||||||
|
|
||||||
|
// There are a lot of icons
|
||||||
|
implementation "androidx.compose.material:material-icons-extended:$compose_version"
|
||||||
|
|
||||||
|
// DataStore
|
||||||
|
implementation "androidx.datastore:datastore-preferences:1.0.0"
|
||||||
|
|
||||||
|
// This is for system status bar color
|
||||||
|
implementation "com.google.accompanist:accompanist-systemuicontroller:0.17.0"
|
||||||
|
|
||||||
|
// Firebase
|
||||||
|
implementation platform('com.google.firebase:firebase-bom:29.0.4')
|
||||||
|
implementation 'com.google.firebase:firebase-analytics-ktx'
|
||||||
|
|
||||||
|
// Crashlytics and Analytics
|
||||||
|
implementation 'com.google.firebase:firebase-crashlytics-ktx'
|
||||||
|
implementation 'com.google.firebase:firebase-analytics-ktx'
|
||||||
|
|
||||||
|
// Room
|
||||||
|
implementation "androidx.room:room-runtime:2.4.2"
|
||||||
|
implementation "androidx.room:room-ktx:2.4.2"
|
||||||
|
kapt "androidx.room:room-compiler:2.4.2"
|
||||||
|
|
||||||
|
// Moshi
|
||||||
|
implementation 'com.squareup.moshi:moshi-kotlin:1.13.0'
|
||||||
|
|
||||||
|
// Retrofit with Moshi Converter
|
||||||
|
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
|
||||||
|
}
|
29
app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
|
# Repackage classes into the top-level.
|
||||||
|
-repackageclasses
|
||||||
|
|
||||||
|
-keepclassmembers class ** {
|
||||||
|
@com.squareup.moshi.FromJson *;
|
||||||
|
@com.squareup.moshi.ToJson *;
|
||||||
|
}
|
30
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.sadellie.unitto">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name=".UnittoApplication"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.Unitto">
|
||||||
|
<meta-data
|
||||||
|
android:name="firebase_analytics_collection_deactivated"
|
||||||
|
android:value="@bool/FIREBASE_ANALYTICS_DEACTIVATED" />
|
||||||
|
<meta-data
|
||||||
|
android:name="firebase_crashlytics_collection_enabled"
|
||||||
|
android:value="@bool/FIREBASE_CRASHLYTICS_ENABLED" />
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
BIN
app/src/main/ic_launcher-playstore.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
99
app/src/main/java/com/sadellie/unitto/MainActivity.kt
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package com.sadellie.unitto
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.NavType
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import androidx.navigation.navArgument
|
||||||
|
import com.sadellie.unitto.data.*
|
||||||
|
import com.sadellie.unitto.data.preferences.AppTheme
|
||||||
|
import com.sadellie.unitto.screens.MainViewModel
|
||||||
|
import com.sadellie.unitto.screens.about.AboutScreen
|
||||||
|
import com.sadellie.unitto.screens.main.MainScreen
|
||||||
|
import com.sadellie.unitto.screens.second.SecondScreen
|
||||||
|
import com.sadellie.unitto.screens.setttings.SettingsScreen
|
||||||
|
import com.sadellie.unitto.ui.theme.AppTheme
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class MainActivity : ComponentActivity() {
|
||||||
|
private val mainViewModel: MainViewModel by viewModels()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
setContent {
|
||||||
|
val navController = rememberNavController()
|
||||||
|
val currentAppTheme: Int by mainViewModel.currentAppTheme.collectAsState(AppTheme.NOT_SET)
|
||||||
|
|
||||||
|
// We don't draw anything until we know what theme we need to use
|
||||||
|
if (currentAppTheme != AppTheme.NOT_SET) {
|
||||||
|
AppTheme(
|
||||||
|
currentAppTheme = currentAppTheme
|
||||||
|
) {
|
||||||
|
UnittoApp(
|
||||||
|
navController = navController,
|
||||||
|
viewModel = mainViewModel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
mainViewModel.saveMe()
|
||||||
|
super.onPause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun UnittoApp(
|
||||||
|
navController: NavHostController,
|
||||||
|
viewModel: MainViewModel,
|
||||||
|
) {
|
||||||
|
Scaffold {
|
||||||
|
NavHost(navController = navController, startDestination = MAIN_SCREEN) {
|
||||||
|
|
||||||
|
composable(MAIN_SCREEN) {
|
||||||
|
MainScreen(
|
||||||
|
navControllerAction = { route -> navController.navigate(route) },
|
||||||
|
viewModel = viewModel
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
composable(
|
||||||
|
"$SECOND_SCREEN/{$LEFT_BUTTON}",
|
||||||
|
arguments = listOf(navArgument(LEFT_BUTTON) { type = NavType.BoolType })
|
||||||
|
) {
|
||||||
|
val leftButton = it.arguments?.getBoolean(LEFT_BUTTON) ?: true
|
||||||
|
SecondScreen(
|
||||||
|
viewModel = viewModel,
|
||||||
|
leftSide = leftButton,
|
||||||
|
navigateUp = { navController.navigateUp() },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
composable(SETTINGS_SCREEN) {
|
||||||
|
SettingsScreen(
|
||||||
|
mainViewModel = viewModel,
|
||||||
|
navigateUpAction = { navController.navigateUp() },
|
||||||
|
navControllerAction = { route -> navController.navigate(route) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
composable(ABOUT_SCREEN) {
|
||||||
|
AboutScreen(navigateUpAction = { navController.navigateUp() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.sadellie.unitto
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
|
|
||||||
|
@HiltAndroidApp
|
||||||
|
class UnittoApplication: Application()
|
20
app/src/main/java/com/sadellie/unitto/data/KeypadSymbols.kt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package com.sadellie.unitto.data
|
||||||
|
|
||||||
|
const val KEY_1 = "1"
|
||||||
|
const val KEY_2 = "2"
|
||||||
|
const val KEY_3 = "3"
|
||||||
|
const val KEY_4 = "4"
|
||||||
|
const val KEY_5 = "5"
|
||||||
|
const val KEY_6 = "6"
|
||||||
|
const val KEY_7 = "7"
|
||||||
|
const val KEY_8 = "8"
|
||||||
|
const val KEY_9 = "9"
|
||||||
|
const val KEY_0 = "0"
|
||||||
|
|
||||||
|
const val KEY_DOT = "."
|
||||||
|
const val KEY_COMMA = ","
|
||||||
|
const val KEY_AC = "AC"
|
||||||
|
const val KEY_CLEAR = "<"
|
||||||
|
const val KEY_NEGATE = "±"
|
||||||
|
const val KEY_MINUS = "-"
|
||||||
|
const val KEY_E = "E"
|
114
app/src/main/java/com/sadellie/unitto/data/Library.kt
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package com.sadellie.unitto.data
|
||||||
|
|
||||||
|
|
||||||
|
data class AppLibrary(
|
||||||
|
val name: String,
|
||||||
|
val dev: String?,
|
||||||
|
val website: String?,
|
||||||
|
val license: String?,
|
||||||
|
val description: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
val ALL_LIBRARIES = lazy {
|
||||||
|
listOf(
|
||||||
|
AppLibrary(
|
||||||
|
name = "Core Kotlin Extensions",
|
||||||
|
dev = "The Android Open Source Project",
|
||||||
|
website = "https://developer.android.com/jetpack/androidx/releases/core",
|
||||||
|
license = "Apache-2.0",
|
||||||
|
description = "Kotlin extensions for 'core' artifact"
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "Material Components for Android",
|
||||||
|
dev = "The Android Open Source Project",
|
||||||
|
website = "https://github.com/material-components/material-components-android",
|
||||||
|
license = "Apache-2.0",
|
||||||
|
description = "Material Components for Android is a static library that you can add to your Android application in order to use APIs that provide implementations of the Material Design specification. Compatible on devices running API 14 or later."
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "Compose UI primitives",
|
||||||
|
dev = "The Android Open Source Project",
|
||||||
|
website = "https://developer.android.com/jetpack/androidx/releases/compose-ui",
|
||||||
|
license = "Apache-2.0",
|
||||||
|
description = "Compose UI primitives. This library contains the primitives that form the Compose UI Toolkit, such as drawing, measurement and layout."
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "Compose Navigation",
|
||||||
|
dev = "The Android Open Source Project",
|
||||||
|
website = "https://developer.android.com/jetpack/androidx/releases/navigation",
|
||||||
|
license = "Apache-2.0",
|
||||||
|
description = "Compose integration with Navigation"
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "Compose Material3 Components",
|
||||||
|
dev = "The Android Open Source Project",
|
||||||
|
website = "https://developer.android.com/jetpack/androidx/releases/compose-material3",
|
||||||
|
license = "Apache-2.0",
|
||||||
|
description = "Compose Material You Design Components library"
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "Navigation Compose Hilt Integration",
|
||||||
|
dev = "The Android Open Source Project",
|
||||||
|
website = "https://developer.android.com/jetpack/androidx/releases/hilt",
|
||||||
|
license = "Apache-2.0",
|
||||||
|
description = "Navigation Compose Hilt Integration"
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "Hilt Android",
|
||||||
|
dev = "The Android Open Source Project",
|
||||||
|
website = "https://github.com/google/dagger",
|
||||||
|
license = "Apache-2.0",
|
||||||
|
description = "A fast dependency injector for Android and Java."
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "Compose Material Icons Extended",
|
||||||
|
dev = "The Android Open Source Project",
|
||||||
|
website = "https://developer.android.com/jetpack/androidx/releases/compose-material",
|
||||||
|
license = "Apache-2.0",
|
||||||
|
description = "Compose Material Design extended icons. This module contains all Material icons. It is a very large dependency and should not be included directly."
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "Android Preferences DataStore",
|
||||||
|
dev = "The Android Open Source Project",
|
||||||
|
website = "https://developer.android.com/jetpack/androidx/releases/datastore",
|
||||||
|
license = "Apache-2.0",
|
||||||
|
description = "Android Preferences DataStore"
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "Accompanist System UI Controller library",
|
||||||
|
dev = "Google",
|
||||||
|
website = "https://github.com/google/accompanist/",
|
||||||
|
license = "Apache-2.0",
|
||||||
|
description = "Utilities for Jetpack Compose"
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "firebase-analytics-ktx",
|
||||||
|
dev = "Google",
|
||||||
|
website = "https://developer.android.com/studio/terms.html",
|
||||||
|
license = "ASDKL",
|
||||||
|
description = "Library to collect and send usage statistics"
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "firebase-crashlytics-ktx",
|
||||||
|
dev = "Google",
|
||||||
|
website = "https://developer.android.com/studio/terms.html",
|
||||||
|
license = "Apache-2.0",
|
||||||
|
description = "Library to collect and send crash logs"
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "currency-api",
|
||||||
|
dev = "Fawaz Ahmed (fawazahmed0)",
|
||||||
|
website = "https://github.com/fawazahmed0/currency-api",
|
||||||
|
license = "The Unlicense",
|
||||||
|
description = "Free Currency Rates API"
|
||||||
|
),
|
||||||
|
AppLibrary(
|
||||||
|
name = "Compose Tooling API",
|
||||||
|
dev = "The Android Open Source Project",
|
||||||
|
website = "https://developer.android.com/jetpack/androidx/releases/compose-ui",
|
||||||
|
license = "Apache-2.0",
|
||||||
|
description = "Compose tooling library API. This library provides the API required to declare @Preview composables in user apps."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
9
app/src/main/java/com/sadellie/unitto/data/NavRoutes.kt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package com.sadellie.unitto.data
|
||||||
|
|
||||||
|
const val MAIN_SCREEN = "MainScreen"
|
||||||
|
|
||||||
|
const val SECOND_SCREEN = "SecondScreen"
|
||||||
|
const val LEFT_BUTTON = "LeftButton"
|
||||||
|
|
||||||
|
const val SETTINGS_SCREEN = "SettingsScreen"
|
||||||
|
const val ABOUT_SCREEN = "AboutScreen"
|
@ -0,0 +1,44 @@
|
|||||||
|
package com.sadellie.unitto.data.preferences
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All possible state of theme in the app
|
||||||
|
*/
|
||||||
|
object AppTheme {
|
||||||
|
// Used on app launch when we don't know which theme to use
|
||||||
|
const val NOT_SET = 0
|
||||||
|
|
||||||
|
const val AUTO = 1
|
||||||
|
const val LIGHT = 2
|
||||||
|
const val DARK = 3
|
||||||
|
const val LIGHT_DYNAMIC = 4
|
||||||
|
const val DARK_DYNAMIC = 5
|
||||||
|
const val AMOLED = 6
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device specific map of available themes. Used in settings
|
||||||
|
*/
|
||||||
|
val APP_THEMES: Map<Int, Int> by lazy {
|
||||||
|
// Dynamic themes are only for Android 8.1 and later
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||||
|
mapOf(
|
||||||
|
AppTheme.AUTO to R.string.force_auto_mode,
|
||||||
|
AppTheme.LIGHT to R.string.force_light_mode,
|
||||||
|
AppTheme.DARK to R.string.force_dark_mode,
|
||||||
|
AppTheme.AMOLED to R.string.force_amoled_mode,
|
||||||
|
AppTheme.LIGHT_DYNAMIC to R.string.force_light_dynamic_mode,
|
||||||
|
AppTheme.DARK_DYNAMIC to R.string.force_dark_dynamic_mode,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mapOf(
|
||||||
|
AppTheme.AUTO to R.string.force_auto_mode,
|
||||||
|
AppTheme.LIGHT to R.string.force_light_mode,
|
||||||
|
AppTheme.DARK to R.string.force_dark_mode,
|
||||||
|
AppTheme.AMOLED to R.string.force_amoled_mode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.sadellie.unitto.data.preferences
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output format here means whether or now use engineering notation
|
||||||
|
*/
|
||||||
|
object OutputFormat {
|
||||||
|
// Never use engineering notation
|
||||||
|
const val PLAIN = 0
|
||||||
|
// Use format that a lower API returns
|
||||||
|
const val ALLOW_ENGINEERING = 1
|
||||||
|
// App will try it's best to use engineering notation
|
||||||
|
const val FORCE_ENGINEERING = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Available formats. Used in settings
|
||||||
|
*/
|
||||||
|
val OUTPUT_FORMAT: Map<Int, Int> by lazy {
|
||||||
|
mapOf(
|
||||||
|
OutputFormat.PLAIN to R.string.plain,
|
||||||
|
OutputFormat.ALLOW_ENGINEERING to R.string.allow_engineering,
|
||||||
|
OutputFormat.FORCE_ENGINEERING to R.string.force_engineering,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.sadellie.unitto.data.preferences
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current maximum scale that will be used in app. Used in various place in code
|
||||||
|
*/
|
||||||
|
const val MAX_PRECISION: Int = 1_000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently available scale options
|
||||||
|
*/
|
||||||
|
val PRECISIONS: Map<Int, Int> by lazy {
|
||||||
|
mapOf(
|
||||||
|
0 to R.string.precision_zero,
|
||||||
|
1 to R.string.precision_one,
|
||||||
|
2 to R.string.precision_two,
|
||||||
|
3 to R.string.precision_three,
|
||||||
|
4 to R.string.precision_four,
|
||||||
|
5 to R.string.precision_five,
|
||||||
|
6 to R.string.precision_six,
|
||||||
|
7 to R.string.precision_seven,
|
||||||
|
8 to R.string.precision_eight,
|
||||||
|
9 to R.string.precision_nine,
|
||||||
|
10 to R.string.precision_ten,
|
||||||
|
11 to R.string.precision_eleven,
|
||||||
|
12 to R.string.precision_twelve,
|
||||||
|
13 to R.string.precision_thirteen,
|
||||||
|
14 to R.string.precision_fourteen,
|
||||||
|
15 to R.string.precision_fifteen,
|
||||||
|
MAX_PRECISION to R.string.max_precision
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.sadellie.unitto.data.preferences
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Separators mean symbols that separate fractional part
|
||||||
|
*/
|
||||||
|
object Separator {
|
||||||
|
const val SPACES = 0
|
||||||
|
const val PERIOD = 1
|
||||||
|
const val COMMA = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of separators that is used in settings
|
||||||
|
*/
|
||||||
|
val SEPARATORS: Map<Int, Int> by lazy {
|
||||||
|
mapOf(
|
||||||
|
Separator.PERIOD to R.string.period,
|
||||||
|
Separator.COMMA to R.string.comma,
|
||||||
|
Separator.SPACES to R.string.spaces
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
package com.sadellie.unitto.data.preferences
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.core.intPreferencesKey
|
||||||
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
|
// It's at the top level to make DataStore singleton
|
||||||
|
// DON'T TOUCH STRINGS!!!
|
||||||
|
private val Context.settingsDataStore by preferencesDataStore("settings")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys for DataStore
|
||||||
|
*/
|
||||||
|
object UserPreferenceKeys {
|
||||||
|
val CURRENT_APP_THEME = intPreferencesKey("CURRENT_APP_THEME")
|
||||||
|
val DIGITS_PRECISION = intPreferencesKey("DIGITS_PRECISION_PREF_KEY")
|
||||||
|
val SEPARATOR = intPreferencesKey("SEPARATOR_PREF_KEY")
|
||||||
|
val OUTPUT_FORMAT = intPreferencesKey("OUTPUT_FORMAT_PREF_KEY")
|
||||||
|
val LATEST_LEFT_SIDE = stringPreferencesKey("LATEST_LEFT_SIDE_PREF_KEY")
|
||||||
|
val LATEST_RIGHT_SIDE = stringPreferencesKey("LATEST_RIGHT_SIDE_PREF_KEY")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository that works with DataStore
|
||||||
|
*
|
||||||
|
* @property context
|
||||||
|
*/
|
||||||
|
class UserPreferences @Inject constructor(@ApplicationContext private val context: Context) {
|
||||||
|
/**
|
||||||
|
* Gets string from datastore
|
||||||
|
*
|
||||||
|
* @param[default] Value to return if didn't find anything on fresh install
|
||||||
|
*/
|
||||||
|
fun getItem(key: Preferences.Key<String>, default: String): Flow<String> {
|
||||||
|
return context.settingsDataStore.data.map {
|
||||||
|
it[key] ?: default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets int from datastore
|
||||||
|
*
|
||||||
|
* @param[default] Value to return if didn't find anything. Used on fresh install
|
||||||
|
*/
|
||||||
|
fun getItem(key: Preferences.Key<Int>, default: Int): Flow<Int> {
|
||||||
|
return context.settingsDataStore.data.map {
|
||||||
|
it[key] ?: default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves string value by key
|
||||||
|
*/
|
||||||
|
suspend fun saveString(key: Preferences.Key<String>, value: String) {
|
||||||
|
context.settingsDataStore.edit {
|
||||||
|
it[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves int value by key
|
||||||
|
*/
|
||||||
|
suspend fun saveInt(key: Preferences.Key<Int>, value: Int) {
|
||||||
|
context.settingsDataStore.edit {
|
||||||
|
it[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.sadellie.unitto.data.units
|
||||||
|
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a basic representation of what a unit must have (properties and convert function)
|
||||||
|
*
|
||||||
|
* @property unitId Unit ID from [MyUnitIDS]
|
||||||
|
* @property displayName String resource with long name, i.e. kilometer
|
||||||
|
* @property shortName String resource with short name, i.e. km
|
||||||
|
* @property basicUnit Used for conversion. Basically tells how big this unit is if comparing with
|
||||||
|
* basicUnit. For example, in [UnitGroup.LENGTH] basic unit is an attometer (1), then nanometer is
|
||||||
|
* 1.0E+9 times bigger than that. This number (1.0E+9) is a basic unit for nanometer
|
||||||
|
* @property group [UnitGroup] of this unit
|
||||||
|
* @property renderedName Used a cache. Stores long name string for this specific device. Need for
|
||||||
|
* search functionality
|
||||||
|
* @property isFavorite Whether this unit is favorite.
|
||||||
|
* @property isEnabled Whether we need to show this unit or not
|
||||||
|
* @property pairedUnit Latest paired unit on the right
|
||||||
|
* @property counter The amount of time this unit was chosen
|
||||||
|
*/
|
||||||
|
abstract class AbstractUnit(
|
||||||
|
val unitId: String,
|
||||||
|
val displayName: Int,
|
||||||
|
val shortName: Int,
|
||||||
|
var basicUnit: BigDecimal,
|
||||||
|
val group: UnitGroup,
|
||||||
|
var renderedName: String = String(),
|
||||||
|
var isFavorite: Boolean = false,
|
||||||
|
var isEnabled: Boolean = true,
|
||||||
|
var pairedUnit: String? = null,
|
||||||
|
var counter: Int = 0
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Convert this unit into another
|
||||||
|
*
|
||||||
|
* @param unitTo Unit we want to convert to (right side unit)
|
||||||
|
* @param value The amount to convert
|
||||||
|
* @param scale Which scale to use (number of decimal places)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
abstract fun convert(
|
||||||
|
unitTo: AbstractUnit,
|
||||||
|
value: BigDecimal,
|
||||||
|
scale: Int
|
||||||
|
): BigDecimal
|
||||||
|
}
|
22
app/src/main/java/com/sadellie/unitto/data/units/AllUnits.kt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package com.sadellie.unitto.data.units
|
||||||
|
|
||||||
|
import com.sadellie.unitto.data.units.collections.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a collection of all available units
|
||||||
|
*/
|
||||||
|
val ALL_UNITS: List<AbstractUnit> by lazy {
|
||||||
|
LENGTH_COLLECTION +
|
||||||
|
CURRENCY_COLLECTION +
|
||||||
|
MASS_COLLECTION +
|
||||||
|
TIME_COLLECTION +
|
||||||
|
TEMPERATURE_COLLECTION +
|
||||||
|
SPEED_COLLECTION +
|
||||||
|
AREA_COLLECTION +
|
||||||
|
VOLUME_COLLECTION +
|
||||||
|
DATA_COLLECTION +
|
||||||
|
ENERGY_COLLECTION +
|
||||||
|
POWER_COLLECTION +
|
||||||
|
ANGLE_COLLECTION +
|
||||||
|
DATA_TRANSFER_COLLECTION
|
||||||
|
}
|
40
app/src/main/java/com/sadellie/unitto/data/units/MyUnit.kt
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package com.sadellie.unitto.data.units
|
||||||
|
|
||||||
|
import com.sadellie.unitto.data.preferences.MAX_PRECISION
|
||||||
|
import com.sadellie.unitto.screens.setMinimumRequiredScale
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a Measurement object
|
||||||
|
* @param[displayName] The string resource, i.e. kilometer
|
||||||
|
* @param[shortName] The string resource for a short name, i.e. km
|
||||||
|
* @param[basicUnit] One unit of this measurement in basic unit
|
||||||
|
* @param[group] THe group this measurement belongs to
|
||||||
|
*/
|
||||||
|
class MyUnit(
|
||||||
|
unitId: String,
|
||||||
|
basicUnit: BigDecimal,
|
||||||
|
group: UnitGroup,
|
||||||
|
displayName: Int,
|
||||||
|
shortName: Int,
|
||||||
|
) : AbstractUnit(
|
||||||
|
unitId = unitId,
|
||||||
|
displayName = displayName,
|
||||||
|
shortName = shortName,
|
||||||
|
basicUnit = basicUnit,
|
||||||
|
group = group,
|
||||||
|
) {
|
||||||
|
override fun convert(
|
||||||
|
unitTo: AbstractUnit,
|
||||||
|
value: BigDecimal,
|
||||||
|
scale: Int
|
||||||
|
): BigDecimal {
|
||||||
|
return this
|
||||||
|
.basicUnit
|
||||||
|
.setScale(MAX_PRECISION)
|
||||||
|
.multiply(value)
|
||||||
|
.div(unitTo.basicUnit)
|
||||||
|
.setMinimumRequiredScale(scale)
|
||||||
|
.stripTrailingZeros()
|
||||||
|
}
|
||||||
|
}
|
392
app/src/main/java/com/sadellie/unitto/data/units/MyUnitIDS.kt
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
package com.sadellie.unitto.data.units
|
||||||
|
|
||||||
|
object MyUnitIDS {
|
||||||
|
|
||||||
|
// LENGTH
|
||||||
|
const val attometer = "attometer"
|
||||||
|
const val nanometer = "nanometer"
|
||||||
|
const val micrometer = "micrometer"
|
||||||
|
const val millimeter = "millimeter"
|
||||||
|
const val decimeter = "decimeter"
|
||||||
|
const val centimeter = "centimeter"
|
||||||
|
const val meter = "meter"
|
||||||
|
const val kilometer = "kilometer"
|
||||||
|
const val mile = "mile"
|
||||||
|
const val yard = "yard"
|
||||||
|
const val foot = "foot"
|
||||||
|
const val inch = "inch"
|
||||||
|
const val light_year = "light_year"
|
||||||
|
|
||||||
|
// MASS
|
||||||
|
const val electron_mass_rest = "electron_mass_rest"
|
||||||
|
const val atomic_mass_unit = "atomic_mass_unit"
|
||||||
|
const val milligram = "milligram"
|
||||||
|
const val gram = "gram"
|
||||||
|
const val kilogram = "kilogram"
|
||||||
|
const val metric_ton = "metric_ton"
|
||||||
|
const val imperial_ton = "imperial_ton"
|
||||||
|
const val pound = "pound"
|
||||||
|
const val ounce = "ounce"
|
||||||
|
const val carat = "carat"
|
||||||
|
|
||||||
|
// TEMPERATURE
|
||||||
|
const val celsius = "celsius"
|
||||||
|
const val fahrenheit = "fahrenheit"
|
||||||
|
const val kelvin = "kelvin"
|
||||||
|
|
||||||
|
// SPEED
|
||||||
|
const val millimeter_per_hour = "millimeter_per_hour" // BASIC UNIT
|
||||||
|
const val millimeter_per_minute = "millimeter_per_minute"
|
||||||
|
const val millimeter_per_second = "millimeter_per_second"
|
||||||
|
const val centimeter_per_hour = "centimeter_per_hour"
|
||||||
|
const val centimeter_per_minute = "centimeter_per_minute"
|
||||||
|
const val centimeter_per_second = "centimeter_per_second"
|
||||||
|
const val kilometer_per_hour = "kilometer_per_hour"
|
||||||
|
const val kilometer_per_minute = "kilometer_per_minute"
|
||||||
|
const val kilometer_per_second = "kilometer_per_second"
|
||||||
|
const val meter_per_hour = "meter_per_hour"
|
||||||
|
const val meter_per_minute = "meter_per_minute"
|
||||||
|
const val meter_per_second = "meter_per_second"
|
||||||
|
const val foot_per_hour = "foot_per_hour"
|
||||||
|
const val foot_per_minute = "foot_per_minute"
|
||||||
|
const val foot_per_second = "foot_per_second"
|
||||||
|
const val yard_per_hour = "yard_per_hour"
|
||||||
|
const val yard_per_minute = "yard_per_minute"
|
||||||
|
const val yard_per_second = "yard_per_second"
|
||||||
|
const val mile_per_hour = "mile_per_hour"
|
||||||
|
const val mile_per_minute = "mile_per_minute"
|
||||||
|
const val mile_per_second = "mile_per_second"
|
||||||
|
const val knot = "knot"
|
||||||
|
const val velocity_of_light_in_vacuum = "velocity_of_light_in_vacuum"
|
||||||
|
const val cosmic_velocity_first = "cosmic_velocity_first"
|
||||||
|
const val cosmic_velocity_second = "cosmic_velocity_second"
|
||||||
|
const val cosmic_velocity_third = "cosmic_velocity_third"
|
||||||
|
const val earths_orbital_speed = "earths_orbital_speed"
|
||||||
|
const val mach = "mach"
|
||||||
|
const val mach_si_standard = "mach_si_standard"
|
||||||
|
|
||||||
|
// DATA
|
||||||
|
const val bit = "bit"
|
||||||
|
const val kibibit = "kibibit"
|
||||||
|
const val kilobit = "kilobit"
|
||||||
|
const val megabit = "megabit"
|
||||||
|
const val gigabit = "gigabit"
|
||||||
|
const val terabit = "terabit"
|
||||||
|
const val petabit = "petabit"
|
||||||
|
const val exabit = "exabit"
|
||||||
|
const val byte = "byte"
|
||||||
|
const val kibibyte = "kibibyte"
|
||||||
|
const val kilobyte = "kilobyte"
|
||||||
|
const val megabyte = "megabyte"
|
||||||
|
const val gigabyte = "gigabyte"
|
||||||
|
const val terabyte = "terabyte"
|
||||||
|
const val petabyte = "petabyte"
|
||||||
|
const val exabyte = "exabyte"
|
||||||
|
|
||||||
|
// VOLUME
|
||||||
|
const val attoliter = "attoliter"
|
||||||
|
const val milliliter = "milliliter"
|
||||||
|
const val liter = "liter"
|
||||||
|
const val us_liquid_gallon = "gallon"
|
||||||
|
const val us_liquid_quart = "us_liquid_quart"
|
||||||
|
const val us_liquid_pint = "us_liquid_pint"
|
||||||
|
const val us_legal_cup = "us_legal_cup"
|
||||||
|
const val us_fluid_ounce = "us_fluid_ounce"
|
||||||
|
const val us_tablespoon = "us_tablespoon"
|
||||||
|
const val us_teaspoon = "us_teaspoon"
|
||||||
|
const val imperial_gallon = "imperial_gallon"
|
||||||
|
const val imperial_quart = "imperial_quart"
|
||||||
|
const val imperial_pint = "imperial_pint"
|
||||||
|
const val imperial_cup = "imperial_cup"
|
||||||
|
const val imperial_fluid_ounce = "imperial_fluid_ounce"
|
||||||
|
const val imperial_tablespoon = "imperial_tablespoon"
|
||||||
|
const val imperial_teaspoon = "imperial_teaspoon"
|
||||||
|
const val cubic_millimeter = "cubic_millimeter"
|
||||||
|
const val cubic_centimeter = "cubic_centimeter"
|
||||||
|
const val cubic_meter = "cubic_meter"
|
||||||
|
const val cubic_kilometer = "cubic_kilometer"
|
||||||
|
|
||||||
|
// TIME
|
||||||
|
const val attosecond = "attosecond"
|
||||||
|
const val nanosecond = "nanosecond"
|
||||||
|
const val microsecond = "microsecond"
|
||||||
|
const val millisecond = "millisecond"
|
||||||
|
const val second = "second"
|
||||||
|
const val minute = "minute"
|
||||||
|
const val hour = "hour"
|
||||||
|
const val day = "day"
|
||||||
|
const val week = "week"
|
||||||
|
|
||||||
|
// AREA
|
||||||
|
const val electron_cross_section = "electron_cross_section"
|
||||||
|
const val acre = "acre"
|
||||||
|
const val hectare = "hectare"
|
||||||
|
const val square_foot = "square_foot"
|
||||||
|
const val square_mile = "square_mile"
|
||||||
|
const val square_yard = "square_yard"
|
||||||
|
const val square_inch = "square_inch"
|
||||||
|
const val square_micrometer = "square_micrometer"
|
||||||
|
const val square_millimeter = "square_millimeter"
|
||||||
|
const val square_centimeter = "square_centimeter"
|
||||||
|
const val square_decimeter = "square_decimeter"
|
||||||
|
const val square_meter = "square_meter"
|
||||||
|
const val square_kilometer = "square_kilometer"
|
||||||
|
|
||||||
|
// ENERGY
|
||||||
|
const val electron_volt = "electron_volt"
|
||||||
|
const val attojoule = "attojoule"
|
||||||
|
const val joule = "joule"
|
||||||
|
const val kilojoule = "kilojoule"
|
||||||
|
const val gigajoule = "gigajoule"
|
||||||
|
const val megajoule = "megajoule"
|
||||||
|
const val energy_ton = "energy_ton"
|
||||||
|
const val kiloton = "kiloton"
|
||||||
|
const val megaton = "megaton"
|
||||||
|
const val gigaton = "gigaton"
|
||||||
|
const val energy_horse_power_metric = "energy_horse_power_metric"
|
||||||
|
const val calorie_th = "calorie_th"
|
||||||
|
const val kilocalorie_th = "kilocalorie_th"
|
||||||
|
|
||||||
|
// POWER
|
||||||
|
const val attowatt = "attowatt"
|
||||||
|
const val watt = "watt"
|
||||||
|
const val kilowatt = "kilowatt"
|
||||||
|
const val megawatt = "megawatt"
|
||||||
|
const val horse_power_mechanical = "horse_power_mechanical"
|
||||||
|
|
||||||
|
// ANGLE
|
||||||
|
const val angle_second = "angle_second"
|
||||||
|
const val angle_minute = "angle_minute"
|
||||||
|
const val degree = "degree"
|
||||||
|
const val radian = "radian"
|
||||||
|
const val sextant = "sextant"
|
||||||
|
const val turn = "turn"
|
||||||
|
|
||||||
|
// DATA TRANSFER
|
||||||
|
const val bit_per_second = "bit_per_second"
|
||||||
|
const val kibibit_per_second = "kibibit_per_second"
|
||||||
|
const val kilobit_per_second = "kilobit_per_second"
|
||||||
|
const val megabit_per_second = "megabit_per_second"
|
||||||
|
const val gigabit_per_second = "gigabit_per_second"
|
||||||
|
const val terabit_per_second = "terabit_per_second"
|
||||||
|
const val petabit_per_second = "petabit_per_second"
|
||||||
|
const val exabit_per_second = "exabit_per_second"
|
||||||
|
const val byte_per_second = "byte_per_second"
|
||||||
|
const val kibibyte_per_second = "kibibyte_per_second"
|
||||||
|
const val kilobyte_per_second = "kilobyte_per_second"
|
||||||
|
const val megabyte_per_second = "megabyte_per_second"
|
||||||
|
const val gigabyte_per_second = "gigabyte_per_second"
|
||||||
|
const val terabyte_per_second = "terabyte_per_second"
|
||||||
|
const val petabyte_per_second = "petabyte_per_second"
|
||||||
|
const val exabyte_per_second = "exabyte_per_second"
|
||||||
|
|
||||||
|
// CURRENCY
|
||||||
|
const val currency_1inch = "1inch"
|
||||||
|
const val currency_ada = "ada"
|
||||||
|
const val currency_aed = "aed"
|
||||||
|
const val currency_afn = "afn"
|
||||||
|
const val currency_algo = "algo"
|
||||||
|
const val currency_all = "all"
|
||||||
|
const val currency_amd = "amd"
|
||||||
|
const val currency_ang = "ang"
|
||||||
|
const val currency_aoa = "aoa"
|
||||||
|
const val currency_ars = "ars"
|
||||||
|
const val currency_atom = "atom"
|
||||||
|
const val currency_aud = "aud"
|
||||||
|
const val currency_avax = "avax"
|
||||||
|
const val currency_awg = "awg"
|
||||||
|
const val currency_azn = "azn"
|
||||||
|
const val currency_bam = "bam"
|
||||||
|
const val currency_bbd = "bbd"
|
||||||
|
const val currency_bch = "bch"
|
||||||
|
const val currency_bdt = "bdt"
|
||||||
|
const val currency_bgn = "bgn"
|
||||||
|
const val currency_bhd = "bhd"
|
||||||
|
const val currency_bif = "bif"
|
||||||
|
const val currency_bmd = "bmd"
|
||||||
|
const val currency_bnb = "bnb"
|
||||||
|
const val currency_bnd = "bnd"
|
||||||
|
const val currency_bob = "bob"
|
||||||
|
const val currency_brl = "brl"
|
||||||
|
const val currency_bsd = "bsd"
|
||||||
|
const val currency_btc = "btc"
|
||||||
|
const val currency_btn = "btn"
|
||||||
|
const val currency_busd = "busd"
|
||||||
|
const val currency_bwp = "bwp"
|
||||||
|
const val currency_byn = "byn"
|
||||||
|
const val currency_byr = "byr"
|
||||||
|
const val currency_bzd = "bzd"
|
||||||
|
const val currency_cad = "cad"
|
||||||
|
const val currency_cdf = "cdf"
|
||||||
|
const val currency_chf = "chf"
|
||||||
|
const val currency_chz = "chz"
|
||||||
|
const val currency_clf = "clf"
|
||||||
|
const val currency_clp = "clp"
|
||||||
|
const val currency_cny = "cny"
|
||||||
|
const val currency_cop = "cop"
|
||||||
|
const val currency_crc = "crc"
|
||||||
|
const val currency_cro = "cro"
|
||||||
|
const val currency_cuc = "cuc"
|
||||||
|
const val currency_cup = "cup"
|
||||||
|
const val currency_cve = "cve"
|
||||||
|
const val currency_czk = "czk"
|
||||||
|
const val currency_dai = "dai"
|
||||||
|
const val currency_djf = "djf"
|
||||||
|
const val currency_dkk = "dkk"
|
||||||
|
const val currency_doge = "doge"
|
||||||
|
const val currency_dop = "dop"
|
||||||
|
const val currency_dot = "dot"
|
||||||
|
const val currency_dzd = "dzd"
|
||||||
|
const val currency_egld = "egld"
|
||||||
|
const val currency_egp = "egp"
|
||||||
|
const val currency_enj = "enj"
|
||||||
|
const val currency_ern = "ern"
|
||||||
|
const val currency_etb = "etb"
|
||||||
|
const val currency_etc = "etc"
|
||||||
|
const val currency_eth = "eth"
|
||||||
|
const val currency_eur = "eur"
|
||||||
|
const val currency_fil = "fil"
|
||||||
|
const val currency_fjd = "fjd"
|
||||||
|
const val currency_fkp = "fkp"
|
||||||
|
const val currency_ftt = "ftt"
|
||||||
|
const val currency_gbp = "gbp"
|
||||||
|
const val currency_gel = "gel"
|
||||||
|
const val currency_ggp = "ggp"
|
||||||
|
const val currency_ghs = "ghs"
|
||||||
|
const val currency_gip = "gip"
|
||||||
|
const val currency_gmd = "gmd"
|
||||||
|
const val currency_gnf = "gnf"
|
||||||
|
const val currency_grt = "grt"
|
||||||
|
const val currency_gtq = "gtq"
|
||||||
|
const val currency_gyd = "gyd"
|
||||||
|
const val currency_hkd = "hkd"
|
||||||
|
const val currency_hnl = "hnl"
|
||||||
|
const val currency_hrk = "hrk"
|
||||||
|
const val currency_htg = "htg"
|
||||||
|
const val currency_huf = "huf"
|
||||||
|
const val currency_icp = "icp"
|
||||||
|
const val currency_idr = "idr"
|
||||||
|
const val currency_ils = "ils"
|
||||||
|
const val currency_imp = "imp"
|
||||||
|
const val currency_inj = "inj"
|
||||||
|
const val currency_inr = "inr"
|
||||||
|
const val currency_iqd = "iqd"
|
||||||
|
const val currency_irr = "irr"
|
||||||
|
const val currency_isk = "isk"
|
||||||
|
const val currency_jep = "jep"
|
||||||
|
const val currency_jmd = "jmd"
|
||||||
|
const val currency_jod = "jod"
|
||||||
|
const val currency_jpy = "jpy"
|
||||||
|
const val currency_kes = "kes"
|
||||||
|
const val currency_kgs = "kgs"
|
||||||
|
const val currency_khr = "khr"
|
||||||
|
const val currency_kmf = "kmf"
|
||||||
|
const val currency_kpw = "kpw"
|
||||||
|
const val currency_krw = "krw"
|
||||||
|
const val currency_ksm = "ksm"
|
||||||
|
const val currency_kwd = "kwd"
|
||||||
|
const val currency_kyd = "kyd"
|
||||||
|
const val currency_kzt = "kzt"
|
||||||
|
const val currency_lak = "lak"
|
||||||
|
const val currency_lbp = "lbp"
|
||||||
|
const val currency_link = "link"
|
||||||
|
const val currency_lkr = "lkr"
|
||||||
|
const val currency_lrd = "lrd"
|
||||||
|
const val currency_lsl = "lsl"
|
||||||
|
const val currency_ltc = "ltc"
|
||||||
|
const val currency_ltl = "ltl"
|
||||||
|
const val currency_luna = "luna"
|
||||||
|
const val currency_lvl = "lvl"
|
||||||
|
const val currency_lyd = "lyd"
|
||||||
|
const val currency_mad = "mad"
|
||||||
|
const val currency_matic = "matic"
|
||||||
|
const val currency_mdl = "mdl"
|
||||||
|
const val currency_mga = "mga"
|
||||||
|
const val currency_mkd = "mkd"
|
||||||
|
const val currency_mmk = "mmk"
|
||||||
|
const val currency_mnt = "mnt"
|
||||||
|
const val currency_mop = "mop"
|
||||||
|
const val currency_mro = "mro"
|
||||||
|
const val currency_mur = "mur"
|
||||||
|
const val currency_mvr = "mvr"
|
||||||
|
const val currency_mwk = "mwk"
|
||||||
|
const val currency_mxn = "mxn"
|
||||||
|
const val currency_myr = "myr"
|
||||||
|
const val currency_mzn = "mzn"
|
||||||
|
const val currency_nad = "nad"
|
||||||
|
const val currency_ngn = "ngn"
|
||||||
|
const val currency_nio = "nio"
|
||||||
|
const val currency_nok = "nok"
|
||||||
|
const val currency_npr = "npr"
|
||||||
|
const val currency_nzd = "nzd"
|
||||||
|
const val currency_omr = "omr"
|
||||||
|
const val currency_one = "one"
|
||||||
|
const val currency_pab = "pab"
|
||||||
|
const val currency_pen = "pen"
|
||||||
|
const val currency_pgk = "pgk"
|
||||||
|
const val currency_php = "php"
|
||||||
|
const val currency_pkr = "pkr"
|
||||||
|
const val currency_pln = "pln"
|
||||||
|
const val currency_pyg = "pyg"
|
||||||
|
const val currency_qar = "qar"
|
||||||
|
const val currency_ron = "ron"
|
||||||
|
const val currency_rsd = "rsd"
|
||||||
|
const val currency_rub = "rub"
|
||||||
|
const val currency_rwf = "rwf"
|
||||||
|
const val currency_sar = "sar"
|
||||||
|
const val currency_sbd = "sbd"
|
||||||
|
const val currency_scr = "scr"
|
||||||
|
const val currency_sdg = "sdg"
|
||||||
|
const val currency_sek = "sek"
|
||||||
|
const val currency_sgd = "sgd"
|
||||||
|
const val currency_shib = "shib"
|
||||||
|
const val currency_shp = "shp"
|
||||||
|
const val currency_sll = "sll"
|
||||||
|
const val currency_sol = "sol"
|
||||||
|
const val currency_sos = "sos"
|
||||||
|
const val currency_srd = "srd"
|
||||||
|
const val currency_std = "std"
|
||||||
|
const val currency_svc = "svc"
|
||||||
|
const val currency_syp = "syp"
|
||||||
|
const val currency_szl = "szl"
|
||||||
|
const val currency_thb = "thb"
|
||||||
|
const val currency_theta = "theta"
|
||||||
|
const val currency_tjs = "tjs"
|
||||||
|
const val currency_tmt = "tmt"
|
||||||
|
const val currency_tnd = "tnd"
|
||||||
|
const val currency_top = "top"
|
||||||
|
const val currency_trx = "trx"
|
||||||
|
const val currency_try = "try"
|
||||||
|
const val currency_ttd = "ttd"
|
||||||
|
const val currency_twd = "twd"
|
||||||
|
const val currency_tzs = "tzs"
|
||||||
|
const val currency_uah = "uah"
|
||||||
|
const val currency_ugx = "ugx"
|
||||||
|
const val currency_uni = "uni"
|
||||||
|
const val currency_usd = "usd"
|
||||||
|
const val currency_usdc = "usdc"
|
||||||
|
const val currency_usdt = "usdt"
|
||||||
|
const val currency_uyu = "uyu"
|
||||||
|
const val currency_uzs = "uzs"
|
||||||
|
const val currency_vef = "vef"
|
||||||
|
const val currency_vet = "vet"
|
||||||
|
const val currency_vnd = "vnd"
|
||||||
|
const val currency_vuv = "vuv"
|
||||||
|
const val currency_wbtc = "wbtc"
|
||||||
|
const val currency_wst = "wst"
|
||||||
|
const val currency_xaf = "xaf"
|
||||||
|
const val currency_xag = "xag"
|
||||||
|
const val currency_xau = "xau"
|
||||||
|
const val currency_xcd = "xcd"
|
||||||
|
const val currency_xdr = "xdr"
|
||||||
|
const val currency_xlm = "xlm"
|
||||||
|
const val currency_xmr = "xmr"
|
||||||
|
const val currency_xof = "xof"
|
||||||
|
const val currency_xpf = "xpf"
|
||||||
|
const val currency_xrp = "xrp"
|
||||||
|
const val currency_yer = "yer"
|
||||||
|
const val currency_zar = "zar"
|
||||||
|
const val currency_zmk = "zmk"
|
||||||
|
const val currency_zmw = "zmw"
|
||||||
|
const val currency_zwl = "zwl"
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.sadellie.unitto.data.units
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
|
||||||
|
val ALL_UNIT_GROUPS: List<UnitGroup> by lazy {
|
||||||
|
UnitGroup.values().toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As not all measurements can be converted between into other, we separate them into groups.
|
||||||
|
* Within one groups all measurements can be converted
|
||||||
|
*/
|
||||||
|
enum class UnitGroup(val res: Int, val canNegate: Boolean = false) {
|
||||||
|
LENGTH(res = R.string.length),
|
||||||
|
CURRENCY(res = R.string.currency),
|
||||||
|
MASS(res = R.string.mass),
|
||||||
|
TIME(res = R.string.time),
|
||||||
|
TEMPERATURE(res = R.string.temperature, canNegate = true),
|
||||||
|
SPEED(res = R.string.speed),
|
||||||
|
AREA(res = R.string.area),
|
||||||
|
VOLUME(res = R.string.volume),
|
||||||
|
DATA(res = R.string.data),
|
||||||
|
ENERGY(res = R.string.energy),
|
||||||
|
POWER(res = R.string.power),
|
||||||
|
ANGLE(res = R.string.angle),
|
||||||
|
DATA_TRANSFER(res = R.string.data_transfer)
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
val ANGLE_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
MyUnit(MyUnitIDS.angle_second, BigDecimal.valueOf(1), UnitGroup.ANGLE, R.string.angle_second, R.string.angle_second_short),
|
||||||
|
MyUnit(MyUnitIDS.angle_minute, BigDecimal.valueOf(60), UnitGroup.ANGLE, R.string.angle_minute, R.string.angle_minute_short),
|
||||||
|
MyUnit(MyUnitIDS.degree, BigDecimal.valueOf(3600), UnitGroup.ANGLE, R.string.degree, R.string.degree_short),
|
||||||
|
MyUnit(MyUnitIDS.radian, BigDecimal.valueOf(206264.8062471), UnitGroup.ANGLE, R.string.radian, R.string.radian_short),
|
||||||
|
MyUnit(MyUnitIDS.sextant, BigDecimal.valueOf(216000), UnitGroup.ANGLE, R.string.sextant, R.string.sextant_short),
|
||||||
|
MyUnit(MyUnitIDS.turn, BigDecimal.valueOf(1296000), UnitGroup.ANGLE, R.string.turn, R.string.turn_short),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
val AREA_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
MyUnit(MyUnitIDS.acre, BigDecimal.valueOf(6.083246572E+31), UnitGroup.AREA, R.string.acre, R.string.acre_short ),
|
||||||
|
MyUnit(MyUnitIDS.hectare, BigDecimal.valueOf(1.503202964E+32), UnitGroup.AREA, R.string.hectare, R.string.hectare_short ),
|
||||||
|
MyUnit(MyUnitIDS.square_foot, BigDecimal.valueOf(1.396521251E+27), UnitGroup.AREA, R.string.square_foot, R.string.square_foot_short),
|
||||||
|
MyUnit(MyUnitIDS.square_mile, BigDecimal.valueOf(3.893277806E+34), UnitGroup.AREA, R.string.square_mile, R.string.square_mile_short),
|
||||||
|
MyUnit(MyUnitIDS.square_yard, BigDecimal.valueOf(1.256869126E+28), UnitGroup.AREA, R.string.square_yard, R.string.square_yard_short),
|
||||||
|
MyUnit(MyUnitIDS.square_inch, BigDecimal.valueOf(9.698064247E+24), UnitGroup.AREA, R.string.square_inch, R.string.square_inch_short),
|
||||||
|
MyUnit(MyUnitIDS.square_micrometer, BigDecimal.valueOf(15032029647492000.0), UnitGroup.AREA, R.string.square_micrometer, R.string.square_micrometer_short),
|
||||||
|
MyUnit(MyUnitIDS.square_millimeter, BigDecimal.valueOf(1.503202964E+22), UnitGroup.AREA, R.string.square_millimeter, R.string.square_millimeter_short),
|
||||||
|
MyUnit(MyUnitIDS.square_centimeter, BigDecimal.valueOf(1.503202964E+24), UnitGroup.AREA, R.string.square_centimeter, R.string.square_centimeter_short),
|
||||||
|
MyUnit(MyUnitIDS.square_decimeter, BigDecimal.valueOf(1.503202964E+26), UnitGroup.AREA, R.string.square_decimeter, R.string.square_decimeter_short),
|
||||||
|
MyUnit(MyUnitIDS.square_meter, BigDecimal.valueOf(1.503202964E+28), UnitGroup.AREA, R.string.square_meter, R.string.square_meter_short),
|
||||||
|
MyUnit(MyUnitIDS.square_kilometer, BigDecimal.valueOf(1.503202964E+34), UnitGroup.AREA, R.string.square_kilometer, R.string.square_kilometer_short),
|
||||||
|
MyUnit(MyUnitIDS.electron_cross_section, BigDecimal.valueOf(1.0), UnitGroup.AREA, R.string.electron_cross_section, R.string.electron_cross_section_short),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,221 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
val CURRENCY_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
MyUnit(MyUnitIDS.currency_1inch, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_1inch, R.string.currency_1inch),
|
||||||
|
MyUnit(MyUnitIDS.currency_ada, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ada, R.string.currency_ada),
|
||||||
|
MyUnit(MyUnitIDS.currency_aed, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_aed, R.string.currency_aed),
|
||||||
|
MyUnit(MyUnitIDS.currency_afn, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_afn, R.string.currency_afn),
|
||||||
|
MyUnit(MyUnitIDS.currency_algo, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_algo, R.string.currency_algo),
|
||||||
|
MyUnit(MyUnitIDS.currency_all, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_all, R.string.currency_all),
|
||||||
|
MyUnit(MyUnitIDS.currency_amd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_amd, R.string.currency_amd),
|
||||||
|
MyUnit(MyUnitIDS.currency_ang, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ang, R.string.currency_ang),
|
||||||
|
MyUnit(MyUnitIDS.currency_aoa, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_aoa, R.string.currency_aoa),
|
||||||
|
MyUnit(MyUnitIDS.currency_ars, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ars, R.string.currency_ars),
|
||||||
|
MyUnit(MyUnitIDS.currency_atom, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_atom, R.string.currency_atom),
|
||||||
|
MyUnit(MyUnitIDS.currency_aud, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_aud, R.string.currency_aud),
|
||||||
|
MyUnit(MyUnitIDS.currency_avax, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_avax, R.string.currency_avax),
|
||||||
|
MyUnit(MyUnitIDS.currency_awg, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_awg, R.string.currency_awg),
|
||||||
|
MyUnit(MyUnitIDS.currency_azn, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_azn, R.string.currency_azn),
|
||||||
|
MyUnit(MyUnitIDS.currency_bam, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bam, R.string.currency_bam),
|
||||||
|
MyUnit(MyUnitIDS.currency_bbd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bbd, R.string.currency_bbd),
|
||||||
|
MyUnit(MyUnitIDS.currency_bch, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bch, R.string.currency_bch),
|
||||||
|
MyUnit(MyUnitIDS.currency_bdt, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bdt, R.string.currency_bdt),
|
||||||
|
MyUnit(MyUnitIDS.currency_bgn, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bgn, R.string.currency_bgn),
|
||||||
|
MyUnit(MyUnitIDS.currency_bhd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bhd, R.string.currency_bhd),
|
||||||
|
MyUnit(MyUnitIDS.currency_bif, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bif, R.string.currency_bif),
|
||||||
|
MyUnit(MyUnitIDS.currency_bmd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bmd, R.string.currency_bmd),
|
||||||
|
MyUnit(MyUnitIDS.currency_bnb, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bnb, R.string.currency_bnb),
|
||||||
|
MyUnit(MyUnitIDS.currency_bnd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bnd, R.string.currency_bnd),
|
||||||
|
MyUnit(MyUnitIDS.currency_bob, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bob, R.string.currency_bob),
|
||||||
|
MyUnit(MyUnitIDS.currency_brl, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_brl, R.string.currency_brl),
|
||||||
|
MyUnit(MyUnitIDS.currency_bsd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bsd, R.string.currency_bsd),
|
||||||
|
MyUnit(MyUnitIDS.currency_btc, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_btc, R.string.currency_btc),
|
||||||
|
MyUnit(MyUnitIDS.currency_btn, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_btn, R.string.currency_btn),
|
||||||
|
MyUnit(MyUnitIDS.currency_busd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_busd, R.string.currency_busd),
|
||||||
|
MyUnit(MyUnitIDS.currency_bwp, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bwp, R.string.currency_bwp),
|
||||||
|
MyUnit(MyUnitIDS.currency_byn, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_byn, R.string.currency_byn),
|
||||||
|
MyUnit(MyUnitIDS.currency_byr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_byr, R.string.currency_byr),
|
||||||
|
MyUnit(MyUnitIDS.currency_bzd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_bzd, R.string.currency_bzd),
|
||||||
|
MyUnit(MyUnitIDS.currency_cad, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_cad, R.string.currency_cad),
|
||||||
|
MyUnit(MyUnitIDS.currency_cdf, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_cdf, R.string.currency_cdf),
|
||||||
|
MyUnit(MyUnitIDS.currency_chf, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_chf, R.string.currency_chf),
|
||||||
|
MyUnit(MyUnitIDS.currency_chz, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_chz, R.string.currency_chz),
|
||||||
|
MyUnit(MyUnitIDS.currency_clf, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_clf, R.string.currency_clf),
|
||||||
|
MyUnit(MyUnitIDS.currency_clp, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_clp, R.string.currency_clp),
|
||||||
|
MyUnit(MyUnitIDS.currency_cny, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_cny, R.string.currency_cny),
|
||||||
|
MyUnit(MyUnitIDS.currency_cop, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_cop, R.string.currency_cop),
|
||||||
|
MyUnit(MyUnitIDS.currency_crc, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_crc, R.string.currency_crc),
|
||||||
|
MyUnit(MyUnitIDS.currency_cro, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_cro, R.string.currency_cro),
|
||||||
|
MyUnit(MyUnitIDS.currency_cuc, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_cuc, R.string.currency_cuc),
|
||||||
|
MyUnit(MyUnitIDS.currency_cup, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_cup, R.string.currency_cup),
|
||||||
|
MyUnit(MyUnitIDS.currency_cve, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_cve, R.string.currency_cve),
|
||||||
|
MyUnit(MyUnitIDS.currency_czk, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_czk, R.string.currency_czk),
|
||||||
|
MyUnit(MyUnitIDS.currency_dai, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_dai, R.string.currency_dai),
|
||||||
|
MyUnit(MyUnitIDS.currency_djf, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_djf, R.string.currency_djf),
|
||||||
|
MyUnit(MyUnitIDS.currency_dkk, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_dkk, R.string.currency_dkk),
|
||||||
|
MyUnit(MyUnitIDS.currency_doge, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_doge, R.string.currency_doge),
|
||||||
|
MyUnit(MyUnitIDS.currency_dop, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_dop, R.string.currency_dop),
|
||||||
|
MyUnit(MyUnitIDS.currency_dot, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_dot, R.string.currency_dot),
|
||||||
|
MyUnit(MyUnitIDS.currency_dzd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_dzd, R.string.currency_dzd),
|
||||||
|
MyUnit(MyUnitIDS.currency_egld, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_egld, R.string.currency_egld),
|
||||||
|
MyUnit(MyUnitIDS.currency_egp, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_egp, R.string.currency_egp),
|
||||||
|
MyUnit(MyUnitIDS.currency_enj, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_enj, R.string.currency_enj),
|
||||||
|
MyUnit(MyUnitIDS.currency_ern, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ern, R.string.currency_ern),
|
||||||
|
MyUnit(MyUnitIDS.currency_etb, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_etb, R.string.currency_etb),
|
||||||
|
MyUnit(MyUnitIDS.currency_etc, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_etc, R.string.currency_etc),
|
||||||
|
MyUnit(MyUnitIDS.currency_eth, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_eth, R.string.currency_eth),
|
||||||
|
MyUnit(MyUnitIDS.currency_eur, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_eur, R.string.currency_eur),
|
||||||
|
MyUnit(MyUnitIDS.currency_fil, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_fil, R.string.currency_fil),
|
||||||
|
MyUnit(MyUnitIDS.currency_fjd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_fjd, R.string.currency_fjd),
|
||||||
|
MyUnit(MyUnitIDS.currency_fkp, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_fkp, R.string.currency_fkp),
|
||||||
|
MyUnit(MyUnitIDS.currency_ftt, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ftt, R.string.currency_ftt),
|
||||||
|
MyUnit(MyUnitIDS.currency_gbp, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_gbp, R.string.currency_gbp),
|
||||||
|
MyUnit(MyUnitIDS.currency_gel, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_gel, R.string.currency_gel),
|
||||||
|
MyUnit(MyUnitIDS.currency_ggp, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ggp, R.string.currency_ggp),
|
||||||
|
MyUnit(MyUnitIDS.currency_ghs, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ghs, R.string.currency_ghs),
|
||||||
|
MyUnit(MyUnitIDS.currency_gip, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_gip, R.string.currency_gip),
|
||||||
|
MyUnit(MyUnitIDS.currency_gmd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_gmd, R.string.currency_gmd),
|
||||||
|
MyUnit(MyUnitIDS.currency_gnf, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_gnf, R.string.currency_gnf),
|
||||||
|
MyUnit(MyUnitIDS.currency_grt, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_grt, R.string.currency_grt),
|
||||||
|
MyUnit(MyUnitIDS.currency_gtq, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_gtq, R.string.currency_gtq),
|
||||||
|
MyUnit(MyUnitIDS.currency_gyd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_gyd, R.string.currency_gyd),
|
||||||
|
MyUnit(MyUnitIDS.currency_hkd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_hkd, R.string.currency_hkd),
|
||||||
|
MyUnit(MyUnitIDS.currency_hnl, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_hnl, R.string.currency_hnl),
|
||||||
|
MyUnit(MyUnitIDS.currency_hrk, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_hrk, R.string.currency_hrk),
|
||||||
|
MyUnit(MyUnitIDS.currency_htg, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_htg, R.string.currency_htg),
|
||||||
|
MyUnit(MyUnitIDS.currency_huf, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_huf, R.string.currency_huf),
|
||||||
|
MyUnit(MyUnitIDS.currency_icp, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_icp, R.string.currency_icp),
|
||||||
|
MyUnit(MyUnitIDS.currency_idr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_idr, R.string.currency_idr),
|
||||||
|
MyUnit(MyUnitIDS.currency_ils, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ils, R.string.currency_ils),
|
||||||
|
MyUnit(MyUnitIDS.currency_imp, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_imp, R.string.currency_imp),
|
||||||
|
MyUnit(MyUnitIDS.currency_inj, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_inj, R.string.currency_inj),
|
||||||
|
MyUnit(MyUnitIDS.currency_inr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_inr, R.string.currency_inr),
|
||||||
|
MyUnit(MyUnitIDS.currency_iqd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_iqd, R.string.currency_iqd),
|
||||||
|
MyUnit(MyUnitIDS.currency_irr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_irr, R.string.currency_irr),
|
||||||
|
MyUnit(MyUnitIDS.currency_isk, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_isk, R.string.currency_isk),
|
||||||
|
MyUnit(MyUnitIDS.currency_jep, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_jep, R.string.currency_jep),
|
||||||
|
MyUnit(MyUnitIDS.currency_jmd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_jmd, R.string.currency_jmd),
|
||||||
|
MyUnit(MyUnitIDS.currency_jod, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_jod, R.string.currency_jod),
|
||||||
|
MyUnit(MyUnitIDS.currency_jpy, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_jpy, R.string.currency_jpy),
|
||||||
|
MyUnit(MyUnitIDS.currency_kes, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_kes, R.string.currency_kes),
|
||||||
|
MyUnit(MyUnitIDS.currency_kgs, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_kgs, R.string.currency_kgs),
|
||||||
|
MyUnit(MyUnitIDS.currency_khr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_khr, R.string.currency_khr),
|
||||||
|
MyUnit(MyUnitIDS.currency_kmf, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_kmf, R.string.currency_kmf),
|
||||||
|
MyUnit(MyUnitIDS.currency_kpw, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_kpw, R.string.currency_kpw),
|
||||||
|
MyUnit(MyUnitIDS.currency_krw, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_krw, R.string.currency_krw),
|
||||||
|
MyUnit(MyUnitIDS.currency_ksm, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ksm, R.string.currency_ksm),
|
||||||
|
MyUnit(MyUnitIDS.currency_kwd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_kwd, R.string.currency_kwd),
|
||||||
|
MyUnit(MyUnitIDS.currency_kyd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_kyd, R.string.currency_kyd),
|
||||||
|
MyUnit(MyUnitIDS.currency_kzt, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_kzt, R.string.currency_kzt),
|
||||||
|
MyUnit(MyUnitIDS.currency_lak, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_lak, R.string.currency_lak),
|
||||||
|
MyUnit(MyUnitIDS.currency_lbp, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_lbp, R.string.currency_lbp),
|
||||||
|
MyUnit(MyUnitIDS.currency_link, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_link, R.string.currency_link),
|
||||||
|
MyUnit(MyUnitIDS.currency_lkr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_lkr, R.string.currency_lkr),
|
||||||
|
MyUnit(MyUnitIDS.currency_lrd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_lrd, R.string.currency_lrd),
|
||||||
|
MyUnit(MyUnitIDS.currency_lsl, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_lsl, R.string.currency_lsl),
|
||||||
|
MyUnit(MyUnitIDS.currency_ltc, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ltc, R.string.currency_ltc),
|
||||||
|
MyUnit(MyUnitIDS.currency_ltl, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ltl, R.string.currency_ltl),
|
||||||
|
MyUnit(MyUnitIDS.currency_luna, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_luna, R.string.currency_luna),
|
||||||
|
MyUnit(MyUnitIDS.currency_lvl, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_lvl, R.string.currency_lvl),
|
||||||
|
MyUnit(MyUnitIDS.currency_lyd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_lyd, R.string.currency_lyd),
|
||||||
|
MyUnit(MyUnitIDS.currency_mad, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mad, R.string.currency_mad),
|
||||||
|
MyUnit(MyUnitIDS.currency_matic, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_matic, R.string.currency_matic),
|
||||||
|
MyUnit(MyUnitIDS.currency_mdl, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mdl, R.string.currency_mdl),
|
||||||
|
MyUnit(MyUnitIDS.currency_mga, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mga, R.string.currency_mga),
|
||||||
|
MyUnit(MyUnitIDS.currency_mkd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mkd, R.string.currency_mkd),
|
||||||
|
MyUnit(MyUnitIDS.currency_mmk, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mmk, R.string.currency_mmk),
|
||||||
|
MyUnit(MyUnitIDS.currency_mnt, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mnt, R.string.currency_mnt),
|
||||||
|
MyUnit(MyUnitIDS.currency_mop, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mop, R.string.currency_mop),
|
||||||
|
MyUnit(MyUnitIDS.currency_mro, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mro, R.string.currency_mro),
|
||||||
|
MyUnit(MyUnitIDS.currency_mur, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mur, R.string.currency_mur),
|
||||||
|
MyUnit(MyUnitIDS.currency_mvr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mvr, R.string.currency_mvr),
|
||||||
|
MyUnit(MyUnitIDS.currency_mwk, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mwk, R.string.currency_mwk),
|
||||||
|
MyUnit(MyUnitIDS.currency_mxn, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mxn, R.string.currency_mxn),
|
||||||
|
MyUnit(MyUnitIDS.currency_myr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_myr, R.string.currency_myr),
|
||||||
|
MyUnit(MyUnitIDS.currency_mzn, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_mzn, R.string.currency_mzn),
|
||||||
|
MyUnit(MyUnitIDS.currency_nad, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_nad, R.string.currency_nad),
|
||||||
|
MyUnit(MyUnitIDS.currency_ngn, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ngn, R.string.currency_ngn),
|
||||||
|
MyUnit(MyUnitIDS.currency_nio, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_nio, R.string.currency_nio),
|
||||||
|
MyUnit(MyUnitIDS.currency_nok, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_nok, R.string.currency_nok),
|
||||||
|
MyUnit(MyUnitIDS.currency_npr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_npr, R.string.currency_npr),
|
||||||
|
MyUnit(MyUnitIDS.currency_nzd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_nzd, R.string.currency_nzd),
|
||||||
|
MyUnit(MyUnitIDS.currency_omr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_omr, R.string.currency_omr),
|
||||||
|
MyUnit(MyUnitIDS.currency_one, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_one, R.string.currency_one),
|
||||||
|
MyUnit(MyUnitIDS.currency_pab, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_pab, R.string.currency_pab),
|
||||||
|
MyUnit(MyUnitIDS.currency_pen, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_pen, R.string.currency_pen),
|
||||||
|
MyUnit(MyUnitIDS.currency_pgk, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_pgk, R.string.currency_pgk),
|
||||||
|
MyUnit(MyUnitIDS.currency_php, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_php, R.string.currency_php),
|
||||||
|
MyUnit(MyUnitIDS.currency_pkr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_pkr, R.string.currency_pkr),
|
||||||
|
MyUnit(MyUnitIDS.currency_pln, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_pln, R.string.currency_pln),
|
||||||
|
MyUnit(MyUnitIDS.currency_pyg, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_pyg, R.string.currency_pyg),
|
||||||
|
MyUnit(MyUnitIDS.currency_qar, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_qar, R.string.currency_qar),
|
||||||
|
MyUnit(MyUnitIDS.currency_ron, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ron, R.string.currency_ron),
|
||||||
|
MyUnit(MyUnitIDS.currency_rsd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_rsd, R.string.currency_rsd),
|
||||||
|
MyUnit(MyUnitIDS.currency_rub, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_rub, R.string.currency_rub),
|
||||||
|
MyUnit(MyUnitIDS.currency_rwf, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_rwf, R.string.currency_rwf),
|
||||||
|
MyUnit(MyUnitIDS.currency_sar, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_sar, R.string.currency_sar),
|
||||||
|
MyUnit(MyUnitIDS.currency_sbd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_sbd, R.string.currency_sbd),
|
||||||
|
MyUnit(MyUnitIDS.currency_scr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_scr, R.string.currency_scr),
|
||||||
|
MyUnit(MyUnitIDS.currency_sdg, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_sdg, R.string.currency_sdg),
|
||||||
|
MyUnit(MyUnitIDS.currency_sek, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_sek, R.string.currency_sek),
|
||||||
|
MyUnit(MyUnitIDS.currency_sgd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_sgd, R.string.currency_sgd),
|
||||||
|
MyUnit(MyUnitIDS.currency_shib, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_shib, R.string.currency_shib),
|
||||||
|
MyUnit(MyUnitIDS.currency_shp, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_shp, R.string.currency_shp),
|
||||||
|
MyUnit(MyUnitIDS.currency_sll, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_sll, R.string.currency_sll),
|
||||||
|
MyUnit(MyUnitIDS.currency_sol, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_sol, R.string.currency_sol),
|
||||||
|
MyUnit(MyUnitIDS.currency_sos, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_sos, R.string.currency_sos),
|
||||||
|
MyUnit(MyUnitIDS.currency_srd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_srd, R.string.currency_srd),
|
||||||
|
MyUnit(MyUnitIDS.currency_std, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_std, R.string.currency_std),
|
||||||
|
MyUnit(MyUnitIDS.currency_svc, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_svc, R.string.currency_svc),
|
||||||
|
MyUnit(MyUnitIDS.currency_syp, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_syp, R.string.currency_syp),
|
||||||
|
MyUnit(MyUnitIDS.currency_szl, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_szl, R.string.currency_szl),
|
||||||
|
MyUnit(MyUnitIDS.currency_thb, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_thb, R.string.currency_thb),
|
||||||
|
MyUnit(MyUnitIDS.currency_theta, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_theta, R.string.currency_theta),
|
||||||
|
MyUnit(MyUnitIDS.currency_tjs, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_tjs, R.string.currency_tjs),
|
||||||
|
MyUnit(MyUnitIDS.currency_tmt, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_tmt, R.string.currency_tmt),
|
||||||
|
MyUnit(MyUnitIDS.currency_tnd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_tnd, R.string.currency_tnd),
|
||||||
|
MyUnit(MyUnitIDS.currency_top, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_top, R.string.currency_top),
|
||||||
|
MyUnit(MyUnitIDS.currency_trx, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_trx, R.string.currency_trx),
|
||||||
|
MyUnit(MyUnitIDS.currency_try, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_try, R.string.currency_try),
|
||||||
|
MyUnit(MyUnitIDS.currency_ttd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ttd, R.string.currency_ttd),
|
||||||
|
MyUnit(MyUnitIDS.currency_twd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_twd, R.string.currency_twd),
|
||||||
|
MyUnit(MyUnitIDS.currency_tzs, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_tzs, R.string.currency_tzs),
|
||||||
|
MyUnit(MyUnitIDS.currency_uah, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_uah, R.string.currency_uah),
|
||||||
|
MyUnit(MyUnitIDS.currency_ugx, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_ugx, R.string.currency_ugx),
|
||||||
|
MyUnit(MyUnitIDS.currency_uni, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_uni, R.string.currency_uni),
|
||||||
|
MyUnit(MyUnitIDS.currency_usd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_usd, R.string.currency_usd),
|
||||||
|
MyUnit(MyUnitIDS.currency_usdc, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_usdc, R.string.currency_usdc),
|
||||||
|
MyUnit(MyUnitIDS.currency_usdt, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_usdt, R.string.currency_usdt),
|
||||||
|
MyUnit(MyUnitIDS.currency_uyu, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_uyu, R.string.currency_uyu),
|
||||||
|
MyUnit(MyUnitIDS.currency_uzs, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_uzs, R.string.currency_uzs),
|
||||||
|
MyUnit(MyUnitIDS.currency_vef, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_vef, R.string.currency_vef),
|
||||||
|
MyUnit(MyUnitIDS.currency_vet, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_vet, R.string.currency_vet),
|
||||||
|
MyUnit(MyUnitIDS.currency_vnd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_vnd, R.string.currency_vnd),
|
||||||
|
MyUnit(MyUnitIDS.currency_vuv, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_vuv, R.string.currency_vuv),
|
||||||
|
MyUnit(MyUnitIDS.currency_wbtc, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_wbtc, R.string.currency_wbtc),
|
||||||
|
MyUnit(MyUnitIDS.currency_wst, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_wst, R.string.currency_wst),
|
||||||
|
MyUnit(MyUnitIDS.currency_xaf, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_xaf, R.string.currency_xaf),
|
||||||
|
MyUnit(MyUnitIDS.currency_xag, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_xag, R.string.currency_xag),
|
||||||
|
MyUnit(MyUnitIDS.currency_xau, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_xau, R.string.currency_xau),
|
||||||
|
MyUnit(MyUnitIDS.currency_xcd, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_xcd, R.string.currency_xcd),
|
||||||
|
MyUnit(MyUnitIDS.currency_xdr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_xdr, R.string.currency_xdr),
|
||||||
|
MyUnit(MyUnitIDS.currency_xlm, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_xlm, R.string.currency_xlm),
|
||||||
|
MyUnit(MyUnitIDS.currency_xmr, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_xmr, R.string.currency_xmr),
|
||||||
|
MyUnit(MyUnitIDS.currency_xof, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_xof, R.string.currency_xof),
|
||||||
|
MyUnit(MyUnitIDS.currency_xpf, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_xpf, R.string.currency_xpf),
|
||||||
|
MyUnit(MyUnitIDS.currency_xrp, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_xrp, R.string.currency_xrp),
|
||||||
|
MyUnit(MyUnitIDS.currency_yer, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_yer, R.string.currency_yer),
|
||||||
|
MyUnit(MyUnitIDS.currency_zar, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_zar, R.string.currency_zar),
|
||||||
|
MyUnit(MyUnitIDS.currency_zmk, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_zmk, R.string.currency_zmk),
|
||||||
|
MyUnit(MyUnitIDS.currency_zmw, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_zmw, R.string.currency_zmw),
|
||||||
|
MyUnit(MyUnitIDS.currency_zwl, BigDecimal.ONE, UnitGroup.CURRENCY, R.string.currency_zwl, R.string.currency_zwl),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
val DATA_TRANSFER_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
MyUnit(MyUnitIDS.bit_per_second, BigDecimal.valueOf(1), UnitGroup.DATA_TRANSFER, R.string.bit_per_second, R.string.bit_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.kibibit_per_second, BigDecimal.valueOf(1_024), UnitGroup.DATA_TRANSFER, R.string.kibibit_per_second, R.string.kibibit_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.kilobit_per_second, BigDecimal.valueOf(1_000), UnitGroup.DATA_TRANSFER, R.string.kilobit_per_second, R.string.kilobit_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.megabit_per_second, BigDecimal.valueOf(1_000_000), UnitGroup.DATA_TRANSFER, R.string.megabit_per_second, R.string.megabit_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.gigabit_per_second, BigDecimal.valueOf(1_000_000_000), UnitGroup.DATA_TRANSFER, R.string.gigabit_per_second, R.string.gigabit_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.terabit_per_second, BigDecimal.valueOf(1_000_000_000_000), UnitGroup.DATA_TRANSFER, R.string.terabit_per_second, R.string.terabit_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.petabit_per_second, BigDecimal.valueOf(1_000_000_000_000_000), UnitGroup.DATA_TRANSFER, R.string.petabit_per_second, R.string.petabit_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.exabit_per_second, BigDecimal.valueOf(1_000_000_000_000_000_000), UnitGroup.DATA_TRANSFER, R.string.exabit_per_second, R.string.exabit_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.byte_per_second, BigDecimal.valueOf(8), UnitGroup.DATA_TRANSFER, R.string.byte_per_second, R.string.byte_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.kibibyte_per_second, BigDecimal.valueOf(8_192), UnitGroup.DATA_TRANSFER, R.string.kibibyte_per_second, R.string.kibibyte_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.kilobyte_per_second, BigDecimal.valueOf(8_000), UnitGroup.DATA_TRANSFER, R.string.kilobyte_per_second, R.string.kilobyte_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.megabyte_per_second, BigDecimal.valueOf(8_000_000), UnitGroup.DATA_TRANSFER, R.string.megabyte_per_second, R.string.megabyte_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.gigabyte_per_second, BigDecimal.valueOf(8_000_000_000), UnitGroup.DATA_TRANSFER, R.string.gigabyte_per_second, R.string.gigabyte_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.terabyte_per_second, BigDecimal.valueOf(8_000_000_000_000), UnitGroup.DATA_TRANSFER, R.string.terabyte_per_second, R.string.terabyte_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.petabyte_per_second, BigDecimal.valueOf(8_000_000_000_000_000), UnitGroup.DATA_TRANSFER, R.string.petabyte_per_second, R.string.petabyte_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.exabyte_per_second, BigDecimal.valueOf(8_000_000_000_000_000_000), UnitGroup.DATA_TRANSFER, R.string.exabyte_per_second, R.string.exabyte_per_second_short),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
val ENERGY_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
MyUnit(MyUnitIDS.electron_volt, BigDecimal.valueOf(1), UnitGroup.ENERGY, R.string.electron_volt, R.string.electron_volt_short),
|
||||||
|
MyUnit(MyUnitIDS.attojoule, BigDecimal.valueOf(6.241506363094), UnitGroup.ENERGY, R.string.attojoule, R.string.attojoule_short),
|
||||||
|
MyUnit(MyUnitIDS.joule, BigDecimal.valueOf(6_241_506_363_094_000_000), UnitGroup.ENERGY, R.string.joule, R.string.joule_short),
|
||||||
|
MyUnit(MyUnitIDS.kilojoule, BigDecimal.valueOf(6.241506358E+21), UnitGroup.ENERGY, R.string.kilojoule, R.string.kilojoule_short),
|
||||||
|
MyUnit(MyUnitIDS.megajoule, BigDecimal.valueOf(6.241506358E+24), UnitGroup.ENERGY, R.string.megajoule, R.string.megajoule_short),
|
||||||
|
MyUnit(MyUnitIDS.gigajoule, BigDecimal.valueOf(6.241506358E+27), UnitGroup.ENERGY, R.string.gigajoule, R.string.gigajoule_short),
|
||||||
|
MyUnit(MyUnitIDS.energy_ton, BigDecimal.valueOf(2.611446262E+28), UnitGroup.ENERGY, R.string.energy_ton, R.string.energy_ton_short),
|
||||||
|
MyUnit(MyUnitIDS.kiloton, BigDecimal.valueOf(2.611446262E+31), UnitGroup.ENERGY, R.string.kiloton, R.string.kiloton_short),
|
||||||
|
MyUnit(MyUnitIDS.megaton, BigDecimal.valueOf(2.611446262E+34), UnitGroup.ENERGY, R.string.megaton, R.string.megaton_short),
|
||||||
|
MyUnit(MyUnitIDS.gigaton, BigDecimal.valueOf(2.611446262E+37), UnitGroup.ENERGY, R.string.gigaton, R.string.gigaton_short),
|
||||||
|
MyUnit(MyUnitIDS.energy_horse_power_metric, BigDecimal.valueOf(1.652623245E+25), UnitGroup.ENERGY, R.string.energy_horse_power_metric, R.string.energy_horse_power_metric_short),
|
||||||
|
MyUnit(MyUnitIDS.calorie_th, BigDecimal.valueOf(26_114_462_623_185_002_000.0), UnitGroup.ENERGY, R.string.calorie_th, R.string.calorie_th_short),
|
||||||
|
MyUnit(MyUnitIDS.kilocalorie_th, BigDecimal.valueOf(2.611446262E+22), UnitGroup.ENERGY, R.string.kilocalorie_th, R.string.kilocalorie_th_short),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
val DATA_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
MyUnit(MyUnitIDS.bit, BigDecimal.valueOf(1), UnitGroup.DATA, R.string.bit, R.string.bit_short),
|
||||||
|
MyUnit(MyUnitIDS.kibibit, BigDecimal.valueOf(1_024), UnitGroup.DATA, R.string.kibibit, R.string.kibibit_short),
|
||||||
|
MyUnit(MyUnitIDS.kilobit, BigDecimal.valueOf(1_000), UnitGroup.DATA, R.string.kilobit, R.string.kilobit_short),
|
||||||
|
MyUnit(MyUnitIDS.megabit, BigDecimal.valueOf(1_000_000), UnitGroup.DATA, R.string.megabit, R.string.megabit_short),
|
||||||
|
MyUnit(MyUnitIDS.gigabit, BigDecimal.valueOf(1_000_000_000), UnitGroup.DATA, R.string.gigabit, R.string.gigabit_short),
|
||||||
|
MyUnit(MyUnitIDS.terabit, BigDecimal.valueOf(1_000_000_000_000), UnitGroup.DATA, R.string.terabit, R.string.terabit_short),
|
||||||
|
MyUnit(MyUnitIDS.petabit, BigDecimal.valueOf(1_000_000_000_000_000), UnitGroup.DATA, R.string.petabit, R.string.petabit_short),
|
||||||
|
MyUnit(MyUnitIDS.exabit, BigDecimal.valueOf(1_000_000_000_000_000_000), UnitGroup.DATA, R.string.exabit, R.string.exabit_short),
|
||||||
|
MyUnit(MyUnitIDS.byte, BigDecimal.valueOf(8), UnitGroup.DATA, R.string.byte_, R.string.byte_short),
|
||||||
|
MyUnit(MyUnitIDS.kibibyte, BigDecimal.valueOf(8_192), UnitGroup.DATA, R.string.kibibyte, R.string.kibibyte_short),
|
||||||
|
MyUnit(MyUnitIDS.kilobyte, BigDecimal.valueOf(8_000), UnitGroup.DATA, R.string.kilobyte, R.string.kilobyte_short),
|
||||||
|
MyUnit(MyUnitIDS.megabyte, BigDecimal.valueOf(8_000_000), UnitGroup.DATA, R.string.megabyte, R.string.megabyte_short),
|
||||||
|
MyUnit(MyUnitIDS.gigabyte, BigDecimal.valueOf(8_000_000_000), UnitGroup.DATA, R.string.gigabyte, R.string.gigabyte_short),
|
||||||
|
MyUnit(MyUnitIDS.terabyte, BigDecimal.valueOf(8_000_000_000_000), UnitGroup.DATA, R.string.terabyte, R.string.terabyte_short),
|
||||||
|
MyUnit(MyUnitIDS.petabyte, BigDecimal.valueOf(8_000_000_000_000_000), UnitGroup.DATA, R.string.petabyte, R.string.petabyte_short),
|
||||||
|
MyUnit(MyUnitIDS.exabyte, BigDecimal.valueOf(8_000_000_000_000_000_000), UnitGroup.DATA, R.string.exabyte, R.string.exabyte_short),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
val LENGTH_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
MyUnit(MyUnitIDS.attometer, BigDecimal.valueOf(1.0), UnitGroup.LENGTH, R.string.attometer, R.string.attometer_short),
|
||||||
|
MyUnit(MyUnitIDS.nanometer, BigDecimal.valueOf(1.0E+9), UnitGroup.LENGTH, R.string.nanometer, R.string.nanometer_short),
|
||||||
|
MyUnit(MyUnitIDS.micrometer, BigDecimal.valueOf(1.0E+12), UnitGroup.LENGTH, R.string.micrometer, R.string.micrometer_short),
|
||||||
|
MyUnit(MyUnitIDS.millimeter, BigDecimal.valueOf(1.0E+15), UnitGroup.LENGTH, R.string.millimeter, R.string.millimeter_short),
|
||||||
|
MyUnit(MyUnitIDS.centimeter, BigDecimal.valueOf(1.0E+16), UnitGroup.LENGTH, R.string.centimeter, R.string.centimeter_short),
|
||||||
|
MyUnit(MyUnitIDS.decimeter, BigDecimal.valueOf(1.0E+17), UnitGroup.LENGTH, R.string.decimeter, R.string.decimeter_short),
|
||||||
|
MyUnit(MyUnitIDS.meter, BigDecimal.valueOf(1.0E+18), UnitGroup.LENGTH, R.string.meter, R.string.meter_short),
|
||||||
|
MyUnit(MyUnitIDS.kilometer, BigDecimal.valueOf(1.0E+21), UnitGroup.LENGTH, R.string.kilometer, R.string.kilometer_short),
|
||||||
|
MyUnit(MyUnitIDS.inch, BigDecimal.valueOf(25_400_000_000_000_308), UnitGroup.LENGTH, R.string.inch, R.string.inch_short),
|
||||||
|
MyUnit(MyUnitIDS.foot, BigDecimal.valueOf(304_800_000_000_002_200), UnitGroup.LENGTH, R.string.foot, R.string.foot_short),
|
||||||
|
MyUnit(MyUnitIDS.yard, BigDecimal.valueOf(914_400_000_000_006_400), UnitGroup.LENGTH, R.string.yard, R.string.yard_short),
|
||||||
|
MyUnit(MyUnitIDS.mile, BigDecimal.valueOf(1_609_344_000_000_010_500_000.0), UnitGroup.LENGTH, R.string.mile, R.string.mile_short),
|
||||||
|
MyUnit(MyUnitIDS.light_year, BigDecimal.valueOf(9.460730472E+33), UnitGroup.LENGTH, R.string.light_year, R.string.light_year_short),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
val MASS_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
MyUnit(MyUnitIDS.electron_mass_rest, BigDecimal.valueOf(1), UnitGroup.MASS, R.string.electron_mass_rest, R.string.electron_mass_rest_short),
|
||||||
|
MyUnit(MyUnitIDS.atomic_mass_unit, BigDecimal.valueOf(1_822.888530062548), UnitGroup.MASS, R.string.atomic_mass_unit, R.string.atomic_mass_unit_short),
|
||||||
|
MyUnit(MyUnitIDS.milligram, BigDecimal.valueOf(1.0977683828808E+24), UnitGroup.MASS, R.string.milligram, R.string.milligram_short),
|
||||||
|
MyUnit(MyUnitIDS.gram, BigDecimal.valueOf(1.0977683828808E+27), UnitGroup.MASS, R.string.gram, R.string.gram_short),
|
||||||
|
MyUnit(MyUnitIDS.kilogram, BigDecimal.valueOf(1.0977683828808E+30), UnitGroup.MASS, R.string.kilogram, R.string.kilogram_short),
|
||||||
|
MyUnit(MyUnitIDS.metric_ton, BigDecimal.valueOf(1.0977683828808E+33), UnitGroup.MASS, R.string.metric_ton, R.string.metric_ton_short),
|
||||||
|
MyUnit(MyUnitIDS.imperial_ton, BigDecimal.valueOf(1.1153841720044124E+33), UnitGroup.MASS, R.string.imperial_ton, R.string.imperial_ton_short),
|
||||||
|
MyUnit(MyUnitIDS.ounce, BigDecimal.valueOf(3.1121210156373456E+28), UnitGroup.MASS, R.string.ounce, R.string.ounce_short),
|
||||||
|
MyUnit(MyUnitIDS.carat, BigDecimal.valueOf(2.1955367657615996E+26), UnitGroup.MASS, R.string.carat, R.string.carat_short),
|
||||||
|
MyUnit(MyUnitIDS.pound, BigDecimal.valueOf(4.979393625019642E+29), UnitGroup.MASS, R.string.pound, R.string.pound_short),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
val POWER_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
MyUnit(MyUnitIDS.attowatt, BigDecimal.valueOf(1), UnitGroup.POWER, R.string.attowatt, R.string.attowatt_short),
|
||||||
|
MyUnit(MyUnitIDS.watt, BigDecimal.valueOf(1_000_000_000_000_000_000), UnitGroup.POWER, R.string.watt, R.string.watt_short),
|
||||||
|
MyUnit(MyUnitIDS.kilowatt, BigDecimal.valueOf(1.0E+21), UnitGroup.POWER, R.string.kilowatt, R.string.kilowatt_short),
|
||||||
|
MyUnit(MyUnitIDS.megawatt, BigDecimal.valueOf(1.0E+24), UnitGroup.POWER, R.string.megawatt, R.string.megawatt_short),
|
||||||
|
MyUnit(MyUnitIDS.horse_power_mechanical, BigDecimal.valueOf(745_699_871_582_285_700_000.0), UnitGroup.POWER, R.string.horse_power_mechanical, R.string.horse_power_mechanical_short),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
val SPEED_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
MyUnit(MyUnitIDS.millimeter_per_hour, BigDecimal.valueOf(1), UnitGroup.SPEED, R.string.millimeter_per_hour, R.string.millimeter_per_hour_short),
|
||||||
|
MyUnit(MyUnitIDS.millimeter_per_minute, BigDecimal.valueOf(60), UnitGroup.SPEED, R.string.millimeter_per_minute, R.string.millimeter_per_minute_short),
|
||||||
|
MyUnit(MyUnitIDS.millimeter_per_second, BigDecimal.valueOf(3_600), UnitGroup.SPEED, R.string.millimeter_per_second, R.string.millimeter_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.centimeter_per_hour, BigDecimal.valueOf(10), UnitGroup.SPEED, R.string.centimeter_per_hour, R.string.centimeter_per_hour_short),
|
||||||
|
MyUnit(MyUnitIDS.centimeter_per_minute, BigDecimal.valueOf(600), UnitGroup.SPEED, R.string.centimeter_per_minute, R.string.centimeter_per_minute_short),
|
||||||
|
MyUnit(MyUnitIDS.centimeter_per_second, BigDecimal.valueOf(36_000), UnitGroup.SPEED, R.string.centimeter_per_second, R.string.centimeter_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.meter_per_hour, BigDecimal.valueOf(1_000), UnitGroup.SPEED, R.string.meter_per_hour, R.string.meter_per_hour_short),
|
||||||
|
MyUnit(MyUnitIDS.meter_per_minute, BigDecimal.valueOf(60_000), UnitGroup.SPEED, R.string.meter_per_minute, R.string.meter_per_minute_short),
|
||||||
|
MyUnit(MyUnitIDS.meter_per_second, BigDecimal.valueOf(3_600_000), UnitGroup.SPEED, R.string.meter_per_second, R.string.meter_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.kilometer_per_hour, BigDecimal.valueOf(1_000_000), UnitGroup.SPEED, R.string.kilometer_per_hour, R.string.kilometer_per_hour_short),
|
||||||
|
MyUnit(MyUnitIDS.kilometer_per_minute, BigDecimal.valueOf(60_000_000), UnitGroup.SPEED, R.string.kilometer_per_minute, R.string.kilometer_per_minute_short),
|
||||||
|
MyUnit(MyUnitIDS.kilometer_per_second, BigDecimal.valueOf(3_600_000_000), UnitGroup.SPEED, R.string.kilometer_per_second, R.string.kilometer_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.foot_per_hour, BigDecimal.valueOf(304), UnitGroup.SPEED, R.string.foot_per_hour, R.string.foot_per_hour_short),
|
||||||
|
MyUnit(MyUnitIDS.foot_per_minute, BigDecimal.valueOf(18_288), UnitGroup.SPEED, R.string.foot_per_minute, R.string.foot_per_minute_short),
|
||||||
|
MyUnit(MyUnitIDS.foot_per_second, BigDecimal.valueOf(1_097_280), UnitGroup.SPEED, R.string.foot_per_second, R.string.foot_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.yard_per_hour, BigDecimal.valueOf(914.4), UnitGroup.SPEED, R.string.yard_per_hour, R.string.yard_per_hour_short),
|
||||||
|
MyUnit(MyUnitIDS.yard_per_minute, BigDecimal.valueOf(54_864), UnitGroup.SPEED, R.string.yard_per_minute, R.string.yard_per_minute_short),
|
||||||
|
MyUnit(MyUnitIDS.yard_per_second, BigDecimal.valueOf(3_291_840), UnitGroup.SPEED, R.string.yard_per_second, R.string.yard_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.mile_per_hour, BigDecimal.valueOf(1_609_344), UnitGroup.SPEED, R.string.mile_per_hour, R.string.mile_per_hour_short),
|
||||||
|
MyUnit(MyUnitIDS.mile_per_minute, BigDecimal.valueOf(96_560_640), UnitGroup.SPEED, R.string.mile_per_minute, R.string.mile_per_minute_short),
|
||||||
|
MyUnit(MyUnitIDS.mile_per_second, BigDecimal.valueOf(5_793_638_400), UnitGroup.SPEED, R.string.mile_per_second, R.string.mile_per_second_short),
|
||||||
|
MyUnit(MyUnitIDS.knot, BigDecimal.valueOf(1_852_000), UnitGroup.SPEED, R.string.knot, R.string.knot_short),
|
||||||
|
MyUnit(MyUnitIDS.velocity_of_light_in_vacuum, BigDecimal.valueOf(1_079_252_848_799_998), UnitGroup.SPEED, R.string.velocity_of_light_in_vacuum, R.string.velocity_of_light_in_vacuum_short),
|
||||||
|
MyUnit(MyUnitIDS.cosmic_velocity_first, BigDecimal.valueOf(28_440_000_000), UnitGroup.SPEED, R.string.cosmic_velocity_first, R.string.cosmic_velocity_first_short),
|
||||||
|
MyUnit(MyUnitIDS.cosmic_velocity_second, BigDecimal.valueOf(40_320_000_000), UnitGroup.SPEED, R.string.cosmic_velocity_second, R.string.cosmic_velocity_second_short),
|
||||||
|
MyUnit(MyUnitIDS.cosmic_velocity_third, BigDecimal.valueOf(60_012_000_000), UnitGroup.SPEED, R.string.cosmic_velocity_third, R.string.cosmic_velocity_third_short),
|
||||||
|
MyUnit(MyUnitIDS.earths_orbital_speed, BigDecimal.valueOf(107_208_000_000), UnitGroup.SPEED, R.string.earths_orbital_speed, R.string.earths_orbital_speed_short),
|
||||||
|
MyUnit(MyUnitIDS.mach, BigDecimal.valueOf(1_236_960_000), UnitGroup.SPEED, R.string.mach, R.string.mach_short),
|
||||||
|
MyUnit(MyUnitIDS.mach_si_standard, BigDecimal.valueOf(1_062_167_040), UnitGroup.SPEED, R.string.mach_si_standard, R.string.mach_si_standard_short),)
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.preferences.MAX_PRECISION
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import com.sadellie.unitto.screens.setMinimumRequiredScale
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.math.RoundingMode
|
||||||
|
|
||||||
|
val TEMPERATURE_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
object : AbstractUnit(
|
||||||
|
unitId = MyUnitIDS.celsius,
|
||||||
|
basicUnit = BigDecimal.ONE,
|
||||||
|
group = UnitGroup.TEMPERATURE,
|
||||||
|
displayName = R.string.celsius,
|
||||||
|
shortName = R.string.celsius_short,
|
||||||
|
) {
|
||||||
|
override fun convert(unitTo: AbstractUnit, value: BigDecimal, scale: Int): BigDecimal {
|
||||||
|
return when (unitTo.unitId) {
|
||||||
|
MyUnitIDS.fahrenheit -> {
|
||||||
|
value
|
||||||
|
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
|
||||||
|
.times(BigDecimal.valueOf(1.8))
|
||||||
|
.plus(BigDecimal(32))
|
||||||
|
}
|
||||||
|
MyUnitIDS.kelvin -> {
|
||||||
|
value
|
||||||
|
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
|
||||||
|
.plus(BigDecimal.valueOf(273.15))
|
||||||
|
}
|
||||||
|
else -> value
|
||||||
|
}
|
||||||
|
.setMinimumRequiredScale(scale)
|
||||||
|
.stripTrailingZeros()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
object : AbstractUnit(
|
||||||
|
unitId = MyUnitIDS.fahrenheit,
|
||||||
|
basicUnit = BigDecimal.ONE,
|
||||||
|
group = UnitGroup.TEMPERATURE,
|
||||||
|
displayName = R.string.fahrenheit,
|
||||||
|
shortName = R.string.fahrenheit_short,
|
||||||
|
) {
|
||||||
|
override fun convert(unitTo: AbstractUnit, value: BigDecimal, scale: Int): BigDecimal {
|
||||||
|
return when (unitTo.unitId) {
|
||||||
|
MyUnitIDS.celsius -> {
|
||||||
|
value
|
||||||
|
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
|
||||||
|
.minus(BigDecimal(32))
|
||||||
|
.times(BigDecimal(5))
|
||||||
|
.div(BigDecimal(9))
|
||||||
|
}
|
||||||
|
MyUnitIDS.kelvin -> {
|
||||||
|
value
|
||||||
|
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
|
||||||
|
.minus(BigDecimal(32))
|
||||||
|
.times(BigDecimal(5))
|
||||||
|
.div(BigDecimal(9))
|
||||||
|
.add(BigDecimal.valueOf(273.15))
|
||||||
|
}
|
||||||
|
else -> value
|
||||||
|
}
|
||||||
|
.setMinimumRequiredScale(scale)
|
||||||
|
.stripTrailingZeros()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
object : AbstractUnit(
|
||||||
|
unitId = MyUnitIDS.kelvin,
|
||||||
|
basicUnit = BigDecimal.ONE,
|
||||||
|
group = UnitGroup.TEMPERATURE,
|
||||||
|
displayName = R.string.kelvin,
|
||||||
|
shortName = R.string.kelvin_short,
|
||||||
|
) {
|
||||||
|
override fun convert(unitTo: AbstractUnit, value: BigDecimal, scale: Int): BigDecimal {
|
||||||
|
return when (unitTo.unitId) {
|
||||||
|
MyUnitIDS.celsius -> {
|
||||||
|
value
|
||||||
|
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
|
||||||
|
.minus(BigDecimal(273.15))
|
||||||
|
}
|
||||||
|
MyUnitIDS.fahrenheit -> {
|
||||||
|
value
|
||||||
|
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
|
||||||
|
.minus(BigDecimal.valueOf(273.15))
|
||||||
|
.times(BigDecimal.valueOf(1.8))
|
||||||
|
.plus(BigDecimal(32))
|
||||||
|
}
|
||||||
|
else -> value
|
||||||
|
}
|
||||||
|
.setMinimumRequiredScale(scale)
|
||||||
|
.stripTrailingZeros()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
val TIME_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
MyUnit(MyUnitIDS.attosecond, BigDecimal.valueOf(1), UnitGroup.TIME, R.string.attosecond, R.string.attosecond_short),
|
||||||
|
MyUnit(MyUnitIDS.nanosecond, BigDecimal.valueOf(1_000_000_000), UnitGroup.TIME, R.string.nanosecond, R.string.nanosecond_short),
|
||||||
|
MyUnit(MyUnitIDS.microsecond, BigDecimal.valueOf(1_000_000_000_000), UnitGroup.TIME, R.string.microsecond, R.string.microsecond_short),
|
||||||
|
MyUnit(MyUnitIDS.millisecond, BigDecimal.valueOf(1_000_000_000_000_000), UnitGroup.TIME, R.string.millisecond, R.string.millisecond_short),
|
||||||
|
MyUnit(MyUnitIDS.second, BigDecimal.valueOf(1_000_000_000_000_000_000), UnitGroup.TIME, R.string.second, R.string.second_short),
|
||||||
|
MyUnit(MyUnitIDS.minute, BigDecimal.valueOf(60_000_000_000_000_000_000.0), UnitGroup.TIME, R.string.minute, R.string.minute_short),
|
||||||
|
MyUnit(MyUnitIDS.hour, BigDecimal.valueOf(3_600_000_000_000_000_000_000.0), UnitGroup.TIME, R.string.hour, R.string.hour_short),
|
||||||
|
MyUnit(MyUnitIDS.day, BigDecimal.valueOf(86_400_000_000_000_000_000_000.0), UnitGroup.TIME, R.string.day, R.string.day_short),
|
||||||
|
MyUnit(MyUnitIDS.week, BigDecimal.valueOf(604_800_000_000_000_000_000_000.0), UnitGroup.TIME, R.string.week, R.string.week_short),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.sadellie.unitto.data.units.collections
|
||||||
|
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
val VOLUME_COLLECTION: List<AbstractUnit> by lazy {
|
||||||
|
listOf(
|
||||||
|
MyUnit(MyUnitIDS.attoliter, BigDecimal.valueOf(1), UnitGroup.VOLUME, R.string.attoliter, R.string.attoliter_short),
|
||||||
|
MyUnit(MyUnitIDS.milliliter, BigDecimal.valueOf(1_000_000_000_000_000), UnitGroup.VOLUME, R.string.milliliter, R.string.milliliter_short),
|
||||||
|
MyUnit(MyUnitIDS.liter, BigDecimal.valueOf(1_000_000_000_000_000_000), UnitGroup.VOLUME, R.string.liter, R.string.liter_short),
|
||||||
|
MyUnit(MyUnitIDS.us_liquid_gallon, BigDecimal.valueOf(3_785_411_783_999_977_000), UnitGroup.VOLUME, R.string.us_liquid_gallon, R.string.us_liquid_gallon_short),
|
||||||
|
MyUnit(MyUnitIDS.us_liquid_quart, BigDecimal.valueOf(946_352_945_999_994_200), UnitGroup.VOLUME, R.string.us_liquid_quart, R.string.us_liquid_quart_short),
|
||||||
|
MyUnit(MyUnitIDS.us_liquid_pint, BigDecimal.valueOf(473_176_472_999_997_100), UnitGroup.VOLUME, R.string.us_liquid_pint, R.string.us_liquid_pint_short),
|
||||||
|
MyUnit(MyUnitIDS.us_legal_cup, BigDecimal.valueOf(236_588_236_499_998_560), UnitGroup.VOLUME, R.string.us_legal_cup, R.string.us_legal_cup_short),
|
||||||
|
MyUnit(MyUnitIDS.us_fluid_ounce, BigDecimal.valueOf(29_573_529_562_499_996), UnitGroup.VOLUME, R.string.us_fluid_ounce, R.string.us_fluid_ounce_short),
|
||||||
|
MyUnit(MyUnitIDS.us_tablespoon, BigDecimal.valueOf(14_786_764_781_249_998), UnitGroup.VOLUME, R.string.us_tablespoon, R.string.us_tablespoon_short),
|
||||||
|
MyUnit(MyUnitIDS.us_teaspoon, BigDecimal.valueOf(4_928_921_593_749_952), UnitGroup.VOLUME, R.string.us_teaspoon, R.string.us_teaspoon_short),
|
||||||
|
MyUnit(MyUnitIDS.imperial_gallon, BigDecimal.valueOf(4_546_089_999_999_954_400), UnitGroup.VOLUME, R.string.imperial_gallon, R.string.imperial_gallon_short),
|
||||||
|
MyUnit(MyUnitIDS.imperial_quart, BigDecimal.valueOf(1_136_522_500_000_001_400), UnitGroup.VOLUME, R.string.imperial_quart, R.string.imperial_quart_short),
|
||||||
|
MyUnit(MyUnitIDS.imperial_pint, BigDecimal.valueOf(568_261_250_000_000_700), UnitGroup.VOLUME, R.string.imperial_pint, R.string.imperial_pint_short),
|
||||||
|
MyUnit(MyUnitIDS.imperial_cup, BigDecimal.valueOf(284_130_625_000_000_350), UnitGroup.VOLUME, R.string.imperial_cup, R.string.imperial_cup_short),
|
||||||
|
MyUnit(MyUnitIDS.imperial_fluid_ounce, BigDecimal.valueOf(28_413_062_500_000_036), UnitGroup.VOLUME, R.string.imperial_fluid_ounce, R.string.imperial_fluid_ounce_short),
|
||||||
|
MyUnit(MyUnitIDS.imperial_tablespoon, BigDecimal.valueOf(17_758_164_062_500_148), UnitGroup.VOLUME, R.string.imperial_tablespoon, R.string.imperial_tablespoon_short),
|
||||||
|
MyUnit(MyUnitIDS.imperial_teaspoon, BigDecimal.valueOf(5_919_388_020_833_314), UnitGroup.VOLUME, R.string.imperial_teaspoon, R.string.imperial_teaspoon_short),
|
||||||
|
MyUnit(MyUnitIDS.cubic_millimeter, BigDecimal.valueOf(1_000_000_000_000), UnitGroup.VOLUME, R.string.cubic_millimeter, R.string.cubic_millimeter_short),
|
||||||
|
MyUnit(MyUnitIDS.cubic_centimeter, BigDecimal.valueOf(1_000_000_000_000_000), UnitGroup.VOLUME, R.string.cubic_centimeter, R.string.cubic_centimeter_short),
|
||||||
|
MyUnit(MyUnitIDS.cubic_meter, BigDecimal.valueOf(1.0E+21), UnitGroup.VOLUME, R.string.cubic_meter, R.string.cubic_meter_short),
|
||||||
|
MyUnit(MyUnitIDS.cubic_kilometer, BigDecimal.valueOf(1.0E+30), UnitGroup.VOLUME, R.string.cubic_kilometer, R.string.cubic_kilometer_short),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.sadellie.unitto.data.units.database
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents one Row in units table in database
|
||||||
|
*
|
||||||
|
* @param isFavorite Is this Unit favorite? Used in a favorites filter in units list
|
||||||
|
* @param pairedUnitId Latest unitId of a unit on the right side.
|
||||||
|
* @param frequency Show the amount of time this unit was used
|
||||||
|
*/
|
||||||
|
@Entity(tableName = "units")
|
||||||
|
class MyBasedUnit(
|
||||||
|
@PrimaryKey val unitId: String,
|
||||||
|
@ColumnInfo(name = "is_favorite") val isFavorite: Boolean?,
|
||||||
|
@ColumnInfo(name = "paired_unit_id") val pairedUnitId: String?,
|
||||||
|
@ColumnInfo(name = "frequency") val frequency: Int? = 0,
|
||||||
|
)
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.sadellie.unitto.data.units.database
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface MyBasedUnitDao {
|
||||||
|
@Query("SELECT * FROM units")
|
||||||
|
suspend fun getAll(): List<MyBasedUnit>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insertUnits(vararg units: MyBasedUnit)
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.sadellie.unitto.data.units.database
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
|
||||||
|
@Database(entities = [MyBasedUnit::class], version = 1, exportSchema = false)
|
||||||
|
abstract class MyBasedUnitDatabase : RoomDatabase() {
|
||||||
|
abstract fun myBasedUnitDao(): MyBasedUnitDao
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Volatile
|
||||||
|
private var INSTANCE: MyBasedUnitDatabase? = null
|
||||||
|
|
||||||
|
fun getDatabase(context: Context): MyBasedUnitDatabase {
|
||||||
|
return INSTANCE ?: synchronized(this) {
|
||||||
|
val instance = Room.databaseBuilder(
|
||||||
|
context.applicationContext,
|
||||||
|
MyBasedUnitDatabase::class.java,
|
||||||
|
"unitto_database"
|
||||||
|
).build()
|
||||||
|
INSTANCE = instance
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.sadellie.unitto.data.units.remote
|
||||||
|
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Path
|
||||||
|
|
||||||
|
private const val BASE_URL = "https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/"
|
||||||
|
|
||||||
|
private val moshi = Moshi.Builder()
|
||||||
|
.add(CurrencyAdapter())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val retrofit = Retrofit.Builder()
|
||||||
|
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||||
|
.baseUrl(BASE_URL)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
interface CurrencyApiService {
|
||||||
|
/**
|
||||||
|
* Gets paired currencies for the given currency
|
||||||
|
*
|
||||||
|
* @param baseCurrency Left side unit
|
||||||
|
* @return Call response with date and currencies
|
||||||
|
*/
|
||||||
|
@GET("{baseCurrency}.json")
|
||||||
|
suspend fun getCurrencyPairs(
|
||||||
|
@Path("baseCurrency") baseCurrency: String
|
||||||
|
) : CurrencyUnitResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
object CurrencyApi {
|
||||||
|
val retrofitService: CurrencyApiService by lazy { retrofit.create(CurrencyApiService::class.java) }
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.sadellie.unitto.data.units.remote
|
||||||
|
|
||||||
|
import com.squareup.moshi.FromJson
|
||||||
|
import com.squareup.moshi.ToJson
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a response object from currency-api
|
||||||
|
*
|
||||||
|
* @property date Date when this information (rates) was collected
|
||||||
|
* @property currency Map with currencies and basicUnits for right side units
|
||||||
|
*/
|
||||||
|
data class CurrencyUnitResponse(
|
||||||
|
val date: String,
|
||||||
|
val currency: Map<String, BigDecimal>
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom parser because API has a weird json structure (dynamic field names)
|
||||||
|
*/
|
||||||
|
class CurrencyAdapter {
|
||||||
|
@Suppress("UNUSED", "UNUSED_PARAMETER")
|
||||||
|
@ToJson fun toJson(card: CurrencyUnitResponse): String? = null
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@FromJson fun fromJson(response: Map<String, Any>): CurrencyUnitResponse {
|
||||||
|
val pairsBD: Map<String, BigDecimal> = (response[response.keys.elementAt(1)] as Map<String, Double>)
|
||||||
|
.mapValues { it.value.toBigDecimal() }
|
||||||
|
|
||||||
|
return CurrencyUnitResponse(
|
||||||
|
date = response["date"] as String,
|
||||||
|
currency = pairsBD
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.sadellie.unitto.screens
|
||||||
|
|
||||||
|
import com.sadellie.unitto.data.KEY_0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents current state of the MainScreen
|
||||||
|
*
|
||||||
|
* @property inputValue Current input value
|
||||||
|
* @property resultValue Current output value
|
||||||
|
* @property deleteButtonEnabled Delete last symbol from input button state
|
||||||
|
* @property dotButtonEnabled Add dot to input button state
|
||||||
|
* @property negateButtonEnabled Switch input between positive and negative button state
|
||||||
|
* @property isLoadingDataStore Whether we are loading data from DataStore. Need on app launch
|
||||||
|
* @property isLoadingNetwork Whether we are loading data from network
|
||||||
|
* @property showError Whether there was an error while loading data from network
|
||||||
|
*/
|
||||||
|
data class MainScreenUIState(
|
||||||
|
var inputValue: String = KEY_0,
|
||||||
|
var resultValue: String = KEY_0,
|
||||||
|
var deleteButtonEnabled: Boolean = false,
|
||||||
|
var dotButtonEnabled: Boolean = true,
|
||||||
|
var negateButtonEnabled: Boolean = false,
|
||||||
|
var isLoadingDataStore: Boolean = true,
|
||||||
|
var isLoadingNetwork: Boolean = false,
|
||||||
|
var showError: Boolean = false,
|
||||||
|
)
|
535
app/src/main/java/com/sadellie/unitto/screens/MainViewModel.kt
Normal file
@ -0,0 +1,535 @@
|
|||||||
|
package com.sadellie.unitto.screens
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
|
import com.sadellie.unitto.data.KEY_0
|
||||||
|
import com.sadellie.unitto.data.KEY_DOT
|
||||||
|
import com.sadellie.unitto.data.KEY_MINUS
|
||||||
|
import com.sadellie.unitto.data.preferences.*
|
||||||
|
import com.sadellie.unitto.data.units.ALL_UNITS
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import com.sadellie.unitto.data.units.collections.CURRENCY_COLLECTION
|
||||||
|
import com.sadellie.unitto.data.units.database.MyBasedUnit
|
||||||
|
import com.sadellie.unitto.data.units.database.MyBasedUnitDao
|
||||||
|
import com.sadellie.unitto.data.units.database.MyBasedUnitDatabase
|
||||||
|
import com.sadellie.unitto.data.units.remote.CurrencyApi
|
||||||
|
import com.sadellie.unitto.data.units.remote.CurrencyUnitResponse
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class MainViewModel @Inject constructor(
|
||||||
|
private val mySettingsPrefs: UserPreferences,
|
||||||
|
private val application: Application
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App database
|
||||||
|
*/
|
||||||
|
private val myBasedUnitDao: MyBasedUnitDao =
|
||||||
|
MyBasedUnitDatabase.getDatabase(application).myBasedUnitDao()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APP THEME
|
||||||
|
*/
|
||||||
|
val currentAppTheme =
|
||||||
|
mySettingsPrefs.getItem(UserPreferenceKeys.CURRENT_APP_THEME, AppTheme.AUTO)
|
||||||
|
|
||||||
|
fun saveCurrentAppTheme(value: Int) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
mySettingsPrefs.saveInt(key = UserPreferenceKeys.CURRENT_APP_THEME, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CONVERSION PRECISION
|
||||||
|
*/
|
||||||
|
var precision: Int by mutableStateOf(0)
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun setPrecisionPref(value: Int) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
precision = value
|
||||||
|
mySettingsPrefs.saveInt(UserPreferenceKeys.DIGITS_PRECISION, value)
|
||||||
|
convertValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SEPARATOR
|
||||||
|
*/
|
||||||
|
var separator: Int by mutableStateOf(0)
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun setSeparatorPref(value: Int) {
|
||||||
|
separator = value
|
||||||
|
viewModelScope.launch {
|
||||||
|
Formatter.setSeparator(value)
|
||||||
|
mySettingsPrefs.saveInt(UserPreferenceKeys.SEPARATOR, value)
|
||||||
|
convertValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OUTPUT FORMAT
|
||||||
|
*/
|
||||||
|
var outputFormat: Int by mutableStateOf(0)
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets given output format and saves it in user preference store
|
||||||
|
* @param value [OutputFormat] to set
|
||||||
|
*/
|
||||||
|
fun setOutputFormatPref(value: Int) {
|
||||||
|
// Updating value in memory
|
||||||
|
outputFormat = value
|
||||||
|
// Updating value on disk
|
||||||
|
viewModelScope.launch {
|
||||||
|
mySettingsPrefs.saveInt(UserPreferenceKeys.OUTPUT_FORMAT, value)
|
||||||
|
convertValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit we converting from (left side)
|
||||||
|
*/
|
||||||
|
var unitFrom: AbstractUnit by mutableStateOf(ALL_UNITS[0])
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit we are converting to (right side)
|
||||||
|
*/
|
||||||
|
var unitTo: AbstractUnit by mutableStateOf(ALL_UNITS[1])
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI state
|
||||||
|
*/
|
||||||
|
var mainUIState: MainScreenUIState by mutableStateOf(MainScreenUIState())
|
||||||
|
private set
|
||||||
|
|
||||||
|
var favoritesOnly: Boolean by mutableStateOf(false)
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun toggleFavoritesOnly() {
|
||||||
|
favoritesOnly = !favoritesOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a grouped list of units that is used for unit selection screen
|
||||||
|
var unitsToShow: Map<UnitGroup, List<AbstractUnit>> by mutableStateOf(emptyMap())
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function takes local variables, converts values and then causes the UI to update
|
||||||
|
*/
|
||||||
|
private fun convertValue() {
|
||||||
|
// We cannot convert values, as we are still user prefs from datastore (precision)
|
||||||
|
if (mainUIState.isLoadingDataStore) return
|
||||||
|
// Converting value using a specified precision
|
||||||
|
val convertedValue: BigDecimal =
|
||||||
|
unitFrom.convert(unitTo, mainUIState.inputValue.toBigDecimal(), precision)
|
||||||
|
// Setting result value using a specified OutputFormat
|
||||||
|
mainUIState = mainUIState.copy(
|
||||||
|
resultValue = when (outputFormat) {
|
||||||
|
OutputFormat.ALLOW_ENGINEERING -> convertedValue.toString()
|
||||||
|
OutputFormat.FORCE_ENGINEERING -> convertedValue.toEngineeringString()
|
||||||
|
else -> convertedValue.toPlainString()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change left side unit. Unit to convert from
|
||||||
|
*
|
||||||
|
* @param clickedUnit Unit we need to change to
|
||||||
|
*/
|
||||||
|
fun changeUnitFrom(clickedUnit: AbstractUnit) {
|
||||||
|
// First we change unit
|
||||||
|
unitFrom = clickedUnit
|
||||||
|
|
||||||
|
// Now we check for negate button
|
||||||
|
mainUIState = mainUIState.copy(negateButtonEnabled = clickedUnit.group.canNegate)
|
||||||
|
// Now we change to positive if the group we switched to supports negate
|
||||||
|
if (!clickedUnit.group.canNegate) {
|
||||||
|
mainUIState =
|
||||||
|
mainUIState.copy(inputValue = mainUIState.inputValue.removePrefix(KEY_MINUS))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now setting up right unit (pair for the left one)
|
||||||
|
unitTo = ALL_UNITS.first {
|
||||||
|
if (unitFrom.pairedUnit.isNullOrEmpty()) {
|
||||||
|
// No pair. Just getting unit from same group
|
||||||
|
it.group == unitFrom.group
|
||||||
|
} else {
|
||||||
|
// There is a paired unit
|
||||||
|
it.unitId == unitFrom.pairedUnit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
// We need to increment counter for the clicked unit
|
||||||
|
incrementCounter(clickedUnit)
|
||||||
|
// Currencies require us to get data from the internet
|
||||||
|
updateCurrenciesBasicUnits()
|
||||||
|
// We can't call outside of this block. It will set precision to 0 in that case
|
||||||
|
convertValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change right side unit. Unit to convert to
|
||||||
|
*
|
||||||
|
* @param clickedUnit Unit we need to change to
|
||||||
|
*/
|
||||||
|
fun changeUnitTo(clickedUnit: AbstractUnit) {
|
||||||
|
// First we change unit
|
||||||
|
unitTo = clickedUnit
|
||||||
|
// Updating paired unit for left side unit in memory (same thing for database below)
|
||||||
|
unitFrom.pairedUnit = unitTo.unitId
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
// Updating paired unit for left side unit in database
|
||||||
|
myBasedUnitDao.insertUnits(
|
||||||
|
MyBasedUnit(
|
||||||
|
unitId = unitFrom.unitId,
|
||||||
|
isFavorite = unitFrom.isFavorite,
|
||||||
|
pairedUnitId = unitFrom.pairedUnit,
|
||||||
|
frequency = unitFrom.counter
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// We also need to increment counter for the selected unit
|
||||||
|
incrementCounter(clickedUnit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changed units, now we can convert
|
||||||
|
convertValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun incrementCounter(unit: AbstractUnit) {
|
||||||
|
myBasedUnitDao.insertUnits(
|
||||||
|
MyBasedUnit(
|
||||||
|
unitId = unit.unitId,
|
||||||
|
isFavorite = unit.isFavorite,
|
||||||
|
pairedUnitId = unit.pairedUnit,
|
||||||
|
// This will increment counter on unit in list too
|
||||||
|
frequency = ++unit.counter
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates basic units properties for all currencies. Uses [unitFrom]
|
||||||
|
*/
|
||||||
|
private suspend fun updateCurrenciesBasicUnits() {
|
||||||
|
// Resetting error and network loading states in case we are not gonna do anything below
|
||||||
|
mainUIState = mainUIState.copy(isLoadingNetwork = false, showError = false)
|
||||||
|
// We update currencies only when needed
|
||||||
|
if (unitFrom.group != UnitGroup.CURRENCY) return
|
||||||
|
|
||||||
|
// Starting to load stuff
|
||||||
|
mainUIState = mainUIState.copy(isLoadingNetwork = true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
val pairs: CurrencyUnitResponse =
|
||||||
|
CurrencyApi.retrofitService.getCurrencyPairs(unitFrom.unitId)
|
||||||
|
CURRENCY_COLLECTION.forEach {
|
||||||
|
// Getting rates from map. We set ZERO as default so that it can be skipped
|
||||||
|
val rate = pairs.currency.getOrElse(it.unitId) { BigDecimal.ZERO }
|
||||||
|
// We make sure that we don't divide by zero
|
||||||
|
if (rate > BigDecimal.ZERO) {
|
||||||
|
it.isEnabled = true
|
||||||
|
it.basicUnit = BigDecimal.ONE.setScale(MAX_PRECISION).div(rate)
|
||||||
|
} else {
|
||||||
|
// Hiding broken currencies
|
||||||
|
it.isEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
when (e) {
|
||||||
|
// 403, Network and Adapter exceptions can be ignored
|
||||||
|
is retrofit2.HttpException, is java.net.UnknownHostException, is com.squareup.moshi.JsonDataException -> {}
|
||||||
|
else -> {
|
||||||
|
// Unexpected exception, should report it
|
||||||
|
FirebaseCrashlytics.getInstance().recordException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mainUIState = mainUIState.copy(showError = true)
|
||||||
|
} finally {
|
||||||
|
// Loaded
|
||||||
|
mainUIState = mainUIState.copy(isLoadingNetwork = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swaps measurement, left to right and vice versa
|
||||||
|
*/
|
||||||
|
fun swapUnits() {
|
||||||
|
unitFrom = unitTo.also {
|
||||||
|
unitTo = unitFrom
|
||||||
|
}
|
||||||
|
viewModelScope.launch { updateCurrenciesBasicUnits() }
|
||||||
|
// Swapped, can convert now
|
||||||
|
convertValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to process input when we click keyboard. Make sure that digits/symbols will be
|
||||||
|
* added properly
|
||||||
|
* @param[digitToAdd] Digit/Symbol we want to add, can be any digit 0..9 or a dot symbol
|
||||||
|
*/
|
||||||
|
fun processInput(digitToAdd: String) {
|
||||||
|
when (digitToAdd) {
|
||||||
|
KEY_DOT -> {
|
||||||
|
// Here we add a dot to input
|
||||||
|
// Disabling dot button to avoid multiple dots in input value
|
||||||
|
// Enabling delete button to so that we can delete this dot from input
|
||||||
|
mainUIState = mainUIState.copy(
|
||||||
|
inputValue = mainUIState.inputValue + digitToAdd,
|
||||||
|
dotButtonEnabled = false,
|
||||||
|
deleteButtonEnabled = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
KEY_0 -> {
|
||||||
|
// We shouldn't add zero to another zero in input, i.e. 00
|
||||||
|
if (mainUIState.inputValue != KEY_0) {
|
||||||
|
mainUIState = mainUIState.copy(inputValue = mainUIState.inputValue + digitToAdd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
/*
|
||||||
|
We want to add digit to input.
|
||||||
|
When there is just a zero, we should replace it with the digit we want to add,
|
||||||
|
avoids input to be like 03 (with this check it will be just 3)
|
||||||
|
*/
|
||||||
|
mainUIState = mainUIState.copy(
|
||||||
|
inputValue = if (mainUIState.inputValue == KEY_0) digitToAdd else mainUIState.inputValue + digitToAdd,
|
||||||
|
deleteButtonEnabled = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
convertValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes last symbol from input and handles buttons state (enabled/disabled)
|
||||||
|
*/
|
||||||
|
fun deleteDigit() {
|
||||||
|
// Last symbol is a dot
|
||||||
|
// We enable DOT button
|
||||||
|
if (mainUIState.inputValue.endsWith(KEY_DOT)) {
|
||||||
|
mainUIState = mainUIState.copy(dotButtonEnabled = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deleting last symbol
|
||||||
|
mainUIState = mainUIState.copy(inputValue = mainUIState.inputValue.dropLast(1))
|
||||||
|
|
||||||
|
/*
|
||||||
|
Now we check what we have left
|
||||||
|
We deleted last symbol and we got Empty string, just minus symbol, or zero
|
||||||
|
Do not allow deleting anything beyond this (disable button)
|
||||||
|
Set input to default (zero)
|
||||||
|
Skipping this block means that we are left we acceptable value, i.e. 123.03
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
mainUIState.inputValue in listOf(String(), KEY_MINUS, KEY_0)
|
||||||
|
) {
|
||||||
|
mainUIState = mainUIState.copy(deleteButtonEnabled = false, inputValue = KEY_0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are sure that input has acceptable value, so we convert it
|
||||||
|
convertValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears input value and sets it to default (ZERO)
|
||||||
|
*/
|
||||||
|
fun clearInput() {
|
||||||
|
mainUIState = mainUIState.copy(
|
||||||
|
inputValue = KEY_0,
|
||||||
|
deleteButtonEnabled = false,
|
||||||
|
dotButtonEnabled = true
|
||||||
|
)
|
||||||
|
convertValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes input from positive to negative and vice versa
|
||||||
|
*/
|
||||||
|
fun negateInput() {
|
||||||
|
mainUIState = mainUIState.copy(
|
||||||
|
inputValue = if (mainUIState.inputValue.getOrNull(0) != KEY_MINUS.single()) {
|
||||||
|
// If input doesn't have minus at the beginning, we give it to it
|
||||||
|
KEY_MINUS + mainUIState.inputValue
|
||||||
|
} else {
|
||||||
|
// Input has minus, meaning we need to remove it
|
||||||
|
mainUIState.inputValue.removePrefix(KEY_MINUS)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
convertValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add or remove from favorites (changes to the opposite of current state)
|
||||||
|
*/
|
||||||
|
fun favoriteUnit(unit: AbstractUnit) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
// Changing unit in list to the opposite
|
||||||
|
unit.isFavorite = !unit.isFavorite
|
||||||
|
// Updating it in database
|
||||||
|
myBasedUnitDao.insertUnits(
|
||||||
|
MyBasedUnit(
|
||||||
|
unitId = unit.unitId,
|
||||||
|
isFavorite = unit.isFavorite,
|
||||||
|
pairedUnitId = unit.pairedUnit,
|
||||||
|
frequency = unit.counter
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves latest pair of units into datastore
|
||||||
|
*/
|
||||||
|
fun saveMe() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
mySettingsPrefs.saveString(UserPreferenceKeys.LATEST_LEFT_SIDE, unitFrom.unitId)
|
||||||
|
mySettingsPrefs.saveString(UserPreferenceKeys.LATEST_RIGHT_SIDE, unitTo.unitId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters and groups [ALL_UNITS] in coroutine
|
||||||
|
*
|
||||||
|
* @param query String search query
|
||||||
|
* @param chosenUnitGroup Currently selected [UnitGroup] (from chips list)
|
||||||
|
* @param leftSide Decide whether or not we are on left side. Need it because right side requires
|
||||||
|
* us to mark disabled currency units
|
||||||
|
*/
|
||||||
|
suspend fun loadUnitToShow(
|
||||||
|
query: String,
|
||||||
|
chosenUnitGroup: UnitGroup?,
|
||||||
|
leftSide: Boolean
|
||||||
|
) {
|
||||||
|
val filterGroup: Boolean = chosenUnitGroup != null
|
||||||
|
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
// Basic filtering
|
||||||
|
val basicFilteredUnits =
|
||||||
|
ALL_UNITS.asSequence()
|
||||||
|
// Unit group and favorite
|
||||||
|
.filter {
|
||||||
|
// Decide which group of units to show
|
||||||
|
when {
|
||||||
|
// Both sides, Chip is selected, Only favorites
|
||||||
|
(filterGroup) and (favoritesOnly) -> {
|
||||||
|
(it.group == chosenUnitGroup) and it.isFavorite
|
||||||
|
}
|
||||||
|
// Both sides, Chip is selected, NOT Only favorites
|
||||||
|
(filterGroup) and (!favoritesOnly) -> it.group == chosenUnitGroup
|
||||||
|
// Chip is NOT selected, Only favorites
|
||||||
|
(!filterGroup) and (favoritesOnly) -> it.isFavorite
|
||||||
|
// Chip is NOT selected, NOT Only favorites
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Hiding broken currency units
|
||||||
|
.filter { if (leftSide) true else it.isEnabled }
|
||||||
|
|
||||||
|
unitsToShow = if (query.isEmpty()) {
|
||||||
|
// Query is empty, i.e. we want to see all units and they need to be sorted by usage
|
||||||
|
basicFilteredUnits
|
||||||
|
.sortedByDescending { it.counter }
|
||||||
|
} else {
|
||||||
|
// We are searching for a specific unit, we don't care about popularity
|
||||||
|
// We need search accuracy
|
||||||
|
basicFilteredUnits
|
||||||
|
.sortedBy {
|
||||||
|
it.renderedName
|
||||||
|
.substring(0, minOf(query.length, it.renderedName.length))
|
||||||
|
.lev(query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Group by unit group
|
||||||
|
.groupBy { it.group }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
// First we load latest pair of units
|
||||||
|
unitFrom = try {
|
||||||
|
ALL_UNITS.first {
|
||||||
|
it.unitId == mySettingsPrefs.getItem(
|
||||||
|
UserPreferenceKeys.LATEST_LEFT_SIDE,
|
||||||
|
MyUnitIDS.kilometer
|
||||||
|
).first()
|
||||||
|
}
|
||||||
|
} catch (e: java.util.NoSuchElementException) {
|
||||||
|
Log.w("MainViewModel", "No unit with the given unitId")
|
||||||
|
ALL_UNITS
|
||||||
|
.first { it.unitId == MyUnitIDS.kilometer }
|
||||||
|
}
|
||||||
|
|
||||||
|
unitTo = try {
|
||||||
|
ALL_UNITS
|
||||||
|
.first {
|
||||||
|
it.unitId == mySettingsPrefs.getItem(
|
||||||
|
UserPreferenceKeys.LATEST_RIGHT_SIDE,
|
||||||
|
MyUnitIDS.mile
|
||||||
|
).first()
|
||||||
|
}
|
||||||
|
} catch (e: java.util.NoSuchElementException) {
|
||||||
|
Log.w("MainViewModel", "No unit with the given unitId")
|
||||||
|
ALL_UNITS
|
||||||
|
.first { it.unitId == MyUnitIDS.mile }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we get the precision so we can convert values
|
||||||
|
precision = mySettingsPrefs.getItem(UserPreferenceKeys.DIGITS_PRECISION, 3).first()
|
||||||
|
// Getting separator and changing it in number formatter
|
||||||
|
separator =
|
||||||
|
mySettingsPrefs.getItem(UserPreferenceKeys.SEPARATOR, Separator.SPACES).first()
|
||||||
|
.also { Formatter.setSeparator(it) }
|
||||||
|
// Getting output format
|
||||||
|
outputFormat =
|
||||||
|
mySettingsPrefs.getItem(UserPreferenceKeys.OUTPUT_FORMAT, OutputFormat.PLAIN)
|
||||||
|
.first()
|
||||||
|
|
||||||
|
// Basic data is loaded, user is free to convert values
|
||||||
|
// Set negate button state according to current group
|
||||||
|
mainUIState = mainUIState.copy(
|
||||||
|
isLoadingDataStore = false,
|
||||||
|
negateButtonEnabled = unitFrom.group.canNegate
|
||||||
|
)
|
||||||
|
updateCurrenciesBasicUnits()
|
||||||
|
convertValue()
|
||||||
|
|
||||||
|
val allBasedUnits = myBasedUnitDao.getAll()
|
||||||
|
|
||||||
|
ALL_UNITS.forEach {
|
||||||
|
// Loading unit names so that we can search through them
|
||||||
|
it.renderedName = application.getString(it.displayName)
|
||||||
|
val based = allBasedUnits.firstOrNull { based -> based.unitId == it.unitId }
|
||||||
|
// Loading paired units
|
||||||
|
it.pairedUnit = based?.pairedUnitId
|
||||||
|
// Loading favorite state
|
||||||
|
it.isFavorite = based?.isFavorite ?: false
|
||||||
|
it.counter = based?.frequency ?: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
142
app/src/main/java/com/sadellie/unitto/screens/Utils.kt
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
package com.sadellie.unitto.screens
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
|
import com.sadellie.unitto.data.KEY_COMMA
|
||||||
|
import com.sadellie.unitto.data.KEY_DOT
|
||||||
|
import com.sadellie.unitto.data.KEY_E
|
||||||
|
import com.sadellie.unitto.data.preferences.Separator
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.math.RoundingMode
|
||||||
|
import java.text.NumberFormat
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.math.floor
|
||||||
|
import kotlin.math.log10
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
|
||||||
|
object Formatter {
|
||||||
|
private var nf: NumberFormat = NumberFormat.getInstance(Locale.GERMANY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently used symbol to separate fractional part
|
||||||
|
*/
|
||||||
|
var fractional = KEY_COMMA
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change current separator
|
||||||
|
*
|
||||||
|
* @param separator [Separator] to change to
|
||||||
|
*/
|
||||||
|
fun setSeparator(separator: Int) {
|
||||||
|
nf = when (separator) {
|
||||||
|
Separator.PERIOD -> NumberFormat.getInstance(Locale.GERMANY)
|
||||||
|
Separator.COMMA -> NumberFormat.getInstance(Locale.US)
|
||||||
|
// SPACE BASICALLY
|
||||||
|
else -> NumberFormat.getInstance(Locale.FRANCE)
|
||||||
|
}
|
||||||
|
fractional = if (separator == Separator.PERIOD) KEY_COMMA else KEY_DOT
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom formatter function which work with big decimals and with strings ending with a dot.
|
||||||
|
* Also doesn't lose any precision
|
||||||
|
* @param[input] The string we want to format. Will be split with dot symbol
|
||||||
|
*/
|
||||||
|
fun format(input: String): String {
|
||||||
|
// NOTE: We receive input like 1234 or 1234. or 1234.5
|
||||||
|
// NOTICE DOTS, not COMMAS
|
||||||
|
|
||||||
|
// For engineering string we only replace decimal separator
|
||||||
|
if (input.contains(KEY_E)) return input.replace(KEY_DOT, fractional)
|
||||||
|
|
||||||
|
// Stupid Huawei catching impossible bugs, stupid workaround
|
||||||
|
return try {
|
||||||
|
var result = String()
|
||||||
|
// Formatting everything before fractional part
|
||||||
|
result += nf.format(input.substringBefore(KEY_DOT).toBigInteger())
|
||||||
|
// Now we add the part after dot
|
||||||
|
if (input.contains(KEY_DOT)) {
|
||||||
|
result += fractional + input.substringAfter(KEY_DOT)
|
||||||
|
}
|
||||||
|
result
|
||||||
|
} catch (e: Exception) {
|
||||||
|
FirebaseCrashlytics.getInstance().recordException(e)
|
||||||
|
input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the minimum scale that is required to get first non zero value in fractional part
|
||||||
|
*
|
||||||
|
* @param[prefScale] Is the preferred scale, the one which will be compared against
|
||||||
|
*/
|
||||||
|
fun BigDecimal.setMinimumRequiredScale(prefScale: Int): BigDecimal {
|
||||||
|
/* Here we are getting the amount of zeros in fractional part before non zero value
|
||||||
|
* For example, for 0.00000123456 we need the length of 00000
|
||||||
|
* Next we add one to get the position of the first non zero value
|
||||||
|
*
|
||||||
|
* Also, this block is only for VERY small numbers
|
||||||
|
* */
|
||||||
|
return this.setScale(
|
||||||
|
max(
|
||||||
|
prefScale,
|
||||||
|
if (this.abs() < BigDecimal.ONE) {
|
||||||
|
// https://stackoverflow.com/a/46136593
|
||||||
|
-floor(log10(this.abs().remainder(BigDecimal.ONE).toDouble())).toInt()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
),
|
||||||
|
RoundingMode.HALF_EVEN
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open given link in browser
|
||||||
|
*/
|
||||||
|
fun openLink(mContext: Context, url: String) {
|
||||||
|
mContext.startActivity(Intent(Intent.ACTION_VIEW).setData(Uri.parse(url)))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute Levenshtein Distance. Doesn't really matter which string goes first
|
||||||
|
*
|
||||||
|
* @param stringB Second string
|
||||||
|
* @return The amount of changes that are needed to transform one string into another
|
||||||
|
*/
|
||||||
|
fun CharSequence.lev(stringB: String): Int {
|
||||||
|
// Skipping computation for this cases
|
||||||
|
if (this == stringB) return 0
|
||||||
|
if (this.isEmpty()) return stringB.length
|
||||||
|
// This case is basically unreal in this app, because stringB is a unit name and are never empty
|
||||||
|
if (stringB.isEmpty()) return this.length
|
||||||
|
|
||||||
|
var cost = IntArray(this.length + 1) { it }
|
||||||
|
var newCost = IntArray(this.length + 1)
|
||||||
|
|
||||||
|
for (i in 1..stringB.length) {
|
||||||
|
// basically shifting this to the right by 1 each time
|
||||||
|
newCost[0] = i
|
||||||
|
|
||||||
|
for (j in 1..this.length) {
|
||||||
|
newCost[j] = minOf(
|
||||||
|
// Adding 1 if they don't match, i.e. need to replace
|
||||||
|
cost[j - 1] + if (this[j - 1] == stringB[i - 1]) 0 else 1,
|
||||||
|
// Insert
|
||||||
|
cost[j] + 1,
|
||||||
|
// Delete
|
||||||
|
newCost[j - 1] + 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swapping costs
|
||||||
|
cost = newCost.also { newCost = cost }
|
||||||
|
}
|
||||||
|
|
||||||
|
return cost[this.length]
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
package com.sadellie.unitto.screens.about
|
||||||
|
|
||||||
|
import androidx.compose.animation.rememberSplineBasedDecay
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Close
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.ALL_LIBRARIES
|
||||||
|
import com.sadellie.unitto.screens.openLink
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Screen with used third party libraries
|
||||||
|
*
|
||||||
|
* @param navigateUpAction Action to be called when clicking back button in top bar
|
||||||
|
*/
|
||||||
|
@Stable
|
||||||
|
@Composable
|
||||||
|
fun AboutScreen(
|
||||||
|
navigateUpAction: () -> Unit = {}
|
||||||
|
) {
|
||||||
|
val mContext = LocalContext.current
|
||||||
|
|
||||||
|
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
|
||||||
|
val scrollBehavior = remember(decayAnimationSpec) {
|
||||||
|
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(decayAnimationSpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
modifier = Modifier
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
topBar = {
|
||||||
|
LargeTopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(id = R.string.third_party_licenses))
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = navigateUpAction) {
|
||||||
|
Icon(
|
||||||
|
Icons.Outlined.Close,
|
||||||
|
contentDescription = stringResource(id = R.string.navigate_up_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
Modifier.padding(horizontal = 16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
contentPadding = PaddingValues(vertical = 16.dp)
|
||||||
|
) {
|
||||||
|
items(items = ALL_LIBRARIES.value) {
|
||||||
|
OutlinedCard(
|
||||||
|
Modifier.clickable { it.website?.let { url -> openLink(mContext, url) } }
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(12.dp)
|
||||||
|
) {
|
||||||
|
Text(text = it.name, style = MaterialTheme.typography.titleLarge)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(top = 4.dp, bottom = 12.dp),
|
||||||
|
text = it.dev ?: "",
|
||||||
|
style = MaterialTheme.typography.bodyLarge
|
||||||
|
)
|
||||||
|
Text(text = it.description ?: "", style = MaterialTheme.typography.bodyMedium)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.align(Alignment.End),
|
||||||
|
text = it.license ?: "",
|
||||||
|
style = MaterialTheme.typography.labelLarge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
155
app/src/main/java/com/sadellie/unitto/screens/main/MainScreen.kt
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package com.sadellie.unitto.screens.main
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.MoreVert
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.SECOND_SCREEN
|
||||||
|
import com.sadellie.unitto.data.SETTINGS_SCREEN
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.screens.MainScreenUIState
|
||||||
|
import com.sadellie.unitto.screens.MainViewModel
|
||||||
|
import com.sadellie.unitto.screens.main.components.Keyboard
|
||||||
|
import com.sadellie.unitto.screens.main.components.TopScreenPart
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MainScreen(
|
||||||
|
navControllerAction: (String) -> Unit = {},
|
||||||
|
viewModel: MainViewModel = viewModel()
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
modifier = Modifier,
|
||||||
|
topBar = {
|
||||||
|
CenterAlignedTopAppBar(
|
||||||
|
modifier = Modifier,
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.app_name),
|
||||||
|
style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.W600)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
IconButton(onClick = { navControllerAction(SETTINGS_SCREEN) }) {
|
||||||
|
Icon(
|
||||||
|
Icons.Outlined.MoreVert,
|
||||||
|
contentDescription = stringResource(id = R.string.open_settings_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Makes the background of the top bar transparent, by default uses secondary color
|
||||||
|
colors = TopAppBarDefaults
|
||||||
|
.centerAlignedTopAppBarColors(containerColor = Color.Transparent)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
PortraitMainScreenContent(
|
||||||
|
unitFrom = viewModel.unitFrom,
|
||||||
|
unitTo = viewModel.unitTo,
|
||||||
|
portrait = LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT,
|
||||||
|
mainScreenUIState = viewModel.mainUIState,
|
||||||
|
navControllerAction = { navControllerAction("$SECOND_SCREEN/${it}") },
|
||||||
|
swapMeasurements = { viewModel.swapUnits() },
|
||||||
|
processInput = { viewModel.processInput(it) },
|
||||||
|
deleteDigit = { viewModel.deleteDigit() },
|
||||||
|
clearInput = { viewModel.clearInput() },
|
||||||
|
negateInput = { viewModel.negateInput() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun PortraitMainScreenContent(
|
||||||
|
unitFrom: AbstractUnit,
|
||||||
|
unitTo: AbstractUnit,
|
||||||
|
portrait: Boolean = true,
|
||||||
|
mainScreenUIState: MainScreenUIState = MainScreenUIState(),
|
||||||
|
navControllerAction: (Boolean) -> Unit = {},
|
||||||
|
swapMeasurements: () -> Unit = {},
|
||||||
|
processInput: (String) -> Unit = {},
|
||||||
|
deleteDigit: () -> Unit = {},
|
||||||
|
clearInput: () -> Unit = {},
|
||||||
|
negateInput: () -> Unit = {},
|
||||||
|
) {
|
||||||
|
if (portrait) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||||
|
) {
|
||||||
|
TopScreenPart(
|
||||||
|
modifier = Modifier,
|
||||||
|
inputValue = mainScreenUIState.inputValue,
|
||||||
|
outputValue = mainScreenUIState.resultValue,
|
||||||
|
unitFrom = unitFrom,
|
||||||
|
unitTo = unitTo,
|
||||||
|
loadingDataStore = mainScreenUIState.isLoadingDataStore,
|
||||||
|
loadingNetwork = mainScreenUIState.isLoadingNetwork,
|
||||||
|
networkError = mainScreenUIState.showError,
|
||||||
|
onUnitSelectionClick = navControllerAction,
|
||||||
|
swapUnits = swapMeasurements
|
||||||
|
)
|
||||||
|
// Keyboard which takes half the screen
|
||||||
|
Keyboard(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize(),
|
||||||
|
addDigit = processInput,
|
||||||
|
deleteDigit = deleteDigit,
|
||||||
|
clearInput = clearInput,
|
||||||
|
negateAction = negateInput,
|
||||||
|
dotButtonEnabled = mainScreenUIState.dotButtonEnabled,
|
||||||
|
deleteButtonEnabled = mainScreenUIState.deleteButtonEnabled,
|
||||||
|
negateButtonEnabled = mainScreenUIState.negateButtonEnabled,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Row(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(8.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
TopScreenPart(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxHeight(),
|
||||||
|
inputValue = mainScreenUIState.inputValue,
|
||||||
|
outputValue = mainScreenUIState.resultValue,
|
||||||
|
unitFrom = unitFrom,
|
||||||
|
unitTo = unitTo,
|
||||||
|
loadingDataStore = mainScreenUIState.isLoadingDataStore,
|
||||||
|
loadingNetwork = mainScreenUIState.isLoadingNetwork,
|
||||||
|
networkError = mainScreenUIState.showError,
|
||||||
|
onUnitSelectionClick = navControllerAction,
|
||||||
|
swapUnits = swapMeasurements
|
||||||
|
)
|
||||||
|
|
||||||
|
// Keyboard which takes half the screen
|
||||||
|
Keyboard(
|
||||||
|
Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxSize(),
|
||||||
|
addDigit = processInput,
|
||||||
|
deleteDigit = deleteDigit,
|
||||||
|
clearInput = clearInput,
|
||||||
|
negateAction = negateInput,
|
||||||
|
dotButtonEnabled = mainScreenUIState.dotButtonEnabled,
|
||||||
|
deleteButtonEnabled = mainScreenUIState.deleteButtonEnabled,
|
||||||
|
negateButtonEnabled = mainScreenUIState.negateButtonEnabled,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package com.sadellie.unitto.screens.main.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.data.*
|
||||||
|
import com.sadellie.unitto.screens.Formatter
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keyboard with button that looks like a calculator
|
||||||
|
*
|
||||||
|
* @param modifier Modifier that is applied to [Row]
|
||||||
|
* @param addDigit Function that is called when clicking number and dot buttons
|
||||||
|
* @param deleteDigit Function that is called when clicking delete "<" button
|
||||||
|
* @param clearInput Function that is called when clicking clear "AC" button
|
||||||
|
* @param negateAction Function that is called when clicking negate "±" button
|
||||||
|
* @param deleteButtonEnabled Current state of delete "<" button
|
||||||
|
* @param dotButtonEnabled Current state of clear "AC" button
|
||||||
|
* @param negateButtonEnabled Current state of negate "±" button
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun Keyboard(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
addDigit: (String) -> Unit = {},
|
||||||
|
deleteDigit: () -> Unit = {},
|
||||||
|
clearInput: () -> Unit = {},
|
||||||
|
negateAction: () -> Unit = {},
|
||||||
|
deleteButtonEnabled: Boolean = false,
|
||||||
|
dotButtonEnabled: Boolean = true,
|
||||||
|
negateButtonEnabled: Boolean = false,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
// Button modifier
|
||||||
|
val bModifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.weight(1f)
|
||||||
|
.padding(4.dp)
|
||||||
|
// Column modifier
|
||||||
|
val cModifier = Modifier.weight(1f)
|
||||||
|
Column(cModifier) {
|
||||||
|
KeyboardButton(bModifier, KEY_7, onClick = addDigit)
|
||||||
|
KeyboardButton(bModifier, KEY_4, onClick = addDigit)
|
||||||
|
KeyboardButton(bModifier, KEY_1, onClick = addDigit)
|
||||||
|
KeyboardButton(bModifier, KEY_0, onClick = addDigit)
|
||||||
|
}
|
||||||
|
Column(cModifier) {
|
||||||
|
KeyboardButton(bModifier, KEY_8, onClick = addDigit)
|
||||||
|
KeyboardButton(bModifier, KEY_5, onClick = addDigit)
|
||||||
|
KeyboardButton(bModifier, KEY_2, onClick = addDigit)
|
||||||
|
KeyboardButton(bModifier, Formatter.fractional, dotButtonEnabled) { addDigit(KEY_DOT) }
|
||||||
|
}
|
||||||
|
Column(cModifier) {
|
||||||
|
KeyboardButton(bModifier, KEY_9, onClick = addDigit)
|
||||||
|
KeyboardButton(bModifier, KEY_6, onClick = addDigit)
|
||||||
|
KeyboardButton(bModifier, KEY_3, onClick = addDigit)
|
||||||
|
KeyboardButton(bModifier, KEY_CLEAR, deleteButtonEnabled) { deleteDigit() }
|
||||||
|
}
|
||||||
|
Column(cModifier) {
|
||||||
|
KeyboardButton(bModifier, KEY_AC, deleteButtonEnabled) { clearInput() }
|
||||||
|
KeyboardButton(bModifier, KEY_NEGATE, negateButtonEnabled) { negateAction() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package com.sadellie.unitto.screens.main.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.ui.theme.NumbersTextStyleTitleLarge
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button for keyboard
|
||||||
|
*
|
||||||
|
* @param modifier Modifier that is applied to a [Button] component
|
||||||
|
* @param digit Symbol to show on button
|
||||||
|
* @param enabled Current state of this button
|
||||||
|
* @param onClick Action to perform when clicking this button
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun KeyboardButton(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
digit: String,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
onClick: (String) -> Unit = {},
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
modifier = modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
),
|
||||||
|
onClick = { onClick(digit) },
|
||||||
|
enabled = enabled,
|
||||||
|
contentPadding = PaddingValues(0.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = digit,
|
||||||
|
style = NumbersTextStyleTitleLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSecondaryContainer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
package com.sadellie.unitto.screens.main.components
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.animation.*
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalClipboardManager
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.screens.Formatter
|
||||||
|
import com.sadellie.unitto.ui.theme.NumbersTextStyleDisplayLarge
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component for input and output
|
||||||
|
*
|
||||||
|
* @param modifier Modifier that is applied to [LazyRow]
|
||||||
|
* @param currentText Current text to show
|
||||||
|
* @param helperText Helper text below current text (short unit name)
|
||||||
|
* @param showLoading Show "Loading" text
|
||||||
|
* @param showError Show "Error" text
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun MyTextField(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
currentText: String = String(),
|
||||||
|
helperText: String = String(),
|
||||||
|
showLoading: Boolean = false,
|
||||||
|
showError: Boolean = false
|
||||||
|
) {
|
||||||
|
val clipboardManager = LocalClipboardManager.current
|
||||||
|
val mc = LocalContext.current
|
||||||
|
val textToShow = when {
|
||||||
|
showError -> stringResource(id = R.string.error_label)
|
||||||
|
showLoading -> stringResource(id = R.string.loading_label)
|
||||||
|
else -> Formatter.format(currentText)
|
||||||
|
}
|
||||||
|
val copiedText: String = stringResource(R.string.copied, textToShow)
|
||||||
|
|
||||||
|
Column {
|
||||||
|
LazyRow(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight()
|
||||||
|
.combinedClickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = rememberRipple(),
|
||||||
|
onClick = {},
|
||||||
|
onLongClick = {
|
||||||
|
clipboardManager.setText(AnnotatedString(currentText))
|
||||||
|
Toast
|
||||||
|
.makeText(mc, copiedText, Toast.LENGTH_SHORT)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
reverseLayout = true,
|
||||||
|
horizontalArrangement = Arrangement.End
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = textToShow,
|
||||||
|
transitionSpec = {
|
||||||
|
// Enter animation
|
||||||
|
(expandHorizontally(clip = false, expandFrom = Alignment.Start) + fadeIn()
|
||||||
|
// Exit animation
|
||||||
|
with fadeOut())
|
||||||
|
.using(SizeTransform(clip = false))
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
text = it,
|
||||||
|
textAlign = TextAlign.End,
|
||||||
|
softWrap = false,
|
||||||
|
style = NumbersTextStyleDisplayLarge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimatedContent(
|
||||||
|
modifier = Modifier.align(Alignment.End),
|
||||||
|
targetState = helperText
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package com.sadellie.unitto.screens.main.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.SwapHoriz
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Top of the main screen. Contains input and output TextFields, and unit selection row of buttons.
|
||||||
|
* It's a separate composable, so that we support album orientation (this element will be on the left)
|
||||||
|
*
|
||||||
|
* @param modifier Modifier that is applied to Column
|
||||||
|
* @param inputValue Current input value (like big decimal)
|
||||||
|
* @param outputValue Current output value (like big decimal)
|
||||||
|
* @param unitFrom [AbstractUnit] on the left
|
||||||
|
* @param unitTo [AbstractUnit] on the right
|
||||||
|
* @param loadingDataStore Are we still loading settings from data store? Disables unit selection buttons
|
||||||
|
* @param loadingNetwork Are we loading data from network? Shows loading text in TextFields
|
||||||
|
* @param networkError Did we got errors while trying to get data from network
|
||||||
|
* @param onUnitSelectionClick Function that is called when clicking unit selection buttons
|
||||||
|
* @param swapUnits Method to swap units
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun TopScreenPart(
|
||||||
|
modifier: Modifier,
|
||||||
|
inputValue: String,
|
||||||
|
outputValue: String,
|
||||||
|
unitFrom: AbstractUnit,
|
||||||
|
unitTo: AbstractUnit,
|
||||||
|
loadingDataStore: Boolean,
|
||||||
|
loadingNetwork: Boolean,
|
||||||
|
networkError: Boolean,
|
||||||
|
onUnitSelectionClick: (Boolean) -> Unit,
|
||||||
|
swapUnits: () -> Unit
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||||
|
) {
|
||||||
|
MyTextField(
|
||||||
|
Modifier.fillMaxWidth(),
|
||||||
|
inputValue,
|
||||||
|
stringResource(id = if (loadingDataStore) R.string.loading_label else unitFrom.shortName),
|
||||||
|
loadingNetwork,
|
||||||
|
networkError
|
||||||
|
)
|
||||||
|
MyTextField(
|
||||||
|
Modifier.fillMaxWidth(),
|
||||||
|
outputValue,
|
||||||
|
stringResource(id = if (loadingDataStore) R.string.loading_label else unitTo.shortName),
|
||||||
|
loadingNetwork,
|
||||||
|
networkError
|
||||||
|
)
|
||||||
|
// Unit selection buttons
|
||||||
|
Row(
|
||||||
|
modifier = Modifier,
|
||||||
|
verticalAlignment = Alignment.Bottom,
|
||||||
|
) {
|
||||||
|
UnitSelectionButton(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f),
|
||||||
|
onClick = { onUnitSelectionClick(true) },
|
||||||
|
label = unitFrom.displayName,
|
||||||
|
loadingState = loadingDataStore
|
||||||
|
)
|
||||||
|
IconButton({ swapUnits() }, enabled = !loadingDataStore) {
|
||||||
|
Icon(
|
||||||
|
Icons.Outlined.SwapHoriz, contentDescription = stringResource(
|
||||||
|
id = R.string.swap_units_description
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
UnitSelectionButton(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f),
|
||||||
|
onClick = { onUnitSelectionClick(false) },
|
||||||
|
label = unitTo.displayName,
|
||||||
|
loadingState = loadingDataStore
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package com.sadellie.unitto.screens.main.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.*
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button to select a unit
|
||||||
|
*
|
||||||
|
* @param modifier Modifier that is applied to a [Button]
|
||||||
|
* @param onClick Function to call when button is clicked (navigate to a unit selection screen)
|
||||||
|
* @param label Text on button
|
||||||
|
* @param loadingState Show "Loading" text and disable button
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun UnitSelectionButton(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onClick: () -> Unit = {},
|
||||||
|
label: Int,
|
||||||
|
loadingState: Boolean
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
modifier = modifier,
|
||||||
|
onClick = { onClick() },
|
||||||
|
enabled = !loadingState,
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
),
|
||||||
|
contentPadding = PaddingValues(vertical = 16.dp, horizontal = 8.dp)
|
||||||
|
) {
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = label,
|
||||||
|
transitionSpec = {
|
||||||
|
if (targetState > initialState) {
|
||||||
|
slideInVertically { height -> height } + fadeIn() with
|
||||||
|
slideOutVertically { height -> -height } + fadeOut()
|
||||||
|
} else {
|
||||||
|
slideInVertically { height -> -height } + fadeIn() with
|
||||||
|
slideOutVertically { height -> height } + fadeOut()
|
||||||
|
}.using(
|
||||||
|
SizeTransform(clip = false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(if (loadingState) R.string.loading_label else label),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
color = MaterialTheme.colorScheme.onSecondaryContainer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
package com.sadellie.unitto.screens.second
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.ALL_UNIT_GROUPS
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import com.sadellie.unitto.screens.MainViewModel
|
||||||
|
import com.sadellie.unitto.screens.second.components.ChipsRow
|
||||||
|
import com.sadellie.unitto.screens.second.components.SearchBar
|
||||||
|
import com.sadellie.unitto.screens.second.components.UnitsList
|
||||||
|
|
||||||
|
/** Second screen which contains a list of measurements
|
||||||
|
* @param leftSide True if we have navigated to this screen by pressing the left button
|
||||||
|
* @param navigateUp Function to navigate to the previous screen from NavHost
|
||||||
|
* @param viewModel MainViewModel. Need it to access current state of the app
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun SecondScreen(
|
||||||
|
leftSide: Boolean = true,
|
||||||
|
navigateUp: () -> Unit,
|
||||||
|
viewModel: MainViewModel = viewModel()
|
||||||
|
) {
|
||||||
|
var searchQuery: String by rememberSaveable { mutableStateOf(String()) }
|
||||||
|
val favoritesOnly: Boolean = viewModel.favoritesOnly
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
|
val unitsList: Map<UnitGroup, List<AbstractUnit>> = viewModel.unitsToShow
|
||||||
|
val chipsRowLazyListState = rememberLazyListState()
|
||||||
|
val currentUnit = if (leftSide) viewModel.unitFrom else viewModel.unitTo
|
||||||
|
var chosenUnitGroup: UnitGroup? by rememberSaveable { mutableStateOf(currentUnit.group) }
|
||||||
|
|
||||||
|
Column {
|
||||||
|
SearchBar(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp),
|
||||||
|
title = stringResource(id = if (leftSide) R.string.units_screen_from else R.string.units_screen_to),
|
||||||
|
value = searchQuery,
|
||||||
|
onValueChange = { searchQuery = it },
|
||||||
|
favoritesOnly = favoritesOnly,
|
||||||
|
favoriteAction = { viewModel.toggleFavoritesOnly() },
|
||||||
|
navigateUpAction = navigateUp,
|
||||||
|
focusManager = focusManager
|
||||||
|
)
|
||||||
|
|
||||||
|
if (leftSide) {
|
||||||
|
ChipsRow(
|
||||||
|
lazyListState = chipsRowLazyListState,
|
||||||
|
items = ALL_UNIT_GROUPS,
|
||||||
|
chosenUnitGroup = chosenUnitGroup,
|
||||||
|
selectAction = { chosenUnitGroup = if (it == chosenUnitGroup) null else it }
|
||||||
|
)
|
||||||
|
UnitsList(
|
||||||
|
groupedUnits = unitsList,
|
||||||
|
changeAction = { viewModel.changeUnitFrom(it); focusManager.clearFocus(true); navigateUp() },
|
||||||
|
favoriteAction = { viewModel.favoriteUnit(it) },
|
||||||
|
currentUnit = viewModel.unitFrom,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
UnitsList(
|
||||||
|
groupedUnits = unitsList,
|
||||||
|
changeAction = { viewModel.changeUnitTo(it); focusManager.clearFocus(true); navigateUp() },
|
||||||
|
favoriteAction = { viewModel.favoriteUnit(it) },
|
||||||
|
currentUnit = viewModel.unitTo,
|
||||||
|
inputValue = viewModel.mainUIState.inputValue.toBigDecimal(),
|
||||||
|
unitFrom = viewModel.unitFrom
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LaunchedEffect(searchQuery, favoritesOnly, chosenUnitGroup) {
|
||||||
|
// Everytime we change query, toggle favorites or click chip, this block will be called
|
||||||
|
viewModel.loadUnitToShow(searchQuery, chosenUnitGroup, leftSide)
|
||||||
|
}
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
// This block is called only once on initial composition
|
||||||
|
// Scrolling chips to current group
|
||||||
|
chosenUnitGroup?.let {
|
||||||
|
chipsRowLazyListState.animateScrollToItem(ALL_UNIT_GROUPS.indexOf(chosenUnitGroup))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package com.sadellie.unitto.screens.second.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
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.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Row of chips with [UnitGroup]s. Temporary solution
|
||||||
|
*
|
||||||
|
* @param items All [UnitGroup]s
|
||||||
|
* @param chosenUnitGroup Currently selected [UnitGroup]
|
||||||
|
* @param selectAction Action to perform when a chip is clicked
|
||||||
|
* @param lazyListState Used for animated scroll when entering unit selection screen
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun ChipsRow(
|
||||||
|
items: List<UnitGroup>,
|
||||||
|
chosenUnitGroup: UnitGroup?,
|
||||||
|
selectAction: (UnitGroup?) -> Unit,
|
||||||
|
lazyListState: LazyListState
|
||||||
|
) {
|
||||||
|
val chipShape = RoundedCornerShape(8.dp)
|
||||||
|
LazyRow(
|
||||||
|
state = lazyListState,
|
||||||
|
contentPadding = PaddingValues(8.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
items(items) { item ->
|
||||||
|
val isSelected: Boolean = item == chosenUnitGroup
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(32.dp)
|
||||||
|
.clip(chipShape)
|
||||||
|
.background(
|
||||||
|
if (isSelected) MaterialTheme.colorScheme.secondaryContainer else Color.Transparent
|
||||||
|
)
|
||||||
|
// Remove border when selected
|
||||||
|
.border(
|
||||||
|
1.dp,
|
||||||
|
if (isSelected) Color.Transparent else MaterialTheme.colorScheme.outline,
|
||||||
|
chipShape
|
||||||
|
)
|
||||||
|
.clickable { selectAction(item) }
|
||||||
|
.padding(
|
||||||
|
start = 8.dp,
|
||||||
|
end = 16.dp
|
||||||
|
),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(visible = isSelected) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.height(18.dp),
|
||||||
|
imageVector = Icons.Default.Check,
|
||||||
|
contentDescription = stringResource(id = R.string.checked_filter_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = 8.dp),
|
||||||
|
text = stringResource(id = item.res),
|
||||||
|
style = MaterialTheme.typography.labelLarge,
|
||||||
|
color = if (isSelected) MaterialTheme.colorScheme.onSecondaryContainer else MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,178 @@
|
|||||||
|
package com.sadellie.unitto.screens.second.components
|
||||||
|
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.animation.*
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
|
import androidx.compose.material.icons.filled.FavoriteBorder
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material.icons.outlined.Clear
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.focus.FocusManager
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search bar on the Second screen. Controls what will be shown in the list above this component
|
||||||
|
*
|
||||||
|
* @param modifier Modifier to be applied to the Row that contains search bar elements
|
||||||
|
* @param title Search bar title
|
||||||
|
* @param value Current query
|
||||||
|
* @param onValueChange Action to perform when search query changes
|
||||||
|
* @param favoritesOnly Current filter state: On or Off
|
||||||
|
* @param favoriteAction Function to toggle favorite filter
|
||||||
|
* @param navigateUpAction Function to navigate to previous screen
|
||||||
|
* @param focusManager Used to hide keyboard when leaving unit selection screen
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun SearchBar(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
title: String = String(),
|
||||||
|
value: String = String(),
|
||||||
|
onValueChange: (String) -> Unit = {},
|
||||||
|
favoritesOnly: Boolean,
|
||||||
|
favoriteAction: () -> Unit,
|
||||||
|
navigateUpAction: () -> Unit = {},
|
||||||
|
focusManager: FocusManager
|
||||||
|
) {
|
||||||
|
var showSearch by rememberSaveable { mutableStateOf(false) }
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
|
Crossfade(targetState = showSearch) { textFieldShown ->
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
when (textFieldShown) {
|
||||||
|
// No search text field
|
||||||
|
false -> {
|
||||||
|
IconButton(onClick = navigateUpAction) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.ArrowBack,
|
||||||
|
contentDescription = stringResource(id = R.string.navigate_up_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f),
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleLarge
|
||||||
|
)
|
||||||
|
// Search button
|
||||||
|
IconButton(onClick = { onValueChange(""); showSearch = true }) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Search,
|
||||||
|
contentDescription = stringResource(id = R.string.search_button_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Favorites button
|
||||||
|
IconButton(onClick = favoriteAction) {
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = favoritesOnly,
|
||||||
|
transitionSpec = {
|
||||||
|
(scaleIn() with scaleOut()).using(SizeTransform(clip = false))
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
if (favoritesOnly) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder,
|
||||||
|
contentDescription = stringResource(id = R.string.favorite_button_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// With text field
|
||||||
|
true -> {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
// Resetting query
|
||||||
|
onValueChange("")
|
||||||
|
focusManager.clearFocus()
|
||||||
|
showSearch = false
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.ArrowBack,
|
||||||
|
contentDescription = stringResource(id = R.string.navigate_up_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
BasicTextField(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f)
|
||||||
|
.focusRequester(focusRequester),
|
||||||
|
value = value,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
singleLine = true,
|
||||||
|
textStyle = MaterialTheme.typography.titleLarge.copy(color = MaterialTheme.colorScheme.onSurface),
|
||||||
|
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurface),
|
||||||
|
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
|
||||||
|
keyboardActions = KeyboardActions(onSearch = {
|
||||||
|
// Close searchbar if there is nothing in search query and user
|
||||||
|
// clicks search button on his keyboard
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
showSearch = false
|
||||||
|
} else {
|
||||||
|
focusManager.clearFocus()
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
decorationBox = { innerTextField ->
|
||||||
|
// Showing placeholder only when there is query is empty
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alpha(0.7f),
|
||||||
|
text = stringResource(id = R.string.search_bar_placeholder),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
color = MaterialTheme.colorScheme.outline
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
innerTextField()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// Clear button
|
||||||
|
IconButton(
|
||||||
|
modifier = Modifier.alpha(if (value != String()) 1f else 0f),
|
||||||
|
onClick = { onValueChange("") }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Outlined.Clear,
|
||||||
|
contentDescription = stringResource(id = R.string.clear_input_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
focusRequester.requestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BackHandler {
|
||||||
|
if (textFieldShown) {
|
||||||
|
// Search text field is open, need to close it and clear search query
|
||||||
|
onValueChange("")
|
||||||
|
showSearch = false
|
||||||
|
} else {
|
||||||
|
// No MyTextField shown, can go back as usual
|
||||||
|
navigateUpAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package com.sadellie.unitto.screens.second.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.SearchOff
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Placeholder that can be seen when there are no units found
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun SearchPlaceholder() {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
// Big icon in the middle
|
||||||
|
Icon(
|
||||||
|
Icons.Default.SearchOff,
|
||||||
|
contentDescription = stringResource(id = R.string.empty_search_result_description),
|
||||||
|
modifier = Modifier.size(48.dp)
|
||||||
|
)
|
||||||
|
// Primary text
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.search_placeholder),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
style = MaterialTheme.typography.bodyLarge
|
||||||
|
)
|
||||||
|
// Secondary text with tips
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.search_placeholder_secondary),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
style = MaterialTheme.typography.bodySmall
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
package com.sadellie.unitto.screens.second.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.*
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
|
import androidx.compose.material.icons.filled.FavoriteBorder
|
||||||
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents one list item. Once clicked will navigate up
|
||||||
|
*
|
||||||
|
* @param modifier Modifier that will be applied to a box that surrounds row.
|
||||||
|
* @param changeAction Function to change current unit. Called when choosing unit
|
||||||
|
* @param favoriteAction Function to mark unit as favorite. It's a toggle
|
||||||
|
* @param item The unit itself
|
||||||
|
* @param isSelected Whether this unit is selected or not (current pair of unit)
|
||||||
|
* @param convertValue Used for right side units. Shows conversion from unit on the left
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun UnitListItem(
|
||||||
|
modifier: Modifier,
|
||||||
|
changeAction: (AbstractUnit) -> Unit,
|
||||||
|
favoriteAction: (AbstractUnit) -> Unit,
|
||||||
|
item: AbstractUnit,
|
||||||
|
isSelected: Boolean,
|
||||||
|
convertValue: (AbstractUnit) -> String
|
||||||
|
) {
|
||||||
|
var isFavorite: Boolean by rememberSaveable { mutableStateOf(item.isFavorite) }
|
||||||
|
var convertedValue: String by remember { mutableStateOf("") }
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.clickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = rememberRipple(), // You can also change the color and radius of the ripple
|
||||||
|
onClick = {
|
||||||
|
changeAction(item)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.padding(horizontal = 12.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
if (isSelected) MaterialTheme.colorScheme.secondaryContainer else Color.Transparent,
|
||||||
|
RoundedCornerShape(24.dp)
|
||||||
|
),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.weight(1f)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = item.displayName),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = convertedValue + stringResource(id = item.shortName),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
IconButton(onClick = { favoriteAction(item); isFavorite = !isFavorite }) {
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = isFavorite,
|
||||||
|
transitionSpec = {
|
||||||
|
(scaleIn() with scaleOut()).using(SizeTransform(clip = false))
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
if (item.isFavorite) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder,
|
||||||
|
contentDescription = stringResource(id = R.string.favorite_button_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
// Converting value
|
||||||
|
convertedValue = convertValue(item)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package com.sadellie.unitto.screens.second.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.data.units.AbstractUnit
|
||||||
|
import com.sadellie.unitto.data.units.UnitGroup
|
||||||
|
import com.sadellie.unitto.screens.Formatter
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component with grouped units
|
||||||
|
*
|
||||||
|
* @param groupedUnits Grouped [AbstractUnit]s to be listed
|
||||||
|
* @param changeAction Action to perform when clicking on a list item
|
||||||
|
* @param favoriteAction Action to perform when clicking "favorite" button on list item
|
||||||
|
* @param inputValue Current input value. Used for right side screen
|
||||||
|
* @param unitFrom Current unit on the left. Used for right side screen
|
||||||
|
* @param currentUnit Currently selected unit. Will be visually distinguishable
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun UnitsList(
|
||||||
|
groupedUnits: Map<UnitGroup, List<AbstractUnit>>,
|
||||||
|
changeAction: (AbstractUnit) -> Unit,
|
||||||
|
favoriteAction: (AbstractUnit) -> Unit,
|
||||||
|
inputValue: BigDecimal = BigDecimal.ONE,
|
||||||
|
unitFrom: AbstractUnit? = null,
|
||||||
|
currentUnit: AbstractUnit,
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
content = {
|
||||||
|
if (groupedUnits.isEmpty()) {
|
||||||
|
item { SearchPlaceholder() }
|
||||||
|
} else {
|
||||||
|
groupedUnits.forEach { (groupOfMeasurements, listOfMeasurements) ->
|
||||||
|
stickyHeader {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(MaterialTheme.colorScheme.background)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 12.dp, horizontal = 8.dp),
|
||||||
|
text = stringResource(id = groupOfMeasurements.res),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
items(listOfMeasurements) { unit ->
|
||||||
|
UnitListItem(
|
||||||
|
modifier = Modifier,
|
||||||
|
changeAction = changeAction,
|
||||||
|
favoriteAction = favoriteAction,
|
||||||
|
item = unit,
|
||||||
|
isSelected = currentUnit == unit,
|
||||||
|
convertValue = {
|
||||||
|
if (unitFrom != null) {
|
||||||
|
Formatter.format(
|
||||||
|
unitFrom
|
||||||
|
.convert(it, inputValue, 3)
|
||||||
|
.toPlainString()
|
||||||
|
.plus(" ")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
package com.sadellie.unitto.screens.setttings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.RadioButton
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alert dialog that has a list of options in it
|
||||||
|
*
|
||||||
|
* @param title Dialog title
|
||||||
|
* @param listItems List of options
|
||||||
|
* @param selectedItemIndex Currently selected item index
|
||||||
|
* @param selectAction Action to perform when clicking an option
|
||||||
|
* @param dismissAction Action to perform when clicking "cancel"
|
||||||
|
* @param supportText Text above list of options
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun AlertDialogWithList(
|
||||||
|
title: String,
|
||||||
|
listItems: Map<Int, Int> = emptyMap(),
|
||||||
|
selectedItemIndex: Int = 0,
|
||||||
|
selectAction: (Int) -> Unit = {},
|
||||||
|
dismissAction: () -> Unit,
|
||||||
|
supportText: String? = null
|
||||||
|
) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = dismissAction,
|
||||||
|
title = {
|
||||||
|
Text(text = title)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||||
|
supportText?.let {
|
||||||
|
Text(supportText)
|
||||||
|
}
|
||||||
|
LazyColumn {
|
||||||
|
items(listItems.toList()) {
|
||||||
|
CustomDialogContentListItem(
|
||||||
|
label = stringResource(it.second),
|
||||||
|
selected = selectedItemIndex == it.first,
|
||||||
|
onClick = {
|
||||||
|
selectAction(it.first)
|
||||||
|
dismissAction()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = dismissAction) {
|
||||||
|
Text(text = stringResource(id = R.string.cancel_label))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An item that represents one option item
|
||||||
|
*
|
||||||
|
* @param label Option label
|
||||||
|
* @param selected Whether this option is selected
|
||||||
|
* @param onClick Action to perform when this item is clicked
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
private fun CustomDialogContentListItem(
|
||||||
|
label: String,
|
||||||
|
selected: Boolean = false,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = rememberRipple(),
|
||||||
|
onClick = onClick
|
||||||
|
),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
RadioButton(
|
||||||
|
selected = selected,
|
||||||
|
onClick = onClick
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = label
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package com.sadellie.unitto.screens.setttings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents one item in list on Settings screen
|
||||||
|
*
|
||||||
|
* @param modifier Modifier that is applied to a [Row]
|
||||||
|
* @param onClick Action to perform when clicking this item
|
||||||
|
* @param label Big text that is above support text
|
||||||
|
* @param supportText Smaller text that is below label
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun SettingsListItem(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
label: String,
|
||||||
|
supportText: String? = null,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = rememberRipple(),
|
||||||
|
onClick = onClick
|
||||||
|
)
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier.padding(horizontal = 0.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
text = label
|
||||||
|
)
|
||||||
|
supportText?.let {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
text = it,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,203 @@
|
|||||||
|
package com.sadellie.unitto.screens.setttings
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.compose.animation.rememberSplineBasedDecay
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.ArrowBack
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
import com.sadellie.unitto.data.ABOUT_SCREEN
|
||||||
|
import com.sadellie.unitto.data.preferences.*
|
||||||
|
import com.sadellie.unitto.screens.MainViewModel
|
||||||
|
import com.sadellie.unitto.screens.openLink
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SettingsScreen(
|
||||||
|
mainViewModel: MainViewModel,
|
||||||
|
navigateUpAction: () -> Unit,
|
||||||
|
navControllerAction: (String) -> Unit
|
||||||
|
) {
|
||||||
|
val mContext = LocalContext.current
|
||||||
|
|
||||||
|
// Scrollable
|
||||||
|
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
|
||||||
|
val scrollBehavior = remember(decayAnimationSpec) {
|
||||||
|
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(decayAnimationSpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentDialogState: Int by rememberSaveable { mutableStateOf(0) }
|
||||||
|
val currentAppTheme: Int by mainViewModel.currentAppTheme.collectAsState(AppTheme.NOT_SET)
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
modifier = Modifier
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
topBar = {
|
||||||
|
LargeTopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(id = R.string.settings_screen))
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = navigateUpAction) {
|
||||||
|
Icon(
|
||||||
|
Icons.Outlined.ArrowBack,
|
||||||
|
contentDescription = stringResource(id = R.string.navigate_up_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
LazyColumn(
|
||||||
|
Modifier.fillMaxHeight()
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
Column {
|
||||||
|
// Group header
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(start = 16.dp, end = 16.dp, top = 24.dp, bottom = 12.dp),
|
||||||
|
text = stringResource(id = R.string.general_settings_group),
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
SettingsListItem(
|
||||||
|
label = stringResource(R.string.precision_setting),
|
||||||
|
supportText = stringResource(R.string.precision_setting_support),
|
||||||
|
onClick = { currentDialogState = 1 }
|
||||||
|
)
|
||||||
|
SettingsListItem(
|
||||||
|
label = stringResource(R.string.separator_setting),
|
||||||
|
supportText = stringResource(R.string.separator_setting_support),
|
||||||
|
onClick = { currentDialogState = 2 }
|
||||||
|
)
|
||||||
|
SettingsListItem(
|
||||||
|
label = stringResource(R.string.output_format_setting),
|
||||||
|
supportText = stringResource(id = R.string.output_format_setting_support),
|
||||||
|
onClick = { currentDialogState = 3 }
|
||||||
|
)
|
||||||
|
SettingsListItem(
|
||||||
|
label = stringResource(R.string.theme_setting),
|
||||||
|
supportText = stringResource(R.string.theme_setting_support),
|
||||||
|
onClick = { currentDialogState = 4 }
|
||||||
|
)
|
||||||
|
SettingsListItem(
|
||||||
|
label = stringResource(id = R.string.currency_rates_note_setting),
|
||||||
|
onClick = { currentDialogState = 5 }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Group header
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(start = 16.dp, end = 16.dp, top = 24.dp, bottom = 12.dp),
|
||||||
|
text = stringResource(id = R.string.additional_settings_group),
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
SettingsListItem(
|
||||||
|
label = stringResource(R.string.terms_and_conditions),
|
||||||
|
onClick = {
|
||||||
|
openLink(
|
||||||
|
mContext,
|
||||||
|
"http://sadellie.github.io/unitto/terms-app.html"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
SettingsListItem(
|
||||||
|
label = stringResource(R.string.privacy_policy),
|
||||||
|
onClick = {
|
||||||
|
openLink(
|
||||||
|
mContext,
|
||||||
|
"http://sadellie.github.io/unitto/privacy-app.html"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
SettingsListItem(
|
||||||
|
label = stringResource(R.string.third_party_licenses),
|
||||||
|
onClick = { navControllerAction(ABOUT_SCREEN) }
|
||||||
|
)
|
||||||
|
SettingsListItem(
|
||||||
|
label = stringResource(R.string.rate_this_app),
|
||||||
|
onClick = {
|
||||||
|
openLink(
|
||||||
|
mContext,
|
||||||
|
"http://play.google.com/store/apps/details?id=com.sadellie.unitto"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Showing dialog
|
||||||
|
when (currentDialogState) {
|
||||||
|
1 -> {
|
||||||
|
AlertDialogWithList(
|
||||||
|
title = stringResource(id = R.string.precision_setting),
|
||||||
|
listItems = PRECISIONS,
|
||||||
|
selectedItemIndex = mainViewModel.precision,
|
||||||
|
selectAction = { mainViewModel.setPrecisionPref(it) },
|
||||||
|
dismissAction = { currentDialogState = 0 },
|
||||||
|
supportText = stringResource(id = R.string.precision_setting_info)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
AlertDialogWithList(
|
||||||
|
title = stringResource(id = R.string.separator_setting),
|
||||||
|
listItems = SEPARATORS,
|
||||||
|
selectedItemIndex = mainViewModel.separator,
|
||||||
|
selectAction = { mainViewModel.setSeparatorPref(it) },
|
||||||
|
dismissAction = { currentDialogState = 0 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
3 -> {
|
||||||
|
AlertDialogWithList(
|
||||||
|
title = stringResource(id = R.string.output_format_setting),
|
||||||
|
listItems = OUTPUT_FORMAT,
|
||||||
|
selectedItemIndex = mainViewModel.outputFormat,
|
||||||
|
selectAction = { mainViewModel.setOutputFormatPref(it) },
|
||||||
|
dismissAction = { currentDialogState = 0 },
|
||||||
|
supportText = stringResource(id = R.string.output_format_setting_info)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
4 -> {
|
||||||
|
AlertDialogWithList(
|
||||||
|
title = stringResource(id = R.string.theme_setting),
|
||||||
|
listItems = APP_THEMES,
|
||||||
|
selectedItemIndex = currentAppTheme,
|
||||||
|
selectAction = { mainViewModel.saveCurrentAppTheme(it) },
|
||||||
|
dismissAction = { currentDialogState = 0 },
|
||||||
|
// Show note for users with devices that support custom Dynamic theming
|
||||||
|
supportText = if (Build.VERSION.SDK_INT in (Build.VERSION_CODES.O_MR1..Build.VERSION_CODES.R)) stringResource(
|
||||||
|
id = R.string.theme_setting_info
|
||||||
|
) else null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
5 -> {
|
||||||
|
AlertDialogWithList(
|
||||||
|
title = stringResource(id = R.string.currency_rates_note_title),
|
||||||
|
dismissAction = { currentDialogState = 0 },
|
||||||
|
supportText = stringResource(id = R.string.currency_rates_note_text)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Dismissing alert dialog
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
59
app/src/main/java/com/sadellie/unitto/ui/theme/Color.kt
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package com.sadellie.unitto.ui.theme
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
|
||||||
|
val md_theme_light_primary = Color(0xFF186c31)
|
||||||
|
val md_theme_light_onPrimary = Color(0xFFffffff)
|
||||||
|
val md_theme_light_primaryContainer = Color(0xFFa3f6ab)
|
||||||
|
val md_theme_light_onPrimaryContainer = Color(0xFF002107)
|
||||||
|
val md_theme_light_secondary = Color(0xFF506350)
|
||||||
|
val md_theme_light_onSecondary = Color(0xFFffffff)
|
||||||
|
val md_theme_light_secondaryContainer = Color(0xFFd4e8d1)
|
||||||
|
val md_theme_light_onSecondaryContainer = Color(0xFF0f1f11)
|
||||||
|
val md_theme_light_tertiary = Color(0xFF39656d)
|
||||||
|
val md_theme_light_onTertiary = Color(0xFFffffff)
|
||||||
|
val md_theme_light_tertiaryContainer = Color(0xFFbceaf3)
|
||||||
|
val md_theme_light_onTertiaryContainer = Color(0xFF001f24)
|
||||||
|
val md_theme_light_error = Color(0xFFba1b1b)
|
||||||
|
val md_theme_light_errorContainer = Color(0xFFffdad4)
|
||||||
|
val md_theme_light_onError = Color(0xFFffffff)
|
||||||
|
val md_theme_light_onErrorContainer = Color(0xFF410001)
|
||||||
|
val md_theme_light_background = Color(0xFFfcfcf7)
|
||||||
|
val md_theme_light_onBackground = Color(0xFF1a1c19)
|
||||||
|
val md_theme_light_surface = Color(0xFFfcfcf7)
|
||||||
|
val md_theme_light_onSurface = Color(0xFF1a1c19)
|
||||||
|
val md_theme_light_surfaceVariant = Color(0xFFdde5d9)
|
||||||
|
val md_theme_light_onSurfaceVariant = Color(0xFF414940)
|
||||||
|
val md_theme_light_outline = Color(0xFF727970)
|
||||||
|
val md_theme_light_inverseOnSurface = Color(0xFFf0f1eb)
|
||||||
|
val md_theme_light_inverseSurface = Color(0xFF2f312e)
|
||||||
|
|
||||||
|
|
||||||
|
val md_theme_dark_primary = Color(0xFF87d991)
|
||||||
|
val md_theme_dark_onPrimary = Color(0xFF003911)
|
||||||
|
val md_theme_dark_primaryContainer = Color(0xFF00531d)
|
||||||
|
val md_theme_dark_onPrimaryContainer = Color(0xFFa3f6ab)
|
||||||
|
val md_theme_dark_secondary = Color(0xFFb8ccb6)
|
||||||
|
val md_theme_dark_onSecondary = Color(0xFF243425)
|
||||||
|
val md_theme_dark_secondaryContainer = Color(0xFF3a4b3a)
|
||||||
|
val md_theme_dark_onSecondaryContainer = Color(0xFFd4e8d1)
|
||||||
|
val md_theme_dark_tertiary = Color(0xFFa1ced6)
|
||||||
|
val md_theme_dark_onTertiary = Color(0xFF00363d)
|
||||||
|
val md_theme_dark_tertiaryContainer = Color(0xFF1f4d54)
|
||||||
|
val md_theme_dark_onTertiaryContainer = Color(0xFFbceaf3)
|
||||||
|
val md_theme_dark_error = Color(0xFFffb4a9)
|
||||||
|
val md_theme_dark_errorContainer = Color(0xFF930006)
|
||||||
|
val md_theme_dark_onError = Color(0xFF680003)
|
||||||
|
val md_theme_dark_onErrorContainer = Color(0xFFffdad4)
|
||||||
|
val md_theme_dark_background = Color(0xFF1a1c19)
|
||||||
|
val md_theme_dark_onBackground = Color(0xFFe1e3dd)
|
||||||
|
val md_theme_dark_surface = Color(0xFF1a1c19)
|
||||||
|
val md_theme_dark_onSurface = Color(0xFFe1e3dd)
|
||||||
|
val md_theme_dark_surfaceVariant = Color(0xFF414940)
|
||||||
|
val md_theme_dark_onSurfaceVariant = Color(0xFFc1c9be)
|
||||||
|
val md_theme_dark_outline = Color(0xFF8b9389)
|
||||||
|
val md_theme_dark_inverseOnSurface = Color(0xFF1a1c19)
|
||||||
|
val md_theme_dark_inverseSurface = Color(0xFFe1e3dd)
|
||||||
|
|
||||||
|
val md_theme_amoled_black = Color(0xFF000000)
|
@ -0,0 +1,174 @@
|
|||||||
|
package com.sadellie.unitto.ui.theme
|
||||||
|
|
||||||
|
import android.app.WallpaperManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.luminance
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shifts colors to make them brighter
|
||||||
|
*/
|
||||||
|
@Stable
|
||||||
|
private fun Color.shiftTo255(ratio: Float): Color {
|
||||||
|
return this
|
||||||
|
.copy(
|
||||||
|
alpha,
|
||||||
|
red + (1.0f - red) * ratio,
|
||||||
|
green + (1.0f - green) * ratio,
|
||||||
|
blue + (1.0f - blue) * ratio,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shifts colors to make them darker
|
||||||
|
*/
|
||||||
|
@Stable
|
||||||
|
private fun Color.shiftTo0(ratio: Float): Color {
|
||||||
|
return this
|
||||||
|
.copy(
|
||||||
|
alpha,
|
||||||
|
red * (1.0f - ratio),
|
||||||
|
green * (1.0f - ratio),
|
||||||
|
blue * (1.0f - ratio),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decides which colors fits the best for the color that is used as a background
|
||||||
|
*/
|
||||||
|
@Stable
|
||||||
|
private fun Color.getAppropriateTextColor(): Color {
|
||||||
|
return if (luminance() > 0.5) Color.Black else Color.White
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to return dynamic light theme. Determines which API to use based on Android version
|
||||||
|
*/
|
||||||
|
fun dynamicLightTheme(context: Context): ColorScheme {
|
||||||
|
return when {
|
||||||
|
// Android 12+ devices use new api
|
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> dynamicLightColorScheme(context)
|
||||||
|
// Dynamic theming for devices with Android 8.1 up to Android 11
|
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> {
|
||||||
|
// Wallpaper colors can be null. We return default theme for such cases
|
||||||
|
val wallpaperColors = WallpaperManager.getInstance(context)
|
||||||
|
.getWallpaperColors(WallpaperManager.FLAG_SYSTEM)
|
||||||
|
?: return LightThemeColors
|
||||||
|
|
||||||
|
val primary = Color(
|
||||||
|
red = wallpaperColors.primaryColor.red(),
|
||||||
|
green = wallpaperColors.primaryColor.green(),
|
||||||
|
blue = wallpaperColors.primaryColor.blue()
|
||||||
|
)
|
||||||
|
// Secondary color can be null. For such cases we just generate it from primary
|
||||||
|
val secondary: Color =
|
||||||
|
if (wallpaperColors.secondaryColor == null) {
|
||||||
|
primary.shiftTo255(0.5f)
|
||||||
|
} else {
|
||||||
|
Color(
|
||||||
|
red = wallpaperColors.secondaryColor!!.red(),
|
||||||
|
green = wallpaperColors.secondaryColor!!.green(),
|
||||||
|
blue = wallpaperColors.secondaryColor!!.blue()
|
||||||
|
)
|
||||||
|
}.shiftTo255(0.5f)
|
||||||
|
val background = primary.shiftTo255(0.9f)
|
||||||
|
val onBackground = background.getAppropriateTextColor()
|
||||||
|
|
||||||
|
return lightColorScheme(
|
||||||
|
// Settings screen group text, units screen units group text
|
||||||
|
primary = primary.shiftTo0(0.7f),
|
||||||
|
// Text color on Third party Licenses screen
|
||||||
|
onPrimary = primary.shiftTo255(0.3f),
|
||||||
|
// Selected unit, group, keyboard buttons
|
||||||
|
secondaryContainer = secondary,
|
||||||
|
onSecondaryContainer = secondary.getAppropriateTextColor(),
|
||||||
|
// Background color for all screens
|
||||||
|
background = background,
|
||||||
|
// Main screen input/output text color
|
||||||
|
onBackground = onBackground,
|
||||||
|
// Collapsable top bar background
|
||||||
|
surface = background,
|
||||||
|
// Main screen Unitto text, disabled buttons
|
||||||
|
// Settings screen title and back icon, dialog window title, not selected radio button color
|
||||||
|
// Third party licenses screen title and back icon
|
||||||
|
onSurface = onBackground,
|
||||||
|
surfaceVariant = primary.shiftTo255(0.5f),
|
||||||
|
// Main Screen settings icon, Not selected chips text, short unit names
|
||||||
|
// Settings items secondary text
|
||||||
|
onSurfaceVariant = primary.shiftTo0(0.80f),
|
||||||
|
// Chips outline and cards outline on Third party licenses screen
|
||||||
|
outline = primary.shiftTo0(0.8f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Just in case
|
||||||
|
else -> LightThemeColors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to return dynamic light theme. Determines which API to use based on Android version
|
||||||
|
*/
|
||||||
|
fun dynamicDarkTheme(context: Context): ColorScheme {
|
||||||
|
return when {
|
||||||
|
// Android 12+ devices use new api
|
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> dynamicDarkColorScheme(context)
|
||||||
|
// Dynamic theming for devices with Android 8.1 up to Android 11
|
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> {
|
||||||
|
// Wallpaper colors can be null. We return default theme for such cases
|
||||||
|
val wallpaperColors = WallpaperManager.getInstance(context)
|
||||||
|
.getWallpaperColors(WallpaperManager.FLAG_SYSTEM)
|
||||||
|
?: return DarkThemeColors
|
||||||
|
|
||||||
|
val primary = Color(
|
||||||
|
red = wallpaperColors.primaryColor.red(),
|
||||||
|
green = wallpaperColors.primaryColor.green(),
|
||||||
|
blue = wallpaperColors.primaryColor.blue()
|
||||||
|
)
|
||||||
|
// Secondary color can be null. For such cases we just generate it from primary
|
||||||
|
val secondary: Color =
|
||||||
|
if (wallpaperColors.secondaryColor == null) {
|
||||||
|
primary.shiftTo0(0.5f)
|
||||||
|
} else {
|
||||||
|
Color(
|
||||||
|
red = wallpaperColors.secondaryColor!!.red(),
|
||||||
|
green = wallpaperColors.secondaryColor!!.green(),
|
||||||
|
blue = wallpaperColors.secondaryColor!!.blue()
|
||||||
|
)
|
||||||
|
}.shiftTo0(0.5f)
|
||||||
|
val background = primary.shiftTo0(0.9f)
|
||||||
|
val onBackground = background.getAppropriateTextColor()
|
||||||
|
|
||||||
|
return darkColorScheme(
|
||||||
|
// Settings screen group text, units screen units group text
|
||||||
|
primary = primary.shiftTo255(0.7f),
|
||||||
|
// Text color on Third party Licenses screen
|
||||||
|
onPrimary = primary.shiftTo0(0.3f),
|
||||||
|
// Selected unit, group, keyboard buttons
|
||||||
|
secondaryContainer = secondary,
|
||||||
|
onSecondaryContainer = secondary.getAppropriateTextColor(),
|
||||||
|
// Background color for all screens
|
||||||
|
background = background,
|
||||||
|
// Main screen input/output text color
|
||||||
|
onBackground = onBackground,
|
||||||
|
// Collapsable top bar background
|
||||||
|
surface = background,
|
||||||
|
// Main screen Unitto text, disabled buttons
|
||||||
|
// Settings screen title and back icon, dialog window title, not selected radio button color
|
||||||
|
// Third party licenses screen title and back icon
|
||||||
|
onSurface = onBackground,
|
||||||
|
surfaceVariant = primary.shiftTo0(0.5f),
|
||||||
|
// Main Screen settings icon, Not selected chips text, short unit names
|
||||||
|
// Settings items secondary text
|
||||||
|
onSurfaceVariant = primary.shiftTo255(0.80f),
|
||||||
|
// Chips outline and cards outline on Third party licenses screen
|
||||||
|
outline = primary.shiftTo255(0.8f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Just in case
|
||||||
|
else -> DarkThemeColors
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package com.sadellie.unitto.ui.theme
|
||||||
|
|
||||||
|
import androidx.compose.material3.darkColorScheme
|
||||||
|
import androidx.compose.material3.lightColorScheme
|
||||||
|
|
||||||
|
val LightThemeColors by lazy {
|
||||||
|
lightColorScheme(
|
||||||
|
primary = md_theme_light_primary,
|
||||||
|
onPrimary = md_theme_light_onPrimary,
|
||||||
|
primaryContainer = md_theme_light_primaryContainer,
|
||||||
|
onPrimaryContainer = md_theme_light_onPrimaryContainer,
|
||||||
|
secondary = md_theme_light_secondary,
|
||||||
|
onSecondary = md_theme_light_onSecondary,
|
||||||
|
secondaryContainer = md_theme_light_secondaryContainer,
|
||||||
|
onSecondaryContainer = md_theme_light_onSecondaryContainer,
|
||||||
|
tertiary = md_theme_light_tertiary,
|
||||||
|
onTertiary = md_theme_light_onTertiary,
|
||||||
|
tertiaryContainer = md_theme_light_tertiaryContainer,
|
||||||
|
onTertiaryContainer = md_theme_light_onTertiaryContainer,
|
||||||
|
error = md_theme_light_error,
|
||||||
|
errorContainer = md_theme_light_errorContainer,
|
||||||
|
onError = md_theme_light_onError,
|
||||||
|
onErrorContainer = md_theme_light_onErrorContainer,
|
||||||
|
background = md_theme_light_background,
|
||||||
|
onBackground = md_theme_light_onBackground,
|
||||||
|
surface = md_theme_light_surface,
|
||||||
|
onSurface = md_theme_light_onSurface,
|
||||||
|
surfaceVariant = md_theme_light_surfaceVariant,
|
||||||
|
onSurfaceVariant = md_theme_light_onSurfaceVariant,
|
||||||
|
outline = md_theme_light_outline,
|
||||||
|
inverseOnSurface = md_theme_light_inverseOnSurface,
|
||||||
|
inverseSurface = md_theme_light_inverseSurface,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val DarkThemeColors by lazy {
|
||||||
|
darkColorScheme(
|
||||||
|
primary = md_theme_dark_primary,
|
||||||
|
onPrimary = md_theme_dark_onPrimary,
|
||||||
|
primaryContainer = md_theme_dark_primaryContainer,
|
||||||
|
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
|
||||||
|
secondary = md_theme_dark_secondary,
|
||||||
|
onSecondary = md_theme_dark_onSecondary,
|
||||||
|
secondaryContainer = md_theme_dark_secondaryContainer,
|
||||||
|
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
|
||||||
|
tertiary = md_theme_dark_tertiary,
|
||||||
|
onTertiary = md_theme_dark_onTertiary,
|
||||||
|
tertiaryContainer = md_theme_dark_tertiaryContainer,
|
||||||
|
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
|
||||||
|
error = md_theme_dark_error,
|
||||||
|
errorContainer = md_theme_dark_errorContainer,
|
||||||
|
onError = md_theme_dark_onError,
|
||||||
|
onErrorContainer = md_theme_dark_onErrorContainer,
|
||||||
|
background = md_theme_dark_background,
|
||||||
|
onBackground = md_theme_dark_onBackground,
|
||||||
|
surface = md_theme_dark_surface,
|
||||||
|
onSurface = md_theme_dark_onSurface,
|
||||||
|
surfaceVariant = md_theme_dark_surfaceVariant,
|
||||||
|
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
|
||||||
|
outline = md_theme_dark_outline,
|
||||||
|
inverseOnSurface = md_theme_dark_inverseOnSurface,
|
||||||
|
inverseSurface = md_theme_dark_inverseSurface,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val AmoledThemeColors by lazy {
|
||||||
|
DarkThemeColors.copy(
|
||||||
|
background = md_theme_amoled_black,
|
||||||
|
surface = md_theme_amoled_black,
|
||||||
|
)
|
||||||
|
}
|
48
app/src/main/java/com/sadellie/unitto/ui/theme/Theme.kt
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package com.sadellie.unitto.ui.theme
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
|
import com.sadellie.unitto.data.preferences.AppTheme
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AppTheme(
|
||||||
|
currentAppTheme: Int,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
// Dynamic color is only for Android 12 and higher
|
||||||
|
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1
|
||||||
|
|
||||||
|
val colors = when (currentAppTheme) {
|
||||||
|
AppTheme.DARK -> DarkThemeColors
|
||||||
|
AppTheme.LIGHT -> LightThemeColors
|
||||||
|
AppTheme.AMOLED -> AmoledThemeColors
|
||||||
|
AppTheme.LIGHT_DYNAMIC -> dynamicLightTheme(LocalContext.current)
|
||||||
|
AppTheme.DARK_DYNAMIC -> dynamicDarkTheme(LocalContext.current)
|
||||||
|
// When it is set to Auto
|
||||||
|
else -> {
|
||||||
|
when {
|
||||||
|
dynamicColor and !isSystemInDarkTheme() -> dynamicLightTheme(LocalContext.current)
|
||||||
|
dynamicColor and isSystemInDarkTheme() -> dynamicDarkTheme(LocalContext.current)
|
||||||
|
!dynamicColor and !isSystemInDarkTheme() -> LightThemeColors
|
||||||
|
!dynamicColor and isSystemInDarkTheme() -> DarkThemeColors
|
||||||
|
// This case is here just in case, not actually used
|
||||||
|
else -> LightThemeColors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rememberSystemUiController().setSystemBarsColor(
|
||||||
|
color = colors.background
|
||||||
|
)
|
||||||
|
|
||||||
|
MaterialTheme(
|
||||||
|
colorScheme = colors,
|
||||||
|
typography = AppTypography,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
140
app/src/main/java/com/sadellie/unitto/ui/theme/Type.kt
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package com.sadellie.unitto.ui.theme
|
||||||
|
|
||||||
|
import androidx.compose.material3.Typography
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.sadellie.unitto.R
|
||||||
|
|
||||||
|
val OPEN_SANS_FONT = FontFamily(
|
||||||
|
Font(R.font.opensans_regular, weight = FontWeight.W400),
|
||||||
|
Font(R.font.opensans_medium, weight = FontWeight.Medium)
|
||||||
|
)
|
||||||
|
|
||||||
|
// This text style is used for text field
|
||||||
|
val NumbersTextStyleDisplayLarge = TextStyle(
|
||||||
|
fontFamily = FontFamily(Font(R.font.lato_regular, weight = FontWeight.W400)),
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
fontSize = 57.sp,
|
||||||
|
lineHeight = 64.sp,
|
||||||
|
letterSpacing = (-0.25).sp,
|
||||||
|
)
|
||||||
|
|
||||||
|
// This text style is used for keyboard button
|
||||||
|
val NumbersTextStyleTitleLarge = TextStyle(
|
||||||
|
fontFamily = FontFamily(Font(R.font.lato_regular, weight = FontWeight.W700)),
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
fontSize = 22.sp,
|
||||||
|
lineHeight = 28.sp,
|
||||||
|
letterSpacing = 0.sp,
|
||||||
|
)
|
||||||
|
|
||||||
|
val AppTypography = Typography(
|
||||||
|
displayLarge = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
fontSize = 57.sp,
|
||||||
|
lineHeight = 64.sp,
|
||||||
|
letterSpacing = (-0.25).sp,
|
||||||
|
),
|
||||||
|
displayMedium = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
fontSize = 45.sp,
|
||||||
|
lineHeight = 52.sp,
|
||||||
|
letterSpacing = 0.sp,
|
||||||
|
),
|
||||||
|
displaySmall = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
fontSize = 36.sp,
|
||||||
|
lineHeight = 44.sp,
|
||||||
|
letterSpacing = 0.sp,
|
||||||
|
),
|
||||||
|
headlineLarge = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
fontSize = 32.sp,
|
||||||
|
lineHeight = 40.sp,
|
||||||
|
letterSpacing = 0.sp,
|
||||||
|
),
|
||||||
|
headlineMedium = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
fontSize = 28.sp,
|
||||||
|
lineHeight = 36.sp,
|
||||||
|
letterSpacing = 0.sp,
|
||||||
|
),
|
||||||
|
headlineSmall = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
fontSize = 24.sp,
|
||||||
|
lineHeight = 32.sp,
|
||||||
|
letterSpacing = 0.sp,
|
||||||
|
),
|
||||||
|
titleLarge = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
fontSize = 22.sp,
|
||||||
|
lineHeight = 28.sp,
|
||||||
|
letterSpacing = 0.sp,
|
||||||
|
),
|
||||||
|
titleMedium = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 24.sp,
|
||||||
|
letterSpacing = 0.1.sp,
|
||||||
|
),
|
||||||
|
titleSmall = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
letterSpacing = 0.1.sp,
|
||||||
|
),
|
||||||
|
labelLarge = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
letterSpacing = 0.1.sp,
|
||||||
|
),
|
||||||
|
bodyLarge = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 24.sp,
|
||||||
|
letterSpacing = 0.5.sp,
|
||||||
|
),
|
||||||
|
bodyMedium = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
letterSpacing = 0.25.sp,
|
||||||
|
),
|
||||||
|
bodySmall = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
lineHeight = 16.sp,
|
||||||
|
letterSpacing = 0.4.sp,
|
||||||
|
),
|
||||||
|
labelMedium = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
lineHeight = 16.sp,
|
||||||
|
letterSpacing = 0.5.sp,
|
||||||
|
),
|
||||||
|
labelSmall = TextStyle(
|
||||||
|
fontFamily = OPEN_SANS_FONT,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
fontSize = 11.sp,
|
||||||
|
lineHeight = 16.sp,
|
||||||
|
letterSpacing = 0.5.sp,
|
||||||
|
),
|
||||||
|
)
|
16
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<group android:scaleX="0.3559164"
|
||||||
|
android:scaleY="0.3559164"
|
||||||
|
android:translateX="37.271927"
|
||||||
|
android:translateY="24.247612">
|
||||||
|
<group android:translateY="133.59375">
|
||||||
|
<path android:pathData="M85.359375,-102L85.359375,-34.09375Q85.359375,-17.90625,74.90625,-8.453125Q64.46875,1,47.109375,1Q29.53125,1,19.1875,-8.3125Q8.859375,-17.625,8.859375,-34.171875L8.859375,-102L26.578125,-102L26.578125,-33.953125Q26.578125,-23.765625,31.78125,-18.375Q36.984375,-13,47.109375,-13Q67.640625,-13,67.640625,-34.515625L67.640625,-102L85.359375,-102Z"
|
||||||
|
android:fillColor="#186C31"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</vector>
|
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 883 B |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 10 KiB |
4
app/src/main/res/values/ic_launcher_background.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#FCFCF7</color>
|
||||||
|
</resources>
|
645
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,645 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name" translatable="false">Unitto</string>
|
||||||
|
|
||||||
|
<!--Length-->
|
||||||
|
<string name="attometer">Attometer</string>
|
||||||
|
<string name="attometer_short">am</string>
|
||||||
|
<string name="nanometer">Nanometer</string>
|
||||||
|
<string name="nanometer_short">nm</string>
|
||||||
|
<string name="micrometer">Micrometer</string>
|
||||||
|
<string name="micrometer_short">μm</string>
|
||||||
|
<string name="millimeter">Millimeter</string>
|
||||||
|
<string name="millimeter_short">mm</string>
|
||||||
|
<string name="centimeter">Centimeter</string>
|
||||||
|
<string name="centimeter_short">cm</string>
|
||||||
|
<string name="decimeter">Decimeter</string>
|
||||||
|
<string name="decimeter_short">dm</string>
|
||||||
|
<string name="meter">Meter</string>
|
||||||
|
<string name="meter_short">m</string>
|
||||||
|
<string name="kilometer">Kilometer</string>
|
||||||
|
<string name="kilometer_short">km</string>
|
||||||
|
<string name="mile">Mile</string>
|
||||||
|
<string name="mile_short">Mi</string>
|
||||||
|
<string name="yard">Yard</string>
|
||||||
|
<string name="yard_short">yd</string>
|
||||||
|
<string name="foot">Foot</string>
|
||||||
|
<string name="foot_short">ft</string>
|
||||||
|
<string name="inch">Inch</string>
|
||||||
|
<string name="inch_short">in</string>
|
||||||
|
<string name="light_year">Light year</string>
|
||||||
|
<string name="light_year_short">ly</string>
|
||||||
|
|
||||||
|
<!--Mass-->
|
||||||
|
<string name="electron_mass_rest">Electron mass</string>
|
||||||
|
<string name="electron_mass_rest_short">me</string>
|
||||||
|
<string name="atomic_mass_unit">Atomic mass unit</string>
|
||||||
|
<string name="atomic_mass_unit_short">u</string>
|
||||||
|
<string name="milligram">Milligram</string>
|
||||||
|
<string name="milligram_short">mg</string>
|
||||||
|
<string name="gram">Gram</string>
|
||||||
|
<string name="gram_short">g</string>
|
||||||
|
<string name="kilogram">Kilogram</string>
|
||||||
|
<string name="kilogram_short">kg</string>
|
||||||
|
<string name="metric_ton">Metric ton</string>
|
||||||
|
<string name="metric_ton_short">t</string>
|
||||||
|
<string name="imperial_ton">Imperial ton</string>
|
||||||
|
<string name="imperial_ton_short">t (UK)</string>
|
||||||
|
<string name="pound">Pound</string>
|
||||||
|
<string name="pound_short">lbs</string>
|
||||||
|
<string name="ounce">Ounce</string>
|
||||||
|
<string name="ounce_short">oz</string>
|
||||||
|
<string name="carat">Carat</string>
|
||||||
|
<string name="carat_short">ct</string>
|
||||||
|
|
||||||
|
<!--Temperature-->
|
||||||
|
<string name="celsius">Celsius</string>
|
||||||
|
<string name="celsius_short">°C</string>
|
||||||
|
<string name="fahrenheit">Fahrenheit</string>
|
||||||
|
<string name="fahrenheit_short">°F</string>
|
||||||
|
<string name="kelvin">Kelvin</string>
|
||||||
|
<string name="kelvin_short">K</string>
|
||||||
|
|
||||||
|
<!--Speed-->
|
||||||
|
<string name="millimeter_per_hour">Millimeter per hour</string>
|
||||||
|
<string name="millimeter_per_hour_short">mm/h</string>
|
||||||
|
<string name="millimeter_per_minute">Millimeter per minute</string>
|
||||||
|
<string name="millimeter_per_minute_short">mm/m</string>
|
||||||
|
<string name="millimeter_per_second">Millimeter per second</string>
|
||||||
|
<string name="millimeter_per_second_short">mm/s</string>
|
||||||
|
<string name="centimeter_per_hour">Centimeter per hour</string>
|
||||||
|
<string name="centimeter_per_hour_short">cm/h</string>
|
||||||
|
<string name="centimeter_per_minute">Centimeter per minute</string>
|
||||||
|
<string name="centimeter_per_minute_short">cm/m</string>
|
||||||
|
<string name="centimeter_per_second">Centimeter per second</string>
|
||||||
|
<string name="centimeter_per_second_short">cm/s</string>
|
||||||
|
<string name="meter_per_hour">Meter per hour</string>
|
||||||
|
<string name="meter_per_hour_short">m/h</string>
|
||||||
|
<string name="meter_per_minute">Meter per minute</string>
|
||||||
|
<string name="meter_per_minute_short">m/m</string>
|
||||||
|
<string name="meter_per_second">Meter per second</string>
|
||||||
|
<string name="meter_per_second_short">m/s</string>
|
||||||
|
<string name="kilometer_per_hour">Kilometer per hour</string>
|
||||||
|
<string name="kilometer_per_hour_short">km/h</string>
|
||||||
|
<string name="kilometer_per_minute">Kilometer per minute</string>
|
||||||
|
<string name="kilometer_per_minute_short">km/m</string>
|
||||||
|
<string name="kilometer_per_second">Kilometer per second</string>
|
||||||
|
<string name="kilometer_per_second_short">km/s</string>
|
||||||
|
<string name="foot_per_hour">Foot per hour</string>
|
||||||
|
<string name="foot_per_hour_short">ft/h</string>
|
||||||
|
<string name="foot_per_minute">Foot per minute</string>
|
||||||
|
<string name="foot_per_minute_short">ft/m</string>
|
||||||
|
<string name="foot_per_second">Foot per second</string>
|
||||||
|
<string name="foot_per_second_short">ft/s</string>
|
||||||
|
<string name="yard_per_hour">Yard per hour</string>
|
||||||
|
<string name="yard_per_hour_short">yd/h</string>
|
||||||
|
<string name="yard_per_minute">Yard per minute</string>
|
||||||
|
<string name="yard_per_minute_short">yd/m</string>
|
||||||
|
<string name="yard_per_second">Yard per second</string>
|
||||||
|
<string name="yard_per_second_short">yd/s</string>
|
||||||
|
<string name="mile_per_hour">Mile per hour</string>
|
||||||
|
<string name="mile_per_hour_short">mi/h</string>
|
||||||
|
<string name="mile_per_minute">Mile per minute</string>
|
||||||
|
<string name="mile_per_minute_short">mi/m</string>
|
||||||
|
<string name="mile_per_second">Mile per second</string>
|
||||||
|
<string name="mile_per_second_short">mi/s</string>
|
||||||
|
<string name="knot">Knot</string>
|
||||||
|
<string name="knot_short">kt</string>
|
||||||
|
<string name="velocity_of_light_in_vacuum">Speed of light in vacuum</string>
|
||||||
|
<string name="velocity_of_light_in_vacuum_short">c</string>
|
||||||
|
<string name="cosmic_velocity_first">First Cosmic Velocity</string>
|
||||||
|
<string name="cosmic_velocity_first_short">v1</string>
|
||||||
|
<string name="cosmic_velocity_second">Second Cosmic Velocity</string>
|
||||||
|
<string name="cosmic_velocity_second_short">v2</string>
|
||||||
|
<string name="cosmic_velocity_third">Third Cosmic Velocity</string>
|
||||||
|
<string name="cosmic_velocity_third_short">v3</string>
|
||||||
|
<string name="earths_orbital_speed">Earth\'s orbital speed</string>
|
||||||
|
<string name="earths_orbital_speed_short">ve</string>
|
||||||
|
<string name="mach">Mach</string>
|
||||||
|
<string name="mach_short">M</string>
|
||||||
|
<string name="mach_si_standard">Mach (SI)</string>
|
||||||
|
<string name="mach_si_standard_short">M</string>
|
||||||
|
|
||||||
|
<!--File size-->
|
||||||
|
<string name="bit">Bit</string>
|
||||||
|
<string name="bit_short">b</string>
|
||||||
|
<string name="kibibit">Kibibit</string>
|
||||||
|
<string name="kibibit_short">Kib</string>
|
||||||
|
<string name="kilobit">Kilobit</string>
|
||||||
|
<string name="kilobit_short">Kb</string>
|
||||||
|
<string name="megabit">Megabit</string>
|
||||||
|
<string name="megabit_short">Mb</string>
|
||||||
|
<string name="gigabit">Gigabit</string>
|
||||||
|
<string name="gigabit_short">Gb</string>
|
||||||
|
<string name="terabit">Terabit</string>
|
||||||
|
<string name="terabit_short">Tb</string>
|
||||||
|
<string name="petabit">Petabit</string>
|
||||||
|
<string name="petabit_short">Pb</string>
|
||||||
|
<string name="exabit">Exabit</string>
|
||||||
|
<string name="exabit_short">Eb</string>
|
||||||
|
<string name="byte_">Byte</string>
|
||||||
|
<string name="byte_short">B</string>
|
||||||
|
<string name="kibibyte">Kibibyte</string>
|
||||||
|
<string name="kibibyte_short">KiB</string>
|
||||||
|
<string name="kilobyte">Kilobyte</string>
|
||||||
|
<string name="kilobyte_short">KB</string>
|
||||||
|
<string name="megabyte">Megabyte</string>
|
||||||
|
<string name="megabyte_short">MB</string>
|
||||||
|
<string name="gigabyte">Gigabyte</string>
|
||||||
|
<string name="gigabyte_short">GB</string>
|
||||||
|
<string name="terabyte">Terabyte</string>
|
||||||
|
<string name="terabyte_short">TB</string>
|
||||||
|
<string name="petabyte">Petabyte</string>
|
||||||
|
<string name="petabyte_short">PB</string>
|
||||||
|
<string name="exabyte">Exabyte</string>
|
||||||
|
<string name="exabyte_short">EB</string>
|
||||||
|
|
||||||
|
<!--Data transfer-->
|
||||||
|
<string name="bit_per_second">Bit per second</string>
|
||||||
|
<string name="bit_per_second_short">b/s</string>
|
||||||
|
<string name="kibibit_per_second">Kibibit per second</string>
|
||||||
|
<string name="kibibit_per_second_short">Kib/s</string>
|
||||||
|
<string name="kilobit_per_second">Kilobit per second</string>
|
||||||
|
<string name="kilobit_per_second_short">Kb/s</string>
|
||||||
|
<string name="megabit_per_second">Megabit per second</string>
|
||||||
|
<string name="megabit_per_second_short">Mb/s</string>
|
||||||
|
<string name="gigabit_per_second">Gigabit per second</string>
|
||||||
|
<string name="gigabit_per_second_short">Gb/s</string>
|
||||||
|
<string name="terabit_per_second">Terabit per second</string>
|
||||||
|
<string name="terabit_per_second_short">Tb/s</string>
|
||||||
|
<string name="petabit_per_second">Petabit per second</string>
|
||||||
|
<string name="petabit_per_second_short">Pb/s</string>
|
||||||
|
<string name="exabit_per_second">Exabit per second</string>
|
||||||
|
<string name="exabit_per_second_short">Eb/s</string>
|
||||||
|
<string name="byte_per_second">Byte per second</string>
|
||||||
|
<string name="byte_per_second_short">B/s</string>
|
||||||
|
<string name="kibibyte_per_second">Kibibyte per second</string>
|
||||||
|
<string name="kibibyte_per_second_short">KiB/s</string>
|
||||||
|
<string name="kilobyte_per_second">Kilobyte per second</string>
|
||||||
|
<string name="kilobyte_per_second_short">KB/s</string>
|
||||||
|
<string name="megabyte_per_second">Megabyte per second</string>
|
||||||
|
<string name="megabyte_per_second_short">MB/s</string>
|
||||||
|
<string name="gigabyte_per_second">Gigabyte per second</string>
|
||||||
|
<string name="gigabyte_per_second_short">GB/s</string>
|
||||||
|
<string name="terabyte_per_second">Terabyte per second</string>
|
||||||
|
<string name="terabyte_per_second_short">TB/s</string>
|
||||||
|
<string name="petabyte_per_second">Petabyte per second</string>
|
||||||
|
<string name="petabyte_per_second_short">PB/s</string>
|
||||||
|
<string name="exabyte_per_second">Exabyte per second</string>
|
||||||
|
<string name="exabyte_per_second_short">EB/s</string>
|
||||||
|
|
||||||
|
<!--Volume-->
|
||||||
|
<string name="attoliter">Attoliter</string>
|
||||||
|
<string name="attoliter_short">aL</string>
|
||||||
|
<string name="milliliter">Milliliter</string>
|
||||||
|
<string name="milliliter_short">mL</string>
|
||||||
|
<string name="liter">Liter</string>
|
||||||
|
<string name="liter_short">L</string>
|
||||||
|
<string name="us_liquid_gallon">US liquid gallon</string>
|
||||||
|
<string name="us_liquid_gallon_short">gal (US)</string>
|
||||||
|
<string name="us_liquid_quart">US liquid quart</string>
|
||||||
|
<string name="us_liquid_quart_short">qt (US)</string>
|
||||||
|
<string name="us_liquid_pint">US liquid pint</string>
|
||||||
|
<string name="us_liquid_pint_short">pt (US)</string>
|
||||||
|
<string name="us_legal_cup">US legal cup</string>
|
||||||
|
<string name="us_legal_cup_short">cup (US)</string>
|
||||||
|
<string name="us_fluid_ounce">US fluid ounce</string>
|
||||||
|
<string name="us_fluid_ounce_short">fl oz (US)</string>
|
||||||
|
<string name="us_tablespoon">US tablespoon</string>
|
||||||
|
<string name="us_tablespoon_short">tablespoon (US)</string>
|
||||||
|
<string name="us_teaspoon">US teaspoon</string>
|
||||||
|
<string name="us_teaspoon_short">teaspoon (US)</string>
|
||||||
|
<string name="imperial_gallon">Imperial gallon</string>
|
||||||
|
<string name="imperial_gallon_short">gal (UK)</string>
|
||||||
|
<string name="imperial_quart">Imperial quart</string>
|
||||||
|
<string name="imperial_quart_short">qt (UK)</string>
|
||||||
|
<string name="imperial_pint">Imperial pint</string>
|
||||||
|
<string name="imperial_pint_short">pt (UK)</string>
|
||||||
|
<string name="imperial_cup">Imperial cup</string>
|
||||||
|
<string name="imperial_cup_short">cup (UK)</string>
|
||||||
|
<string name="imperial_fluid_ounce">Imperial fluid ounce</string>
|
||||||
|
<string name="imperial_fluid_ounce_short">fl oz (UK)</string>
|
||||||
|
<string name="imperial_tablespoon">Imperial tablespoon</string>
|
||||||
|
<string name="imperial_tablespoon_short">tablespoon (UK)</string>
|
||||||
|
<string name="imperial_teaspoon">Imperial teaspoon</string>
|
||||||
|
<string name="imperial_teaspoon_short">teaspoon (UK)</string>
|
||||||
|
<string name="cubic_millimeter">Cubic millimeter</string>
|
||||||
|
<string name="cubic_millimeter_short">mm^3</string>
|
||||||
|
<string name="cubic_centimeter">Cubic centimeter</string>
|
||||||
|
<string name="cubic_centimeter_short">cm^3</string>
|
||||||
|
<string name="cubic_meter">Cubic meter</string>
|
||||||
|
<string name="cubic_meter_short">m^3</string>
|
||||||
|
<string name="cubic_kilometer">Cubic kilometer</string>
|
||||||
|
<string name="cubic_kilometer_short">km^3</string>
|
||||||
|
|
||||||
|
<!--Time-->
|
||||||
|
<string name="attosecond">Attosecond</string>
|
||||||
|
<string name="attosecond_short">as</string>
|
||||||
|
<string name="nanosecond">Nanosecond</string>
|
||||||
|
<string name="nanosecond_short">ns</string>
|
||||||
|
<string name="microsecond">Microsecond</string>
|
||||||
|
<string name="microsecond_short">µs</string>
|
||||||
|
<string name="millisecond">Millisecond</string>
|
||||||
|
<string name="millisecond_short">ms</string>
|
||||||
|
<string name="second">Second</string>
|
||||||
|
<string name="second_short">s</string>
|
||||||
|
<string name="minute">Minute</string>
|
||||||
|
<string name="minute_short">m</string>
|
||||||
|
<string name="hour">Hour</string>
|
||||||
|
<string name="hour_short">h</string>
|
||||||
|
<string name="day">Day</string>
|
||||||
|
<string name="day_short">d</string>
|
||||||
|
<string name="week">Week</string>
|
||||||
|
<string name="week_short">w</string>
|
||||||
|
|
||||||
|
<!--Area-->
|
||||||
|
<string name="electron_cross_section">Electron cross section</string>
|
||||||
|
<string name="electron_cross_section_short">ecs</string>
|
||||||
|
<string name="acre">Acre</string>
|
||||||
|
<string name="acre_short">ac</string>
|
||||||
|
<string name="hectare">Hectare</string>
|
||||||
|
<string name="hectare_short">ha</string>
|
||||||
|
<string name="square_foot">Square foot</string>
|
||||||
|
<string name="square_foot_short">ft^2</string>
|
||||||
|
<string name="square_mile">Square mile</string>
|
||||||
|
<string name="square_mile_short">mi^2</string>
|
||||||
|
<string name="square_yard">Square yard</string>
|
||||||
|
<string name="square_yard_short">yd^2</string>
|
||||||
|
<string name="square_inch">Square inch</string>
|
||||||
|
<string name="square_inch_short">in^2</string>
|
||||||
|
<string name="square_micrometer">Square micrometer</string>
|
||||||
|
<string name="square_micrometer_short">µm^2</string>
|
||||||
|
<string name="square_millimeter">Square millimeter</string>
|
||||||
|
<string name="square_millimeter_short">mm^2</string>
|
||||||
|
<string name="square_centimeter">Square centimeter</string>
|
||||||
|
<string name="square_centimeter_short">cm^2</string>
|
||||||
|
<string name="square_decimeter">Square decimeter</string>
|
||||||
|
<string name="square_decimeter_short">dm^2</string>
|
||||||
|
<string name="square_meter">Square meter</string>
|
||||||
|
<string name="square_meter_short">m^2</string>
|
||||||
|
<string name="square_kilometer">Square kilometer</string>
|
||||||
|
<string name="square_kilometer_short">km^2</string>
|
||||||
|
|
||||||
|
<!--Energy-->
|
||||||
|
<string name="electron_volt">Electron volt</string>
|
||||||
|
<string name="electron_volt_short">eV</string>
|
||||||
|
<string name="attojoule">Attojoule</string>
|
||||||
|
<string name="attojoule_short">aJ</string>
|
||||||
|
<string name="energy_horse_power_metric">Horse power</string>
|
||||||
|
<string name="energy_horse_power_metric_short">hp</string>
|
||||||
|
<string name="joule">Joule</string>
|
||||||
|
<string name="joule_short">J</string>
|
||||||
|
<string name="kilojoule">Kilojoule</string>
|
||||||
|
<string name="kilojoule_short">kJ</string>
|
||||||
|
<string name="gigajoule">Megajoule</string>
|
||||||
|
<string name="gigajoule_short">MJ</string>
|
||||||
|
<string name="megajoule">Gigajoule</string>
|
||||||
|
<string name="megajoule_short">GJ</string>
|
||||||
|
<string name="energy_ton">Ton of TNT</string>
|
||||||
|
<string name="energy_ton_short">t</string>
|
||||||
|
<string name="kiloton">Kiloton of TNT</string>
|
||||||
|
<string name="kiloton_short">kt</string>
|
||||||
|
<string name="megaton">Megaton of TNT</string>
|
||||||
|
<string name="megaton_short">Mt</string>
|
||||||
|
<string name="gigaton">Gigaton of TNT</string>
|
||||||
|
<string name="gigaton_short">Gt</string>
|
||||||
|
<string name="calorie_th">Calorie (th)</string>
|
||||||
|
<string name="calorie_th_short">cal</string>
|
||||||
|
<string name="kilocalorie_th">Kilocalorie (th)</string>
|
||||||
|
<string name="kilocalorie_th_short">kcal</string>
|
||||||
|
|
||||||
|
<!--Power-->
|
||||||
|
<string name="attowatt">Attowatt</string>
|
||||||
|
<string name="attowatt_short">aW</string>
|
||||||
|
<string name="watt">Watt</string>
|
||||||
|
<string name="watt_short">W</string>
|
||||||
|
<string name="kilowatt">Kilowatt</string>
|
||||||
|
<string name="kilowatt_short">kW</string>
|
||||||
|
<string name="megawatt">Megawatt</string>
|
||||||
|
<string name="megawatt_short">MG</string>
|
||||||
|
<string name="horse_power_mechanical">Horsepower</string>
|
||||||
|
<string name="horse_power_mechanical_short">hp</string>
|
||||||
|
|
||||||
|
<!--Angle-->
|
||||||
|
<string name="angle_second">Second</string>
|
||||||
|
<string name="angle_second_short">\"</string>
|
||||||
|
<string name="angle_minute">Minute</string>
|
||||||
|
<string name="angle_minute_short">\'</string>
|
||||||
|
<string name="degree">Degree</string>
|
||||||
|
<string name="degree_short">°</string>
|
||||||
|
<string name="radian">Radian</string>
|
||||||
|
<string name="radian_short">rad</string>
|
||||||
|
<string name="sextant">Sextant</string>
|
||||||
|
<string name="sextant_short">sxt</string>
|
||||||
|
<string name="turn">Turn</string>
|
||||||
|
<string name="turn_short">tr</string>
|
||||||
|
|
||||||
|
<!--Currency-->
|
||||||
|
<string name="currency_1inch">1inch Network</string>
|
||||||
|
<string name="currency_ada">Cardano</string>
|
||||||
|
<string name="currency_aed">United Arab Emirates Dirham</string>
|
||||||
|
<string name="currency_afn">Afghan afghani</string>
|
||||||
|
<string name="currency_algo">Algorand</string>
|
||||||
|
<string name="currency_all">Albanian lek</string>
|
||||||
|
<string name="currency_amd">Armenian dram</string>
|
||||||
|
<string name="currency_ang">Netherlands Antillean Guilder</string>
|
||||||
|
<string name="currency_aoa">Angolan kwanza</string>
|
||||||
|
<string name="currency_ars">Argentine peso</string>
|
||||||
|
<string name="currency_atom">Atomic Coin</string>
|
||||||
|
<string name="currency_aud">Australian dollar</string>
|
||||||
|
<string name="currency_avax">Avalanche</string>
|
||||||
|
<string name="currency_awg">Aruban florin</string>
|
||||||
|
<string name="currency_azn">Azerbaijani manat</string>
|
||||||
|
<string name="currency_bam">Bosnia-Herzegovina Convertible Mark</string>
|
||||||
|
<string name="currency_bbd">Bajan dollar</string>
|
||||||
|
<string name="currency_bch">Bitcoin Cash</string>
|
||||||
|
<string name="currency_bdt">Bangladeshi taka</string>
|
||||||
|
<string name="currency_bgn">Bulgarian lev</string>
|
||||||
|
<string name="currency_bhd">Bahraini dinar</string>
|
||||||
|
<string name="currency_bif">Burundian Franc</string>
|
||||||
|
<string name="currency_bmd">Bermudan dollar</string>
|
||||||
|
<string name="currency_bnb">Binance Coin</string>
|
||||||
|
<string name="currency_bnd">Brunei dollar</string>
|
||||||
|
<string name="currency_bob">Bolivian boliviano</string>
|
||||||
|
<string name="currency_brl">Brazilian real</string>
|
||||||
|
<string name="currency_bsd">Bahamian dollar</string>
|
||||||
|
<string name="currency_btc">Bitcoin</string>
|
||||||
|
<string name="currency_btn">Bhutan currency</string>
|
||||||
|
<string name="currency_busd">Binance USD</string>
|
||||||
|
<string name="currency_bwp">Botswanan Pula</string>
|
||||||
|
<string name="currency_byn">New Belarusian Ruble</string>
|
||||||
|
<string name="currency_byr">Belarusian Ruble</string>
|
||||||
|
<string name="currency_bzd">Belize dollar</string>
|
||||||
|
<string name="currency_cad">Canadian dollar</string>
|
||||||
|
<string name="currency_cdf">Congolese franc</string>
|
||||||
|
<string name="currency_chf">Swiss franc</string>
|
||||||
|
<string name="currency_chz">Chiliz</string>
|
||||||
|
<string name="currency_clf">Chilean Unit of Account (UF)</string>
|
||||||
|
<string name="currency_clp">Chilean peso</string>
|
||||||
|
<string name="currency_cny">Chinese Yuan</string>
|
||||||
|
<string name="currency_cop">Colombian peso</string>
|
||||||
|
<string name="currency_crc">Costa Rican Colón</string>
|
||||||
|
<string name="currency_cro">Crypto.com Chain Token</string>
|
||||||
|
<string name="currency_cuc">Cuban peso</string>
|
||||||
|
<string name="currency_cup">Cuban Peso</string>
|
||||||
|
<string name="currency_cve">Cape Verdean escudo</string>
|
||||||
|
<string name="currency_czk">Czech koruna</string>
|
||||||
|
<string name="currency_dai">Dai</string>
|
||||||
|
<string name="currency_djf">Djiboutian franc</string>
|
||||||
|
<string name="currency_dkk">Danish krone</string>
|
||||||
|
<string name="currency_doge">Dogecoin</string>
|
||||||
|
<string name="currency_dop">Dominican peso</string>
|
||||||
|
<string name="currency_dot">Dotcoin</string>
|
||||||
|
<string name="currency_dzd">Algerian dinar</string>
|
||||||
|
<string name="currency_egld">Elrond</string>
|
||||||
|
<string name="currency_egp">Egyptian pound</string>
|
||||||
|
<string name="currency_enj">Enjin Coin</string>
|
||||||
|
<string name="currency_ern">Eritrean nakfa</string>
|
||||||
|
<string name="currency_etb">Ethiopian birr</string>
|
||||||
|
<string name="currency_etc">Ethereum Classic</string>
|
||||||
|
<string name="currency_eth">Ether</string>
|
||||||
|
<string name="currency_eur">Euro</string>
|
||||||
|
<string name="currency_fil">FileCoin</string>
|
||||||
|
<string name="currency_fjd">Fijian dollar</string>
|
||||||
|
<string name="currency_fkp">Falkland Islands pound</string>
|
||||||
|
<string name="currency_ftt">FarmaTrust</string>
|
||||||
|
<string name="currency_gbp">Pound sterling</string>
|
||||||
|
<string name="currency_gel">Georgian lari</string>
|
||||||
|
<string name="currency_ggp">GGPro</string>
|
||||||
|
<string name="currency_ghs">Ghanaian cedi</string>
|
||||||
|
<string name="currency_gip">Gibraltar pound</string>
|
||||||
|
<string name="currency_gmd">Gambian dalasi</string>
|
||||||
|
<string name="currency_gnf">Guinean franc</string>
|
||||||
|
<string name="currency_grt">Golden Ratio Token</string>
|
||||||
|
<string name="currency_gtq">Guatemalan quetzal</string>
|
||||||
|
<string name="currency_gyd">Guyanaese Dollar</string>
|
||||||
|
<string name="currency_hkd">Hong Kong dollar</string>
|
||||||
|
<string name="currency_hnl">Honduran lempira</string>
|
||||||
|
<string name="currency_hrk">Croatian kuna</string>
|
||||||
|
<string name="currency_htg">Haitian gourde</string>
|
||||||
|
<string name="currency_huf">Hungarian forint</string>
|
||||||
|
<string name="currency_icp">Internet Computer</string>
|
||||||
|
<string name="currency_idr">Indonesian rupiah</string>
|
||||||
|
<string name="currency_ils">Israeli New Shekel</string>
|
||||||
|
<string name="currency_imp">CoinIMP</string>
|
||||||
|
<string name="currency_inj">Injective</string>
|
||||||
|
<string name="currency_inr">Indian rupee</string>
|
||||||
|
<string name="currency_iqd">Iraqi dinar</string>
|
||||||
|
<string name="currency_irr">Iranian rial</string>
|
||||||
|
<string name="currency_isk">Icelandic króna</string>
|
||||||
|
<string name="currency_jep">Jersey Pound</string>
|
||||||
|
<string name="currency_jmd">Jamaican dollar</string>
|
||||||
|
<string name="currency_jod">Jordanian dinar</string>
|
||||||
|
<string name="currency_jpy">Japanese yen</string>
|
||||||
|
<string name="currency_kes">Kenyan shilling</string>
|
||||||
|
<string name="currency_kgs">Kyrgystani Som</string>
|
||||||
|
<string name="currency_khr">Cambodian riel</string>
|
||||||
|
<string name="currency_kmf">Comorian franc</string>
|
||||||
|
<string name="currency_kpw">North Korean won</string>
|
||||||
|
<string name="currency_krw">South Korean won</string>
|
||||||
|
<string name="currency_ksm">Kusama</string>
|
||||||
|
<string name="currency_kwd">Kuwaiti dinar</string>
|
||||||
|
<string name="currency_kyd">Cayman Islands dollar</string>
|
||||||
|
<string name="currency_kzt">Kazakhstani tenge</string>
|
||||||
|
<string name="currency_lak">Laotian Kip</string>
|
||||||
|
<string name="currency_lbp">Lebanese pound</string>
|
||||||
|
<string name="currency_link">ChainLink</string>
|
||||||
|
<string name="currency_lkr">Sri Lankan rupee</string>
|
||||||
|
<string name="currency_lrd">Liberian dollar</string>
|
||||||
|
<string name="currency_lsl">Lesotho loti</string>
|
||||||
|
<string name="currency_ltc">Litecoin</string>
|
||||||
|
<string name="currency_ltl">Lithuanian litas</string>
|
||||||
|
<string name="currency_luna">Luna Coin</string>
|
||||||
|
<string name="currency_lvl">Latvian lats</string>
|
||||||
|
<string name="currency_lyd">Libyan dinar</string>
|
||||||
|
<string name="currency_mad">Moroccan dirham</string>
|
||||||
|
<string name="currency_matic">Polygon</string>
|
||||||
|
<string name="currency_mdl">Moldovan leu</string>
|
||||||
|
<string name="currency_mga">Malagasy ariary</string>
|
||||||
|
<string name="currency_mkd">Macedonian denar</string>
|
||||||
|
<string name="currency_mmk">Myanmar Kyat</string>
|
||||||
|
<string name="currency_mnt">Mongolian tugrik</string>
|
||||||
|
<string name="currency_mop">Macanese pataca</string>
|
||||||
|
<string name="currency_mro">Mauritanian ouguiya</string>
|
||||||
|
<string name="currency_mur">Mauritian rupee</string>
|
||||||
|
<string name="currency_mvr">Maldivian rufiyaa</string>
|
||||||
|
<string name="currency_mwk">Malawian kwacha</string>
|
||||||
|
<string name="currency_mxn">Mexican peso</string>
|
||||||
|
<string name="currency_myr">Malaysian ringgit</string>
|
||||||
|
<string name="currency_mzn">Mozambican Metical</string>
|
||||||
|
<string name="currency_nad">Namibian dollar</string>
|
||||||
|
<string name="currency_ngn">Nigerian naira</string>
|
||||||
|
<string name="currency_nio">Nicaraguan córdoba</string>
|
||||||
|
<string name="currency_nok">Norwegian krone</string>
|
||||||
|
<string name="currency_npr">Nepalese rupee</string>
|
||||||
|
<string name="currency_nzd">New Zealand dollar</string>
|
||||||
|
<string name="currency_omr">Omani rial</string>
|
||||||
|
<string name="currency_one">Menlo One</string>
|
||||||
|
<string name="currency_pab">Panamanian balboa</string>
|
||||||
|
<string name="currency_pen">Sol</string>
|
||||||
|
<string name="currency_pgk">Papua New Guinean kina</string>
|
||||||
|
<string name="currency_php">Philippine peso</string>
|
||||||
|
<string name="currency_pkr">Pakistani rupee</string>
|
||||||
|
<string name="currency_pln">Poland złoty</string>
|
||||||
|
<string name="currency_pyg">Paraguayan guarani</string>
|
||||||
|
<string name="currency_qar">Qatari Rial</string>
|
||||||
|
<string name="currency_ron">Romanian leu</string>
|
||||||
|
<string name="currency_rsd">Serbian dinar</string>
|
||||||
|
<string name="currency_rub">Russian ruble</string>
|
||||||
|
<string name="currency_rwf">Rwandan Franc</string>
|
||||||
|
<string name="currency_sar">Saudi riyal</string>
|
||||||
|
<string name="currency_sbd">Solomon Islands dollar</string>
|
||||||
|
<string name="currency_scr">Seychellois rupee</string>
|
||||||
|
<string name="currency_sdg">Sudanese pound</string>
|
||||||
|
<string name="currency_sek">Swedish krona</string>
|
||||||
|
<string name="currency_sgd">Singapore dollar</string>
|
||||||
|
<string name="currency_shib">Shiba Inu</string>
|
||||||
|
<string name="currency_shp">Saint Helena pound</string>
|
||||||
|
<string name="currency_sll">Sierra Leonean leone</string>
|
||||||
|
<string name="currency_sol">Sola</string>
|
||||||
|
<string name="currency_sos">Somali shilling</string>
|
||||||
|
<string name="currency_srd">Surinamese dollar</string>
|
||||||
|
<string name="currency_std">São Tomé and Príncipe Dobra (pre-2018)</string>
|
||||||
|
<string name="currency_svc">Salvadoran Colón</string>
|
||||||
|
<string name="currency_syp">Syrian pound</string>
|
||||||
|
<string name="currency_szl">Swazi lilangeni</string>
|
||||||
|
<string name="currency_thb">Thai baht</string>
|
||||||
|
<string name="currency_theta">Theta</string>
|
||||||
|
<string name="currency_tjs">Tajikistani somoni</string>
|
||||||
|
<string name="currency_tmt">Turkmenistani manat</string>
|
||||||
|
<string name="currency_tnd">Tunisian dinar</string>
|
||||||
|
<string name="currency_top">Tongan Paʻanga</string>
|
||||||
|
<string name="currency_trx">TRON</string>
|
||||||
|
<string name="currency_try">Turkish lira</string>
|
||||||
|
<string name="currency_ttd">Trinidad & Tobago Dollar</string>
|
||||||
|
<string name="currency_twd">New Taiwan dollar</string>
|
||||||
|
<string name="currency_tzs">Tanzanian shilling</string>
|
||||||
|
<string name="currency_uah">Ukrainian hryvnia</string>
|
||||||
|
<string name="currency_ugx">Ugandan shilling</string>
|
||||||
|
<string name="currency_uni">Universe</string>
|
||||||
|
<string name="currency_usd">United States dollar</string>
|
||||||
|
<string name="currency_usdc">USD Coin</string>
|
||||||
|
<string name="currency_usdt">Tether</string>
|
||||||
|
<string name="currency_uyu">Uruguayan peso</string>
|
||||||
|
<string name="currency_uzs">Uzbekistani som</string>
|
||||||
|
<string name="currency_vef">Sovereign Bolivar</string>
|
||||||
|
<string name="currency_vet">Vechain</string>
|
||||||
|
<string name="currency_vnd">Vietnamese dong</string>
|
||||||
|
<string name="currency_vuv">Vanuatu vatu</string>
|
||||||
|
<string name="currency_wbtc">Wrapped Bitcoin</string>
|
||||||
|
<string name="currency_wst">Samoan tala</string>
|
||||||
|
<string name="currency_xaf">Central African CFA franc</string>
|
||||||
|
<string name="currency_xag">Silver Ounce</string>
|
||||||
|
<string name="currency_xau">XauCoin</string>
|
||||||
|
<string name="currency_xcd">East Caribbean dollar</string>
|
||||||
|
<string name="currency_xdr">Special Drawing Rights</string>
|
||||||
|
<string name="currency_xlm">Stellar</string>
|
||||||
|
<string name="currency_xmr">Monero</string>
|
||||||
|
<string name="currency_xof">West African CFA franc</string>
|
||||||
|
<string name="currency_xpf">CFP franc</string>
|
||||||
|
<string name="currency_xrp">XRP</string>
|
||||||
|
<string name="currency_yer">Yemeni rial</string>
|
||||||
|
<string name="currency_zar">South African rand</string>
|
||||||
|
<string name="currency_zmk">Zambian kwacha</string>
|
||||||
|
<string name="currency_zmw">Zambian Kwacha</string>
|
||||||
|
<string name="currency_zwl">Zimbabwean Dollar</string>
|
||||||
|
|
||||||
|
|
||||||
|
<!--Groups-->
|
||||||
|
<string name="length">Length</string>
|
||||||
|
<string name="time">Time</string>
|
||||||
|
<string name="volume">Volume</string>
|
||||||
|
<string name="area">Area</string>
|
||||||
|
<string name="temperature">Temperature</string>
|
||||||
|
<string name="speed">Speed</string>
|
||||||
|
<string name="mass">Mass</string>
|
||||||
|
<string name="data">Data</string>
|
||||||
|
<string name="energy">Energy</string>
|
||||||
|
<string name="power">Power</string>
|
||||||
|
<string name="angle">Angle</string>
|
||||||
|
<string name="data_transfer">Data transfer</string>
|
||||||
|
<string name="currency">Currency</string>
|
||||||
|
|
||||||
|
<!--Screen names-->
|
||||||
|
<string name="units_screen_from">Convert from</string>
|
||||||
|
<string name="units_screen_to">Convert to</string>
|
||||||
|
<string name="settings_screen">Settings</string>
|
||||||
|
|
||||||
|
<!--Settings items-->
|
||||||
|
<string name="theme_setting">Theme</string>
|
||||||
|
<string name="precision_setting">Precision</string>
|
||||||
|
<string name="separator_setting">Separator</string>
|
||||||
|
<string name="output_format_setting">Output format</string>
|
||||||
|
<string name="currency_rates_note_setting">Wrong currency rates?</string>
|
||||||
|
<string name="currency_rates_note_title">Note</string>
|
||||||
|
<string name="currency_rates_note_text">Currency rates are updated daily. There\'s no real-time market monitoring in the app</string>
|
||||||
|
<string name="terms_and_conditions">Terms and Conditions</string>
|
||||||
|
<string name="privacy_policy">Privacy Policy</string>
|
||||||
|
<string name="third_party_licenses">Third party licenses</string>
|
||||||
|
<string name="rate_this_app">Rate this app on Play Store</string>
|
||||||
|
<string name="general_settings_group">General</string>
|
||||||
|
<string name="additional_settings_group">Additional</string>
|
||||||
|
|
||||||
|
<!--Precision-->
|
||||||
|
<string name="precision_setting_support">Number of decimal places</string>
|
||||||
|
<string name="precision_setting_info">Converted values may have a precision higher than the preferred one.</string>
|
||||||
|
<string name="precision_zero">0</string>
|
||||||
|
<string name="precision_one">1</string>
|
||||||
|
<string name="precision_two">2</string>
|
||||||
|
<string name="precision_three">3</string>
|
||||||
|
<string name="precision_four">4</string>
|
||||||
|
<string name="precision_five">5</string>
|
||||||
|
<string name="precision_six">6</string>
|
||||||
|
<string name="precision_seven">7</string>
|
||||||
|
<string name="precision_eight">8</string>
|
||||||
|
<string name="precision_nine">9</string>
|
||||||
|
<string name="precision_ten">10</string>
|
||||||
|
<string name="precision_eleven">11</string>
|
||||||
|
<string name="precision_twelve">12</string>
|
||||||
|
<string name="precision_thirteen">13</string>
|
||||||
|
<string name="precision_fourteen">14</string>
|
||||||
|
<string name="precision_fifteen">15</string>
|
||||||
|
<string name="max_precision">Max precision (1 000)</string>
|
||||||
|
|
||||||
|
<!--Separator-->
|
||||||
|
<string name="separator_setting_support">Group separator symbol</string>
|
||||||
|
<string name="period">Period</string>
|
||||||
|
<string name="comma">Comma</string>
|
||||||
|
<string name="spaces">Spaces</string>
|
||||||
|
|
||||||
|
<!--Output format-->
|
||||||
|
<string name="output_format_setting_support">Result value formatting</string>
|
||||||
|
<string name="output_format_setting_info">Engineering strings look like 1E-21</string>
|
||||||
|
<string name="plain">Default</string>
|
||||||
|
<string name="allow_engineering">Allow engineering</string>
|
||||||
|
<string name="force_engineering">Force engineering</string>
|
||||||
|
|
||||||
|
<!--Theme-->
|
||||||
|
<string name="theme_setting_support">App look and feel</string>
|
||||||
|
<string name="theme_setting_info">Your device supports custom Dynamic Theming implementation. Note that this feature may not work properly for some devices (default theme will be used).</string>
|
||||||
|
<string name="force_auto_mode">Auto</string>
|
||||||
|
<string name="force_light_mode">Light</string>
|
||||||
|
<string name="force_dark_mode">Dark</string>
|
||||||
|
<string name="force_amoled_mode">AMOLED Dark</string>
|
||||||
|
<string name="force_light_dynamic_mode">Dynamic light</string>
|
||||||
|
<string name="force_dark_dynamic_mode">Dynamic dark</string>
|
||||||
|
|
||||||
|
<!--MISC.-->
|
||||||
|
<string name="loading_label">Loading…</string>
|
||||||
|
<string name="error_label">Error</string>
|
||||||
|
<string name="copied">Copied %1$s!</string>
|
||||||
|
<string name="cancel_label">Cancel</string>
|
||||||
|
<string name="search_bar_placeholder">Search units</string>
|
||||||
|
<string name="search_placeholder">No results found</string>
|
||||||
|
<string name="search_placeholder_secondary">Make sure there are no typos or try different filters</string>
|
||||||
|
|
||||||
|
<!--Content descriptions-->
|
||||||
|
<string name="navigate_up_description">Navigate up</string>
|
||||||
|
<string name="checked_filter_description">Checked filter</string>
|
||||||
|
<string name="open_settings_description">Open settings</string>
|
||||||
|
<string name="swap_units_description">Swap units</string>
|
||||||
|
<string name="search_button_description">Search button</string>
|
||||||
|
<string name="clear_input_description">Clear input</string>
|
||||||
|
<string name="favorite_button_description">Add or remove unit from favorites</string>
|
||||||
|
<string name="empty_search_result_description">Empty search result</string>
|
||||||
|
|
||||||
|
</resources>
|
3
app/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<style name="Theme.Unitto" parent="@android:style/Theme.Material.NoActionBar"/>
|
||||||
|
</resources>
|
42
build.gradle
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
buildscript {
|
||||||
|
ext {
|
||||||
|
compose_version = '1.2.0-alpha07'
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:7.1.3'
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
|
||||||
|
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.38.1'
|
||||||
|
|
||||||
|
// Google services
|
||||||
|
classpath 'com.google.gms:google-services:4.3.10'
|
||||||
|
|
||||||
|
// Crashlytics
|
||||||
|
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
|
||||||
|
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This block was added to make opt-in project wide
|
||||||
|
allprojects {
|
||||||
|
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
|
||||||
|
kotlinOptions {
|
||||||
|
freeCompilerArgs += [
|
||||||
|
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
|
||||||
|
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
||||||
|
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
|
||||||
|
"-opt-in=androidx.compose.ui.unit.ExperimentalUnitApi",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
21
gradle.properties
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
|
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||||
|
# Android operating system, and which are packaged with your app"s APK
|
||||||
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
|
android.useAndroidX=true
|
||||||
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
|
android.enableJetifier=true
|
||||||
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
|
kotlin.code.style=official
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
185
gradlew
vendored
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
89
gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
9
settings.gradle
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
dependencyResolutionManagement {
|
||||||
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rootProject.name = "Unitto"
|
||||||
|
include ':app'
|