diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 2a70628d..00000000 --- a/app/build.gradle +++ /dev/null @@ -1,73 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' -apply plugin: 'kotlin-kapt' - -def keystorePropertiesFile = rootProject.file("keystore.properties") -def keystoreProperties = new Properties() -if (keystorePropertiesFile.exists()) { - keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) -} - -android { - compileSdkVersion 33 - - defaultConfig { - applicationId "com.simplemobiletools.calculator" - minSdkVersion 23 - targetSdkVersion 33 - versionCode 60 - versionName "5.11.3" - setProperty("archivesBaseName", "calculator") - } - - signingConfigs { - if (keystorePropertiesFile.exists()) { - release { - keyAlias keystoreProperties['keyAlias'] - keyPassword keystoreProperties['keyPassword'] - storeFile file(keystoreProperties['storeFile']) - storePassword keystoreProperties['storePassword'] - } - } - } - - buildTypes { - debug { - applicationIdSuffix ".debug" - } - release { - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - if (keystorePropertiesFile.exists()) { - signingConfig signingConfigs.release - } - } - } - - flavorDimensions "variants" - productFlavors { - core {} - fdroid {} - prepaid {} - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - lintOptions { - checkReleaseBuilds false - abortOnError false - } -} - -dependencies { - implementation 'com.github.SimpleMobileTools:Simple-Commons:4c83ec8740' - implementation 'me.grantland:autofittextview:0.2.1' - implementation 'net.objecthunter:exp4j:0.4.8' - - kapt 'androidx.room:room-compiler:2.5.1' - implementation 'androidx.room:room-runtime:2.5.1' - annotationProcessor 'androidx.room:room-compiler:2.5.1' -} diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 00000000..64a912d5 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,113 @@ +import java.io.FileInputStream +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.konan.properties.Properties + +plugins { + alias(libs.plugins.android) + alias(libs.plugins.kotlinAndroid) + alias(libs.plugins.ksp) +} + +val keystorePropertiesFile: File = rootProject.file("keystore.properties") +val keystoreProperties = Properties() +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(FileInputStream(keystorePropertiesFile)) +} + +android { + compileSdk = project.libs.versions.app.build.compileSDKVersion.get().toInt() + + defaultConfig { + applicationId = libs.versions.app.version.appId.get() + minSdk = project.libs.versions.app.build.minimumSDK.get().toInt() + targetSdk = project.libs.versions.app.build.targetSDK.get().toInt() + versionName = project.libs.versions.app.version.versionName.get() + versionCode = project.libs.versions.app.version.versionCode.get().toInt() + setProperty("archivesBaseName", "calculator") + } + + signingConfigs { + if (keystorePropertiesFile.exists()) { + register("release") { + keyAlias = keystoreProperties.getProperty("keyAlias") + keyPassword = keystoreProperties.getProperty("keyPassword") + storeFile = file(keystoreProperties.getProperty("storeFile")) + storePassword = keystoreProperties.getProperty("storePassword") + } + } + } + + buildTypes { + debug { + applicationIdSuffix = ".debug" + } + release { + isMinifyEnabled = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + if (keystorePropertiesFile.exists()) { + signingConfig = signingConfigs.getByName("release") + } + } + } + + buildFeatures { + viewBinding = true + compose = true + buildConfig = true + } + + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() + } + + flavorDimensions.add("variants") + productFlavors { + register("core") + register("fdroid") + register("prepaid") + } + + compileOptions { + val currentJavaVersionFromLibs = JavaVersion.valueOf(libs.versions.app.build.javaVersion.get().toString()) + sourceCompatibility = currentJavaVersionFromLibs + targetCompatibility = currentJavaVersionFromLibs + } + + tasks.withType { + kotlinOptions.jvmTarget = project.libs.versions.app.build.kotlinJVMTarget.get() + kotlinOptions.freeCompilerArgs = listOf( + "-opt-in=kotlin.RequiresOptIn", + "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api", + "-opt-in=androidx.compose.material.ExperimentalMaterialApi", + "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", + "-Xcontext-receivers" + ) + } + + sourceSets { + getByName("main").java.srcDirs("src/main/kotlin") + } + + lint { + checkReleaseBuilds = false + abortOnError = false + } + namespace = "com.simplemobiletools.calculator" +} + +dependencies { + implementation(libs.simple.tools.commons) + implementation(libs.auto.fit.text.view) + implementation(libs.exp4j) + implementation(libs.bundles.lifecycle) + implementation(libs.bundles.compose) + implementation(libs.bundles.accompanist) + debugImplementation(libs.bundles.compose.preview) + + implementation(libs.bundles.room) + ksp(libs.androidx.room.compiler) + +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ee0ed452..319e89c1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + it?.setVibratingOnClickListener { view -> calc.numpadClicked(view.id) } } - btn_equals.setVibratingOnClickListener { calc.handleEquals() } - formula.setOnLongClickListener { copyToClipboard(false) } - result.setOnLongClickListener { copyToClipboard(true) } - AutofitHelper.create(result) - AutofitHelper.create(formula) + binding.btnEquals?.setVibratingOnClickListener { calc.handleEquals() } + binding.formula?.setOnLongClickListener { copyToClipboard(false) } + binding.result?.setOnLongClickListener { copyToClipboard(true) } + AutofitHelper.create(binding.result) + AutofitHelper.create(binding.formula) storeStateVariables() - updateViewColors(calculator_holder, getProperTextColor()) + binding.calculatorHolder?.let { updateViewColors(it, getProperTextColor()) } setupDecimalSeparator() checkWhatsNewDialog() checkAppOnSDCard() @@ -81,9 +83,9 @@ class MainActivity : SimpleActivity(), Calculator { override fun onResume() { super.onResume() - setupToolbar(main_toolbar) + setupToolbar(binding.mainToolbar) if (storedTextColor != config.textColor) { - updateViewColors(calculator_holder, getProperTextColor()) + binding.calculatorHolder?.let { updateViewColors(it, getProperTextColor()) } } if (config.preventPhoneFromSleeping) { @@ -97,14 +99,16 @@ class MainActivity : SimpleActivity(), Calculator { vibrateOnButtonPress = config.vibrateOnButtonPress - arrayOf(btn_percent, btn_power, btn_root, btn_clear, btn_reset, btn_divide, btn_multiply, btn_plus, btn_minus, btn_equals, btn_decimal).forEach { - it.background = ResourcesCompat.getDrawable(resources, R.drawable.pill_background, theme) - it.background?.alpha = MEDIUM_ALPHA_INT - } + binding.apply { + arrayOf(btnPercent, btnPower, btnRoot, btnClear, btnReset, btnDivide, btnMultiply, btnPlus, btnMinus, btnEquals, btnDecimal).forEach { + it?.background = ResourcesCompat.getDrawable(resources, com.simplemobiletools.commons.R.drawable.pill_background, theme) + it?.background?.alpha = MEDIUM_ALPHA_INT + } - arrayOf(btn_0, btn_1, btn_2, btn_3, btn_4, btn_5, btn_6, btn_7, btn_8, btn_9).forEach { - it.background = ResourcesCompat.getDrawable(resources, R.drawable.pill_background, theme) - it.background?.alpha = LOWER_ALPHA_INT + arrayOf(btn0, btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9).forEach { + it?.background = ResourcesCompat.getDrawable(resources, com.simplemobiletools.commons.R.drawable.pill_background, theme) + it?.background?.alpha = LOWER_ALPHA_INT + } } } @@ -129,7 +133,7 @@ class MainActivity : SimpleActivity(), Calculator { } private fun setupOptionsMenu() { - main_toolbar.setOnMenuItemClickListener { menuItem -> + binding.mainToolbar.setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { R.id.history -> showHistory() R.id.more_apps_from_us -> launchMoreAppsFromUsIntent() @@ -142,8 +146,8 @@ class MainActivity : SimpleActivity(), Calculator { } private fun refreshMenuItems() { - main_toolbar.menu.apply { - findItem(R.id.more_apps_from_us).isVisible = !resources.getBoolean(R.bool.hide_google_relations) + binding.mainToolbar.menu.apply { + findItem(R.id.more_apps_from_us).isVisible = !resources.getBoolean(com.simplemobiletools.commons.R.bool.hide_google_relations) } } @@ -163,7 +167,7 @@ class MainActivity : SimpleActivity(), Calculator { private fun showHistory() { HistoryHelper(this).getHistory { if (it.isEmpty()) { - toast(R.string.history_empty) + toast(com.simplemobiletools.calculator.R.string.history_empty) } else { HistoryDialog(this, it, calc) } @@ -180,27 +184,29 @@ class MainActivity : SimpleActivity(), Calculator { val faqItems = arrayListOf( FAQItem(R.string.faq_1_title, R.string.faq_1_text), - FAQItem(R.string.faq_1_title_commons, R.string.faq_1_text_commons), - FAQItem(R.string.faq_4_title_commons, R.string.faq_4_text_commons) + FAQItem(com.simplemobiletools.commons.R.string.faq_1_title_commons, com.simplemobiletools.commons.R.string.faq_1_text_commons), + FAQItem(com.simplemobiletools.commons.R.string.faq_4_title_commons, com.simplemobiletools.commons.R.string.faq_4_text_commons) ) - if (!resources.getBoolean(R.bool.hide_google_relations)) { - faqItems.add(FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons)) - faqItems.add(FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons)) + if (!resources.getBoolean(com.simplemobiletools.commons.R.bool.hide_google_relations)) { + faqItems.add(FAQItem(com.simplemobiletools.commons.R.string.faq_2_title_commons, com.simplemobiletools.commons.R.string.faq_2_text_commons)) + faqItems.add(FAQItem(com.simplemobiletools.commons.R.string.faq_6_title_commons, com.simplemobiletools.commons.R.string.faq_6_text_commons)) } startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true) } - private fun getButtonIds() = arrayOf(btn_decimal, btn_0, btn_1, btn_2, btn_3, btn_4, btn_5, btn_6, btn_7, btn_8, btn_9) + private fun getButtonIds() = binding.run { + arrayOf(btnDecimal, btn0, btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9) + } private fun copyToClipboard(copyResult: Boolean): Boolean { - var value = formula.value + var value = binding.formula?.value if (copyResult) { - value = result.value + value = binding.result?.value } - return if (value.isEmpty()) { + return if (value.isNullOrEmpty()) { false } else { copyToClipboard(value) @@ -209,19 +215,19 @@ class MainActivity : SimpleActivity(), Calculator { } override fun showNewResult(value: String, context: Context) { - result.text = value + binding.result?.text = value } private fun checkWhatsNewDialog() { arrayListOf().apply { - add(Release(18, R.string.release_18)) - add(Release(28, R.string.release_28)) + add(Release(18, com.simplemobiletools.calculator.R.string.release_18)) + add(Release(28, com.simplemobiletools.calculator.R.string.release_28)) checkWhatsNew(this, BuildConfig.VERSION_CODE) } } override fun showNewFormula(value: String, context: Context) { - formula.text = value + binding.formula?.text = value } private fun setupDecimalSeparator() { @@ -234,7 +240,7 @@ class MainActivity : SimpleActivity(), Calculator { groupingSeparator = COMMA } calc.updateSeparators(decimalSeparator, groupingSeparator) - btn_decimal.text = decimalSeparator + binding.btnDecimal?.text = decimalSeparator } private fun View.setVibratingOnClickListener(callback: (view: View) -> Unit) { diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/activities/SettingsActivity.kt index 201745ed..2d9f8450 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calculator/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/activities/SettingsActivity.kt @@ -1,115 +1,104 @@ package com.simplemobiletools.calculator.activities +import android.annotation.SuppressLint import android.content.Intent import android.os.Bundle -import com.simplemobiletools.calculator.R -import com.simplemobiletools.calculator.extensions.calculatorDB -import com.simplemobiletools.calculator.extensions.config -import com.simplemobiletools.calculator.extensions.updateWidgets +import androidx.activity.compose.setContent +import androidx.appcompat.app.AppCompatActivity +import androidx.compose.runtime.* +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.core.view.WindowCompat +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.simplemobiletools.calculator.compose.extensions.TransparentSystemBars +import com.simplemobiletools.calculator.compose.extensions.onEventValue +import com.simplemobiletools.calculator.compose.screens.SettingsScreen +import com.simplemobiletools.calculator.compose.theme.AppThemeSurface +import com.simplemobiletools.calculator.compose.theme.getAppIconIds +import com.simplemobiletools.calculator.compose.theme.getAppLauncherName +import com.simplemobiletools.calculator.extensions.* +import com.simplemobiletools.commons.activities.CustomizationActivity import com.simplemobiletools.commons.extensions.* -import com.simplemobiletools.commons.helpers.IS_CUSTOMIZING_COLORS -import com.simplemobiletools.commons.helpers.NavigationIcon -import com.simplemobiletools.commons.helpers.ensureBackgroundThread -import com.simplemobiletools.commons.helpers.isTiramisuPlus -import kotlinx.android.synthetic.main.activity_settings.* -import java.util.* +import com.simplemobiletools.commons.helpers.* +import java.util.Locale import kotlin.system.exitProcess -class SettingsActivity : SimpleActivity() { +class SettingsActivity : AppCompatActivity() { + + private val preferences by lazy { config } + + @SuppressLint("NewApi") override fun onCreate(savedInstanceState: Bundle?) { - isMaterialActivity = true super.onCreate(savedInstanceState) - setContentView(R.layout.activity_settings) + WindowCompat.setDecorFitsSystemWindows(window, false) + setContent { + TransparentSystemBars() + AppThemeSurface { + val context = LocalContext.current + val preventPhoneFromSleeping by preferences.preventPhoneFromSleepingFlow.collectAsStateWithLifecycle(preferences.preventPhoneFromSleeping) + val vibrateOnButtonPressFlow by preferences.vibrateOnButtonPressFlow.collectAsStateWithLifecycle(preferences.vibrateOnButtonPress) + val wasUseEnglishToggledFlow by preferences.wasUseEnglishToggledFlow.collectAsStateWithLifecycle(preferences.wasUseEnglishToggled) + val useEnglishFlow by preferences.useEnglishFlow.collectAsStateWithLifecycle(preferences.useEnglish) + val useCommaAsDecimalMarkFlow by preferences.useCommaAsDecimalMarkFlow.collectAsStateWithLifecycle(preferences.useCommaAsDecimalMark) + val isUseEnglishEnabled by remember(wasUseEnglishToggledFlow) { + derivedStateOf { + (wasUseEnglishToggledFlow || Locale.getDefault().language != "en") && !isTiramisuPlus() + } + } + val isOrWasThankYouInstalled = onEventValue { context.isOrWasThankYouInstalled() } + val lockedCustomizeColorText by remember(isOrWasThankYouInstalled) { + derivedStateOf { if (isOrWasThankYouInstalled) null else getCustomizeColorsString() } + } + val statusBarColor = onEventValue { context.getColoredMaterialStatusBarColor() } + val contrastColor by remember(statusBarColor) { + derivedStateOf { statusBarColor.getContrastColor() } + } - updateMaterialActivityViews(settings_coordinator, settings_holder, useTransparentNavigation = true, useTopSearchMenu = false) - setupMaterialScrollListener(settings_nested_scrollview, settings_toolbar) - } - - override fun onResume() { - super.onResume() - setupToolbar(settings_toolbar, NavigationIcon.Arrow) - - setupPurchaseThankYou() - setupCustomizeColors() - setupUseEnglish() - setupLanguage() - setupVibrate() - setupPreventPhoneFromSleeping() - setupUseCommaAsDecimalMark() - setupCustomizeWidgetColors() - updateTextColors(settings_nested_scrollview) - - arrayOf(settings_color_customization_section_label, settings_general_settings_label).forEach { - it.setTextColor(getProperPrimaryColor()) - } - } - - private fun setupPurchaseThankYou() { - settings_purchase_thank_you_holder.beGoneIf(isOrWasThankYouInstalled()) - settings_purchase_thank_you_holder.setOnClickListener { - launchPurchaseThankYouIntent() - } - } - - private fun setupCustomizeColors() { - settings_color_customization_label.text = getCustomizeColorsString() - settings_color_customization_holder.setOnClickListener { - handleCustomizeColorsClick() - } - } - - private fun setupUseEnglish() { - settings_use_english_holder.beVisibleIf((config.wasUseEnglishToggled || Locale.getDefault().language != "en") && !isTiramisuPlus()) - settings_use_english.isChecked = config.useEnglish - settings_use_english_holder.setOnClickListener { - settings_use_english.toggle() - config.useEnglish = settings_use_english.isChecked - exitProcess(0) - } - } - - private fun setupLanguage() { - settings_language.text = Locale.getDefault().displayLanguage - settings_language_holder.beVisibleIf(isTiramisuPlus()) - settings_language_holder.setOnClickListener { - launchChangeAppLanguageIntent() - } - } - - private fun setupVibrate() { - settings_vibrate.isChecked = config.vibrateOnButtonPress - settings_vibrate_holder.setOnClickListener { - settings_vibrate.toggle() - config.vibrateOnButtonPress = settings_vibrate.isChecked - } - } - - private fun setupPreventPhoneFromSleeping() { - settings_prevent_phone_from_sleeping.isChecked = config.preventPhoneFromSleeping - settings_prevent_phone_from_sleeping_holder.setOnClickListener { - settings_prevent_phone_from_sleeping.toggle() - config.preventPhoneFromSleeping = settings_prevent_phone_from_sleeping.isChecked - } - } - - private fun setupUseCommaAsDecimalMark() { - settings_use_comma_as_decimal_mark.isChecked = config.useCommaAsDecimalMark - settings_use_comma_as_decimal_mark_holder.setOnClickListener { - settings_use_comma_as_decimal_mark.toggle() - config.useCommaAsDecimalMark = settings_use_comma_as_decimal_mark.isChecked - updateWidgets() - ensureBackgroundThread { - applicationContext.calculatorDB.deleteHistory() + SettingsScreen( + goBack = ::finish, + customizeColors = ::handleCustomizeColorsClick, + customizeWidgetColors = ::setupCustomizeWidgetColors, + topBarsScrolledContainerColor = Color(statusBarColor), + preventPhoneFromSleeping = preventPhoneFromSleeping, + onPreventPhoneFromSleeping = preferences::preventPhoneFromSleeping::set, + vibrateOnButtonPressFlow = vibrateOnButtonPressFlow, + onVibrateOnButtonPressFlow = preferences::vibrateOnButtonPress::set, + isOrWasThankYouInstalled = isOrWasThankYouInstalled, + onThankYou = ::launchPurchaseThankYouIntent, + isUseEnglishEnabled = isUseEnglishEnabled, + isUseEnglishChecked = useEnglishFlow, + onUseEnglishPress = { isChecked -> + preferences.useEnglish = isChecked + exitProcess(0) + }, + onSetupLanguagePress = ::launchChangeAppLanguageIntent, + useCommaAsDecimalMarkFlow = useCommaAsDecimalMarkFlow, + onUseCommaAsDecimalMarkFlow = { isChecked -> + preferences.useCommaAsDecimalMark = isChecked + updateWidgets() + ensureBackgroundThread { + applicationContext.calculatorDB.deleteHistory() + } + }, + lockedCustomizeColorText = lockedCustomizeColorText, + topBarsContentColor = Color(contrastColor) + ) } } } + private fun handleCustomizeColorsClick() { + Intent(applicationContext, CustomizationActivity::class.java).apply { + putExtra(APP_ICON_IDS, getAppIconIds()) + putExtra(APP_LAUNCHER_NAME, getAppLauncherName()) + startActivity(this) + } + } + private fun setupCustomizeWidgetColors() { - settings_widget_color_customization_holder.setOnClickListener { - Intent(this, WidgetConfigureActivity::class.java).apply { - putExtra(IS_CUSTOMIZING_COLORS, true) - startActivity(this) - } + Intent(this, WidgetConfigureActivity::class.java).apply { + putExtra(IS_CUSTOMIZING_COLORS, true) + startActivity(this) } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/activities/WidgetConfigureActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/activities/WidgetConfigureActivity.kt index 724f0a31..0fca9887 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calculator/activities/WidgetConfigureActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/activities/WidgetConfigureActivity.kt @@ -10,14 +10,14 @@ import android.widget.RemoteViews import android.widget.SeekBar import android.widget.TextView import com.simplemobiletools.calculator.R +import com.simplemobiletools.calculator.databinding.WidgetConfigBinding import com.simplemobiletools.calculator.extensions.config +import com.simplemobiletools.calculator.extensions.viewBinding import com.simplemobiletools.calculator.helpers.MyWidgetProvider import com.simplemobiletools.commons.dialogs.ColorPickerDialog import com.simplemobiletools.commons.dialogs.FeatureLockedDialog import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.IS_CUSTOMIZING_COLORS -import kotlinx.android.synthetic.main.activity_main.* -import kotlinx.android.synthetic.main.widget_config.* class WidgetConfigureActivity : SimpleActivity() { private var mBgAlpha = 0f @@ -27,11 +27,13 @@ class WidgetConfigureActivity : SimpleActivity() { private var mBgColorWithoutTransparency = 0 private var mFeatureLockedDialog: FeatureLockedDialog? = null + private val binding by viewBinding(WidgetConfigBinding::inflate) + public override fun onCreate(savedInstanceState: Bundle?) { useDynamicTheme = false super.onCreate(savedInstanceState) setResult(Activity.RESULT_CANCELED) - setContentView(R.layout.widget_config) + setContentView(binding.root) initVariables() val isCustomizingColors = intent.extras?.getBoolean(IS_CUSTOMIZING_COLORS) ?: false @@ -41,12 +43,12 @@ class WidgetConfigureActivity : SimpleActivity() { finish() } - config_save.setOnClickListener { saveConfig() } - config_bg_color.setOnClickListener { pickBackgroundColor() } - config_text_color.setOnClickListener { pickTextColor() } + binding.configSave.setOnClickListener { saveConfig() } + binding.configBgColor.setOnClickListener { pickBackgroundColor() } + binding.configTextColor.setOnClickListener { pickTextColor() } val primaryColor = getProperPrimaryColor() - config_bg_seekbar.setColors(mTextColor, primaryColor, primaryColor) + binding.configBgSeekbar.setColors(mTextColor, primaryColor, primaryColor) if (!isCustomizingColors && !isOrWasThankYouInstalled()) { mFeatureLockedDialog = FeatureLockedDialog(this) { @@ -70,27 +72,27 @@ class WidgetConfigureActivity : SimpleActivity() { mBgColor = config.widgetBgColor mBgAlpha = Color.alpha(mBgColor) / 255.toFloat() - btn_reset.beVisible() + binding.configCalc.btnReset.beVisible() mBgColorWithoutTransparency = Color.rgb(Color.red(mBgColor), Color.green(mBgColor), Color.blue(mBgColor)) - config_bg_seekbar.setOnSeekBarChangeListener(seekbarChangeListener) - config_bg_seekbar.progress = (mBgAlpha * 100).toInt() + binding.configBgSeekbar.setOnSeekBarChangeListener(seekbarChangeListener) + binding.configBgSeekbar.progress = (mBgAlpha * 100).toInt() updateBackgroundColor() mTextColor = config.widgetTextColor - if (mTextColor == resources.getColor(R.color.default_widget_text_color) && config.isUsingSystemTheme) { - mTextColor = resources.getColor(R.color.you_primary_color, theme) + if (mTextColor == resources.getColor(com.simplemobiletools.commons.R.color.default_widget_text_color, theme) && config.isUsingSystemTheme) { + mTextColor = resources.getColor(com.simplemobiletools.commons.R.color.you_primary_color, theme) } updateTextColor() - formula.text = "15,937*5" - result.text = "79,685" + binding.configCalc.formula.text = "15,937*5" + binding.configCalc.result.text = "79,685" } private fun saveConfig() { val appWidgetManager = AppWidgetManager.getInstance(this) ?: return val views = RemoteViews(packageName, R.layout.widget).apply { - applyColorFilter(R.id.widget_background, mBgColor) + applyColorFilter(binding.widgetBackground.id, mBgColor) } appWidgetManager.updateAppWidget(mWidgetId, views) @@ -121,13 +123,13 @@ class WidgetConfigureActivity : SimpleActivity() { private fun updateBackgroundColor() { mBgColor = mBgColorWithoutTransparency.adjustAlpha(mBgAlpha) - widget_background.applyColorFilter(mBgColor) - config_bg_color.setFillWithStroke(mBgColor, mBgColor) - config_save.backgroundTintList = ColorStateList.valueOf(getProperPrimaryColor()) + binding.widgetBackground.applyColorFilter(mBgColor) + binding.configBgColor.setFillWithStroke(mBgColor, mBgColor) + binding.configSave.backgroundTintList = ColorStateList.valueOf(getProperPrimaryColor()) } private fun updateTextColor() { - config_text_color.setFillWithStroke(mTextColor, mTextColor) + binding.configTextColor.setFillWithStroke(mTextColor, mTextColor) val viewIds = intArrayOf( R.id.btn_0, R.id.btn_1, R.id.btn_2, R.id.btn_3, R.id.btn_4, R.id.btn_5, R.id.btn_6, R.id.btn_7, R.id.btn_8, @@ -135,9 +137,10 @@ class WidgetConfigureActivity : SimpleActivity() { R.id.btn_minus, R.id.btn_plus, R.id.btn_decimal, R.id.btn_equals ) - result.setTextColor(mTextColor) - formula.setTextColor(mTextColor) - config_save.setTextColor(getProperPrimaryColor().getContrastColor()) + + binding.configCalc.result.setTextColor(mTextColor) + binding.configCalc.formula.setTextColor(mTextColor) + binding.configSave.setTextColor(getProperPrimaryColor().getContrastColor()) viewIds.forEach { (findViewById(it)).setTextColor(mTextColor) diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/adapters/HistoryAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/adapters/HistoryAdapter.kt index 1d2d468e..51b85202 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calculator/adapters/HistoryAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/adapters/HistoryAdapter.kt @@ -3,23 +3,21 @@ package com.simplemobiletools.calculator.adapters import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import com.simplemobiletools.calculator.R import com.simplemobiletools.calculator.activities.SimpleActivity +import com.simplemobiletools.calculator.databinding.HistoryViewBinding import com.simplemobiletools.calculator.helpers.CalculatorImpl import com.simplemobiletools.calculator.models.History import com.simplemobiletools.commons.extensions.copyToClipboard import com.simplemobiletools.commons.extensions.getProperTextColor -import kotlinx.android.synthetic.main.history_view.view.* class HistoryAdapter(val activity: SimpleActivity, val items: List, val calc: CalculatorImpl, val itemClick: () -> Unit) : RecyclerView.Adapter() { private var textColor = activity.getProperTextColor() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view = activity.layoutInflater.inflate(R.layout.history_view, parent, false) - return ViewHolder(view) - } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = + ViewHolder(HistoryViewBinding.inflate(activity.layoutInflater, parent, false)) + override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = items[position] @@ -28,13 +26,13 @@ class HistoryAdapter(val activity: SimpleActivity, val items: List, val override fun getItemCount() = items.size - inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { + inner class ViewHolder(private val binding: HistoryViewBinding) : RecyclerView.ViewHolder(binding.root) { fun bindView(item: History): View { itemView.apply { - item_formula.text = item.formula - item_result.text = item.result - item_formula.setTextColor(textColor) - item_result.setTextColor(textColor) + binding.itemFormula.text = item.formula + binding.itemResult.text = item.result + binding.itemFormula.setTextColor(textColor) + binding.itemResult.setTextColor(textColor) setOnClickListener { calc.addNumberToFormula(item.result) diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/BooleanPreviewParameterProvider.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/BooleanPreviewParameterProvider.kt new file mode 100644 index 00000000..1933640c --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/BooleanPreviewParameterProvider.kt @@ -0,0 +1,8 @@ +package com.simplemobiletools.calculator.compose.extensions + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider + +class BooleanPreviewParameterProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf(false, true) +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/ComposeExtensions.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/ComposeExtensions.kt new file mode 100644 index 00000000..ed088c31 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/ComposeExtensions.kt @@ -0,0 +1,39 @@ +package com.simplemobiletools.calculator.compose.extensions + +import android.app.Activity +import android.content.Context +import android.content.ContextWrapper +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.* +import androidx.compose.ui.graphics.Color +import androidx.lifecycle.Lifecycle +import com.google.accompanist.systemuicontroller.rememberSystemUiController + +fun Context.getActivity(): Activity { + return when (this) { + is Activity -> this + is ContextWrapper -> baseContext.getActivity() + else -> getActivity() + } +} + +@Composable +fun TransparentSystemBars() { + val systemUiController = rememberSystemUiController() + val isSystemInDarkTheme = isSystemInDarkTheme() + SideEffect { + systemUiController.setSystemBarsColor(Color.Transparent, darkIcons = !isSystemInDarkTheme) + } +} + +@Composable +fun onEventValue(event: Lifecycle.Event = Lifecycle.Event.ON_START, value: () -> T): T { + val rememberLatestUpdateState by rememberUpdatedState(newValue = value) + var rememberedValue by remember { mutableStateOf(value()) } + OnLifecycleEvent { lifecycleEvent -> + if (lifecycleEvent == event) { + rememberedValue = rememberLatestUpdateState() + } + } + return rememberedValue +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/LifecycleExtensions.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/LifecycleExtensions.kt new file mode 100644 index 00000000..c5711c9e --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/LifecycleExtensions.kt @@ -0,0 +1,27 @@ +package com.simplemobiletools.calculator.compose.extensions + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner + +@Composable +fun OnLifecycleEvent( + lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current, + onEvent: (event: Lifecycle.Event) -> Unit +) { + val currentOnEvent by rememberUpdatedState(onEvent) + DisposableEffect(lifecycleOwner) { + val observer = LifecycleEventObserver { _, event -> + currentOnEvent(event) + } + lifecycleOwner.lifecycle.addObserver(observer) + onDispose { + lifecycleOwner.lifecycle.removeObserver(observer) + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/NoRippleTheme.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/NoRippleTheme.kt new file mode 100644 index 00000000..e4f2ca54 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/NoRippleTheme.kt @@ -0,0 +1,19 @@ +package com.simplemobiletools.calculator.compose.extensions + +import androidx.compose.material.ripple.RippleAlpha +import androidx.compose.material.ripple.RippleTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +object NoRippleTheme : RippleTheme { + @Composable + override fun defaultColor(): Color = Color.Unspecified + + @Composable + override fun rippleAlpha(): RippleAlpha = RippleAlpha( + draggedAlpha = 0f, + focusedAlpha = 0f, + hoveredAlpha = 0f, + pressedAlpha = 0f, + ) +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/PreviewExtensions.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/PreviewExtensions.kt new file mode 100644 index 00000000..e685cbc8 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/extensions/PreviewExtensions.kt @@ -0,0 +1,28 @@ +package com.simplemobiletools.calculator.compose.extensions + +import android.content.res.Configuration +import androidx.compose.ui.tooling.preview.Devices +import androidx.compose.ui.tooling.preview.Preview + +const val LIGHT = "Light" +const val DARK = "Dark" + +@MyDevicesDarkOnly +@MyDevicesLightOnly +annotation class MyDevices + +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_4_XL, name = "6.3 inches dark", group = DARK) +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_2, name = "5.0 inches dark", group = DARK) +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_2_XL, name = "6.0 inches dark", group = DARK) +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_4_XL, name = "5.5 inches dark", group = DARK) +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_4, name = "5.7 inches dark", group = DARK) +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.TABLET, name = "Tablet", group = DARK) +annotation class MyDevicesDarkOnly + +@Preview(showBackground = true, device = Devices.PIXEL_4_XL, name = "6.3 inches light", group = LIGHT) +@Preview(showBackground = true, device = Devices.PIXEL_2, name = "5.0 inches light", group = LIGHT) +@Preview(showBackground = true, device = Devices.PIXEL_2_XL, name = "6.0 inches light", group = LIGHT) +@Preview(showBackground = true, device = Devices.PIXEL_XL, name = "5.5 inches light", group = LIGHT) +@Preview(showBackground = true, device = Devices.PIXEL_4, name = "5.7 inches light", group = LIGHT) +@Preview(showBackground = true, device = Devices.TABLET, name = "Tablet", group = DARK) +annotation class MyDevicesLightOnly diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/screens/SettingsScreen.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/screens/SettingsScreen.kt new file mode 100644 index 00000000..cf400737 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/screens/SettingsScreen.kt @@ -0,0 +1,226 @@ +package com.simplemobiletools.calculator.compose.screens + +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.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.lerp +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.google.accompanist.systemuicontroller.rememberSystemUiController +import com.simplemobiletools.calculator.compose.extensions.MyDevices +import com.simplemobiletools.calculator.compose.settings.SettingsCheckBoxComponent +import com.simplemobiletools.calculator.compose.settings.SettingsGroup +import com.simplemobiletools.calculator.compose.settings.SettingsPreferenceComponent +import com.simplemobiletools.calculator.compose.settings.SettingsTitleTextComponent +import com.simplemobiletools.calculator.compose.theme.AppThemeSurface +import com.simplemobiletools.calculator.compose.theme.divider_grey +import com.simplemobiletools.calculator.compose.theme.isNotLitWell +import com.simplemobiletools.calculator.compose.theme.isSurfaceLitWell +import com.simplemobiletools.commons.R +import com.simplemobiletools.commons.helpers.isTiramisuPlus +import java.util.Locale + +@Composable +fun SettingsScreen( + goBack: () -> Unit, + customizeColors: () -> Unit, + customizeWidgetColors: () -> Unit, + topBarsScrolledContainerColor: Color, + nonScrolledTextColor: Color = if (isSurfaceLitWell()) Color.Black else Color.White, + preventPhoneFromSleeping: Boolean, + onPreventPhoneFromSleeping: (Boolean) -> Unit, + vibrateOnButtonPressFlow: Boolean, + onVibrateOnButtonPressFlow: (Boolean) -> Unit, + isOrWasThankYouInstalled: Boolean, + onThankYou: () -> Unit, + isUseEnglishEnabled: Boolean, + isUseEnglishChecked: Boolean, + onUseEnglishPress: (Boolean) -> Unit, + onSetupLanguagePress: () -> Unit, + useCommaAsDecimalMarkFlow: Boolean, + onUseCommaAsDecimalMarkFlow: (Boolean) -> Unit, + lockedCustomizeColorText: String?, + topBarsContentColor: Color +) { + val systemUiController = rememberSystemUiController() + val displayLanguage = remember { Locale.getDefault().displayLanguage } + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + val colorTransitionFraction = scrollBehavior.state.overlappedFraction + val fraction = if (colorTransitionFraction > 0.01f) 1f else 0f + val scrolledColor = lerp( + start = nonScrolledTextColor, + stop = topBarsContentColor, + fraction = fraction + ) + SideEffect { + systemUiController.setStatusBarColor(Color.Transparent, darkIcons = scrolledColor.isNotLitWell()) + } + val startingPadding = Modifier.padding(horizontal = 4.dp) + val navigationIconInteractionSource = remember { MutableInteractionSource() } + Scaffold( + modifier = Modifier + .fillMaxSize() + .nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + TopAppBar( + title = { + Text( + text = stringResource(id = R.string.settings), + modifier = Modifier + .padding(start = 8.dp) + .fillMaxWidth(), + color = scrolledColor + ) + }, + navigationIcon = { + Box( + Modifier + .padding(start = 8.dp) + .clip(RoundedCornerShape(50)) + .clickable( + navigationIconInteractionSource, rememberRipple( + color = MaterialTheme.colorScheme.onSurface, + bounded = true + ) + ) { goBack() } + ) { + Icon( + imageVector = Icons.Filled.ArrowBack, contentDescription = stringResource(id = R.string.back), + tint = scrolledColor, + modifier = Modifier.padding(5.dp) + ) + } + + }, + scrollBehavior = scrollBehavior, + colors = TopAppBarDefaults.largeTopAppBarColors( + scrolledContainerColor = topBarsScrolledContainerColor, + containerColor = if (colorTransitionFraction == 1f) topBarsContentColor else MaterialTheme.colorScheme.surface, + navigationIconContentColor = if (colorTransitionFraction == 1f) topBarsContentColor else MaterialTheme.colorScheme.surface + ), + ) + } + ) { paddingValues -> + Box( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .background(MaterialTheme.colorScheme.surface) + ) { + Column( + Modifier + .matchParentSize() + .verticalScroll(rememberScrollState()), + ) { + SettingsGroup(title = { + SettingsTitleTextComponent(text = stringResource(id = R.string.color_customization), modifier = startingPadding) + }) { + SettingsPreferenceComponent( + modifier = Modifier + .padding(bottom = 12.dp, top = 8.dp) + .then(startingPadding), + preferenceTitle = stringResource(id = R.string.customize_colors), + doOnPreferenceClick = customizeColors, + isPreferenceEnabled = isOrWasThankYouInstalled, + preferenceSummary = lockedCustomizeColorText + ) + SettingsPreferenceComponent( + modifier = Modifier + .padding(bottom = 12.dp) + .then(startingPadding), + preferenceTitle = stringResource(id = R.string.customize_widget_colors), + doOnPreferenceClick = customizeWidgetColors + ) + } + Divider(color = divider_grey) + SettingsGroup(title = { + SettingsTitleTextComponent(text = stringResource(id = R.string.general_settings), modifier = startingPadding) + }) { + if (!isOrWasThankYouInstalled) { + SettingsPreferenceComponent( + preferenceTitle = stringResource(id = R.string.purchase_simple_thank_you), + doOnPreferenceClick = onThankYou, + modifier = startingPadding, + ) + } + if (isUseEnglishEnabled) { + SettingsCheckBoxComponent( + title = stringResource(id = R.string.use_english_language), + initialValue = isUseEnglishChecked, + onChange = onUseEnglishPress, + modifier = startingPadding, + ) + } + if (isTiramisuPlus()) { + SettingsPreferenceComponent( + preferenceTitle = stringResource(id = R.string.language), + preferenceSummary = displayLanguage, + doOnPreferenceClick = onSetupLanguagePress, + preferenceSummaryColor = MaterialTheme.colorScheme.onSurface, + modifier = startingPadding, + ) + } + SettingsCheckBoxComponent( + title = stringResource(id = R.string.vibrate_on_button_press), + initialValue = vibrateOnButtonPressFlow, + onChange = onVibrateOnButtonPressFlow, + modifier = startingPadding, + ) + SettingsCheckBoxComponent( + title = stringResource(id = R.string.prevent_phone_from_sleeping), + initialValue = preventPhoneFromSleeping, + onChange = onPreventPhoneFromSleeping, + modifier = startingPadding, + ) + SettingsCheckBoxComponent( + title = stringResource(id = com.simplemobiletools.calculator.R.string.use_comma_as_decimal_mark), + initialValue = useCommaAsDecimalMarkFlow, + onChange = onUseCommaAsDecimalMarkFlow, + modifier = startingPadding, + ) + } + } + } + } +} + +@MyDevices +@Composable +private fun SettingsScreenPreview() { + AppThemeSurface { + SettingsScreen( + goBack = {}, + customizeColors = {}, + customizeWidgetColors = {}, + topBarsScrolledContainerColor = MaterialTheme.colorScheme.primary, + nonScrolledTextColor = Color.White, + preventPhoneFromSleeping = false, + onPreventPhoneFromSleeping = {}, + vibrateOnButtonPressFlow = false, + onVibrateOnButtonPressFlow = {}, + isOrWasThankYouInstalled = false, + onThankYou = {}, + isUseEnglishEnabled = false, + isUseEnglishChecked = false, + onUseEnglishPress = {}, + onSetupLanguagePress = {}, + useCommaAsDecimalMarkFlow = false, onUseCommaAsDecimalMarkFlow = {}, lockedCustomizeColorText = null, + topBarsContentColor = MaterialTheme.colorScheme.onPrimary + ) + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/settings/SettingsCheckBoxComponent.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/settings/SettingsCheckBoxComponent.kt new file mode 100644 index 00000000..1446a9a2 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/settings/SettingsCheckBoxComponent.kt @@ -0,0 +1,103 @@ +package com.simplemobiletools.calculator.compose.settings + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.LocalIndication +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.LocalRippleTheme +import androidx.compose.material3.Checkbox +import androidx.compose.material3.CheckboxDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.unit.dp +import com.simplemobiletools.calculator.compose.extensions.MyDevices +import com.simplemobiletools.calculator.compose.extensions.NoRippleTheme +import com.simplemobiletools.calculator.compose.theme.AppThemeSurface +import com.simplemobiletools.calculator.compose.theme.preferenceSummaryColor +import com.simplemobiletools.calculator.compose.theme.preferenceTitleColor +import com.simplemobiletools.commons.R + +@Composable +fun SettingsCheckBoxComponent( + modifier: Modifier = Modifier, + title: String, + summary: String? = null, + initialValue: Boolean = false, + isPreferenceEnabled: Boolean = true, + onChange: ((Boolean) -> Unit)? = null, + checkboxColor: Color = MaterialTheme.colorScheme.primary +) { + val interactionSource = remember { MutableInteractionSource() } + val indication = LocalIndication.current + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + .fillMaxWidth() + .clickable( + onClick = { onChange?.invoke(!initialValue) }, + interactionSource = interactionSource, + indication = indication + ) + .padding(horizontal = 16.dp, vertical = 6.dp), + ) { + Column( + modifier = Modifier.weight(1f) + ) { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(end = 16.dp), + text = title, + color = preferenceTitleColor(isEnabled = isPreferenceEnabled), + fontSize = with(LocalDensity.current) { + dimensionResource(id = R.dimen.normal_text_size).toSp() + } + ) + AnimatedVisibility(visible = !summary.isNullOrBlank()) { + Text( + text = summary.toString(), + modifier = Modifier + .fillMaxWidth() + .padding(end = 16.dp), + color = preferenceSummaryColor(isEnabled = isPreferenceEnabled), + ) + } + } + CompositionLocalProvider(LocalRippleTheme provides NoRippleTheme) { + Checkbox( + checked = initialValue, + onCheckedChange = { onChange?.invoke(it) }, + enabled = isPreferenceEnabled, + colors = CheckboxDefaults.colors( + checkedColor = checkboxColor, + checkmarkColor = MaterialTheme.colorScheme.surface + ), + interactionSource = interactionSource + ) + } + } +} + +@MyDevices +@Composable +private fun SettingsCheckBoxComponentPreview() { + AppThemeSurface { + SettingsCheckBoxComponent( + title = "Some title", + summary = "Some summary", + ) + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/settings/SettingsGroup.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/settings/SettingsGroup.kt new file mode 100644 index 00000000..20c1b390 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/settings/SettingsGroup.kt @@ -0,0 +1,63 @@ +package com.simplemobiletools.calculator.compose.settings + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ProvideTextStyle +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.simplemobiletools.calculator.compose.extensions.MyDevices + +@Composable +fun SettingsGroup( + modifier: Modifier = Modifier, + title: @Composable (() -> Unit)? = null, + content: @Composable ColumnScope.() -> Unit, +) { + Column( + modifier = modifier.fillMaxWidth(), + ) { + if (title != null) { + SettingsGroupTitle(title = title) + } + content() + } +} + +@Composable +fun SettingsGroupTitle( + modifier: Modifier = Modifier, + title: @Composable () -> Unit +) { + Box( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + contentAlignment = Alignment.CenterStart + ) { + val primary = MaterialTheme.colorScheme.primary + val titleStyle = MaterialTheme.typography.headlineMedium.copy(color = primary) + ProvideTextStyle(value = titleStyle) { title() } + } +} + +@MyDevices +@Composable +private fun SettingsGroupPreview() { + MaterialTheme { + SettingsGroup( + title = { Text(text = "Title") } + ) { + Box( + modifier = Modifier + .height(64.dp) + .fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + Text(text = "Settings group") + } + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/settings/SettingsPreferenceComponent.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/settings/SettingsPreferenceComponent.kt new file mode 100644 index 00000000..c2348fe6 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/settings/SettingsPreferenceComponent.kt @@ -0,0 +1,75 @@ +package com.simplemobiletools.calculator.compose.settings + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.simplemobiletools.calculator.compose.extensions.MyDevices +import com.simplemobiletools.calculator.compose.theme.AppThemeSurface +import com.simplemobiletools.calculator.compose.theme.preferenceSummaryColor +import com.simplemobiletools.calculator.compose.theme.preferenceTitleColor +import com.simplemobiletools.commons.R + +@Composable +fun SettingsPreferenceComponent( + modifier: Modifier = Modifier, + preferenceTitle: String, + preferenceSummary: String? = null, + isPreferenceEnabled: Boolean = true, + doOnPreferenceLongClick: (() -> Unit)? = null, + doOnPreferenceClick: (() -> Unit)? = null, + preferenceSummaryColor: Color = preferenceSummaryColor(isEnabled = isPreferenceEnabled) +) { + Column( + modifier = Modifier + .fillMaxWidth() + .combinedClickable( + enabled = isPreferenceEnabled, + onClick = { doOnPreferenceClick?.invoke() }, + onLongClick = { doOnPreferenceLongClick?.invoke() }, + ) + .padding(horizontal = 16.dp, vertical = 8.dp) + .then(modifier), + ) { + Text( + text = preferenceTitle, + modifier = Modifier.fillMaxWidth(), + color = preferenceTitleColor(isEnabled = isPreferenceEnabled), + fontSize = with(LocalDensity.current) { + dimensionResource(id = R.dimen.normal_text_size).toSp() + } + ) + AnimatedVisibility(visible = !preferenceSummary.isNullOrBlank()) { + Text( + text = preferenceSummary.toString(), + modifier = Modifier + .fillMaxWidth(), + color = preferenceSummaryColor.copy(alpha = 0.6f), + fontSize = with(LocalDensity.current) { + dimensionResource(id = R.dimen.normal_text_size).toSp() + } + ) + } + } +} + +@MyDevices +@Composable +private fun SettingsPreferencePreview() { + AppThemeSurface { + SettingsPreferenceComponent( + preferenceTitle = stringResource(id = R.string.language), + preferenceSummary = stringResource(id = R.string.translation_english), + isPreferenceEnabled = true, + ) + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/settings/SettingsTitleTextComponent.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/settings/SettingsTitleTextComponent.kt new file mode 100644 index 00000000..e1443cad --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/settings/SettingsTitleTextComponent.kt @@ -0,0 +1,36 @@ +package com.simplemobiletools.calculator.compose.settings + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.unit.dp +import com.simplemobiletools.calculator.compose.extensions.MyDevices +import com.simplemobiletools.calculator.compose.theme.AppThemeSurface + +@Composable +fun SettingsTitleTextComponent( + modifier: Modifier = Modifier, + text: String, +) { + Box(modifier = Modifier.padding(top = 24.dp, bottom = 8.dp)) { + Text( + text = text.uppercase(), + modifier = modifier, + color = MaterialTheme.colorScheme.primary, + fontSize = with(LocalDensity.current) { + dimensionResource(id = com.simplemobiletools.commons.R.dimen.normal_text_size).toSp() + }, + ) + } +} + +@MyDevices +@Composable +private fun SettingsTitleTextComponentPreview() = AppThemeSurface { + SettingsTitleTextComponent(text = "Color customization") +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/AppModifiers.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/AppModifiers.kt new file mode 100644 index 00000000..20ade471 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/AppModifiers.kt @@ -0,0 +1,47 @@ +package com.simplemobiletools.calculator.compose.theme + +import android.app.Activity +import android.app.ActivityManager +import android.content.Context +import android.graphics.BitmapFactory +import com.simplemobiletools.calculator.compose.extensions.getActivity +import com.simplemobiletools.calculator.helpers.Config +import com.simplemobiletools.commons.R +import com.simplemobiletools.commons.helpers.APP_ICON_IDS +import com.simplemobiletools.commons.helpers.APP_LAUNCHER_NAME + +fun Activity.getAppIconIds(): ArrayList = ArrayList(intent.getIntegerArrayListExtra(APP_ICON_IDS).orEmpty()) +fun Activity.getAppLauncherName(): String = intent.getStringExtra(APP_LAUNCHER_NAME).orEmpty() +internal fun updateRecentsAppIcon(baseConfig: Config, context: Context) { + if (baseConfig.isUsingModifiedAppIcon) { + val appIconIDs = context.getAppIconIds() + val currentAppIconColorIndex = baseConfig.getCurrentAppIconColorIndex(context) + if (appIconIDs.size - 1 < currentAppIconColorIndex) { + return + } + + val recentsIcon = BitmapFactory.decodeResource(context.resources, appIconIDs[currentAppIconColorIndex]) + val title = context.getAppLauncherName() + val color = baseConfig.primaryColor + + val description = ActivityManager.TaskDescription(title, recentsIcon, color) + context.getActivity().setTaskDescription(description) + } +} + +private fun Config.getCurrentAppIconColorIndex(context: Context): Int { + val appIconColor = appIconColor + context.getAppIconColors().forEachIndexed { index, color -> + if (color == appIconColor) { + return index + } + } + return 0 +} + +private fun Context.getAppIconColors() = resources.getIntArray(R.array.md_app_icon_colors).toCollection(ArrayList()) + +private fun Context.getAppIconIds(): List = getActivity().getAppIconIds() + +private fun Context.getAppLauncherName(): String = getActivity().getAppLauncherName() + diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/AppTheme.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/AppTheme.kt new file mode 100644 index 00000000..9b167e7c --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/AppTheme.kt @@ -0,0 +1,40 @@ +package com.simplemobiletools.calculator.compose.theme + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Surface +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.lifecycle.Lifecycle +import com.simplemobiletools.calculator.compose.extensions.OnLifecycleEvent +import com.simplemobiletools.calculator.compose.theme.model.Theme.Companion.systemDefaultMaterialYou + +@Composable +fun AppThemeSurface( + modifier: Modifier = Modifier, + content: @Composable () -> Unit, +) { + val view = LocalView.current + + val context = LocalContext.current + val materialYouTheme = systemDefaultMaterialYou() + var currentTheme by remember { + mutableStateOf( + if (view.isInEditMode) materialYouTheme else getTheme( + context = context, + materialYouTheme = materialYouTheme + ) + ) + } + OnLifecycleEvent { event -> + if (event == Lifecycle.Event.ON_START && !view.isInEditMode) { + currentTheme = getTheme(context = context, materialYouTheme = materialYouTheme) + } + } + Theme(theme = currentTheme) { + Surface(modifier = modifier.fillMaxSize()) { + content() + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/ColorSchemes.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/ColorSchemes.kt new file mode 100644 index 00000000..23261aeb --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/ColorSchemes.kt @@ -0,0 +1,9 @@ +package com.simplemobiletools.calculator.compose.theme + +import androidx.compose.material3.darkColorScheme + +internal val darkColorScheme = darkColorScheme( + primary = color_primary, + secondary = color_primary_dark, + tertiary = color_accent, +) diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/Colors.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/Colors.kt new file mode 100644 index 00000000..24004598 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/Colors.kt @@ -0,0 +1,444 @@ +package com.simplemobiletools.calculator.compose.theme + +import androidx.compose.ui.graphics.Color + +val color_primary = Color(0xFFF57C00) +val color_primary_dark = Color(0xFFD76D00) +val color_accent = color_primary + +val pressed_item_foreground = Color(0x08000000) +val activated_item_foreground = Color(0x44888888) +val divider_grey = Color(0x55808080) +val gradient_grey_start = Color(0xCC000000) +val dark_grey = Color(0xFF333333) +val bottom_tabs_light_background = Color(0xFFF1F1F1) +val disabled_text_color_highlight = Color(0x00FFFFFF) + +val hint_white = Color(0x99FFFFFF) +val hint_black = Color(0x66000000) +val light_grey_stroke = Color(0x40FFFFFF) + +val thumb_deactivated = Color(0xFFECECEC) +val track_deactivated = Color(0xFFB2B2B2) + +val radiobutton_disabled = Color(0xFF757575) + +val md_amber = Color(0xFFFFC107) +val md_blue = Color(0xFF2196F3) +val md_blue_grey = Color(0xFF607D8B) +val md_brown = Color(0xFF795548) +val md_cyan = Color(0xFF00BCD4) +val md_deep_orange = Color(0xFFFF5722) +val md_deep_purple = Color(0xFF673AB7) +val md_green = Color(0xFF4CAF50) +val md_grey = Color(0xFF9E9E9E) +val md_indigo = Color(0xFF3F51B5) +val md_light_blue = Color(0xFF03A9F4) +val md_light_green = Color(0xFF8BC34A) +val md_lime = Color(0xFFCDDC39) +val md_orange = Color(0xFFFF9800) +val md_pink = Color(0xFFE91E63) +val md_purple = Color(0xFF9C27B0) +val md_red = Color(0xFFF44336) +val md_teal = Color(0xFF009688) +val md_yellow = Color(0xFFFFEB3B) + +val md_amber_100 = Color(0xFFFFECB3) +val md_amber_200 = Color(0xFFFFE082) +val md_amber_300 = Color(0xFFFFD54F) +val md_amber_400 = Color(0xFFFFCA28) +val md_amber_500 = Color(0xFFFFC107) +val md_amber_600 = Color(0xFFFFB300) +val md_amber_700 = Color(0xFFFFA000) +val md_amber_800 = Color(0xFFFF8F00) +val md_amber_900 = Color(0xFFFF6F00) + +val md_amber_100_dark = Color(0xFFFFE28A) +val md_amber_200_dark = Color(0xFFFFD659) +val md_amber_300_dark = Color(0xFFFFCC26) +val md_amber_400_dark = Color(0xFFFFC100) +val md_amber_500_dark = Color(0xFFDEA700) +val md_amber_600_dark = Color(0xFFD79700) +val md_amber_700_dark = Color(0xFFD78700) +val md_amber_800_dark = Color(0xFFD77800) +val md_amber_900_dark = Color(0xFFD75D00) + +val md_blue_100 = Color(0xFFBBDEFB) +val md_blue_200 = Color(0xFF90CAF9) +val md_blue_300 = Color(0xFF64B5F6) +val md_blue_400 = Color(0xFF42A5F5) +val md_blue_500 = Color(0xFF2196F3) +val md_blue_600 = Color(0xFF1E88E5) +val md_blue_700 = Color(0xFF1976D2) +val md_blue_800 = Color(0xFF1565C0) +val md_blue_900 = Color(0xFF0D47A1) + +val md_blue_100_dark = Color(0xFF94CCF9) +val md_blue_200_dark = Color(0xFF69B8F7) +val md_blue_300_dark = Color(0xFF3DA2F4) +val md_blue_400_dark = Color(0xFF1A92F3) +val md_blue_500_dark = Color(0xFF0B82E0) +val md_blue_600_dark = Color(0xFF1673C4) +val md_blue_700_dark = Color(0xFF1462AE) +val md_blue_800_dark = Color(0xFF11529B) +val md_blue_900_dark = Color(0xFF09367B) + +val md_blue_grey_100 = Color(0xFFCFD8DC) +val md_blue_grey_200 = Color(0xFFB0BBC5) +val md_blue_grey_300 = Color(0xFF90A4AE) +val md_blue_grey_400 = Color(0xFF78909C) +val md_blue_grey_500 = Color(0xFF607D8B) +val md_blue_grey_600 = Color(0xFF546E7A) +val md_blue_grey_700 = Color(0xFF455A64) +val md_blue_grey_800 = Color(0xFF37474F) +val md_blue_grey_900 = Color(0xFF263238) + +val md_blue_grey_100_dark = Color(0xFFB8C5CB) +val md_blue_grey_200_dark = Color(0xFF99A7B4) +val md_blue_grey_300_dark = Color(0xFF78919D) +val md_blue_grey_400_dark = Color(0xFF647C88) +val md_blue_grey_500_dark = Color(0xFF4F6873) +val md_blue_grey_600_dark = Color(0xFF445962) +val md_blue_grey_700_dark = Color(0xFF34454C) +val md_blue_grey_800_dark = Color(0xFF263237) +val md_blue_grey_900_dark = Color(0xFF151C1F) + +val md_brown_100 = Color(0xFFD7CCC8) +val md_brown_200 = Color(0xFFBCAAA4) +val md_brown_300 = Color(0xFFA1887F) +val md_brown_400 = Color(0xFF8D6E63) +val md_brown_500 = Color(0xFF795548) +val md_brown_600 = Color(0xFF6D4C41) +val md_brown_700 = Color(0xFF5D4037) +val md_brown_800 = Color(0xFF4E342E) +val md_brown_900 = Color(0xFF3E2723) + +val md_brown_100_dark = Color(0xFFC6B7B1) +val md_brown_200_dark = Color(0xFFAB958D) +val md_brown_300_dark = Color(0xFF8F7369) +val md_brown_400_dark = Color(0xFF755B52) +val md_brown_500_dark = Color(0xFF5F4339) +val md_brown_600_dark = Color(0xFF533A31) +val md_brown_700_dark = Color(0xFF432E28) +val md_brown_800_dark = Color(0xFF34231F) +val md_brown_900_dark = Color(0xFF241714) + +val md_cyan_100 = Color(0xFFB2EBF2) +val md_cyan_200 = Color(0xFF80DEEA) +val md_cyan_300 = Color(0xFF4DD0E1) +val md_cyan_400 = Color(0xFF26C6DA) +val md_cyan_500 = Color(0xFF00BCD4) +val md_cyan_600 = Color(0xFF00ACC1) +val md_cyan_700 = Color(0xFF0097A7) +val md_cyan_800 = Color(0xFF00838F) +val md_cyan_900 = Color(0xFF006064) + +val md_cyan_100_dark = Color(0xFF90E3ED) +val md_cyan_200_dark = Color(0xFF5DD5E5) +val md_cyan_300_dark = Color(0xFF2AC7DB) +val md_cyan_400_dark = Color(0xFF1FA7B8) +val md_cyan_500_dark = Color(0xFF0098AB) +val md_cyan_600_dark = Color(0xFF008898) +val md_cyan_700_dark = Color(0xFF00727E) +val md_cyan_800_dark = Color(0xFF005E66) +val md_cyan_900_dark = Color(0xFF00393B) + +val md_deep_orange_100 = Color(0xFFFFCCBC) +val md_deep_orange_200 = Color(0xFFFFAB91) +val md_deep_orange_300 = Color(0xFFFF8A65) +val md_deep_orange_400 = Color(0xFFFF7043) +val md_deep_orange_500 = Color(0xFFFF5722) +val md_deep_orange_600 = Color(0xFFF4511E) +val md_deep_orange_700 = Color(0xFFE64A19) +val md_deep_orange_800 = Color(0xFFD84315) +val md_deep_orange_900 = Color(0xFFBF360C) + +val md_deep_orange_100_dark = Color(0xFFFFAD93) +val md_deep_orange_200_dark = Color(0xFFFF8C68) +val md_deep_orange_300_dark = Color(0xFFFF6B3C) +val md_deep_orange_400_dark = Color(0xFFFF511A) +val md_deep_orange_500_dark = Color(0xFFF93C00) +val md_deep_orange_600_dark = Color(0xFFDF3D0A) +val md_deep_orange_700_dark = Color(0xFFC13E14) +val md_deep_orange_800_dark = Color(0xFFB33710) +val md_deep_orange_900_dark = Color(0xFF992B09) + +val md_deep_purple_100 = Color(0xFFD1C4E9) +val md_deep_purple_200 = Color(0xFFB39DDB) +val md_deep_purple_300 = Color(0xFF9575CD) +val md_deep_purple_400 = Color(0xFF7E57C2) +val md_deep_purple_500 = Color(0xFF673AB7) +val md_deep_purple_600 = Color(0xFF5E35B1) +val md_deep_purple_700 = Color(0xFF512DA8) +val md_deep_purple_800 = Color(0xFF4527A0) +val md_deep_purple_900 = Color(0xFF311B92) + +val md_deep_purple_100_dark = Color(0xFFBAA6DE) +val md_deep_purple_200_dark = Color(0xFF9C7FD0) +val md_deep_purple_300_dark = Color(0xFF7E56C2) +val md_deep_purple_400_dark = Color(0xFF693FB0) +val md_deep_purple_500_dark = Color(0xFF563098) +val md_deep_purple_600_dark = Color(0xFF4E2B92) +val md_deep_purple_700_dark = Color(0xFF412488) +val md_deep_purple_800_dark = Color(0xFF371F7F) +val md_deep_purple_900_dark = Color(0xFF251470) + +val md_green_100 = Color(0xFFC8E6C9) +val md_green_200 = Color(0xFFA5D6A7) +val md_green_300 = Color(0xFF81C784) +val md_green_400 = Color(0xFF66BB6A) +val md_green_500 = Color(0xFF4CAF50) +val md_green_600 = Color(0xFF43A047) +val md_green_700 = Color(0xFF388E3C) +val md_green_800 = Color(0xFF2E7D32) +val md_green_900 = Color(0xFF1B5E20) + +val md_green_100_dark = Color(0xFFACDAAE) +val md_green_200_dark = Color(0xFF89CA8D) +val md_green_300_dark = Color(0xFF65BB69) +val md_green_400_dark = Color(0xFF4CAC51) +val md_green_500_dark = Color(0xFF409343) +val md_green_600_dark = Color(0xFF37833A) +val md_green_700_dark = Color(0xFF2C7130) +val md_green_800_dark = Color(0xFF235F26) +val md_green_900_dark = Color(0xFF113E15) + +val md_grey_white = Color(0xFFFFFFFF) +val md_grey_200 = Color(0xFFEEEEEE) +val md_grey_300 = Color(0xFFE0E0E0) +val md_grey_400 = Color(0xFFBDBDBD) +val md_grey_500 = Color(0xFF9E9E9E) +val md_grey_600 = Color(0xFF757575) +val md_grey_700 = Color(0xFF616161) +val md_grey_800 = Color(0xFF424242) +val md_grey_black = Color(0xFF000000) + +val md_grey_white_dark = Color(0xFFDFDFDF) +val md_grey_200_dark = Color(0xFFDADADA) +val md_grey_300_dark = Color(0xFFCCCCCC) +val md_grey_400_dark = Color(0xFFA9A9A9) +val md_grey_500_dark = Color(0xFF8A8A8A) +val md_grey_600_dark = Color(0xFF606060) +val md_grey_700_dark = Color(0xFF4C4C4C) +val md_grey_800_dark = Color(0xFF2D2D2D) +val md_grey_black_dark = Color(0xFF000000) + +val md_indigo_100 = Color(0xFFC5CAE9) +val md_indigo_200 = Color(0xFF9FA8DA) +val md_indigo_300 = Color(0xFF7986CB) +val md_indigo_400 = Color(0xFF5C6BC0) +val md_indigo_500 = Color(0xFF3F51B5) +val md_indigo_600 = Color(0xFF3949AB) +val md_indigo_700 = Color(0xFF303F9F) +val md_indigo_800 = Color(0xFF283593) +val md_indigo_900 = Color(0xFF1A237E) + +val md_indigo_100_dark = Color(0xFFA8AFDE) +val md_indigo_200_dark = Color(0xFF828ECF) +val md_indigo_300_dark = Color(0xFF5B6CC0) +val md_indigo_400_dark = Color(0xFF4354B0) +val md_indigo_500_dark = Color(0xFF344497) +val md_indigo_600_dark = Color(0xFF2E3C8C) +val md_indigo_700_dark = Color(0xFF263380) +val md_indigo_800_dark = Color(0xFF1F2973) +val md_indigo_900_dark = Color(0xFF12195C) + +val md_light_blue_100 = Color(0xFFB3E5FC) +val md_light_blue_200 = Color(0xFF81D4fA) +val md_light_blue_300 = Color(0xFF4fC3F7) +val md_light_blue_400 = Color(0xFF29B6FC) +val md_light_blue_500 = Color(0xFF03A9F4) +val md_light_blue_600 = Color(0xFF039BE5) +val md_light_blue_700 = Color(0xFF0288D1) +val md_light_blue_800 = Color(0xFF0277BD) +val md_light_blue_900 = Color(0xFF01579B) + +val md_light_blue_100_dark = Color(0xFF8BD8FB) +val md_light_blue_200_dark = Color(0xFF59C7F9) +val md_light_blue_300_dark = Color(0xFF27B6F6) +val md_light_blue_400_dark = Color(0xFF02A7F9) +val md_light_blue_500_dark = Color(0xFF028DCC) +val md_light_blue_600_dark = Color(0xFF0280BD) +val md_light_blue_700_dark = Color(0xFF016EA9) +val md_light_blue_800_dark = Color(0xFF015E95) +val md_light_blue_900_dark = Color(0xFF004072) + +val md_light_green_100 = Color(0xFFDCEDC8) +val md_light_green_200 = Color(0xFFC5E1A5) +val md_light_green_300 = Color(0xFFAED581) +val md_light_green_400 = Color(0xFF9CCC65) +val md_light_green_500 = Color(0xFF8BC34A) +val md_light_green_600 = Color(0xFF7CB342) +val md_light_green_700 = Color(0xFF689F38) +val md_light_green_800 = Color(0xFF558B2F) +val md_light_green_900 = Color(0xFF33691E) + +val md_light_green_100_dark = Color(0xFFC9E3A9) +val md_light_green_200_dark = Color(0xFFB2D787) +val md_light_green_300_dark = Color(0xFF9BCB62) +val md_light_green_400_dark = Color(0xFF89C246) +val md_light_green_500_dark = Color(0xFF76AC38) +val md_light_green_600_dark = Color(0xFF679537) +val md_light_green_700_dark = Color(0xFF54812D) +val md_light_green_800_dark = Color(0xFF426C24) +val md_light_green_900_dark = Color(0xFF234915) + +val md_lime_100 = Color(0xFFF0F4C3) +val md_lime_200 = Color(0xFFE6EE9C) +val md_lime_300 = Color(0xFFDCE775) +val md_lime_400 = Color(0xFFD4E157) +val md_lime_500 = Color(0xFFCDDC39) +val md_lime_600 = Color(0xFFC0CA33) +val md_lime_700 = Color(0xFFA4B42B) +val md_lime_800 = Color(0xFF9E9D24) +val md_lime_900 = Color(0xFF827717) + +val md_lime_100_dark = Color(0xFFE8EEA0) +val md_lime_200_dark = Color(0xFFDEE879) +val md_lime_300_dark = Color(0xFFD3E152) +val md_lime_400_dark = Color(0xFFCBDB34) +val md_lime_500_dark = Color(0xFFBAC923) +val md_lime_600_dark = Color(0xFFA2AA2A) +val md_lime_700_dark = Color(0xFF869323) +val md_lime_800_dark = Color(0xFF7D7D1C) +val md_lime_900_dark = Color(0xFF5F5710) + +val md_orange_100 = Color(0xFFFFE0B2) +val md_orange_200 = Color(0xFFFFCC80) +val md_orange_300 = Color(0xFFFFB74D) +val md_orange_400 = Color(0xFFFFA726) +val md_orange_500 = Color(0xFFFF9800) +val md_orange_600 = Color(0xFFFB8C00) +val md_orange_700 = Color(0xFFF57C00) +val md_orange_800 = Color(0xFFEF6C00) +val md_orange_900 = Color(0xFFE65100) + +val md_orange_100_dark = Color(0xFFFFD089) +val md_orange_200_dark = Color(0xFFFFBC57) +val md_orange_300_dark = Color(0xFFFFA724) +val md_orange_400_dark = Color(0xFFFD9600) +val md_orange_500_dark = Color(0xFFD78000) +val md_orange_600_dark = Color(0xFFD37600) +val md_orange_700_dark = Color(0xFFCD6800) +val md_orange_800_dark = Color(0xFFC65A00) +val md_orange_900_dark = Color(0xFFBD4200) + +val md_pink_100 = Color(0xFFF8BBD0) +val md_pink_200 = Color(0xFFF48FB1) +val md_pink_300 = Color(0xFFF06292) +val md_pink_400 = Color(0xFFEC407A) +val md_pink_500 = Color(0xFFE91E63) +val md_pink_600 = Color(0xFFD81B60) +val md_pink_700 = Color(0xFFC2185B) +val md_pink_800 = Color(0xFFAD1457) +val md_pink_900 = Color(0xFF880E4F) + +val md_pink_100_dark = Color(0xFFF596B7) +val md_pink_200_dark = Color(0xFFF16998) +val md_pink_300_dark = Color(0xFFED3C78) +val md_pink_400_dark = Color(0xFFE91A60) +val md_pink_500_dark = Color(0xFFCB1352) +val md_pink_600_dark = Color(0xFFB4154F) +val md_pink_700_dark = Color(0xFF9E134A) +val md_pink_800_dark = Color(0xFF880F44) +val md_pink_900_dark = Color(0xFF630A3A) + +val md_purple_100 = Color(0xFFE1BEE7) +val md_purple_200 = Color(0xFFCE93D8) +val md_purple_300 = Color(0xFFBA68C8) +val md_purple_400 = Color(0xFFAB47BC) +val md_purple_500 = Color(0xFF9C27B0) +val md_purple_600 = Color(0xFF8E24AA) +val md_purple_700 = Color(0xFF7B1FA2) +val md_purple_800 = Color(0xFF6A1B9A) +val md_purple_900 = Color(0xFF4A148C) + +val md_purple_100_dark = Color(0xFFD3A0DC) +val md_purple_200_dark = Color(0xFFC175CD) +val md_purple_300_dark = Color(0xFFAC4ABD) +val md_purple_400_dark = Color(0xFF913AA0) +val md_purple_500_dark = Color(0xFF7F1F8F) +val md_purple_600_dark = Color(0xFF711C88) +val md_purple_700_dark = Color(0xFF611880) +val md_purple_800_dark = Color(0xFF521477) +val md_purple_900_dark = Color(0xFF370E68) + +val md_red_100 = Color(0xFFFFCDD2) +val md_red_200 = Color(0xFFEF9A9A) +val md_red_300 = Color(0xFFE57373) +val md_red_400 = Color(0xFFEF5350) +val md_red_500 = Color(0xFFF44336) +val md_red_600 = Color(0xFFE53935) +val md_red_700 = Color(0xFFD32F2F) +val md_red_800 = Color(0xFFC62828) +val md_red_900 = Color(0xFFB71C1C) + +val md_red_100_dark = Color(0xFFFFA4AE) +val md_red_200_dark = Color(0xFFEA7777) +val md_red_300_dark = Color(0xFFDF5050) +val md_red_400_dark = Color(0xFFEC2E2A) +val md_red_500_dark = Color(0xFFF21F0F) +val md_red_600_dark = Color(0xFFD61F1A) +val md_red_700_dark = Color(0xFFB32525) +val md_red_800_dark = Color(0xFFA42020) +val md_red_900_dark = Color(0xFF941616) + +val md_teal_100 = Color(0xFFB2DFDB) +val md_teal_200 = Color(0xFF80CBC4) +val md_teal_300 = Color(0xFF4DB6AC) +val md_teal_400 = Color(0xFF26A69A) +val md_teal_500 = Color(0xFF009688) +val md_teal_600 = Color(0xFF00897B) +val md_teal_700 = Color(0xFF00796B) +val md_teal_800 = Color(0xFF00695C) +val md_teal_900 = Color(0xFF004D40) + +val md_teal_100_dark = Color(0xFF95D3CE) +val md_teal_200_dark = Color(0xFF63BFB7) +val md_teal_300_dark = Color(0xFF3F9B92) +val md_teal_400_dark = Color(0xFF1E857C) +val md_teal_500_dark = Color(0xFF006D63) +val md_teal_600_dark = Color(0xFF006056) +val md_teal_700_dark = Color(0xFF005047) +val md_teal_800_dark = Color(0xFF004038) +val md_teal_900_dark = Color(0xFF00241E) + +val md_yellow_100 = Color(0xFFFFF9C4) +val md_yellow_200 = Color(0xFFFFF590) +val md_yellow_300 = Color(0xFFFFF176) +val md_yellow_400 = Color(0xFFFFEE58) +val md_yellow_500 = Color(0xFFFFEB3B) +val md_yellow_600 = Color(0xFFFDD835) +val md_yellow_700 = Color(0xFFFBC02D) +val md_yellow_800 = Color(0xFFF9A825) +val md_yellow_900 = Color(0xFFF57F17) + +val md_yellow_100_dark = Color(0xFFFFF59B) +val md_yellow_200_dark = Color(0xFFFFF267) +val md_yellow_300_dark = Color(0xFFFFED4D) +val md_yellow_400_dark = Color(0xFFFFEA2F) +val md_yellow_500_dark = Color(0xFFFFE712) +val md_yellow_600_dark = Color(0xFFFDD10B) +val md_yellow_700_dark = Color(0xFFFBB504) +val md_yellow_800_dark = Color(0xFFEF9606) +val md_yellow_900_dark = Color(0xFFDA6B09) + +val theme_light_text_color = md_grey_800_dark +val theme_light_background_color = md_grey_white +val theme_dark_text_color = md_grey_200 +val theme_dark_background_color = md_grey_800_dark +val theme_solarized_background_color = md_indigo_900_dark +val theme_solarized_text_color = md_amber_700 +val theme_solarized_primary_color = md_indigo_900 +val theme_dark_red_primary_color = md_red_700 + +val default_text_color = theme_dark_text_color +val default_background_color = theme_dark_background_color +val default_primary_color = color_primary +val default_app_icon_color = color_primary +val default_accent_color = color_primary +val default_widget_bg_color = Color(0xAA000000) +val default_widget_text_color = color_primary + +val ripple_light = Color(0x1f000000) +val ripple_dark = Color(0x33ffffff) diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/ColorsExtensions.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/ColorsExtensions.kt new file mode 100644 index 00000000..a3851721 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/ColorsExtensions.kt @@ -0,0 +1,34 @@ +package com.simplemobiletools.calculator.compose.theme + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.luminance + +@get:ReadOnlyComposable +val disabledTextColor @Composable get() = if (isInDarkThemeAndSurfaceIsNotLitWell()) Color.DarkGray else Color.LightGray + +@get:ReadOnlyComposable +val textSubTitleColor + @Composable get() = if (isInDarkThemeAndSurfaceIsNotLitWell()) { + Color.White.copy(0.5f) + } else { + Color.Black.copy( + 0.5f, + ) + } + + +@Composable +@ReadOnlyComposable +fun preferenceSummaryColor(isEnabled: Boolean) = + if (isEnabled) textSubTitleColor else disabledTextColor + +@Composable +@ReadOnlyComposable +fun preferenceTitleColor(isEnabled: Boolean) = if (isEnabled) MaterialTheme.colorScheme.onSurface else disabledTextColor + +fun Color.isLitWell(threshold : Float = LUMINANCE_THRESHOLD) = luminance() > threshold + +fun Color.isNotLitWell(threshold: Float = LUMINANCE_THRESHOLD) = luminance() < threshold diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/DynamicTheme.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/DynamicTheme.kt new file mode 100644 index 00000000..8a466446 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/DynamicTheme.kt @@ -0,0 +1,249 @@ +package com.simplemobiletools.calculator.compose.theme + +import android.content.Context +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import com.simplemobiletools.calculator.compose.theme.model.Theme +import com.simplemobiletools.calculator.extensions.config +import com.simplemobiletools.commons.extensions.isBlackAndWhiteTheme +import com.simplemobiletools.commons.extensions.isWhiteTheme + +fun getTheme(context: Context, materialYouTheme: Theme.SystemDefaultMaterialYou): Theme { + val baseConfig = context.config + val primaryColorInt = baseConfig.primaryColor + val isSystemInDarkTheme = context.isDarkMode() + val accentColor = baseConfig.accentColor + + + val backgroundColorTheme = if (baseConfig.isUsingSystemTheme || baseConfig.isUsingAutoTheme) { + if (isSystemInDarkTheme) theme_dark_background_color else Color.White + } else { + Color(baseConfig.backgroundColor) + } + + val backgroundColor = backgroundColorTheme.toArgb() + val appIconColor = baseConfig.appIconColor + val textColor = baseConfig.textColor + + val theme = when { + baseConfig.isUsingSystemTheme -> materialYouTheme + context.isBlackAndWhiteTheme() -> Theme.BlackAndWhite( + accentColor = accentColor, + primaryColorInt = primaryColorInt, + backgroundColorInt = backgroundColor, + appIconColorInt = appIconColor, + textColorInt = textColor + ) + + context.isWhiteTheme() -> Theme.White( + accentColor = accentColor, + primaryColorInt = primaryColorInt, + backgroundColorInt = backgroundColor, + appIconColorInt = appIconColor, + textColorInt = textColor + ) + + else -> { + val customPrimaryColor = when (primaryColorInt) { + -12846 -> md_red_100 + -1074534 -> md_red_200 + -1739917 -> md_red_300 + -1092784 -> md_red_400 + -769226 -> md_red_500 + -1754827 -> md_red_600 + -2937041 -> md_red_700 + -3790808 -> md_red_800 + -4776932 -> md_red_900 + + -476208 -> md_pink_100 + -749647 -> md_pink_200 + -1023342 -> md_pink_300 + -1294214 -> md_pink_400 + -1499549 -> md_pink_500 + -2614432 -> md_pink_600 + -4056997 -> md_pink_700 + -5434281 -> md_pink_800 + -7860657 -> md_pink_900 + + -1982745 -> md_purple_100 + -3238952 -> md_purple_200 + -4560696 -> md_purple_300 + -5552196 -> md_purple_400 + -6543440 -> md_purple_500 + -7461718 -> md_purple_600 + -8708190 -> md_purple_700 + -9823334 -> md_purple_800 + -11922292 -> md_purple_900 + + -3029783 -> md_deep_purple_100 + -5005861 -> md_deep_purple_200 + -6982195 -> md_deep_purple_300 + -8497214 -> md_deep_purple_400 + -10011977 -> md_deep_purple_500 + -10603087 -> md_deep_purple_600 + -11457112 -> md_deep_purple_700 + -12245088 -> md_deep_purple_800 + -13558894 -> md_deep_purple_900 + + -3814679 -> md_indigo_100 + -6313766 -> md_indigo_200 + -8812853 -> md_indigo_300 + -10720320 -> md_indigo_400 + -12627531 -> md_indigo_500 + -13022805 -> md_indigo_600 + -13615201 -> md_indigo_700 + -14142061 -> md_indigo_800 + -15064194 -> md_indigo_900 + + -4464901 -> md_blue_100 + -7288071 -> md_blue_200 + -10177034 -> md_blue_300 + -12409355 -> md_blue_400 + -14575885 -> md_blue_500 + -14776091 -> md_blue_600 + -15108398 -> md_blue_700 + -15374912 -> md_blue_800 + -15906911 -> md_blue_900 + + -4987396 -> md_light_blue_100 + -8268550 -> md_light_blue_200 + -11549705 -> md_light_blue_300 + -14043396 -> md_light_blue_400 + -16537100 -> md_light_blue_500 + -16540699 -> md_light_blue_600 + -16611119 -> md_light_blue_700 + -16615491 -> md_light_blue_800 + -16689253 -> md_light_blue_900 + + -5051406 -> md_cyan_100 + -8331542 -> md_cyan_200 + -11677471 -> md_cyan_300 + -14235942 -> md_cyan_400 + -16728876 -> md_cyan_500 + -16732991 -> md_cyan_600 + -16738393 -> md_cyan_700 + -16743537 -> md_cyan_800 + -16752540 -> md_cyan_900 + + -5054501 -> md_teal_100 + -8336444 -> md_teal_200 + -11684180 -> md_teal_300 + -14244198 -> md_teal_400 + -16738680 -> md_teal_500 + -16742021 -> md_teal_600 + -16746133 -> md_teal_700 + -16750244 -> md_teal_800 + -16757440 -> md_teal_900 + + -3610935 -> md_green_100 + -5908825 -> md_green_200 + -8271996 -> md_green_300 + -10044566 -> md_green_400 + -11751600 -> md_green_500 + -12345273 -> md_green_600 + -13070788 -> md_green_700 + -13730510 -> md_green_800 + -14983648 -> md_green_900 + + -2298424 -> md_light_green_100 + -3808859 -> md_light_green_200 + -5319295 -> md_light_green_300 + -6501275 -> md_light_green_400 + -7617718 -> md_light_green_500 + -8604862 -> md_light_green_600 + -9920712 -> md_light_green_700 + -11171025 -> md_light_green_800 + -13407970 -> md_light_green_900 + + -985917 -> md_lime_100 + -1642852 -> md_lime_200 + -2300043 -> md_lime_300 + -2825897 -> md_lime_400 + -3285959 -> md_lime_500 + -4142541 -> md_lime_600 + -5983189 -> md_lime_700 + -6382300 -> md_lime_800 + -8227049 -> md_lime_900 + + -1596 -> md_yellow_100 + -2672 -> md_yellow_200 + -3722 -> md_yellow_300 + -4520 -> md_yellow_400 + -5317 -> md_yellow_500 + -141259 -> md_yellow_600 + -278483 -> md_yellow_700 + -415707 -> md_yellow_800 + -688361 -> md_yellow_900 + + -4941 -> md_amber_100 + -8062 -> md_amber_200 + -10929 -> md_amber_300 + -13784 -> md_amber_400 + -16121 -> md_amber_500 + -19712 -> md_amber_600 + -24576 -> md_amber_700 + -28928 -> md_amber_800 + -37120 -> md_amber_900 + + -8014 -> md_orange_100 + -13184 -> md_orange_200 + -18611 -> md_orange_300 + -22746 -> md_orange_400 + -26624 -> md_orange_500 + -291840 -> md_orange_600 + -689152 -> md_orange_700 + -1086464 -> md_orange_800 + -1683200 -> md_orange_900 + + -13124 -> md_deep_orange_100 + -21615 -> md_deep_orange_200 + -30107 -> md_deep_orange_300 + -36797 -> md_deep_orange_400 + -43230 -> md_deep_orange_500 + -765666 -> md_deep_orange_600 + -1684967 -> md_deep_orange_700 + -2604267 -> md_deep_orange_800 + -4246004 -> md_deep_orange_900 + + -2634552 -> md_brown_100 + -4412764 -> md_brown_200 + -6190977 -> md_brown_300 + -7508381 -> md_brown_400 + -8825528 -> md_brown_500 + -9614271 -> md_brown_600 + -10665929 -> md_brown_700 + -11652050 -> md_brown_800 + -12703965 -> md_brown_900 + + -3155748 -> md_blue_grey_100 + -5194811 -> md_blue_grey_200 + -7297874 -> md_blue_grey_300 + -8875876 -> md_blue_grey_400 + -10453621 -> md_blue_grey_500 + -11243910 -> md_blue_grey_600 + -12232092 -> md_blue_grey_700 + -13154481 -> md_blue_grey_800 + -14273992 -> md_blue_grey_900 + + -1 -> md_grey_black_dark + -1118482 -> md_grey_200 + -2039584 -> md_grey_300 + -4342339 -> md_grey_400 + -6381922 -> md_grey_500 + -9079435 -> md_grey_600 + -10395295 -> md_grey_700 + -12434878 -> md_grey_800 + -16777216 -> md_grey_black_dark + + else -> md_orange_700 + } + Theme.Custom( + primaryColorInt = customPrimaryColor.toArgb(), + backgroundColorInt = backgroundColor, + appIconColorInt = appIconColor, + textColorInt = textColor + ) + } + } + return theme +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/Theme.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/Theme.kt new file mode 100644 index 00000000..ffec3153 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/Theme.kt @@ -0,0 +1,79 @@ +package com.simplemobiletools.calculator.compose.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import com.google.accompanist.systemuicontroller.rememberSystemUiController +import com.simplemobiletools.calculator.compose.theme.model.Theme +import com.simplemobiletools.calculator.compose.theme.model.Theme.Companion.systemDefaultMaterialYou +import com.simplemobiletools.calculator.extensions.config +import com.simplemobiletools.commons.helpers.isSPlus + +@Composable +internal fun Theme( + theme: Theme = systemDefaultMaterialYou(), + content: @Composable () -> Unit, +) { + val view = LocalView.current + val context = LocalContext.current + val systemUiController = rememberSystemUiController() + val baseConfig = remember { context.config } + + val colorScheme = if (!view.isInEditMode) { + + val colorScheme = when { + theme is Theme.SystemDefaultMaterialYou && isSPlus() -> { + if (isSystemInDarkTheme()) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + theme is Theme.Custom || theme is Theme.Dark -> darkColorScheme( + primary = theme.primaryColor, surface = theme.backgroundColor, + onSurface = theme.textColor + ) + + theme is Theme.White -> darkColorScheme( + primary = Color(theme.accentColor), + surface = theme.backgroundColor, + tertiary = theme.primaryColor, + onSurface = theme.textColor + ) + + theme is Theme.BlackAndWhite -> darkColorScheme( + primary = Color(theme.accentColor), + surface = theme.backgroundColor, + tertiary = theme.primaryColor, + onSurface = theme.textColor + ) + + else -> darkColorScheme + } + + colorScheme + + } else { + darkColorScheme + } + + SideEffect { + systemUiController.setNavigationBarColor(colorScheme.surface, navigationBarContrastEnforced = false) + } + + SideEffect { + updateRecentsAppIcon(baseConfig, context) + } + + + MaterialTheme( + colorScheme = colorScheme, + content = content, + ) +} + diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/ThemeExtensions.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/ThemeExtensions.kt new file mode 100644 index 00000000..f936fb03 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/ThemeExtensions.kt @@ -0,0 +1,34 @@ +package com.simplemobiletools.calculator.compose.theme + +import android.content.Context +import android.content.res.Configuration +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.graphics.luminance +import androidx.compose.ui.platform.LocalContext +import com.simplemobiletools.calculator.compose.theme.model.Theme.Companion.systemDefaultMaterialYou + + +@Composable +fun getCurrentTheme() = getTheme(LocalContext.current, systemDefaultMaterialYou()) + +@Composable +@ReadOnlyComposable +fun isInDarkThemeAndSurfaceIsNotLitWell() = isSystemInDarkTheme() || isSurfaceNotLitWell() + +internal const val LUMINANCE_THRESHOLD = 0.5f + +@Composable +@ReadOnlyComposable +fun isSurfaceNotLitWell(threshold: Float = LUMINANCE_THRESHOLD) = MaterialTheme.colorScheme.surface.luminance() < threshold + +@Composable +@ReadOnlyComposable +fun isSurfaceLitWell(threshold: Float = LUMINANCE_THRESHOLD) = MaterialTheme.colorScheme.surface.luminance() > threshold + +fun Context.isDarkMode(): Boolean { + val darkModeFlag = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + return darkModeFlag == Configuration.UI_MODE_NIGHT_YES +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/model/CommonTheme.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/model/CommonTheme.kt new file mode 100644 index 00000000..e4541a6e --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/model/CommonTheme.kt @@ -0,0 +1,18 @@ +package com.simplemobiletools.calculator.compose.theme.model + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color + +@Immutable +interface CommonTheme { + val primaryColorInt: Int + val backgroundColorInt: Int + val appIconColorInt: Int + val textColorInt: Int + + val primaryColor get() = Color(primaryColorInt) + val backgroundColor get() = Color(backgroundColorInt) + val appIconColor get() = Color(appIconColorInt) + val textColor get() = Color(textColorInt) + +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/model/Theme.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/model/Theme.kt new file mode 100644 index 00000000..146f2cce --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/compose/theme/model/Theme.kt @@ -0,0 +1,60 @@ +package com.simplemobiletools.calculator.compose.theme.model + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import com.simplemobiletools.calculator.compose.theme.isInDarkThemeAndSurfaceIsNotLitWell +import com.simplemobiletools.calculator.extensions.config + +@Stable +sealed class Theme : CommonTheme { + data class SystemDefaultMaterialYou( + override val primaryColorInt: Int, + override val backgroundColorInt: Int, + override val appIconColorInt: Int, + override val textColorInt: Int + ) : Theme() + + data class White( + val accentColor: Int, + override val primaryColorInt: Int, + override val backgroundColorInt: Int, + override val appIconColorInt: Int, + override val textColorInt: Int + ) : Theme() + + data class Dark( + override val primaryColorInt: Int, + override val backgroundColorInt: Int, + override val appIconColorInt: Int, + override val textColorInt: Int + ) : Theme() + + data class BlackAndWhite( + val accentColor: Int, + override val primaryColorInt: Int, + override val backgroundColorInt: Int, + override val appIconColorInt: Int, + override val textColorInt: Int + ) : Theme() + + data class Custom( + override val primaryColorInt: Int, + override val backgroundColorInt: Int, + override val appIconColorInt: Int, + override val textColorInt: Int + ) : Theme() + + + companion object { + @Composable + fun systemDefaultMaterialYou() = SystemDefaultMaterialYou( + appIconColorInt = LocalContext.current.config.appIconColor, + primaryColorInt = LocalContext.current.config.primaryColor, + backgroundColorInt = LocalContext.current.config.backgroundColor, + textColorInt = (if (isInDarkThemeAndSurfaceIsNotLitWell()) Color.White else Color.Black).toArgb() + ) + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/dialogs/HistoryDialog.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/dialogs/HistoryDialog.kt index bba3242f..41121a95 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calculator/dialogs/HistoryDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/dialogs/HistoryDialog.kt @@ -4,6 +4,7 @@ import androidx.appcompat.app.AlertDialog import com.simplemobiletools.calculator.R import com.simplemobiletools.calculator.activities.SimpleActivity import com.simplemobiletools.calculator.adapters.HistoryAdapter +import com.simplemobiletools.calculator.databinding.DialogHistoryBinding import com.simplemobiletools.calculator.extensions.calculatorDB import com.simplemobiletools.calculator.helpers.CalculatorImpl import com.simplemobiletools.calculator.models.History @@ -11,28 +12,28 @@ import com.simplemobiletools.commons.extensions.getAlertDialogBuilder import com.simplemobiletools.commons.extensions.setupDialogStuff import com.simplemobiletools.commons.extensions.toast import com.simplemobiletools.commons.helpers.ensureBackgroundThread -import kotlinx.android.synthetic.main.dialog_history.view.* class HistoryDialog(activity: SimpleActivity, items: List, calculator: CalculatorImpl) { - var dialog: AlertDialog? = null + private var dialog: AlertDialog? = null init { - val view = activity.layoutInflater.inflate(R.layout.dialog_history, null) + + val view = DialogHistoryBinding.inflate(activity.layoutInflater, null, false) activity.getAlertDialogBuilder() - .setPositiveButton(R.string.ok, null) + .setPositiveButton(com.simplemobiletools.commons.R.string.ok, null) .setNeutralButton(R.string.clear_history) { _, _ -> ensureBackgroundThread { activity.applicationContext.calculatorDB.deleteHistory() activity.toast(R.string.history_cleared) } }.apply { - activity.setupDialogStuff(view, this, R.string.history) { alertDialog -> + activity.setupDialogStuff(view.root, this, R.string.history) { alertDialog -> dialog = alertDialog } } - view.history_list.adapter = HistoryAdapter(activity, items, calculator) { + view.historyList.adapter = HistoryAdapter(activity, items, calculator) { dialog?.dismiss() } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/extensions/Binding.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/extensions/Binding.kt new file mode 100644 index 00000000..98a4ea20 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/extensions/Binding.kt @@ -0,0 +1,10 @@ +package com.simplemobiletools.calculator.extensions + +import android.app.Activity +import android.view.LayoutInflater +import androidx.viewbinding.ViewBinding + +inline fun Activity.viewBinding(crossinline bindingInflater: (LayoutInflater) -> T) = + lazy(LazyThreadSafetyMode.NONE) { + bindingInflater.invoke(layoutInflater) + } diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/extensions/Context.kt index 04a6efdd..82ab729f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calculator/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/extensions/Context.kt @@ -4,13 +4,18 @@ import android.appwidget.AppWidgetManager import android.content.ComponentName import android.content.Context import android.content.Intent +import android.net.Uri +import android.os.Build +import android.provider.Settings import android.view.ViewGroup import android.widget.Button import android.widget.TextView +import androidx.annotation.RequiresApi import com.simplemobiletools.calculator.databases.CalculatorDatabase import com.simplemobiletools.calculator.helpers.Config import com.simplemobiletools.calculator.helpers.MyWidgetProvider import com.simplemobiletools.calculator.interfaces.CalculatorDao +import com.simplemobiletools.commons.extensions.showErrorToast val Context.config: Config get() = Config.newInstance(applicationContext) @@ -40,3 +45,22 @@ fun Context.updateWidgets() { } } } + +@RequiresApi(Build.VERSION_CODES.TIRAMISU) +fun Context.launchChangeAppLanguageIntent() { + try { + Intent(Settings.ACTION_APP_LOCALE_SETTINGS).apply { + data = Uri.fromParts("package", packageName, null) + startActivity(this) + } + } catch (e: Exception) { + try { + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + data = Uri.fromParts("package", packageName, null) + startActivity(this) + } + } catch (e: Exception) { + showErrorToast(e) + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/extensions/SharedPreferencesProducerExtensions.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/extensions/SharedPreferencesProducerExtensions.kt new file mode 100644 index 00000000..60b360fc --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/extensions/SharedPreferencesProducerExtensions.kt @@ -0,0 +1,18 @@ +package com.simplemobiletools.calculator.extensions + +import android.content.SharedPreferences +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.callbackFlow + +context (SharedPreferences) +fun sharedPreferencesCallback( + value: () -> T?, +) = callbackFlow { + val sharedPreferencesListener = + SharedPreferences.OnSharedPreferenceChangeListener { _, _ -> + trySend(value()) + } + + registerOnSharedPreferenceChangeListener(sharedPreferencesListener) + awaitClose { unregisterOnSharedPreferenceChangeListener(sharedPreferencesListener) } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/helpers/CalculatorImpl.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/helpers/CalculatorImpl.kt index 480978ea..0d71b41a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calculator/helpers/CalculatorImpl.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/helpers/CalculatorImpl.kt @@ -281,7 +281,7 @@ class CalculatorImpl( } if (result.isInfinite() || result.isNaN()) { - context.toast(R.string.unknown_error_occurred) + context.toast(com.simplemobiletools.commons.R.string.unknown_error_occurred) return } @@ -295,7 +295,7 @@ class CalculatorImpl( inputDisplayedFormula = result.format() baseValue = result } catch (e: Exception) { - context.toast(R.string.unknown_error_occurred) + context.toast(com.simplemobiletools.commons.R.string.unknown_error_occurred) } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calculator/helpers/Config.kt b/app/src/main/kotlin/com/simplemobiletools/calculator/helpers/Config.kt index a5e47c8b..a6a952ba 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calculator/helpers/Config.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calculator/helpers/Config.kt @@ -1,7 +1,10 @@ package com.simplemobiletools.calculator.helpers import android.content.Context +import com.simplemobiletools.calculator.extensions.sharedPreferencesCallback import com.simplemobiletools.commons.helpers.BaseConfig +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filterNotNull class Config(context: Context) : BaseConfig(context) { companion object { @@ -11,4 +14,10 @@ class Config(context: Context) : BaseConfig(context) { var useCommaAsDecimalMark: Boolean get() = prefs.getBoolean(USE_COMMA_AS_DECIMAL_MARK, getDecimalSeparator() == COMMA) set(useCommaAsDecimalMark) = prefs.edit().putBoolean(USE_COMMA_AS_DECIMAL_MARK, useCommaAsDecimalMark).apply() + + val preventPhoneFromSleepingFlow: Flow = prefs.run { sharedPreferencesCallback { preventPhoneFromSleeping } }.filterNotNull() + val vibrateOnButtonPressFlow: Flow = prefs.run { sharedPreferencesCallback { vibrateOnButtonPress } }.filterNotNull() + val wasUseEnglishToggledFlow: Flow = prefs.run { sharedPreferencesCallback { wasUseEnglishToggled } }.filterNotNull() + val useEnglishFlow: Flow = prefs.run { sharedPreferencesCallback { useEnglish } }.filterNotNull() + val useCommaAsDecimalMarkFlow: Flow = prefs.run { sharedPreferencesCallback { useCommaAsDecimalMark } }.filterNotNull() } diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml deleted file mode 100644 index 08503998..00000000 --- a/app/src/main/res/layout/activity_settings.xml +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 2d1ded29..00000000 --- a/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - google() - mavenCentral() - maven { url "https://jitpack.io" } - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..9a736eea --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,5 @@ +plugins { + alias(libs.plugins.android).apply(false) + alias(libs.plugins.kotlinAndroid).apply(false) + alias(libs.plugins.ksp).apply(false) +} diff --git a/gradle.properties b/gradle.properties index 915f0e66..ea724e7b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,20 +1,12 @@ -# 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. -# Default value: -Xmx10248m -XX:MaxPermSize=256m -# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -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 +org.gradle.jvmargs=-Xmx4g -XX:+UseGCOverheadLimit -XX:GCTimeLimit=10 -Dfile.encoding=UTF-8 -XX:+UseParallelGC +org.gradle.workers.max=8 +kotlin.code.style=official +android.useAndroidX=true android.enableJetifier=true -android.useAndroidX=true \ No newline at end of file +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.vfs.watch=true +org.gradle.configureondemand=true +org.gradle.unsafe.configuration-cache=true +org.gradle.unsafe.configuration-cache-problems=warn +android.nonTransitiveRClass=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..c361f7b9 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,104 @@ +[versions] +#jetbrains +kotlin = "1.9.0" +#KSP +ksp = "1.9.0-1.0.12" +#Androidx +androidx-customView = "1.2.0-alpha02" +androidx-customViewPooling = "1.0.0" +androidx-lifecycle = "2.6.1" +#Compose +composeActivity = "1.8.0-alpha06" +compose = "1.6.0-alpha01" +composeCompiler = "1.5.0" +composeMaterial3 = "1.2.0-alpha03" +accompanist = "0.31.5-beta" +#AutoFitTextView +autofittextview = "0.2.1" +#exp4j +exp4j = "0.4.8" +#Room +room = "2.5.2" +#Simple tools +simple-commons = "4c83ec8740" +#Gradle +gradlePlugins-agp = "8.0.2" +#build +app-build-compileSDKVersion = "34" +app-build-targetSDK = "33" +app-build-minimumSDK = "23" +app-build-javaVersion = "VERSION_17" +app-build-kotlinJVMTarget = "17" +#versioning +app-version-appId = "com.simplemobiletools.calculator" +app-version-versionCode = "60" +app-version-versionName = "5.11.3" + +[libraries] +#Android X +androidx-customView = { module = "androidx.customview:customview", version.ref = "androidx-customView" } +androidx-customViewPooling = { module = "androidx.customview:customview-poolingcontainer", version.ref = "androidx-customViewPooling" } +#Android X lifecycle +androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" } +androidx-lifecycle-viewModel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" } +androidx-lifecycle-viewModel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" } +androidx-lifecycle-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" } +#Room +androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" } +androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" } +androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } +#Compose +compose-compiler = { module = "androidx.compose.compiler:compiler", version.ref = "composeCompiler" } +compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" } +compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "composeMaterial3" } +compose-material2 = { module = "androidx.compose.material:material", version.ref = "compose" } +compose-material-icons = { module = "androidx.compose.material:material-icons-extended", version.ref = "compose" } +compose-animation = { module = "androidx.compose.animation:animation", version.ref = "compose" } +compose-activity = { module = "androidx.activity:activity-compose", version.ref = "composeActivity" } +compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" } +compose-runtime = { module = "androidx.compose.runtime:runtime", version.ref = "compose" } +compose-uiTooling-debug = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" } +compose-uiTooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" } +#UI +auto-fit-text-view = { module = "me.grantland:autofittextview", version.ref = "autofittextview" } +#Simple Mobile Tools +simple-tools-commons = { module = "com.github.SimpleMobileTools:Simple-Commons", version.ref = "simple-commons" } +#Exp4j +exp4j = { module = "net.objecthunter:exp4j", version.ref = "exp4j" } +#Accompanist +accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" } +[bundles] +compose = [ + "compose-activity", + "compose-animation", + "compose-compiler", + "compose-foundation", + "compose-material-icons", + "compose-material3", + "compose-runtime", + "compose-ui", + "compose-uiTooling-preview", +] +compose-preview = [ + "androidx-customView", + "androidx-customViewPooling", + "compose-uiTooling-debug", +] +room = [ + "androidx-room-ktx", + "androidx-room-runtime", +] +accompanist = [ + "accompanist-systemuicontroller", +] +lifecycle = [ + "androidx-lifecycle-compose", + "androidx-lifecycle-runtime", + "androidx-lifecycle-viewModel", + "androidx-lifecycle-viewModel-compose", +] +[plugins] +android = { id = "com.android.application", version.ref = "gradlePlugins-agp" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa8192b8..3ecd7f43 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index e7b4def4..00000000 --- a/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app' diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..45478cbe --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + maven { setUrl("https://jitpack.io") } + } +} +include(":app")