Covert to kotlin

This commit is contained in:
hemanths@live.com 2018-12-26 08:59:12 +05:30
parent 79e0db37ef
commit a773074c77
51 changed files with 2817 additions and 0 deletions

View file

@ -0,0 +1,119 @@
package code.name.monkey.appthemehelper
import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.content.Context
import android.os.Build
import android.view.View
import androidx.annotation.ColorInt
import androidx.appcompat.widget.Toolbar
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
/**
* @author Karim Abou Zeid (kabouzeid)
*/
object ATH {
@SuppressLint("CommitPrefEdits")
fun didThemeValuesChange(context: Context, since: Long): Boolean {
return ThemeStore.isConfigured(context) && ThemeStore.prefs(context).getLong(ThemeStore.VALUES_CHANGED, -1) > since
}
fun setStatusbarColorAuto(activity: Activity) {
setStatusbarColor(activity, ThemeStore.statusBarColor(activity))
}
fun setStatusbarColor(activity: Activity, color: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.window.statusBarColor = color
setLightStatusbarAuto(activity, color)
}
}
fun setLightStatusbarAuto(activity: Activity, bgColor: Int) {
setLightStatusbar(activity, ColorUtil.isColorLight(bgColor))
}
fun setLightStatusbar(activity: Activity, enabled: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val decorView = activity.window.decorView
val systemUiVisibility = decorView.systemUiVisibility
if (enabled) {
decorView.systemUiVisibility = systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
decorView.systemUiVisibility = systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
}
}
fun setLightNavigationbar(activity: Activity, enabled: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val decorView = activity.window.decorView
var systemUiVisibility = decorView.systemUiVisibility
if (enabled) {
systemUiVisibility = systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
} else {
systemUiVisibility = systemUiVisibility and SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
}
decorView.systemUiVisibility = systemUiVisibility
}
}
fun setLightNavigationbarAuto(activity: Activity, bgColor: Int) {
setLightNavigationbar(activity, ColorUtil.isColorLight(bgColor))
}
fun setNavigationbarColorAuto(activity: Activity) {
setNavigationbarColor(activity, ThemeStore.navigationBarColor(activity))
}
fun setNavigationbarColor(activity: Activity, color: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
activity.window.navigationBarColor = color
} else {
activity.window.navigationBarColor = ColorUtil.darkenColor(color)
}
setLightNavigationbarAuto(activity, color)
}
fun setActivityToolbarColorAuto(activity: Activity, toolbar: Toolbar?) {
setActivityToolbarColor(activity, toolbar, ThemeStore.primaryColor(activity))
}
fun setActivityToolbarColor(activity: Activity, toolbar: Toolbar?,
color: Int) {
if (toolbar == null) {
return
}
toolbar.setBackgroundColor(color)
ToolbarContentTintHelper.setToolbarContentColorBasedOnToolbarColor(activity, toolbar, color)
}
fun setTaskDescriptionColorAuto(activity: Activity) {
setTaskDescriptionColor(activity, ThemeStore.primaryColor(activity))
}
fun setTaskDescriptionColor(activity: Activity, @ColorInt color: Int) {
var colorFinal = color
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Task description requires fully opaque color
colorFinal = ColorUtil.stripAlpha(colorFinal)
// Sets color of entry in the system recents page
activity.setTaskDescription(ActivityManager.TaskDescription(activity.title as String, null, colorFinal))
}
}
fun setTint(view: View, @ColorInt color: Int) {
TintHelper.setTintAuto(view, color, false)
}
fun setBackgroundTint(view: View, @ColorInt color: Int) {
TintHelper.setTintAuto(view, color, true)
}
}

View file

@ -0,0 +1,43 @@
package code.name.monkey.appthemehelper
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatActivity
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
open class ATHActivity : AppCompatActivity() {
private var updateTime: Long = -1
private val themeRes: Int
@StyleRes
get() = ThemeStore.activityTheme(this)
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(themeRes)
super.onCreate(savedInstanceState)
updateTime = System.currentTimeMillis()
}
override fun onResume() {
super.onResume()
if (ATH.didThemeValuesChange(this, updateTime)) {
onThemeChanged()
}
}
fun onThemeChanged() {
postRecreate()
}
fun postRecreate() {
// hack to prevent java.lang.RuntimeException: Performing pause of activity that is not resumed
// makes sure recreate() is called right after and not in onResume()
Handler().post { recreate() }
}
}

View file

@ -0,0 +1,288 @@
package code.name.monkey.appthemehelper
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Color
import androidx.annotation.AttrRes
import androidx.annotation.CheckResult
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.IntRange
import androidx.annotation.StyleRes
import androidx.core.content.ContextCompat
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
class ThemeStore @SuppressLint("CommitPrefEdits")
private constructor(private val mContext: Context) : ThemeStorePrefKeys, ThemeStoreInterface {
private val mEditor: SharedPreferences.Editor
init {
mEditor = prefs(mContext).edit()
}
override fun activityTheme(@StyleRes theme: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_ACTIVITY_THEME, theme)
return this
}
override fun primaryColor(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_PRIMARY_COLOR, color)
if (autoGeneratePrimaryDark(mContext))
primaryColorDark(ColorUtil.darkenColor(color))
return this
}
override fun primaryColorRes(@ColorRes colorRes: Int): ThemeStore {
return primaryColor(ContextCompat.getColor(mContext, colorRes))
}
override fun primaryColorAttr(@AttrRes colorAttr: Int): ThemeStore {
return primaryColor(ATHUtil.resolveColor(mContext, colorAttr))
}
override fun primaryColorDark(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_PRIMARY_COLOR_DARK, color)
return this
}
override fun primaryColorDarkRes(@ColorRes colorRes: Int): ThemeStore {
return primaryColorDark(ContextCompat.getColor(mContext, colorRes))
}
override fun primaryColorDarkAttr(@AttrRes colorAttr: Int): ThemeStore {
return primaryColorDark(ATHUtil.resolveColor(mContext, colorAttr))
}
override fun accentColor(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_ACCENT_COLOR, color)
return this
}
override fun accentColorRes(@ColorRes colorRes: Int): ThemeStore {
return accentColor(ContextCompat.getColor(mContext, colorRes))
}
override fun accentColorAttr(@AttrRes colorAttr: Int): ThemeStore {
return accentColor(ATHUtil.resolveColor(mContext, colorAttr))
}
override fun statusBarColor(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_STATUS_BAR_COLOR, color)
return this
}
override fun statusBarColorRes(@ColorRes colorRes: Int): ThemeStore {
return statusBarColor(ContextCompat.getColor(mContext, colorRes))
}
override fun statusBarColorAttr(@AttrRes colorAttr: Int): ThemeStore {
return statusBarColor(ATHUtil.resolveColor(mContext, colorAttr))
}
override fun navigationBarColor(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_NAVIGATION_BAR_COLOR, color)
return this
}
// Commit method
override fun navigationBarColorRes(@ColorRes colorRes: Int): ThemeStore {
return navigationBarColor(ContextCompat.getColor(mContext, colorRes))
}
// Static getters
override fun navigationBarColorAttr(@AttrRes colorAttr: Int): ThemeStore {
return navigationBarColor(ATHUtil.resolveColor(mContext, colorAttr))
}
override fun textColorPrimary(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY, color)
return this
}
override fun textColorPrimaryRes(@ColorRes colorRes: Int): ThemeStore {
return textColorPrimary(ContextCompat.getColor(mContext, colorRes))
}
override fun textColorPrimaryAttr(@AttrRes colorAttr: Int): ThemeStore {
return textColorPrimary(ATHUtil.resolveColor(mContext, colorAttr))
}
override fun textColorPrimaryInverse(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY_INVERSE, color)
return this
}
override fun textColorPrimaryInverseRes(@ColorRes colorRes: Int): ThemeStore {
return textColorPrimaryInverse(ContextCompat.getColor(mContext, colorRes))
}
override fun textColorPrimaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore {
return textColorPrimaryInverse(ATHUtil.resolveColor(mContext, colorAttr))
}
override fun textColorSecondary(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY, color)
return this
}
override fun textColorSecondaryRes(@ColorRes colorRes: Int): ThemeStore {
return textColorSecondary(ContextCompat.getColor(mContext, colorRes))
}
override fun textColorSecondaryAttr(@AttrRes colorAttr: Int): ThemeStore {
return textColorSecondary(ATHUtil.resolveColor(mContext, colorAttr))
}
override fun textColorSecondaryInverse(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY_INVERSE, color)
return this
}
override fun textColorSecondaryInverseRes(@ColorRes colorRes: Int): ThemeStore {
return textColorSecondaryInverse(ContextCompat.getColor(mContext, colorRes))
}
override fun textColorSecondaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore {
return textColorSecondaryInverse(ATHUtil.resolveColor(mContext, colorAttr))
}
override fun coloredStatusBar(colored: Boolean): ThemeStore {
mEditor.putBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARYDARK_STATUSBAR, colored)
return this
}
override fun coloredNavigationBar(applyToNavBar: Boolean): ThemeStore {
mEditor.putBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARY_NAVBAR, applyToNavBar)
return this
}
override fun autoGeneratePrimaryDark(autoGenerate: Boolean): ThemeStore {
mEditor.putBoolean(ThemeStorePrefKeys.KEY_AUTO_GENERATE_PRIMARYDARK, autoGenerate)
return this
}
override fun commit() {
mEditor.putLong(ThemeStorePrefKeys.VALUES_CHANGED, System.currentTimeMillis())
.putBoolean(ThemeStorePrefKeys.IS_CONFIGURED_KEY, true)
.commit()
}
companion object {
fun editTheme(context: Context): ThemeStore {
return ThemeStore(context)
}
@CheckResult
protected fun prefs(context: Context): SharedPreferences {
return context.getSharedPreferences(ThemeStorePrefKeys.CONFIG_PREFS_KEY_DEFAULT, Context.MODE_PRIVATE)
}
fun markChanged(context: Context) {
ThemeStore(context).commit()
}
@CheckResult
@StyleRes
fun activityTheme(context: Context): Int {
return prefs(context).getInt(ThemeStorePrefKeys.KEY_ACTIVITY_THEME, 0)
}
@CheckResult
@ColorInt
fun primaryColor(context: Context): Int {
return prefs(context).getInt(ThemeStorePrefKeys.KEY_PRIMARY_COLOR, ATHUtil.resolveColor(context, R.attr.colorPrimary, Color.parseColor("#455A64")))
}
@CheckResult
@ColorInt
fun primaryColorDark(context: Context): Int {
return prefs(context).getInt(ThemeStorePrefKeys.KEY_PRIMARY_COLOR_DARK, ATHUtil.resolveColor(context, R.attr.colorPrimaryDark, Color.parseColor("#37474F")))
}
@CheckResult
@ColorInt
fun accentColor(context: Context): Int {
return prefs(context).getInt(ThemeStorePrefKeys.KEY_ACCENT_COLOR, ATHUtil.resolveColor(context, R.attr.colorAccent, Color.parseColor("#263238")))
}
@CheckResult
@ColorInt
fun statusBarColor(context: Context): Int {
return if (!coloredStatusBar(context)) {
Color.BLACK
} else prefs(context).getInt(ThemeStorePrefKeys.KEY_STATUS_BAR_COLOR, primaryColorDark(context))
}
@CheckResult
@ColorInt
fun navigationBarColor(context: Context): Int {
return if (!coloredNavigationBar(context)) {
Color.BLACK
} else prefs(context).getInt(ThemeStorePrefKeys.KEY_NAVIGATION_BAR_COLOR, primaryColor(context))
}
@CheckResult
@ColorInt
fun textColorPrimary(context: Context): Int {
return prefs(context).getInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY, ATHUtil.resolveColor(context, android.R.attr.textColorPrimary))
}
@CheckResult
@ColorInt
fun textColorPrimaryInverse(context: Context): Int {
return prefs(context).getInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY_INVERSE, ATHUtil.resolveColor(context, android.R.attr.textColorPrimaryInverse))
}
@CheckResult
@ColorInt
fun textColorSecondary(context: Context): Int {
return prefs(context).getInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY, ATHUtil.resolveColor(context, android.R.attr.textColorSecondary))
}
@CheckResult
@ColorInt
fun textColorSecondaryInverse(context: Context): Int {
return prefs(context).getInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY_INVERSE, ATHUtil.resolveColor(context, android.R.attr.textColorSecondaryInverse))
}
@CheckResult
fun coloredStatusBar(context: Context): Boolean {
return prefs(context).getBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARYDARK_STATUSBAR, true)
}
@CheckResult
fun coloredNavigationBar(context: Context): Boolean {
return prefs(context).getBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARY_NAVBAR, false)
}
@CheckResult
fun autoGeneratePrimaryDark(context: Context): Boolean {
return prefs(context).getBoolean(ThemeStorePrefKeys.KEY_AUTO_GENERATE_PRIMARYDARK, true)
}
@CheckResult
fun isConfigured(context: Context): Boolean {
return prefs(context).getBoolean(ThemeStorePrefKeys.IS_CONFIGURED_KEY, false)
}
@SuppressLint("CommitPrefEdits")
fun isConfigured(context: Context, @IntRange(from = 0, to = Integer.MAX_VALUE.toLong()) version: Int): Boolean {
val prefs = prefs(context)
val lastVersion = prefs.getInt(ThemeStorePrefKeys.IS_CONFIGURED_VERSION_KEY, -1)
if (version > lastVersion) {
prefs.edit().putInt(ThemeStorePrefKeys.IS_CONFIGURED_VERSION_KEY, version).commit()
return false
}
return true
}
}
}

