From 816d7815e94f8b1286e17b1459c3a01a9aba7369 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Sat, 2 Sep 2023 20:37:25 +0700 Subject: [PATCH] "Updates" widget for Galaxy Z Flip5 cover screen (#9892) --- app/src/main/AndroidManifest.xml | 14 -- .../src/main/AndroidManifest.xml | 39 ++++- .../widget/BaseUpdatesGridGlanceWidget.kt | 153 ++++++++++++++++++ .../widget/TachiyomiWidgetManager.kt | 3 +- .../UpdatesGridCoverScreenGlanceReceiver.kt | 9 ++ .../UpdatesGridCoverScreenGlanceWidget.kt | 13 ++ .../widget/UpdatesGridGlanceReceiver.kt | 3 +- .../widget/UpdatesGridGlanceWidget.kt | 135 +--------------- .../widget/components/LockedWidget.kt | 11 +- .../widget/components/UpdatesWidget.kt | 93 ++++++----- .../presentation/widget/util/GlanceUtils.kt | 7 +- ...dates_grid_coverscreen_widget_preview.webp | Bin 0 -> 5200 bytes .../updates_grid_widget_preview.webp | Bin .../appwidget_coverscreen_background.xml | 5 + .../layout/appwidget_coverscreen_loading.xml | 14 ++ .../src/main/res/values/colors_appwidget.xml | 1 + .../updates_grid_homescreen_widget_info.xml | 0 .../updates_grid_lockscreen_widget_info.xml | 7 + ...updates_grid_samsung_cover_widget_info.xml | 4 + 19 files changed, 322 insertions(+), 189 deletions(-) create mode 100644 presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt create mode 100644 presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridCoverScreenGlanceReceiver.kt create mode 100644 presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridCoverScreenGlanceWidget.kt create mode 100644 presentation-widget/src/main/res/drawable-nodpi/updates_grid_coverscreen_widget_preview.webp rename {app => presentation-widget}/src/main/res/drawable-nodpi/updates_grid_widget_preview.webp (100%) create mode 100644 presentation-widget/src/main/res/drawable/appwidget_coverscreen_background.xml create mode 100644 presentation-widget/src/main/res/layout/appwidget_coverscreen_loading.xml rename app/src/main/res/xml/updates_grid_glance_widget_info.xml => presentation-widget/src/main/res/xml/updates_grid_homescreen_widget_info.xml (100%) create mode 100644 presentation-widget/src/main/res/xml/updates_grid_lockscreen_widget_info.xml create mode 100644 presentation-widget/src/main/res/xml/updates_grid_samsung_cover_widget_info.xml 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 0000000000000000000000000000000000000000..b28fb91b9a34e2b9eadbec41cb593ca7d7a93204 GIT binary patch literal 5200 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 + + + 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 @@ + +