diff --git a/.gitignore b/.gitignore index 9e28947a..6f45ca55 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,4 @@ lint/tmp/ /app/google-services.json /tools +/dirty diff --git a/README.md b/README.md index f1c69866..4ad6519f 100644 --- a/README.md +++ b/README.md @@ -10,25 +10,44 @@

## đŸ“Č Download -Google Play -F-Droid +Google Play +F-Droid +GitHub ## 😎 Features - **Instant** expression evaluation - Expressions **history** - **Copy**, **paste** and **cut** expression - **Material You** Theme even for **old devices** -- **569** units and currencies +- **583** units and currencies - **Smart** units search - **Adaptive** units sorting algorithm - **Small** app size - **Bulk convert** units - **Favorite** units - Ability to **disable unit groups** +- Built-in **Date calculator** - Customizable number **formatter** - **SI Standard** -**Currency converter** needs Internet connection, stop asking stupid questions. +## ⚠ Security + +### `com.sadellie.unitto.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION` +Read (boring): https://developer.android.com/about/versions/14/behavior-changes-14#runtime-receivers-exported + +### `android.permission.INTERNET` +Used in **Unit Converter** to update currency rates. Requests are made *only* when you select a currency unit. See: [CurrencyApiService.kt](https://github.com/sadellie/unitto/blob/2405a2656ac8de3877a647f19813b4498f24c7a8/data/units/src/main/java/com/sadellie/unitto/data/units/remote/CurrencyApiService.kt) and [UnitsRepository.kt](https://github.com/sadellie/unitto/blob/49f1520d88843ed3cc7ebc02307e877950c9899b/data/units/src/main/java/com/sadellie/unitto/data/units/UnitsRepository.kt) + +### `android.permission.ACCESS_NETWORK_STATE` +Used in **Unit Converter** as a callback. Retries to update currency rates if there was an error (no network, for example) and the Internet connection is back. See: [NetworkUtils.kt](https://github.com/sadellie/unitto/blob/d7db2780c83cdda33335c5278cafe4148c5e7778/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/NetworkUtils.kt) and [ConverterScreen.kt](https://github.com/sadellie/unitto/blob/6fcf340abac7d34d2de9b142bf8208b55a09079f/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt) + +### Non-free network service +*Non-free* doesn't mean that you need to pay, put your credit card away. In this context it means that you can't host it on your machine (why the fuck would anyone want to host a currency API service?). + +The app uses [Free Currency Rates API](https://github.com/fawazahmed0/currency-api) by [fawazahmed0](https://github.com/fawazahmed0). +Requests are send to `cdn.jsdelivr.net`. + +TL;DR: the app is legit ## 👅 [Translate](https://poeditor.com/join/project/T4zjmoq8dx) Join on **POEditor** to help. @@ -40,9 +59,8 @@ Report bugs or request improvements. I may close your issue as not planned and r If you think that your question will not fit in "Issues", start a discussion. ## đŸ‘©â€đŸ’» ~~Contribute code~~ -Code contributions are **not** welcomed. If you really want to, **ask me** first. - -Hard forks and alterations of Unitto are **not** welcomed. Use a "Fork" button so that commits' author is not lost. +1. At the moment I do not need any help in code. +2. Hard forks and alterations of Unitto are **not** welcomed. Use a "Fork" button so that commits' author is not lost. ## 🔎 Additional Terms and Conditions: https://sadellie.github.io/unitto/terms diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9d3bcad9..8e01d9c6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,8 +16,6 @@ * along with this program. If not, see . */ -@file:Suppress("UnstableApiUsage") - plugins { // Basic stuff id("com.android.application") @@ -34,8 +32,21 @@ android { applicationId = "com.sadellie.unitto" minSdk = 21 targetSdk = 34 - versionCode = 24 - versionName = "Mikado Yellow" + versionCode = 26 + versionName = "Nadeshiko Pink" + resourceConfigurations += setOf( + "en", + "en-rGB", + "de", + "es", + "fr", + "hu", + "in", + "it", + "nl", + "ru", + "tr", + ) } buildTypes { @@ -91,9 +102,6 @@ android { kotlinOptions { jvmTarget = JavaVersion.VERSION_11.toString() - freeCompilerArgs = freeCompilerArgs + listOf( - "-opt-in=androidx.lifecycle.compose.ExperimentalLifecycleComposeApi" - ) } composeOptions { @@ -108,24 +116,24 @@ tasks.withType().configureEach } dependencies { - implementation(libs.androidx.core) - coreLibraryDesugaring(libs.android.desugarJdkLibs) + implementation(libs.androidx.core.core.ktx) + coreLibraryDesugaring(libs.com.android.tools.desugar.jdk.libs) implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.material.icons.extended) - implementation(libs.androidx.lifecycle.runtime.compose) + implementation(libs.androidx.lifecycle.lifecycle.runtime.compose) implementation(libs.com.github.sadellie.themmo) - implementation(libs.com.google.accompanist.systemuicontroller) - implementation(libs.androidx.datastore) + implementation(libs.com.google.accompanist.accompanist.systemuicontroller) + implementation(libs.androidx.datastore.datastore.preferences) + implementation(libs.androidx.appcompat.appcompat) - implementation(project(mapOf("path" to ":feature:converter"))) - implementation(project(mapOf("path" to ":feature:calculator"))) - implementation(project(mapOf("path" to ":feature:settings"))) - implementation(project(mapOf("path" to ":feature:unitslist"))) - implementation(project(mapOf("path" to ":feature:datecalculator"))) - implementation(project(mapOf("path" to ":feature:timezone"))) - implementation(project(mapOf("path" to ":data:model"))) - implementation(project(mapOf("path" to ":data:userprefs"))) - implementation(project(mapOf("path" to ":core:ui"))) - implementation(project(mapOf("path" to ":core:base"))) + implementation(project(":feature:converter")) + implementation(project(":feature:calculator")) + implementation(project(":feature:settings")) + implementation(project(":feature:datecalculator")) + implementation(project(":feature:timezone")) + implementation(project(":data:model")) + implementation(project(":data:userprefs")) + implementation(project(":core:ui")) + implementation(project(":core:base")) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4d47d3f2..ba0a4a1f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,8 +7,8 @@ android:name=".UnittoApplication" android:icon="@mipmap/ic_launcher_icon" android:label="@string/calculator" - android:supportsRtl="false" + android:localeConfig="@xml/locales_config" android:theme="@style/Theme.Unitto"> + + + + \ No newline at end of file diff --git a/app/src/main/java/com/sadellie/unitto/MainActivity.kt b/app/src/main/java/com/sadellie/unitto/MainActivity.kt index b68879ed..f7935a0d 100644 --- a/app/src/main/java/com/sadellie/unitto/MainActivity.kt +++ b/app/src/main/java/com/sadellie/unitto/MainActivity.kt @@ -23,8 +23,8 @@ import android.content.Context import android.os.Bundle import android.util.AttributeSet import android.view.View -import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.sadellie.unitto.data.userprefs.UserPreferencesRepository @@ -32,7 +32,7 @@ import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @AndroidEntryPoint -internal class MainActivity : ComponentActivity() { +internal class MainActivity : AppCompatActivity() { @Inject lateinit var userPrefsRepository: UserPreferencesRepository @@ -41,10 +41,10 @@ internal class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContent { - val uiPrefs = userPrefsRepository.uiPreferencesFlow + val prefs = userPrefsRepository.appPrefs .collectAsStateWithLifecycle(null).value - if (uiPrefs != null) UnittoApp(uiPrefs) + if (prefs != null) UnittoApp(prefs) } } diff --git a/app/src/main/java/com/sadellie/unitto/UnittoApp.kt b/app/src/main/java/com/sadellie/unitto/UnittoApp.kt index 48797e95..93d72b12 100644 --- a/app/src/main/java/com/sadellie/unitto/UnittoApp.kt +++ b/app/src/main/java/com/sadellie/unitto/UnittoApp.kt @@ -46,27 +46,29 @@ import com.sadellie.unitto.core.ui.common.isOpen import com.sadellie.unitto.core.ui.common.open import com.sadellie.unitto.core.ui.common.rememberUnittoDrawerState import com.sadellie.unitto.core.ui.model.DrawerItems -import com.sadellie.unitto.core.ui.theme.AppTypography +import com.sadellie.unitto.core.ui.pushDynamicShortcut +import com.sadellie.unitto.core.ui.theme.TypographySystem +import com.sadellie.unitto.core.ui.theme.TypographyUnitto import com.sadellie.unitto.core.ui.theme.DarkThemeColors import com.sadellie.unitto.core.ui.theme.LightThemeColors -import com.sadellie.unitto.data.userprefs.UIPreferences +import com.sadellie.unitto.data.userprefs.AppPreferences import io.github.sadellie.themmo.Themmo import io.github.sadellie.themmo.rememberThemmoController import kotlinx.coroutines.launch @OptIn(ExperimentalFoundationApi::class) @Composable -internal fun UnittoApp(uiPrefs: UIPreferences) { +internal fun UnittoApp(prefs: AppPreferences) { val mContext = LocalContext.current val themmoController = rememberThemmoController( lightColorScheme = LightThemeColors, darkColorScheme = DarkThemeColors, - themingMode = uiPrefs.themingMode, - dynamicThemeEnabled = uiPrefs.enableDynamicTheme, - amoledThemeEnabled = uiPrefs.enableAmoledTheme, - customColor = uiPrefs.customColor, - monetMode = uiPrefs.monetMode + themingMode = prefs.themingMode, + dynamicThemeEnabled = prefs.enableDynamicTheme, + amoledThemeEnabled = prefs.enableAmoledTheme, + customColor = prefs.customColor, + monetMode = prefs.monetMode ) val navController = rememberNavController() val sysUiController = rememberSystemUiController() @@ -77,9 +79,9 @@ internal fun UnittoApp(uiPrefs: UIPreferences) { val shortcutsScope = rememberCoroutineScope() - val tabs by remember(uiPrefs.enableToolsExperiment) { + val tabs by remember(prefs.enableToolsExperiment) { derivedStateOf { - if (uiPrefs.enableToolsExperiment) { + if (prefs.enableToolsExperiment) { listOf( DrawerItems.Calculator, DrawerItems.Converter, @@ -105,7 +107,7 @@ internal fun UnittoApp(uiPrefs: UIPreferences) { Themmo( themmoController = themmoController, - typography = AppTypography, + typography = if (prefs.systemFont) TypographySystem else TypographyUnitto, animationSpec = tween(250) ) { val backgroundColor = MaterialTheme.colorScheme.background @@ -150,7 +152,7 @@ internal fun UnittoApp(uiPrefs: UIPreferences) { UnittoNavigation( navController = navController, themmoController = it, - startDestination = uiPrefs.startingScreen, + startDestination = prefs.startingScreen, openDrawer = { drawerScope.launch { drawerState.open() } } ) } diff --git a/app/src/main/java/com/sadellie/unitto/UnittoNavigation.kt b/app/src/main/java/com/sadellie/unitto/UnittoNavigation.kt index 69ab8f48..1b205fa3 100644 --- a/app/src/main/java/com/sadellie/unitto/UnittoNavigation.kt +++ b/app/src/main/java/com/sadellie/unitto/UnittoNavigation.kt @@ -18,25 +18,20 @@ package com.sadellie.unitto +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut import androidx.compose.foundation.background import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import com.sadellie.unitto.feature.calculator.navigation.calculatorGraph -import com.sadellie.unitto.feature.converter.ConverterViewModel import com.sadellie.unitto.feature.converter.navigation.converterGraph import com.sadellie.unitto.feature.datecalculator.navigation.dateCalculatorGraph import com.sadellie.unitto.feature.settings.navigation.navigateToSettings import com.sadellie.unitto.feature.settings.navigation.navigateToUnitGroups import com.sadellie.unitto.feature.settings.navigation.settingGraph -import com.sadellie.unitto.feature.unitslist.UnitsListViewModel -import com.sadellie.unitto.feature.unitslist.navigation.leftScreen -import com.sadellie.unitto.feature.unitslist.navigation.navigateToLeftSide -import com.sadellie.unitto.feature.unitslist.navigation.navigateToRightSide -import com.sadellie.unitto.feature.unitslist.navigation.rightScreen import com.sadellie.unitto.timezone.navigation.timeZoneGraph import io.github.sadellie.themmo.ThemmoController @@ -47,34 +42,18 @@ internal fun UnittoNavigation( startDestination: String, openDrawer: () -> Unit ) { - val converterViewModel: ConverterViewModel = hiltViewModel() - val unitsListViewModel: UnitsListViewModel = hiltViewModel() - NavHost( navController = navController, startDestination = startDestination, - modifier = Modifier.background(MaterialTheme.colorScheme.background) + modifier = Modifier.background(MaterialTheme.colorScheme.background), + enterTransition = { fadeIn() }, + exitTransition = { fadeOut() } ) { converterGraph( - navigateToLeftScreen = navController::navigateToLeftSide, - navigateToRightScreen = navController::navigateToRightSide, + openDrawer = openDrawer, + navController = navController, navigateToSettings = navController::navigateToSettings, - navigateToMenu = openDrawer, - viewModel = converterViewModel - ) - - leftScreen( - viewModel = unitsListViewModel, - navigateUp = navController::navigateUp, - navigateToUnitGroups = navController::navigateToUnitGroups, - onSelect = converterViewModel::updateUnitFrom - ) - - rightScreen( - viewModel = unitsListViewModel, - navigateUp = navController::navigateUp, - navigateToUnitGroups = navController::navigateToUnitGroups, - onSelect = converterViewModel::updateUnitTo + navigateToUnitGroups = navController::navigateToUnitGroups ) settingGraph( diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 4f6a9a46..ecaeb63c 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,5 +1,5 @@ -