View file

@ -0,0 +1,94 @@
package code.name.monkey.appthemehelper
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.StyleRes
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
internal interface ThemeStoreInterface {
// Activity theme
fun activityTheme(@StyleRes theme: Int): ThemeStore
// Primary colors
fun primaryColor(@ColorInt color: Int): ThemeStore
fun primaryColorRes(@ColorRes colorRes: Int): ThemeStore
fun primaryColorAttr(@AttrRes colorAttr: Int): ThemeStore
fun autoGeneratePrimaryDark(autoGenerate: Boolean): ThemeStore
fun primaryColorDark(@ColorInt color: Int): ThemeStore
fun primaryColorDarkRes(@ColorRes colorRes: Int): ThemeStore
fun primaryColorDarkAttr(@AttrRes colorAttr: Int): ThemeStore
// Accent colors
fun accentColor(@ColorInt color: Int): ThemeStore
fun accentColorRes(@ColorRes colorRes: Int): ThemeStore
fun accentColorAttr(@AttrRes colorAttr: Int): ThemeStore
// Status bar color
fun statusBarColor(@ColorInt color: Int): ThemeStore
fun statusBarColorRes(@ColorRes colorRes: Int): ThemeStore
fun statusBarColorAttr(@AttrRes colorAttr: Int): ThemeStore
// Navigation bar color
fun navigationBarColor(@ColorInt color: Int): ThemeStore
fun navigationBarColorRes(@ColorRes colorRes: Int): ThemeStore
fun navigationBarColorAttr(@AttrRes colorAttr: Int): ThemeStore
// Primary text color
fun textColorPrimary(@ColorInt color: Int): ThemeStore
fun textColorPrimaryRes(@ColorRes colorRes: Int): ThemeStore
fun textColorPrimaryAttr(@AttrRes colorAttr: Int): ThemeStore
fun textColorPrimaryInverse(@ColorInt color: Int): ThemeStore
fun textColorPrimaryInverseRes(@ColorRes colorRes: Int): ThemeStore
fun textColorPrimaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore
// Secondary text color
fun textColorSecondary(@ColorInt color: Int): ThemeStore
fun textColorSecondaryRes(@ColorRes colorRes: Int): ThemeStore
fun textColorSecondaryAttr(@AttrRes colorAttr: Int): ThemeStore
fun textColorSecondaryInverse(@ColorInt color: Int): ThemeStore
fun textColorSecondaryInverseRes(@ColorRes colorRes: Int): ThemeStore
fun textColorSecondaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore
// Toggle configurations
fun coloredStatusBar(colored: Boolean): ThemeStore
fun coloredNavigationBar(applyToNavBar: Boolean): ThemeStore
// Commit/apply
fun commit()
}

View file

@ -0,0 +1,31 @@
package code.name.monkey.appthemehelper
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
internal interface ThemeStorePrefKeys {
companion object {
const val CONFIG_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]"
const val IS_CONFIGURED_KEY = "is_configured"
const val IS_CONFIGURED_VERSION_KEY = "is_configured_version"
const val VALUES_CHANGED = "values_changed"
const val KEY_ACTIVITY_THEME = "activity_theme"
const val KEY_PRIMARY_COLOR = "primary_color"
const val KEY_PRIMARY_COLOR_DARK = "primary_color_dark"
const val KEY_ACCENT_COLOR = "accent_color"
const val KEY_STATUS_BAR_COLOR = "status_bar_color"
const val KEY_NAVIGATION_BAR_COLOR = "navigation_bar_color"
const val KEY_TEXT_COLOR_PRIMARY = "text_color_primary"
const val KEY_TEXT_COLOR_PRIMARY_INVERSE = "text_color_primary_inverse"
const val KEY_TEXT_COLOR_SECONDARY = "text_color_secondary"
const val KEY_TEXT_COLOR_SECONDARY_INVERSE = "text_color_secondary_inverse"
const val KEY_APPLY_PRIMARYDARK_STATUSBAR = "apply_primarydark_statusbar"
const val KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar"
const val KEY_AUTO_GENERATE_PRIMARYDARK = "auto_generate_primarydark"
}
}

View file

@ -0,0 +1,12 @@
package code.name.monkey.appthemehelper.common
import androidx.appcompat.widget.Toolbar
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
class ATHActionBarActivity : ATHToolbarActivity() {
override fun getATHToolbar(): Toolbar? {
return ToolbarContentTintHelper.getSupportActionBarView(supportActionBar)
}
}

View file

