Merge pull request #310 from FunkyMuse/master

feat: view binding and compose
This commit is contained in:
Tibor Kaputa 2023-07-25 13:55:49 +02:00 committed by GitHub
commit 8bb7a40641
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 2132 additions and 492 deletions

View File

@ -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'
}

113
app/build.gradle.kts Normal file
View File

@ -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<KotlinCompile> {
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)
}

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.simplemobiletools.calculator"
android:installLocation="auto">
<uses-permission

View File

@ -9,6 +9,7 @@ import androidx.core.content.res.ResourcesCompat
import com.simplemobiletools.calculator.BuildConfig
import com.simplemobiletools.calculator.R
import com.simplemobiletools.calculator.databases.CalculatorDatabase
import com.simplemobiletools.calculator.databinding.ActivityMainBinding
import com.simplemobiletools.calculator.dialogs.HistoryDialog
import com.simplemobiletools.calculator.extensions.config
import com.simplemobiletools.calculator.extensions.updateViewColors
@ -20,7 +21,6 @@ import com.simplemobiletools.commons.helpers.LOWER_ALPHA_INT
import com.simplemobiletools.commons.helpers.MEDIUM_ALPHA_INT
import com.simplemobiletools.commons.models.FAQItem
import com.simplemobiletools.commons.models.Release
import kotlinx.android.synthetic.main.activity_main.*
import me.grantland.widget.AutofitHelper
class MainActivity : SimpleActivity(), Calculator {
@ -32,48 +32,50 @@ class MainActivity : SimpleActivity(), Calculator {
private var saveCalculatorState: String = ""
private lateinit var calc: CalculatorImpl
private val binding by lazy(LazyThreadSafetyMode.NONE) { ActivityMainBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
isMaterialActivity = true
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setContentView(binding.root)
appLaunched(BuildConfig.APPLICATION_ID)
setupOptionsMenu()
refreshMenuItems()
updateMaterialActivityViews(main_coordinator, null, useTransparentNavigation = false, useTopSearchMenu = false)
setupMaterialScrollListener(main_nested_scrollview, main_toolbar)
updateMaterialActivityViews(binding.mainCoordinator, null, useTransparentNavigation = false, useTopSearchMenu = false)
setupMaterialScrollListener(binding.mainNestedScrollview, binding.mainToolbar)
if (savedInstanceState != null) {
saveCalculatorState = savedInstanceState.getCharSequence(CALCULATOR_STATE) as String
}
calc = CalculatorImpl(this, applicationContext, decimalSeparator, groupingSeparator, saveCalculatorState)
btn_plus.setOnClickOperation(PLUS)
btn_minus.setOnClickOperation(MINUS)
btn_multiply.setOnClickOperation(MULTIPLY)
btn_divide.setOnClickOperation(DIVIDE)
btn_percent.setOnClickOperation(PERCENT)
btn_power.setOnClickOperation(POWER)
btn_root.setOnClickOperation(ROOT)
btn_minus.setOnLongClickListener { calc.turnToNegative() }
btn_clear.setVibratingOnClickListener { calc.handleClear() }
btn_clear.setOnLongClickListener {
binding.btnPlus?.setOnClickOperation(PLUS)
binding.btnMinus?.setOnClickOperation(MINUS)
binding.btnMultiply?.setOnClickOperation(MULTIPLY)
binding.btnDivide?.setOnClickOperation(DIVIDE)
binding.btnPercent?.setOnClickOperation(PERCENT)
binding.btnPower?.setOnClickOperation(POWER)
binding.btnRoot?.setOnClickOperation(ROOT)
binding.btnMinus?.setOnLongClickListener { calc.turnToNegative() }
binding.btnClear?.setVibratingOnClickListener { calc.handleClear() }
binding.btnClear?.setOnLongClickListener {
calc.handleReset()
true
}
getButtonIds().forEach {
it.setVibratingOnClickListener { view ->
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<Release>().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) {

View File

@ -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)
}
}
}

View File

@ -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<TextView>(it)).setTextColor(mTextColor)

View File

@ -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<History>, val calc: CalculatorImpl, val itemClick: () -> Unit) :
RecyclerView.Adapter<HistoryAdapter.ViewHolder>() {
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<History>, 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)

View File

@ -0,0 +1,8 @@
package com.simplemobiletools.calculator.compose.extensions
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
class BooleanPreviewParameterProvider : PreviewParameterProvider<Boolean> {
override val values: Sequence<Boolean>
get() = sequenceOf(false, true)
}

