feat(core): colors (#892)
Co-authored-by: Theonethom <141059521+Theonethom@users.noreply.github.com> Co-authored-by: auth <64337177+authorisation@users.noreply.github.com>
This commit is contained in:
@@ -249,6 +249,7 @@ Thanks to everyone involved including the [third-party libraries](https://github
|
||||
- [TheVisual](https://github.com/TheVisual)
|
||||
- [CanerKaraca23](https://github.com/CanerKaraca23)
|
||||
- [bocajthomas](https://github.com/bocajthomas)
|
||||
|
||||
## Donate
|
||||
- LTC: LbBnT9GxgnFhwy891EdDKqGmpn7XtduBdE
|
||||
- BCH: qpu57a05kqljjadvpgjc6t894apprvth9slvlj4vpj
|
||||
|
||||
@@ -2,6 +2,7 @@ package me.rhunk.snapenhance.ui.util
|
||||
|
||||
import android.content.Context
|
||||
import android.view.MotionEvent
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
@@ -175,6 +176,7 @@ class AlertDialogs(
|
||||
@Composable
|
||||
fun KeyboardInputDialog(property: PropertyPair<*>, dismiss: () -> Unit = {}) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val context = LocalContext.current
|
||||
|
||||
DefaultDialogCard {
|
||||
var fieldValue by remember {
|
||||
@@ -215,7 +217,7 @@ class AlertDialogs(
|
||||
}
|
||||
Button(onClick = {
|
||||
if (fieldValue.text.isNotEmpty() && property.key.params.inputCheck?.invoke(fieldValue.text) == false) {
|
||||
dismiss()
|
||||
Toast.makeText(context, "Invalid input! Make sure you entered a valid value.", Toast.LENGTH_SHORT).show() //TODO: i18n
|
||||
return@Button
|
||||
}
|
||||
|
||||
|
||||
@@ -358,9 +358,35 @@
|
||||
"name": "Enable App Appearance Settings",
|
||||
"description": "Enables the hidden App Appearance Setting\nMay not be required on newer Snapchat versions"
|
||||
},
|
||||
"amoled_dark_mode": {
|
||||
"name": "AMOLED Dark Mode",
|
||||
"description": "Enables AMOLED dark mode\nMake sure Snapchats Dark mode is enabled"
|
||||
"customize_ui": {
|
||||
"name": "Colors",
|
||||
"description": "Customize Snapchats Colors",
|
||||
"properties": {
|
||||
"text_colour": {
|
||||
"name": "Text Color",
|
||||
"description": "Changes Snapchats text color\nInput hex color code"
|
||||
},
|
||||
"chat_colour": {
|
||||
"name": "Sent & Received Color",
|
||||
"description": "Changes Snapchats Sent and Received text color on the friend feed\nInput a hex color code"
|
||||
},
|
||||
"background_colour": {
|
||||
"name": "Background Color",
|
||||
"description": "Changes Snapchats background color\nInput a hex color code"
|
||||
},
|
||||
"background_colour_surface": {
|
||||
"name": "Background Surface Color",
|
||||
"description": "Changes Snapchats background surface color\nInput a hex color code"
|
||||
},
|
||||
"action_menu_background_colour": {
|
||||
"name": "Action Menu Background Color",
|
||||
"description": "Changes Snapchats chat action menu background color\nInput a hex color code"
|
||||
},
|
||||
"action_menu_round_background_colour": {
|
||||
"name": "Action Menu Round Background Color",
|
||||
"description": "Changes Snapchats chat action menu round background color\nInput a hex color code"
|
||||
}
|
||||
}
|
||||
},
|
||||
"friend_feed_message_preview": {
|
||||
"name": "Friend Feed Message Preview",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.rhunk.snapenhance.common.config.impl
|
||||
|
||||
import android.graphics.Color
|
||||
import me.rhunk.snapenhance.common.config.ConfigContainer
|
||||
import me.rhunk.snapenhance.common.config.FeatureNotice
|
||||
import me.rhunk.snapenhance.common.data.MessagingRuleType
|
||||
@@ -18,12 +19,24 @@ class UserInterfaceTweaks : ConfigContainer() {
|
||||
val amount = integer("amount", defaultValue = 1)
|
||||
}
|
||||
|
||||
inner class CustomizeUIConfig : ConfigContainer(hasGlobalState = true) {
|
||||
private val checkInputColor = { value: String ->
|
||||
value.isEmpty() || runCatching { Color.parseColor(value) }.isSuccess
|
||||
}
|
||||
val textColour = string("text_colour") { inputCheck = checkInputColor }
|
||||
val backgroundColour = string("background_colour") { inputCheck = checkInputColor }
|
||||
val backgroundColourSurface = string("background_colour_surface") { inputCheck = checkInputColor }
|
||||
val actionMenuBackgroundColour = string("action_menu_background_colour") { inputCheck = checkInputColor }
|
||||
val actionMenuRoundBackgroundColour = string("action_menu_round_background_colour") { inputCheck = checkInputColor }
|
||||
val chatColour = string("chat_colour") { inputCheck = checkInputColor }
|
||||
}
|
||||
|
||||
val friendFeedMenuButtons = multiple(
|
||||
"friend_feed_menu_buttons","conversation_info", "mark_snaps_as_seen", "mark_stories_as_seen_locally", *MessagingRuleType.entries.filter { it.showInFriendMenu }.map { it.key }.toTypedArray()
|
||||
).apply {
|
||||
set(mutableListOf("conversation_info", MessagingRuleType.STEALTH.key))
|
||||
}
|
||||
val amoledDarkMode = boolean("amoled_dark_mode") { addNotices(FeatureNotice.UNSTABLE); requireRestart() }
|
||||
val customizeUi = container("customize_ui", CustomizeUIConfig()) { addNotices(FeatureNotice.UNSTABLE); requireRestart() }
|
||||
val friendFeedMessagePreview = container("friend_feed_message_preview", FriendFeedMessagePreview()) { requireRestart() }
|
||||
val snapPreview = boolean("snap_preview") { addNotices(FeatureNotice.UNSTABLE); requireRestart() }
|
||||
val bootstrapOverride = container("bootstrap_override", BootstrapOverride()) { requireRestart() }
|
||||
|
||||
@@ -84,7 +84,6 @@ class FeatureManager(
|
||||
AppLock(),
|
||||
CameraTweaks(),
|
||||
InfiniteStoryBoost(),
|
||||
AmoledDarkMode(),
|
||||
PinConversations(),
|
||||
DeviceSpooferHook(),
|
||||
ClientBootstrapOverride(),
|
||||
@@ -120,6 +119,7 @@ class FeatureManager(
|
||||
AccountSwitcher(),
|
||||
RemoveGroupsLockedStatus(),
|
||||
BypassMessageActionRestrictions(),
|
||||
CustomizeUI(),
|
||||
BetterLocation(),
|
||||
MediaFilePicker(),
|
||||
HideActiveMusic(),
|
||||
@@ -128,7 +128,6 @@ class FeatureManager(
|
||||
ComposerHooks(),
|
||||
DisableCustomTabs(),
|
||||
)
|
||||
|
||||
initializeFeatures()
|
||||
}
|
||||
|
||||
@@ -190,4 +189,4 @@ class FeatureManager(
|
||||
context.log.verbose("feature manager onActivityCreate took $it ms")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package me.rhunk.snapenhance.core.features.impl.experiments
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.TypedArray
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import me.rhunk.snapenhance.core.features.Feature
|
||||
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||
import me.rhunk.snapenhance.core.util.hook.Hooker
|
||||
import me.rhunk.snapenhance.core.util.hook.hook
|
||||
import me.rhunk.snapenhance.core.util.ktx.getIdentifier
|
||||
|
||||
class AmoledDarkMode : Feature("Amoled Dark Mode", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) {
|
||||
@SuppressLint("DiscouragedApi")
|
||||
override fun onActivityCreate() {
|
||||
if (!context.config.userInterface.amoledDarkMode.get()) return
|
||||
val attributeCache = mutableMapOf<String, Int>()
|
||||
|
||||
fun getAttribute(name: String): Int {
|
||||
if (attributeCache.containsKey(name)) return attributeCache[name]!!
|
||||
return context.resources.getIdentifier(name, "attr").also { attributeCache[name] = it }
|
||||
}
|
||||
|
||||
context.androidContext.theme.javaClass.getMethod("obtainStyledAttributes", IntArray::class.java).hook(
|
||||
HookStage.AFTER) { param ->
|
||||
val array = param.arg<IntArray>(0)
|
||||
val result = param.getResult() as TypedArray
|
||||
|
||||
fun ephemeralHook(methodName: String, content: Any) {
|
||||
Hooker.ephemeralHookObjectMethod(result::class.java, result, methodName, HookStage.BEFORE) {
|
||||
it.setResult(content)
|
||||
}
|
||||
}
|
||||
|
||||
when (array[0]) {
|
||||
getAttribute("sigColorTextPrimary") -> {
|
||||
ephemeralHook("getColor", 0xFFFFFFFF.toInt())
|
||||
}
|
||||
getAttribute("sigColorBackgroundMain"),
|
||||
getAttribute("sigColorBackgroundSurface") -> {
|
||||
ephemeralHook("getColor", 0xFF000000.toInt())
|
||||
}
|
||||
getAttribute("actionSheetBackgroundDrawable"),
|
||||
getAttribute("actionSheetRoundedBackgroundDrawable") -> {
|
||||
ephemeralHook("getDrawable", ColorDrawable(0xFF000000.toInt()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package me.rhunk.snapenhance.core.features.impl.ui
|
||||
|
||||
import android.content.res.TypedArray
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import me.rhunk.snapenhance.core.features.Feature
|
||||
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||
import me.rhunk.snapenhance.core.util.hook.Hooker
|
||||
import me.rhunk.snapenhance.core.util.hook.hook
|
||||
import me.rhunk.snapenhance.core.util.ktx.getIdentifier
|
||||
|
||||
class CustomizeUI: Feature("Customize UI", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) {
|
||||
private fun parseColor(color: String): Int? {
|
||||
return color.takeIf { it.isNotEmpty() }?.let {
|
||||
runCatching { Color.parseColor(color) }.getOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityCreate() {
|
||||
val isAmoledMode = context.config.userInterface.amoledDarkMode.get()
|
||||
val isCustomizeUI = context.config.userInterface.customizeUi.globalState == true
|
||||
|
||||
if (!isAmoledMode && !isCustomizeUI) return
|
||||
|
||||
//TODO: color picker
|
||||
val customizeUIConfig = context.config.userInterface.customizeUi
|
||||
val effectiveTextColour by lazy { parseColor(customizeUIConfig.textColour.get()) }
|
||||
val effectiveBackgroundColour by lazy { parseColor(customizeUIConfig.backgroundColour.get()) }
|
||||
val effectiveBackgroundColourSurface by lazy { parseColor(customizeUIConfig.backgroundColourSurface.get()) }
|
||||
val effectiveActionMenuBackgroundColour by lazy { parseColor(customizeUIConfig.actionMenuBackgroundColour.get()) }
|
||||
val effectiveActionMenuRoundBackgroundColour by lazy { parseColor(customizeUIConfig.actionMenuRoundBackgroundColour.get()) }
|
||||
val effectiveChatColour by lazy { parseColor(customizeUIConfig.chatColour.get()) }
|
||||
|
||||
val attributeCache = mutableMapOf<String, Int>()
|
||||
|
||||
fun getAttribute(name: String): Int {
|
||||
if (attributeCache.containsKey(name)) return attributeCache[name]!!
|
||||
return context.resources.getIdentifier(name, "attr").also { attributeCache[name] = it }
|
||||
}
|
||||
|
||||
context.androidContext.theme.javaClass.getMethod("obtainStyledAttributes", IntArray::class.java).hook(
|
||||
HookStage.AFTER) { param ->
|
||||
val array = param.arg<IntArray>(0)
|
||||
val result = param.getResult() as TypedArray
|
||||
|
||||
fun ephemeralHook(methodName: String, content: Any) {
|
||||
Hooker.ephemeralHookObjectMethod(result::class.java, result, methodName, HookStage.BEFORE) {
|
||||
it.setResult(content)
|
||||
}
|
||||
}
|
||||
|
||||
if (isAmoledMode) {
|
||||
when (array[0]) {
|
||||
getAttribute("sigColorTextPrimary") -> {
|
||||
ephemeralHook("getColor", 0xFFFFFFFF.toInt())
|
||||
}
|
||||
getAttribute("sigColorBackgroundMain"),
|
||||
getAttribute("sigColorBackgroundSurface") -> {
|
||||
ephemeralHook("getColor", 0xFF000000.toInt())
|
||||
}
|
||||
getAttribute("actionSheetBackgroundDrawable"),
|
||||
getAttribute("actionSheetRoundedBackgroundDrawable") -> {
|
||||
ephemeralHook("getDrawable", ColorDrawable(0xFF000000.toInt()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isCustomizeUI) {
|
||||
when (array[0]) {
|
||||
getAttribute("sigColorTextPrimary") -> {
|
||||
ephemeralHook("getColor", effectiveTextColour ?: return@hook)
|
||||
}
|
||||
|
||||
getAttribute("sigColorBackgroundMain") -> {
|
||||
ephemeralHook("getColor", effectiveBackgroundColour ?: return@hook)
|
||||
}
|
||||
|
||||
getAttribute("sigColorBackgroundSurface") -> {
|
||||
ephemeralHook("getColor", effectiveBackgroundColourSurface ?: return@hook)
|
||||
}
|
||||
|
||||
getAttribute("actionSheetBackgroundDrawable") -> {
|
||||
ephemeralHook("getDrawable", ColorDrawable(effectiveActionMenuBackgroundColour ?: return@hook))
|
||||
}
|
||||
|
||||
getAttribute("actionSheetRoundedBackgroundDrawable") -> {
|
||||
ephemeralHook("getDrawable", ColorDrawable(effectiveActionMenuRoundBackgroundColour ?: return@hook))
|
||||
}
|
||||
getAttribute("sigColorChatActivity") -> {
|
||||
ephemeralHook("getColor", effectiveChatColour ?: return@hook)
|
||||
}
|
||||
getAttribute("sigColorChatChat") -> {
|
||||
ephemeralHook("getColor", effectiveChatColour ?: return@hook)
|
||||
}
|
||||
getAttribute("sigColorChatPendingSending") -> {
|
||||
ephemeralHook("getColor", effectiveChatColour ?: return@hook)
|
||||
}
|
||||
getAttribute("sigColorChatSnapWithSound") -> {
|
||||
ephemeralHook("getColor", effectiveChatColour ?: return@hook)
|
||||
}
|
||||
getAttribute("sigColorChatSnapWithoutSound") -> {
|
||||
ephemeralHook("getColor", effectiveChatColour ?: return@hook)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user