"Add to home screen" shortcut in list settings

pull/3207/head
Alex Baker 12 months ago
parent ece1fd4ef3
commit 761ba583b5

@ -1,32 +1,56 @@
package org.tasks.activities package org.tasks.activities
import android.app.PendingIntent
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Paint
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.tasks.R
import org.tasks.analytics.Firebase
import org.tasks.compose.DeleteButton import org.tasks.compose.DeleteButton
import org.tasks.compose.IconPickerActivity.Companion.launchIconPicker import org.tasks.compose.IconPickerActivity.Companion.launchIconPicker
import org.tasks.compose.IconPickerActivity.Companion.registerForIconPickerResult import org.tasks.compose.IconPickerActivity.Companion.registerForIconPickerResult
import org.tasks.compose.settings.BaseSettingsContent import org.tasks.compose.settings.ListSettingsContent
import org.tasks.compose.settings.ListSettingsScaffold import org.tasks.compose.settings.ListSettingsScaffold
import org.tasks.data.UUIDHelper
import org.tasks.dialogs.ColorPalettePicker import org.tasks.dialogs.ColorPalettePicker
import org.tasks.dialogs.ColorPalettePicker.Companion.newColorPalette import org.tasks.dialogs.ColorPalettePicker.Companion.newColorPalette
import org.tasks.dialogs.ColorPickerAdapter.Palette import org.tasks.dialogs.ColorPickerAdapter.Palette
import org.tasks.dialogs.ColorWheelPicker import org.tasks.dialogs.ColorWheelPicker
import org.tasks.extensions.addBackPressedCallback import org.tasks.extensions.addBackPressedCallback
import org.tasks.filters.Filter
import org.tasks.icons.OutlinedGoogleMaterial
import org.tasks.intents.TaskIntents
import org.tasks.preferences.DefaultFilterProvider
import org.tasks.themes.ColorProvider import org.tasks.themes.ColorProvider
import org.tasks.themes.Theme import org.tasks.themes.Theme
import org.tasks.themes.ThemeColor import org.tasks.themes.ThemeColor
import org.tasks.themes.contentColorFor
import javax.inject.Inject import javax.inject.Inject
abstract class BaseListSettingsActivity : AppCompatActivity(), ColorPalettePicker.ColorPickedCallback, ColorWheelPicker.ColorPickedCallback { abstract class BaseListSettingsActivity : AppCompatActivity(), ColorPalettePicker.ColorPickedCallback, ColorWheelPicker.ColorPickedCallback {
@Inject lateinit var tasksTheme: Theme @Inject lateinit var tasksTheme: Theme
@Inject lateinit var colorProvider: ColorProvider @Inject lateinit var colorProvider: ColorProvider
@Inject lateinit var defaultFilterProvider: DefaultFilterProvider
@Inject lateinit var firebase: Firebase
protected abstract val defaultIcon: String protected abstract val defaultIcon: String
protected var selectedColor = 0 protected var selectedColor = 0
protected var selectedIcon = mutableStateOf<String?>(null) protected var selectedIcon = mutableStateOf<String?>(null)
@ -62,7 +86,10 @@ abstract class BaseListSettingsActivity : AppCompatActivity(), ColorPalettePicke
protected abstract fun hasChanges(): Boolean protected abstract fun hasChanges(): Boolean
protected abstract suspend fun save() protected abstract suspend fun save()
protected abstract val isNew: Boolean protected val isNew: Boolean
get() = filter == null
protected abstract val filter: Filter?
protected abstract val toolbarTitle: String? protected abstract val toolbarTitle: String?
protected abstract suspend fun delete() protected abstract suspend fun delete()
protected open fun discard() { protected open fun discard() {
@ -117,8 +144,8 @@ abstract class BaseListSettingsActivity : AppCompatActivity(), ColorPalettePicke
optionButton: @Composable () -> Unit = { optionButton: @Composable () -> Unit = {
if (!isNew) DeleteButton(toolbarTitle ?: "") { delete() } if (!isNew) DeleteButton(toolbarTitle ?: "") { delete() }
}, },
extensionContent: @Composable ColumnScope.() -> Unit = {},
fab: @Composable () -> Unit = {}, fab: @Composable () -> Unit = {},
extensionContent: @Composable ColumnScope.() -> Unit = {},
) { ) {
ListSettingsScaffold( ListSettingsScaffold(
title = title, title = title,
@ -134,12 +161,13 @@ abstract class BaseListSettingsActivity : AppCompatActivity(), ColorPalettePicke
actions = optionButton, actions = optionButton,
fab = fab, fab = fab,
) { ) {
BaseSettingsContent( ListSettingsContent(
color = colorState.value, color = colorState.value,
icon = selectedIcon.value ?: defaultIcon, icon = selectedIcon.value ?: defaultIcon,
text = textState.value, text = textState.value,
error = errorState.value, error = errorState.value,
requestKeyboard = requestKeyboard, requestKeyboard = requestKeyboard,
isNew = isNew,
setText = { setText = {
textState.value = it textState.value = it
errorState.value = "" errorState.value = ""
@ -147,14 +175,125 @@ abstract class BaseListSettingsActivity : AppCompatActivity(), ColorPalettePicke
pickColor = { showThemePicker() }, pickColor = { showThemePicker() },
clearColor = { clearColor() }, clearColor = { clearColor() },
pickIcon = { showIconPicker() }, pickIcon = { showIconPicker() },
addToHome = { createShortcut() },
extensionContent = extensionContent, extensionContent = extensionContent,
) )
} }
} }
protected fun createShortcut() {
filter?.let {
val filterId = defaultFilterProvider.getFilterPreferenceValue(it)
val iconColor = if (colorState.value == Color.Unspecified)
Color(tasksTheme.themeColor.primaryColor)
else
colorState.value
val shortcutInfo = ShortcutInfoCompat.Builder(this, UUIDHelper.newUUID())
.setShortLabel(title)
.setIcon(
selectedIcon.value
?.let { icon ->
createShortcutIcon(
context = this,
backgroundColor = iconColor,
icon = icon,
iconColor = contentColorFor(iconColor.toArgb()),
)
}
?: createShortcutIcon(
this,
backgroundColor = iconColor
)
)
.setIntent(TaskIntents.getTaskListByIdIntent(this, filterId))
.build()
val pinnedShortcutCallbackIntent = ShortcutManagerCompat
.createShortcutResultIntent(this, shortcutInfo)
// Create callback intent
val successCallback = PendingIntent.getBroadcast(
this, 0,
pinnedShortcutCallbackIntent,
PendingIntent.FLAG_IMMUTABLE
)
ShortcutManagerCompat.requestPinShortcut(
this,
shortcutInfo,
successCallback.intentSender
)
firebase.logEvent(R.string.event_create_shortcut, R.string.param_type to "settings_activity")
}
}
companion object { companion object {
private const val EXTRA_SELECTED_THEME = "extra_selected_theme" private const val EXTRA_SELECTED_THEME = "extra_selected_theme"
private const val EXTRA_SELECTED_ICON = "extra_selected_icon" private const val EXTRA_SELECTED_ICON = "extra_selected_icon"
private const val FRAG_TAG_COLOR_PICKER = "frag_tag_color_picker" private const val FRAG_TAG_COLOR_PICKER = "frag_tag_color_picker"
fun createShortcutIcon(context: Context, backgroundColor: Color): IconCompat {
val size = context.resources.getDimensionPixelSize(android.R.dimen.app_icon_size)
val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
Canvas(bitmap).apply {
// Draw circular background
val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = backgroundColor.toArgb()
}
drawCircle(size/2f, size/2f, size/2f, paint)
// Draw foreground icon
val foreground = ResourcesCompat.getDrawable(
context.resources,
org.tasks.kmp.R.drawable.ic_launcher_no_shadow_foreground,
null
)
foreground?.let {
it.setBounds(0, 0, size, size)
it.draw(this)
}
}
return IconCompat.createWithBitmap(bitmap)
}
fun createShortcutIcon(
context: Context,
backgroundColor: Color,
icon: String,
iconColor: Color = Color.White,
iconSizeDp: Int = 24
): IconCompat {
val size = context.resources.getDimensionPixelSize(android.R.dimen.app_icon_size)
val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
Canvas(bitmap).apply {
// Draw circular background
val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = backgroundColor.toArgb()
}
drawCircle(size/2f, size/2f, size/2f, paint)
// Create and draw IconicsDrawable
val drawable = IconicsDrawable(context, OutlinedGoogleMaterial.getIcon("gmo_$icon")).apply {
colorInt = iconColor.toArgb()
sizeDp = iconSizeDp
}
// Center the icon
val iconSize = (size * 0.5f).toInt()
drawable.setBounds(
(size - iconSize) / 2,
(size - iconSize) / 2,
(size + iconSize) / 2,
(size + iconSize) / 2
)
drawable.draw(this)
}
return IconCompat.createWithBitmap(bitmap)
}
} }
} }

