/* * Copyright 2018 Allan Wang * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package com.pitchedapps.frost.injectors import android.content.Context import android.graphics.Color import ca.allanwang.kau.utils.adjustAlpha import ca.allanwang.kau.utils.colorToBackground import ca.allanwang.kau.utils.colorToForeground import ca.allanwang.kau.utils.isColorVisibleOn import ca.allanwang.kau.utils.toRgbaString import ca.allanwang.kau.utils.use import ca.allanwang.kau.utils.withAlpha import com.pitchedapps.frost.enums.FACEBOOK_BLUE import com.pitchedapps.frost.enums.Theme import com.pitchedapps.frost.enums.ThemeCategory import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.L import java.io.BufferedReader import java.io.FileNotFoundException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.koin.core.context.GlobalContext /** * Provides [InjectorContract] for each [ThemeCategory]. * Can be reloaded to take in changes from [Prefs] */ class ThemeProvider(private val context: Context, private val prefs: Prefs) { private var theme: Theme = Theme.values[prefs.theme] private val injectors: MutableMap = mutableMapOf() val textColor: Int get() = theme.textColorGetter(prefs) val accentColor: Int get() = theme.accentColorGetter(prefs) val accentColorForWhite: Int get() = when { accentColor.isColorVisibleOn(Color.WHITE) -> accentColor textColor.isColorVisibleOn(Color.WHITE) -> textColor else -> FACEBOOK_BLUE } val nativeBgColor: Int get() = bgColor.withAlpha(30) fun nativeBgColor(unread: Boolean) = bgColor .colorToForeground(if (unread) 0.7f else 0.0f) .withAlpha(30) val bgColor: Int get() = theme.backgroundColorGetter(prefs) val headerColor: Int get() = theme.headerColorGetter(prefs) val iconColor: Int get() = theme.iconColorGetter(prefs) val isCustomTheme: Boolean get() = theme == Theme.CUSTOM /** * Note that while this can be loaded from any thread, it is typically done through [preload]] */ fun injector(category: ThemeCategory): InjectorContract = injectors.getOrPut(category) { createInjector(category) } /** * Note that while this can be loaded from any thread, it is typically done through [preload] */ private fun createInjector(category: ThemeCategory): InjectorContract { val file = theme.file ?: return JsActions.EMPTY try { var content = context.assets.open("css/${category.folder}/themes/$file").bufferedReader() .use(BufferedReader::readText) if (theme == Theme.CUSTOM) { val bt = if (Color.alpha(bgColor) == 255) bgColor.toRgbaString() else "transparent" val bb = bgColor.colorToForeground(0.35f) content = content .replace("\$T\$", textColor.toRgbaString()) .replace("\$TT\$", textColor.colorToBackground(0.05f).toRgbaString()) .replace("\$TD\$", textColor.adjustAlpha(0.6f).toRgbaString()) .replace("\$A\$", accentColor.toRgbaString()) .replace("\$AT\$", iconColor.toRgbaString()) .replace("\$B\$", bgColor.toRgbaString()) .replace("\$BT\$", bt) .replace("\$BBT\$", bb.withAlpha(51).toRgbaString()) .replace("\$O\$", bgColor.withAlpha(255).toRgbaString()) .replace("\$OO\$", bb.withAlpha(255).toRgbaString()) .replace("\$D\$", textColor.adjustAlpha(0.3f).toRgbaString()) .replace("\$TI\$", bb.withAlpha(60).toRgbaString()) .replace("\$C\$", bt) } return JsBuilder().css(content).build() } catch (e: FileNotFoundException) { L.e(e) { "CssAssets file not found" } return JsActions.EMPTY } } fun setTheme(id: Int) { theme = Theme.values[id] reset() } fun reset() { injectors.clear() } suspend fun preload() { withContext(Dispatchers.IO) { reset() ThemeCategory.values().forEach { injector(it) } } } companion object { fun get(): ThemeProvider = GlobalContext.get().get() fun module() = org.koin.dsl.module { single { ThemeProvider(get(), get()) } } } }