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}