@ -67,7 +67,7 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
private val newCriterionTypes: MutableState<List<CustomFilterCriterion>?> = mutableStateOf(null) private val newCriterionTypes: MutableState<List<CustomFilterCriterion>?> = mutableStateOf(null)
private val newCriterionOptions: MutableState<CriterionInstance?> = mutableStateOf(null) private val newCriterionOptions: MutableState<CriterionInstance?> = mutableStateOf(null)
private val filter: CustomFilter? override val filter: CustomFilter?
get() = viewModel.viewState.value.filter get() = viewModel.viewState.value.filter
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -100,9 +100,6 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
} }
} }
override val isNew: Boolean
get() = viewModel.viewState.value.filter == null
override val toolbarTitle: String override val toolbarTitle: String
get() = if (isNew) getString(R.string.FLA_new_filter) else viewModel.viewState.value.filter?.title ?: "" get() = if (isNew) getString(R.string.FLA_new_filter) else viewModel.viewState.value.filter?.title ?: ""

@ -22,6 +22,7 @@ import org.tasks.compose.settings.Toaster
import org.tasks.data.dao.GoogleTaskListDao import org.tasks.data.dao.GoogleTaskListDao
import org.tasks.data.entity.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.filters.Filter
import org.tasks.filters.GtasksFilter import org.tasks.filters.GtasksFilter
import org.tasks.themes.TasksIcons import org.tasks.themes.TasksIcons
import org.tasks.themes.TasksTheme import org.tasks.themes.TasksTheme
@ -78,8 +79,8 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() {
updateTheme() updateTheme()
} }
override val isNew: Boolean override val filter: Filter?
get() = isNewList get() = if (isNewList) null else GtasksFilter(gtasksList)
override val toolbarTitle: String override val toolbarTitle: String
get() = if (isNew) getString(R.string.new_list) else gtasksList.name!! get() = if (isNew) getString(R.string.new_list) else gtasksList.name!!
@ -131,10 +132,6 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() {
} }
} }
override fun finish() {
super.finish()
}
override fun promptDelete() { override fun promptDelete() {
if (!requestInProgress()) { if (!requestInProgress()) {
super.promptDelete() super.promptDelete()

@ -34,6 +34,7 @@ import org.tasks.data.dao.LocationDao
import org.tasks.data.entity.Place import org.tasks.data.entity.Place
import org.tasks.data.mapPosition import org.tasks.data.mapPosition
import org.tasks.extensions.formatNumber import org.tasks.extensions.formatNumber
import org.tasks.filters.Filter
import org.tasks.filters.PlaceFilter import org.tasks.filters.PlaceFilter
import org.tasks.location.MapFragment import org.tasks.location.MapFragment
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
@ -108,8 +109,8 @@ class PlaceSettingsActivity : BaseListSettingsActivity(),
Text(stringResource(id = R.string.geofence_radius)) Text(stringResource(id = R.string.geofence_radius))
Row(horizontalArrangement = Arrangement.End) { Row(horizontalArrangement = Arrangement.End) {
Text(getString( Text(getString(
R.string.location_radius_meters, R.string.location_radius_meters,
locale.formatNumber(sliderPos.floatValue.roundToInt() locale.formatNumber(sliderPos.floatValue.roundToInt()
))) )))
} }
} }
@ -183,8 +184,8 @@ class PlaceSettingsActivity : BaseListSettingsActivity(),
finish() finish()
} }
override val isNew: Boolean override val filter: Filter
get() = false get() = PlaceFilter(place)
override val toolbarTitle: String override val toolbarTitle: String
get() = place.address ?: place.displayName get() = place.address ?: place.displayName

