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 final var colorMatch = sharedPreferences.getBoolean("custom_theme_color_match", false);
66 if (sharedPreferences.contains("custom_theme_primary")) {
67 final var base = sharedPreferences.getInt("custom_theme_primary", 0);
68 final var roles = MaterialColors.getColorRoles(base, true);
69 colors.put(R.color.md_theme_light_primary, colorMatch ? base : roles.getAccent());
70 colors.put(R.color.md_theme_light_onPrimary, roles.getOnAccent());
71 colors.put(R.color.md_theme_light_primaryContainer, colorMatch ? base : roles.getAccentContainer());
72 colors.put(R.color.md_theme_light_onPrimaryContainer, roles.getOnAccentContainer());
73 }
74 if (sharedPreferences.contains("custom_theme_primary_dark")) {
75 final var base = sharedPreferences.getInt("custom_theme_primary_dark", 0);
76 final var roles = MaterialColors.getColorRoles(base, true);
77 colors.put(R.color.md_theme_light_secondary, colorMatch ? base : roles.getAccent());
78 colors.put(R.color.md_theme_light_onSecondary, roles.getOnAccent());
79 colors.put(R.color.md_theme_light_secondaryContainer, colorMatch ? base : roles.getAccentContainer());
80 colors.put(R.color.md_theme_light_onSecondaryContainer, roles.getOnAccentContainer());
81 }
82 if (sharedPreferences.contains("custom_theme_accent")) {
83 final var base = sharedPreferences.getInt("custom_theme_accent", 0);
84 final var roles = MaterialColors.getColorRoles(base, true);
85 colors.put(R.color.md_theme_light_tertiary, colorMatch ? base : roles.getAccent());
86 colors.put(R.color.md_theme_light_onTertiary, roles.getOnAccent());
87 colors.put(R.color.md_theme_light_tertiaryContainer, colorMatch ? base : roles.getAccentContainer());
88 colors.put(R.color.md_theme_light_onTertiaryContainer, roles.getOnAccentContainer());
89 }
90 if (sharedPreferences.contains("custom_theme_background_primary")) {
91 int background_primary = sharedPreferences.getInt("custom_theme_background_primary", 0);
92 int alpha = (background_primary >> 24) & 0xFF;
93 int red = (background_primary >> 16) & 0xFF;
94 int green = (background_primary >> 8) & 0xFF;
95 int blue = background_primary & 0xFF;
96 colors.put(R.color.md_theme_light_background, background_primary);
97 colors.put(R.color.md_theme_light_surface, background_primary);
98 //colors.put(R.color.md_theme_light_surface, (int)((alpha << 24) | ((int)(red*.9) << 16) | ((int)(green*.9) << 8) | (int)(blue*.9)));
99 colors.put(R.color.md_theme_light_surfaceVariant, (int)((alpha << 24) | ((int)(red*.85) << 16) | ((int)(green*.85) << 8) | (int)(blue*.85)));
100 }
101 if (sharedPreferences.contains("custom_dark_theme_primary")) {
102 final var base = sharedPreferences.getInt("custom_dark_theme_primary", 0);
103 final var roles = MaterialColors.getColorRoles(base, false);
104 colors.put(R.color.md_theme_dark_primary, colorMatch ? base : roles.getAccent());
105 colors.put(R.color.md_theme_dark_onPrimary, roles.getOnAccent());
106 colors.put(R.color.md_theme_dark_primaryContainer, colorMatch ? base : roles.getAccentContainer());
107 colors.put(R.color.md_theme_dark_onPrimaryContainer, colorMatch && MaterialColors.isColorLight(base) ? roles.getOnAccent() : roles.getOnAccentContainer());
108 }
109 if (sharedPreferences.contains("custom_dark_theme_primary_dark")) {
110 final var base = sharedPreferences.getInt("custom_dark_theme_primary_dark", 0);
111 final var roles = MaterialColors.getColorRoles(base, false);
112 colors.put(R.color.md_theme_dark_secondary, colorMatch ? base : roles.getAccent());
113 colors.put(R.color.md_theme_dark_onSecondary, roles.getOnAccent());
114 colors.put(R.color.md_theme_dark_secondaryContainer, colorMatch ? base : roles.getAccentContainer());
115 colors.put(R.color.md_theme_dark_onSecondaryContainer, colorMatch && MaterialColors.isColorLight(base) ? roles.getOnAccent() : roles.getOnAccentContainer());
116 }
117 if (sharedPreferences.contains("custom_dark_theme_accent")) {
118 final var base = sharedPreferences.getInt("custom_dark_theme_accent", 0);
119 final var roles = MaterialColors.getColorRoles(base, false);
120 colors.put(R.color.md_theme_dark_tertiary, colorMatch ? base : roles.getAccent());
121 colors.put(R.color.md_theme_dark_onTertiary, roles.getOnAccent());
122 colors.put(R.color.md_theme_dark_tertiaryContainer, colorMatch ? base : roles.getAccentContainer());
123 colors.put(R.color.md_theme_dark_onTertiaryContainer, colorMatch && MaterialColors.isColorLight(base) ? roles.getOnAccent() : roles.getOnAccentContainer());
124 }
125 if (sharedPreferences.contains("custom_dark_theme_background_primary")) {
126 int background_primary = sharedPreferences.getInt("custom_dark_theme_background_primary", 0);
127 int alpha = (background_primary >> 24) & 0xFF;
128 int red = (background_primary >> 16) & 0xFF;
129 int green = (background_primary >> 8) & 0xFF;
130 int blue = background_primary & 0xFF;
131 colors.put(R.color.md_theme_dark_background, background_primary);
132 colors.put(R.color.md_theme_dark_surface, background_primary);
133 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)));
134 }
135 if (colors.isEmpty()) return colors;
136
137 ResourcesLoader loader = ColorResourcesLoaderCreator.create(context, colors);
138 try {
139 if (loader != null) context.getResources().addLoaders(loader);
140 } catch (final IllegalArgumentException e) {
141 Log.w(Config.LOGTAG, "Custom colour failed: " + e);
142 }
143 return colors;
144 }
145}