mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-13 05:37:48 +02:00
feat: config export
- activity launcher helper
This commit is contained in:
@ -4,6 +4,8 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import coil.decode.VideoFrameDecoder
|
import coil.decode.VideoFrameDecoder
|
||||||
@ -20,6 +22,7 @@ import me.rhunk.snapenhance.ui.manager.data.ModMappingsInfo
|
|||||||
import me.rhunk.snapenhance.ui.manager.data.SnapchatAppInfo
|
import me.rhunk.snapenhance.ui.manager.data.SnapchatAppInfo
|
||||||
import me.rhunk.snapenhance.ui.setup.Requirements
|
import me.rhunk.snapenhance.ui.setup.Requirements
|
||||||
import me.rhunk.snapenhance.ui.setup.SetupActivity
|
import me.rhunk.snapenhance.ui.setup.SetupActivity
|
||||||
|
import me.rhunk.snapenhance.ui.util.ActivityLauncherHelper
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
class RemoteSideContext(
|
class RemoteSideContext(
|
||||||
@ -28,9 +31,14 @@ class RemoteSideContext(
|
|||||||
private var _activity: WeakReference<Activity>? = null
|
private var _activity: WeakReference<Activity>? = null
|
||||||
lateinit var bridgeService: BridgeService
|
lateinit var bridgeService: BridgeService
|
||||||
|
|
||||||
|
lateinit var activityLauncherHelper: ActivityLauncherHelper
|
||||||
var activity: Activity?
|
var activity: Activity?
|
||||||
get() = _activity?.get()
|
get() = _activity?.get()
|
||||||
set(value) { _activity?.clear(); _activity = WeakReference(value) }
|
set(value) {
|
||||||
|
_activity?.clear();
|
||||||
|
_activity = WeakReference(value)
|
||||||
|
activityLauncherHelper = ActivityLauncherHelper(value as ComponentActivity)
|
||||||
|
}
|
||||||
|
|
||||||
val config = ModConfig()
|
val config = ModConfig()
|
||||||
val translation = LocaleWrapper()
|
val translation = LocaleWrapper()
|
||||||
@ -84,6 +92,20 @@ class RemoteSideContext(
|
|||||||
} else null
|
} else null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun longToast(message: Any) {
|
||||||
|
activity?.runOnUiThread {
|
||||||
|
Toast.makeText(activity, message.toString(), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
Logger.debug(message.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shortToast(message: Any) {
|
||||||
|
activity?.runOnUiThread {
|
||||||
|
Toast.makeText(activity, message.toString(), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
Logger.debug(message.toString())
|
||||||
|
}
|
||||||
|
|
||||||
fun checkForRequirements(overrideRequirements: Int? = null): Boolean {
|
fun checkForRequirements(overrideRequirements: Int? = null): Boolean {
|
||||||
var requirements = overrideRequirements ?: 0
|
var requirements = overrideRequirements ?: 0
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package me.rhunk.snapenhance.ui.manager.sections.features
|
package me.rhunk.snapenhance.ui.manager.sections.features
|
||||||
|
|
||||||
import androidx.activity.ComponentActivity
|
import android.net.Uri
|
||||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
@ -27,12 +27,14 @@ import androidx.compose.foundation.text.KeyboardActions
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.material.icons.filled.FolderOpen
|
import androidx.compose.material.icons.filled.FolderOpen
|
||||||
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material.icons.filled.OpenInNew
|
import androidx.compose.material.icons.filled.OpenInNew
|
||||||
import androidx.compose.material.icons.filled.Rule
|
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.rounded.Save
|
import androidx.compose.material.icons.rounded.Save
|
||||||
import androidx.compose.material3.BottomSheetScaffoldState
|
import androidx.compose.material3.BottomSheetScaffoldState
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FilledIconButton
|
import androidx.compose.material3.FilledIconButton
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
@ -78,7 +80,9 @@ import me.rhunk.snapenhance.core.config.PropertyKey
|
|||||||
import me.rhunk.snapenhance.core.config.PropertyPair
|
import me.rhunk.snapenhance.core.config.PropertyPair
|
||||||
import me.rhunk.snapenhance.core.config.PropertyValue
|
import me.rhunk.snapenhance.core.config.PropertyValue
|
||||||
import me.rhunk.snapenhance.ui.manager.Section
|
import me.rhunk.snapenhance.ui.manager.Section
|
||||||
import me.rhunk.snapenhance.ui.util.ChooseFolderHelper
|
import me.rhunk.snapenhance.ui.util.chooseFolder
|
||||||
|
import me.rhunk.snapenhance.ui.util.openFile
|
||||||
|
import me.rhunk.snapenhance.ui.util.saveFile
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
class FeaturesSection : Section() {
|
class FeaturesSection : Section() {
|
||||||
@ -90,8 +94,6 @@ class FeaturesSection : Section() {
|
|||||||
const val SEARCH_FEATURE_ROUTE = "search_feature/{keyword}"
|
const val SEARCH_FEATURE_ROUTE = "search_feature/{keyword}"
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var openFolderCallback: (uri: String) -> Unit
|
|
||||||
private lateinit var openFolderLauncher: () -> Unit
|
|
||||||
|
|
||||||
private val featuresRouteName by lazy { context.translation["manager.routes.features"] }
|
private val featuresRouteName by lazy { context.translation["manager.routes.features"] }
|
||||||
|
|
||||||
@ -122,32 +124,13 @@ class FeaturesSection : Section() {
|
|||||||
properties
|
properties
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun init() {
|
|
||||||
openFolderLauncher = ChooseFolderHelper.createChooseFolder(context.activity!! as ComponentActivity) {
|
|
||||||
openFolderCallback(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun canGoBack() = sectionTopBarName() != featuresRouteName
|
override fun canGoBack() = sectionTopBarName() != featuresRouteName
|
||||||
|
|
||||||
override fun sectionTopBarName(): String {
|
override fun sectionTopBarName(): String {
|
||||||
navController.currentBackStackEntry?.arguments?.getString("name")?.let { routeName ->
|
navController.currentBackStackEntry?.arguments?.getString("name")?.let { routeName ->
|
||||||
val currentContainerPair = allContainers[routeName]
|
val currentContainerPair = allContainers[routeName]
|
||||||
val propertyTree = run {
|
return context.translation["${currentContainerPair?.key?.propertyTranslationPath()}.name"]
|
||||||
var key = currentContainerPair?.key
|
|
||||||
val tree = mutableListOf<String>()
|
|
||||||
while (key != null) {
|
|
||||||
tree.add(key.propertyTranslationPath())
|
|
||||||
key = key.parentKey
|
|
||||||
}
|
|
||||||
tree
|
|
||||||
}
|
|
||||||
|
|
||||||
val translatedKey = propertyTree.reversed().joinToString(" > ") {
|
|
||||||
context.translation["$it.name"]
|
|
||||||
}
|
|
||||||
|
|
||||||
return "$featuresRouteName > $translatedKey"
|
|
||||||
}
|
}
|
||||||
return featuresRouteName
|
return featuresRouteName
|
||||||
}
|
}
|
||||||
@ -203,10 +186,9 @@ class FeaturesSection : Section() {
|
|||||||
|
|
||||||
if (property.key.params.flags.contains(ConfigFlag.FOLDER)) {
|
if (property.key.params.flags.contains(ConfigFlag.FOLDER)) {
|
||||||
IconButton(onClick = registerClickCallback {
|
IconButton(onClick = registerClickCallback {
|
||||||
openFolderCallback = { uri ->
|
context.activityLauncherHelper.chooseFolder { uri ->
|
||||||
propertyValue.setAny(uri)
|
propertyValue.setAny(uri)
|
||||||
}
|
}
|
||||||
openFolderLauncher()
|
|
||||||
}.let { { it.invoke(true) } }) {
|
}.let { { it.invoke(true) } }) {
|
||||||
Icon(Icons.Filled.FolderOpen, contentDescription = null)
|
Icon(Icons.Filled.FolderOpen, contentDescription = null)
|
||||||
}
|
}
|
||||||
@ -460,6 +442,63 @@ class FeaturesSection : Section() {
|
|||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showSearchBar) return
|
||||||
|
|
||||||
|
var showExportDropdownMenu by remember { mutableStateOf(false) }
|
||||||
|
val actions = remember {
|
||||||
|
mapOf(
|
||||||
|
"Export" to {
|
||||||
|
context.activityLauncherHelper.saveFile("config.json", "application/json") { uri ->
|
||||||
|
context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
|
||||||
|
context.config.writeConfig()
|
||||||
|
context.config.exportToString().byteInputStream().copyTo(it)
|
||||||
|
context.shortToast("Config exported successfully!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Import" to {
|
||||||
|
context.activityLauncherHelper.openFile("application/json") { uri ->
|
||||||
|
context.androidContext.contentResolver.openInputStream(Uri.parse(uri))?.use {
|
||||||
|
runCatching {
|
||||||
|
context.config.loadFromString(it.readBytes().toString(Charsets.UTF_8))
|
||||||
|
}.onFailure {
|
||||||
|
context.longToast("Failed to import config ${it.message}")
|
||||||
|
return@use
|
||||||
|
}
|
||||||
|
context.shortToast("Config successfully loaded!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Reset" to {
|
||||||
|
context.config.reset()
|
||||||
|
context.shortToast("Config successfully reset!")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton(onClick = { showExportDropdownMenu = !showExportDropdownMenu}) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.MoreVert,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showExportDropdownMenu) {
|
||||||
|
DropdownMenu(expanded = showExportDropdownMenu, onDismissRequest = { showExportDropdownMenu = false }) {
|
||||||
|
actions.forEach { (name, action) ->
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = {
|
||||||
|
Text(text = name)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
action()
|
||||||
|
showExportDropdownMenu = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package me.rhunk.snapenhance.ui.setup.screens.impl
|
package me.rhunk.snapenhance.ui.setup.screens.impl
|
||||||
|
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
@ -10,12 +9,11 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import me.rhunk.snapenhance.Logger
|
import me.rhunk.snapenhance.Logger
|
||||||
import me.rhunk.snapenhance.ui.setup.screens.SetupScreen
|
import me.rhunk.snapenhance.ui.setup.screens.SetupScreen
|
||||||
import me.rhunk.snapenhance.ui.util.ChooseFolderHelper
|
|
||||||
import me.rhunk.snapenhance.ui.util.ObservableMutableState
|
import me.rhunk.snapenhance.ui.util.ObservableMutableState
|
||||||
|
import me.rhunk.snapenhance.ui.util.chooseFolder
|
||||||
|
|
||||||
class SaveFolderScreen : SetupScreen() {
|
class SaveFolderScreen : SetupScreen() {
|
||||||
private lateinit var saveFolder: ObservableMutableState<String>
|
private lateinit var saveFolder: ObservableMutableState<String>
|
||||||
private lateinit var openFolderLauncher: () -> Unit
|
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
saveFolder = ObservableMutableState(
|
saveFolder = ObservableMutableState(
|
||||||
@ -29,9 +27,6 @@ class SaveFolderScreen : SetupScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
openFolderLauncher = ChooseFolderHelper.createChooseFolder(context.activity as ComponentActivity) { uri ->
|
|
||||||
saveFolder.value = uri
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -39,7 +34,9 @@ class SaveFolderScreen : SetupScreen() {
|
|||||||
DialogText(text = context.translation["setup.dialogs.save_folder"])
|
DialogText(text = context.translation["setup.dialogs.save_folder"])
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
openFolderLauncher()
|
context.activityLauncherHelper.chooseFolder {
|
||||||
|
saveFolder.value = it
|
||||||
|
}
|
||||||
}) {
|
}) {
|
||||||
Text(text = context.translation["setup.dialogs.select_save_folder_button"])
|
Text(text = context.translation["setup.dialogs.select_save_folder_button"])
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
package me.rhunk.snapenhance.ui.util
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import me.rhunk.snapenhance.Logger
|
||||||
|
|
||||||
|
class ActivityLauncherHelper(
|
||||||
|
val activity: ComponentActivity
|
||||||
|
) {
|
||||||
|
private var callback: ((Intent) -> Unit)? = null
|
||||||
|
private var activityResultLauncher: ActivityResultLauncher<Intent> =
|
||||||
|
activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
|
if (result.resultCode == ComponentActivity.RESULT_OK) {
|
||||||
|
runCatching {
|
||||||
|
callback?.let { it(result.data!!) }
|
||||||
|
}.onFailure {
|
||||||
|
Logger.error("Failed to process activity result", it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun launch(intent: Intent, callback: (Intent) -> Unit) {
|
||||||
|
if (this.callback != null) {
|
||||||
|
throw IllegalStateException("Already launching an activity")
|
||||||
|
}
|
||||||
|
this.callback = callback
|
||||||
|
activityResultLauncher.launch(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ActivityLauncherHelper.chooseFolder(callback: (uri: String) -> Unit) {
|
||||||
|
launch(
|
||||||
|
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||||
|
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||||
|
) {
|
||||||
|
val uri = it.data ?: return@launch
|
||||||
|
val value = uri.toString()
|
||||||
|
this.activity.contentResolver.takePersistableUriPermission(
|
||||||
|
uri,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
|
)
|
||||||
|
callback(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ActivityLauncherHelper.saveFile(name: String, type: String = "*/*", callback: (uri: String) -> Unit) {
|
||||||
|
launch(
|
||||||
|
Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||||
|
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
.setType(type)
|
||||||
|
.putExtra(Intent.EXTRA_TITLE, name)
|
||||||
|
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||||
|
) {
|
||||||
|
val uri = it.data ?: return@launch
|
||||||
|
val value = uri.toString()
|
||||||
|
this.activity.contentResolver.takePersistableUriPermission(
|
||||||
|
uri,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
|
)
|
||||||
|
callback(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun ActivityLauncherHelper.openFile(type: String = "*/*", callback: (uri: String) -> Unit) {
|
||||||
|
launch(
|
||||||
|
Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||||
|
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
.setType(type)
|
||||||
|
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||||
|
) {
|
||||||
|
val uri = it.data ?: return@launch
|
||||||
|
val value = uri.toString()
|
||||||
|
this.activity.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||||
|
callback(value)
|
||||||
|
}
|
||||||
|
}
|
@ -1,26 +0,0 @@
|
|||||||
package me.rhunk.snapenhance.ui.util
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
|
|
||||||
object ChooseFolderHelper {
|
|
||||||
fun createChooseFolder(activity: ComponentActivity, callback: (uri: String) -> Unit): () -> Unit {
|
|
||||||
val activityResultLauncher = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) result@{
|
|
||||||
if (it.resultCode != Activity.RESULT_OK) return@result
|
|
||||||
val uri = it.data?.data ?: return@result
|
|
||||||
val value = uri.toString()
|
|
||||||
activity.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
|
||||||
callback(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
activityResultLauncher.launch(
|
|
||||||
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
|
||||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -191,10 +191,6 @@
|
|||||||
"name": "Anonymous Story Viewing",
|
"name": "Anonymous Story Viewing",
|
||||||
"description": "Prevents anyone from knowing you've seen their story"
|
"description": "Prevents anyone from knowing you've seen their story"
|
||||||
},
|
},
|
||||||
"prevent_read_receipts": {
|
|
||||||
"name": "Prevent Read Receipts",
|
|
||||||
"description": "Prevent anyone from knowing you've opened their Snaps/Chats"
|
|
||||||
},
|
|
||||||
"hide_bitmoji_presence": {
|
"hide_bitmoji_presence": {
|
||||||
"name": "Hide Bitmoji Presence",
|
"name": "Hide Bitmoji Presence",
|
||||||
"description": "Hides your Bitmoji presence from the chat"
|
"description": "Hides your Bitmoji presence from the chat"
|
||||||
|
@ -8,11 +8,8 @@ enum class BridgeFileType(val value: Int, val fileName: String, val displayName:
|
|||||||
CONFIG(0, "config.json", "Config"),
|
CONFIG(0, "config.json", "Config"),
|
||||||
MAPPINGS(1, "mappings.json", "Mappings"),
|
MAPPINGS(1, "mappings.json", "Mappings"),
|
||||||
MESSAGE_LOGGER_DATABASE(2, "message_logger.db", "Message Logger",true),
|
MESSAGE_LOGGER_DATABASE(2, "message_logger.db", "Message Logger",true),
|
||||||
STEALTH(3, "stealth.txt", "Stealth Conversations"),
|
AUTO_UPDATER_TIMESTAMP(3, "auto_updater_timestamp.txt", "Auto Updater Timestamp"),
|
||||||
ANTI_AUTO_DOWNLOAD(4, "anti_auto_download.txt", "Anti Auto Download"),
|
PINNED_CONVERSATIONS(4, "pinned_conversations.txt", "Pinned Conversations");
|
||||||
ANTI_AUTO_SAVE(5, "anti_auto_save.txt", "Anti Auto Save"),
|
|
||||||
AUTO_UPDATER_TIMESTAMP(6, "auto_updater_timestamp.txt", "Auto Updater Timestamp"),
|
|
||||||
PINNED_CONVERSATIONS(7, "pinned_conversations.txt", "Pinned Conversations");
|
|
||||||
|
|
||||||
fun resolve(context: Context): File = if (isDatabase) {
|
fun resolve(context: Context): File = if (isDatabase) {
|
||||||
context.getDatabasePath(fileName)
|
context.getDatabasePath(fileName)
|
||||||
|
@ -19,10 +19,11 @@ class ModConfig {
|
|||||||
private val file = FileLoaderWrapper(BridgeFileType.CONFIG, "{}".toByteArray(Charsets.UTF_8))
|
private val file = FileLoaderWrapper(BridgeFileType.CONFIG, "{}".toByteArray(Charsets.UTF_8))
|
||||||
var wasPresent by Delegates.notNull<Boolean>()
|
var wasPresent by Delegates.notNull<Boolean>()
|
||||||
|
|
||||||
val root = RootConfig()
|
lateinit var root: RootConfig
|
||||||
operator fun getValue(thisRef: Any?, property: Any?) = root
|
operator fun getValue(thisRef: Any?, property: Any?) = root
|
||||||
|
|
||||||
private fun load() {
|
private fun load() {
|
||||||
|
root = RootConfig()
|
||||||
wasPresent = file.isFileExists()
|
wasPresent = file.isFileExists()
|
||||||
if (!file.isFileExists()) {
|
if (!file.isFileExists()) {
|
||||||
writeConfig()
|
writeConfig()
|
||||||
@ -44,10 +45,26 @@ class ModConfig {
|
|||||||
root.fromJson(configObject)
|
root.fromJson(configObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeConfig() {
|
fun exportToString(): String {
|
||||||
val configObject = root.toJson()
|
val configObject = root.toJson()
|
||||||
configObject.addProperty("_locale", locale)
|
configObject.addProperty("_locale", locale)
|
||||||
file.write(configObject.toString().toByteArray(Charsets.UTF_8))
|
return configObject.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
root = RootConfig()
|
||||||
|
writeConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeConfig() {
|
||||||
|
file.write(exportToString().toByteArray(Charsets.UTF_8))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadFromString(string: String) {
|
||||||
|
val configObject = gson.fromJson(string, JsonObject::class.java)
|
||||||
|
locale = configObject.get("_locale")?.asString ?: LocaleWrapper.DEFAULT_LOCALE
|
||||||
|
root.fromJson(configObject)
|
||||||
|
writeConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadFromContext(context: Context) {
|
fun loadFromContext(context: Context) {
|
||||||
|
@ -6,12 +6,9 @@ import me.rhunk.snapenhance.data.NotificationType
|
|||||||
|
|
||||||
class MessagingTweaks : ConfigContainer() {
|
class MessagingTweaks : ConfigContainer() {
|
||||||
val anonymousStoryViewing = boolean("anonymous_story_viewing")
|
val anonymousStoryViewing = boolean("anonymous_story_viewing")
|
||||||
val preventReadReceipts = boolean("prevent_read_receipts")
|
|
||||||
val hideBitmojiPresence = boolean("hide_bitmoji_presence")
|
val hideBitmojiPresence = boolean("hide_bitmoji_presence")
|
||||||
val hideTypingNotifications = boolean("hide_typing_notifications")
|
val hideTypingNotifications = boolean("hide_typing_notifications")
|
||||||
val unlimitedSnapViewTime = boolean("unlimited_snap_view_time")
|
val unlimitedSnapViewTime = boolean("unlimited_snap_view_time")
|
||||||
val preventMessageSending = multiple("prevent_message_sending", *NotificationType.getOutgoingValues().map { it.key }.toTypedArray())
|
|
||||||
val messageLogger = boolean("message_logger") { addNotices(FeatureNotice.MAY_CAUSE_CRASHES) }
|
|
||||||
val autoSaveMessagesInConversations = multiple("auto_save_messages_in_conversations",
|
val autoSaveMessagesInConversations = multiple("auto_save_messages_in_conversations",
|
||||||
"CHAT",
|
"CHAT",
|
||||||
"SNAP",
|
"SNAP",
|
||||||
@ -19,7 +16,8 @@ class MessagingTweaks : ConfigContainer() {
|
|||||||
"EXTERNAL_MEDIA",
|
"EXTERNAL_MEDIA",
|
||||||
"STICKER"
|
"STICKER"
|
||||||
)
|
)
|
||||||
|
val preventMessageSending = multiple("prevent_message_sending", *NotificationType.getOutgoingValues().map { it.key }.toTypedArray())
|
||||||
|
val messageLogger = boolean("message_logger") { addNotices(FeatureNotice.MAY_CAUSE_CRASHES) }
|
||||||
val galleryMediaSendOverride = boolean("gallery_media_send_override")
|
val galleryMediaSendOverride = boolean("gallery_media_send_override")
|
||||||
val messagePreviewLength = integer("message_preview_length", defaultValue = 20)
|
val messagePreviewLength = integer("message_preview_length", defaultValue = 20)
|
||||||
}
|
}
|
@ -61,12 +61,3 @@ data class MessagingFriendInfo(
|
|||||||
val bitmojiId: String?,
|
val bitmojiId: String?,
|
||||||
val selfieId: String?
|
val selfieId: String?
|
||||||
) : SerializableDataObject()
|
) : SerializableDataObject()
|
||||||
|
|
||||||
|
|
||||||
data class MessagingRule(
|
|
||||||
val id: Int,
|
|
||||||
val type: MessagingRuleType,
|
|
||||||
val socialScope: SocialScope,
|
|
||||||
val targetUuid: String,
|
|
||||||
//val mode: Mode?,
|
|
||||||
) : SerializableDataObject()
|
|
@ -9,9 +9,7 @@ import me.rhunk.snapenhance.hook.Hooker
|
|||||||
|
|
||||||
class PreventReadReceipts : Feature("PreventReadReceipts", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) {
|
class PreventReadReceipts : Feature("PreventReadReceipts", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) {
|
||||||
override fun onActivityCreate() {
|
override fun onActivityCreate() {
|
||||||
val preventReadReceipts by context.config.messaging.preventReadReceipts
|
|
||||||
val isConversationInStealthMode: (SnapUUID) -> Boolean = hook@{
|
val isConversationInStealthMode: (SnapUUID) -> Boolean = hook@{
|
||||||
if (preventReadReceipts) return@hook true
|
|
||||||
context.feature(StealthMode::class).canUseRule(it.toString())
|
context.feature(StealthMode::class).canUseRule(it.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
package me.rhunk.snapenhance.features.impl.tweaks
|
|
||||||
|
|
||||||
import me.rhunk.snapenhance.bridge.types.BridgeFileType
|
|
||||||
import me.rhunk.snapenhance.features.BridgeFileFeature
|
|
||||||
import me.rhunk.snapenhance.features.FeatureLoadParams
|
|
||||||
|
|
||||||
class AntiAutoSave : BridgeFileFeature("AntiAutoSave", BridgeFileType.ANTI_AUTO_SAVE, loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) {
|
|
||||||
override fun onActivityCreate() {
|
|
||||||
readFile()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setConversationIgnored(userId: String, state: Boolean) {
|
|
||||||
setState(userId.hashCode().toLong().toString(16), state)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isConversationIgnored(userId: String): Boolean {
|
|
||||||
return exists(userId.hashCode().toLong().toString(16))
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,7 +21,6 @@ import me.rhunk.snapenhance.features.impl.spying.AnonymousStoryViewing
|
|||||||
import me.rhunk.snapenhance.features.impl.spying.MessageLogger
|
import me.rhunk.snapenhance.features.impl.spying.MessageLogger
|
||||||
import me.rhunk.snapenhance.features.impl.spying.PreventReadReceipts
|
import me.rhunk.snapenhance.features.impl.spying.PreventReadReceipts
|
||||||
import me.rhunk.snapenhance.features.impl.spying.StealthMode
|
import me.rhunk.snapenhance.features.impl.spying.StealthMode
|
||||||
import me.rhunk.snapenhance.features.impl.tweaks.AntiAutoSave
|
|
||||||
import me.rhunk.snapenhance.features.impl.tweaks.AutoSave
|
import me.rhunk.snapenhance.features.impl.tweaks.AutoSave
|
||||||
import me.rhunk.snapenhance.features.impl.tweaks.CameraTweaks
|
import me.rhunk.snapenhance.features.impl.tweaks.CameraTweaks
|
||||||
import me.rhunk.snapenhance.features.impl.tweaks.DisableVideoLengthRestriction
|
import me.rhunk.snapenhance.features.impl.tweaks.DisableVideoLengthRestriction
|
||||||
@ -76,7 +75,6 @@ class FeatureManager(private val context: ModContext) : Manager {
|
|||||||
register(UITweaks::class)
|
register(UITweaks::class)
|
||||||
register(ConfigurationOverride::class)
|
register(ConfigurationOverride::class)
|
||||||
register(GalleryMediaSendOverride::class)
|
register(GalleryMediaSendOverride::class)
|
||||||
register(AntiAutoSave::class)
|
|
||||||
register(UnlimitedSnapViewTime::class)
|
register(UnlimitedSnapViewTime::class)
|
||||||
register(DisableVideoLengthRestriction::class)
|
register(DisableVideoLengthRestriction::class)
|
||||||
register(MediaQualityLevelOverride::class)
|
register(MediaQualityLevelOverride::class)
|
||||||
|
Reference in New Issue
Block a user