@ -0,0 +1,70 @@
package code.name.monkey.appthemehelper.common.prefs
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.preference.CheckBoxPreference
import android.preference.Preference
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.R
import code.name.monkey.appthemehelper.ThemeStore
/**
* @author Aidan Follestad (afollestad)
*/
class ATECheckBoxPreference @TargetApi(Build.VERSION_CODES.LOLLIPOP) constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : CheckBoxPreference(context, attrs, defStyleAttr, defStyleRes) {
init {
init()
}
private fun init() {
layoutResource = R.layout.ate_preference_custom
try {
val canRecycleLayoutField = Preference::class.java.getDeclaredField("mCanRecycleLayout")
canRecycleLayoutField.isAccessible = true
canRecycleLayoutField.setBoolean(this, true)
} catch (ignored: Exception) {
}
try {
val hasSpecifiedLayout = Preference::class.java.getDeclaredField("mHasSpecifiedLayout")
hasSpecifiedLayout.isAccessible = true
hasSpecifiedLayout.setBoolean(this, true)
} catch (ignored: Exception) {
}
}
override fun onBindView(view: View) {
super.onBindView(view)
val parentCheckbox = findCheckboxView(view)
if (parentCheckbox != null) {
ATH.setTint(parentCheckbox, ThemeStore.accentColor(view.context))
}
}
private fun findCheckboxView(view: View): View? {
if (view is ViewGroup) {
for (i in 0 until view.childCount) {
val child = view.getChildAt(i)
if (child is CheckBox) {
return child
} else if (child is ViewGroup) {
val potentialCheckbox = findCheckboxView(child)
if (potentialCheckbox != null) return potentialCheckbox
}
}
} else if (view is CheckBox) {
return view
}
return null
}
}

View file

@ -0,0 +1,54 @@
package code.name.monkey.appthemehelper.common.prefs
import android.content.Context
import android.preference.Preference
import android.util.AttributeSet
import android.view.View
import code.name.monkey.appthemehelper.R
/**
* @author Aidan Follestad (afollestad)
*/
class ATEColorPreference(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : Preference(context, attrs, defStyleAttr) {
private var mView: View? = null
private var color: Int = 0
private var border: Int = 0
init {
init()
}
private fun init() {
layoutResource = R.layout.ate_preference_custom
widgetLayoutResource = R.layout.ate_preference_color
isPersistent = false
}
override fun onBindView(view: View) {
super.onBindView(view)
mView = view
invalidateColor()
}
fun setColor(color: Int, border: Int) {
this.color = color
this.border = border
invalidateColor()
}
private fun invalidateColor() {
if (mView != null) {
val circle = mView!!.findViewById<View>(R.id.circle) as BorderCircleView
if (this.color != 0) {
circle.visibility = View.VISIBLE
circle.setBackgroundColor(color)
circle.setBorderColor(border)
} else {
circle.visibility = View.GONE
}
}
}
}

View file

@ -0,0 +1,20 @@
package code.name.monkey.appthemehelper.common.prefs
import android.content.Context
import code.name.monkey.appthemehelper.R
import com.afollestad.materialdialogs.prefs.MaterialDialogPreference
/**
* @author Aidan Follestad (afollestad)
*/
class ATEDialogPreference(context: Context) : MaterialDialogPreference(context) {
init {
init()
}
private fun init() {
layoutResource = R.layout.ate_preference_custom
}
}

View file

@ -0,0 +1,20 @@
package code.name.monkey.appthemehelper.common.prefs
import android.content.Context
import android.util.AttributeSet
import com.afollestad.materialdialogs.prefs.MaterialEditTextPreference
/**
* @author Aidan Follestad (afollestad)
*/
class ATEEditTextPreference(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : MaterialEditTextPreference(context, attrs, defStyleAttr, defStyleRes) {
init {
init()
}
private fun init() {
layoutResource = code.name.monkey.appthemehelper.R.layout.ate_preference_custom
}
}

View file

@ -0,0 +1,35 @@
package code.name.monkey.appthemehelper.common.prefs
import android.content.Context
import android.util.AttributeSet
import com.afollestad.materialdialogs.prefs.MaterialListPreference
import code.name.monkey.appthemehelper.R
/**
* @author Aidan Follestad (afollestad)
*/
class ATEListPreference : MaterialListPreference {
constructor(context: Context) : super(context) {
init()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init()
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init()
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init()
}
private fun init() {
layoutResource = R.layout.ate_preference_custom
if (summary == null || summary.toString().trim { it <= ' ' }.isEmpty())
summary = "%s"
}
}

View file

@ -0,0 +1,21 @@
package code.name.monkey.appthemehelper.common.prefs
import android.content.Context
import android.util.AttributeSet
import com.afollestad.materialdialogs.prefs.MaterialListPreference
import code.name.monkey.appthemehelper.R
/**
* @author Aidan Follestad (afollestad)
*/
class ATEMultiSelectPreference(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : MaterialListPreference(context, attrs, defStyleAttr, defStyleRes) {
init {
init()
}
private fun init() {
layoutResource = R.layout.ate_preference_custom
}
}

View file

@ -0,0 +1,23 @@
package code.name.monkey.appthemehelper.common.prefs
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.preference.Preference
import android.util.AttributeSet
import code.name.monkey.appthemehelper.R
/**
* @author Aidan Follestad (afollestad)
*/
class ATEPreference @TargetApi(Build.VERSION_CODES.LOLLIPOP) constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : Preference(context, attrs, defStyleAttr, defStyleRes) {
init {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
layoutResource = R.layout.ate_preference_custom
}
}

View file

@ -0,0 +1,20 @@
package code.name.monkey.appthemehelper.common.prefs
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.preference.PreferenceCategory
import android.util.AttributeSet
import android.view.View
import android.widget.TextView
import code.name.monkey.appthemehelper.ThemeStore
class ATEPreferenceCategory @TargetApi(Build.VERSION_CODES.LOLLIPOP) constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : PreferenceCategory(context, attrs, defStyleAttr, defStyleRes) {
override fun onBindView(view: View) {
super.onBindView(view)
val mTitle = view.findViewById<View>(android.R.id.title) as TextView
mTitle.setTextColor(ThemeStore.accentColor(view.context))
}
}

View file

@ -0,0 +1,110 @@
package code.name.monkey.appthemehelper.common.prefs
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.preference.Preference
import android.preference.SwitchPreference
import androidx.appcompat.widget.SwitchCompat
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.Switch
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.R
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.common.views.ATESwitch
import java.lang.reflect.Field
/**
* @author Aidan Follestad (afollestad)
*/
class ATESwitchPreference : SwitchPreference {
private var mSwitch: ATESwitch? = null
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
layoutResource = R.layout.ate_preference_custom
if (COMPAT_MODE) {
widgetLayoutResource = R.layout.ate_preference_switch
} else {
try {
val canRecycleLayoutField = Preference::class.java.getDeclaredField("mCanRecycleLayout")
canRecycleLayoutField.isAccessible = true
canRecycleLayoutField.setBoolean(this, true)
} catch (ignored: Exception) {
}
try {
val hasSpecifiedLayout = Preference::class.java.getDeclaredField("mHasSpecifiedLayout")
hasSpecifiedLayout.isAccessible = true
hasSpecifiedLayout.setBoolean(this, true)
} catch (ignored: Exception) {
}
}
}
override fun onBindView(view: View) {
super.onBindView(view)
if (COMPAT_MODE) {
mSwitch = view.findViewById<View>(R.id.switchWidget) as ATESwitch
mSwitch!!.isChecked = isChecked
} else {
val parentSwitch = findSwitchView(view)
if (parentSwitch != null) {
ATH.setTint(parentSwitch, ThemeStore.accentColor(view.context))
}
}
}
private fun findSwitchView(view: View): View? {
if (view is ViewGroup) {
for (i in 0 until view.childCount) {
val child = view.getChildAt(i)
if (child is Switch || child is SwitchCompat) {
return child
} else if (child is ViewGroup) {
val potentialSwitch = findSwitchView(child)
if (potentialSwitch != null) return potentialSwitch
}
}
} else if (view is Switch || view is SwitchCompat) {
return view
}
return null
}
override fun setChecked(checked: Boolean) {
super.setChecked(checked)
if (COMPAT_MODE) {
if (mSwitch != null) {
mSwitch!!.isChecked = checked
}
}
}
companion object {
internal val COMPAT_MODE = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
}
}

View file

@ -0,0 +1,98 @@
package code.name.monkey.appthemehelper.common.prefs
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.Drawable
import androidx.core.content.ContextCompat
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import code.name.monkey.appthemehelper.R
class BorderCircleView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : FrameLayout(context, attrs, defStyleAttr) {
private val mCheck: Drawable? = ContextCompat.getDrawable(context, R.drawable.ate_check)
private val paint: Paint = Paint()
private val paintBorder: Paint
private val borderWidth: Int = resources.getDimension(R.dimen.ate_circleview_border).toInt()
private var paintCheck: Paint? = null
private var blackFilter: PorterDuffColorFilter? = null
private var whiteFilter: PorterDuffColorFilter? = null
init {
paint.isAntiAlias = true
paintBorder = Paint()
paintBorder.isAntiAlias = true
paintBorder.color = Color.BLACK
setWillNotDraw(false)
}
override fun setBackgroundColor(color: Int) {
paint.color = color
requestLayout()
invalidate()
}
fun setBorderColor(color: Int) {
paintBorder.color = color
requestLayout()
invalidate()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val widthMode = View.MeasureSpec.getMode(widthMeasureSpec)
val heightMode = View.MeasureSpec.getMode(heightMeasureSpec)
if (widthMode == View.MeasureSpec.EXACTLY && heightMode != View.MeasureSpec.EXACTLY) {
val width = View.MeasureSpec.getSize(widthMeasureSpec)
var height = width
if (heightMode == View.MeasureSpec.AT_MOST) {
height = Math.min(height, View.MeasureSpec.getSize(heightMeasureSpec))
}
setMeasuredDimension(width, height)
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
var canvasSize = canvas.width
if (canvas.height < canvasSize) {
canvasSize = canvas.height
}
val circleCenter = (canvasSize - borderWidth * 2) / 2
canvas.drawCircle((circleCenter + borderWidth).toFloat(), (circleCenter + borderWidth).toFloat(), (canvasSize - borderWidth * 2) / 2 + borderWidth - 4.0f, paintBorder)
canvas.drawCircle((circleCenter + borderWidth).toFloat(), (circleCenter + borderWidth).toFloat(), (canvasSize - borderWidth * 2) / 2 - 4.0f, paint)
if (isActivated) {
val offset = canvasSize / 2 - mCheck!!.intrinsicWidth / 2
if (paintCheck == null) {
paintCheck = Paint()
paintCheck!!.isAntiAlias = true
}
if (whiteFilter == null || blackFilter == null) {
blackFilter = PorterDuffColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN)
whiteFilter = PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)
}
if (paint.color == Color.WHITE) {
paintCheck!!.colorFilter = blackFilter
} else {
paintCheck!!.colorFilter = whiteFilter
}
mCheck.setBounds(offset, offset, mCheck.intrinsicWidth - offset, mCheck.intrinsicHeight - offset)
mCheck.draw(canvas)
}
}
}

View file

@ -0,0 +1,38 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import androidx.preference.CheckBoxPreference
import android.util.AttributeSet
import code.name.monkey.appthemehelper.R
/**
* @author Aidan Follestad (afollestad)
*/
class ATECheckBoxPreference : CheckBoxPreference {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
layoutResource = R.layout.ate_preference_custom_support
widgetLayoutResource = R.layout.ate_preference_checkbox
}
}

