fix: fixed errors of API usage

This commit is contained in:
danilkinkin 2025-07-07 00:52:21 +02:00
parent 862be94038
commit 9e41a7ede9
5 changed files with 308 additions and 185 deletions

View file

@ -90,6 +90,8 @@ dependencies {
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.9.1")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.9.1")
implementation("androidx.glance:glance-appwidget:1.1.1")
implementation("androidx.glance:glance-appwidget-preview:1.1.1")
implementation("androidx.glance:glance-preview:1.1.1")
implementation("androidx.core:core-splashscreen:1.0.1")
implementation("com.google.accompanist:accompanist-systemuicontroller:0.36.0")
implementation("com.google.dagger:dagger:2.56.2")
@ -105,7 +107,6 @@ dependencies {
// Debug
debugImplementation("androidx.compose.ui:ui-tooling:1.8.3")
debugImplementation("androidx.compose.ui:ui-test-manifest:1.8.3")
debugImplementation("com.google.android.glance.tools:appwidget-viewer:0.2.2")
// Testing
androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2")

View file

@ -8,22 +8,28 @@ import android.graphics.Paint
import android.graphics.Rect
import android.graphics.Typeface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.graphics.createBitmap
import androidx.glance.ColorFilter
import androidx.glance.GlanceComposable
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.LocalContext
import androidx.glance.text.TextStyle
import androidx.glance.GlanceModifier
import androidx.glance.layout.ContentScale
import androidx.glance.layout.Row
import androidx.glance.layout.width
import androidx.glance.preview.ExperimentalGlancePreviewApi
import androidx.glance.preview.Preview
import androidx.glance.text.FontWeight
import androidx.glance.text.TextAlign
import androidx.glance.unit.ColorProvider
import androidx.glance.text.TextStyle
import com.danilkinkin.buckwheat.base.Size
@ -77,11 +83,7 @@ fun drawText(context: Context, text: String, style: TextStyle): Bitmap {
val paint = Paint().applyFontToPaint(context, style)
val size = calcTextSize(context, text, style)
val bitmap: Bitmap = Bitmap.createBitmap(
size.width,
size.height,
Bitmap.Config.ARGB_8888,
)
val bitmap: Bitmap = createBitmap(size.width, size.height)
Canvas(bitmap).drawText(text, 0F, -paint.ascent(), paint)
@ -102,7 +104,7 @@ fun CanvasText(
val width = 0.dp
//.plus(modifier.collectPaddingInDp(context.resources)?.start ?: 0.dp)
.plus(Dp(size.width / context.resources.displayMetrics.density))
//.plus(modifier.collectPaddingInDp(context.resources)?.end ?: 0.dp)
//.plus(modifier.collectPaddingInDp(context.resources)?.end ?: 0.dp)
if (noTint) {
@ -119,7 +121,7 @@ fun CanvasText(
drawText(
context,
text,
style.copy(ColorProvider(Color.Black))
style.copy(Color.Black.toColorProvider())
)
),
colorFilter = ColorFilter.tint(style.color),
@ -128,3 +130,84 @@ fun CanvasText(
)
}
}
@OptIn(ExperimentalGlancePreviewApi::class)
@Preview(300, 55)
@Composable
@GlanceComposable
fun PreviewColoredText() {
GlanceTheme {
CompositionLocalProvider(
LocalContentColor provides GlanceTheme.colors.onSurface,
) {
CanvasText(
text = "Hey!",
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 36.sp,
color = Color.Green.toColorProvider()
)
)
}
}
}
@OptIn(ExperimentalGlancePreviewApi::class)
@Preview(300, 55)
@Composable
@GlanceComposable
fun PreviewWithEmojiTint() {
GlanceTheme {
CompositionLocalProvider(
LocalContentColor provides GlanceTheme.colors.onSurface,
) {
Row {
CanvasText(
text = "Hey \uD83D\uDC4B\uD83C\uDFFB!",
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 36.sp,
)
)
}
}
}
}
@OptIn(ExperimentalGlancePreviewApi::class)
@Preview(300, 55)
@Composable
@GlanceComposable
fun PreviewWithEmojiNoTint() {
GlanceTheme {
CompositionLocalProvider(
LocalContentColor provides GlanceTheme.colors.onSurface,
) {
Row {
CanvasText(
text = "Hey ",
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 36.sp,
)
)
CanvasText(
text = "\uD83D\uDC4B\uD83C\uDFFB",
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 36.sp,
),
noTint = true
)
CanvasText(
text = "!",
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 36.sp,
)
)
}
}
}
}