@ -18,6 +18,7 @@ import org.tasks.Strings.isNullOrEmpty
import org.tasks.data.dao.TagDao import org.tasks.data.dao.TagDao
import org.tasks.data.dao.TagDataDao import org.tasks.data.dao.TagDataDao
import org.tasks.data.entity.TagData import org.tasks.data.entity.TagData
import org.tasks.filters.Filter
import org.tasks.filters.TagFilter import org.tasks.filters.TagFilter
import org.tasks.themes.TasksIcons import org.tasks.themes.TasksIcons
import org.tasks.themes.TasksTheme import org.tasks.themes.TasksTheme
@ -55,8 +56,8 @@ class TagSettingsActivity : BaseListSettingsActivity() {
updateTheme() updateTheme()
} }
override val isNew: Boolean override val filter: Filter?
get() = isNewTag get() = if (isNewTag) null else TagFilter(tagData)
override val toolbarTitle: String override val toolbarTitle: String
get() = if (isNew) getString(R.string.new_tag) else tagData.name!! get() = if (isNew) getString(R.string.new_tag) else tagData.name!!

@ -23,6 +23,7 @@ import org.tasks.data.dao.CaldavDao
import org.tasks.data.entity.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.filters.CaldavFilter import org.tasks.filters.CaldavFilter
import org.tasks.filters.Filter
import org.tasks.themes.TasksIcons import org.tasks.themes.TasksIcons
import org.tasks.ui.DisplayableException import org.tasks.ui.DisplayableException
import java.net.ConnectException import java.net.ConnectException
@ -58,8 +59,8 @@ abstract class BaseCaldavCalendarSettingsActivity : BaseListSettingsActivity() {
updateTheme() updateTheme()
} }
override val isNew: Boolean override val filter: Filter?
get() = caldavCalendar == null get() = caldavCalendar?.let { CaldavFilter(it) }
override val toolbarTitle: String override val toolbarTitle: String
get() = if (isNew) getString(R.string.new_list) else caldavCalendar!!.name ?: "" get() = if (isNew) getString(R.string.new_list) else caldavCalendar!!.name ?: ""