View file

@ -0,0 +1,64 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7
import android.content.Context
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import android.util.AttributeSet
import android.view.View
import code.name.monkey.appthemehelper.R
import code.name.monkey.appthemehelper.common.prefs.BorderCircleView
/**
* @author Aidan Follestad (afollestad)
*/
class ATEColorPreference(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : Preference(context, attrs, defStyleAttr) {
private var mView: View? = null
private var color: Int = 0
private var border: Int = 0
constructor(context: Context) : this(context, null, 0) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) {
init(context, attrs)
}
init {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
layoutResource = R.layout.ate_preference_custom_support
widgetLayoutResource = R.layout.ate_preference_color
isPersistent = false
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
mView = holder.itemView
invalidateColor()
}
fun setColor(color: Int, border: Int) {
this.color = color
this.border = border
invalidateColor()
}
private fun invalidateColor() {
if (mView != null) {
val circle = mView!!.findViewById<View>(R.id.circle) as BorderCircleView
if (this.color != 0) {
circle.visibility = View.VISIBLE
circle.setBackgroundColor(color)
circle.setBorderColor(border)
} else {
circle.visibility = View.GONE
}
}
}
}

View file

@ -0,0 +1,34 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7
import android.content.Context
import androidx.preference.DialogPreference
import android.util.AttributeSet
import code.name.monkey.appthemehelper.R
/**
* @author Aidan Follestad (afollestad)
*/
open class ATEDialogPreference : DialogPreference {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
layoutResource = R.layout.ate_preference_custom_support
}
}

View file

@ -0,0 +1,34 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7
import android.content.Context
import androidx.preference.EditTextPreference
import android.util.AttributeSet
import code.name.monkey.appthemehelper.R
/**
* @author Aidan Follestad (afollestad)
*/
class ATEEditTextPreference : EditTextPreference {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
layoutResource = R.layout.ate_preference_custom_support
}
}

View file

@ -0,0 +1,35 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7
import android.content.Context
import androidx.preference.ListPreference
import android.util.AttributeSet
import code.name.monkey.appthemehelper.R
/**
* @author Aidan Follestad (afollestad)
*/
class ATEListPreference : ListPreference {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
layoutResource = R.layout.ate_preference_custom_support
if (summary == null || summary.toString().trim { it <= ' ' }.isEmpty())
summary = "%s"
}
}

View file

@ -0,0 +1,36 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import androidx.preference.Preference
import android.util.AttributeSet
import code.name.monkey.appthemehelper.R
/**
* @author Aidan Follestad (afollestad)
*/
class ATEPreference : Preference {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
layoutResource = R.layout.ate_preference_custom_support
}
}

View file

@ -0,0 +1,42 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceViewHolder
import android.util.AttributeSet
import android.widget.TextView
import code.name.monkey.appthemehelper.R
import code.name.monkey.appthemehelper.ThemeStore
class ATEPreferenceCategory : PreferenceCategory {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context) : super(context) {
init(context, null)
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
val mTitle = holder.itemView as TextView
mTitle.setTextColor(ThemeStore.accentColor(context))
}
private fun init(context: Context, attrs: AttributeSet?) {
layoutResource = R.layout.ate_preference_category
}
}

View file

@ -0,0 +1,52 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7
import android.annotation.SuppressLint
import androidx.fragment.app.DialogFragment
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import code.name.monkey.appthemehelper.common.prefs.supportv7.dialogs.ATEEditTextPreferenceDialogFragmentCompat
import code.name.monkey.appthemehelper.common.prefs.supportv7.dialogs.ATEListPreferenceDialogFragmentCompat
import code.name.monkey.appthemehelper.common.prefs.supportv7.dialogs.ATEPreferenceDialogFragment
/**
* @author Karim Abou Zeid (kabouzeid)
*/
@SuppressLint("RestrictedApi")
abstract class ATEPreferenceFragmentCompat : PreferenceFragmentCompat() {
override fun onDisplayPreferenceDialog(preference: Preference) {
if (this.callbackFragment is PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback) {
(this.callbackFragment as PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback).onPreferenceDisplayDialog(this, preference)
return
}
if (this.activity is PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback) {
(this.activity as PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback).onPreferenceDisplayDialog(this, preference)
return
}
if (this.fragmentManager!!.findFragmentByTag("android.support.v7.preference.PreferenceFragment.DIALOG") == null) {
val dialogFragment = onCreatePreferenceDialog(preference)
if (dialogFragment != null) {
dialogFragment.setTargetFragment(this, 0)
dialogFragment.show(this.fragmentManager!!, "android.support.v7.preference.PreferenceFragment.DIALOG")
return
}
}
super.onDisplayPreferenceDialog(preference)
}
open fun onCreatePreferenceDialog(preference: Preference): DialogFragment? {
if (preference is ATEEditTextPreference) {
return ATEEditTextPreferenceDialogFragmentCompat.newInstance(preference.getKey())
} else if (preference is ATEListPreference) {
return ATEListPreferenceDialogFragmentCompat.newInstance(preference.getKey())
} else if (preference is ATEDialogPreference) {
return ATEPreferenceDialogFragment.newInstance(preference.getKey())
}
return null
}
}

View file

@ -0,0 +1,8 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7
import android.content.Context
import android.util.AttributeSet
import androidx.preference.SeekBarPreference
class ATESeekBarPreference(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : SeekBarPreference(context, attrs, defStyleAttr, defStyleRes)

View file

@ -0,0 +1,37 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import androidx.preference.CheckBoxPreference
import android.util.AttributeSet
import code.name.monkey.appthemehelper.R
/**
* @author Aidan Follestad (afollestad)
*/
class ATESwitchPreference : CheckBoxPreference {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
layoutResource = R.layout.ate_preference_custom_support
widgetLayoutResource = R.layout.ate_preference_switch_support
}
}

View file

@ -0,0 +1,50 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7.dialogs
import android.os.Bundle
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEEditTextPreference
import com.afollestad.materialdialogs.MaterialDialog
/**
* @author Karim Abou Zeid (kabouzeid)
*/
class ATEEditTextPreferenceDialogFragmentCompat : ATEPreferenceDialogFragment(), MaterialDialog.InputCallback {
private var input: CharSequence? = null
private val editTextPreference: ATEEditTextPreference
get() = preference as ATEEditTextPreference
override fun onPrepareDialogBuilder(builder: MaterialDialog.Builder) {
super.onPrepareDialogBuilder(builder)
builder.input("", editTextPreference.text, this)
}
override fun needInputMethod(): Boolean {
return true
}
override fun onDialogClosed(positiveResult: Boolean) {
if (positiveResult) {
val value = input!!.toString()
if (this.editTextPreference.callChangeListener(value)) {
this.editTextPreference.text = value
}
}
}
override fun onInput(dialog: MaterialDialog, input: CharSequence) {
this.input = input
}
companion object {
fun newInstance(key: String): ATEEditTextPreferenceDialogFragmentCompat {
val fragment = ATEEditTextPreferenceDialogFragmentCompat()
val b = Bundle(1)
b.putString(ATEPreferenceDialogFragment.ARG_KEY, key)
fragment.arguments = b
return fragment
}
}
}

View file

