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