Skip to content

Commit

Permalink
Reworked saving of themes and added ability to set defaults
Browse files Browse the repository at this point in the history
  • Loading branch information
prathameshmm02 committed Jun 10, 2022
1 parent 4a78d1b commit b073432
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ class ColorAdapter(private val colorArray: List<Int>) :
var checkedPosition = -1
private set

fun setCheckedPosition(value: Int) {
fun setCheckedPosition(value: Theme) {
val lastCheckedPosition: Int = checkedPosition
checkedPosition = value
checkedPosition = value.ordinal
notifyItemChanged(lastCheckedPosition)
notifyItemChanged(value)
notifyItemChanged(checkedPosition)
}

init {
Expand Down Expand Up @@ -45,7 +45,6 @@ class ColorAdapter(private val colorArray: List<Int>) :

override fun getItemCount() = colorArray.size


inner class ViewHolder(val binding: ItemColorBinding) : RecyclerView.ViewHolder(binding.root) {
init {
binding.colorView.setOnClickListener {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,31 @@ package com.quickersilver.themeengine

import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
import androidx.annotation.BoolRes
import androidx.annotation.ChecksSdkIntAtLeast
import androidx.annotation.IntegerRes

val Context.isDarkMode
get() = (resources.configuration.uiMode and
Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES

@ChecksSdkIntAtLeast(api = 31)
fun hasS() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S

fun Context.getBooleanSafe(@BoolRes res: Int, defaultValue: Boolean): Boolean {
return try {
resources.getBoolean(res)
} catch (e: Resources.NotFoundException) {
defaultValue
}
}

fun Context.getIntSafe(@IntegerRes res: Int, defaultValue: Int): Int {
return try {
resources.getInteger(res)
} catch (e: Resources.NotFoundException) {
defaultValue
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import android.content.DialogInterface.BUTTON_POSITIVE
import android.view.LayoutInflater
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.annotation.StyleRes
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
Expand All @@ -15,44 +14,27 @@ import com.quickersilver.themeengine.databinding.RecyclerviewBinding
/**
* Builder class to create a Theme Chooser Dialog
* @param context Context to use
* @param themes list of themes
* @param primaryColorsLight list of primary light colors of the above themes
* @param primaryColorsDark list of primary dark colors of the above themes
* @return ThemeChooserDialogBuilder
* @author Prathamesh M
*/
class ThemeChooserDialogBuilder(
private val context: Context,
private val themes: List<Int> = Themes.themes,
private val primaryColorsLight: List<Int> = Themes.primaryColorsLight,
private val primaryColorsDark: List<Int> = Themes.primaryColorsDark,
) {
class ThemeChooserDialogBuilder(private val context: Context) {

private lateinit var builder: MaterialAlertDialogBuilder

private lateinit var colorAdapter: ColorAdapter
private val themes = Theme.values()

init {
require(themes.size == primaryColorsDark.size) {
"List size of themes not equal to list size of primary colors"
}
require(primaryColorsDark.size == primaryColorsLight.size) {
"List size of primary colors is different"
}
createDialog()
}

private fun createDialog() {
val binding = RecyclerviewBinding.inflate(LayoutInflater.from(context))

val themeEngine = ThemeEngine.getInstance(context)
val colorArray = if (context.isDarkMode) {
primaryColorsDark
} else {
primaryColorsLight
}
val colorArray = themes.map { it.primaryColor }
colorAdapter = ColorAdapter(colorArray)
colorAdapter.setCheckedPosition(themes.indexOf(themeEngine.staticTheme))
colorAdapter.setCheckedPosition(themeEngine.staticTheme)
binding.recyclerView.apply {
layoutManager = GridLayoutManager(context, 4)
adapter = colorAdapter
Expand All @@ -70,6 +52,7 @@ class ThemeChooserDialogBuilder(
builder.setTitle(res)
return this
}

/**
* Set icon for the Dialog using the given drawable resource id
* @return This Builder object to allow for chaining of calls to set methods
Expand All @@ -89,8 +72,7 @@ class ThemeChooserDialogBuilder(
builder.setPositiveButton(text) { _, which ->
if (which == BUTTON_POSITIVE) {
val checkedPosition = colorAdapter.checkedPosition
val theme = themes[checkedPosition]
listener.onClick(checkedPosition, theme)
listener.onClick(checkedPosition, themes[checkedPosition])
}
}
return this
Expand All @@ -102,7 +84,10 @@ class ThemeChooserDialogBuilder(
* @param listener The OnClickListener to use when the button is clicked.
* @return This Builder object to allow for chaining of calls to set methods
*/
fun setPositiveButton(@StringRes res: Int, listener: OnClickListener): ThemeChooserDialogBuilder {
fun setPositiveButton(
@StringRes res: Int,
listener: OnClickListener
): ThemeChooserDialogBuilder {
setPositiveButton(context.getString(res), listener)
return this
}
Expand Down Expand Up @@ -150,7 +135,10 @@ class ThemeChooserDialogBuilder(
* @param listener The OnClickListener to use when the button is clicked.
* @return This Builder object to allow for chaining of calls to set methods
*/
fun setNeutralButton(@StringRes res: Int, listener: OnClickListener): ThemeChooserDialogBuilder {
fun setNeutralButton(
@StringRes res: Int,
listener: OnClickListener
): ThemeChooserDialogBuilder {
setNeutralButton(context.getString(res), listener)
return this
}
Expand All @@ -163,6 +151,6 @@ class ThemeChooserDialogBuilder(
}

fun interface OnClickListener {
fun onClick(position: Int, @StyleRes theme: Int)
fun onClick(position: Int, theme: Theme)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,34 @@ import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.edit
import androidx.preference.PreferenceManager

class ThemeEngine(context: Context) {
private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
private val prefs = context.getSharedPreferences("theme_engine_prefs", Context.MODE_PRIVATE)

private var isFirstStart
get() = prefs.getBoolean(FIRST_START, true)
set(value) = prefs.edit { putBoolean(FIRST_START, value) }

init {
if (isFirstStart) {
setDefaultValues(context)
isFirstStart = false
}
}

/**
* Returns current [ThemeMode].
* Setting this property applies the given theme mode to the activity.
*/
var themeMode: Int
get() = prefs.getInt(THEME_MODE, 0)
get() = prefs.getInt(THEME_MODE, ThemeMode.AUTO)
set(value) {
require(value in 0..2) {
"Incompatible value! Set this property with help of ThemeMode object."
}
prefs.edit { putInt(THEME_MODE, value) }
AppCompatDelegate.setDefaultNightMode(
when (themeMode) {
when (value) {
ThemeMode.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
ThemeMode.DARK -> AppCompatDelegate.MODE_NIGHT_YES
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
Expand Down Expand Up @@ -52,15 +65,15 @@ class ThemeEngine(context: Context) {
* @return a dynamic theme if [isDynamicTheme] is enabled or a static theme otherwise.
*/
fun getTheme(): Int {
return if (hasS() && isDynamicTheme) R.style.Theme_ThemeEngine_Dynamic else staticTheme
return if (hasS() && isDynamicTheme) R.style.Theme_ThemeEngine_Dynamic else staticTheme.themeId
}

/**
* Get current static app theme, the theme which is used when dynamic color is disabled
*/
var staticTheme
get() = prefs.getInt(APP_THEME, R.style.Theme_ThemeEngine_Blue)
set(value) = prefs.edit { putInt(APP_THEME, value) }
var staticTheme: Theme
get() = Theme.values()[prefs.getInt(APP_THEME, 1)]
set(value) = prefs.edit { putInt(APP_THEME, value.ordinal) }

/**
* Resets static theme
Expand All @@ -73,6 +86,13 @@ class ThemeEngine(context: Context) {
get() = prefs.getBoolean(TRUE_BLACK, false)
set(value) = prefs.edit { putBoolean(TRUE_BLACK, value) }

private fun setDefaultValues(context: Context) {
isTrueBlack = context.getBooleanSafe(R.bool.true_black, false)
themeMode = context.getIntSafe(R.integer.theme_mode, ThemeMode.AUTO)
prefs.edit { putInt(APP_THEME, context.getIntSafe(R.integer.static_theme, 1)) }
isDynamicTheme = context.getBooleanSafe(R.bool.dynamic_theme, hasS()) && hasS()
}

companion object {
private var INSTANCE: ThemeEngine? = null

Expand Down Expand Up @@ -119,6 +139,7 @@ class ThemeEngine(context: Context) {
private const val DYNAMIC_THEME = "dynamic_theme"
private const val APP_THEME = "app_theme"
private const val TRUE_BLACK = "true_black"
private const val FIRST_START = "first_start"
}
}

Expand Down
87 changes: 22 additions & 65 deletions themeEngine/src/main/java/com/quickersilver/themeengine/Themes.kt
Original file line number Diff line number Diff line change
@@ -1,69 +1,26 @@
package com.quickersilver.themeengine

object Themes {
val themes = listOf(
R.style.Theme_ThemeEngine_Amber,
R.style.Theme_ThemeEngine_Blue,
R.style.Theme_ThemeEngine_BlueVariant,
R.style.Theme_ThemeEngine_Brown,
R.style.Theme_ThemeEngine_Cyan,
R.style.Theme_ThemeEngine_DeepOrange,
R.style.Theme_ThemeEngine_DeepPurple,
R.style.Theme_ThemeEngine_Green,
R.style.Theme_ThemeEngine_Indigo,
R.style.Theme_ThemeEngine_LightBlue,
R.style.Theme_ThemeEngine_LightGreen,
R.style.Theme_ThemeEngine_Lime,
R.style.Theme_ThemeEngine_Orange,
R.style.Theme_ThemeEngine_Pink,
R.style.Theme_ThemeEngine_Purple,
R.style.Theme_ThemeEngine_Red,
R.style.Theme_ThemeEngine_Teal,
R.style.Theme_ThemeEngine_Violet,
R.style.Theme_ThemeEngine_Yellow,
)
import androidx.annotation.ColorRes
import androidx.annotation.StyleRes

val primaryColorsLight = listOf(
R.color.amber_light_primary,
R.color.blue_light_primary,
R.color.blue_variant_light_primary,
R.color.brown_light_primary,
R.color.cyan_light_primary,
R.color.deep_orange_light_primary,
R.color.deep_purple_light_primary,
R.color.green_light_primary,
R.color.indigo_light_primary,
R.color.light_blue_light_primary,
R.color.light_green_light_primary,
R.color.lime_light_primary,
R.color.orange_light_primary,
R.color.pink_light_primary,
R.color.purple_light_primary,
R.color.red_light_primary,
R.color.teal_light_primary,
R.color.violet_light_primary,
R.color.yellow_light_primary
)

val primaryColorsDark = listOf(
R.color.amber_dark_primary,
R.color.blue_dark_primary,
R.color.blue_variant_dark_primary,
R.color.brown_dark_primary,
R.color.cyan_dark_primary,
R.color.deep_orange_dark_primary,
R.color.deep_purple_dark_primary,
R.color.green_dark_primary,
R.color.indigo_dark_primary,
R.color.light_blue_dark_primary,
R.color.light_green_dark_primary,
R.color.lime_dark_primary,
R.color.orange_dark_primary,
R.color.pink_dark_primary,
R.color.purple_dark_primary,
R.color.red_dark_primary,
R.color.teal_dark_primary,
R.color.violet_dark_primary,
R.color.yellow_dark_primary,
)
enum class Theme(@StyleRes val themeId: Int, @ColorRes val primaryColor: Int) {
Amber(R.style.Theme_ThemeEngine_Amber, R.color.amber_primary),
Blue(R.style.Theme_ThemeEngine_Blue, R.color.blue_primary),
BlueVariant(R.style.Theme_ThemeEngine_BlueVariant, R.color.blue_variant_primary),
Brown(R.style.Theme_ThemeEngine_Brown, R.color.brown_primary),
Cyan(R.style.Theme_ThemeEngine_Cyan, R.color.cyan_primary),
DeepOrange(R.style.Theme_ThemeEngine_DeepOrange, R.color.deep_orange_primary),
DeepPurple(R.style.Theme_ThemeEngine_DeepPurple, R.color.deep_purple_primary),
Green(R.style.Theme_ThemeEngine_Green, R.color.green_primary),
Indigo(R.style.Theme_ThemeEngine_Indigo, R.color.indigo_primary),
LightBlue(R.style.Theme_ThemeEngine_LightBlue, R.color.light_blue_primary),
LightGreen(R.style.Theme_ThemeEngine_LightGreen, R.color.light_green_primary),
Lime(R.style.Theme_ThemeEngine_Lime, R.color.lime_primary),
Orange(R.style.Theme_ThemeEngine_Orange, R.color.orange_primary),
Pink(R.style.Theme_ThemeEngine_Pink, R.color.pink_primary),
Purple(R.style.Theme_ThemeEngine_Purple, R.color.purple_primary),
Red(R.style.Theme_ThemeEngine_Red, R.color.red_primary),
Teal(R.style.Theme_ThemeEngine_Teal, R.color.teal_primary),
Violet(R.style.Theme_ThemeEngine_Violet, R.color.violet_primary),
Yellow(R.style.Theme_ThemeEngine_Yellow, R.color.yellow_primary)
}
7 changes: 7 additions & 0 deletions themeEngine/src/main/res/values/defaults.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="true_black">false</bool>
<bool name="dynamic_theme">true</bool>
<integer name="theme_mode">0</integer>
<integer name="static_theme">1</integer>
</resources>

0 comments on commit b073432

Please sign in to comment.