@ -0,0 +1,73 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7.dialogs
import android.os.Bundle
import androidx.preference.ListPreference
import android.view.View
import com.afollestad.materialdialogs.DialogAction
import com.afollestad.materialdialogs.MaterialDialog
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEListPreference
/**
* @author Karim Abou Zeid (kabouzeid)
*/
class ATEListPreferenceDialogFragmentCompat : ATEPreferenceDialogFragment(), MaterialDialog.ListCallbackSingleChoice {
private var mClickedDialogEntryIndex: Int = 0
private val listPreference: ATEListPreference
get() = preference as ATEListPreference
override fun onPrepareDialogBuilder(builder: MaterialDialog.Builder) {
super.onPrepareDialogBuilder(builder)
val preference = listPreference
if (preference.entries == null || preference.entryValues == null) {
throw IllegalStateException(
"ListPreference requires an entries array and an entryValues array.")
}
mClickedDialogEntryIndex = preference.findIndexOfValue(preference.value)
builder.items(*preference.entries)
.alwaysCallSingleChoiceCallback()
.itemsCallbackSingleChoice(mClickedDialogEntryIndex, this)
/*
* The typical interaction for list-based dialogs is to have
* click-on-an-item dismiss the dialog instead of the user having to
* press 'Ok'.
*/
builder.positiveText("")
builder.negativeText("")
builder.neutralText("")
}
override fun onDialogClosed(positiveResult: Boolean) {
val preference = listPreference
if (positiveResult && mClickedDialogEntryIndex >= 0 &&
preference.entryValues != null) {
val value = preference.entryValues[mClickedDialogEntryIndex].toString()
if (preference.callChangeListener(value)) {
preference.value = value
}
}
}
override fun onSelection(dialog: MaterialDialog, itemView: View, which: Int, text: CharSequence): Boolean {
mClickedDialogEntryIndex = which
onClick(dialog, DialogAction.POSITIVE)
dismiss()
return true
}
companion object {
fun newInstance(key: String): ATEListPreferenceDialogFragmentCompat {
val fragment = ATEListPreferenceDialogFragmentCompat()
val b = Bundle(1)
b.putString(ATEPreferenceDialogFragment.ARG_KEY, key)
fragment.arguments = b
return fragment
}
}
}

View file

@ -0,0 +1,85 @@
package code.name.monkey.appthemehelper.common.prefs.supportv7.dialogs
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.preference.DialogPreference
import com.afollestad.materialdialogs.DialogAction
import com.afollestad.materialdialogs.MaterialDialog
/**
* @author Karim Abou Zeid (kabouzeid)
*/
open class ATEPreferenceDialogFragment : DialogFragment(), MaterialDialog.SingleButtonCallback {
private var mWhichButtonClicked: DialogAction? = null
var preference: DialogPreference? = null
private set
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val rawFragment = this.targetFragment
if (rawFragment !is DialogPreference.TargetFragment) {
throw IllegalStateException("Target fragment must implement TargetFragment interface")
} else {
val fragment = rawFragment as DialogPreference.TargetFragment?
val key = this.arguments!!.getString(ARG_KEY)
this.preference = fragment!!.findPreference(key) as DialogPreference
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val context = this.activity
val builder = MaterialDialog.Builder(context!!)
.title(this.preference!!.dialogTitle)
.icon(this.preference!!.dialogIcon)
.onAny(this)
.positiveText(this.preference!!.positiveButtonText)
.negativeText(this.preference!!.negativeButtonText)
builder.content(this.preference!!.dialogMessage)
this.onPrepareDialogBuilder(builder)
val dialog = builder.build()
if (this.needInputMethod()) {
this.requestInputMethod(dialog)
}
return dialog
}
protected open fun onPrepareDialogBuilder(builder: MaterialDialog.Builder) {}
protected open fun needInputMethod(): Boolean {
return false
}
private fun requestInputMethod(dialog: Dialog) {
val window = dialog.window
window!!.setSoftInputMode(5)
}
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
mWhichButtonClicked = which
}
override fun onDismiss(dialog: DialogInterface?) {
super.onDismiss(dialog)
onDialogClosed(mWhichButtonClicked == DialogAction.POSITIVE)
}
open fun onDialogClosed(positiveResult: Boolean) {
}
companion object {
const val ARG_KEY = "key"
fun newInstance(key: String): ATEPreferenceDialogFragment {
val fragment = ATEPreferenceDialogFragment()
val b = Bundle(1)
b.putString(ARG_KEY, key)
fragment.arguments = b
return fragment
}
}
}

View file

@ -0,0 +1,31 @@
package code.name.monkey.appthemehelper.common.views
import android.content.Context
import androidx.appcompat.widget.AppCompatCheckBox
import android.util.AttributeSet
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.ThemeStore
/**
* @author Aidan Follestad (afollestad)
*/
class ATECheckBox : AppCompatCheckBox {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
ATH.setTint(this, ThemeStore.accentColor(context))
}
}

View file

@ -0,0 +1,32 @@
package code.name.monkey.appthemehelper.common.views
import android.content.Context
import androidx.appcompat.widget.AppCompatEditText
import android.util.AttributeSet
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.ThemeStore
/**
* @author Aidan Follestad (afollestad)
*/
class ATEEditText : AppCompatEditText {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
ATH.setTint(this, ThemeStore.accentColor(context))
setTextColor(ThemeStore.textColorPrimary(context))
}
}

View file

@ -0,0 +1,32 @@
package code.name.monkey.appthemehelper.common.views
import android.content.Context
import androidx.appcompat.widget.AppCompatTextView
import android.text.Layout
import android.util.AttributeSet
import code.name.monkey.appthemehelper.ThemeStore
/**
* @author Aidan Follestad (afollestad)
*/
class ATEPrimaryTextView : AppCompatTextView {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
setTextColor(ThemeStore.textColorPrimary(context))
}
}

View file

@ -0,0 +1,38 @@
package code.name.monkey.appthemehelper.common.views
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.widget.ProgressBar
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.ThemeStore
/**
* @author Aidan Follestad (afollestad)
*/
class ATEProgressBar : ProgressBar {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
ATH.setTint(this, ThemeStore.accentColor(context))
}
}

View file

@ -0,0 +1,37 @@
package code.name.monkey.appthemehelper.common.views
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.widget.RadioButton
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.ThemeStore
/**
* @author Aidan Follestad (afollestad)
*/
class ATERadioButton : RadioButton {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
ATH.setTint(this, ThemeStore.accentColor(context))
}
}

View file

@ -0,0 +1,29 @@
package code.name.monkey.appthemehelper.common.views
import android.content.Context
import androidx.appcompat.widget.AppCompatTextView
import android.util.AttributeSet
import code.name.monkey.appthemehelper.ThemeStore
/**
* @author Aidan Follestad (afollestad)
*/
class ATESecondaryTextView : AppCompatTextView {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
setTextColor(ThemeStore.textColorSecondary(context))
}
}

View file

@ -0,0 +1,38 @@
package code.name.monkey.appthemehelper.common.views
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.widget.SeekBar
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.ThemeStore
/**
* @author Aidan Follestad (afollestad)
*/
class ATESeekBar : SeekBar {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
ATH.setTint(this, ThemeStore.accentColor(context))
}
}

View file

@ -0,0 +1,36 @@
package code.name.monkey.appthemehelper.common.views
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.Switch
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.ThemeStore
/**
* @author Aidan Follestad (afollestad)
*/
class ATEStockSwitch : Switch {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
ATH.setTint(this, ThemeStore.accentColor(context))
}
override fun isShown(): Boolean {
return parent != null && visibility == View.VISIBLE
}
}

View file

@ -0,0 +1,36 @@
package code.name.monkey.appthemehelper.common.views
import android.content.Context
import androidx.appcompat.widget.SwitchCompat
import android.util.AttributeSet
import android.view.View
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.ThemeStore
/**
* @author Aidan Follestad (afollestad)
*/
class ATESwitch : SwitchCompat {
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
ATH.setTint(this, ThemeStore.accentColor(context))
}
override fun isShown(): Boolean {
return parent != null && visibility == View.VISIBLE
}
}

View file

@ -0,0 +1,43 @@
package code.name.monkey.appthemehelper.util
import android.content.Context
import android.content.res.TypedArray
import androidx.annotation.AttrRes
/**
* @author Aidan Follestad (afollestad)
*/
object ATHUtil {
fun isWindowBackgroundDark(context: Context): Boolean {
return !ColorUtil.isColorLight(ATHUtil.resolveColor(context, android.R.attr.windowBackground))
}
@JvmOverloads
fun resolveColor(context: Context, @AttrRes attr: Int, fallback: Int = 0): Int {
val a = context.theme.obtainStyledAttributes(intArrayOf(attr))
try {
return a.getColor(0, fallback)
} finally {
a.recycle()
}
}
fun isInClassPath(clsName: String): Boolean {
try {
return inClassPath(clsName) != null
} catch (t: Throwable) {
return false
}
}
fun inClassPath(clsName: String): Class<*> {
try {
return Class.forName(clsName)
} catch (t: Throwable) {
throw IllegalStateException(String.format("%s is not in your class path! You must include the associated library.", clsName))
}
}
}

View file

