mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	@@ -189,6 +189,7 @@ dependencies {
 | 
			
		||||
    implementation(androidx.splashscreen)
 | 
			
		||||
    implementation(androidx.recyclerview)
 | 
			
		||||
    implementation(androidx.viewpager)
 | 
			
		||||
    implementation(androidx.glance)
 | 
			
		||||
 | 
			
		||||
    implementation(androidx.bundles.lifecycle)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -167,6 +167,20 @@
 | 
			
		||||
            android:name=".data.notification.NotificationReceiver"
 | 
			
		||||
            android:exported="false" />
 | 
			
		||||
 | 
			
		||||
        <receiver
 | 
			
		||||
            android:name=".glance.UpdatesGridGlanceReceiver"
 | 
			
		||||
            android:enabled="@bool/glance_appwidget_available"
 | 
			
		||||
            android:exported="false"
 | 
			
		||||
            android:label="@string/label_recent_updates">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
            <meta-data
 | 
			
		||||
                android:name="android.appwidget.provider"
 | 
			
		||||
                android:resource="@xml/updates_grid_glance_widget_info" />
 | 
			
		||||
        </receiver>
 | 
			
		||||
 | 
			
		||||
        <service
 | 
			
		||||
            android:name=".data.library.LibraryUpdateService"
 | 
			
		||||
            android:exported="false" />
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ import android.webkit.WebView
 | 
			
		||||
import androidx.appcompat.app.AppCompatDelegate
 | 
			
		||||
import androidx.core.app.NotificationManagerCompat
 | 
			
		||||
import androidx.core.content.getSystemService
 | 
			
		||||
import androidx.glance.appwidget.GlanceAppWidgetManager
 | 
			
		||||
import androidx.lifecycle.DefaultLifecycleObserver
 | 
			
		||||
import androidx.lifecycle.LifecycleOwner
 | 
			
		||||
import androidx.lifecycle.ProcessLifecycleOwner
 | 
			
		||||
@@ -24,6 +25,7 @@ import coil.decode.GifDecoder
 | 
			
		||||
import coil.decode.ImageDecoderDecoder
 | 
			
		||||
import coil.disk.DiskCache
 | 
			
		||||
import coil.util.DebugLogger
 | 
			
		||||
import eu.kanade.data.DatabaseHandler
 | 
			
		||||
import eu.kanade.domain.DomainModule
 | 
			
		||||
import eu.kanade.tachiyomi.data.coil.DomainMangaKeyer
 | 
			
		||||
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
 | 
			
		||||
@@ -33,6 +35,7 @@ import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder
 | 
			
		||||
import eu.kanade.tachiyomi.data.notification.Notifications
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.glance.UpdatesGridGlanceWidget
 | 
			
		||||
import eu.kanade.tachiyomi.network.NetworkHelper
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
 | 
			
		||||
import eu.kanade.tachiyomi.util.preference.asHotFlow
 | 
			
		||||
@@ -42,6 +45,8 @@ import eu.kanade.tachiyomi.util.system.animatorDurationScale
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.isDevFlavor
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.logcat
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.notification
 | 
			
		||||
import kotlinx.coroutines.flow.distinctUntilChanged
 | 
			
		||||
import kotlinx.coroutines.flow.drop
 | 
			
		||||
import kotlinx.coroutines.flow.launchIn
 | 
			
		||||
import kotlinx.coroutines.flow.onEach
 | 
			
		||||
import logcat.AndroidLogcatLogger
 | 
			
		||||
@@ -125,6 +130,19 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
 | 
			
		||||
                )
 | 
			
		||||
            }.launchIn(ProcessLifecycleOwner.get().lifecycleScope)
 | 
			
		||||
 | 
			
		||||
        // Updates widget update
 | 
			
		||||
        Injekt.get<DatabaseHandler>()
 | 
			
		||||
            .subscribeToList { updatesViewQueries.updates(after = UpdatesGridGlanceWidget.DateLimit.timeInMillis) }
 | 
			
		||||
            .drop(1)
 | 
			
		||||
            .distinctUntilChanged()
 | 
			
		||||
            .onEach {
 | 
			
		||||
                val manager = GlanceAppWidgetManager(this)
 | 
			
		||||
                if (manager.getGlanceIds(UpdatesGridGlanceWidget::class.java).isNotEmpty()) {
 | 
			
		||||
                    UpdatesGridGlanceWidget().loadData(it)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            .launchIn(ProcessLifecycleOwner.get().lifecycleScope)
 | 
			
		||||
 | 
			
		||||
        if (!LogcatLogger.isInstalled && preferences.verboseLogging()) {
 | 
			
		||||
            LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE))
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								app/src/main/java/eu/kanade/tachiyomi/glance/GlanceUtils.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								app/src/main/java/eu/kanade/tachiyomi/glance/GlanceUtils.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
package eu.kanade.tachiyomi.glance
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.StringRes
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.glance.GlanceModifier
 | 
			
		||||
import androidx.glance.LocalContext
 | 
			
		||||
import androidx.glance.appwidget.cornerRadius
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
 | 
			
		||||
fun GlanceModifier.appWidgetBackgroundRadius(): GlanceModifier {
 | 
			
		||||
    return this.cornerRadius(R.dimen.appwidget_background_radius)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun GlanceModifier.appWidgetInnerRadius(): GlanceModifier {
 | 
			
		||||
    return this.cornerRadius(R.dimen.appwidget_inner_radius)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun stringResource(@StringRes id: Int): String {
 | 
			
		||||
    return LocalContext.current.getString(id)
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,8 @@
 | 
			
		||||
package eu.kanade.tachiyomi.glance
 | 
			
		||||
 | 
			
		||||
import androidx.glance.appwidget.GlanceAppWidget
 | 
			
		||||
import androidx.glance.appwidget.GlanceAppWidgetReceiver
 | 
			
		||||
 | 
			
		||||
class UpdatesGridGlanceReceiver : GlanceAppWidgetReceiver() {
 | 
			
		||||
    override val glanceAppWidget: GlanceAppWidget = UpdatesGridGlanceWidget().apply { loadData() }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,287 @@
 | 
			
		||||
package eu.kanade.tachiyomi.glance
 | 
			
		||||
 | 
			
		||||
import android.app.Application
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.graphics.Bitmap
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.unit.DpSize
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import androidx.compose.ui.unit.sp
 | 
			
		||||
import androidx.core.graphics.drawable.toBitmap
 | 
			
		||||
import androidx.glance.GlanceModifier
 | 
			
		||||
import androidx.glance.Image
 | 
			
		||||
import androidx.glance.ImageProvider
 | 
			
		||||
import androidx.glance.LocalContext
 | 
			
		||||
import androidx.glance.LocalSize
 | 
			
		||||
import androidx.glance.action.clickable
 | 
			
		||||
import androidx.glance.appwidget.CircularProgressIndicator
 | 
			
		||||
import androidx.glance.appwidget.GlanceAppWidget
 | 
			
		||||
import androidx.glance.appwidget.GlanceAppWidgetManager
 | 
			
		||||
import androidx.glance.appwidget.SizeMode
 | 
			
		||||
import androidx.glance.appwidget.action.actionStartActivity
 | 
			
		||||
import androidx.glance.appwidget.appWidgetBackground
 | 
			
		||||
import androidx.glance.appwidget.updateAll
 | 
			
		||||
import androidx.glance.background
 | 
			
		||||
import androidx.glance.layout.Alignment
 | 
			
		||||
import androidx.glance.layout.Box
 | 
			
		||||
import androidx.glance.layout.Column
 | 
			
		||||
import androidx.glance.layout.ContentScale
 | 
			
		||||
import androidx.glance.layout.Row
 | 
			
		||||
import androidx.glance.layout.fillMaxSize
 | 
			
		||||
import androidx.glance.layout.fillMaxWidth
 | 
			
		||||
import androidx.glance.layout.padding
 | 
			
		||||
import androidx.glance.layout.size
 | 
			
		||||
import androidx.glance.text.Text
 | 
			
		||||
import androidx.glance.text.TextAlign
 | 
			
		||||
import androidx.glance.text.TextStyle
 | 
			
		||||
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.data.DatabaseHandler
 | 
			
		||||
import eu.kanade.domain.manga.model.MangaCover
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.ui.main.MainActivity
 | 
			
		||||
import eu.kanade.tachiyomi.ui.manga.MangaController
 | 
			
		||||
import eu.kanade.tachiyomi.util.lang.launchIO
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.dpToPx
 | 
			
		||||
import kotlinx.coroutines.MainScope
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import view.UpdatesView
 | 
			
		||||
import java.util.Calendar
 | 
			
		||||
import java.util.Date
 | 
			
		||||
 | 
			
		||||
class UpdatesGridGlanceWidget : GlanceAppWidget() {
 | 
			
		||||
    private val app: Application by injectLazy()
 | 
			
		||||
    private val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val coroutineScope = MainScope()
 | 
			
		||||
 | 
			
		||||
    var data: List<Pair<Long, Bitmap?>>? = null
 | 
			
		||||
 | 
			
		||||
    override val sizeMode = SizeMode.Exact
 | 
			
		||||
 | 
			
		||||
    @Composable
 | 
			
		||||
    override fun Content() {
 | 
			
		||||
        // App lock enabled, don't do anything
 | 
			
		||||
        if (preferences.useAuthenticator().get()) {
 | 
			
		||||
            WidgetNotAvailable()
 | 
			
		||||
        } else {
 | 
			
		||||
            UpdatesWidget()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Composable
 | 
			
		||||
    private fun WidgetNotAvailable() {
 | 
			
		||||
        val intent = Intent(LocalContext.current, MainActivity::class.java).apply {
 | 
			
		||||
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
 | 
			
		||||
        }
 | 
			
		||||
        Box(
 | 
			
		||||
            modifier = GlanceModifier
 | 
			
		||||
                .clickable(actionStartActivity(intent))
 | 
			
		||||
                .then(ContainerModifier)
 | 
			
		||||
                .padding(8.dp),
 | 
			
		||||
            contentAlignment = Alignment.Center,
 | 
			
		||||
        ) {
 | 
			
		||||
            Text(
 | 
			
		||||
                text = stringResource(id = R.string.appwidget_unavailable_locked),
 | 
			
		||||
                style = TextStyle(
 | 
			
		||||
                    color = ColorProvider(R.color.appwidget_on_secondary_container),
 | 
			
		||||
                    fontSize = 12.sp,
 | 
			
		||||
                    textAlign = TextAlign.Center,
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Composable
 | 
			
		||||
    private fun UpdatesWidget() {
 | 
			
		||||
        val (rowCount, columnCount) = LocalSize.current.calculateRowAndColumnCount()
 | 
			
		||||
        Column(
 | 
			
		||||
            modifier = ContainerModifier,
 | 
			
		||||
            verticalAlignment = Alignment.CenterVertically,
 | 
			
		||||
            horizontalAlignment = Alignment.CenterHorizontally,
 | 
			
		||||
        ) {
 | 
			
		||||
            val inData = data
 | 
			
		||||
            if (inData == null) {
 | 
			
		||||
                CircularProgressIndicator()
 | 
			
		||||
            } else if (inData.isEmpty()) {
 | 
			
		||||
                Text(text = stringResource(id = R.string.information_no_recent))
 | 
			
		||||
            } else {
 | 
			
		||||
                (0 until rowCount).forEach { i ->
 | 
			
		||||
                    val coverRow = (0 until columnCount).mapNotNull { j ->
 | 
			
		||||
                        inData.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, MainActivity::class.java).apply {
 | 
			
		||||
                                        action = MainActivity.SHORTCUT_MANGA
 | 
			
		||||
                                        putExtra(MangaController.MANGA_EXTRA, mangaId)
 | 
			
		||||
                                        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
 | 
			
		||||
                                        addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
 | 
			
		||||
 | 
			
		||||
                                        // https://issuetracker.google.com/issues/238793260
 | 
			
		||||
                                        addCategory(mangaId.toString())
 | 
			
		||||
                                    }
 | 
			
		||||
                                    Cover(
 | 
			
		||||
                                        modifier = GlanceModifier.clickable(actionStartActivity(intent)),
 | 
			
		||||
                                        cover = cover,
 | 
			
		||||
                                    )
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Composable
 | 
			
		||||
    private fun Cover(
 | 
			
		||||
        modifier: GlanceModifier = GlanceModifier,
 | 
			
		||||
        cover: Bitmap?,
 | 
			
		||||
    ) {
 | 
			
		||||
        Box(
 | 
			
		||||
            modifier = modifier
 | 
			
		||||
                .size(width = CoverWidth, height = CoverHeight)
 | 
			
		||||
                .appWidgetInnerRadius()
 | 
			
		||||
                .background(ColorProvider(R.color.appwidget_surface_variant)),
 | 
			
		||||
        ) {
 | 
			
		||||
            if (cover != null) {
 | 
			
		||||
                Image(
 | 
			
		||||
                    provider = ImageProvider(cover),
 | 
			
		||||
                    contentDescription = null,
 | 
			
		||||
                    modifier = GlanceModifier
 | 
			
		||||
                        .fillMaxSize()
 | 
			
		||||
                        .appWidgetInnerRadius(),
 | 
			
		||||
                    contentScale = ContentScale.Crop,
 | 
			
		||||
                )
 | 
			
		||||
            } else {
 | 
			
		||||
                // Enjoy placeholder
 | 
			
		||||
                Image(
 | 
			
		||||
                    provider = ImageProvider(R.drawable.appwidget_cover_placeholder),
 | 
			
		||||
                    contentDescription = null,
 | 
			
		||||
                    modifier = GlanceModifier
 | 
			
		||||
                        .fillMaxSize()
 | 
			
		||||
                        .padding(4.dp),
 | 
			
		||||
                    contentScale = ContentScale.Crop,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun loadData(list: List<UpdatesView>? = null) {
 | 
			
		||||
        coroutineScope.launchIO {
 | 
			
		||||
            // Don't show anything when lock is active
 | 
			
		||||
            if (preferences.useAuthenticator().get()) {
 | 
			
		||||
                updateAll(app)
 | 
			
		||||
                return@launchIO
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val manager = GlanceAppWidgetManager(app)
 | 
			
		||||
            val ids = manager.getGlanceIds(this@UpdatesGridGlanceWidget::class.java)
 | 
			
		||||
            if (ids.isEmpty()) return@launchIO
 | 
			
		||||
 | 
			
		||||
            val processList = list
 | 
			
		||||
                ?: Injekt.get<DatabaseHandler>()
 | 
			
		||||
                    .awaitList { updatesViewQueries.updates(after = DateLimit.timeInMillis) }
 | 
			
		||||
            val (rowCount, columnCount) = ids
 | 
			
		||||
                .flatMap { manager.getAppWidgetSizes(it) }
 | 
			
		||||
                .maxBy { it.height.value * it.width.value }
 | 
			
		||||
                .calculateRowAndColumnCount()
 | 
			
		||||
 | 
			
		||||
            data = prepareList(processList, rowCount * columnCount)
 | 
			
		||||
            ids.forEach { update(app, it) }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun prepareList(processList: List<UpdatesView>, take: Int): List<Pair<Long, Bitmap?>> {
 | 
			
		||||
        // Resize to cover size
 | 
			
		||||
        val widthPx = CoverWidth.value.toInt().dpToPx
 | 
			
		||||
        val heightPx = CoverHeight.value.toInt().dpToPx
 | 
			
		||||
        val roundPx = app.resources.getDimension(R.dimen.appwidget_inner_radius)
 | 
			
		||||
        return processList
 | 
			
		||||
            .distinctBy { it.mangaId }
 | 
			
		||||
            .take(take)
 | 
			
		||||
            .map { updatesView ->
 | 
			
		||||
                val request = ImageRequest.Builder(app)
 | 
			
		||||
                    .data(
 | 
			
		||||
                        MangaCover(
 | 
			
		||||
                            mangaId = updatesView.mangaId,
 | 
			
		||||
                            sourceId = updatesView.source,
 | 
			
		||||
                            isMangaFavorite = updatesView.favorite,
 | 
			
		||||
                            url = updatesView.thumbnailUrl,
 | 
			
		||||
                            lastModified = updatesView.coverLastModified,
 | 
			
		||||
                        ),
 | 
			
		||||
                    )
 | 
			
		||||
                    .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, app.imageLoader.executeBlocking(request).drawable?.toBitmap())
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val DateLimit: Calendar
 | 
			
		||||
            get() = Calendar.getInstance().apply {
 | 
			
		||||
                time = Date()
 | 
			
		||||
                add(Calendar.MONTH, -3)
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private val CoverWidth = 58.dp
 | 
			
		||||
private val CoverHeight = 87.dp
 | 
			
		||||
 | 
			
		||||
private val ContainerModifier = GlanceModifier
 | 
			
		||||
    .fillMaxSize()
 | 
			
		||||
    .background(ImageProvider(R.drawable.appwidget_background))
 | 
			
		||||
    .appWidgetBackground()
 | 
			
		||||
    .appWidgetBackgroundRadius()
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Calculates row-column count.
 | 
			
		||||
 *
 | 
			
		||||
 * Row
 | 
			
		||||
 * Numerator: Container height - container vertical padding
 | 
			
		||||
 * Denominator: Cover height + cover vertical padding
 | 
			
		||||
 *
 | 
			
		||||
 * Column
 | 
			
		||||
 * Numerator: Container width - container horizontal padding
 | 
			
		||||
 * Denominator: Cover width + cover horizontal padding
 | 
			
		||||
 *
 | 
			
		||||
 * @return pair of row and column count
 | 
			
		||||
 */
 | 
			
		||||
private fun DpSize.calculateRowAndColumnCount(): Pair<Int, Int> {
 | 
			
		||||
    // Hack: Size provided by Glance manager is not reliable so take at least 1 row and 1 column
 | 
			
		||||
    val rowCount = (height.value / 95).toInt().coerceAtLeast(1)
 | 
			
		||||
    val columnCount = (width.value / 64).toInt().coerceAtLeast(1)
 | 
			
		||||
    return Pair(rowCount, columnCount)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-nodpi/updates_grid_widget_preview.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable-nodpi/updates_grid_widget_preview.webp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 410 KiB  | 
							
								
								
									
										6
									
								
								app/src/main/res/drawable/appwidget_background.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								app/src/main/res/drawable/appwidget_background.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:shape="rectangle">
 | 
			
		||||
    <solid android:color="@color/appwidget_secondary_container" />
 | 
			
		||||
    <corners android:radius="@dimen/appwidget_background_radius" />
 | 
			
		||||
</shape>
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportWidth="24"
 | 
			
		||||
    android:viewportHeight="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="@color/appwidget_background"
 | 
			
		||||
        android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z" />
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										14
									
								
								app/src/main/res/layout/appwidget_loading.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/src/main/res/layout/appwidget_loading.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="match_parent"
 | 
			
		||||
    android:background="@drawable/appwidget_background">
 | 
			
		||||
 | 
			
		||||
    <TextView
 | 
			
		||||
        android:layout_width="wrap_content"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_gravity="center"
 | 
			
		||||
        android:text="@string/loading"
 | 
			
		||||
        android:textColor="?android:attr/textColorPrimary" />
 | 
			
		||||
 | 
			
		||||
</FrameLayout>
 | 
			
		||||
							
								
								
									
										9
									
								
								app/src/main/res/values-night-v31/colors_appwidget.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/src/main/res/values-night-v31/colors_appwidget.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
    <color name="appwidget_background">@color/m3_sys_color_dynamic_dark_surface</color>
 | 
			
		||||
    <color name="appwidget_on_background">@color/m3_sys_color_dynamic_dark_on_surface</color>
 | 
			
		||||
    <color name="appwidget_surface_variant">@color/m3_sys_color_dynamic_dark_surface_variant</color>
 | 
			
		||||
    <color name="appwidget_on_surface_variant">@color/m3_sys_color_dynamic_dark_on_surface_variant</color>
 | 
			
		||||
    <color name="appwidget_secondary_container">@color/m3_sys_color_dynamic_dark_secondary_container</color>
 | 
			
		||||
    <color name="appwidget_on_secondary_container">@color/m3_sys_color_dynamic_dark_on_secondary_container</color>
 | 
			
		||||
</resources>
 | 
			
		||||
							
								
								
									
										9
									
								
								app/src/main/res/values-v31/colors_appwidget.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/src/main/res/values-v31/colors_appwidget.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
    <color name="appwidget_background">@color/m3_sys_color_dynamic_light_surface</color>
 | 
			
		||||
    <color name="appwidget_on_background">@color/m3_sys_color_dynamic_light_on_surface</color>
 | 
			
		||||
    <color name="appwidget_surface_variant">@color/m3_sys_color_dynamic_light_surface_variant</color>
 | 
			
		||||
    <color name="appwidget_on_surface_variant">@color/m3_sys_color_dynamic_light_on_surface_variant</color>
 | 
			
		||||
    <color name="appwidget_secondary_container">@color/m3_sys_color_dynamic_light_secondary_container</color>
 | 
			
		||||
    <color name="appwidget_on_secondary_container">@color/m3_sys_color_dynamic_light_on_secondary_container</color>
 | 
			
		||||
</resources>
 | 
			
		||||
							
								
								
									
										3
									
								
								app/src/main/res/values-v31/dimens.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/src/main/res/values-v31/dimens.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
<resources>
 | 
			
		||||
    <dimen name="appwidget_background_radius">@android:dimen/system_app_widget_background_radius</dimen>
 | 
			
		||||
</resources>
 | 
			
		||||
							
								
								
									
										9
									
								
								app/src/main/res/values/colors_appwidget.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/src/main/res/values/colors_appwidget.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
    <color name="appwidget_background">@color/tachiyomi_surface</color>
 | 
			
		||||
    <color name="appwidget_on_background">@color/tachiyomi_onSurface</color>
 | 
			
		||||
    <color name="appwidget_surface_variant">@color/tachiyomi_surfaceVariant</color>
 | 
			
		||||
    <color name="appwidget_on_surface_variant">@color/tachiyomi_onSurfaceVariant</color>
 | 
			
		||||
    <color name="appwidget_secondary_container">@color/tachiyomi_secondaryContainer</color>
 | 
			
		||||
    <color name="appwidget_on_secondary_container">@color/tachiyomi_onSecondaryContainer</color>
 | 
			
		||||
</resources>
 | 
			
		||||
@@ -15,4 +15,7 @@
 | 
			
		||||
 | 
			
		||||
    <dimen name="tablet_horizontal_cover_margin">128dp</dimen>
 | 
			
		||||
    <dimen name="tablet_sidebar_max_width">450dp</dimen>
 | 
			
		||||
 | 
			
		||||
    <dimen name="appwidget_background_radius">16dp</dimen>
 | 
			
		||||
    <dimen name="appwidget_inner_radius">12dp</dimen>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -853,4 +853,8 @@
 | 
			
		||||
    <!-- S Pen actions -->
 | 
			
		||||
    <string name="spen_previous_page">Previous page</string>
 | 
			
		||||
    <string name="spen_next_page">Next page</string>
 | 
			
		||||
 | 
			
		||||
    <!-- App widget -->
 | 
			
		||||
    <string name="appwidget_updates_description">See your recently updated manga</string>
 | 
			
		||||
    <string name="appwidget_unavailable_locked">Widget not available when app lock is enabled</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								app/src/main/res/xml/updates_grid_glance_widget_info.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								app/src/main/res/xml/updates_grid_glance_widget_info.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:description="@string/appwidget_updates_description"
 | 
			
		||||
    android:previewImage="@drawable/updates_grid_widget_preview"
 | 
			
		||||
    android:initialLayout="@layout/appwidget_loading"
 | 
			
		||||
    android:minWidth="240dp"
 | 
			
		||||
    android:minHeight="80dp"
 | 
			
		||||
    android:minResizeWidth="80dp"
 | 
			
		||||
    android:minResizeHeight="110dp"
 | 
			
		||||
    android:maxResizeWidth="600dp"
 | 
			
		||||
    android:maxResizeHeight="600dp"
 | 
			
		||||
    android:targetCellWidth="4"
 | 
			
		||||
    android:targetCellHeight="2"
 | 
			
		||||
    android:resizeMode="horizontal|vertical"
 | 
			
		||||
    android:widgetCategory="home_screen" />
 | 
			
		||||
@@ -12,6 +12,7 @@ corektx = "androidx.core:core-ktx:1.8.0"
 | 
			
		||||
splashscreen = "androidx.core:core-splashscreen:1.0.0-alpha02"
 | 
			
		||||
recyclerview = "androidx.recyclerview:recyclerview:1.3.0-beta01"
 | 
			
		||||
viewpager = "androidx.viewpager:viewpager:1.1.0-alpha01"
 | 
			
		||||
glance = "androidx.glance:glance-appwidget:1.0.0-alpha03"
 | 
			
		||||
 | 
			
		||||
lifecycle-common = { module = "androidx.lifecycle:lifecycle-common", version.ref = "lifecycle_version" }
 | 
			
		||||
lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle_version" }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user