mirror of
https://github.com/null2264/yokai.git
synced 2025-06-20 18:24:42 +00:00
chore: Some more effort moving widget to its own module
This commit is contained in:
parent
79b5494307
commit
4a9a7813e0
34 changed files with 205 additions and 64 deletions
17
presentation/core/build.gradle.kts
Normal file
17
presentation/core/build.gradle.kts
Normal file
|
@ -0,0 +1,17 @@
|
|||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "yokai.presentation.core"
|
||||
|
||||
defaultConfig {
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(libs.material)
|
||||
}
|
0
presentation/core/consumer-rules.pro
Normal file
0
presentation/core/consumer-rules.pro
Normal file
21
presentation/core/proguard-rules.pro
vendored
Normal file
21
presentation/core/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
2
presentation/core/src/main/AndroidManifest.xml
Normal file
2
presentation/core/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest />
|
|
@ -0,0 +1,12 @@
|
|||
package yokai.presentation.core
|
||||
|
||||
object Constants {
|
||||
const val MAIN_ACTIVITY = "eu.kanade.tachiyomi.ui.main.MainActivity"
|
||||
const val SEARCH_ACTIVITY = "eu.kanade.tachiyomi.ui.main.SearchActivity"
|
||||
|
||||
const val SHORTCUT_RECENTS = "eu.kanade.tachiyomi.SHOW_RECENTS"
|
||||
const val SHORTCUT_MANGA = "eu.kanade.tachiyomi.SHOW_MANGA"
|
||||
const val SHORTCUT_MANGA_BACK = "eu.kanade.tachiyomi.SHOW_MANGA_BACK"
|
||||
|
||||
const val MANGA_EXTRA = "manga"
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package yokai.presentation.core.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import yokai.presentation.core.Constants
|
||||
|
||||
object IntentCommon {
|
||||
fun openManga(context: Context, id: Long?, canReturnToMain: Boolean = false) =
|
||||
Intent(context, Class.forName(Constants.SEARCH_ACTIVITY))
|
||||
.apply {
|
||||
action = if (canReturnToMain) Constants.SHORTCUT_MANGA_BACK else Constants.SHORTCUT_MANGA
|
||||
putExtra(Constants.MANGA_EXTRA, id)
|
||||
}
|
||||
}
|
4
presentation/core/src/main/res/values/colors.xml
Normal file
4
presentation/core/src/main/res/values/colors.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="cover_placeholder">#1F888888</color>
|
||||
</resources>
|
33
presentation/widget/build.gradle.kts
Normal file
33
presentation/widget/build.gradle.kts
Normal file
|
@ -0,0 +1,33 @@
|
|||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "yokai.presentation.widget"
|
||||
|
||||
defaultConfig {
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = compose.versions.compose.compiler.get()
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.core)
|
||||
implementation(projects.data)
|
||||
implementation(projects.domain)
|
||||
implementation(projects.i18n)
|
||||
implementation(projects.presentation.core)
|
||||
|
||||
implementation(androidx.glance.appwidget)
|
||||
|
||||
implementation(libs.coil3)
|
||||
}
|
0
presentation/widget/consumer-rules.pro
Normal file
0
presentation/widget/consumer-rules.pro
Normal file
21
presentation/widget/proguard-rules.pro
vendored
Normal file
21
presentation/widget/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
18
presentation/widget/src/main/AndroidManifest.xml
Normal file
18
presentation/widget/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application>
|
||||
<receiver
|
||||
android:name="eu.kanade.tachiyomi.appwidget.UpdatesGridGlanceReceiver"
|
||||
android:enabled="@bool/glance_appwidget_available"
|
||||
android:exported="false"
|
||||
android:label="@string/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>
|
||||
</application>
|
||||
</manifest>
|
|
@ -0,0 +1,14 @@
|
|||
package yokai.presentation.widget
|
||||
|
||||
import android.content.Context
|
||||
import androidx.glance.appwidget.GlanceAppWidgetManager
|
||||
|
||||
class TachiyomiWidgetManager {
|
||||
|
||||
suspend fun Context.init() {
|
||||
val manager = GlanceAppWidgetManager(this)
|
||||
if (manager.getGlanceIds(UpdatesGridGlanceWidget::class.java).isNotEmpty()) {
|
||||
UpdatesGridGlanceWidget().loadData()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package yokai.presentation.widget
|
||||
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
import androidx.glance.appwidget.GlanceAppWidgetReceiver
|
||||
|
||||
class UpdatesGridGlanceReceiver : GlanceAppWidgetReceiver() {
|
||||
override val glanceAppWidget: GlanceAppWidget = UpdatesGridGlanceWidget().apply { loadData() }
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package yokai.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.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.appwidget.updateAll
|
||||
import androidx.glance.background
|
||||
import androidx.glance.layout.fillMaxSize
|
||||
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.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.ui.recents.RecentsPresenter
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.launchIO
|
||||
import kotlinx.coroutines.MainScope
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import yokai.presentation.widget.components.CoverHeight
|
||||
import yokai.presentation.widget.components.CoverWidth
|
||||
import yokai.presentation.widget.components.LockedWidget
|
||||
import yokai.presentation.widget.components.UpdatesWidget
|
||||
import yokai.presentation.widget.util.appWidgetBackgroundRadius
|
||||
import yokai.presentation.widget.util.calculateRowAndColumnCount
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
// FIXME
|
||||
class UpdatesGridGlanceWidget(
|
||||
private val app: Application = Injekt.get(),
|
||||
//private val preferences: PreferencesHelper by injectLazy(),
|
||||
) : GlanceAppWidget() {
|
||||
private val coroutineScope = MainScope()
|
||||
|
||||
private var data: List<Pair<Long, Bitmap?>>? = null
|
||||
|
||||
override val sizeMode = SizeMode.Exact
|
||||
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
||||
provideContent {
|
||||
// If app lock enabled, don't do anything
|
||||
if (preferences.useBiometrics().get()) {
|
||||
LockedWidget()
|
||||
} else {
|
||||
UpdatesWidget(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadData(list: List<Pair<Manga, Long>>? = null) {
|
||||
coroutineScope.launchIO {
|
||||
// Don't show anything when lock is active
|
||||
if (preferences.useBiometrics().get()) {
|
||||
updateAll(app)
|
||||
return@launchIO
|
||||
}
|
||||
|
||||
val manager = GlanceAppWidgetManager(app)
|
||||
val ids = manager.getGlanceIds(this@UpdatesGridGlanceWidget::class.java)
|
||||
if (ids.isEmpty()) return@launchIO
|
||||
|
||||
val (rowCount, columnCount) = ids
|
||||
.flatMap { manager.getAppWidgetSizes(it) }
|
||||
.maxBy { it.height.value * it.width.value }
|
||||
.calculateRowAndColumnCount()
|
||||
val processList = list ?: RecentsPresenter.getRecentManga(customAmount = min(50, rowCount * columnCount))
|
||||
|
||||
data = prepareList(processList, rowCount * columnCount)
|
||||
ids.forEach { update(app, it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareList(processList: List<Pair<Manga, Long>>, 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.first.id }
|
||||
.sortedByDescending { it.second }
|
||||
.take(take)
|
||||
.map { it.first }
|
||||
.map { updatesView ->
|
||||
val request = ImageRequest.Builder(app)
|
||||
.data(updatesView)
|
||||
.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.id!!, app.imageLoader.executeBlocking(request).image?.asDrawable(app.resources)?.toBitmap())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DateLimit: Calendar
|
||||
get() = Calendar.getInstance().apply {
|
||||
time = Date()
|
||||
add(Calendar.MONTH, -3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val ContainerModifier = GlanceModifier
|
||||
.fillMaxSize()
|
||||
.background(ImageProvider(R.drawable.appwidget_background))
|
||||
.appWidgetBackground()
|
||||
.appWidgetBackgroundRadius()
|
|
@ -0,0 +1,48 @@
|
|||
package yokai.presentation.widget.components
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.LocalContext
|
||||
import androidx.glance.action.clickable
|
||||
import androidx.glance.appwidget.action.actionStartActivity
|
||||
import androidx.glance.layout.Alignment
|
||||
import androidx.glance.layout.Box
|
||||
import androidx.glance.layout.padding
|
||||
import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextAlign
|
||||
import androidx.glance.text.TextStyle
|
||||
import androidx.glance.unit.ColorProvider
|
||||
import yokai.i18n.MR
|
||||
import yokai.presentation.core.Constants
|
||||
import yokai.presentation.widget.ContainerModifier
|
||||
import yokai.presentation.widget.R
|
||||
import yokai.presentation.widget.util.stringResource
|
||||
|
||||
@Composable
|
||||
fun LockedWidget() {
|
||||
val context = LocalContext.current
|
||||
val clazz = Class.forName(Constants.MAIN_ACTIVITY)
|
||||
val intent = Intent(context, clazz).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
Box(
|
||||
modifier = GlanceModifier
|
||||
.clickable(actionStartActivity(intent))
|
||||
.then(ContainerModifier)
|
||||
.padding(8.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(MR.strings.appwidget_unavailable_locked),
|
||||
style = TextStyle(
|
||||
color = ColorProvider(Color(context.getColor(R.color.appwidget_on_secondary_container))),
|
||||
fontSize = 12.sp,
|
||||
textAlign = TextAlign.Center,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package yokai.presentation.widget.components
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.Image
|
||||
import androidx.glance.ImageProvider
|
||||
import androidx.glance.layout.Box
|
||||
import androidx.glance.layout.ContentScale
|
||||
import androidx.glance.layout.fillMaxSize
|
||||
import androidx.glance.layout.size
|
||||
import yokai.presentation.widget.R
|
||||
import yokai.presentation.widget.util.appWidgetInnerRadius
|
||||
|
||||
val CoverWidth = 58.dp
|
||||
val CoverHeight = 87.dp
|
||||
|
||||
@Composable
|
||||
fun UpdatesMangaCover(
|
||||
modifier: GlanceModifier = GlanceModifier,
|
||||
cover: Bitmap?,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(width = CoverWidth, height = CoverHeight)
|
||||
.appWidgetInnerRadius(),
|
||||
) {
|
||||
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_error),
|
||||
contentDescription = null,
|
||||
modifier = GlanceModifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package yokai.presentation.widget.components
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.LocalContext
|
||||
import androidx.glance.LocalSize
|
||||
import androidx.glance.action.clickable
|
||||
import androidx.glance.appwidget.CircularProgressIndicator
|
||||
import androidx.glance.appwidget.action.actionStartActivity
|
||||
import androidx.glance.layout.Alignment
|
||||
import androidx.glance.layout.Box
|
||||
import androidx.glance.layout.Column
|
||||
import androidx.glance.layout.Row
|
||||
import androidx.glance.layout.fillMaxWidth
|
||||
import androidx.glance.layout.padding
|
||||
import androidx.glance.text.Text
|
||||
import yokai.i18n.MR
|
||||
import yokai.presentation.core.Constants
|
||||
import yokai.presentation.core.util.IntentCommon
|
||||
import yokai.presentation.widget.ContainerModifier
|
||||
import yokai.presentation.widget.util.calculateRowAndColumnCount
|
||||
import yokai.presentation.widget.util.stringResource
|
||||
|
||||
@Composable
|
||||
fun UpdatesWidget(data: List<Pair<Long, Bitmap?>>?) {
|
||||
val (rowCount, columnCount) = LocalSize.current.calculateRowAndColumnCount()
|
||||
val clazz = Class.forName(Constants.MAIN_ACTIVITY)
|
||||
val mainIntent = Intent(LocalContext.current, clazz).setAction(Constants.SHORTCUT_RECENTS)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
Column(
|
||||
modifier = ContainerModifier.clickable(actionStartActivity(mainIntent)),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
if (data == null) {
|
||||
CircularProgressIndicator()
|
||||
} else if (data.isEmpty()) {
|
||||
Text(text = stringResource(MR.strings.no_recent_read_updated_manga))
|
||||
} else {
|
||||
(0 until rowCount).forEach { i ->
|
||||
val coverRow = (0 until columnCount).mapNotNull { j ->
|
||||
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 = IntentCommon.openManga(LocalContext.current, mangaId, true)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
// https://issuetracker.google.com/issues/238793260
|
||||
.addCategory(mangaId.toString())
|
||||
UpdatesMangaCover(
|
||||
modifier = GlanceModifier.clickable(actionStartActivity(intent)),
|
||||
cover = cover,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package yokai.presentation.widget.util
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.LocalContext
|
||||
import androidx.glance.appwidget.cornerRadius
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import yokai.presentation.widget.R
|
||||
import yokai.util.lang.getMString
|
||||
|
||||
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(id: StringResource): String {
|
||||
return LocalContext.current.getMString(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
fun DpSize.calculateRowAndColumnCount(): Pair<Int, Int> {
|
||||
// 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 rowCount = (height.value / 95).toInt().coerceIn(1, 10)
|
||||
val columnCount = (width.value / 64).toInt().coerceIn(1, 10)
|
||||
return Pair(rowCount, columnCount)
|
||||
}
|
|
@ -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,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/cover_placeholder" />
|
||||
<corners android:radius="@dimen/appwidget_inner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:top="24dp"
|
||||
android:bottom="24dp"
|
||||
android:left="24dp"
|
||||
android:right="24dp">
|
||||
<vector
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/cover_placeholder"
|
||||
android:pathData="M21,5v6.59l-2.29,-2.3c-0.39,-0.39 -1.03,-0.39 -1.42,0L14,12.59 10.71,9.3c-0.39,-0.39 -1.02,-0.39 -1.41,0L6,12.59 3,9.58L3,5c0,-1.1 0.9,-2 2,-2h14c1.1,0 2,0.9 2,2zM18,11.42l3,3.01L21,19c0,1.1 -0.9,2 -2,2L5,21c-1.1,0 -2,-0.9 -2,-2v-6.58l2.29,2.29c0.39,0.39 1.02,0.39 1.41,0l3.3,-3.3 3.29,3.29c0.39,0.39 1.02,0.39 1.41,0l3.3,-3.28z"/>
|
||||
</vector>
|
||||
</item>
|
||||
</layer-list>
|
9
presentation/widget/src/main/res/values-v31/colors.xml
Normal file
9
presentation/widget/src/main/res/values-v31/colors.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>
|
5
presentation/widget/src/main/res/values/dimens.xml
Normal file
5
presentation/widget/src/main/res/values/dimens.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="appwidget_background_radius">16dp</dimen>
|
||||
<dimen name="appwidget_inner_radius">12dp</dimen>
|
||||
</resources>
|
Loading…
Add table
Add a link
Reference in a new issue