@ -0,0 +1,64 @@
package org.tasks.compose.settings
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.pm.ShortcutManagerCompat
import org.tasks.R
import org.tasks.compose.Constants
import org.tasks.kmp.org.tasks.compose.settings.SettingRow
import org.tasks.themes.TasksTheme
@Composable
fun AddToHomeRow(onClick: () -> Unit) {
val context = LocalContext.current
val isRequestPinShortcutSupported = LocalInspectionMode.current || remember {
ShortcutManagerCompat.isRequestPinShortcutSupported(context)
}
if (isRequestPinShortcutSupported) {
SettingRow(
modifier = Modifier.clickable(onClick = onClick),
left = {
Box(
modifier = Modifier.size(48.dp),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = Icons.Outlined.Home,
contentDescription = null,
tint = colorResource(R.color.icon_tint_with_alpha),
)
}
},
center = {
Text(
text = stringResource(R.string.add_to_home_screen),
modifier = Modifier.padding(start = Constants.KEYLINE_FIRST)
)
}
)
}
}
@Composable
@Preview(showBackground = true)
private fun AddToHomePreview() {
TasksTheme {
AddToHomeRow(onClick = {})
}
}

@ -1,47 +0,0 @@
package org.tasks.compose.settings
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import org.tasks.compose.Constants
@Composable
fun BaseSettingsContent(
color: Color,
icon: String,
text: String,
error: String,
requestKeyboard: Boolean,
setText: (String) -> Unit,
pickColor: () -> Unit,
clearColor: () -> Unit,
pickIcon: () -> Unit,
extensionContent: @Composable ColumnScope.() -> Unit,
) {
Column(
modifier = Modifier
.fillMaxSize(),
) {
TitleInput(
text = text,
error = error,
requestKeyboard = requestKeyboard,
modifier = Modifier.padding(horizontal = Constants.KEYLINE_FIRST),
setText = { setText(it) },
)
SelectColorRow(
color = color,
selectColor = { pickColor() },
clearColor = { clearColor() },
)
SelectIconRow(
icon = icon,
selectIcon = { pickIcon() },
)
extensionContent()
}
}

@ -0,0 +1,48 @@
package org.tasks.compose.settings
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import org.tasks.compose.Constants
@Composable
fun ColumnScope.ListSettingsContent(
color: Color,
icon: String,
text: String,
error: String,
requestKeyboard: Boolean,
isNew: Boolean,
setText: (String) -> Unit,
pickColor: () -> Unit,
clearColor: () -> Unit,
pickIcon: () -> Unit,
addToHome: () -> Unit,
extensionContent: @Composable ColumnScope.() -> Unit,
) {
TitleInput(
text = text,
error = error,
requestKeyboard = requestKeyboard,
modifier = Modifier.padding(horizontal = Constants.KEYLINE_FIRST),
setText = { setText(it) },
)
SelectColorRow(
color = color,
selectColor = { pickColor() },
clearColor = { clearColor() },
)
SelectIconRow(
icon = icon,
selectIcon = { pickIcon() },
)
if (!isNew) {
// TODO: support this for new filters too
AddToHomeRow(
onClick = { addToHome() },
)
}
extensionContent()
}

