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.Intent
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import coil.ImageLoader
|
||||
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.setup.Requirements
|
||||
import me.rhunk.snapenhance.ui.setup.SetupActivity
|
||||
import me.rhunk.snapenhance.ui.util.ActivityLauncherHelper
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class RemoteSideContext(
|
||||
@ -28,9 +31,14 @@ class RemoteSideContext(
|
||||
private var _activity: WeakReference<Activity>? = null
|
||||
lateinit var bridgeService: BridgeService
|
||||
|
||||
lateinit var activityLauncherHelper: ActivityLauncherHelper
|
||||
var activity: Activity?
|
||||
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 translation = LocaleWrapper()
|
||||
@ -84,6 +92,20 @@ class RemoteSideContext(
|
||||
} 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 {
|
||||
var requirements = overrideRequirements ?: 0
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
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.core.tween
|
||||
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.filled.Close
|
||||
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.Rule
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.rounded.Save
|
||||
import androidx.compose.material3.BottomSheetScaffoldState
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FilledIconButton
|
||||
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.PropertyValue
|
||||
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)
|
||||
class FeaturesSection : Section() {
|
||||
@ -90,8 +94,6 @@ class FeaturesSection : Section() {
|
||||
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"] }
|
||||
|
||||
@ -122,32 +124,13 @@ class FeaturesSection : Section() {
|
||||
properties
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
openFolderLauncher = ChooseFolderHelper.createChooseFolder(context.activity!! as ComponentActivity) {
|
||||
openFolderCallback(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun canGoBack() = sectionTopBarName() != featuresRouteName
|
||||
|
||||
override fun sectionTopBarName(): String {
|
||||
navController.currentBackStackEntry?.arguments?.getString("name")?.let { routeName ->
|
||||
val currentContainerPair = allContainers[routeName]
|
||||
val propertyTree = run {
|
||||
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 context.translation["${currentContainerPair?.key?.propertyTranslationPath()}.name"]
|
||||
}
|
||||
return featuresRouteName
|
||||
}
|
||||
@ -203,10 +186,9 @@ class FeaturesSection : Section() {
|
||||
|
||||
if (property.key.params.flags.contains(ConfigFlag.FOLDER)) {
|
||||
IconButton(onClick = registerClickCallback {
|
||||
openFolderCallback = { uri ->
|
||||
context.activityLauncherHelper.chooseFolder { uri ->
|
||||
propertyValue.setAny(uri)
|
||||
}
|
||||
openFolderLauncher()
|
||||
}.let { { it.invoke(true) } }) {
|
||||
Icon(Icons.Filled.FolderOpen, contentDescription = null)
|
||||
}
|
||||
@ -460,6 +442,63 @@ class FeaturesSection : Section() {
|
||||
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
|
||||
|
@ -1,6 +1,5 @@
|
||||
package me.rhunk.snapenhance.ui.setup.screens.impl
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.Button
|
||||
@ -10,12 +9,11 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import me.rhunk.snapenhance.Logger
|
||||
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.chooseFolder
|
||||
|
||||
class SaveFolderScreen : SetupScreen() {
|
||||
private lateinit var saveFolder: ObservableMutableState<String>
|
||||
private lateinit var openFolderLauncher: () -> Unit
|
||||
|
||||
override fun init() {
|
||||
saveFolder = ObservableMutableState(
|
||||
@ -29,9 +27,6 @@ class SaveFolderScreen : SetupScreen() {
|
||||
}
|
||||
}
|
||||
)
|
||||
openFolderLauncher = ChooseFolderHelper.createChooseFolder(context.activity as ComponentActivity) { uri ->
|
||||
saveFolder.value = uri
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ -39,7 +34,9 @@ class SaveFolderScreen : SetupScreen() {
|
||||
DialogText(text = context.translation["setup.dialogs.save_folder"])
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Button(onClick = {
|
||||
openFolderLauncher()
|
||||
context.activityLauncherHelper.chooseFolder {
|
||||
saveFolder.value = it
|
||||
}
|
||||
}) {
|
||||
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",
|
||||
"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": {
|
||||
"name": "Hide Bitmoji Presence",
|
||||
"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"),
|
||||
MAPPINGS(1, "mappings.json", "Mappings"),
|
||||
MESSAGE_LOGGER_DATABASE(2, "message_logger.db", "Message Logger",true),
|
||||
STEALTH(3, "stealth.txt", "Stealth Conversations"),
|
||||
ANTI_AUTO_DOWNLOAD(4, "anti_auto_download.txt", "Anti Auto Download"),
|
||||
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");
|
||||
AUTO_UPDATER_TIMESTAMP(3, "auto_updater_timestamp.txt", "Auto Updater Timestamp"),
|
||||
PINNED_CONVERSATIONS(4, "pinned_conversations.txt", "Pinned Conversations");
|
||||
|
||||
fun resolve(context: Context): File = if (isDatabase) {
|
||||
context.getDatabasePath(fileName)
|
||||
|
@ -19,10 +19,11 @@ class ModConfig {
|
||||
private val file = FileLoaderWrapper(BridgeFileType.CONFIG, "{}".toByteArray(Charsets.UTF_8))
|
||||
var wasPresent by Delegates.notNull<Boolean>()
|
||||
|
||||
val root = RootConfig()
|
||||
lateinit var root: RootConfig
|
||||
operator fun getValue(thisRef: Any?, property: Any?) = root
|
||||
|
||||
private fun load() {
|
||||
root = RootConfig()
|
||||
wasPresent = file.isFileExists()
|
||||
if (!file.isFileExists()) {
|
||||
writeConfig()
|
||||
@ -44,10 +45,26 @@ class ModConfig {
|
||||
root.fromJson(configObject)
|
||||
}
|
||||
|
||||
fun writeConfig() {
|
||||
fun exportToString(): String {
|
||||
val configObject = root.toJson()
|
||||
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) {
|
||||
|
@ -6,12 +6,9 @@ import me.rhunk.snapenhance.data.NotificationType
|
||||
|
||||
class MessagingTweaks : ConfigContainer() {
|
||||
val anonymousStoryViewing = boolean("anonymous_story_viewing")
|
||||
val preventReadReceipts = boolean("prevent_read_receipts")
|
||||
val hideBitmojiPresence = boolean("hide_bitmoji_presence")
|
||||
val hideTypingNotifications = boolean("hide_typing_notifications")
|
||||
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",
|
||||
"CHAT",
|
||||
"SNAP",
|
||||
@ -19,7 +16,8 @@ class MessagingTweaks : ConfigContainer() {
|
||||
"EXTERNAL_MEDIA",
|
||||
"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 messagePreviewLength = integer("message_preview_length", defaultValue = 20)
|
||||
}
|
@ -61,12 +61,3 @@ data class MessagingFriendInfo(
|
||||
val bitmojiId: String?,
|
||||
val selfieId: String?
|
||||
) : 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) {
|
||||
override fun onActivityCreate() {
|
||||
val preventReadReceipts by context.config.messaging.preventReadReceipts
|
||||
val isConversationInStealthMode: (SnapUUID) -> Boolean = hook@{
|
||||
if (preventReadReceipts) return@hook true
|
||||
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.PreventReadReceipts
|
||||
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.CameraTweaks
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.DisableVideoLengthRestriction
|
||||
@ -76,7 +75,6 @@ class FeatureManager(private val context: ModContext) : Manager {
|
||||
register(UITweaks::class)
|
||||
register(ConfigurationOverride::class)
|
||||
register(GalleryMediaSendOverride::class)
|
||||
register(AntiAutoSave::class)
|
||||
register(UnlimitedSnapViewTime::class)
|
||||
register(DisableVideoLengthRestriction::class)
|
||||
register(MediaQualityLevelOverride::class)
|
||||
|
Reference in New Issue
Block a user