Files
mihon/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt
renovate[bot] c0f9de88e7 fix(deps): update dependency io.coil-kt.coil3:coil-bom to v3.0.0-alpha07 (#960)
* fix(deps): update dependency io.coil-kt.coil3:coil-bom to v3.0.0-alpha07

* Fix build

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
2024-06-30 01:44:17 +06:00

162 lines
6.2 KiB
Kotlin

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 coil3.annotation.ExperimentalCoilApi
import coil3.asDrawable
import coil3.executeBlocking
import coil3.imageLoader
import coil3.request.CachePolicy
import coil3.request.ImageRequest
import coil3.request.transformations
import coil3.size.Precision
import coil3.size.Scale
import coil3.transform.RoundedCornersTransformation
import eu.kanade.tachiyomi.core.security.SecurityPreferences
import eu.kanade.tachiyomi.util.system.dpToPx
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.map
import tachiyomi.core.common.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.time.Instant
import java.time.ZonedDateTime
abstract class BaseUpdatesGridGlanceWidget(
private val context: Context = Injekt.get<Application>(),
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.toEpochMilli())
.map { rawData ->
rawData.prepareData(rowCount, columnCount)
}
}
val data by flow.collectAsState(initial = null)
UpdatesWidget(
data = data,
contentColor = foreground,
topPadding = topPadding,
bottomPadding = bottomPadding,
modifier = containerModifier,
)
}
}
@OptIn(ExperimentalCoilApi::class)
private suspend fun List<UpdatesWithRelations>.prepareData(
rowCount: Int,
columnCount: Int,
): ImmutableList<Pair<Long, Bitmap?>> {
// 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()
val bitmap = context.imageLoader.executeBlocking(request)
.image
?.asDrawable(context.resources)
?.toBitmap()
Pair(updatesView.mangaId, bitmap)
}
.toImmutableList()
}
}
companion object {
val DateLimit: Instant
get() = ZonedDateTime.now().minusMonths(3).toInstant()
}
}