diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f8034f711..c3c2f288c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -141,20 +141,6 @@
android:name=".data.notification.NotificationReceiver"
android:exported="false" />
-
-
-
-
-
-
-
-
diff --git a/presentation-widget/src/main/AndroidManifest.xml b/presentation-widget/src/main/AndroidManifest.xml
index 568741e54..38943f13e 100644
--- a/presentation-widget/src/main/AndroidManifest.xml
+++ b/presentation-widget/src/main/AndroidManifest.xml
@@ -1,2 +1,39 @@
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt
new file mode 100644
index 000000000..26e1cd918
--- /dev/null
+++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt
@@ -0,0 +1,153 @@
+package tachiyomi.presentation.widget
+
+import android.app.Application
+import android.content.Context
+import android.graphics.Bitmap
+import android.os.Build
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.unit.Dp
+import androidx.core.graphics.drawable.toBitmap
+import androidx.glance.GlanceId
+import androidx.glance.GlanceModifier
+import androidx.glance.ImageProvider
+import androidx.glance.appwidget.GlanceAppWidget
+import androidx.glance.appwidget.GlanceAppWidgetManager
+import androidx.glance.appwidget.SizeMode
+import androidx.glance.appwidget.appWidgetBackground
+import androidx.glance.appwidget.provideContent
+import androidx.glance.background
+import androidx.glance.layout.fillMaxSize
+import androidx.glance.layout.padding
+import androidx.glance.unit.ColorProvider
+import coil.executeBlocking
+import coil.imageLoader
+import coil.request.CachePolicy
+import coil.request.ImageRequest
+import coil.size.Precision
+import coil.size.Scale
+import coil.transform.RoundedCornersTransformation
+import eu.kanade.tachiyomi.core.security.SecurityPreferences
+import eu.kanade.tachiyomi.util.system.dpToPx
+import kotlinx.coroutines.flow.map
+import tachiyomi.core.util.lang.withIOContext
+import tachiyomi.domain.manga.model.MangaCover
+import tachiyomi.domain.updates.interactor.GetUpdates
+import tachiyomi.domain.updates.model.UpdatesWithRelations
+import tachiyomi.presentation.widget.components.CoverHeight
+import tachiyomi.presentation.widget.components.CoverWidth
+import tachiyomi.presentation.widget.components.LockedWidget
+import tachiyomi.presentation.widget.components.UpdatesWidget
+import tachiyomi.presentation.widget.util.appWidgetBackgroundRadius
+import tachiyomi.presentation.widget.util.calculateRowAndColumnCount
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+import java.util.Calendar
+import java.util.Date
+
+abstract class BaseUpdatesGridGlanceWidget(
+ private val context: Context = Injekt.get(),
+ private val getUpdates: GetUpdates = Injekt.get(),
+ private val preferences: SecurityPreferences = Injekt.get(),
+) : GlanceAppWidget() {
+
+ override val sizeMode = SizeMode.Exact
+
+ abstract val foreground: ColorProvider
+ abstract val background: ImageProvider
+ abstract val topPadding: Dp
+ abstract val bottomPadding: Dp
+
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ val locked = preferences.useAuthenticator().get()
+ val containerModifier = GlanceModifier
+ .fillMaxSize()
+ .background(background)
+ .appWidgetBackground()
+ .padding(top = topPadding, bottom = bottomPadding)
+ .appWidgetBackgroundRadius()
+
+ val manager = GlanceAppWidgetManager(context)
+ val ids = manager.getGlanceIds(javaClass)
+ val (rowCount, columnCount) = ids
+ .flatMap { manager.getAppWidgetSizes(it) }
+ .maxBy { it.height.value * it.width.value }
+ .calculateRowAndColumnCount(topPadding, bottomPadding)
+
+ provideContent {
+ // If app lock enabled, don't do anything
+ if (locked) {
+ LockedWidget(
+ foreground = foreground,
+ modifier = containerModifier,
+ )
+ return@provideContent
+ }
+
+ val flow = remember {
+ getUpdates
+ .subscribe(false, DateLimit.timeInMillis)
+ .map { rawData ->
+ rawData.prepareData(rowCount, columnCount)
+ }
+ }
+ val data by flow.collectAsState(initial = null)
+ UpdatesWidget(
+ data = data,
+ modifier = containerModifier,
+ contentColor = foreground,
+ topPadding = topPadding,
+ bottomPadding = bottomPadding,
+ )
+ }
+ }
+
+ private suspend fun List.prepareData(
+ rowCount: Int,
+ columnCount: Int,
+ ): List> {
+ // Resize to cover size
+ val widthPx = CoverWidth.value.toInt().dpToPx
+ val heightPx = CoverHeight.value.toInt().dpToPx
+ val roundPx = context.resources.getDimension(R.dimen.appwidget_inner_radius)
+ return withIOContext {
+ this@prepareData
+ .distinctBy { it.mangaId }
+ .take(rowCount * columnCount)
+ .map { updatesView ->
+ val request = ImageRequest.Builder(context)
+ .data(
+ MangaCover(
+ mangaId = updatesView.mangaId,
+ sourceId = updatesView.sourceId,
+ isMangaFavorite = true,
+ url = updatesView.coverData.url,
+ lastModified = updatesView.coverData.lastModified,
+ ),
+ )
+ .memoryCachePolicy(CachePolicy.DISABLED)
+ .precision(Precision.EXACT)
+ .size(widthPx, heightPx)
+ .scale(Scale.FILL)
+ .let {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+ it.transformations(RoundedCornersTransformation(roundPx))
+ } else {
+ it // Handled by system
+ }
+ }
+ .build()
+ Pair(updatesView.mangaId, context.imageLoader.executeBlocking(request).drawable?.toBitmap())
+ }
+ }
+ }
+
+ companion object {
+ val DateLimit: Calendar
+ get() = Calendar.getInstance().apply {
+ time = Date()
+ add(Calendar.MONTH, -3)
+ }
+ }
+}
diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/TachiyomiWidgetManager.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/TachiyomiWidgetManager.kt
index e51b0337d..8227c6c51 100644
--- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/TachiyomiWidgetManager.kt
+++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/TachiyomiWidgetManager.kt
@@ -19,7 +19,7 @@ class TachiyomiWidgetManager(
fun Context.init(scope: LifecycleCoroutineScope) {
combine(
- getUpdates.subscribe(read = false, after = UpdatesGridGlanceWidget.DateLimit.timeInMillis),
+ getUpdates.subscribe(read = false, after = BaseUpdatesGridGlanceWidget.DateLimit.timeInMillis),
securityPreferences.useAuthenticator().changes(),
transform = { a, _ -> a },
)
@@ -27,6 +27,7 @@ class TachiyomiWidgetManager(
.onEach {
try {
UpdatesGridGlanceWidget().updateAll(this)
+ UpdatesGridCoverScreenGlanceWidget().updateAll(this)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Failed to update widget" }
}
diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridCoverScreenGlanceReceiver.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridCoverScreenGlanceReceiver.kt
new file mode 100644
index 000000000..721c6a3ac
--- /dev/null
+++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridCoverScreenGlanceReceiver.kt
@@ -0,0 +1,9 @@
+package tachiyomi.presentation.widget
+
+import androidx.glance.appwidget.GlanceAppWidget
+import androidx.glance.appwidget.GlanceAppWidgetReceiver
+
+class UpdatesGridCoverScreenGlanceReceiver : GlanceAppWidgetReceiver() {
+ override val glanceAppWidget: GlanceAppWidget
+ get() = UpdatesGridCoverScreenGlanceWidget()
+}
diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridCoverScreenGlanceWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridCoverScreenGlanceWidget.kt
new file mode 100644
index 000000000..efb67f1c3
--- /dev/null
+++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridCoverScreenGlanceWidget.kt
@@ -0,0 +1,13 @@
+package tachiyomi.presentation.widget
+
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.glance.ImageProvider
+import androidx.glance.unit.ColorProvider
+
+class UpdatesGridCoverScreenGlanceWidget : BaseUpdatesGridGlanceWidget() {
+ override val foreground = ColorProvider(Color.White)
+ override val background = ImageProvider(R.drawable.appwidget_coverscreen_background)
+ override val topPadding = 0.dp
+ override val bottomPadding = 24.dp
+}
diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridGlanceReceiver.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridGlanceReceiver.kt
index 93a4cf735..3ee232781 100644
--- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridGlanceReceiver.kt
+++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridGlanceReceiver.kt
@@ -4,5 +4,6 @@ import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
class UpdatesGridGlanceReceiver : GlanceAppWidgetReceiver() {
- override val glanceAppWidget: GlanceAppWidget = UpdatesGridGlanceWidget()
+ override val glanceAppWidget: GlanceAppWidget
+ get() = UpdatesGridGlanceWidget()
}
diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridGlanceWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridGlanceWidget.kt
index 4275d08f0..2de89ecce 100644
--- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridGlanceWidget.kt
+++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridGlanceWidget.kt
@@ -1,133 +1,12 @@
package tachiyomi.presentation.widget
-import android.app.Application
-import android.content.Context
-import android.graphics.Bitmap
-import android.os.Build
-import androidx.core.graphics.drawable.toBitmap
-import androidx.glance.GlanceId
-import androidx.glance.GlanceModifier
+import androidx.compose.ui.unit.dp
import androidx.glance.ImageProvider
-import androidx.glance.appwidget.GlanceAppWidget
-import androidx.glance.appwidget.GlanceAppWidgetManager
-import androidx.glance.appwidget.SizeMode
-import androidx.glance.appwidget.appWidgetBackground
-import androidx.glance.appwidget.provideContent
-import androidx.glance.background
-import androidx.glance.layout.fillMaxSize
-import coil.executeBlocking
-import coil.imageLoader
-import coil.request.CachePolicy
-import coil.request.ImageRequest
-import coil.size.Precision
-import coil.size.Scale
-import coil.transform.RoundedCornersTransformation
-import eu.kanade.tachiyomi.core.security.SecurityPreferences
-import eu.kanade.tachiyomi.util.system.dpToPx
-import tachiyomi.core.util.lang.withIOContext
-import tachiyomi.domain.manga.model.MangaCover
-import tachiyomi.domain.updates.interactor.GetUpdates
-import tachiyomi.domain.updates.model.UpdatesWithRelations
-import tachiyomi.presentation.widget.components.CoverHeight
-import tachiyomi.presentation.widget.components.CoverWidth
-import tachiyomi.presentation.widget.components.LockedWidget
-import tachiyomi.presentation.widget.components.UpdatesWidget
-import tachiyomi.presentation.widget.util.appWidgetBackgroundRadius
-import tachiyomi.presentation.widget.util.calculateRowAndColumnCount
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-import java.util.Calendar
-import java.util.Date
+import androidx.glance.unit.ColorProvider
-class UpdatesGridGlanceWidget(
- private val context: Context = Injekt.get(),
- private val getUpdates: GetUpdates = Injekt.get(),
- private val preferences: SecurityPreferences = Injekt.get(),
-) : GlanceAppWidget() {
-
- private var data: List>? = null
-
- override val sizeMode = SizeMode.Exact
-
- override suspend fun provideGlance(context: Context, id: GlanceId) {
- val locked = preferences.useAuthenticator().get()
- if (!locked) loadData()
-
- provideContent {
- // If app lock enabled, don't do anything
- if (locked) {
- LockedWidget()
- return@provideContent
- }
- UpdatesWidget(data)
- }
- }
-
- private suspend fun loadData() {
- val manager = GlanceAppWidgetManager(context)
- val ids = manager.getGlanceIds(this@UpdatesGridGlanceWidget::class.java)
- if (ids.isEmpty()) return
-
- withIOContext {
- val updates = getUpdates.await(
- read = false,
- after = DateLimit.timeInMillis,
- )
- val (rowCount, columnCount) = ids
- .flatMap { manager.getAppWidgetSizes(it) }
- .maxBy { it.height.value * it.width.value }
- .calculateRowAndColumnCount()
-
- data = prepareList(updates, rowCount * columnCount)
- }
- }
-
- private fun prepareList(processList: List, take: Int): List> {
- // Resize to cover size
- val widthPx = CoverWidth.value.toInt().dpToPx
- val heightPx = CoverHeight.value.toInt().dpToPx
- val roundPx = context.resources.getDimension(R.dimen.appwidget_inner_radius)
- return processList
- .distinctBy { it.mangaId }
- .take(take)
- .map { updatesView ->
- val request = ImageRequest.Builder(context)
- .data(
- MangaCover(
- mangaId = updatesView.mangaId,
- sourceId = updatesView.sourceId,
- isMangaFavorite = true,
- url = updatesView.coverData.url,
- lastModified = updatesView.coverData.lastModified,
- ),
- )
- .memoryCachePolicy(CachePolicy.DISABLED)
- .precision(Precision.EXACT)
- .size(widthPx, heightPx)
- .scale(Scale.FILL)
- .let {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
- it.transformations(RoundedCornersTransformation(roundPx))
- } else {
- it // Handled by system
- }
- }
- .build()
- Pair(updatesView.mangaId, context.imageLoader.executeBlocking(request).drawable?.toBitmap())
- }
- }
-
- companion object {
- val DateLimit: Calendar
- get() = Calendar.getInstance().apply {
- time = Date()
- add(Calendar.MONTH, -3)
- }
- }
+class UpdatesGridGlanceWidget : BaseUpdatesGridGlanceWidget() {
+ override val foreground = ColorProvider(R.color.appwidget_on_secondary_container)
+ override val background = ImageProvider(R.drawable.appwidget_background)
+ override val topPadding = 0.dp
+ override val bottomPadding = 0.dp
}
-
-val ContainerModifier = GlanceModifier
- .fillMaxSize()
- .background(ImageProvider(R.drawable.appwidget_background))
- .appWidgetBackground()
- .appWidgetBackgroundRadius()
diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/LockedWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/LockedWidget.kt
index 730ab0670..6acb8d7ec 100644
--- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/LockedWidget.kt
+++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/LockedWidget.kt
@@ -16,26 +16,27 @@ import androidx.glance.text.TextAlign
import androidx.glance.text.TextStyle
import androidx.glance.unit.ColorProvider
import tachiyomi.core.Constants
-import tachiyomi.presentation.widget.ContainerModifier
import tachiyomi.presentation.widget.R
import tachiyomi.presentation.widget.util.stringResource
@Composable
-fun LockedWidget() {
+fun LockedWidget(
+ foreground: ColorProvider,
+ modifier: GlanceModifier = GlanceModifier,
+) {
val intent = Intent(LocalContext.current, Class.forName(Constants.MAIN_ACTIVITY)).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
Box(
- modifier = GlanceModifier
+ modifier = modifier
.clickable(actionStartActivity(intent))
- .then(ContainerModifier)
.padding(8.dp),
contentAlignment = Alignment.Center,
) {
Text(
text = stringResource(R.string.appwidget_unavailable_locked),
style = TextStyle(
- color = ColorProvider(R.color.appwidget_on_secondary_container),
+ color = foreground,
fontSize = 12.sp,
textAlign = TextAlign.Center,
),
diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/UpdatesWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/UpdatesWidget.kt
index f7ecd2e71..63a3b329d 100644
--- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/UpdatesWidget.kt
+++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/UpdatesWidget.kt
@@ -3,6 +3,7 @@ package tachiyomi.presentation.widget.components
import android.content.Intent
import android.graphics.Bitmap
import androidx.compose.runtime.Composable
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.glance.GlanceModifier
import androidx.glance.LocalContext
@@ -14,59 +15,75 @@ import androidx.glance.layout.Alignment
import androidx.glance.layout.Box
import androidx.glance.layout.Column
import androidx.glance.layout.Row
+import androidx.glance.layout.fillMaxHeight
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.text.Text
+import androidx.glance.text.TextStyle
+import androidx.glance.unit.ColorProvider
import tachiyomi.core.Constants
-import tachiyomi.presentation.widget.ContainerModifier
import tachiyomi.presentation.widget.R
import tachiyomi.presentation.widget.util.calculateRowAndColumnCount
import tachiyomi.presentation.widget.util.stringResource
@Composable
-fun UpdatesWidget(data: List>?) {
- val (rowCount, columnCount) = LocalSize.current.calculateRowAndColumnCount()
- Column(
- modifier = ContainerModifier,
- verticalAlignment = Alignment.CenterVertically,
- horizontalAlignment = Alignment.CenterHorizontally,
+fun UpdatesWidget(
+ data: List>?,
+ modifier: GlanceModifier = GlanceModifier,
+ contentColor: ColorProvider,
+ topPadding: Dp,
+ bottomPadding: Dp,
+) {
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = modifier,
) {
if (data == null) {
- CircularProgressIndicator()
+ CircularProgressIndicator(color = contentColor)
} else if (data.isEmpty()) {
- Text(text = stringResource(R.string.information_no_recent))
+ Text(
+ text = stringResource(R.string.information_no_recent),
+ style = TextStyle(color = contentColor),
+ )
} else {
- (0..
- val coverRow = (0..
- data.getOrNull(j + (i * columnCount))
- }
- if (coverRow.isNotEmpty()) {
- Row(
- modifier = GlanceModifier
- .padding(vertical = 4.dp)
- .fillMaxWidth(),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- coverRow.forEach { (mangaId, cover) ->
- Box(
- modifier = GlanceModifier
- .padding(horizontal = 3.dp),
- contentAlignment = Alignment.Center,
- ) {
- val intent = Intent(LocalContext.current, Class.forName(Constants.MAIN_ACTIVITY)).apply {
- action = Constants.SHORTCUT_MANGA
- putExtra(Constants.MANGA_EXTRA, mangaId)
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ val (rowCount, columnCount) = LocalSize.current.calculateRowAndColumnCount(topPadding, bottomPadding)
+ Column(
+ modifier = GlanceModifier.fillMaxHeight(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ (0..
+ val coverRow = (0..
+ data.getOrNull(j + (i * columnCount))
+ }
+ if (coverRow.isNotEmpty()) {
+ Row(
+ modifier = GlanceModifier
+ .padding(vertical = 4.dp)
+ .fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ coverRow.forEach { (mangaId, cover) ->
+ Box(
+ modifier = GlanceModifier
+ .padding(horizontal = 3.dp),
+ contentAlignment = Alignment.Center,
+ ) {
+ val intent = Intent(LocalContext.current, Class.forName(Constants.MAIN_ACTIVITY)).apply {
+ action = Constants.SHORTCUT_MANGA
+ putExtra(Constants.MANGA_EXTRA, mangaId)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
- // https://issuetracker.google.com/issues/238793260
- addCategory(mangaId.toString())
+ // https://issuetracker.google.com/issues/238793260
+ addCategory(mangaId.toString())
+ }
+ UpdatesMangaCover(
+ modifier = GlanceModifier.clickable(actionStartActivity(intent)),
+ cover = cover,
+ )
}
- UpdatesMangaCover(
- modifier = GlanceModifier.clickable(actionStartActivity(intent)),
- cover = cover,
- )
}
}
}
diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/util/GlanceUtils.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/util/GlanceUtils.kt
index fe8360a0e..51710a1be 100644
--- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/util/GlanceUtils.kt
+++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/util/GlanceUtils.kt
@@ -2,6 +2,7 @@ package tachiyomi.presentation.widget.util
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.glance.GlanceModifier
import androidx.glance.LocalContext
@@ -34,9 +35,13 @@ fun stringResource(@StringRes id: Int): String {
*
* @return pair of row and column count
*/
-fun DpSize.calculateRowAndColumnCount(): Pair {
+fun DpSize.calculateRowAndColumnCount(
+ topPadding: Dp,
+ bottomPadding: Dp,
+): Pair {
// Hack: Size provided by Glance manager is not reliable so take at least 1 row and 1 column
// Set max to 10 children each direction because of Glance limitation
+ val height = this.height - topPadding - bottomPadding
val rowCount = (height.value / 95).toInt().coerceIn(1, 10)
val columnCount = (width.value / 64).toInt().coerceIn(1, 10)
return Pair(rowCount, columnCount)
diff --git a/presentation-widget/src/main/res/drawable-nodpi/updates_grid_coverscreen_widget_preview.webp b/presentation-widget/src/main/res/drawable-nodpi/updates_grid_coverscreen_widget_preview.webp
new file mode 100644
index 000000000..b28fb91b9
Binary files /dev/null and b/presentation-widget/src/main/res/drawable-nodpi/updates_grid_coverscreen_widget_preview.webp differ
diff --git a/app/src/main/res/drawable-nodpi/updates_grid_widget_preview.webp b/presentation-widget/src/main/res/drawable-nodpi/updates_grid_widget_preview.webp
similarity index 100%
rename from app/src/main/res/drawable-nodpi/updates_grid_widget_preview.webp
rename to presentation-widget/src/main/res/drawable-nodpi/updates_grid_widget_preview.webp
diff --git a/presentation-widget/src/main/res/drawable/appwidget_coverscreen_background.xml b/presentation-widget/src/main/res/drawable/appwidget_coverscreen_background.xml
new file mode 100644
index 000000000..066a95b17
--- /dev/null
+++ b/presentation-widget/src/main/res/drawable/appwidget_coverscreen_background.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/presentation-widget/src/main/res/layout/appwidget_coverscreen_loading.xml b/presentation-widget/src/main/res/layout/appwidget_coverscreen_loading.xml
new file mode 100644
index 000000000..057df58f8
--- /dev/null
+++ b/presentation-widget/src/main/res/layout/appwidget_coverscreen_loading.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/presentation-widget/src/main/res/values/colors_appwidget.xml b/presentation-widget/src/main/res/values/colors_appwidget.xml
index 7d07ea1f8..a5526c94c 100644
--- a/presentation-widget/src/main/res/values/colors_appwidget.xml
+++ b/presentation-widget/src/main/res/values/colors_appwidget.xml
@@ -1,6 +1,7 @@
@color/tachiyomi_surface
+ #00000000
@color/tachiyomi_onSurface
@color/tachiyomi_surfaceVariant
@color/tachiyomi_onSurfaceVariant
diff --git a/app/src/main/res/xml/updates_grid_glance_widget_info.xml b/presentation-widget/src/main/res/xml/updates_grid_homescreen_widget_info.xml
similarity index 100%
rename from app/src/main/res/xml/updates_grid_glance_widget_info.xml
rename to presentation-widget/src/main/res/xml/updates_grid_homescreen_widget_info.xml
diff --git a/presentation-widget/src/main/res/xml/updates_grid_lockscreen_widget_info.xml b/presentation-widget/src/main/res/xml/updates_grid_lockscreen_widget_info.xml
new file mode 100644
index 000000000..62a91467c
--- /dev/null
+++ b/presentation-widget/src/main/res/xml/updates_grid_lockscreen_widget_info.xml
@@ -0,0 +1,7 @@
+
+
diff --git a/presentation-widget/src/main/res/xml/updates_grid_samsung_cover_widget_info.xml b/presentation-widget/src/main/res/xml/updates_grid_samsung_cover_widget_info.xml
new file mode 100644
index 000000000..5f20ff228
--- /dev/null
+++ b/presentation-widget/src/main/res/xml/updates_grid_samsung_cover_widget_info.xml
@@ -0,0 +1,4 @@
+
+