commit 7acfe42fc6dbe5724046dc0904fa5a9c29d14f28
parent 931dcd0b00963ab2c9a91d77465b4aa1c6ba34de
Author: Jacob Thomas <41988041+bocajthomas@users.noreply.github.com>
Date: Sat, 27 Apr 2024 12:19:38 +0100
feat(core): colors (#892)
Co-authored-by: Theonethom <141059521+Theonethom@users.noreply.github.com>
Co-authored-by: auth <64337177+authorisation@users.noreply.github.com>
Diffstat:
7 files changed, 160 insertions(+), 60 deletions(-)
diff --git a/README.md b/README.md
@@ -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
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/AlertDialogs.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/AlertDialogs.kt
@@ -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
}
diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json
@@ -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",
diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/UserInterfaceTweaks.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/UserInterfaceTweaks.kt
@@ -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() }
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt
@@ -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")
}
}
-}-
\ No newline at end of file
+}
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/AmoledDarkMode.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/AmoledDarkMode.kt
@@ -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()))
- }
- }
- }
- }
-}-
\ No newline at end of file
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/ui/CustomizeUI.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/ui/CustomizeUI.kt
@@ -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)
+ }
+ }
+ }
+ }
+ }
+}
+
+