From f88fba44bbcb0406c63665d77179b28b5f7d1560 Mon Sep 17 00:00:00 2001 From: Sad Ellie Date: Sun, 7 Jan 2024 00:20:19 +0300 Subject: [PATCH] Refactor widget --- .../feature/glance/glance/IconButton.kt | 95 +++ .../feature/glance/glance/StringUtils.kt | 30 + .../glance/glance/UnittoCalculatorWidget.kt | 610 ++++++++++-------- .../glance/glance/UpdateInputAction.kt | 36 ++ .../feature/glance/glance/WidgetTheme.kt | 40 ++ 5 files changed, 527 insertions(+), 284 deletions(-) create mode 100644 feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/IconButton.kt create mode 100644 feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/StringUtils.kt create mode 100644 feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/WidgetTheme.kt diff --git a/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/IconButton.kt b/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/IconButton.kt new file mode 100644 index 00000000..81d1f12f --- /dev/null +++ b/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/IconButton.kt @@ -0,0 +1,95 @@ +/* + * Unitto is a unit converter for Android + * Copyright (c) 2024 Elshan Agaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sadellie.unitto.feature.glance.glance + +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.dp +import androidx.glance.ColorFilter +import androidx.glance.GlanceModifier +import androidx.glance.GlanceTheme +import androidx.glance.Image +import androidx.glance.ImageProvider +import androidx.glance.action.Action +import androidx.glance.action.clickable +import androidx.glance.appwidget.cornerRadius +import androidx.glance.background +import androidx.glance.color.ColorProviders +import androidx.glance.layout.Alignment +import androidx.glance.layout.Box +import androidx.glance.layout.fillMaxWidth +import androidx.glance.layout.padding +import androidx.glance.unit.ColorProvider + +@Composable +internal fun IconButton( + glanceModifier: GlanceModifier, + containerColor: ColorProvider, + @DrawableRes iconRes: Int, + contentColor: ColorProvider = GlanceTheme.colors.contentColorFor(containerColor), + onClickKey: String = iconRes.toString(), + onClick: Action, +) { + Box( + modifier = glanceModifier + .padding(4.dp), + contentAlignment = Alignment.Center + ) { + Image( + modifier = GlanceModifier + .fillMaxWidth() + .clickable(onClick) + .cornerRadius(100.dp) + .background(containerColor) + .padding(horizontal = 16.dp, vertical = 8.dp), + provider = ImageProvider(iconRes), + contentDescription = null, + colorFilter = ColorFilter.tint(contentColor) + ) + } +} + +private fun ColorProviders.contentColorFor(backgroundColor: ColorProvider): ColorProvider = + when (backgroundColor) { + primary -> onPrimary + primaryContainer -> onPrimaryContainer + inverseOnSurface -> onSurfaceVariant + tertiaryContainer -> onTertiaryContainer + else -> onBackground + } + +// https://gist.github.com/rozPierog/1145af6e1f10c9199000828ab4bd6bad +// Kinda works, but corners parameter needs to be split +//@SuppressLint("RestrictedApi") +//fun GlanceModifier.cornerRadiusCompat( +// cornerRadius: Int, +// @ColorInt color: Int, +// @FloatRange(from = 0.0, to = 1.0) backgroundAlpha: Float = 1f, +//): GlanceModifier { +// return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { +// this.background(Color(color).copy(alpha = backgroundAlpha)) +// .cornerRadius(cornerRadius.dp) +// } else { +// val radii = FloatArray(8) { cornerRadius.toFloat() } +// val shape = ShapeDrawable(RoundRectShape(radii, null, null)) +// shape.paint.color = ColorUtils.setAlphaComponent(color, (255 * backgroundAlpha).toInt()) +// val bitmap = shape.toBitmap(width = 150, height = 75) +// this.background(BitmapImageProvider(bitmap)) +// } +//} diff --git a/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/StringUtils.kt b/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/StringUtils.kt new file mode 100644 index 00000000..57093cf5 --- /dev/null +++ b/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/StringUtils.kt @@ -0,0 +1,30 @@ +/* + * Unitto is a unit converter for Android + * Copyright (c) 2024 Elshan Agaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sadellie.unitto.feature.glance.glance + +import androidx.compose.ui.text.TextRange +import androidx.compose.ui.text.input.TextFieldValue +import com.sadellie.unitto.core.ui.common.textfield.addBracket +import com.sadellie.unitto.core.ui.common.textfield.addTokens + +internal fun String.addToken(token: String): String = + TextFieldValue(this, TextRange(length)).addTokens(token).text + +internal fun String.addBracket(): String = + TextFieldValue(this, TextRange(length)).addBracket().text diff --git a/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/UnittoCalculatorWidget.kt b/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/UnittoCalculatorWidget.kt index 134e95ea..91ad39c6 100644 --- a/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/UnittoCalculatorWidget.kt +++ b/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/UnittoCalculatorWidget.kt @@ -18,60 +18,42 @@ package com.sadellie.unitto.feature.glance.glance -import android.content.ComponentName import android.content.Context -import android.os.Build -import androidx.annotation.DrawableRes import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState -import androidx.compose.ui.text.TextRange -import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.stringPreferencesKey -import androidx.glance.ColorFilter import androidx.glance.GlanceId import androidx.glance.GlanceModifier import androidx.glance.GlanceTheme -import androidx.glance.Image -import androidx.glance.ImageProvider import androidx.glance.LocalContext import androidx.glance.action.Action import androidx.glance.action.ActionParameters -import androidx.glance.action.actionParametersOf -import androidx.glance.action.actionStartActivity -import androidx.glance.action.clickable import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.action.actionRunCallback import androidx.glance.appwidget.appWidgetBackground -import androidx.glance.appwidget.cornerRadius import androidx.glance.appwidget.provideContent import androidx.glance.background -import androidx.glance.color.ColorProviders import androidx.glance.currentState import androidx.glance.layout.Alignment import androidx.glance.layout.Box import androidx.glance.layout.Column -import androidx.glance.layout.ColumnScope import androidx.glance.layout.Row import androidx.glance.layout.fillMaxSize import androidx.glance.layout.fillMaxWidth import androidx.glance.layout.padding -import androidx.glance.material3.ColorProviders import androidx.glance.state.GlanceStateDefinition import androidx.glance.state.PreferencesGlanceStateDefinition import androidx.glance.text.Text import androidx.glance.text.TextAlign import androidx.glance.text.TextStyle -import androidx.glance.unit.ColorProvider import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols -import com.sadellie.unitto.core.ui.common.textfield.addBracket -import com.sadellie.unitto.core.ui.common.textfield.addTokens +import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.ui.common.textfield.formatExpression -import com.sadellie.unitto.core.ui.theme.DarkThemeColors -import com.sadellie.unitto.core.ui.theme.LightThemeColors import com.sadellie.unitto.data.model.repository.UserPreferencesRepository import com.sadellie.unitto.data.model.userprefs.CalculatorPreferences import com.sadellie.unitto.feature.glance.R @@ -102,278 +84,338 @@ class UnittoCalculatorWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { - val userPrefsRepository = EntryPoints.get(context, UserPrefEntryPoint::class.java).userPrefRep() + val userPrefsRepository = + EntryPoints.get(context, UserPrefEntryPoint::class.java).userPrefRep() provideContent { val appPrefs = userPrefsRepository.calculatorPrefs.collectAsState(initial = null).value WidgetTheme { - if (appPrefs == null) { - LoadingUI() - return@WidgetTheme - } - - ReadyUI(appPrefs) + if (appPrefs == null) LoadingUI() else ReadyUI(appPrefs) } } } - - @Composable - private fun WidgetTheme(content: @Composable () -> Unit) = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - GlanceTheme { - content() - } - } else { - GlanceTheme( - colors = ColorProviders(light = LightThemeColors, dark = DarkThemeColors) - ) { - content() - } - } - - @Composable - private fun ReadyUI( - appPrefs: CalculatorPreferences, - ) { - val glancePrefs = currentState() - val input = glancePrefs[inputPrefKey] ?: "" - val output = glancePrefs[outputPrefKey] ?: "" - val formatterSymbols = AllFormatterSymbols.getById(appPrefs.separator) - - fun runCalculateAction(input: String): Action = actionRunCallback( - actionParametersOf( - inputKey to input, - precisionKey to appPrefs.precision, - outputFormatKey to appPrefs.outputFormat - ) - ) - - fun runCopyAction(): Action = actionRunCallback( - actionParametersOf( - outputKey to output.replace(Token.Digit.dot, formatterSymbols.fractional) - ) - ) - - Column( - modifier = GlanceModifier - .appWidgetBackground() - .background(GlanceTheme.colors.background) - .fillMaxSize() - ) { - Column( - modifier = GlanceModifier - .background(GlanceTheme.colors.surfaceVariant) - ) { - Row(modifier = GlanceModifier.fillMaxWidth().padding(8.dp)) { - val boxModifier = GlanceModifier.fillMaxWidth().defaultWeight() - - GlanceKeyboardButton(boxModifier, GlanceTheme.colors.primary, R.drawable.content_copy, onClick = runCopyAction()) - GlanceKeyboardButton(boxModifier, GlanceTheme.colors.primary, R.drawable.open_in_new, onClick = actionStartActivity(ComponentName(LocalContext.current, "com.sadellie.unitto.MainActivity"))) - } - - Text( - text = input.formatExpression(formatterSymbols), - modifier = GlanceModifier.fillMaxWidth(), - maxLines = 2, - style = TextStyle( - fontSize = 36.sp, - textAlign = TextAlign.End, - color = GlanceTheme.colors.onSurfaceVariant - ), - ) - Text( - text = output.formatExpression(formatterSymbols), - modifier = GlanceModifier.fillMaxWidth(), - maxLines = 1, - style = TextStyle( - fontSize = 36.sp, - textAlign = TextAlign.End, - color = GlanceTheme.colors.onSurfaceVariant - ), - ) - } - - Column( - modifier = GlanceModifier - .padding(8.dp) - ) { - GlanceKeyboard( - addTokenAction = { - runCalculateAction(input.addToken(it)) - }, - replaceInputAction = { - runCalculateAction(it) - }, - addBracketAction = { - runCalculateAction(input.addBracket()) - }, - deleteTokenAction = { - runCalculateAction(input.dropLast(1)) - }, - equalAction = equal@{ - if (input.isEmpty()) return@equal actionRunCallback() - - runCalculateAction(output) - }, - useDot = formatterSymbols.fractional == Token.Digit.dot, - middleZero = appPrefs.middleZero - ) - } - } - } - - @Composable - private fun LoadingUI() { - Box( - modifier = GlanceModifier - .appWidgetBackground() - .background(GlanceTheme.colors.background) - .fillMaxSize(), - contentAlignment = Alignment.Center - ) { - GlanceKeyboardButton( - glanceModifier = GlanceModifier, - containerColor = GlanceTheme.colors.primary, - iconRes = R.drawable.refresh, - onClick = actionRunCallback(), - ) - } - } - - @Composable - private fun ColumnScope.GlanceKeyboard( - addTokenAction: (String) -> Action, - replaceInputAction: (String) -> Action, - addBracketAction: () -> Action, - deleteTokenAction: () -> Action, - equalAction: () -> Action, - useDot: Boolean, - middleZero: Boolean, - ) { - val rowModifier = GlanceModifier.defaultWeight().fillMaxWidth() - - Row( - modifier = rowModifier - ) { - val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() - - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.tertiaryContainer, R.drawable.clear, onClick = replaceInputAction("")) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.brackets, onClick = addBracketAction()) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.percent, onClick = addTokenAction(Token.Operator.percent)) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.divide, onClick = addTokenAction(Token.Operator.divide)) - } - - Row( - modifier = rowModifier - ) { - val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() - - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key7, onClick = addTokenAction(Token.Digit._7)) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key8, onClick = addTokenAction(Token.Digit._8)) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key9, onClick = addTokenAction(Token.Digit._9)) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.multiply, onClick = addTokenAction(Token.Operator.multiply)) - } - - Row( - modifier = rowModifier - ) { - val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() - - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key4, onClick = addTokenAction(Token.Digit._4)) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key5, onClick = addTokenAction(Token.Digit._5)) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key6, onClick = addTokenAction(Token.Digit._6)) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.minus, onClick = addTokenAction(Token.Operator.minus)) - } - - Row( - modifier = rowModifier - ) { - val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() - - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key1, onClick = addTokenAction(Token.Digit._1)) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key2, onClick = addTokenAction(Token.Digit._2)) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key3, onClick = addTokenAction(Token.Digit._3)) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.plus, onClick = addTokenAction(Token.Operator.plus)) - } - - Row( - modifier = rowModifier - ) { - val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() - - if (middleZero) { - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, if (useDot) R.drawable.dot else R.drawable.comma, onClick = addTokenAction(Token.Digit.dot)) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key0, onClick = addTokenAction(Token.Digit._0)) - } else { - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key0, onClick = addTokenAction(Token.Digit._0)) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, if (useDot) R.drawable.dot else R.drawable.comma, onClick = addTokenAction(Token.Digit.dot)) - } - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.backspace, onClick = deleteTokenAction()) - GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.equal, onClick = equalAction()) - } - } - - @Composable - private fun GlanceKeyboardButton( - glanceModifier: GlanceModifier, - containerColor: ColorProvider, - @DrawableRes iconRes: Int, - contentColor: ColorProvider = GlanceTheme.colors.contentColorFor(containerColor), - onClickKey: String = iconRes.toString(), - onClick: Action, - ) { - Box( - modifier = glanceModifier - .padding(4.dp), - contentAlignment = Alignment.Center - ) { - Image( - modifier = GlanceModifier - .fillMaxWidth() - .clickable(onClick) - .cornerRadius(100.dp) - .background(containerColor) - .padding(horizontal = 16.dp, vertical = 8.dp), - provider = ImageProvider(iconRes), - contentDescription = null, - colorFilter = ColorFilter.tint(contentColor) - ) - } - } } -// https://gist.github.com/rozPierog/1145af6e1f10c9199000828ab4bd6bad -// Kinda works, but corners parameter needs to be split -//@SuppressLint("RestrictedApi") -//fun GlanceModifier.cornerRadiusCompat( -// cornerRadius: Int, -// @ColorInt color: Int, -// @FloatRange(from = 0.0, to = 1.0) backgroundAlpha: Float = 1f, -//): GlanceModifier { -// return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { -// this.background(Color(color).copy(alpha = backgroundAlpha)) -// .cornerRadius(cornerRadius.dp) -// } else { -// val radii = FloatArray(8) { cornerRadius.toFloat() } -// val shape = ShapeDrawable(RoundRectShape(radii, null, null)) -// shape.paint.color = ColorUtils.setAlphaComponent(color, (255 * backgroundAlpha).toInt()) -// val bitmap = shape.toBitmap(width = 150, height = 75) -// this.background(BitmapImageProvider(bitmap)) -// } -//} - -private fun String.addToken(token: String): String = - TextFieldValue(this, TextRange(length)).addTokens(token).text - -private fun String.addBracket(): String = - TextFieldValue(this, TextRange(length)).addBracket().text - -private fun ColorProviders.contentColorFor(backgroundColor: ColorProvider): ColorProvider = - when (backgroundColor) { - primary -> onPrimary - primaryContainer -> onPrimaryContainer - inverseOnSurface -> onSurfaceVariant - tertiaryContainer -> onTertiaryContainer - else -> onBackground +@Composable +private fun LoadingUI() { + Box( + modifier = GlanceModifier + .appWidgetBackground() + .background(GlanceTheme.colors.background) + .fillMaxSize(), + contentAlignment = Alignment.Center + ) { + IconButton( + glanceModifier = GlanceModifier, + containerColor = GlanceTheme.colors.primary, + iconRes = R.drawable.refresh, + onClick = actionRunCallback(), + ) } +} + +@Composable +private fun ReadyUI( + appPrefs: CalculatorPreferences, +) { + val glancePrefs = currentState() + val input = glancePrefs[UnittoCalculatorWidget.inputPrefKey] ?: "" + val output = glancePrefs[UnittoCalculatorWidget.outputPrefKey] ?: "" + val formatterSymbols = AllFormatterSymbols.getById(appPrefs.separator) + + fun runCalculateAction(input: String): Action = updateInputAction( + input = input, + precision = appPrefs.precision, + outputFormat = appPrefs.outputFormat + ) + + Column( + modifier = GlanceModifier + .appWidgetBackground() + .background(GlanceTheme.colors.background) + .fillMaxSize() + ) { + Column( + modifier = GlanceModifier + .background(GlanceTheme.colors.surfaceVariant) + ) { + ActionButtons( + modifier = GlanceModifier.fillMaxWidth().padding(8.dp), + onCopyClick = copyAction( + output = output, + fractional = formatterSymbols.fractional + ), + onLaunchClick = launchAction(LocalContext.current) + ) + TextField( + modifier = GlanceModifier.fillMaxWidth(), + input = input, + formatterSymbols = formatterSymbols, + fontSize = 36.sp, + maxLines = 2 + ) + TextField( + modifier = GlanceModifier.fillMaxWidth(), + input = output, + formatterSymbols = formatterSymbols, + fontSize = 28.sp, + maxLines = 1 + ) + } + + GlanceKeyboard( + modifier = GlanceModifier + .padding(8.dp), + addTokenAction = { + runCalculateAction(input.addToken(it)) + }, + replaceInputAction = { + runCalculateAction(it) + }, + addBracketAction = { + runCalculateAction(input.addBracket()) + }, + deleteTokenAction = { + runCalculateAction(input.dropLast(1)) + }, + equalAction = equal@{ + if (output.isEmpty()) return@equal actionRunCallback() + + runCalculateAction(output) + }, + useDot = formatterSymbols.fractional == Token.Digit.dot, + middleZero = appPrefs.middleZero + ) + } +} + +@Composable +private fun ActionButtons( + modifier: GlanceModifier, + onCopyClick: Action, + onLaunchClick: Action, +) { + Row( + modifier = modifier + ) { + val boxModifier = GlanceModifier.fillMaxWidth().defaultWeight() + + IconButton( + glanceModifier = boxModifier, + containerColor = GlanceTheme.colors.primary, + iconRes = R.drawable.content_copy, + onClick = onCopyClick + ) + IconButton( + glanceModifier = boxModifier, + containerColor = GlanceTheme.colors.primary, + iconRes = R.drawable.open_in_new, + onClick = onLaunchClick + ) + } +} + +@Composable +private fun TextField( + modifier: GlanceModifier, + input: String, + formatterSymbols: FormatterSymbols, + fontSize: TextUnit, + maxLines: Int, +) { + Text( + text = input.formatExpression(formatterSymbols), + modifier = modifier, + maxLines = maxLines, + style = TextStyle( + fontSize = fontSize, + textAlign = TextAlign.End, + color = GlanceTheme.colors.onSurfaceVariant + ) + ) +} + +@Composable +private fun GlanceKeyboard( + modifier: GlanceModifier, + addTokenAction: (String) -> Action, + replaceInputAction: (String) -> Action, + addBracketAction: () -> Action, + deleteTokenAction: () -> Action, + equalAction: () -> Action, + useDot: Boolean, + middleZero: Boolean, +) = Column(modifier = modifier) { + val rowModifier = GlanceModifier.defaultWeight().fillMaxWidth() + + Row( + modifier = rowModifier + ) { + val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() + + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.tertiaryContainer, + iconRes = R.drawable.clear, + onClick = replaceInputAction("") + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.primaryContainer, + iconRes = R.drawable.brackets, + onClick = addBracketAction() + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.primaryContainer, + iconRes = R.drawable.percent, + onClick = addTokenAction(Token.Operator.percent) + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.primaryContainer, + iconRes = R.drawable.divide, + onClick = addTokenAction(Token.Operator.divide) + ) + } + + Row( + modifier = rowModifier + ) { + val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() + + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = R.drawable.key7, + onClick = addTokenAction(Token.Digit._7) + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = R.drawable.key8, + onClick = addTokenAction(Token.Digit._8) + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = R.drawable.key9, + onClick = addTokenAction(Token.Digit._9) + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.primaryContainer, + iconRes = R.drawable.multiply, + onClick = addTokenAction(Token.Operator.multiply) + ) + } + + Row( + modifier = rowModifier + ) { + val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() + + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = R.drawable.key4, + onClick = addTokenAction(Token.Digit._4) + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = R.drawable.key5, + onClick = addTokenAction(Token.Digit._5) + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = R.drawable.key6, + onClick = addTokenAction(Token.Digit._6) + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.primaryContainer, + iconRes = R.drawable.minus, + onClick = addTokenAction(Token.Operator.minus) + ) + } + + Row( + modifier = rowModifier + ) { + val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() + + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = R.drawable.key1, + onClick = addTokenAction(Token.Digit._1) + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = R.drawable.key2, + onClick = addTokenAction(Token.Digit._2) + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = R.drawable.key3, + onClick = addTokenAction(Token.Digit._3) + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.primaryContainer, + iconRes = R.drawable.plus, + onClick = addTokenAction(Token.Operator.plus) + ) + } + + Row( + modifier = rowModifier + ) { + val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() + + if (middleZero) { + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = if (useDot) R.drawable.dot else R.drawable.comma, + onClick = addTokenAction(Token.Digit.dot) + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = R.drawable.key0, + onClick = addTokenAction(Token.Digit._0) + ) + } else { + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = R.drawable.key0, + onClick = addTokenAction(Token.Digit._0) + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = if (useDot) R.drawable.dot else R.drawable.comma, + onClick = addTokenAction(Token.Digit.dot) + ) + } + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.inverseOnSurface, + iconRes = R.drawable.backspace, + onClick = deleteTokenAction() + ) + IconButton( + glanceModifier = buttonModifier, + containerColor = GlanceTheme.colors.primaryContainer, + iconRes = R.drawable.equal, + onClick = equalAction() + ) + } +} diff --git a/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/UpdateInputAction.kt b/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/UpdateInputAction.kt index f9eef5ad..ded252b8 100644 --- a/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/UpdateInputAction.kt +++ b/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/UpdateInputAction.kt @@ -20,11 +20,17 @@ package com.sadellie.unitto.feature.glance.glance import android.content.ClipData import android.content.ClipboardManager +import android.content.ComponentName import android.content.Context import androidx.glance.GlanceId +import androidx.glance.action.Action import androidx.glance.action.ActionParameters +import androidx.glance.action.actionParametersOf +import androidx.glance.action.actionStartActivity import androidx.glance.appwidget.action.ActionCallback +import androidx.glance.appwidget.action.actionRunCallback import androidx.glance.appwidget.state.updateAppWidgetState +import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.data.common.format import com.sadellie.unitto.data.common.isExpression import io.github.sadellie.evaluatto.Expression @@ -87,6 +93,36 @@ internal class RestartWidget : ActionCallback { } } +internal fun updateInputAction( + input: String, + precision: Int, + outputFormat: Int +): Action = actionRunCallback( + actionParametersOf( + UnittoCalculatorWidget.inputKey to input, + UnittoCalculatorWidget.precisionKey to precision, + UnittoCalculatorWidget.outputFormatKey to outputFormat + ) +) + +internal fun copyAction( + output: String, + fractional: String +): Action = actionRunCallback( + actionParametersOf( + UnittoCalculatorWidget.outputKey to output.replace(Token.Digit.dot, fractional) + ) +) + +internal fun launchAction( + mContext: Context +): Action = actionStartActivity( + ComponentName( + mContext, + "com.sadellie.unitto.MainActivity" + ) +) + private fun calculate( input: String, precision: Int, diff --git a/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/WidgetTheme.kt b/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/WidgetTheme.kt new file mode 100644 index 00000000..358a5cd2 --- /dev/null +++ b/feature/glance/src/main/java/com/sadellie/unitto/feature/glance/glance/WidgetTheme.kt @@ -0,0 +1,40 @@ +/* + * Unitto is a unit converter for Android + * Copyright (c) 2024 Elshan Agaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sadellie.unitto.feature.glance.glance + +import android.os.Build +import androidx.compose.runtime.Composable +import androidx.glance.GlanceTheme +import androidx.glance.material3.ColorProviders +import com.sadellie.unitto.core.ui.theme.DarkThemeColors +import com.sadellie.unitto.core.ui.theme.LightThemeColors + +@Composable +internal fun WidgetTheme(content: @Composable () -> Unit) = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + GlanceTheme { + content() + } + } else { + GlanceTheme( + colors = ColorProviders(light = LightThemeColors, dark = DarkThemeColors) + ) { + content() + } + }