From b0f1b54403e76f26077c6efd8f88988a03ae6cc8 Mon Sep 17 00:00:00 2001 From: Samuel Born Date: Fri, 27 Jun 2025 10:53:21 +0200 Subject: [PATCH 1/4] Add custom widget theme with color picker This commit introduces a new "Custom" theme option for the widget, allowing users to select a custom background color. Changes include: - Added "Custom" theme to widget_themes array in arrays.xml. - Introduced new string resources for custom theme color in strings.xml. - Modified preferences_widget.xml to include a color picker preference for the custom theme. - Updated WidgetSettings.kt to manage the visibility and functionality of the custom color picker based on theme selection. - Extended WidgetPreferences.kt to store and retrieve the custom theme color, and to apply it to the widget's background. - Implemented a isLight utility function in Theme.kt to determine text color based on the custom background color. --- .../preferences/fragments/WidgetSettings.kt | 19 ++++++++++++++ app/src/main/java/org/tasks/themes/Theme.kt | 7 ++++++ .../org/tasks/widget/WidgetPreferences.kt | 25 +++++++++++++------ app/src/main/res/values/arrays.xml | 3 ++- app/src/main/res/values/strings.xml | 3 +++ app/src/main/res/xml/preferences_widget.xml | 4 +++ 6 files changed, 52 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/tasks/preferences/fragments/WidgetSettings.kt b/app/src/main/java/org/tasks/preferences/fragments/WidgetSettings.kt index 455ad9097..5270bb7c3 100644 --- a/app/src/main/java/org/tasks/preferences/fragments/WidgetSettings.kt +++ b/app/src/main/java/org/tasks/preferences/fragments/WidgetSettings.kt @@ -44,6 +44,7 @@ class WidgetSettings : InjectingPreferenceFragment() { companion object { private const val REQUEST_THEME_SELECTION = 1006 private const val REQUEST_COLOR_SELECTION = 1007 + private const val REQUEST_CUSTOM_THEME_COLOR_SELECTION = 1009 private const val REQUEST_SORT = 1008 const val EXTRA_WIDGET_ID = "extra_widget_id" @@ -163,6 +164,13 @@ class WidgetSettings : InjectingPreferenceFragment() { updateFilter() updateTheme() updateColor() + + val customThemeColor = findPreference(R.string.p_widget_custom_theme_color) + customThemeColor.onPreferenceClickListener = Preference.OnPreferenceClickListener { + newColorPalette(this, REQUEST_CUSTOM_THEME_COLOR_SELECTION, widgetPreferences.customThemeColor, Palette.WIDGET) + .show(parentFragmentManager, FRAG_TAG_COLOR_PICKER) + false + } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -182,6 +190,12 @@ class WidgetSettings : InjectingPreferenceFragment() { ) updateColor() } + REQUEST_CUSTOM_THEME_COLOR_SELECTION -> if (resultCode == Activity.RESULT_OK) { + widgetPreferences.setCustomThemeColor( + data!!.getIntExtra(ColorWheelPicker.EXTRA_SELECTED, 0) + ) + updateCustomThemeColor() + } REQUEST_SORT -> if (resultCode == Activity.RESULT_OK) updateSort() else -> super.onActivityResult(requestCode, resultCode, data) } @@ -198,12 +212,17 @@ class WidgetSettings : InjectingPreferenceFragment() { val widgetNames = resources.getStringArray(R.array.widget_themes) findPreference(R.string.p_widget_theme).summary = widgetNames[index] findPreference(R.string.p_widget_color_v2).isVisible = index != 4 + findPreference(R.string.p_widget_custom_theme_color).isVisible = index == 5 } private fun updateColor() { tintColorPreference(R.string.p_widget_color_v2, widgetPreferences.color) } + private fun updateCustomThemeColor() { + tintColorPreference(R.string.p_widget_custom_theme_color, widgetPreferences.customThemeColor) + } + private fun updateFilter() = lifecycleScope.launch { findPreference(R.string.p_widget_filter).summary = getFilter().title updateSort() diff --git a/app/src/main/java/org/tasks/themes/Theme.kt b/app/src/main/java/org/tasks/themes/Theme.kt index ac2256acc..62e890149 100644 --- a/app/src/main/java/org/tasks/themes/Theme.kt +++ b/app/src/main/java/org/tasks/themes/Theme.kt @@ -1,6 +1,7 @@ package org.tasks.themes import android.app.Activity +import androidx.core.graphics.ColorUtils import javax.inject.Inject class Theme @Inject constructor( @@ -15,4 +16,10 @@ class Theme @Inject constructor( fun applyTheme(activity: Activity) { themeBase.set(activity) } + + companion object { + fun isLight(color: Int): Boolean { + return ColorUtils.calculateLuminance(color) > 0.5 + } + } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/widget/WidgetPreferences.kt b/app/src/main/java/org/tasks/widget/WidgetPreferences.kt index 78c946c78..8de1e9c57 100644 --- a/app/src/main/java/org/tasks/widget/WidgetPreferences.kt +++ b/app/src/main/java/org/tasks/widget/WidgetPreferences.kt @@ -13,6 +13,8 @@ import org.tasks.preferences.QueryPreferences import org.tasks.tasklist.SectionedDataSource.Companion.HEADER_COMPLETED import timber.log.Timber +import org.tasks.themes.Theme + class WidgetPreferences( private val context: Context, private val preferences: Preferences, @@ -92,18 +94,18 @@ class WidgetPreferences( get() = when (themeIndex) { 0 -> false 3, 4 -> context.isNightMode + 5 -> !Theme.isLight(backgroundColor) else -> true } private val backgroundColor: Int - get() = context.getColor( - when (themeIndex) { - 1 -> android.R.color.black - 2 -> R.color.md_background_dark - 3, 4 -> R.color.widget_background_follow_system - else -> android.R.color.white - } - ) + get() = when (themeIndex) { + 1 -> context.getColor(android.R.color.black) + 2 -> context.getColor(R.color.md_background_dark) + 3, 4 -> context.getColor(R.color.widget_background_follow_system) + 5 -> customThemeColor + else -> context.getColor(android.R.color.white) + } var collapsed: Set get() { @@ -160,6 +162,13 @@ class WidgetPreferences( fun setColor(color: Int) { setInt(R.string.p_widget_color_v2, color) } + + val customThemeColor: Int + get() = getInt(R.string.p_widget_custom_theme_color, 0) + + fun setCustomThemeColor(color: Int) { + setInt(R.string.p_widget_custom_theme_color, color) + } val footerOpacity: Int get() = getAlphaValue(R.string.p_widget_footer_opacity) val rowOpacity: Int diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 2b37df7b3..32f66a44f 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -35,7 +35,8 @@ @string/theme_black @string/theme_dark @string/theme_system_default - @string/theme_dynamic + @string/theme_dynamic + @string/theme_custom diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1f0104f68..e2750ab24 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -386,11 +386,14 @@ File %1$s contained %2$s.\n\n Launcher icon Black Light + Custom Dark Wallpaper Day/Night System default Dynamic + p_widget_custom_theme_color + Custom theme color Language Restart Tasks for this change to take effect Restart now diff --git a/app/src/main/res/xml/preferences_widget.xml b/app/src/main/res/xml/preferences_widget.xml index 46434b0e8..0646053df 100644 --- a/app/src/main/res/xml/preferences_widget.xml +++ b/app/src/main/res/xml/preferences_widget.xml @@ -10,6 +10,10 @@ android:key="@string/p_widget_theme" android:title="@string/theme" /> + + From 4497914371940e0c553a8fea5d357cc2ca83a30b Mon Sep 17 00:00:00 2001 From: Samuel Born <58636569+SamuelBorn@users.noreply.github.com> Date: Fri, 27 Jun 2025 12:50:24 +0200 Subject: [PATCH 2/4] Fix indenting --- app/src/main/res/values/arrays.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 32f66a44f..d4eaa4159 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -35,7 +35,7 @@ @string/theme_black @string/theme_dark @string/theme_system_default - @string/theme_dynamic + @string/theme_dynamic @string/theme_custom @@ -155,4 +155,4 @@ -1 - \ No newline at end of file + From b8a75f5938f7a93da41b2e133720110acfebfe6b Mon Sep 17 00:00:00 2001 From: Samuel Born Date: Sat, 9 Aug 2025 21:47:26 +0200 Subject: [PATCH 3/4] move p_widget_custom_theme_color to correct location --- app/src/main/res/values/keys.xml | 1 + app/src/main/res/values/strings.xml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index 326d2f050..fed586af6 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -327,6 +327,7 @@ widget-compact- widget-color- widget-color-v2 + p_widget_custom_theme_color widget-opacity-v3- widget-font-size- widget-show-due-date- diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ece4e9a0f..b58ceac1c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -392,7 +392,6 @@ File %1$s contained %2$s.\n\n Day/Night System default Dynamic - p_widget_custom_theme_color Custom theme color Language Restart Tasks for this change to take effect From d9e064d1b95460e24a6f70086162ae1e4b9158d7 Mon Sep 17 00:00:00 2001 From: Samuel Born Date: Sat, 9 Aug 2025 21:50:38 +0200 Subject: [PATCH 4/4] - recolor chip when background = list color - auto update custom widget color preview --- .../org/tasks/preferences/fragments/WidgetSettings.kt | 1 + .../main/java/org/tasks/widget/TasksWidgetViewFactory.kt | 8 ++++++++ app/src/main/java/org/tasks/widget/WidgetChipProvider.kt | 8 +++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/tasks/preferences/fragments/WidgetSettings.kt b/app/src/main/java/org/tasks/preferences/fragments/WidgetSettings.kt index 5270bb7c3..adbed37dc 100644 --- a/app/src/main/java/org/tasks/preferences/fragments/WidgetSettings.kt +++ b/app/src/main/java/org/tasks/preferences/fragments/WidgetSettings.kt @@ -164,6 +164,7 @@ class WidgetSettings : InjectingPreferenceFragment() { updateFilter() updateTheme() updateColor() + updateCustomThemeColor() val customThemeColor = findPreference(R.string.p_widget_custom_theme_color) customThemeColor.onPreferenceClickListener = Preference.OnPreferenceClickListener { diff --git a/app/src/main/java/org/tasks/widget/TasksWidgetViewFactory.kt b/app/src/main/java/org/tasks/widget/TasksWidgetViewFactory.kt index 849313bcb..aff0d530a 100644 --- a/app/src/main/java/org/tasks/widget/TasksWidgetViewFactory.kt +++ b/app/src/main/java/org/tasks/widget/TasksWidgetViewFactory.kt @@ -64,6 +64,14 @@ internal class TasksWidgetViewFactory( init { chipProvider.isDark = settings.isDark + + val widgetThemes = context.resources.getStringArray(R.array.widget_themes) + val customThemeName = context.getString(R.string.theme_custom) + val customThemeIndex = widgetThemes.indexOf(customThemeName) + + if (widgetPreferences.themeIndex == customThemeIndex) { + chipProvider.customBackgroundColor = widgetPreferences.customThemeColor + } } override fun onCreate() {} diff --git a/app/src/main/java/org/tasks/widget/WidgetChipProvider.kt b/app/src/main/java/org/tasks/widget/WidgetChipProvider.kt index 37a54832a..0970aaf2d 100644 --- a/app/src/main/java/org/tasks/widget/WidgetChipProvider.kt +++ b/app/src/main/java/org/tasks/widget/WidgetChipProvider.kt @@ -32,6 +32,7 @@ class WidgetChipProvider @Inject constructor( private val inventory: Inventory, ) { var isDark = false + @ColorInt var customBackgroundColor: Int? = null fun getSubtaskChip(task: TaskContainer): RemoteViews { return newChip().apply { @@ -127,13 +128,18 @@ class WidgetChipProvider @Inject constructor( } private fun newChip(@ColorInt color: Int = 0) = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.widget_chip).apply { - val tint = if (color == 0) { + var tint = if (color == 0) { context.getColor( if (isDark) R.color.icon_tint_dark_alpha else R.color.icon_tint_light_alpha ) } else { color } + + if (customBackgroundColor != null && tint == customBackgroundColor) { + tint = context.getColor(if (isDark) R.color.white_87 else R.color.black_87) + } + setColorFilter(R.id.chip_icon, tint) setColorFilter(R.id.chip_background, tint) setTextColor(R.id.chip_text, tint)