refactor: reorganize (#15)

* refactor: organization (1/?)

open for ideas and suggestions
TODO:
    • Merge UI Hide Elements
    • Remove Longitude and Latitude from the menu and integrate into the MapActivity
    • Move New Map UI to Experimental
    • Shorten „Show Message Content in Notification“

And possibly even more

* Merge hide UI Elements

bugfix(?): merge overlay

* add: download options

* add: config listeners

* fix(ci): debug

remove prod as it's not getting built anymore

* fix(messagelogger): clear database bug

* update locale, reorganize config

* add: set precise location dialog

* config: better notifications

---------

Co-authored-by: rhunk <101876869+rhunk@users.noreply.github.com>
This commit is contained in:
auth 2023-06-04 01:53:05 +02:00 committed by GitHub
parent cfb0ba5d65
commit 85335dafa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 483 additions and 335 deletions

View File

@ -34,8 +34,3 @@ jobs:
with:
name: app-armv7-release
path: app/build/outputs/apk/armv7/debug/*.apk
- name: Upload prod
uses: actions/upload-artifact@v3.1.2
with:
name: app-prod-release
path: app/build/outputs/apk/prod/debug/*.apk

3
.gitignore vendored
View File

@ -1,10 +1,9 @@
*.iml
.gradle
/local.properties
local.properties
/.idea/
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

View File

@ -1,22 +1,16 @@
{
"category": {
"general": "General",
"spying": "Spying",
"media_download": "Media Downloader",
"privacy": "Privacy",
"ui": "UI",
"extras": "Extras",
"tweaks": "Tweaks",
"location_spoof": "Location Spoof",
"experimental": "Experimental",
"debugging": "Debugging"
"spying_privacy": "Spying & Privacy",
"media_manager": "Media Manager",
"ui_tweaks": "UI & Tweaks",
"experimental_debugging": "Experimental"
},
"action": {
"clean_cache": "Clean Cache",
"clear_message_logger": "Clear Message Logger",
"refresh_mappings": "Refresh Mappings",
"open_map": "Pick a location on the map"
"open_map": "Choose location on map"
},
"property": {
@ -24,16 +18,14 @@
"prevent_read_receipts": "Prevent Read Receipts",
"hide_bitmoji_presence": "Hide Bitmoji Presence",
"show_message_content_in_notifications": "Show Message Content In Notifications",
"better_notifications": "Better Notifications",
"notification_blacklist": "Notification Blacklist",
"message_logger": "Message Logger",
"unlimited_snap_view_time": "Unlimited Snap View Time",
"auto_download_snaps": "Auto Download Snaps",
"auto_download_stories": "Auto Download Stories",
"auto_download_public_stories": "Auto Download Public Stories",
"auto_download_spotlight": "Auto Download Spotlight",
"auto_download_options": "Auto Download Options",
"download_options": "Download Options",
"download_inchat_snaps": "Download Inchat Snaps",
"anti_auto_download_button": "Anti Auto Download Button",
"chat_download_context_menu": "Enable Chat Download Context Menu",
"auto_download_blacklist": "Auto Download Blacklist",
"disable_metrics": "Disable Metrics",
"prevent_screenshot_notifications": "Prevent Screenshot Notifications",
"prevent_status_notifications": "Prevent Status Notifications (Save to camera roll, missed calls)",
@ -42,7 +34,7 @@
"menu_slot_id": "Friend Menu Slot ID",
"message_preview_length": "Message Preview Length",
"gallery_media_send_override": "Gallery Media Send Override",
"auto_save": "Auto Save",
"auto_save_messages": "Auto Save Messages",
"anti_auto_save": "Anti Auto Save Button",
"snapchat_plus": "Snapchat Plus",
"disable_snap_splitting": "Disable Snap Splitting",
@ -62,11 +54,17 @@
"meo_passcode_bypass": "My Eyes Only Passcode Bypass",
"location_spoof": "Snapmap Location Spoofer",
"latitude_value": "Latitude",
"longitude_value": "Longitude"
"longitude_value": "Longitude",
"hide_ui_elements": "Hide UI Elements"
},
"option": {
"property": {
"better_notifications": {
"chat": "Show chat messages",
"snap": "Show medias",
"reply_button": "Add reply button"
},
"download_options": {
"format_user_folder": "Create folder for each user",
"format_hash": "Add a unique hash to the file path",
@ -74,10 +72,46 @@
"format_date_time": "Add the date and time to the file path",
"merge_overlay": "Merge Snap Image Overlays"
},
"auto_download_options": {
"friend_snaps": "Friend Snaps",
"friend_stories": "Friend Stories",
"public_stories": "Public Stories",
"spotlight": "Spotlight"
},
"auto_save_messages": {
"NOTE": "Audio Note",
"CHAT": "Chat",
"EXTERNAL_MEDIA": "External Media",
"SNAP": "Snap",
"STICKER": "Sticker"
},
"notification_blacklist": {
"chat": "Chat",
"snap": "Snap",
"typing": "Typing"
},
"gallery_media_send_override": {
"OFF": "Off",
"NOTE": "Audio Note",
"SNAP": "Snap",
"LIVE_SNAP": "Snap with audio"
},
"media_quality_level": {
"LEVEL_NONE": "Level None",
"LEVEL_1": "Level 1",
"LEVEL_2": "Level 2",
"LEVEL_3": "Level 3",
"LEVEL_4": "Level 4",
"LEVEL_5": "Level 5",
"LEVEL_6": "Level 6",
"LEVEL_7": "Level 7",
"LEVEL_MAX": "Level Max"
},
"hide_ui_elements": {
"remove_call_buttons": "Remove Call Buttons",
"remove_cognac_button": "Remove Cognac Button",
"remove_stickers_button": "Remove Stickers Button",
"remove_voice_record_button": "Remove Voice Record Button"
}
}
},
@ -94,6 +128,12 @@
"preview": "Preview"
},
"chat_action_menu": {
"preview_button": "Preview",
"download_button": "Download",
"delete_logged_message_button": "Delete Logged Message"
},
"opera_context_menu": {
"download": "Download Media"
},

View File

@ -13,7 +13,7 @@ class MessageLoggerRequest(
override fun write(bundle: Bundle) {
bundle.putString("action", action!!.name)
bundle.putString("conversationId", conversationId)
bundle.putLong("messageId", messageId!!)
bundle.putLong("messageId", messageId ?: 0)
bundle.putByteArray("message", message)
}

View File

@ -3,13 +3,8 @@ package me.rhunk.snapenhance.config
enum class ConfigCategory(
val key: String
) {
GENERAL("category.general"),
SPYING("category.spying"),
MEDIA_DOWNLOADER("category.media_download"),
PRIVACY("category.privacy"),
UI("category.ui"),
EXTRAS("category.extras"),
TWEAKS("category.tweaks"),
LOCATION_SPOOF("category.location_spoof"),
EXPERIMENTAL("category.experimental");
SPYING_PRIVACY("category.spying_privacy"),
MEDIA_MANAGEMENT("category.media_manager"),
UI_TWEAKS("category.ui_tweaks"),
EXPERIMENTAL_DEBUGGING("category.experimental_debugging");
}

View File

@ -12,30 +12,45 @@ enum class ConfigProperty(
val nameKey: String,
val descriptionKey: String,
val category: ConfigCategory,
val valueContainer: ConfigValue<*>
val valueContainer: ConfigValue<*>,
val shouldAppearInSettings: Boolean = true
) {
//SPYING AND PRIVACY
MESSAGE_LOGGER("property.message_logger",
"description.message_logger",
ConfigCategory.SPYING_PRIVACY,
ConfigStateValue(false)
),
PREVENT_READ_RECEIPTS(
"property.prevent_read_receipts",
"description.prevent_read_receipts",
ConfigCategory.SPYING,
ConfigCategory.SPYING_PRIVACY,
ConfigStateValue(false)
),
HIDE_BITMOJI_PRESENCE(
"property.hide_bitmoji_presence",
"description.hide_bitmoji_presence",
ConfigCategory.SPYING,
ConfigCategory.SPYING_PRIVACY,
ConfigStateValue(false)
),
SHOW_MESSAGE_CONTENT_IN_NOTIFICATIONS(
"property.show_message_content_in_notifications",
"description.show_message_content_in_notifications",
ConfigCategory.SPYING,
ConfigStateValue(false)
BETTER_NOTIFICATIONS(
"property.better_notifications",
"description.better_notifications",
ConfigCategory.SPYING_PRIVACY,
ConfigStateListValue(
listOf("snap", "chat", "reply_button"),
mutableMapOf(
"snap" to false,
"chat" to false,
"reply_button" to false
)
)
),
NOTIFICATION_BLACKLIST(
"property.notification_blacklist",
"description.notification_blacklist",
ConfigCategory.SPYING,
ConfigCategory.SPYING_PRIVACY,
ConfigStateListValue(
listOf("snap", "chat", "typing"),
mutableMapOf(
@ -45,43 +60,68 @@ enum class ConfigProperty(
)
)
),
DISABLE_METRICS("property.disable_metrics",
"description.disable_metrics",
ConfigCategory.SPYING_PRIVACY,
ConfigStateValue(false)
),
BLOCK_ADS("property.block_ads",
"description.block_ads",
ConfigCategory.SPYING_PRIVACY,
ConfigStateValue(false)
),
UNLIMITED_SNAP_VIEW_TIME("property.unlimited_snap_view_time",
"description.unlimited_snap_view_time",
ConfigCategory.SPYING_PRIVACY,
ConfigStateValue(false)
),
PREVENT_SCREENSHOT_NOTIFICATIONS(
"property.prevent_screenshot_notifications",
"description.prevent_screenshot_notifications",
ConfigCategory.SPYING_PRIVACY,
ConfigStateValue(false)
),
PREVENT_STATUS_NOTIFICATIONS(
"property.prevent_status_notifications",
"description.prevent_status_notifications",
ConfigCategory.SPYING_PRIVACY,
ConfigStateValue(false)
),
ANONYMOUS_STORY_VIEW(
"property.anonymous_story_view",
"description.anonymous_story_view",
ConfigCategory.SPYING_PRIVACY,
ConfigStateValue(false)
),
HIDE_TYPING_NOTIFICATION(
"property.hide_typing_notification",
"description.hide_typing_notification",
ConfigCategory.SPYING_PRIVACY,
ConfigStateValue(false)
),
MESSAGE_LOGGER("property.message_logger", "description.message_logger", ConfigCategory.SPYING, ConfigStateValue(false)),
UNLIMITED_SNAP_VIEW_TIME("property.unlimited_snap_view_time", "description.unlimited_snap_view_time", ConfigCategory.SPYING, ConfigStateValue(false)),
//MEDIA MANAGEMENT
SAVE_FOLDER(
"property.save_folder", "description.save_folder", ConfigCategory.MEDIA_DOWNLOADER,
"property.save_folder", "description.save_folder", ConfigCategory.MEDIA_MANAGEMENT,
ConfigStringValue(File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).absolutePath + "/Snapchat",
"SnapEnhance"
).absolutePath)
),
AUTO_DOWNLOAD_SNAPS(
"property.auto_download_snaps",
"description.auto_download_snaps",
ConfigCategory.MEDIA_DOWNLOADER,
ConfigStateValue(false)
),
AUTO_DOWNLOAD_STORIES(
"property.auto_download_stories",
"description.auto_download_stories",
ConfigCategory.MEDIA_DOWNLOADER,
ConfigStateValue(false)
),
AUTO_DOWNLOAD_PUBLIC_STORIES(
"property.auto_download_public_stories",
"description.auto_download_public_stories",
ConfigCategory.MEDIA_DOWNLOADER,
ConfigStateValue(false)
),
AUTO_DOWNLOAD_SPOTLIGHT(
"property.auto_download_spotlight",
"description.auto_download_spotlight",
ConfigCategory.MEDIA_DOWNLOADER,
ConfigStateValue(false)
AUTO_DOWNLOAD_OPTIONS(
"property.auto_download_options", "description.auto_download_options", ConfigCategory.MEDIA_MANAGEMENT,
ConfigStateListValue(
listOf("friend_snaps", "friend_stories", "public_stories", "spotlight"),
mutableMapOf(
"friend_snaps" to false,
"friend_stories" to false,
"public_stories" to false,
"spotlight" to false
)
)
),
DOWNLOAD_OPTIONS(
"property.download_options", "description.download_options", ConfigCategory.MEDIA_DOWNLOADER,
"property.download_options", "description.download_options", ConfigCategory.MEDIA_MANAGEMENT,
ConfigStateListValue(
listOf("format_user_folder", "format_hash", "format_date_time", "format_username", "merge_overlay"),
mutableMapOf(
@ -93,167 +133,154 @@ enum class ConfigProperty(
)
)
),
DOWNLOAD_INCHAT_SNAPS(
"property.download_inchat_snaps",
"description.download_inchat_snaps",
ConfigCategory.MEDIA_DOWNLOADER,
CHAT_DOWNLOAD_CONTEXT_MENU(
"property.chat_download_context_menu",
"description.chat_download_context_menu",
ConfigCategory.MEDIA_MANAGEMENT,
ConfigStateValue(false)
),
ANTI_DOWNLOAD_BUTTON(
"property.anti_auto_download_button",
"description.anti_auto_download_button",
ConfigCategory.MEDIA_DOWNLOADER,
DOWNLOAD_BLACKLIST(
"property.auto_download_blacklist",
"description.auto_download_blacklist",
ConfigCategory.MEDIA_MANAGEMENT,
ConfigStateValue(false)
),
DISABLE_METRICS("property.disable_metrics", "description.disable_metrics", ConfigCategory.PRIVACY, ConfigStateValue(false)),
PREVENT_SCREENSHOT_NOTIFICATIONS(
"property.prevent_screenshot_notifications",
"description.prevent_screenshot_notifications",
ConfigCategory.PRIVACY,
ConfigStateValue(false)
),
PREVENT_STATUS_NOTIFICATIONS(
"property.prevent_status_notifications",
"description.prevent_status_notifications",
ConfigCategory.PRIVACY,
ConfigStateValue(false)
),
ANONYMOUS_STORY_VIEW(
"property.anonymous_story_view",
"description.anonymous_story_view",
ConfigCategory.PRIVACY,
ConfigStateValue(false)
),
HIDE_TYPING_NOTIFICATION(
"property.hide_typing_notification",
"description.hide_typing_notification",
ConfigCategory.PRIVACY,
ConfigStateValue(false)
),
MENU_SLOT_ID("property.menu_slot_id", "description.menu_slot_id", ConfigCategory.UI, ConfigIntegerValue(1)),
MESSAGE_PREVIEW_LENGTH(
"property.message_preview_length",
"description.message_preview_length",
ConfigCategory.UI,
ConfigIntegerValue(20)
),
AUTO_SAVE("property.auto_save", "description.auto_save", ConfigCategory.EXTRAS, ConfigStateValue(false)),
ANTI_AUTO_SAVE("property.anti_auto_save", "description.anti_auto_save", ConfigCategory.EXTRAS, ConfigStateValue(false)),
SNAPCHAT_PLUS("property.snapchat_plus", "description.snapchat_plus", ConfigCategory.EXTRAS, ConfigStateValue(false)),
DISABLE_SNAP_SPLITTING(
"property.disable_snap_splitting",
"description.disable_snap_splitting",
ConfigCategory.EXTRAS,
ConfigStateValue(false)
),
DISABLE_VIDEO_LENGTH_RESTRICTION(
"property.disable_video_length_restriction",
"description.disable_video_length_restriction",
ConfigCategory.EXTRAS,
ConfigStateValue(false)
),
OVERRIDE_MEDIA_QUALITY(
"property.override_media_quality",
"description.override_media_quality",
ConfigCategory.EXTRAS,
ConfigStateValue(false)
),
MEDIA_QUALITY_LEVEL(
"property.media_quality_level",
"description.media_quality_level",
ConfigCategory.EXTRAS,
ConfigStateSelection(
listOf("LEVEL_NONE", "LEVEL_1", "LEVEL_2", "LEVEL_3", "LEVEL_4", "LEVEL_5", "LEVEL_6", "LEVEL_7", "LEVEL_MAX"),
"LEVEL_NONE"
)
),
GALLERY_MEDIA_SEND_OVERRIDE(
"property.gallery_media_send_override",
"description.gallery_media_send_override",
ConfigCategory.EXTRAS,
ConfigCategory.MEDIA_MANAGEMENT,
ConfigStateSelection(
listOf("OFF", "NOTE", "SNAP", "LIVE_SNAP"),
"OFF"
)
),
AUTO_SAVE_MESSAGES("property.auto_save_messages",
"description.auto_save_messages",
ConfigCategory.MEDIA_MANAGEMENT,
ConfigStateListValue(
listOf("CHAT", "SNAP", "NOTE", "EXTERNAL_MEDIA", "STICKER")
)
),
ANTI_AUTO_SAVE("property.anti_auto_save",
"description.anti_auto_save",
ConfigCategory.MEDIA_MANAGEMENT,
ConfigStateValue(false)
),
REMOVE_VOICE_RECORD_BUTTON(
"property.remove_voice_record_button",
"description.remove_voice_record_button",
ConfigCategory.TWEAKS,
OVERRIDE_MEDIA_QUALITY(
"property.override_media_quality",
"description.override_media_quality",
ConfigCategory.MEDIA_MANAGEMENT,
ConfigStateValue(false)
),
REMOVE_STICKERS_BUTTON(
"property.remove_stickers_button",
"description.remove_stickers_button",
ConfigCategory.TWEAKS,
ConfigStateValue(false)
MEDIA_QUALITY_LEVEL(
"property.media_quality_level",
"description.media_quality_level",
ConfigCategory.MEDIA_MANAGEMENT,
ConfigStateSelection(
listOf("LEVEL_NONE", "LEVEL_1", "LEVEL_2", "LEVEL_3", "LEVEL_4", "LEVEL_5", "LEVEL_6", "LEVEL_7", "LEVEL_MAX"),
"LEVEL_NONE"
)
),
REMOVE_COGNAC_BUTTON(
"property.remove_cognac_button",
"description.remove_cognac_button",
ConfigCategory.TWEAKS,
ConfigStateValue(false)
//UI AND TWEAKS
HIDE_UI_ELEMENTS(
"property.hide_ui_elements",
"description.hide_ui_elements",
ConfigCategory.UI_TWEAKS,
ConfigStateListValue(
listOf("remove_voice_record_button", "remove_stickers_button", "remove_cognac_button", "remove_call_buttons"),
mutableMapOf(
"remove_voice_record_button" to false,
"remove_stickers_button" to false,
"remove_cognac_button" to false,
"remove_call_buttons" to false,
)
)
),
REMOVE_CALL_BUTTONS(
"property.remove_call_buttons",
"description.remove_call_buttons",
ConfigCategory.TWEAKS,
ConfigStateValue(false)
),
BLOCK_ADS("property.block_ads", "description.block_ads", ConfigCategory.TWEAKS, ConfigStateValue(false)),
STREAK_EXPIRATION_INFO(
"property.streak_expiration_info",
"description.streakexpirationinfo",
ConfigCategory.TWEAKS,
ConfigCategory.UI_TWEAKS,
ConfigStateValue(false)
),
DISABLE_SNAP_SPLITTING(
"property.disable_snap_splitting",
"description.disable_snap_splitting",
ConfigCategory.UI_TWEAKS,
ConfigStateValue(false)
),
DISABLE_VIDEO_LENGTH_RESTRICTION(
"property.disable_video_length_restriction",
"description.disable_video_length_restriction",
ConfigCategory.UI_TWEAKS,
ConfigStateValue(false)
),
SNAPCHAT_PLUS("property.snapchat_plus",
"description.snapchat_plus",
ConfigCategory.UI_TWEAKS,
ConfigStateValue(false)
),
NEW_MAP_UI("property.new_map_ui",
"description.new_map_ui",
ConfigCategory.UI_TWEAKS,
ConfigStateValue(false)
),
NEW_MAP_UI("property.new_map_ui", "description.new_map_ui", ConfigCategory.TWEAKS, ConfigStateValue(false)),
LOCATION_SPOOF(
"property.location_spoof",
"description.location_spoof",
ConfigCategory.LOCATION_SPOOF,
ConfigCategory.UI_TWEAKS,
ConfigStateValue(false)
),
LATITUDE(
"property.latitude_value",
"description.latitude_value",
ConfigCategory.LOCATION_SPOOF,
ConfigStringValue("0.0000")
ConfigCategory.UI_TWEAKS,
ConfigStringValue("0.0000"),
shouldAppearInSettings = false
),
LONGITUDE(
"property.longitude_value",
"description.longitude_value",
ConfigCategory.LOCATION_SPOOF,
ConfigStringValue("0.0000")
ConfigCategory.UI_TWEAKS,
ConfigStringValue("0.0000"),
shouldAppearInSettings = false
),
MENU_SLOT_ID("property.menu_slot_id",
"description.menu_slot_id",
ConfigCategory.UI_TWEAKS,
ConfigIntegerValue(1)
),
MESSAGE_PREVIEW_LENGTH(
"property.message_preview_length",
"description.message_preview_length",
ConfigCategory.UI_TWEAKS,
ConfigIntegerValue(20)
),
// EXPERIMENTAL DEBUGGING
USE_DOWNLOAD_MANAGER(
"property.use_download_manager",
"description.use_download_manager",
ConfigCategory.EXPERIMENTAL,
ConfigCategory.EXPERIMENTAL_DEBUGGING,
ConfigStateValue(false)
),
APP_PASSCODE(
"property.app_passcode",
"description.app_passcode",
ConfigCategory.EXPERIMENTAL,
ConfigStringValue("")
ConfigCategory.EXPERIMENTAL_DEBUGGING,
ConfigStringValue("", isHidden = true)
),
APP_LOCK_ON_RESUME(
"property.app_lock_on_resume",
"description.app_lock_on_resume",
ConfigCategory.EXPERIMENTAL,
ConfigCategory.EXPERIMENTAL_DEBUGGING,
ConfigStateValue(false)
),
MEO_PASSCODE_BYPASS(
"property.meo_passcode_bypass",
"description.meo_passcode_bypass",
ConfigCategory.EXPERIMENTAL,
ConfigCategory.EXPERIMENTAL_DEBUGGING,
ConfigStateValue(false)
);

View File

@ -1,7 +1,22 @@
package me.rhunk.snapenhance.config
abstract class ConfigValue<T> {
private val propertyChangeListeners = mutableListOf<(T) -> Unit>()
fun addPropertyChangeListener(listener: (T) -> Unit) = propertyChangeListeners.add(listener)
fun removePropertyChangeListener(listener: (T) -> Unit) = propertyChangeListeners.remove(listener)
abstract fun value(): T
abstract fun write(): String
abstract fun read(value: String)
abstract fun read(): String
protected abstract fun write(value: String)
protected fun onValueChanged() {
propertyChangeListeners.forEach { it(value()) }
}
fun writeFrom(value: String) {
val oldValue = read()
write(value)
if (oldValue != value) onValueChanged()
}
}

View File

@ -3,15 +3,15 @@ package me.rhunk.snapenhance.config.impl
import me.rhunk.snapenhance.config.ConfigValue
class ConfigIntegerValue(
var value: Int
private var value: Int
) : ConfigValue<Int>() {
override fun value() = value
override fun write(): String {
override fun read(): String {
return value.toString()
}
override fun read(value: String) {
override fun write(value: String) {
this.value = value.toInt()
}
}

View File

@ -4,17 +4,22 @@ import me.rhunk.snapenhance.config.ConfigValue
class ConfigStateListValue(
private val keys: List<String>,
var states: MutableMap<String, Boolean> = mutableMapOf()
private var states: MutableMap<String, Boolean> = mutableMapOf()
) : ConfigValue<Map<String, Boolean>>() {
override fun value() = states
fun value(key: String) = states[key] ?: false
fun setKey(key: String, state: Boolean) {
states[key] = state
onValueChanged()
}
override fun write(): String {
operator fun get(key: String) = states[key] ?: false
override fun read(): String {
return keys.joinToString("|") { "$it:${states[it]}" }
}
override fun read(value: String) {
override fun write(value: String) {
value.split("|").forEach {
val (key, state) = it.split(":")
states[key] = state.toBoolean()

View File

@ -4,25 +4,19 @@ import me.rhunk.snapenhance.config.ConfigValue
class ConfigStateSelection(
private val keys: List<String>,
var state: String = ""
private var state: String = ""
) : ConfigValue<String>() {
fun keys(): List<String> {
return keys
}
override fun value(): String {
override fun value() = state
override fun read(): String {
return state
}
fun value(key: String) {
state = key
}
override fun write(): String {
return state
}
override fun read(value: String) {
override fun write(value: String) {
state = value
}
}

View File

@ -3,15 +3,15 @@ package me.rhunk.snapenhance.config.impl
import me.rhunk.snapenhance.config.ConfigValue
class ConfigStateValue(
var value: Boolean
private var value: Boolean
) : ConfigValue<Boolean>() {
override fun value() = value
override fun write(): String {
override fun read(): String {
return value.toString()
}
override fun read(value: String) {
override fun write(value: String) {
this.value = value.toBoolean()
}
}

View File

@ -3,15 +3,18 @@ package me.rhunk.snapenhance.config.impl
import me.rhunk.snapenhance.config.ConfigValue
class ConfigStringValue(
var value: String = ""
private var value: String = "",
val isHidden: Boolean = false
) : ConfigValue<String>() {
override fun value() = value
override fun write(): String {
fun hiddenValue() = if (isHidden) value.map { '*' }.joinToString("") else value
override fun read(): String {
return value
}
override fun read(value: String) {
override fun write(value: String) {
this.value = value
}
}

View File

@ -60,7 +60,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
}
private fun canMergeOverlay(): Boolean {
if (context.config.options(ConfigProperty.DOWNLOAD_OPTIONS)["overlay_merge"] == false) return false
if (context.config.options(ConfigProperty.DOWNLOAD_OPTIONS)["merge_overlay"] == false) return false
return isFFmpegPresent
}
@ -228,9 +228,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
forceDownload: Boolean
) {
//messages
if (paramMap.containsKey("MESSAGE_ID") &&
(forceDownload || context.config.bool(ConfigProperty.AUTO_DOWNLOAD_SNAPS))) {
val id = paramMap["MESSAGE_ID"].toString()
paramMap["MESSAGE_ID"]?.toString()?.takeIf { forceDownload || canAutoDownload("friend_snaps") }?.let { id ->
val messageId = id.substring(id.lastIndexOf(":") + 1).toLong()
val senderId: String = context.database.getConversationMessageFromId(messageId)!!.sender_id!!
@ -244,23 +242,25 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
}
//private stories
val playlistV2Group =
if (paramMap.containsKey("PLAYLIST_V2_GROUP")) paramMap["PLAYLIST_V2_GROUP"].toString() else null
if (playlistV2Group != null &&
playlistV2Group.contains("storyUserId=") &&
(forceDownload || context.config.bool(ConfigProperty.AUTO_DOWNLOAD_STORIES))
) {
val storyIdStartIndex = playlistV2Group.indexOf("storyUserId=") + 12
val storyUserId = playlistV2Group.substring(storyIdStartIndex, playlistV2Group.indexOf(",", storyIdStartIndex))
paramMap["PLAYLIST_V2_GROUP"]?.toString()?.takeIf {
it.contains("storyUserId=") && (forceDownload || canAutoDownload("friend_stories"))
}?.let { playlistGroup ->
val storyIdStartIndex = playlistGroup.indexOf("storyUserId=") + 12
val storyUserId = playlistGroup.substring(
storyIdStartIndex,
playlistGroup.indexOf(",", storyIdStartIndex)
)
val author = context.database.getFriendInfo(if (storyUserId == "null") context.database.getMyUserId()!! else storyUserId)
downloadOperaMedia(mediaInfoMap, author!!.usernameForSorting!!)
return
}
val snapSource = paramMap["SNAP_SOURCE"].toString()
//public stories
if ((snapSource == "PUBLIC_USER" || snapSource == "SAVED_STORY") &&
(forceDownload || context.config.bool(ConfigProperty.AUTO_DOWNLOAD_PUBLIC_STORIES))) {
(forceDownload || canAutoDownload("public_stories"))) {
val userDisplayName = (if (paramMap.containsKey("USER_DISPLAY_NAME")) paramMap["USER_DISPLAY_NAME"].toString() else "").replace(
"[^\\x00-\\x7F]".toRegex(),
"")
@ -269,7 +269,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
}
//spotlight
if (snapSource == "SINGLE_SNAP_STORY" && (forceDownload || context.config.bool(ConfigProperty.AUTO_DOWNLOAD_SPOTLIGHT))) {
if (snapSource == "SINGLE_SNAP_STORY" && (forceDownload || canAutoDownload("spotlight"))) {
downloadOperaMedia(mediaInfoMap, "Spotlight")
return
}
@ -323,11 +323,9 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
}
}
private fun canAutoDownload(): Boolean {
return context.config.bool(ConfigProperty.AUTO_DOWNLOAD_SNAPS) ||
context.config.bool(ConfigProperty.AUTO_DOWNLOAD_STORIES) ||
context.config.bool(ConfigProperty.AUTO_DOWNLOAD_PUBLIC_STORIES) ||
context.config.bool(ConfigProperty.AUTO_DOWNLOAD_SPOTLIGHT)
private fun canAutoDownload(keyFilter: String? = null): Boolean {
val options = context.config.options(ConfigProperty.AUTO_DOWNLOAD_OPTIONS)
return options.filter { it.value }.any { keyFilter == null || it.key.contains(keyFilter, true) }
}
override fun asyncOnActivityCreate() {

View File

@ -1,4 +1,4 @@
package me.rhunk.snapenhance.features.impl.extras
package me.rhunk.snapenhance.features.impl.tweaks
import me.rhunk.snapenhance.bridge.common.impl.file.BridgeFileType
import me.rhunk.snapenhance.features.BridgeFileFeature

View File

@ -1,8 +1,7 @@
package me.rhunk.snapenhance.features.impl.extras
package me.rhunk.snapenhance.features.impl.tweaks
import me.rhunk.snapenhance.Logger
import me.rhunk.snapenhance.config.ConfigProperty
import me.rhunk.snapenhance.data.ContentType
import me.rhunk.snapenhance.data.MessageState
import me.rhunk.snapenhance.data.wrapper.impl.Message
import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID
@ -61,17 +60,14 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR
private fun canSaveMessage(message: Message): Boolean {
if (message.messageMetadata.savedBy.any { uuid -> uuid.toString() == myUserId }) return false
//only save chats
with(message.messageContent.contentType) {
if (this != ContentType.CHAT &&
this != ContentType.NOTE &&
this != ContentType.STICKER &&
this != ContentType.EXTERNAL_MEDIA) return false
}
return true
val contentType = message.messageContent.contentType.toString()
return context.config.options(ConfigProperty.AUTO_SAVE_MESSAGES).filter { it.value }.any { it.key == contentType }
}
private fun canSave(): Boolean {
if (context.config.options(ConfigProperty.AUTO_SAVE_MESSAGES).none { it.value }) return false
with(context.feature(Messaging::class)) {
if (lastOpenedConversationUUID == null) return@canSave false
val conversation = lastOpenedConversationUUID.toString()
@ -87,7 +83,7 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR
context.mappings.getMappedClass("callbacks", "FetchConversationWithMessagesCallback"),
"onFetchConversationWithMessagesComplete",
HookStage.BEFORE,
{ context.config.bool(ConfigProperty.AUTO_SAVE) && canSave()}
{ canSave() }
) { param ->
val conversationId = SnapUUID(param.arg<Any>(0).getObjectField("mConversationId")!!)
val messages = param.arg<List<Any>>(1).map { Message(it) }
@ -104,7 +100,7 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR
context.mappings.getMappedClass("callbacks", "FetchMessageCallback"),
"onFetchMessageComplete",
HookStage.BEFORE,
{ context.config.bool(ConfigProperty.AUTO_SAVE) && canSave()}
{ canSave() }
) { param ->
val message = Message(param.arg(0))
if (!canSaveMessage(message)) return@hook
@ -119,7 +115,7 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR
context.mappings.getMappedClass("callbacks", "SendMessageCallback"),
"onSuccess",
HookStage.BEFORE,
{ context.config.bool(ConfigProperty.AUTO_SAVE) && canSave()}
{ canSave() }
) {
val callback = CallbackBuilder(fetchConversationWithMessagesCallbackClass).build()
runCatching {

View File

@ -1,4 +1,4 @@
package me.rhunk.snapenhance.features.impl.extras
package me.rhunk.snapenhance.features.impl.tweaks
import me.rhunk.snapenhance.config.ConfigProperty
import me.rhunk.snapenhance.features.Feature

View File

@ -1,4 +1,4 @@
package me.rhunk.snapenhance.features.impl.extras
package me.rhunk.snapenhance.features.impl.tweaks
import me.rhunk.snapenhance.config.ConfigProperty
import me.rhunk.snapenhance.data.ContentType

View File

@ -1,4 +1,4 @@
package me.rhunk.snapenhance.features.impl.extras
package me.rhunk.snapenhance.features.impl.tweaks
import android.content.Intent
import me.rhunk.snapenhance.config.ConfigProperty
@ -17,8 +17,8 @@ class LocationSpoofer: Feature("LocationSpoof", loadParams = FeatureLoadParams.A
val longitude = bundle.getFloat("longitude")
with(context.config) {
get(ConfigProperty.LATITUDE).read(latitude.toString())
get(ConfigProperty.LONGITUDE).read(longitude.toString())
get(ConfigProperty.LATITUDE).writeFrom(latitude.toString())
get(ConfigProperty.LONGITUDE).writeFrom(longitude.toString())
writeConfig()
}
context.longToast("Location set to $latitude, $longitude")

View File

@ -1,4 +1,4 @@
package me.rhunk.snapenhance.features.impl.extras
package me.rhunk.snapenhance.features.impl.tweaks
import me.rhunk.snapenhance.config.ConfigProperty
import me.rhunk.snapenhance.features.Feature

View File

@ -1,4 +1,4 @@
package me.rhunk.snapenhance.features.impl.extras
package me.rhunk.snapenhance.features.impl.tweaks
import android.app.Notification
import android.app.NotificationManager
@ -206,7 +206,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
else it
}[MediaType.ORIGINAL] ?: throw Throwable("Failed to download media")
val bitmapPreview = PreviewUtils.createPreview(downloadedMedia, mediaType == MediaReferenceType.VIDEO)!!
val bitmapPreview = PreviewUtils.createPreview(downloadedMedia, mediaType.name.contains("VIDEO"))!!
val notificationBuilder = XposedHelpers.newInstance(
Notification.Builder::class.java,
context.androidContext,
@ -227,7 +227,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
}
}
if (contentType == ContentType.CHAT) {
if (contentType == ContentType.CHAT && context.config.options(ConfigProperty.BETTER_NOTIFICATIONS)["reply_button"] == true) {
setupNotificationActionButtons(conversationId, notificationData)
}
@ -264,9 +264,8 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
return@hook
}
if (!context.config.bool(ConfigProperty.SHOW_MESSAGE_CONTENT_IN_NOTIFICATIONS)) return@hook
if (!notificationType.endsWith("CHAT") && !notificationType.endsWith("SNAP")) return@hook
if (context.config.options(ConfigProperty.BETTER_NOTIFICATIONS)
.filter { it.value }.none { notificationType.endsWith(it.key.uppercase())}) return@hook
val conversationManager: Any = context.feature(Messaging::class).conversationManager
notificationDataQueue[messageId.toLong()] = notificationData

View File

@ -1,4 +1,4 @@
package me.rhunk.snapenhance.features.impl.extras
package me.rhunk.snapenhance.features.impl.tweaks
import me.rhunk.snapenhance.config.ConfigProperty
import me.rhunk.snapenhance.features.Feature

View File

@ -1,4 +1,4 @@
package me.rhunk.snapenhance.features.impl.extras
package me.rhunk.snapenhance.features.impl.tweaks
import me.rhunk.snapenhance.config.ConfigProperty
import me.rhunk.snapenhance.data.ContentType

View File

@ -22,14 +22,15 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE
val chatNoteRecordButton = resources.getIdentifier("chat_note_record_button", "id", Constants.SNAPCHAT_PACKAGE_NAME)
val chatInputBarSticker = resources.getIdentifier("chat_input_bar_sticker", "id", Constants.SNAPCHAT_PACKAGE_NAME)
val chatInputBarCognac = resources.getIdentifier("chat_input_bar_cognac", "id", Constants.SNAPCHAT_PACKAGE_NAME)
val hiddenElements = context.config.options(ConfigProperty.HIDE_UI_ELEMENTS)
Hooker.hook(View::class.java, "setVisibility", HookStage.BEFORE) { methodParam ->
val viewId = (methodParam.thisObject() as View).id
if (viewId == chatNoteRecordButton && context.config.bool(ConfigProperty.REMOVE_VOICE_RECORD_BUTTON)) {
if (viewId == chatNoteRecordButton && hiddenElements["remove_voice_record_button"] == true) {
methodParam.setArg(0, View.GONE)
}
if (viewId == callButton1 || viewId == callButton2) {
if (!context.config.bool(ConfigProperty.REMOVE_CALL_BUTTONS)) return@hook
if (hiddenElements["remove_call_buttons"] == false) return@hook
methodParam.setArg(0, View.GONE)
}
}
@ -45,23 +46,23 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE
val view: View = param.arg(0)
val viewId = view.id
if (viewId == chatNoteRecordButton && context.config.bool(ConfigProperty.REMOVE_VOICE_RECORD_BUTTON)) {
if (viewId == chatNoteRecordButton && hiddenElements["remove_voice_record_button"] == true) {
view.isEnabled = false
view.setWillNotDraw(true)
}
if (chatInputBarCognac == viewId && context.config.bool(ConfigProperty.REMOVE_COGNAC_BUTTON)) {
if (chatInputBarCognac == viewId && hiddenElements["remove_cognac_button"] == true) {
view.visibility = View.GONE
}
if (chatInputBarSticker == viewId && context.config.bool(ConfigProperty.REMOVE_STICKERS_BUTTON)) {
if (chatInputBarSticker == viewId && hiddenElements["remove_stickers_button"] == true) {
view.visibility = View.GONE
}
if (viewId == callButton1 || viewId == callButton2) {
if (!context.config.bool(ConfigProperty.REMOVE_CALL_BUTTONS)) return@hook
if (hiddenElements["remove_call_buttons"] == false) return@hook
if (view.visibility == View.GONE) return@hook
}
if (viewId == callButtonsStub) {
if (!context.config.bool(ConfigProperty.REMOVE_CALL_BUTTONS)) return@hook
if (hiddenElements["remove_call_buttons"] == false) return@hook
param.setResult(null)
}
}

View File

@ -1,10 +1,13 @@
package me.rhunk.snapenhance.features.impl.ui.menus
import android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog
import android.content.Context
import android.os.Bundle
import android.view.MotionEvent
import android.widget.Button
import android.widget.EditText
import me.rhunk.snapenhance.R
import org.osmdroid.config.Configuration
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
@ -20,12 +23,13 @@ class MapActivity : Activity() {
private lateinit var mapView: MapView
@SuppressLint("MissingInflatedId", "ResourceType")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val contextBundle = intent.extras?.getBundle("location") ?: return
val latitude = contextBundle.getDouble("latitude")
val longitude = contextBundle.getDouble("longitude")
val locationLatitude = contextBundle.getDouble("latitude")
val locationLongitude = contextBundle.getDouble("longitude")
Configuration.getInstance().load(applicationContext, getSharedPreferences("osmdroid", Context.MODE_PRIVATE))
@ -35,7 +39,7 @@ class MapActivity : Activity() {
mapView.setMultiTouchControls(true);
mapView.setTileSource(TileSourceFactory.MAPNIK)
val startPoint = GeoPoint(latitude, longitude)
val startPoint = GeoPoint(locationLatitude, locationLongitude)
mapView.controller.setZoom(10.0)
mapView.controller.setCenter(startPoint)
@ -64,6 +68,28 @@ class MapActivity : Activity() {
setResult(RESULT_OK, intent.putExtra("location", bundle))
finish()
}
val setPreciseLocationButton = findViewById<Button>(R.id.set_precise_location_button)
setPreciseLocationButton.setOnClickListener {
val locationDialog = layoutInflater.inflate(R.layout.precise_location_dialog, null)
val dialogLatitude = locationDialog.findViewById<EditText>(R.id.dialog_latitude).also { it.setText(marker.position.latitude.toString()) }
val dialogLongitude = locationDialog.findViewById<EditText>(R.id.dialog_longitude).also { it.setText(marker.position.longitude.toString()) }
AlertDialog.Builder(this)
.setView(locationDialog)
.setTitle("Set a precise location")
.setPositiveButton("Set") { _, _ ->
val latitude = dialogLatitude.text.toString().toDoubleOrNull()
val longitude = dialogLongitude.text.toString().toDoubleOrNull()
if (latitude != null && longitude != null) {
val preciseLocation = GeoPoint(latitude, longitude)
mapView.controller.setCenter(preciseLocation)
marker.position = preciseLocation
mapView.invalidate()
}
}.setNegativeButton("Cancel") { _, _ -> }.show()
}
}
override fun onDestroy() {

View File

@ -64,34 +64,35 @@ class ChatActionMenu : AbstractMenu() {
)
)
}
if (context.config.bool(ConfigProperty.DOWNLOAD_INCHAT_SNAPS)) {
val previewButton = Button(viewGroup.context)
applyButtonTheme(parent, previewButton)
previewButton.text = "Preview"
previewButton.setOnClickListener {
closeActionMenu()
context.executeAsync { context.feature(MediaDownloader::class).onMessageActionMenu(true) }
}
parent.addView(previewButton)
}
if (context.config.bool(ConfigProperty.CHAT_DOWNLOAD_CONTEXT_MENU)) {
parent.addView(Button(viewGroup.context).apply {
applyButtonTheme(parent, this)
text = this@ChatActionMenu.context.translation.get("chat_action_menu.preview_button")
setOnClickListener {
closeActionMenu()
this@ChatActionMenu.context.executeAsync { this@ChatActionMenu.context.feature(MediaDownloader::class).onMessageActionMenu(true) }
}
})
//download snap in chat
if (context.config.bool(ConfigProperty.DOWNLOAD_INCHAT_SNAPS)) {
val downloadButton = Button(viewGroup.context)
applyButtonTheme(parent, downloadButton)
downloadButton.text = "Download"
downloadButton.setOnClickListener {
closeActionMenu()
context.executeAsync { context.feature(MediaDownloader::class).onMessageActionMenu(false) }
}
parent.addView(downloadButton)
parent.addView(Button(viewGroup.context).apply {
applyButtonTheme(parent, this)
text = this@ChatActionMenu.context.translation.get("chat_action_menu.download_button")
setOnClickListener {
closeActionMenu()
this@ChatActionMenu.context.executeAsync {
this@ChatActionMenu.context.feature(
MediaDownloader::class
).onMessageActionMenu(false)
}
}
})
}
//delete logged message button
if (context.config.bool(ConfigProperty.MESSAGE_LOGGER)) {
val downloadButton = Button(viewGroup.context)
applyButtonTheme(parent, downloadButton)
downloadButton.text = "Deleted logged message"
downloadButton.text = context.translation.get("chat_action_menu.delete_logged_message_button")
downloadButton.setOnClickListener {
closeActionMenu()
context.executeAsync {

View File

@ -21,7 +21,7 @@ import me.rhunk.snapenhance.database.objects.FriendInfo
import me.rhunk.snapenhance.database.objects.UserConversationLink
import me.rhunk.snapenhance.features.impl.Messaging
import me.rhunk.snapenhance.features.impl.downloader.AntiAutoDownload
import me.rhunk.snapenhance.features.impl.extras.AntiAutoSave
import me.rhunk.snapenhance.features.impl.tweaks.AntiAutoSave
import me.rhunk.snapenhance.features.impl.spying.StealthMode
import me.rhunk.snapenhance.features.impl.ui.menus.AbstractMenu
import me.rhunk.snapenhance.features.impl.ui.menus.ViewAppearanceHelper.applyTheme
@ -215,7 +215,7 @@ class FriendFeedInfoMenu : AbstractMenu() {
run {
val userId = context.database.getFriendFeedInfoByConversationId(conversationId)?.friendUserId ?: return@run
if (context.config.bool(ConfigProperty.ANTI_DOWNLOAD_BUTTON)) {
if (context.config.bool(ConfigProperty.DOWNLOAD_BLACKLIST)) {
createToggleFeature(viewModel,
viewConsumer,
"friend_menu_option.anti_auto_download",

View File

@ -35,10 +35,11 @@ class SettingsMenu : AbstractMenu() {
@SuppressLint("SetTextI18n")
private fun createPropertyView(viewModel: View, property: ConfigProperty): View {
val updateButtonText: (TextView, String) -> Unit = { textView, text ->
textView.text = "${context.translation.get(property.nameKey)} $text"
textView.text = "${context.translation.get(property.nameKey)}${if (text.isEmpty()) "" else ": $text"}"
}
val updateStateSelectionText: (TextView, String) -> Unit = { textView, text ->
updateButtonText(textView, text.let { if (it.isEmpty()) "(empty)" else ": $it" })
val updateLocalizedText: (TextView, String) -> Unit = { textView, value ->
updateButtonText(textView, value.let { if (it.isEmpty()) "(empty)" else context.translation.get("option." + property.nameKey + "." + it) })
}
val textEditor: ((String) -> Unit) -> Unit = { updateValue ->
@ -61,23 +62,29 @@ class SettingsMenu : AbstractMenu() {
val resultView: View = when (property.valueContainer) {
is ConfigStringValue -> {
val textView = TextView(viewModel.context)
updateButtonText(textView, property.valueContainer.value)
updateButtonText(textView, property.valueContainer.let {
if (it.isHidden) it.hiddenValue()
else it.value()
})
ViewAppearanceHelper.applyTheme(viewModel, textView)
textView.setOnClickListener {
textEditor { value ->
property.valueContainer.value = value
updateButtonText(textView, value)
property.valueContainer.writeFrom(value)
updateButtonText(textView, property.valueContainer.let {
if (it.isHidden) it.hiddenValue()
else it.value()
})
}
}
textView
}
is ConfigIntegerValue -> {
val button = Button(viewModel.context)
updateButtonText(button, property.valueContainer.value.toString())
updateButtonText(button, property.valueContainer.value().toString())
button.setOnClickListener {
textEditor { value ->
runCatching {
property.valueContainer.value = value.toInt()
property.valueContainer.writeFrom(value)
updateButtonText(button, value)
}.onFailure {
context.shortToast("Invalid value")
@ -90,30 +97,30 @@ class SettingsMenu : AbstractMenu() {
is ConfigStateValue -> {
val switch = Switch(viewModel.context)
switch.text = context.translation.get(property.nameKey)
switch.isChecked = property.valueContainer.value
switch.isChecked = property.valueContainer.value()
switch.setOnCheckedChangeListener { _, isChecked ->
property.valueContainer.value = isChecked
property.valueContainer.writeFrom(isChecked.toString())
}
ViewAppearanceHelper.applyTheme(viewModel, switch)
switch
}
is ConfigStateSelection -> {
val button = Button(viewModel.context)
updateStateSelectionText(button, property.valueContainer.value())
updateLocalizedText(button, property.valueContainer.value())
button.setOnClickListener {_ ->
val builder = AlertDialog.Builder(viewModel.context)
builder.setTitle(context.translation.get(property.nameKey))
builder.setSingleChoiceItems(
property.valueContainer.keys().toTypedArray(),
property.valueContainer.keys().toTypedArray().map { context.translation.get("option." + property.nameKey + "." + it) }.toTypedArray(),
property.valueContainer.keys().indexOf(property.valueContainer.value())
) { _, which ->
property.valueContainer.value(property.valueContainer.keys()[which])
property.valueContainer.writeFrom(property.valueContainer.keys()[which])
}
builder.setPositiveButton("OK") { _, _ ->
updateStateSelectionText(button, property.valueContainer.value())
updateLocalizedText(button, property.valueContainer.value())
}
builder.show()
@ -123,25 +130,25 @@ class SettingsMenu : AbstractMenu() {
}
is ConfigStateListValue -> {
val button = Button(viewModel.context)
updateStateSelectionText(button, property.valueContainer.toString())
updateButtonText(button, "(${property.valueContainer.value().count { it.value }})")
button.setOnClickListener {_ ->
val builder = AlertDialog.Builder(viewModel.context)
builder.setTitle(context.translation.get(property.nameKey))
val sortedStates = property.valueContainer.states.toSortedMap()
val sortedStates = property.valueContainer.value().toSortedMap()
builder.setMultiChoiceItems(
sortedStates.toSortedMap().map { context.translation.get("option." + property.nameKey + "." +it.key) }.toTypedArray(),
sortedStates.map { it.value }.toBooleanArray()
) { _, which, isChecked ->
sortedStates.keys.toList()[which].let { key ->
property.valueContainer.states[key] = isChecked
property.valueContainer.setKey(key, isChecked)
}
}
builder.setPositiveButton("OK") { _, _ ->
updateStateSelectionText(button, property.valueContainer.toString())
updateButtonText(button, "(${property.valueContainer.value().count { it.value }})")
}
builder.show()
@ -191,15 +198,15 @@ class SettingsMenu : AbstractMenu() {
it.key.category
}.forEach { (category, value) ->
addView(createCategoryTitle(viewModel, category.key))
value.forEach {
addView(createPropertyView(viewModel, it.key))
actions.find { pair -> pair.first.dependsOnProperty == it.key }?.let { pair ->
value.filter { it.key.shouldAppearInSettings }.forEach { (property, _) ->
addView(createPropertyView(viewModel, property))
actions.find { pair -> pair.first.dependsOnProperty == property}?.let { pair ->
addView(pair.second())
}
}
}
addView(createCategoryTitle(viewModel, "category.debugging"))
//addView(createCategoryTitle(viewModel, "category.debugging"))
actions.filter { it.first.dependsOnProperty == null }.forEach {
addView(it.second())
}

View File

@ -41,14 +41,14 @@ class ConfigManager(
JsonObject::class.java
)
entries().forEach { (key, value) ->
value.read(configObject.get(key.name)?.asString ?: value.write())
value.writeFrom(configObject.get(key.name)?.asString ?: value.read())
}
}
fun writeConfig() {
val configObject = JsonObject()
entries().forEach { (key, value) ->
configObject.addProperty(key.name, value.write())
configObject.addProperty(key.name, value.read())
}
context.bridgeClient.writeFile(
BridgeFileType.CONFIG,

View File

@ -10,15 +10,15 @@ import me.rhunk.snapenhance.features.impl.downloader.AntiAutoDownload
import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader
import me.rhunk.snapenhance.features.impl.experiments.AppPasscode
import me.rhunk.snapenhance.features.impl.experiments.MeoPasscodeBypass
import me.rhunk.snapenhance.features.impl.extras.AntiAutoSave
import me.rhunk.snapenhance.features.impl.extras.AutoSave
import me.rhunk.snapenhance.features.impl.extras.DisableVideoLengthRestriction
import me.rhunk.snapenhance.features.impl.extras.GalleryMediaSendOverride
import me.rhunk.snapenhance.features.impl.extras.LocationSpoofer
import me.rhunk.snapenhance.features.impl.extras.MediaQualityLevelOverride
import me.rhunk.snapenhance.features.impl.extras.Notifications
import me.rhunk.snapenhance.features.impl.extras.SnapchatPlus
import me.rhunk.snapenhance.features.impl.extras.UnlimitedSnapViewTime
import me.rhunk.snapenhance.features.impl.tweaks.AntiAutoSave
import me.rhunk.snapenhance.features.impl.tweaks.AutoSave
import me.rhunk.snapenhance.features.impl.tweaks.DisableVideoLengthRestriction
import me.rhunk.snapenhance.features.impl.tweaks.GalleryMediaSendOverride
import me.rhunk.snapenhance.features.impl.tweaks.LocationSpoofer
import me.rhunk.snapenhance.features.impl.tweaks.MediaQualityLevelOverride
import me.rhunk.snapenhance.features.impl.tweaks.Notifications
import me.rhunk.snapenhance.features.impl.tweaks.SnapchatPlus
import me.rhunk.snapenhance.features.impl.tweaks.UnlimitedSnapViewTime
import me.rhunk.snapenhance.features.impl.privacy.DisableMetrics
import me.rhunk.snapenhance.features.impl.privacy.PreventMessageSending
import me.rhunk.snapenhance.features.impl.spying.AnonymousStoryViewing

View File

@ -11,15 +11,34 @@
</org.osmdroid.views.MapView>
<Button
android:id="@+id/apply_location_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_marginEnd="20dp"
android:layout_marginBottom="20dp"
android:textSize="20sp"
android:background="@android:color/white"
android:text="Apply"
tools:ignore="HardcodedText,RtlHardcoded" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/set_precise_location_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="left"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:padding="10dp"
android:background="@android:color/white"
android:text="Set Precise Location"
android:textSize="20sp"
tools:ignore="HardcodedText,RtlHardcoded" />
<Button
android:id="@+id/apply_location_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
android:background="@android:color/white"
android:text="Apply"
android:textSize="20sp"
tools:ignore="HardcodedText,RtlHardcoded" />
</FrameLayout>
</FrameLayout>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:orientation="vertical"
tools:ignore="HardcodedText">
<EditText
android:id="@+id/dialog_latitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="numberDecimal"
android:hint="Latitude"
android:autofillHints="" />
<EditText
android:id="@+id/dialog_longitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="numberDecimal"
android:hint="Longitude"
android:autofillHints="" />
</LinearLayout>