@ -0,0 +1,74 @@
package code.name.monkey.appthemehelper.util
import android.graphics.Color
import androidx.annotation.ColorInt
import androidx.annotation.FloatRange
object ColorUtil {
fun stripAlpha(@ColorInt color: Int): Int {
return -0x1000000 or color
}
@ColorInt
fun shiftColor(@ColorInt color: Int, @FloatRange(from = 0.0, to = 2.0) by: Float): Int {
if (by == 1f) return color
val alpha = Color.alpha(color)
val hsv = FloatArray(3)
Color.colorToHSV(color, hsv)
hsv[2] *= by // value component
return (alpha shl 24) + (0x00ffffff and Color.HSVToColor(hsv))
}
@ColorInt
fun darkenColor(@ColorInt color: Int): Int {
return shiftColor(color, 0.9f)
}
@ColorInt
fun lightenColor(@ColorInt color: Int): Int {
return shiftColor(color, 1.1f)
}
fun isColorLight(@ColorInt color: Int): Boolean {
val darkness = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255
return darkness < 0.4
}
@ColorInt
fun invertColor(@ColorInt color: Int): Int {
val r = 255 - Color.red(color)
val g = 255 - Color.green(color)
val b = 255 - Color.blue(color)
return Color.argb(Color.alpha(color), r, g, b)
}
@ColorInt
fun adjustAlpha(@ColorInt color: Int, @FloatRange(from = 0.0, to = 1.0) factor: Float): Int {
val alpha = Math.round(Color.alpha(color) * factor)
val red = Color.red(color)
val green = Color.green(color)
val blue = Color.blue(color)
return Color.argb(alpha, red, green, blue)
}
@ColorInt
fun withAlpha(@ColorInt baseColor: Int, @FloatRange(from = 0.0, to = 1.0) alpha: Float): Int {
val a = Math.min(255, Math.max(0, (alpha * 255).toInt())) shl 24
val rgb = 0x00ffffff and baseColor
return a + rgb
}
/**
* Taken from CollapsingToolbarLayout's CollapsingTextHelper class.
*/
fun blendColors(color1: Int, color2: Int, @FloatRange(from = 0.0, to = 1.0) ratio: Float): Int {
val inverseRatio = 1f - ratio
val a = Color.alpha(color1) * inverseRatio + Color.alpha(color2) * ratio
val r = Color.red(color1) * inverseRatio + Color.red(color2) * ratio
val g = Color.green(color1) * inverseRatio + Color.green(color2) * ratio
val b = Color.blue(color1) * inverseRatio + Color.blue(color2) * ratio
return Color.argb(a.toInt(), r.toInt(), g.toInt(), b.toInt())
}
}

View file

@ -0,0 +1,23 @@
package code.name.monkey.appthemehelper.util
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.TransitionDrawable
import androidx.annotation.ColorInt
object DrawableUtil {
fun createTransitionDrawable(@ColorInt startColor: Int, @ColorInt endColor: Int): TransitionDrawable {
return createTransitionDrawable(ColorDrawable(startColor), ColorDrawable(endColor))
}
fun createTransitionDrawable(start: Drawable, end: Drawable): TransitionDrawable {
val drawables = arrayOfNulls<Drawable>(2)
drawables[0] = start
drawables[1] = end
return TransitionDrawable(drawables)
}
}

View file

