ThemeHelper.java

  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}