Refactor widget

This commit is contained in:
Sad Ellie 2024-01-07 00:20:19 +03:00
parent 3638af2e45
commit f88fba44bb
5 changed files with 527 additions and 284 deletions

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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))
// }
//}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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

View File

@ -18,60 +18,42 @@
package com.sadellie.unitto.feature.glance.glance package com.sadellie.unitto.feature.glance.glance
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.os.Build
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.ui.text.TextRange import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.glance.ColorFilter
import androidx.glance.GlanceId import androidx.glance.GlanceId
import androidx.glance.GlanceModifier import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme import androidx.glance.GlanceTheme
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.LocalContext import androidx.glance.LocalContext
import androidx.glance.action.Action import androidx.glance.action.Action
import androidx.glance.action.ActionParameters 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.GlanceAppWidget
import androidx.glance.appwidget.action.actionRunCallback import androidx.glance.appwidget.action.actionRunCallback
import androidx.glance.appwidget.appWidgetBackground import androidx.glance.appwidget.appWidgetBackground
import androidx.glance.appwidget.cornerRadius
import androidx.glance.appwidget.provideContent import androidx.glance.appwidget.provideContent
import androidx.glance.background import androidx.glance.background
import androidx.glance.color.ColorProviders
import androidx.glance.currentState import androidx.glance.currentState
import androidx.glance.layout.Alignment import androidx.glance.layout.Alignment
import androidx.glance.layout.Box import androidx.glance.layout.Box
import androidx.glance.layout.Column import androidx.glance.layout.Column
import androidx.glance.layout.ColumnScope
import androidx.glance.layout.Row import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxSize import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding import androidx.glance.layout.padding
import androidx.glance.material3.ColorProviders
import androidx.glance.state.GlanceStateDefinition import androidx.glance.state.GlanceStateDefinition
import androidx.glance.state.PreferencesGlanceStateDefinition import androidx.glance.state.PreferencesGlanceStateDefinition
import androidx.glance.text.Text import androidx.glance.text.Text
import androidx.glance.text.TextAlign import androidx.glance.text.TextAlign
import androidx.glance.text.TextStyle import androidx.glance.text.TextStyle
import androidx.glance.unit.ColorProvider
import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols 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.FormatterSymbols
import com.sadellie.unitto.core.ui.common.textfield.addTokens
import com.sadellie.unitto.core.ui.common.textfield.formatExpression 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.repository.UserPreferencesRepository
import com.sadellie.unitto.data.model.userprefs.CalculatorPreferences import com.sadellie.unitto.data.model.userprefs.CalculatorPreferences
import com.sadellie.unitto.feature.glance.R import com.sadellie.unitto.feature.glance.R
@ -102,57 +84,50 @@ class UnittoCalculatorWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) { 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 { provideContent {
val appPrefs = userPrefsRepository.calculatorPrefs.collectAsState(initial = null).value val appPrefs = userPrefsRepository.calculatorPrefs.collectAsState(initial = null).value
WidgetTheme { WidgetTheme {
if (appPrefs == null) { if (appPrefs == null) LoadingUI() else ReadyUI(appPrefs)
LoadingUI()
return@WidgetTheme
} }
}
}
}
ReadyUI(appPrefs) @Composable
} private fun LoadingUI() {
} Box(
} modifier = GlanceModifier
.appWidgetBackground()
@Composable .background(GlanceTheme.colors.background)
private fun WidgetTheme(content: @Composable () -> Unit) = .fillMaxSize(),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { contentAlignment = Alignment.Center
GlanceTheme {
content()
}
} else {
GlanceTheme(
colors = ColorProviders(light = LightThemeColors, dark = DarkThemeColors)
) { ) {
content() IconButton(
} glanceModifier = GlanceModifier,
containerColor = GlanceTheme.colors.primary,
iconRes = R.drawable.refresh,
onClick = actionRunCallback<RestartWidget>(),
)
} }
}
@Composable @Composable
private fun ReadyUI( private fun ReadyUI(
appPrefs: CalculatorPreferences, appPrefs: CalculatorPreferences,
) { ) {
val glancePrefs = currentState<Preferences>() val glancePrefs = currentState<Preferences>()
val input = glancePrefs[inputPrefKey] ?: "" val input = glancePrefs[UnittoCalculatorWidget.inputPrefKey] ?: ""
val output = glancePrefs[outputPrefKey] ?: "" val output = glancePrefs[UnittoCalculatorWidget.outputPrefKey] ?: ""
val formatterSymbols = AllFormatterSymbols.getById(appPrefs.separator) val formatterSymbols = AllFormatterSymbols.getById(appPrefs.separator)
fun runCalculateAction(input: String): Action = actionRunCallback<UpdateInputAction>( fun runCalculateAction(input: String): Action = updateInputAction(
actionParametersOf( input = input,
inputKey to input, precision = appPrefs.precision,
precisionKey to appPrefs.precision, outputFormat = appPrefs.outputFormat
outputFormatKey to appPrefs.outputFormat
)
)
fun runCopyAction(): Action = actionRunCallback<CopyResultAction>(
actionParametersOf(
outputKey to output.replace(Token.Digit.dot, formatterSymbols.fractional)
)
) )
Column( Column(
@ -165,40 +140,33 @@ class UnittoCalculatorWidget : GlanceAppWidget() {
modifier = GlanceModifier modifier = GlanceModifier
.background(GlanceTheme.colors.surfaceVariant) .background(GlanceTheme.colors.surfaceVariant)
) { ) {
Row(modifier = GlanceModifier.fillMaxWidth().padding(8.dp)) { ActionButtons(
val boxModifier = GlanceModifier.fillMaxWidth().defaultWeight() modifier = GlanceModifier.fillMaxWidth().padding(8.dp),
onCopyClick = copyAction(
GlanceKeyboardButton(boxModifier, GlanceTheme.colors.primary, R.drawable.content_copy, onClick = runCopyAction()) output = output,
GlanceKeyboardButton(boxModifier, GlanceTheme.colors.primary, R.drawable.open_in_new, onClick = actionStartActivity(ComponentName(LocalContext.current, "com.sadellie.unitto.MainActivity"))) fractional = formatterSymbols.fractional
}
Text(
text = input.formatExpression(formatterSymbols),
modifier = GlanceModifier.fillMaxWidth(),
maxLines = 2,
style = TextStyle(
fontSize = 36.sp,
textAlign = TextAlign.End,
color = GlanceTheme.colors.onSurfaceVariant
), ),
onLaunchClick = launchAction(LocalContext.current)
) )
Text( TextField(
text = output.formatExpression(formatterSymbols),
modifier = GlanceModifier.fillMaxWidth(), modifier = GlanceModifier.fillMaxWidth(),
maxLines = 1, input = input,
style = TextStyle( formatterSymbols = formatterSymbols,
fontSize = 36.sp, fontSize = 36.sp,
textAlign = TextAlign.End, maxLines = 2
color = GlanceTheme.colors.onSurfaceVariant )
), TextField(
modifier = GlanceModifier.fillMaxWidth(),
input = output,
formatterSymbols = formatterSymbols,
fontSize = 28.sp,
maxLines = 1
) )
} }
Column(
modifier = GlanceModifier
.padding(8.dp)
) {
GlanceKeyboard( GlanceKeyboard(
modifier = GlanceModifier
.padding(8.dp),
addTokenAction = { addTokenAction = {
runCalculateAction(input.addToken(it)) runCalculateAction(input.addToken(it))
}, },
@ -212,7 +180,7 @@ class UnittoCalculatorWidget : GlanceAppWidget() {
runCalculateAction(input.dropLast(1)) runCalculateAction(input.dropLast(1))
}, },
equalAction = equal@{ equalAction = equal@{
if (input.isEmpty()) return@equal actionRunCallback<UpdateInputAction>() if (output.isEmpty()) return@equal actionRunCallback<UpdateInputAction>()
runCalculateAction(output) runCalculateAction(output)
}, },
@ -220,29 +188,57 @@ class UnittoCalculatorWidget : GlanceAppWidget() {
middleZero = appPrefs.middleZero middleZero = appPrefs.middleZero
) )
} }
} }
}
@Composable @Composable
private fun LoadingUI() { private fun ActionButtons(
Box( modifier: GlanceModifier,
modifier = GlanceModifier onCopyClick: Action,
.appWidgetBackground() onLaunchClick: Action,
.background(GlanceTheme.colors.background) ) {
.fillMaxSize(), Row(
contentAlignment = Alignment.Center modifier = modifier
) { ) {
GlanceKeyboardButton( val boxModifier = GlanceModifier.fillMaxWidth().defaultWeight()
glanceModifier = GlanceModifier,
IconButton(
glanceModifier = boxModifier,
containerColor = GlanceTheme.colors.primary, containerColor = GlanceTheme.colors.primary,
iconRes = R.drawable.refresh, iconRes = R.drawable.content_copy,
onClick = actionRunCallback<RestartWidget>(), onClick = onCopyClick
)
IconButton(
glanceModifier = boxModifier,
containerColor = GlanceTheme.colors.primary,
iconRes = R.drawable.open_in_new,
onClick = onLaunchClick
) )
} }
} }
@Composable @Composable
private fun ColumnScope.GlanceKeyboard( 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, addTokenAction: (String) -> Action,
replaceInputAction: (String) -> Action, replaceInputAction: (String) -> Action,
addBracketAction: () -> Action, addBracketAction: () -> Action,
@ -250,7 +246,7 @@ class UnittoCalculatorWidget : GlanceAppWidget() {
equalAction: () -> Action, equalAction: () -> Action,
useDot: Boolean, useDot: Boolean,
middleZero: Boolean, middleZero: Boolean,
) { ) = Column(modifier = modifier) {
val rowModifier = GlanceModifier.defaultWeight().fillMaxWidth() val rowModifier = GlanceModifier.defaultWeight().fillMaxWidth()
Row( Row(
@ -258,10 +254,30 @@ class UnittoCalculatorWidget : GlanceAppWidget() {
) { ) {
val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight()
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.tertiaryContainer, R.drawable.clear, onClick = replaceInputAction("")) IconButton(
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.brackets, onClick = addBracketAction()) glanceModifier = buttonModifier,
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.percent, onClick = addTokenAction(Token.Operator.percent)) containerColor = GlanceTheme.colors.tertiaryContainer,
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.divide, onClick = addTokenAction(Token.Operator.divide)) 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( Row(
@ -269,10 +285,30 @@ class UnittoCalculatorWidget : GlanceAppWidget() {
) { ) {
val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight()
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key7, onClick = addTokenAction(Token.Digit._7)) IconButton(
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key8, onClick = addTokenAction(Token.Digit._8)) glanceModifier = buttonModifier,
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key9, onClick = addTokenAction(Token.Digit._9)) containerColor = GlanceTheme.colors.inverseOnSurface,
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.multiply, onClick = addTokenAction(Token.Operator.multiply)) 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( Row(
@ -280,10 +316,30 @@ class UnittoCalculatorWidget : GlanceAppWidget() {
) { ) {
val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight()
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key4, onClick = addTokenAction(Token.Digit._4)) IconButton(
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key5, onClick = addTokenAction(Token.Digit._5)) glanceModifier = buttonModifier,
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key6, onClick = addTokenAction(Token.Digit._6)) containerColor = GlanceTheme.colors.inverseOnSurface,
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.minus, onClick = addTokenAction(Token.Operator.minus)) 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( Row(
@ -291,10 +347,30 @@ class UnittoCalculatorWidget : GlanceAppWidget() {
) { ) {
val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight()
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key1, onClick = addTokenAction(Token.Digit._1)) IconButton(
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key2, onClick = addTokenAction(Token.Digit._2)) glanceModifier = buttonModifier,
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key3, onClick = addTokenAction(Token.Digit._3)) containerColor = GlanceTheme.colors.inverseOnSurface,
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.plus, onClick = addTokenAction(Token.Operator.plus)) 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( Row(
@ -303,77 +379,43 @@ class UnittoCalculatorWidget : GlanceAppWidget() {
val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight() val buttonModifier = GlanceModifier.fillMaxSize().defaultWeight()
if (middleZero) { if (middleZero) {
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, if (useDot) R.drawable.dot else R.drawable.comma, onClick = addTokenAction(Token.Digit.dot)) IconButton(
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key0, onClick = addTokenAction(Token.Digit._0)) 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 { } else {
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.key0, onClick = addTokenAction(Token.Digit._0)) IconButton(
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, if (useDot) R.drawable.dot else R.drawable.comma, onClick = addTokenAction(Token.Digit.dot)) glanceModifier = buttonModifier,
} containerColor = GlanceTheme.colors.inverseOnSurface,
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.inverseOnSurface, R.drawable.backspace, onClick = deleteTokenAction()) iconRes = R.drawable.key0,
GlanceKeyboardButton(buttonModifier, GlanceTheme.colors.primaryContainer, R.drawable.equal, onClick = equalAction()) onClick = addTokenAction(Token.Digit._0)
} )
} IconButton(
glanceModifier = buttonModifier,
@Composable containerColor = GlanceTheme.colors.inverseOnSurface,
private fun GlanceKeyboardButton( iconRes = if (useDot) R.drawable.dot else R.drawable.comma,
glanceModifier: GlanceModifier, onClick = addTokenAction(Token.Digit.dot)
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)
) )
} }
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()
)
} }
} }
// 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
}

View File

@ -20,11 +20,17 @@ package com.sadellie.unitto.feature.glance.glance
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.ComponentName
import android.content.Context import android.content.Context
import androidx.glance.GlanceId import androidx.glance.GlanceId
import androidx.glance.action.Action
import androidx.glance.action.ActionParameters 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.ActionCallback
import androidx.glance.appwidget.action.actionRunCallback
import androidx.glance.appwidget.state.updateAppWidgetState 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.format
import com.sadellie.unitto.data.common.isExpression import com.sadellie.unitto.data.common.isExpression
import io.github.sadellie.evaluatto.Expression 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<UpdateInputAction>(
actionParametersOf(
UnittoCalculatorWidget.inputKey to input,
UnittoCalculatorWidget.precisionKey to precision,
UnittoCalculatorWidget.outputFormatKey to outputFormat
)
)
internal fun copyAction(
output: String,
fractional: String
): Action = actionRunCallback<CopyResultAction>(
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( private fun calculate(
input: String, input: String,
precision: Int, precision: Int,

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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()
}
}