@ -3,9 +3,12 @@ package org.tasks.compose.settings
import androidx.activity.SystemBarStyle import androidx.activity.SystemBarStyle
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Save import androidx.compose.material.icons.outlined.Save
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
@ -21,6 +24,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import org.tasks.R import org.tasks.R
import org.tasks.extensions.Context.findActivity import org.tasks.extensions.Context.findActivity
import org.tasks.themes.colorOn import org.tasks.themes.colorOn
@ -37,7 +41,7 @@ fun ListSettingsScaffold(
discard: () -> Unit, discard: () -> Unit,
actions: @Composable () -> Unit = {}, actions: @Composable () -> Unit = {},
fab: @Composable () -> Unit = {}, fab: @Composable () -> Unit = {},
content: @Composable () -> Unit, content: @Composable ColumnScope.() -> Unit,
) { ) {
Scaffold( Scaffold(
topBar = { topBar = {
@ -63,7 +67,11 @@ fun ListSettingsScaffold(
actionIconContentColor = contentColor, actionIconContentColor = contentColor,
), ),
title = { title = {
Text(text = title) Text(
text = title,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}, },
navigationIcon = { navigationIcon = {
IconButton( IconButton(
@ -84,7 +92,11 @@ fun ListSettingsScaffold(
}, },
floatingActionButton = { fab() }, floatingActionButton = { fab() },
) { paddingValues -> ) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues)) { Column(modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.verticalScroll(rememberScrollState()),
) {
content() content()
} }
PromptAction( PromptAction(

@ -7,6 +7,7 @@ import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.tasks.caldav.BaseCaldavCalendarSettingsActivity import org.tasks.caldav.BaseCaldavCalendarSettingsActivity
import org.tasks.compose.settings.AddToHomeRow
import org.tasks.compose.settings.ListSettingsScaffold import org.tasks.compose.settings.ListSettingsScaffold
import org.tasks.compose.settings.SelectIconRow import org.tasks.compose.settings.SelectIconRow
import org.tasks.compose.settings.Toaster import org.tasks.compose.settings.Toaster
@ -35,6 +36,7 @@ class OpenTasksListSettingsActivity : BaseCaldavCalendarSettingsActivity() {
discard = { finish() }, discard = { finish() },
) { ) {
SelectIconRow(icon = selectedIcon.value?: defaultIcon) { showIconPicker() } SelectIconRow(icon = selectedIcon.value?: defaultIcon) { showIconPicker() }
AddToHomeRow(onClick = { createShortcut() })
} }
Toaster(state = snackbar) Toaster(state = snackbar)
} }

@ -424,6 +424,7 @@
<string name="event_add_task">add_task</string> <string name="event_add_task">add_task</string>
<string name="event_complete_task">complete_task</string> <string name="event_complete_task">complete_task</string>
<string name="event_request_review">request_review</string> <string name="event_request_review">request_review</string>
<string name="event_create_shortcut">create_shortcut</string>
<string name="param_type">type</string> <string name="param_type">type</string>
<string name="p_map_theme">map_theme</string> <string name="p_map_theme">map_theme</string>
<string name="p_picker_mode_date">picker_mode_date</string> <string name="p_picker_mode_date">picker_mode_date</string>

@ -734,4 +734,5 @@ File %1$s contained %2$s.\n\n
<string name="sort_ascending">Ascending</string> <string name="sort_ascending">Ascending</string>
<string name="sort_descending">Descending</string> <string name="sort_descending">Descending</string>
<string name="sort_not_available">Not available for tags, filters, or places</string> <string name="sort_not_available">Not available for tags, filters, or places</string>
<string name="add_to_home_screen">Add to home screen</string>
</resources> </resources>

@ -1,9 +1,19 @@
package org.tasks.themes package org.tasks.themes
import androidx.compose.ui.graphics.Color
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
import kotlin.math.pow import kotlin.math.pow
fun contentColorFor(backgroundColor: Int): Color =
if (backgroundColor == 0) {
Color.White
} else if (calculateContrast(WHITE, backgroundColor) < 3) {
Color.Black
} else {
Color.White
}
fun calculateContrast(foreground: Int, background: Int): Double { fun calculateContrast(foreground: Int, background: Int): Double {
var foreground = foreground var foreground = foreground
require(alpha(background) == 255) { require(alpha(background) == 255) {

@ -41,16 +41,7 @@ private val wallpaperScheme = darkColorScheme.copy(
fun colorOn(color: Color) = colorOn(color.toArgb()) fun colorOn(color: Color) = colorOn(color.toArgb())
@Composable @Composable
fun colorOn(color: Int) = fun colorOn(color: Int) = remember (color) { contentColorFor(color) }
remember (color) {
if (color == 0) {
Color.White
} else if (calculateContrast(WHITE, color) < 3) {
Color.Black
} else {
Color.White
}
}
@Composable @Composable
fun TasksTheme( fun TasksTheme(

Loading…
Cancel
Save