parent
4efb736e56
commit
29e1976b90
@ -189,6 +189,7 @@ dependencies {
|
|||||||
implementation(androidx.splashscreen)
|
implementation(androidx.splashscreen)
|
||||||
implementation(androidx.recyclerview)
|
implementation(androidx.recyclerview)
|
||||||
implementation(androidx.viewpager)
|
implementation(androidx.viewpager)
|
||||||
|
implementation(androidx.glance)
|
||||||
|
|
||||||
implementation(androidx.bundles.lifecycle)
|
implementation(androidx.bundles.lifecycle)
|
||||||
|
|
||||||
|
@ -167,6 +167,20 @@
|
|||||||
android:name=".data.notification.NotificationReceiver"
|
android:name=".data.notification.NotificationReceiver"
|
||||||
android:exported="false" />
|
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
|
<service
|
||||||
android:name=".data.library.LibraryUpdateService"
|
android:name=".data.library.LibraryUpdateService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
@ -14,6 +14,7 @@ import android.webkit.WebView
|
|||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
|
import androidx.glance.appwidget.GlanceAppWidgetManager
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
@ -24,6 +25,7 @@ import coil.decode.GifDecoder
|
|||||||
import coil.decode.ImageDecoderDecoder
|
import coil.decode.ImageDecoderDecoder
|
||||||
import coil.disk.DiskCache
|
import coil.disk.DiskCache
|
||||||
import coil.util.DebugLogger
|
import coil.util.DebugLogger
|
||||||
|
import eu.kanade.data.DatabaseHandler
|
||||||
import eu.kanade.domain.DomainModule
|
import eu.kanade.domain.DomainModule
|
||||||
import eu.kanade.tachiyomi.data.coil.DomainMangaKeyer
|
import eu.kanade.tachiyomi.data.coil.DomainMangaKeyer
|
||||||
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
|
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.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.glance.UpdatesGridGlanceWidget
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
|
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.preference.asHotFlow
|
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.isDevFlavor
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
import eu.kanade.tachiyomi.util.system.notification
|
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.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import logcat.AndroidLogcatLogger
|
import logcat.AndroidLogcatLogger
|
||||||
@ -125,6 +130,19 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
|||||||
)
|
)
|
||||||
}.launchIn(ProcessLifecycleOwner.get().lifecycleScope)
|
}.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()) {
|
if (!LogcatLogger.isInstalled && preferences.verboseLogging()) {
|
||||||
LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE))
|
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_horizontal_cover_margin">128dp</dimen>
|
||||||
<dimen name="tablet_sidebar_max_width">450dp</dimen>
|
<dimen name="tablet_sidebar_max_width">450dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="appwidget_background_radius">16dp</dimen>
|
||||||
|
<dimen name="appwidget_inner_radius">12dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -853,4 +853,8 @@
|
|||||||
<!-- S Pen actions -->
|
<!-- S Pen actions -->
|
||||||
<string name="spen_previous_page">Previous page</string>
|
<string name="spen_previous_page">Previous page</string>
|
||||||
<string name="spen_next_page">Next 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>
|
</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"
|
splashscreen = "androidx.core:core-splashscreen:1.0.0-alpha02"
|
||||||
recyclerview = "androidx.recyclerview:recyclerview:1.3.0-beta01"
|
recyclerview = "androidx.recyclerview:recyclerview:1.3.0-beta01"
|
||||||
viewpager = "androidx.viewpager:viewpager:1.1.0-alpha01"
|
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-common = { module = "androidx.lifecycle:lifecycle-common", version.ref = "lifecycle_version" }
|
||||||
lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle_version" }
|
lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle_version" }
|
||||||
|
Loading…
Reference in New Issue
Block a user