@ -0,0 +1,342 @@
package code.name.monkey.appthemehelper.util
import android.annotation.TargetApi
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.os.Build
import androidx.annotation.ColorInt
import androidx.viewpager.widget.ViewPager
import androidx.core.widget.EdgeEffectCompat
import androidx.core.widget.NestedScrollView
import androidx.recyclerview.widget.RecyclerView
import android.widget.AbsListView
import android.widget.EdgeEffect
import android.widget.ScrollView
import code.name.monkey.appthemehelper.BuildConfig
import java.lang.reflect.Field
class EdgeGlowUtil protected constructor() {
companion object {
// Invalidation methods
private var EDGE_GLOW_FIELD_EDGE: Field? = null
private var EDGE_GLOW_FIELD_GLOW: Field? = null
private var EDGE_EFFECT_COMPAT_FIELD_EDGE_EFFECT: Field? = null
private fun invalidateEdgeEffectFields() {
if (EDGE_GLOW_FIELD_EDGE != null && EDGE_GLOW_FIELD_GLOW != null &&
EDGE_EFFECT_COMPAT_FIELD_EDGE_EFFECT != null) {
EDGE_GLOW_FIELD_EDGE!!.isAccessible = true
EDGE_GLOW_FIELD_GLOW!!.isAccessible = true
EDGE_EFFECT_COMPAT_FIELD_EDGE_EFFECT!!.isAccessible = true
return
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
var edge: Field? = null
var glow: Field? = null
var cls: Class<*>? = null
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
try {
cls = Class.forName("android.widget.EdgeGlow")
} catch (e: ClassNotFoundException) {
if (BuildConfig.DEBUG) e.printStackTrace()
}
} else {
cls = EdgeEffect::class.java
}
if (cls != null) {
for (f in cls.declaredFields) {
when (f.name) {
"mEdge" -> {
f.isAccessible = true
edge = f
}
"mGlow" -> {
f.isAccessible = true
glow = f
}
}
}
}
EDGE_GLOW_FIELD_EDGE = edge
EDGE_GLOW_FIELD_GLOW = glow
} else {
EDGE_GLOW_FIELD_EDGE = null
EDGE_GLOW_FIELD_GLOW = null
}
var efc: Field? = null
try {
efc = EdgeEffectCompat::class.java.getDeclaredField("mEdgeEffect")
} catch (e: NoSuchFieldException) {
if (BuildConfig.DEBUG) e.printStackTrace()
}
EDGE_EFFECT_COMPAT_FIELD_EDGE_EFFECT = efc
}
private var SCROLL_VIEW_FIELD_EDGE_GLOW_TOP: Field? = null
private var SCROLL_VIEW_FIELD_EDGE_GLOW_BOTTOM: Field? = null
private fun invalidateScrollViewFields() {
if (SCROLL_VIEW_FIELD_EDGE_GLOW_TOP != null && SCROLL_VIEW_FIELD_EDGE_GLOW_BOTTOM != null) {
SCROLL_VIEW_FIELD_EDGE_GLOW_TOP!!.isAccessible = true
SCROLL_VIEW_FIELD_EDGE_GLOW_BOTTOM!!.isAccessible = true
return
}
val cls = ScrollView::class.java
for (f in cls.declaredFields) {
when (f.name) {
"mEdgeGlowTop" -> {
f.isAccessible = true
SCROLL_VIEW_FIELD_EDGE_GLOW_TOP = f
}
"mEdgeGlowBottom" -> {
f.isAccessible = true
SCROLL_VIEW_FIELD_EDGE_GLOW_BOTTOM = f
}
}
}
}
private var NESTED_SCROLL_VIEW_FIELD_EDGE_GLOW_TOP: Field? = null
private var NESTED_SCROLL_VIEW_FIELD_EDGE_GLOW_BOTTOM: Field? = null
private fun invalidateNestedScrollViewFields() {
if (NESTED_SCROLL_VIEW_FIELD_EDGE_GLOW_TOP != null && NESTED_SCROLL_VIEW_FIELD_EDGE_GLOW_BOTTOM != null) {
NESTED_SCROLL_VIEW_FIELD_EDGE_GLOW_TOP!!.isAccessible = true
NESTED_SCROLL_VIEW_FIELD_EDGE_GLOW_BOTTOM!!.isAccessible = true
return
}
val cls = ATHUtil.inClassPath("android.support.v4.widget.NestedScrollView")
for (f in cls.declaredFields) {
when (f.name) {
"mEdgeGlowTop" -> {
f.isAccessible = true
NESTED_SCROLL_VIEW_FIELD_EDGE_GLOW_TOP = f
}
"mEdgeGlowBottom" -> {
f.isAccessible = true
NESTED_SCROLL_VIEW_FIELD_EDGE_GLOW_BOTTOM = f
}
}
}
}
private var LIST_VIEW_FIELD_EDGE_GLOW_TOP: Field? = null
private var LIST_VIEW_FIELD_EDGE_GLOW_BOTTOM: Field? = null
private fun invalidateListViewFields() {
if (LIST_VIEW_FIELD_EDGE_GLOW_TOP != null && LIST_VIEW_FIELD_EDGE_GLOW_BOTTOM != null) {
LIST_VIEW_FIELD_EDGE_GLOW_TOP!!.isAccessible = true
LIST_VIEW_FIELD_EDGE_GLOW_BOTTOM!!.isAccessible = true
return
}
val cls = AbsListView::class.java
for (f in cls.declaredFields) {
when (f.name) {
"mEdgeGlowTop" -> {
f.isAccessible = true
LIST_VIEW_FIELD_EDGE_GLOW_TOP = f
}
"mEdgeGlowBottom" -> {
f.isAccessible = true
LIST_VIEW_FIELD_EDGE_GLOW_BOTTOM = f
}
}
}
}
private var RECYCLER_VIEW_FIELD_EDGE_GLOW_TOP: Field? = null
private var RECYCLER_VIEW_FIELD_EDGE_GLOW_LEFT: Field? = null
private var RECYCLER_VIEW_FIELD_EDGE_GLOW_RIGHT: Field? = null
private var RECYCLER_VIEW_FIELD_EDGE_GLOW_BOTTOM: Field? = null
private fun invalidateRecyclerViewFields() {
if (RECYCLER_VIEW_FIELD_EDGE_GLOW_TOP != null && RECYCLER_VIEW_FIELD_EDGE_GLOW_LEFT != null &&
RECYCLER_VIEW_FIELD_EDGE_GLOW_RIGHT != null && RECYCLER_VIEW_FIELD_EDGE_GLOW_BOTTOM != null) {
RECYCLER_VIEW_FIELD_EDGE_GLOW_TOP!!.isAccessible = true
RECYCLER_VIEW_FIELD_EDGE_GLOW_LEFT!!.isAccessible = true
RECYCLER_VIEW_FIELD_EDGE_GLOW_RIGHT!!.isAccessible = true
RECYCLER_VIEW_FIELD_EDGE_GLOW_BOTTOM!!.isAccessible = true
return
}
val cls = ATHUtil.inClassPath("android.support.v7.widget.RecyclerView")
for (f in cls.declaredFields) {
when (f.name) {
"mTopGlow" -> {
f.isAccessible = true
RECYCLER_VIEW_FIELD_EDGE_GLOW_TOP = f
}
"mBottomGlow" -> {
f.isAccessible = true
RECYCLER_VIEW_FIELD_EDGE_GLOW_BOTTOM = f
}
"mLeftGlow" -> {
f.isAccessible = true
RECYCLER_VIEW_FIELD_EDGE_GLOW_LEFT = f
}
"mRightGlow" -> {
f.isAccessible = true
RECYCLER_VIEW_FIELD_EDGE_GLOW_RIGHT = f
}
}
}
}
private var VIEW_PAGER_FIELD_EDGE_GLOW_LEFT: Field? = null
private var VIEW_PAGER_FIELD_EDGE_GLOW_RIGHT: Field? = null
private fun invalidateViewPagerFields() {
if (VIEW_PAGER_FIELD_EDGE_GLOW_LEFT != null && VIEW_PAGER_FIELD_EDGE_GLOW_RIGHT != null) {
VIEW_PAGER_FIELD_EDGE_GLOW_LEFT!!.isAccessible = true
VIEW_PAGER_FIELD_EDGE_GLOW_RIGHT!!.isAccessible = true
return
}
val cls = ATHUtil.inClassPath("android.support.v4.view.ViewPager")
for (f in cls.declaredFields) {
when (f.name) {
"mLeftEdge" -> {
f.isAccessible = true
VIEW_PAGER_FIELD_EDGE_GLOW_LEFT = f
}
"mRightEdge" -> {
f.isAccessible = true
VIEW_PAGER_FIELD_EDGE_GLOW_RIGHT = f
}
}
}
}
// Setter methods
fun setEdgeGlowColor(scrollView: ScrollView, @ColorInt color: Int) {
invalidateScrollViewFields()
try {
var ee: Any
ee = SCROLL_VIEW_FIELD_EDGE_GLOW_TOP!!.get(scrollView)
setEffectColor(ee, color)
ee = SCROLL_VIEW_FIELD_EDGE_GLOW_BOTTOM!!.get(scrollView)
setEffectColor(ee, color)
} catch (ex: Exception) {
if (BuildConfig.DEBUG) ex.printStackTrace()
}
}
fun setEdgeGlowColor(scrollView: NestedScrollView, @ColorInt color: Int) {
invalidateNestedScrollViewFields()
try {
var ee: Any
ee = NESTED_SCROLL_VIEW_FIELD_EDGE_GLOW_TOP!!.get(scrollView)
setEffectColor(ee, color)
ee = NESTED_SCROLL_VIEW_FIELD_EDGE_GLOW_BOTTOM!!.get(scrollView)
setEffectColor(ee, color)
} catch (ex: Exception) {
if (BuildConfig.DEBUG) ex.printStackTrace()
}
}
fun setEdgeGlowColor(listView: AbsListView, @ColorInt color: Int) {
invalidateListViewFields()
try {
var ee: Any
ee = LIST_VIEW_FIELD_EDGE_GLOW_TOP!!.get(listView)
setEffectColor(ee, color)
ee = LIST_VIEW_FIELD_EDGE_GLOW_BOTTOM!!.get(listView)
setEffectColor(ee, color)
} catch (ex: Exception) {
if (BuildConfig.DEBUG) ex.printStackTrace()
}
}
fun setEdgeGlowColor(scrollView: RecyclerView, @ColorInt color: Int, scrollListener: RecyclerView.OnScrollListener?) {
var scrollListener = scrollListener
invalidateRecyclerViewFields()
invalidateRecyclerViewFields()
if (scrollListener == null) {
scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
EdgeGlowUtil.setEdgeGlowColor(recyclerView, color, this)
}
}
scrollView.addOnScrollListener(scrollListener)
}
try {
var ee: Any
ee = RECYCLER_VIEW_FIELD_EDGE_GLOW_TOP!!.get(scrollView)
setEffectColor(ee, color)
ee = RECYCLER_VIEW_FIELD_EDGE_GLOW_BOTTOM!!.get(scrollView)
setEffectColor(ee, color)
ee = RECYCLER_VIEW_FIELD_EDGE_GLOW_LEFT!!.get(scrollView)
setEffectColor(ee, color)
ee = RECYCLER_VIEW_FIELD_EDGE_GLOW_RIGHT!!.get(scrollView)
setEffectColor(ee, color)
} catch (ex: Exception) {
if (BuildConfig.DEBUG) ex.printStackTrace()
}
}
fun setEdgeGlowColor(pager: ViewPager, @ColorInt color: Int) {
invalidateViewPagerFields()
try {
var ee: Any
ee = VIEW_PAGER_FIELD_EDGE_GLOW_LEFT!!.get(pager)
setEffectColor(ee, color)
ee = VIEW_PAGER_FIELD_EDGE_GLOW_RIGHT!!.get(pager)
setEffectColor(ee, color)
} catch (ex: Exception) {
if (BuildConfig.DEBUG) ex.printStackTrace()
}
}
// Utilities
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun setEffectColor(edgeEffect: Any?, @ColorInt color: Int) {
var edgeEffect = edgeEffect
invalidateEdgeEffectFields()
if (edgeEffect is EdgeEffectCompat) {
// EdgeEffectCompat
try {
EDGE_EFFECT_COMPAT_FIELD_EDGE_EFFECT!!.isAccessible = true
edgeEffect = EDGE_EFFECT_COMPAT_FIELD_EDGE_EFFECT!!.get(edgeEffect)
} catch (e: IllegalAccessException) {
e.printStackTrace()
return
}
}
if (edgeEffect == null)
return
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// EdgeGlow
try {
EDGE_GLOW_FIELD_EDGE!!.isAccessible = true
val mEdge = EDGE_GLOW_FIELD_EDGE!!.get(edgeEffect) as Drawable
EDGE_GLOW_FIELD_GLOW!!.isAccessible = true
val mGlow = EDGE_GLOW_FIELD_GLOW!!.get(edgeEffect) as Drawable
mEdge.setColorFilter(color, PorterDuff.Mode.SRC_IN)
mGlow.setColorFilter(color, PorterDuff.Mode.SRC_IN)
mEdge.callback = null // free up any references
mGlow.callback = null // free up any references
} catch (ex: Exception) {
ex.printStackTrace()
}
} else {
// EdgeEffect
(edgeEffect as EdgeEffect).color = color
}
}
}
}

View file

@ -0,0 +1,24 @@
package code.name.monkey.appthemehelper.util
import android.content.Context
import android.content.res.ColorStateList
import com.afollestad.materialdialogs.internal.ThemeSingleton
import code.name.monkey.appthemehelper.ThemeStore
object MaterialDialogsUtil {
fun updateMaterialDialogsThemeSingleton(context: Context) {
val md = ThemeSingleton.get()
md.titleColor = ThemeStore.textColorPrimary(context)
md.contentColor = ThemeStore.textColorSecondary(context)
md.itemColor = md.titleColor
md.widgetColor = ThemeStore.accentColor(context)
md.linkColor = ColorStateList.valueOf(md.widgetColor)
md.positiveColor = ColorStateList.valueOf(md.widgetColor)
md.neutralColor = ColorStateList.valueOf(md.widgetColor)
md.negativeColor = ColorStateList.valueOf(md.widgetColor)
md.darkTheme = ATHUtil.isWindowBackgroundDark(context)
}
}

View file

@ -0,0 +1,57 @@
package code.name.monkey.appthemehelper.util
import android.content.Context
import android.content.res.ColorStateList
import com.google.android.material.button.MaterialButton
import com.google.android.material.textfield.TextInputLayout
import code.name.monkey.appthemehelper.ThemeStore
object MaterialUtil {
fun setTint(button: MaterialButton) {
setTint(button, ThemeStore.accentColor(button.context))
}
private fun setTint(button: MaterialButton, accentColor: Int) {
val context = button.context
val textColor = ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(accentColor)))
button.setTextColor(textColor)
}
@JvmOverloads
fun setTint(button: MaterialButton, background: Boolean, color: Int = ThemeStore.accentColor(button.context)) {
//button.setPadding(48, 48, 48, 48);
button.isAllCaps = false
val context = button.context
val colorState = ColorStateList.valueOf(color)
val textColor = ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color)))
if (background) {
button.backgroundTintList = colorState
button.setTextColor(textColor)
button.iconTint = textColor
} else {
button.strokeColor = colorState
button.setTextColor(colorState)
button.iconTint = colorState
}
}
fun setTint(textInputLayout: TextInputLayout, background: Boolean) {
val context = textInputLayout.context
val accentColor = ThemeStore.accentColor(context)
val colorState = ColorStateList.valueOf(accentColor)
if (background) {
textInputLayout.backgroundTintList = colorState
textInputLayout.defaultHintTextColor = colorState
} else {
textInputLayout.boxStrokeColor = accentColor
textInputLayout.defaultHintTextColor = colorState
}
}
}