View File

@ -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 <T : Any> 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
}

View File

@ -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)
}
}
}

View File

@ -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,
)
}

View File

@ -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

View File

@ -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
)
}
}

View File

@ -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",
)
}
}

View File

@ -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")
}
}
}
}

View File

@ -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,
)
}
}

View File

@ -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")
}

View File

@ -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<Int> = 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<Int> = getActivity().getAppIconIds()
private fun Context.getAppLauncherName(): String = getActivity().getAppLauncherName()

View File

@ -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()
}
}
}

View File

@ -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,
)

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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,
)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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()
)
}
}

View File

@ -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<History>, 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()
}
}

View File

@ -0,0 +1,10 @@
package com.simplemobiletools.calculator.extensions
import android.app.Activity
import android.view.LayoutInflater
import androidx.viewbinding.ViewBinding
inline fun <T : ViewBinding> Activity.viewBinding(crossinline bindingInflater: (LayoutInflater) -> T) =
lazy(LazyThreadSafetyMode.NONE) {
bindingInflater.invoke(layoutInflater)
}

View File

@ -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)
}
}
}

View File

@ -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 <T> sharedPreferencesCallback(
value: () -> T?,
) = callbackFlow {
val sharedPreferencesListener =
SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
trySend(value())
}
registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
awaitClose { unregisterOnSharedPreferenceChangeListener(sharedPreferencesListener) }
}

View File

@ -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)
}
}
}

View File

@ -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<Boolean> = prefs.run { sharedPreferencesCallback { preventPhoneFromSleeping } }.filterNotNull()
val vibrateOnButtonPressFlow: Flow<Boolean> = prefs.run { sharedPreferencesCallback { vibrateOnButtonPress } }.filterNotNull()
val wasUseEnglishToggledFlow: Flow<Boolean> = prefs.run { sharedPreferencesCallback { wasUseEnglishToggled } }.filterNotNull()
val useEnglishFlow: Flow<Boolean> = prefs.run { sharedPreferencesCallback { useEnglish } }.filterNotNull()
val useCommaAsDecimalMarkFlow: Flow<Boolean> = prefs.run { sharedPreferencesCallback { useCommaAsDecimalMark } }.filterNotNull()
}

View File

@ -1,182 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/settings_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/settings_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/color_primary"
app:title="@string/settings"
app:titleTextAppearance="@style/AppTheme.ActionBar.TitleTextStyle" />
<androidx.core.widget.NestedScrollView
android:id="@+id/settings_nested_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:fillViewport="true"
android:scrollbars="none">
<LinearLayout
android:id="@+id/settings_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/settings_color_customization_section_label"
style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/color_customization" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/settings_color_customization_holder"
style="@style/SettingsHolderTextViewOneLinerStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_color_customization_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/customize_colors"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/settings_widget_color_customization_holder"
style="@style/SettingsHolderTextViewOneLinerStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_widget_color_customization_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/customize_widget_colors"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<include
android:id="@+id/settings_color_customization_divider"
layout="@layout/divider" />
<TextView
android:id="@+id/settings_general_settings_label"
style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/general_settings" />
<RelativeLayout
android:id="@+id/settings_purchase_thank_you_holder"
style="@style/SettingsHolderTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_purchase_thank_you"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/purchase_simple_thank_you" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_use_english_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_use_english"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/use_english_language" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_language_holder"
style="@style/SettingsHolderTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_language_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/language" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_language"
style="@style/SettingsTextValueStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/settings_language_label"
tools:text="English" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_vibrate_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_vibrate"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/vibrate_on_button_press" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_prevent_phone_from_sleeping_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_prevent_phone_from_sleeping"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/prevent_phone_from_sleeping" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_use_comma_as_decimal_mark_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_use_comma_as_decimal_mark"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/use_comma_as_decimal_mark" />
</RelativeLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -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
}

5
build.gradle.kts Normal file
View File

@ -0,0 +1,5 @@
plugins {
alias(libs.plugins.android).apply(false)
alias(libs.plugins.kotlinAndroid).apply(false)
alias(libs.plugins.ksp).apply(false)
}

View File

@ -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
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

104
gradle/libs.versions.toml Normal file
View File

@ -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" }

View File

@ -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

View File

@ -1 +0,0 @@
include ':app'

16
settings.gradle.kts Normal file
View File

@ -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")