1/*
2 * Copyright (c) 2018, Daniel Gultsch All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation and/or
12 * other materials provided with the distribution.
13 *
14 * 3. Neither the name of the copyright holder nor the names of its contributors
15 * may be used to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30package eu.siacs.conversations.utils;
31
32import android.content.Context;
33import android.content.SharedPreferences;
34import android.content.res.Configuration;
35import android.content.res.Resources;
36import android.content.res.TypedArray;
37import android.content.res.loader.ResourcesLoader;
38import android.os.Build;
39import android.preference.PreferenceManager;
40import android.util.Log;
41import android.util.TypedValue;
42import android.widget.TextView;
43
44import androidx.annotation.StyleRes;
45import androidx.core.content.ContextCompat;
46
47import com.cheogram.android.ColorResourcesLoaderCreator;
48
49import com.google.android.material.color.MaterialColors;
50
51import java.util.HashMap;
52
53import eu.siacs.conversations.R;
54import eu.siacs.conversations.Config;
55import eu.siacs.conversations.Conversations;
56
57public class ThemeHelper {
58
59 public static HashMap<Integer, Integer> applyCustomColors(final Context context) {
60 HashMap<Integer, Integer> colors = new HashMap<>();
61 if (Build.VERSION.SDK_INT < 30) return colors;
62 if (!Conversations.isCustomColorsDesired(context)) return colors;
63
64 final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
65 if (sharedPreferences.contains("custom_theme_primary")) {
66 final var roles = MaterialColors.getColorRoles(sharedPreferences.getInt("custom_theme_primary", 0), true);
67 colors.put(R.color.md_theme_light_primary, roles.getAccent());
68 colors.put(R.color.md_theme_light_onPrimary, roles.getOnAccent());
69 colors.put(R.color.md_theme_light_primaryContainer, roles.getAccentContainer());
70 colors.put(R.color.md_theme_light_onPrimaryContainer, roles.getOnAccentContainer());
71 }
72 if (sharedPreferences.contains("custom_theme_primary_dark")) {
73 final var roles = MaterialColors.getColorRoles(sharedPreferences.getInt("custom_theme_primary_dark", 0), true);
74 colors.put(R.color.md_theme_light_secondary, roles.getAccent());
75 colors.put(R.color.md_theme_light_onSecondary, roles.getOnAccent());
76 colors.put(R.color.md_theme_light_secondaryContainer, roles.getAccentContainer());
77 colors.put(R.color.md_theme_light_onSecondaryContainer, roles.getOnAccentContainer());
78 }
79 if (sharedPreferences.contains("custom_theme_accent")) {
80 final var roles = MaterialColors.getColorRoles(sharedPreferences.getInt("custom_theme_accent", 0), true);
81 colors.put(R.color.md_theme_light_tertiary, roles.getAccent());
82 colors.put(R.color.md_theme_light_onTertiary, roles.getOnAccent());
83 colors.put(R.color.md_theme_light_tertiaryContainer, roles.getAccentContainer());
84 colors.put(R.color.md_theme_light_onTertiaryContainer, roles.getOnAccentContainer());
85 }
86 if (sharedPreferences.contains("custom_theme_background_primary")) {
87 int background_primary = sharedPreferences.getInt("custom_theme_background_primary", 0);
88 int alpha = (background_primary >> 24) & 0xFF;
89 int red = (background_primary >> 16) & 0xFF;
90 int green = (background_primary >> 8) & 0xFF;
91 int blue = background_primary & 0xFF;
92 colors.put(R.color.md_theme_light_background, background_primary);
93 colors.put(R.color.md_theme_light_surface, background_primary);
94 //colors.put(R.color.md_theme_light_surface, (int)((alpha << 24) | ((int)(red*.9) << 16) | ((int)(green*.9) << 8) | (int)(blue*.9)));
95 colors.put(R.color.md_theme_light_surfaceVariant, (int)((alpha << 24) | ((int)(red*.85) << 16) | ((int)(green*.85) << 8) | (int)(blue*.85)));
96 }
97 if (sharedPreferences.contains("custom_dark_theme_primary")) {
98 final var base = sharedPreferences.getInt("custom_dark_theme_primary", 0);
99 final var black = base == context.getColor(android.R.color.black);
100 final var roles = MaterialColors.getColorRoles(base, false);
101 colors.put(R.color.md_theme_dark_primary, black ? base : roles.getAccent());
102 colors.put(R.color.md_theme_dark_onPrimary, black ? context.getColor(R.color.white) : roles.getOnAccent());
103 colors.put(R.color.md_theme_dark_primaryContainer, black ? base : roles.getAccentContainer());
104 colors.put(R.color.md_theme_dark_onPrimaryContainer, black ? context.getColor(R.color.white) : roles.getOnAccentContainer());
105 }
106 if (sharedPreferences.contains("custom_dark_theme_primary_dark")) {
107 final var base = sharedPreferences.getInt("custom_dark_theme_primary_dark", 0);
108 final var black = base == context.getColor(android.R.color.black);
109 final var roles = MaterialColors.getColorRoles(base, false);
110 colors.put(R.color.md_theme_dark_secondary, black ? base : roles.getAccent());
111 colors.put(R.color.md_theme_dark_onSecondary, black ? context.getColor(R.color.white) : roles.getOnAccent());
112 colors.put(R.color.md_theme_dark_secondaryContainer, black ? base : roles.getAccentContainer());
113 colors.put(R.color.md_theme_dark_onSecondaryContainer, black ? context.getColor(R.color.white) : roles.getOnAccentContainer());
114 }
115 if (sharedPreferences.contains("custom_dark_theme_accent")) {
116 final var base = sharedPreferences.getInt("custom_dark_theme_accent", 0);
117 final var black = base == context.getColor(android.R.color.black);
118 final var roles = MaterialColors.getColorRoles(base, false);
119 colors.put(R.color.md_theme_dark_tertiary, black ? base : roles.getAccent());
120 colors.put(R.color.md_theme_dark_onTertiary, black ? context.getColor(R.color.white) : roles.getOnAccent());
121 colors.put(R.color.md_theme_dark_tertiaryContainer, black ? base : roles.getAccentContainer());
122 colors.put(R.color.md_theme_dark_onTertiaryContainer, black ? context.getColor(R.color.white) : roles.getOnAccentContainer());
123 }
124 if (sharedPreferences.contains("custom_dark_theme_background_primary")) {
125 int background_primary = sharedPreferences.getInt("custom_dark_theme_background_primary", 0);
126 int alpha = (background_primary >> 24) & 0xFF;
127 int red = (background_primary >> 16) & 0xFF;
128 int green = (background_primary >> 8) & 0xFF;
129 int blue = background_primary & 0xFF;
130 colors.put(R.color.md_theme_dark_background, background_primary);
131 colors.put(R.color.md_theme_dark_surface, background_primary);
132 colors.put(R.color.md_theme_dark_surfaceVariant, (int)((alpha << 24) | ((int)(40 + red*.84) << 16) | ((int)(40 + green*.84) << 8) | (int)(40 + blue*.84)));
133 }
134 if (colors.isEmpty()) return colors;
135
136 ResourcesLoader loader = ColorResourcesLoaderCreator.create(context, colors);
137 try {
138 if (loader != null) context.getResources().addLoaders(loader);
139 } catch (final IllegalArgumentException e) {
140 Log.w(Config.LOGTAG, "Custom colour failed: " + e);
141 }
142 return colors;
143 }
144}