View file

@ -0,0 +1,44 @@
package code.name.monkey.appthemehelper.util
import android.annotation.SuppressLint
import android.content.Context
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import code.name.monkey.appthemehelper.R
object MaterialValueHelper {
@SuppressLint("PrivateResource")
@ColorInt
fun getPrimaryTextColor(context: Context, dark: Boolean): Int {
return if (dark) {
ContextCompat.getColor(context, R.color.primary_text_default_material_light)
} else ContextCompat.getColor(context, R.color.primary_text_default_material_dark)
}
@SuppressLint("PrivateResource")
@ColorInt
fun getSecondaryTextColor(context: Context, dark: Boolean): Int {
return if (dark) {
ContextCompat.getColor(context, R.color.secondary_text_default_material_light)
} else ContextCompat.getColor(context, R.color.secondary_text_default_material_dark)
}
@SuppressLint("PrivateResource")
@ColorInt
fun getPrimaryDisabledTextColor(context: Context, dark: Boolean): Int {
return if (dark) {
ContextCompat.getColor(context, R.color.primary_text_disabled_material_light)
} else ContextCompat.getColor(context, R.color.primary_text_disabled_material_dark)
}
@SuppressLint("PrivateResource")
@ColorInt
fun getSecondaryDisabledTextColor(context: Context, dark: Boolean): Int {
return if (dark) {
ContextCompat.getColor(context, R.color.secondary_text_disabled_material_light)
} else ContextCompat.getColor(context, R.color.secondary_text_disabled_material_dark)
}
}

View file

@ -0,0 +1,41 @@
package code.name.monkey.appthemehelper.util
import android.content.res.ColorStateList
import androidx.annotation.ColorInt
import code.name.monkey.appthemehelper.ThemeStore
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.navigation.NavigationView
/**
* @author Karim Abou Zeid (kabouzeid)
*/
object NavigationViewUtil {
fun setItemIconColors(navigationView: NavigationView, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) {
val iconSl = ColorStateList(arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)), intArrayOf(normalColor, selectedColor))
navigationView.itemIconTintList = iconSl
val drawable = navigationView.itemBackground
navigationView.itemBackground = TintHelper.createTintedDrawable(drawable, ColorUtil.withAlpha(ThemeStore.accentColor(navigationView.context), 0.2f))
}
fun setItemTextColors(navigationView: NavigationView, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) {
val textSl = ColorStateList(
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)),
intArrayOf(normalColor, selectedColor))
navigationView.itemTextColor = textSl
}
fun setItemIconColors(bottomNavigationView: BottomNavigationView, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) {
val iconSl = ColorStateList(
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)),
intArrayOf(normalColor, selectedColor))
bottomNavigationView.itemIconTintList = iconSl
}
fun setItemTextColors(bottomNavigationView: BottomNavigationView, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) {
val textSl = ColorStateList(
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)),
intArrayOf(normalColor, selectedColor))
bottomNavigationView.itemTextColor = textSl
}
}

View file

@ -0,0 +1,23 @@
package code.name.monkey.appthemehelper.util
import android.content.res.ColorStateList
import androidx.annotation.ColorInt
import com.google.android.material.tabs.TabLayout
object TabLayoutUtil {
fun setTabIconColors(tabLayout: TabLayout?, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) {
if (tabLayout == null)
return
val sl = ColorStateList(arrayOf(intArrayOf(-android.R.attr.state_selected), intArrayOf(android.R.attr.state_selected)),
intArrayOf(normalColor, selectedColor))
for (i in 0 until tabLayout.tabCount) {
val tab = tabLayout.getTabAt(i)
if (tab != null && tab.icon != null) {
tab.icon = TintHelper.createTintedDrawable(tab.icon, sl)
}
}
}
}

View file

@ -0,0 +1,35 @@
package code.name.monkey.appthemehelper.util
import android.content.res.ColorStateList
import androidx.annotation.ColorInt
import com.google.android.material.textfield.TextInputLayout
import java.lang.reflect.Field
/**
* @author Aidan Follestad (afollestad)
*/
object TextInputLayoutUtil {
fun setHint(view: TextInputLayout, @ColorInt hintColor: Int) {
try {
val mDefaultTextColorField = TextInputLayout::class.java.getDeclaredField("mDefaultTextColor")
mDefaultTextColorField.isAccessible = true
mDefaultTextColorField.set(view, ColorStateList.valueOf(hintColor))
} catch (t: Throwable) {
throw RuntimeException("Failed to set TextInputLayout hint (collapsed) color: " + t.localizedMessage, t)
}
}
fun setAccent(view: TextInputLayout, @ColorInt accentColor: Int) {
try {
val mFocusedTextColorField = TextInputLayout::class.java.getDeclaredField("mFocusedTextColor")
mFocusedTextColorField.isAccessible = true
mFocusedTextColorField.set(view, ColorStateList.valueOf(accentColor))
} catch (t: Throwable) {
throw RuntimeException("Failed to set TextInputLayout accent (expanded) color: " + t.localizedMessage, t)
}
}
}

View file

@ -0,0 +1,50 @@
package code.name.monkey.appthemehelper.util
/**
* @author Aidan Follestad (afollestad)
*/
import android.content.Context
import android.graphics.Typeface
import androidx.collection.SimpleArrayMap
/*
Each call to Typeface.createFromAsset will load a new instance of the typeface into memory,
and this memory is not consistently get garbage collected
http://code.google.com/p/android/issues/detail?id=9904
(It states released but even on Lollipop you can see the typefaces accumulate even after
multiple GC passes)
You can detect this by running:
adb shell dumpsys meminfo com.your.packagenage
You will see output like:
Asset Allocations
zip:/data/app/com.your.packagenage-1.apk:/assets/Roboto-Medium.ttf: 125K
zip:/data/app/com.your.packagenage-1.apk:/assets/Roboto-Medium.ttf: 125K
zip:/data/app/com.your.packagenage-1.apk:/assets/Roboto-Medium.ttf: 125K
zip:/data/app/com.your.packagenage-1.apk:/assets/Roboto-Regular.ttf: 123K
zip:/data/app/com.your.packagenage-1.apk:/assets/Roboto-Medium.ttf: 125K
*/
object TypefaceHelper {
private val cache = SimpleArrayMap<String, Typeface>()
operator fun get(c: Context, name: String): Typeface? {
synchronized(cache) {
if (!cache.containsKey(name)) {
try {
val t = Typeface.createFromAsset(c.assets, name)
cache.put(name, t)
return t
} catch (e: RuntimeException) {
return null
}
}
return cache.get(name)
}
}
}

View file

@ -0,0 +1,59 @@
package code.name.monkey.appthemehelper.util
import android.os.Build
/**
* @author Hemanth S (h4h13).
*/
object VersionUtils {
/**
* @return true if device is running API >= 19
*/
fun hasKitKat(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
}
/**
* @return true if device is running API >= 20
*/
fun hasAndroidLPreview(): Boolean {
return Build.VERSION.SDK_INT >= 20
}
/**
* @return true if device is running API >= 21
*/
fun hasLollipop(): Boolean {
return Build.VERSION.SDK_INT >= 21
}
/**
* @return true if device is running API >= 23
*/
fun hasMarshmallow(): Boolean {
return Build.VERSION.SDK_INT >= 23
}
/**
* @return true if device is running API >= 24
*/
fun hasNougat(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
}
/**
* @return true if device is running API >= 24
*/
fun hasNougatMR(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1
}
/**
* @return true if device is running API >= 26
*/
fun hasOreo(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
}
}

View file

@ -0,0 +1,47 @@
package code.name.monkey.appthemehelper.util
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.TransitionDrawable
import android.os.Build
import androidx.annotation.ColorInt
import android.view.View
import android.view.ViewTreeObserver
object ViewUtil {
fun removeOnGlobalLayoutListener(v: View, listener: ViewTreeObserver.OnGlobalLayoutListener) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
v.viewTreeObserver.removeGlobalOnLayoutListener(listener)
} else {
v.viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
}
fun setBackgroundCompat(view: View, drawable: Drawable?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
view.background = drawable
else
view.setBackgroundDrawable(drawable)
}
fun setBackgroundTransition(view: View, newDrawable: Drawable): TransitionDrawable {
val transition = DrawableUtil.createTransitionDrawable(view.background, newDrawable)
setBackgroundCompat(view, transition)
return transition
}
fun setBackgroundColorTransition(view: View, @ColorInt newColor: Int): TransitionDrawable {
val oldColor = view.background
val start = oldColor ?: ColorDrawable(view.solidColor)
val end = ColorDrawable(newColor)
val transition = DrawableUtil.createTransitionDrawable(start, end)
setBackgroundCompat(view, transition)
return transition
}
}