Add option to use theme colors in app widgets and use light background variant when appropriate
This commit is contained in:
parent
afbe84fcf9
commit
7056c5963a
@ -2,7 +2,6 @@ package de.mm20.launcher2.ui.launcher.sheets
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.ActivityOptions
|
||||
import android.appwidget.AppWidgetHost
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProviderInfo
|
||||
import android.content.Intent
|
||||
@ -41,6 +40,7 @@ import androidx.compose.material.icons.rounded.OpenInNew
|
||||
import androidx.compose.material.icons.rounded.UnfoldMore
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
@ -87,7 +87,9 @@ import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.component.preferences.CheckboxPreference
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||
import de.mm20.launcher2.ui.launcher.widgets.external.ExternalWidget
|
||||
import de.mm20.launcher2.ui.launcher.widgets.external.AppWidgetHost
|
||||
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
||||
import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper
|
||||
import de.mm20.launcher2.ui.settings.SettingsActivity
|
||||
import de.mm20.launcher2.widgets.AppWidget
|
||||
import de.mm20.launcher2.widgets.CalendarWidget
|
||||
@ -344,12 +346,14 @@ fun ColumnScope.ConfigureAppWidget(
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
) {
|
||||
ExternalWidget(
|
||||
AppWidgetHost(
|
||||
widgetInfo = widgetInfo,
|
||||
widgetId = widget.config.widgetId,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
height = widget.config.height + dragDelta,
|
||||
borderless = widget.config.borderless,
|
||||
useThemeColors = widget.config.themeColors,
|
||||
onLightBackground = (!LocalDarkTheme.current && widget.config.background) || LocalPreferDarkContentOverWallpaper.current
|
||||
)
|
||||
}
|
||||
val density = LocalDensity.current
|
||||
@ -422,7 +426,7 @@ fun ColumnScope.ConfigureAppWidget(
|
||||
onValueChange = {
|
||||
val intValue = it.toIntOrNull()
|
||||
if (intValue == null) textFieldValue = ""
|
||||
else if (intValue in 1..500) {
|
||||
else if (intValue in 1..1000) {
|
||||
onWidgetUpdated(
|
||||
widget.copy(
|
||||
config = widget.config.copy(
|
||||
@ -458,7 +462,7 @@ fun ColumnScope.ConfigureAppWidget(
|
||||
onWidgetUpdated(widget.copy(config = widget.config.copy(borderless = it)))
|
||||
}
|
||||
)
|
||||
Divider()
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.widget_config_appwidget_background),
|
||||
iconPadding = false,
|
||||
@ -467,6 +471,17 @@ fun ColumnScope.ConfigureAppWidget(
|
||||
onWidgetUpdated(widget.copy(config = widget.config.copy(background = it)))
|
||||
}
|
||||
)
|
||||
if (isAtLeastApiLevel(31)) {
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.widget_use_theme_colors),
|
||||
iconPadding = false,
|
||||
value = widget.config.themeColors,
|
||||
onValueChanged = {
|
||||
onWidgetUpdated(widget.copy(config = widget.config.copy(themeColors = it)))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isAtLeastApiLevel(28) && widgetInfo.widgetFeatures and AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE != 0) {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package de.mm20.launcher2.ui.launcher.widgets
|
||||
|
||||
import android.appwidget.AppWidgetHost
|
||||
import android.appwidget.AppWidgetManager
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
@ -43,7 +42,7 @@ import de.mm20.launcher2.ui.component.LauncherCard
|
||||
import de.mm20.launcher2.ui.launcher.sheets.ConfigureWidgetSheet
|
||||
import de.mm20.launcher2.ui.launcher.sheets.WidgetPickerSheet
|
||||
import de.mm20.launcher2.ui.launcher.widgets.calendar.CalendarWidget
|
||||
import de.mm20.launcher2.ui.launcher.widgets.external.ExternalWidget
|
||||
import de.mm20.launcher2.ui.launcher.widgets.external.AppWidget
|
||||
import de.mm20.launcher2.ui.launcher.widgets.favorites.FavoritesWidget
|
||||
import de.mm20.launcher2.ui.launcher.widgets.music.MusicWidget
|
||||
import de.mm20.launcher2.ui.launcher.widgets.notes.NotesWidget
|
||||
@ -174,60 +173,11 @@ fun WidgetItem(
|
||||
}
|
||||
|
||||
is AppWidget -> {
|
||||
val widgetInfo = remember(widget.config.widgetId) {
|
||||
AppWidgetManager.getInstance(context)
|
||||
.getAppWidgetInfo(widget.config.widgetId)
|
||||
}
|
||||
if (widgetInfo == null) {
|
||||
var replaceWidget by rememberSaveable {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
Banner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = stringResource(R.string.app_widget_loading_failed),
|
||||
icon = Icons.Rounded.Warning,
|
||||
secondaryAction = {
|
||||
OutlinedButton(onClick = onWidgetRemove) {
|
||||
Text(stringResource(R.string.widget_action_remove))
|
||||
}
|
||||
},
|
||||
primaryAction = {
|
||||
Button(onClick = { replaceWidget = true }) {
|
||||
Text(stringResource(R.string.widget_action_replace))
|
||||
}
|
||||
}
|
||||
)
|
||||
if (replaceWidget) {
|
||||
WidgetPickerSheet(
|
||||
onDismiss = { replaceWidget = false },
|
||||
onWidgetSelected = {
|
||||
val updatedWidget = when (it) {
|
||||
is AppWidget -> widget.copy(
|
||||
config = widget.config.copy(
|
||||
widgetId = it.config.widgetId
|
||||
)
|
||||
)
|
||||
|
||||
is WeatherWidget -> it.copy(id = widget.id)
|
||||
is MusicWidget -> it.copy(id = widget.id)
|
||||
is CalendarWidget -> it.copy(id = widget.id)
|
||||
is FavoritesWidget -> it.copy(id = widget.id)
|
||||
is NotesWidget -> it.copy(id = widget.id)
|
||||
}
|
||||
onWidgetUpdate(updatedWidget)
|
||||
replaceWidget = false
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
ExternalWidget(
|
||||
widgetId = widget.config.widgetId,
|
||||
widgetInfo = widgetInfo,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
height = widget.config.height,
|
||||
borderless = widget.config.borderless,
|
||||
)
|
||||
}
|
||||
AppWidget(
|
||||
widget,
|
||||
onWidgetUpdate = onWidgetUpdate,
|
||||
onWidgetRemove = onWidgetRemove,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -464,7 +464,7 @@ fun ConfigureClockWidgetSheet(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_clock_widget_use_theme_color),
|
||||
title = stringResource(R.string.widget_use_theme_colors),
|
||||
icon = Icons.Rounded.ColorLens,
|
||||
value = useAccentColor,
|
||||
onValueChanged = {
|
||||
|
||||
@ -2,20 +2,14 @@ package de.mm20.launcher2.ui.launcher.widgets.clock.clocks
|
||||
|
||||
import android.appwidget.AppWidgetManager
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material3.ElevatedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.preferences.ClockWidgetStyle
|
||||
import de.mm20.launcher2.ui.launcher.sheets.WidgetPickerSheet
|
||||
import de.mm20.launcher2.ui.launcher.widgets.external.ExternalWidget
|
||||
import de.mm20.launcher2.ui.launcher.widgets.external.AppWidgetHost
|
||||
|
||||
@Composable
|
||||
fun CustomClock(
|
||||
@ -35,7 +29,7 @@ fun CustomClock(
|
||||
.getAppWidgetInfo(widgetId)
|
||||
}
|
||||
if (widgetInfo != null) {
|
||||
ExternalWidget(
|
||||
AppWidgetHost(
|
||||
widgetInfo = widgetInfo,
|
||||
widgetId = widgetId,
|
||||
height = if (compact) 64 else 200,
|
||||
|
||||
@ -1,165 +1,103 @@
|
||||
package de.mm20.launcher2.ui.launcher.widgets.external
|
||||
|
||||
import android.appwidget.AppWidgetHost
|
||||
import android.appwidget.AppWidgetProviderInfo
|
||||
import android.os.Build
|
||||
import android.util.SparseIntArray
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ListView
|
||||
import android.widget.ScrollView
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import android.appwidget.AppWidgetManager
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Warning
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.view.iterator
|
||||
import androidx.core.view.setPadding
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.ui.base.LocalAppWidgetHost
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.Banner
|
||||
import de.mm20.launcher2.ui.ktx.toPixels
|
||||
import palettes.TonalPalette
|
||||
import de.mm20.launcher2.ui.launcher.sheets.WidgetPickerSheet
|
||||
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
||||
import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper
|
||||
import de.mm20.launcher2.widgets.AppWidget
|
||||
import de.mm20.launcher2.widgets.CalendarWidget
|
||||
import de.mm20.launcher2.widgets.FavoritesWidget
|
||||
import de.mm20.launcher2.widgets.MusicWidget
|
||||
import de.mm20.launcher2.widgets.NotesWidget
|
||||
import de.mm20.launcher2.widgets.WeatherWidget
|
||||
import de.mm20.launcher2.widgets.Widget
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun ExternalWidget(
|
||||
widgetInfo: AppWidgetProviderInfo,
|
||||
widgetId: Int,
|
||||
height: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
borderless: Boolean = false,
|
||||
useThemeColors: Boolean = false,
|
||||
onLightBackground: Boolean = false,
|
||||
fun AppWidget(
|
||||
widget: AppWidget,
|
||||
onWidgetUpdate: (Widget) -> Unit,
|
||||
onWidgetRemove: () -> Unit,
|
||||
) {
|
||||
val padding = if (borderless) 0 else 8.dp.toPixels().roundToInt()
|
||||
val context = LocalContext.current
|
||||
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
val appWidgetHost = LocalAppWidgetHost.current
|
||||
val lightBackground = (!LocalDarkTheme.current && widget.config.background) || LocalPreferDarkContentOverWallpaper.current
|
||||
|
||||
BoxWithConstraints {
|
||||
val maxWidth = maxWidth
|
||||
key(widgetId) {
|
||||
AndroidView(
|
||||
modifier = modifier
|
||||
.height(height.dp),
|
||||
factory = {
|
||||
val view = appWidgetHost.createView(it.applicationContext, widgetId, widgetInfo)
|
||||
enableNestedScroll(view)
|
||||
return@AndroidView view
|
||||
},
|
||||
update = {
|
||||
if (isAtLeastApiLevel(31)) {
|
||||
if (useThemeColors) {
|
||||
val colorMapping = getColorMapping(colorScheme)
|
||||
it.setColorResources(colorMapping)
|
||||
} else {
|
||||
it.resetColorResources()
|
||||
}
|
||||
val widgetInfo = remember(widget.config.widgetId) {
|
||||
AppWidgetManager.getInstance(context)
|
||||
.getAppWidgetInfo(widget.config.widgetId)
|
||||
}
|
||||
if (widgetInfo == null) {
|
||||
var replaceWidget by rememberSaveable {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
Banner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = stringResource(R.string.app_widget_loading_failed),
|
||||
icon = Icons.Rounded.Warning,
|
||||
secondaryAction = {
|
||||
OutlinedButton(onClick = onWidgetRemove) {
|
||||
Text(stringResource(R.string.widget_action_remove))
|
||||
}
|
||||
},
|
||||
primaryAction = {
|
||||
Button(onClick = { replaceWidget = true }) {
|
||||
Text(stringResource(R.string.widget_action_replace))
|
||||
}
|
||||
}
|
||||
)
|
||||
if (replaceWidget) {
|
||||
WidgetPickerSheet(
|
||||
onDismiss = { replaceWidget = false },
|
||||
onWidgetSelected = {
|
||||
val updatedWidget = when (it) {
|
||||
is AppWidget -> widget.copy(
|
||||
config = widget.config.copy(
|
||||
widgetId = it.config.widgetId
|
||||
)
|
||||
)
|
||||
|
||||
is WeatherWidget -> it.copy(id = widget.id)
|
||||
is MusicWidget -> it.copy(id = widget.id)
|
||||
is CalendarWidget -> it.copy(id = widget.id)
|
||||
is FavoritesWidget -> it.copy(id = widget.id)
|
||||
is NotesWidget -> it.copy(id = widget.id)
|
||||
}
|
||||
|
||||
if (isAtLeastApiLevel(29)) {
|
||||
it.setOnLightBackground(onLightBackground)
|
||||
}
|
||||
|
||||
it.updateAppWidgetSize(
|
||||
null,
|
||||
maxWidth.value.roundToInt(),
|
||||
height,
|
||||
maxWidth.value.roundToInt(),
|
||||
height,
|
||||
)
|
||||
it.setPadding(padding)
|
||||
onWidgetUpdate(updatedWidget)
|
||||
replaceWidget = false
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
AppWidgetHost(
|
||||
widgetId = widget.config.widgetId,
|
||||
widgetInfo = widgetInfo,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
height = widget.config.height,
|
||||
borderless = widget.config.borderless,
|
||||
useThemeColors = widget.config.themeColors,
|
||||
onLightBackground = lightBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun enableNestedScroll(view: View) {
|
||||
if (view is ViewGroup) {
|
||||
for (child in view.iterator()) {
|
||||
enableNestedScroll(child)
|
||||
}
|
||||
}
|
||||
if (view is ListView || view is ScrollView) view.isNestedScrollingEnabled = true
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
private fun getColorMapping(colorScheme: ColorScheme): SparseIntArray {
|
||||
val p = TonalPalette.fromInt(colorScheme.primary.toArgb())
|
||||
val s = TonalPalette.fromInt(colorScheme.secondary.toArgb())
|
||||
val t = TonalPalette.fromInt(colorScheme.tertiary.toArgb())
|
||||
val n = TonalPalette.fromInt(colorScheme.outline.toArgb())
|
||||
val nv = TonalPalette.fromInt(colorScheme.outlineVariant.toArgb())
|
||||
|
||||
val colorResources = SparseIntArray()
|
||||
colorResources.append(android.R.color.system_accent1_0, p.tone(100))
|
||||
colorResources.append(android.R.color.system_accent1_10, p.tone(99))
|
||||
colorResources.append(android.R.color.system_accent1_100, p.tone(90))
|
||||
colorResources.append(android.R.color.system_accent1_200, p.tone(80))
|
||||
colorResources.append(android.R.color.system_accent1_300, p.tone(70))
|
||||
colorResources.append(android.R.color.system_accent1_400, p.tone(60))
|
||||
colorResources.append(android.R.color.system_accent1_500, p.tone(50))
|
||||
colorResources.append(android.R.color.system_accent1_600, p.tone(40))
|
||||
colorResources.append(android.R.color.system_accent1_700, p.tone(30))
|
||||
colorResources.append(android.R.color.system_accent1_800, p.tone(20))
|
||||
colorResources.append(android.R.color.system_accent1_900, p.tone(10))
|
||||
colorResources.append(android.R.color.system_accent1_1000, s.tone(0))
|
||||
colorResources.append(android.R.color.system_accent2_0, s.tone(100))
|
||||
colorResources.append(android.R.color.system_accent2_10, s.tone(99))
|
||||
colorResources.append(android.R.color.system_accent2_100, s.tone(90))
|
||||
colorResources.append(android.R.color.system_accent2_200, s.tone(80))
|
||||
colorResources.append(android.R.color.system_accent2_300, s.tone(70))
|
||||
colorResources.append(android.R.color.system_accent2_400, s.tone(60))
|
||||
colorResources.append(android.R.color.system_accent2_500, s.tone(50))
|
||||
colorResources.append(android.R.color.system_accent2_600, s.tone(40))
|
||||
colorResources.append(android.R.color.system_accent2_700, s.tone(30))
|
||||
colorResources.append(android.R.color.system_accent2_800, s.tone(20))
|
||||
colorResources.append(android.R.color.system_accent2_900, s.tone(10))
|
||||
colorResources.append(android.R.color.system_accent2_1000, t.tone(0))
|
||||
colorResources.append(android.R.color.system_accent3_0, t.tone(100))
|
||||
colorResources.append(android.R.color.system_accent3_10, t.tone(99))
|
||||
colorResources.append(android.R.color.system_accent3_100, t.tone(90))
|
||||
colorResources.append(android.R.color.system_accent3_200, t.tone(80))
|
||||
colorResources.append(android.R.color.system_accent3_300, t.tone(70))
|
||||
colorResources.append(android.R.color.system_accent3_400, t.tone(60))
|
||||
colorResources.append(android.R.color.system_accent3_500, t.tone(50))
|
||||
colorResources.append(android.R.color.system_accent3_600, t.tone(40))
|
||||
colorResources.append(android.R.color.system_accent3_700, t.tone(30))
|
||||
colorResources.append(android.R.color.system_accent3_800, t.tone(20))
|
||||
colorResources.append(android.R.color.system_accent3_900, t.tone(10))
|
||||
colorResources.append(android.R.color.system_accent3_1000, t.tone(0))
|
||||
colorResources.append(android.R.color.system_neutral1_0, n.tone(100))
|
||||
colorResources.append(android.R.color.system_neutral1_10, n.tone(99))
|
||||
colorResources.append(android.R.color.system_neutral1_100, n.tone(90))
|
||||
colorResources.append(android.R.color.system_neutral1_200, n.tone(80))
|
||||
colorResources.append(android.R.color.system_neutral1_300, n.tone(70))
|
||||
colorResources.append(android.R.color.system_neutral1_400, n.tone(60))
|
||||
colorResources.append(android.R.color.system_neutral1_500, n.tone(50))
|
||||
colorResources.append(android.R.color.system_neutral1_600, n.tone(40))
|
||||
colorResources.append(android.R.color.system_neutral1_700, n.tone(30))
|
||||
colorResources.append(android.R.color.system_neutral1_800, n.tone(20))
|
||||
colorResources.append(android.R.color.system_neutral1_900, n.tone(10))
|
||||
colorResources.append(android.R.color.system_neutral1_1000, nv.tone(0))
|
||||
colorResources.append(android.R.color.system_neutral2_0, nv.tone(100))
|
||||
colorResources.append(android.R.color.system_neutral2_10, nv.tone(99))
|
||||
colorResources.append(android.R.color.system_neutral2_100, nv.tone(90))
|
||||
colorResources.append(android.R.color.system_neutral2_200, nv.tone(80))
|
||||
colorResources.append(android.R.color.system_neutral2_300, nv.tone(70))
|
||||
colorResources.append(android.R.color.system_neutral2_400, nv.tone(60))
|
||||
colorResources.append(android.R.color.system_neutral2_500, nv.tone(50))
|
||||
colorResources.append(android.R.color.system_neutral2_600, nv.tone(40))
|
||||
colorResources.append(android.R.color.system_neutral2_700, nv.tone(30))
|
||||
colorResources.append(android.R.color.system_neutral2_800, nv.tone(20))
|
||||
colorResources.append(android.R.color.system_neutral2_900, nv.tone(10))
|
||||
colorResources.append(android.R.color.system_neutral2_1000, nv.tone(0))
|
||||
|
||||
return colorResources
|
||||
}
|
||||
167
app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/external/AppWidgetHost.kt
vendored
Normal file
167
app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/external/AppWidgetHost.kt
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
package de.mm20.launcher2.ui.launcher.widgets.external
|
||||
|
||||
import android.appwidget.AppWidgetHost
|
||||
import android.appwidget.AppWidgetProviderInfo
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.util.SparseIntArray
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ListView
|
||||
import android.widget.ScrollView
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.view.iterator
|
||||
import androidx.core.view.setPadding
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.ui.base.LocalAppWidgetHost
|
||||
import de.mm20.launcher2.ui.ktx.toPixels
|
||||
import palettes.TonalPalette
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun AppWidgetHost(
|
||||
widgetInfo: AppWidgetProviderInfo,
|
||||
widgetId: Int,
|
||||
height: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
borderless: Boolean = false,
|
||||
useThemeColors: Boolean = false,
|
||||
onLightBackground: Boolean = false,
|
||||
) {
|
||||
val padding = if (borderless) 0 else 8.dp.toPixels().roundToInt()
|
||||
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
val appWidgetHost = LocalAppWidgetHost.current
|
||||
|
||||
BoxWithConstraints {
|
||||
val maxWidth = maxWidth
|
||||
key(widgetId) {
|
||||
AndroidView(
|
||||
modifier = modifier
|
||||
.height(height.dp),
|
||||
factory = {
|
||||
val view = appWidgetHost.createView(it.applicationContext, widgetId, widgetInfo)
|
||||
enableNestedScroll(view)
|
||||
return@AndroidView view
|
||||
},
|
||||
update = {
|
||||
if (isAtLeastApiLevel(29)) {
|
||||
it.setOnLightBackground(onLightBackground)
|
||||
}
|
||||
if (isAtLeastApiLevel(31)) {
|
||||
if (useThemeColors) {
|
||||
val colorMapping = getColorMapping(colorScheme)
|
||||
it.setColorResources(colorMapping)
|
||||
} else {
|
||||
it.resetColorResources()
|
||||
}
|
||||
}
|
||||
|
||||
it.updateAppWidgetSize(
|
||||
null,
|
||||
maxWidth.value.roundToInt(),
|
||||
height,
|
||||
maxWidth.value.roundToInt(),
|
||||
height,
|
||||
)
|
||||
it.setPadding(padding)
|
||||
// Workaround to force update of the widget view
|
||||
it.updateAppWidgetOptions(Bundle())
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun enableNestedScroll(view: View) {
|
||||
if (view is ViewGroup) {
|
||||
for (child in view.iterator()) {
|
||||
enableNestedScroll(child)
|
||||
}
|
||||
}
|
||||
if (view is ListView || view is ScrollView) view.isNestedScrollingEnabled = true
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
private fun getColorMapping(colorScheme: ColorScheme): SparseIntArray {
|
||||
val p = TonalPalette.fromInt(colorScheme.primary.toArgb())
|
||||
val s = TonalPalette.fromInt(colorScheme.secondary.toArgb())
|
||||
val t = TonalPalette.fromInt(colorScheme.tertiary.toArgb())
|
||||
val n = TonalPalette.fromInt(colorScheme.outline.toArgb())
|
||||
val nv = TonalPalette.fromInt(colorScheme.outlineVariant.toArgb())
|
||||
|
||||
val colorResources = SparseIntArray()
|
||||
colorResources.append(android.R.color.system_accent1_0, p.tone(100))
|
||||
colorResources.append(android.R.color.system_accent1_10, p.tone(99))
|
||||
colorResources.append(android.R.color.system_accent1_100, p.tone(90))
|
||||
colorResources.append(android.R.color.system_accent1_200, p.tone(80))
|
||||
colorResources.append(android.R.color.system_accent1_300, p.tone(70))
|
||||
colorResources.append(android.R.color.system_accent1_400, p.tone(60))
|
||||
colorResources.append(android.R.color.system_accent1_500, p.tone(50))
|
||||
colorResources.append(android.R.color.system_accent1_600, p.tone(40))
|
||||
colorResources.append(android.R.color.system_accent1_700, p.tone(30))
|
||||
colorResources.append(android.R.color.system_accent1_800, p.tone(20))
|
||||
colorResources.append(android.R.color.system_accent1_900, p.tone(10))
|
||||
colorResources.append(android.R.color.system_accent1_1000, s.tone(0))
|
||||
colorResources.append(android.R.color.system_accent2_0, s.tone(100))
|
||||
colorResources.append(android.R.color.system_accent2_10, s.tone(99))
|
||||
colorResources.append(android.R.color.system_accent2_100, s.tone(90))
|
||||
colorResources.append(android.R.color.system_accent2_200, s.tone(80))
|
||||
colorResources.append(android.R.color.system_accent2_300, s.tone(70))
|
||||
colorResources.append(android.R.color.system_accent2_400, s.tone(60))
|
||||
colorResources.append(android.R.color.system_accent2_500, s.tone(50))
|
||||
colorResources.append(android.R.color.system_accent2_600, s.tone(40))
|
||||
colorResources.append(android.R.color.system_accent2_700, s.tone(30))
|
||||
colorResources.append(android.R.color.system_accent2_800, s.tone(20))
|
||||
colorResources.append(android.R.color.system_accent2_900, s.tone(10))
|
||||
colorResources.append(android.R.color.system_accent2_1000, t.tone(0))
|
||||
colorResources.append(android.R.color.system_accent3_0, t.tone(100))
|
||||
colorResources.append(android.R.color.system_accent3_10, t.tone(99))
|
||||
colorResources.append(android.R.color.system_accent3_100, t.tone(90))
|
||||
colorResources.append(android.R.color.system_accent3_200, t.tone(80))
|
||||
colorResources.append(android.R.color.system_accent3_300, t.tone(70))
|
||||
colorResources.append(android.R.color.system_accent3_400, t.tone(60))
|
||||
colorResources.append(android.R.color.system_accent3_500, t.tone(50))
|
||||
colorResources.append(android.R.color.system_accent3_600, t.tone(40))
|
||||
colorResources.append(android.R.color.system_accent3_700, t.tone(30))
|
||||
colorResources.append(android.R.color.system_accent3_800, t.tone(20))
|
||||
colorResources.append(android.R.color.system_accent3_900, t.tone(10))
|
||||
colorResources.append(android.R.color.system_accent3_1000, t.tone(0))
|
||||
colorResources.append(android.R.color.system_neutral1_0, n.tone(100))
|
||||
colorResources.append(android.R.color.system_neutral1_10, n.tone(99))
|
||||
colorResources.append(android.R.color.system_neutral1_100, n.tone(90))
|
||||
colorResources.append(android.R.color.system_neutral1_200, n.tone(80))
|
||||
colorResources.append(android.R.color.system_neutral1_300, n.tone(70))
|
||||
colorResources.append(android.R.color.system_neutral1_400, n.tone(60))
|
||||
colorResources.append(android.R.color.system_neutral1_500, n.tone(50))
|
||||
colorResources.append(android.R.color.system_neutral1_600, n.tone(40))
|
||||
colorResources.append(android.R.color.system_neutral1_700, n.tone(30))
|
||||
colorResources.append(android.R.color.system_neutral1_800, n.tone(20))
|
||||
colorResources.append(android.R.color.system_neutral1_900, n.tone(10))
|
||||
colorResources.append(android.R.color.system_neutral1_1000, nv.tone(0))
|
||||
colorResources.append(android.R.color.system_neutral2_0, nv.tone(100))
|
||||
colorResources.append(android.R.color.system_neutral2_10, nv.tone(99))
|
||||
colorResources.append(android.R.color.system_neutral2_100, nv.tone(90))
|
||||
colorResources.append(android.R.color.system_neutral2_200, nv.tone(80))
|
||||
colorResources.append(android.R.color.system_neutral2_300, nv.tone(70))
|
||||
colorResources.append(android.R.color.system_neutral2_400, nv.tone(60))
|
||||
colorResources.append(android.R.color.system_neutral2_500, nv.tone(50))
|
||||
colorResources.append(android.R.color.system_neutral2_600, nv.tone(40))
|
||||
colorResources.append(android.R.color.system_neutral2_700, nv.tone(30))
|
||||
colorResources.append(android.R.color.system_neutral2_800, nv.tone(20))
|
||||
colorResources.append(android.R.color.system_neutral2_900, nv.tone(10))
|
||||
colorResources.append(android.R.color.system_neutral2_1000, nv.tone(0))
|
||||
|
||||
return colorResources
|
||||
}
|
||||
@ -710,7 +710,7 @@
|
||||
<string name="preference_search_bar_separate_work_profile_summary">Mostra les aplicacions del perfil de treball en una pestanya independent</string>
|
||||
<string name="preference_search_bar_separate_work_profile">Aplicacions del perfil de treball separades</string>
|
||||
<string name="preference_clock_widget_show_seconds">Mostra els segons</string>
|
||||
<string name="preference_clock_widget_use_theme_color">Utilitzeu el color del tema</string>
|
||||
<string name="widget_use_theme_colors">Utilitzeu el color del tema</string>
|
||||
<string name="clock_style_digital1">Negreta</string>
|
||||
<string name="clock_style_digital2">Simple</string>
|
||||
<string name="clock_style_orbit">Orbit</string>
|
||||
|
||||
@ -715,7 +715,7 @@
|
||||
<string name="preference_search_bar_separate_work_profile">Oddělit aplikace pracovního profilu</string>
|
||||
<string name="preference_search_bar_separate_work_profile_summary">Zobrazit aplikace pracovního profilu v oddělené kartě</string>
|
||||
<string name="preference_clock_widget_show_seconds">Zobrazit sekundy</string>
|
||||
<string name="preference_clock_widget_use_theme_color">Použít barvu motivu</string>
|
||||
<string name="widget_use_theme_colors">Použít barvu motivu</string>
|
||||
<string name="clock_style_digital1">Tučné</string>
|
||||
<string name="clock_style_digital2">Jednoduché</string>
|
||||
<string name="clock_style_orbit">Kruh</string>
|
||||
|
||||
@ -687,7 +687,7 @@
|
||||
<string name="menu_website">Åbn hjemmeside</string>
|
||||
<string name="missing_permission_location_search">Giv tilladelse til placering for at søge i nærheden.</string>
|
||||
<string name="preference_clock_widget_show_seconds">Vis sekunder</string>
|
||||
<string name="preference_clock_widget_use_theme_color">Brug temafarve</string>
|
||||
<string name="widget_use_theme_colors">Brug temafarve</string>
|
||||
<string name="preference_search_locations">Steder</string>
|
||||
<string name="preference_search_locations_radius">Søgeradius</string>
|
||||
<string name="preference_search_locations_summary">Søg i det lokale område efter butikker og andre steder</string>
|
||||
|
||||
@ -710,7 +710,7 @@
|
||||
<string name="preference_search_bar_separate_work_profile_summary">Apps im Arbeitsprofil in einem separaten Tab anzeigen</string>
|
||||
<string name="unavailable_searchable">Dieses Element existiert nicht mehr.</string>
|
||||
<string name="preference_clock_widget_show_seconds">Sekunden anzeigen</string>
|
||||
<string name="preference_clock_widget_use_theme_color">Design-Farbe verwenden</string>
|
||||
<string name="widget_use_theme_colors">Design-Farbe verwenden</string>
|
||||
<string name="clock_style_digital1">Fett</string>
|
||||
<string name="clock_style_digital2">Einfach</string>
|
||||
<string name="clock_style_orbit">Orbit</string>
|
||||
|
||||
@ -693,7 +693,7 @@
|
||||
<string name="preference_search_bar_separate_work_profile">Separar apps del perfil de trabajo</string>
|
||||
<string name="preference_search_bar_separate_work_profile_summary">Mostrar apps del perfil de trabajo en una pestaña separada</string>
|
||||
<string name="preference_clock_widget_show_seconds">Mostrar segundos</string>
|
||||
<string name="preference_clock_widget_use_theme_color">Usar color del tema</string>
|
||||
<string name="widget_use_theme_colors">Usar color del tema</string>
|
||||
<string name="preference_search_locations_radius">Radio de búsqueda</string>
|
||||
<string name="preference_search_locations_show_position_on_map_summary">Mostrar tu ubicación en el mapa</string>
|
||||
<string name="clock_style_digital1">Negrita</string>
|
||||
|
||||
@ -709,7 +709,7 @@
|
||||
<string name="unavailable_searchable">Ez az elem már nem létezik.</string>
|
||||
<string name="clock_style_segment">7-szegmenses</string>
|
||||
<string name="clock_variant_standard">Általános</string>
|
||||
<string name="preference_clock_widget_use_theme_color">Színséma használata</string>
|
||||
<string name="widget_use_theme_colors">Színséma használata</string>
|
||||
<string name="preference_search_bar_separate_work_profile">Lap elkülönítés az alkalmazásokhoz</string>
|
||||
<string name="preference_search_bar_separate_work_profile_summary">Külön lapon mutatja az alkalmazásokat</string>
|
||||
<string name="preference_clock_widget_show_seconds">Másodperc megjelenítése</string>
|
||||
|
||||
@ -690,7 +690,7 @@
|
||||
<string name="location_open_next_day">Apertura %1$s</string>
|
||||
<string name="preference_search_locations_hide_uncategorized">Nascondi luoghi senza categoria</string>
|
||||
<string name="preference_clock_widget_show_seconds">Mostra secondi</string>
|
||||
<string name="preference_clock_widget_use_theme_color">Usa colore del tema</string>
|
||||
<string name="widget_use_theme_colors">Usa colore del tema</string>
|
||||
<string name="preference_search_locations">Luoghi</string>
|
||||
<string name="preference_search_locations_radius">Raggio di ricerca</string>
|
||||
<string name="preference_search_locations_summary">Cerca nella zona negozi e altri luoghi</string>
|
||||
|
||||
@ -601,7 +601,7 @@
|
||||
<string name="preference_clockwidget_layout_vertical">Default</string>
|
||||
<string name="preference_clockwidget_layout_horizontal">Compact</string>
|
||||
<string name="preference_clock_widget_show_seconds">Show seconds</string>
|
||||
<string name="preference_clock_widget_use_theme_color">Use theme color</string>
|
||||
<string name="widget_use_theme_colors">Use theme color</string>
|
||||
<string name="preference_clock_widget_fill_height">Fill screen height</string>
|
||||
<string name="preference_clock_widget_fill_height_summary">Insert extra space above the clock to fill the entire screen height</string>
|
||||
<string name="preference_clock_widget_color">Color</string>
|
||||
|
||||
@ -17,6 +17,7 @@ data class AppWidgetConfig(
|
||||
val height: Int,
|
||||
val borderless: Boolean = false,
|
||||
val background: Boolean = true,
|
||||
val themeColors: Boolean = true,
|
||||
)
|
||||
|
||||
data class AppWidget(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user