View file

@ -3,7 +3,9 @@ package com.danilkinkin.buckwheat.widget
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.datastore.preferences.core.floatPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
@ -23,8 +25,20 @@ import java.math.RoundingMode
import java.util.Date
import javax.inject.Inject
val LocalContentColor = compositionLocalOf { ColorProvider(Color.Black) }
val LocalAccentColor = compositionLocalOf { ColorProvider(Color.White) }
@Composable
fun Color.toColorProvider(): ColorProvider {
return try {
val constructor = ColorProvider::class.java.getDeclaredConstructor(Color::class.java)
constructor.isAccessible = true
constructor.newInstance(this)
} catch (e: Exception) {
LocalContentColor.current
}
}
val LocalContentColor = compositionLocalOf<ColorProvider> { throw Error("No set") }
val LocalAccentColor = compositionLocalOf<ColorProvider> { throw Error("No set") }
abstract class WidgetReceiver : GlanceAppWidgetReceiver() {

View file

@ -3,7 +3,6 @@ package com.danilkinkin.buckwheat.widget.extend
import android.content.Intent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.res.ResourcesCompat
@ -32,6 +31,8 @@ import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.layout.size
import androidx.glance.layout.width
import androidx.glance.preview.ExperimentalGlancePreviewApi
import androidx.glance.preview.Preview
import androidx.glance.text.FontWeight
import androidx.glance.text.TextStyle
import com.danilkinkin.buckwheat.BuildConfig
@ -367,35 +368,37 @@ fun ExtendWidgetContent() {
}
}
}
}
if (BuildConfig.DEBUG) {
Box(
modifier = GlanceModifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
CanvasText(
modifier = GlanceModifier.padding(top = 8.dp),
text = "${size.width}x${size.height}",
style = TextStyle(
color = GlanceTheme.colors.onSurfaceVariant,
fontWeight = FontWeight.Bold,
fontSize = 10.sp,
if (BuildConfig.DEBUG) {
Box(
modifier = GlanceModifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
CanvasText(
modifier = GlanceModifier.padding(top = 8.dp),
text = "${size.width}x${size.height}",
style = TextStyle(
color = GlanceTheme.colors.onSurfaceVariant,
fontWeight = FontWeight.Bold,
fontSize = 10.sp,
)
)
)
}
}
}
Box(
modifier = GlanceModifier
.cornerRadius(32.dp)
.fillMaxSize()
.clickable(actionStartActivity(intent))
) {}
Box(
modifier = GlanceModifier
.cornerRadius(32.dp)
.fillMaxSize()
.clickable(actionStartActivity(intent))
) {}
}
}
@Preview
@OptIn(ExperimentalGlancePreviewApi::class)
@Preview(400, 300)
@Composable
@GlanceComposable
private fun Preview() {
GlanceTheme {
ExtendWidgetContent()

View file

@ -2,6 +2,7 @@ package com.danilkinkin.buckwheat.widget.minimal
import android.content.Intent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.res.ResourcesCompat
@ -28,13 +29,18 @@ import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.layout.size
import androidx.glance.preview.ExperimentalGlancePreviewApi
import androidx.glance.preview.Preview
import androidx.glance.text.FontWeight
import androidx.glance.text.TextStyle
import com.danilkinkin.buckwheat.BuildConfig
import com.danilkinkin.buckwheat.MainActivity
import com.danilkinkin.buckwheat.R
import com.danilkinkin.buckwheat.widget.CanvasText
import com.danilkinkin.buckwheat.widget.LocalAccentColor
import com.danilkinkin.buckwheat.widget.LocalContentColor
import com.danilkinkin.buckwheat.widget.WidgetReceiver
import com.danilkinkin.buckwheat.widget.extend.ExtendWidgetContent
@Composable
@GlanceComposable
@ -48,182 +54,198 @@ fun MinimalWidgetContent() {
prefs[WidgetReceiver.stateBudgetPreferenceKey]
?: WidgetReceiver.StateBudget.NOT_SET.name
)
Box(
modifier = GlanceModifier
.cornerRadius(48.dp)
.fillMaxSize()
.background(ImageProvider(R.drawable.minimal_widget_preview_background)),
contentAlignment = Alignment.Center,
CompositionLocalProvider(
LocalContentColor provides GlanceTheme.colors.onSurface,
LocalAccentColor provides GlanceTheme.colors.primary,
) {
Column(
modifier = GlanceModifier.padding(8.dp),
horizontalAlignment = Alignment.Start,
verticalAlignment = Alignment.CenterVertically,
Box(
modifier = GlanceModifier
.cornerRadius(48.dp)
.fillMaxSize()
.background(ImageProvider(R.drawable.minimal_widget_preview_background)),
contentAlignment = Alignment.Center,
) {
if (
stateBudget !== WidgetReceiver.StateBudget.NOT_SET &&
stateBudget !== WidgetReceiver.StateBudget.END_PERIOD
Column(
modifier = GlanceModifier.padding(8.dp),
horizontalAlignment = Alignment.Start,
verticalAlignment = Alignment.CenterVertically,
) {
Row(
modifier = GlanceModifier.padding(12.dp),
verticalAlignment = Alignment.CenterVertically,
if (
stateBudget !== WidgetReceiver.StateBudget.NOT_SET &&
stateBudget !== WidgetReceiver.StateBudget.END_PERIOD
) {
Row(
modifier = GlanceModifier.padding(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
CanvasText(
modifier = GlanceModifier.padding(
0.dp,
0.dp,
when (size) {
MinimalWidget.largeMode -> 8.dp
MinimalWidget.smallMode -> 4.dp
else -> 6.dp
},
0.dp,
),
text = context.resources.getString(
when (size) {
MinimalWidget.smallMode -> R.string.add_spent_short
else -> R.string.add_spent
}
),
style = TextStyle(
color = GlanceTheme.colors.onSurface,
fontWeight = FontWeight.Medium,
fontSize = when (size) {
MinimalWidget.largeMode -> 22.sp
MinimalWidget.smallMode -> 14.sp
else -> 18.sp
},
)
)
val drawable = ResourcesCompat.getDrawable(
context.resources,
R.drawable.ic_add,
null,
)!!
Image(
modifier = GlanceModifier.size(
when (size) {
MinimalWidget.largeMode -> 32.dp
MinimalWidget.smallMode -> 24.dp
else -> 28.dp
}
),
provider = ImageProvider(drawable.toBitmap()),
colorFilter = ColorFilter.tint(GlanceTheme.colors.onSurface),
contentDescription = null,
)
}
}
if (
stateBudget === WidgetReceiver.StateBudget.NOT_SET ||
stateBudget === WidgetReceiver.StateBudget.END_PERIOD
) {
CanvasText(
modifier = GlanceModifier.padding(
0.dp,
0.dp,
when (size) {
MinimalWidget.largeMode -> 8.dp
MinimalWidget.smallMode -> 4.dp
else -> 6.dp
},
0.dp,
),
text = context.resources.getString(
when (size) {
MinimalWidget.smallMode -> R.string.add_spent_short
else -> R.string.add_spent
}
),
modifier = GlanceModifier.padding(0.dp, 0.dp, 8.dp, 0.dp),
text = if (stateBudget === WidgetReceiver.StateBudget.NOT_SET) {
context.resources.getString(
R.string.budget_not_set
)
} else {
context.resources.getString(
R.string.finish_period_title
)
},
style = TextStyle(
color = GlanceTheme.colors.onSurface,
fontWeight = FontWeight.Medium,
fontWeight = FontWeight.Bold,
fontSize = when (size) {
MinimalWidget.largeMode -> 22.sp
MinimalWidget.smallMode -> 14.sp
MinimalWidget.largeMode -> 24.sp
MinimalWidget.smallMode -> 18.sp
else -> 18.sp
},
)
)
val drawable = ResourcesCompat.getDrawable(
context.resources,
R.drawable.ic_add,
null,
)!!
Image(
modifier = GlanceModifier.size(
when (size) {
MinimalWidget.largeMode -> 32.dp
MinimalWidget.smallMode -> 24.dp
else -> 28.dp
}
),
provider = ImageProvider(drawable.toBitmap()),
colorFilter = ColorFilter.tint(GlanceTheme.colors.onSurface),
contentDescription = null,
)
}
}
if (
stateBudget === WidgetReceiver.StateBudget.NOT_SET ||
stateBudget === WidgetReceiver.StateBudget.END_PERIOD
) {
CanvasText(
modifier = GlanceModifier.padding(0.dp, 0.dp, 8.dp, 0.dp),
text = if (stateBudget === WidgetReceiver.StateBudget.NOT_SET) {
context.resources.getString(
R.string.budget_not_set
)
} else {
context.resources.getString(
R.string.finish_period_title
)
},
style = TextStyle(
color = GlanceTheme.colors.onSurface,
fontWeight = FontWeight.Bold,
fontSize = when (size) {
MinimalWidget.largeMode -> 24.sp
MinimalWidget.smallMode -> 18.sp
else -> 18.sp
},
)
)
Row(
modifier = GlanceModifier.padding(
0.dp,
when (size) {
MinimalWidget.largeMode -> 4.dp
MinimalWidget.smallMode -> 0.dp
else -> 2.dp
},
0.dp,
0.dp,
),
verticalAlignment = Alignment.CenterVertically,
) {
CanvasText(
Row(
modifier = GlanceModifier.padding(
0.dp,
0.dp,
when (size) {
MinimalWidget.largeMode -> 6.dp
MinimalWidget.smallMode -> 2.dp
else -> 4.dp
MinimalWidget.largeMode -> 4.dp
MinimalWidget.smallMode -> 0.dp
else -> 2.dp
},
0.dp,
0.dp,
),
text = context.resources.getString(
R.string.set_period_title
),
style = TextStyle(
color = GlanceTheme.colors.primary,
fontWeight = FontWeight.Bold,
fontSize = when (size) {
MinimalWidget.largeMode -> 16.sp
MinimalWidget.smallMode -> 12.sp
else -> 14.sp
},
verticalAlignment = Alignment.CenterVertically,
) {
CanvasText(
modifier = GlanceModifier.padding(
0.dp,
0.dp,
when (size) {
MinimalWidget.largeMode -> 6.dp
MinimalWidget.smallMode -> 2.dp
else -> 4.dp
},
0.dp,
),
text = context.resources.getString(
R.string.set_period_title
),
style = TextStyle(
color = GlanceTheme.colors.primary,
fontWeight = FontWeight.Bold,
fontSize = when (size) {
MinimalWidget.largeMode -> 16.sp
MinimalWidget.smallMode -> 12.sp
else -> 14.sp
},
)
)
)
val drawable = ResourcesCompat.getDrawable(
context.resources,
R.drawable.ic_arrow_forward,
null,
)!!
val drawable = ResourcesCompat.getDrawable(
context.resources,
R.drawable.ic_arrow_forward,
null,
)!!
Image(
modifier = GlanceModifier.size(
when (size) {
MinimalWidget.largeMode -> 22.dp
MinimalWidget.smallMode -> 14.dp
else -> 20.dp
}
),
provider = ImageProvider(drawable.toBitmap()),
colorFilter = ColorFilter.tint(GlanceTheme.colors.primary),
contentDescription = null,
)
Image(
modifier = GlanceModifier.size(
when (size) {
MinimalWidget.largeMode -> 22.dp
MinimalWidget.smallMode -> 14.dp
else -> 20.dp
}
),
provider = ImageProvider(drawable.toBitmap()),
colorFilter = ColorFilter.tint(GlanceTheme.colors.primary),
contentDescription = null,
)
}
}
}
}
}
if (BuildConfig.DEBUG) {
Box(
modifier = GlanceModifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
CanvasText(
modifier = GlanceModifier.padding(top = 8.dp),
text = "${size.width}x${size.height}", style = TextStyle(
color = GlanceTheme.colors.onSurfaceVariant,
fontWeight = FontWeight.Bold,
fontSize = 10.sp,
if (BuildConfig.DEBUG) {
Box(
modifier = GlanceModifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
CanvasText(
modifier = GlanceModifier.padding(top = 8.dp),
text = "${size.width}x${size.height}", style = TextStyle(
color = GlanceTheme.colors.onSurfaceVariant,
fontWeight = FontWeight.Bold,
fontSize = 10.sp,
)
)
)
}
}
}
Box(
modifier = GlanceModifier
.cornerRadius(48.dp)
.fillMaxSize()
.clickable(actionStartActivity(intent))
) {}
Box(
modifier = GlanceModifier
.cornerRadius(48.dp)
.fillMaxSize()
.clickable(actionStartActivity(intent))
) {}
}
}
@OptIn(ExperimentalGlancePreviewApi::class)
@Preview(400, 300)
@Composable
@GlanceComposable
private fun Preview() {
GlanceTheme {
MinimalWidgetContent()
}
}