commit 79d3bb5ba922e54f728391458932c528067cef58
parent e2249417eb58aec283ec2439ea92f702df1421d9
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Tue,  1 Aug 2023 18:37:35 +0200

config refactor + ui

Diffstat:
Mapp/src/main/AndroidManifest.xml | 10----------
Dapp/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt | 190-------------------------------------------------------------------------------
Mapp/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt | 3---
Mapp/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt | 10++++------
Mapp/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt | 11++++++++++-
Mapp/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt | 1-
Dapp/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt | 236-------------------------------------------------------------------------------
Aapp/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/CallbackAlias.kt | 5+++++
Aapp/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt | 294+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapp/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt | 6------
Mcore/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt | 4+---
Mcore/src/main/kotlin/me/rhunk/snapenhance/action/impl/CheckForUpdates.kt | 3+--
Mcore/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt | 3+--
Mcore/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt | 2+-
Dcore/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt | 74--------------------------------------------------------------------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt | 13-------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/config/ConfigAccessor.kt | 63---------------------------------------------------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt | 14--------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt | 418-------------------------------------------------------------------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/config/ConfigValue.kt | 23-----------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigIntegerValue.kt | 18------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateListValue.kt | 33---------------------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateSelection.kt | 23-----------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateValue.kt | 18------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStringValue.kt | 22----------------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt | 5++---
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt | 14+++++++++++---
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/config/DataProcessors.kt | 2+-
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt | 2+-
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/RootConfig.kt | 2--
Mcore/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt | 2--
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt | 5-----
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt | 3+--
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt | 2--
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt | 2--
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt | 2--
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt | 2+-
Dcore/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ConfigManager.kt | 59-----------------------------------------------------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt | 1-
Dcore/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt | 87-------------------------------------------------------------------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt | 280-------------------------------------------------------------------------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt | 11+----------
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt | 2+-
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt | 4++--
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt | 2--
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt | 2+-
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt | 3+--
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsMenu.kt | 23++++-------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt | 112-------------------------------------------------------------------------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt | 2+-
Mgradle/libs.versions.toml | 4++--
77 files changed, 549 insertions(+), 1810 deletions(-)

diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml @@ -50,16 +50,6 @@ android:name=".ui.map.MapActivity" android:exported="true" android:excludeFromRecents="true" /> - <activity - android:name=".ui.config.ConfigActivity" - android:theme="@style/AppTheme" - android:excludeFromRecents="true" - android:exported="true" /> - <activity - android:name=".ui.spoof.DeviceSpooferActivity" - android:theme="@style/AppTheme" - android:excludeFromRecents="true" - android:exported="true" /> <activity android:name=".bridge.ForceStartActivity" android:theme="@android:style/Theme.NoDisplay" android:excludeFromRecents="true" diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt @@ -1,190 +0,0 @@ -package me.rhunk.snapenhance.manager - -import androidx.compose.foundation.ScrollState -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Button -import androidx.compose.material3.Card -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.RadioButton -import androidx.compose.material3.Switch -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.text.TextRange -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.unit.dp -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.config.impl.ConfigIntegerValue -import me.rhunk.snapenhance.config.impl.ConfigStateListValue -import me.rhunk.snapenhance.config.impl.ConfigStateSelection -import me.rhunk.snapenhance.manager.data.ManagerContext - - -class Dialogs( - private val context: ManagerContext -) { - @Composable - fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) { - Card( - shape = MaterialTheme.shapes.medium, - modifier = Modifier - .padding(10.dp, 5.dp, 10.dp, 10.dp), - ) { - Column( - modifier = Modifier - .padding(10.dp, 10.dp, 10.dp, 10.dp) - .verticalScroll(ScrollState(0)), - ) { content() } - } - } - - @Composable - fun DefaultEntryText(text: String, modifier: Modifier = Modifier) { - Text( - text = text, - modifier = Modifier - .padding(10.dp, 10.dp, 10.dp, 10.dp) - .then(modifier) - ) - } - - @Composable - fun StateSelectionDialog(config: ConfigProperty) { - assert(config.valueContainer is ConfigStateSelection) - val keys = (config.valueContainer as ConfigStateSelection).keys() - val selectedValue = remember { - mutableStateOf(config.valueContainer.value()) - } - DefaultDialogCard { - keys.forEach { item -> - fun select() { - selectedValue.value = item - config.valueContainer.writeFrom(item) - } - - Row( - modifier = Modifier.clickable { select() }, - verticalAlignment = Alignment.CenterVertically - ) { - DefaultEntryText( - text = if (config.disableValueLocalization) - item - else context.translation.propertyOption(config, item), - modifier = Modifier.weight(1f) - ) - RadioButton( - selected = selectedValue.value == item, - onClick = { select() } - ) - } - } - } - } - - @Composable - fun KeyboardInputDialog(config: ConfigProperty, dismiss: () -> Unit = {}) { - val focusRequester = remember { FocusRequester() } - - DefaultDialogCard { - val fieldValue = remember { - mutableStateOf(config.valueContainer.read().let { - TextFieldValue( - text = it, - selection = TextRange(it.length) - ) - }) - } - - TextField( - modifier = Modifier - .fillMaxWidth() - .padding(all = 10.dp) - .onGloballyPositioned { - focusRequester.requestFocus() - } - .focusRequester(focusRequester), - value = fieldValue.value, - onValueChange = { - fieldValue.value = it - }, - keyboardOptions = when (config.valueContainer) { - is ConfigIntegerValue -> { - KeyboardOptions(keyboardType = KeyboardType.Number) - } - else -> { - KeyboardOptions(keyboardType = KeyboardType.Text) - } - }, - singleLine = true - ) - - Row( - modifier = Modifier.padding(top = 10.dp).fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceEvenly, - ) { - Button(onClick = { dismiss() }) { - Text(text = "Cancel") - } - Button(onClick = { - config.valueContainer.writeFrom(fieldValue.value.text) - dismiss() - }) { - Text(text = "Ok") - } - } - } - } - - @Composable - fun StateListDialog(config: ConfigProperty) { - assert(config.valueContainer is ConfigStateListValue) - val stateList = (config.valueContainer as ConfigStateListValue).value() - DefaultDialogCard { - stateList.keys.forEach { key -> - val state = remember { - mutableStateOf(stateList[key] ?: false) - } - - fun toggle(value: Boolean? = null) { - state.value = value ?: !state.value - stateList[key] = state.value - } - - Row( - modifier = Modifier.clickable { toggle() }, - verticalAlignment = Alignment.CenterVertically - ) { - DefaultEntryText( - text = if (config.disableValueLocalization) - key - else context.translation.propertyOption(config, key), - modifier = Modifier - .weight(1f) - ) - Switch( - checked = state.value, - onCheckedChange = { - toggle(it) - } - ) - } - } - } - } -} diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt @@ -1,15 +1,12 @@ package me.rhunk.snapenhance.manager import android.annotation.SuppressLint -import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.navigation.compose.rememberNavController -import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.manager.data.ManagerContext import me.rhunk.snapenhance.manager.util.SaveFolderChecker import me.rhunk.snapenhance.util.ActivityResultCallback diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt @@ -20,7 +20,6 @@ import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import me.rhunk.snapenhance.manager.data.ManagerContext @@ -36,16 +35,15 @@ class Navigation( ) { val sections = remember { EnumSection.values().toList().map { it to it.section.constructors.first().call() - }.onEach { (_, instance) -> + }.onEach { (section, instance) -> + instance.enumSection = section instance.manager = context instance.navController = navController } } NavHost(navController, startDestination = startDestination.route, Modifier.padding(innerPadding)) { - sections.forEach { (section, instance) -> - composable(section.route) { - instance.Content() - } + sections.forEach { (_, instance) -> + instance.build(this) } } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt @@ -9,10 +9,12 @@ import androidx.compose.material.icons.filled.Stars import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.vector.ImageVector import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable import me.rhunk.snapenhance.manager.data.ManagerContext -import me.rhunk.snapenhance.manager.sections.FeaturesSection import me.rhunk.snapenhance.manager.sections.HomeSection import me.rhunk.snapenhance.manager.sections.NotImplemented +import me.rhunk.snapenhance.manager.sections.features.FeaturesSection import kotlin.reflect.KClass enum class EnumSection( @@ -59,9 +61,16 @@ enum class EnumSection( open class Section { + lateinit var enumSection: EnumSection lateinit var manager: ManagerContext lateinit var navController: NavController @Composable open fun Content() { NotImplemented() } + + open fun build(navGraphBuilder: NavGraphBuilder) { + navGraphBuilder.composable(enumSection.route) { + Content() + } + } } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.manager.data import android.content.Context -import me.rhunk.snapenhance.bridge.wrapper.ConfigWrapper import me.rhunk.snapenhance.bridge.wrapper.MappingsWrapper import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper import me.rhunk.snapenhance.core.config.ModConfig diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt @@ -1,235 +0,0 @@ -package me.rhunk.snapenhance.manager.sections - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.layout.wrapContentWidth -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.MaterialTheme -import androidx.compose.material.SnackbarHost -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.OpenInNew -import androidx.compose.material.icons.rounded.Save -import androidx.compose.material.rememberScaffoldState -import androidx.compose.material3.Card -import androidx.compose.material3.FilledIconButton -import androidx.compose.material3.FloatingActionButton -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Switch -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.compose.ui.window.Dialog -import androidx.compose.ui.window.DialogProperties -import kotlinx.coroutines.launch -import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.config.impl.ConfigIntegerValue -import me.rhunk.snapenhance.config.impl.ConfigStateListValue -import me.rhunk.snapenhance.config.impl.ConfigStateSelection -import me.rhunk.snapenhance.config.impl.ConfigStateValue -import me.rhunk.snapenhance.config.impl.ConfigStringValue -import me.rhunk.snapenhance.manager.Dialogs -import me.rhunk.snapenhance.manager.Section - -typealias ClickCallback = (Boolean) -> Unit -typealias RegisterClickCallback = (ClickCallback) -> ClickCallback - -class FeaturesSection : Section() { - private val dialogs by lazy { Dialogs(manager) } - - @Composable - private fun PropertyAction(item: ConfigProperty, registerClickCallback: RegisterClickCallback) { - val showDialog = remember { mutableStateOf(false) } - val dialogComposable = remember { mutableStateOf<@Composable () -> Unit>({})} - - fun registerDialogOnClickCallback() = registerClickCallback { - showDialog.value = true - } - - if (showDialog.value) { - Dialog(onDismissRequest = { showDialog.value = false }, properties = DialogProperties()) { - dialogComposable.value() - } - } - - when (val container = remember { item.valueContainer }) { - is ConfigStateValue -> { - val state = remember { mutableStateOf(container.value()) } - Switch( - checked = state.value, - onCheckedChange = registerClickCallback { - state.value = state.value.not() - container.writeFrom(state.value.toString()) - } - ) - } - - is ConfigStateSelection -> { - registerDialogOnClickCallback() - dialogComposable.value = { - dialogs.StateSelectionDialog(item) - } - Text( - overflow = TextOverflow.Ellipsis, - maxLines = 1, - modifier = Modifier.widthIn(0.dp, 120.dp), - text = if (item.disableValueLocalization) container.value() else { - manager.translation.propertyOption(item, container.value()) - }, - ) - } - - is ConfigStateListValue, is ConfigStringValue, is ConfigIntegerValue -> { - dialogComposable.value = { - when (container) { - is ConfigStateListValue -> { - dialogs.StateListDialog(item) - } - is ConfigStringValue, is ConfigIntegerValue -> { - dialogs.KeyboardInputDialog(item) { showDialog.value = false } - } - } - } - - registerDialogOnClickCallback().let { { it.invoke(true) } }.also { - if (container is ConfigIntegerValue) { - FilledIconButton(onClick = it) { - Text(text = container.value().toString(), modifier = Modifier.wrapContentWidth(), overflow = TextOverflow.Ellipsis) - } - } else { - IconButton(onClick = it) { - Icon(Icons.Filled.OpenInNew, contentDescription = null) - } - } - } - } - } - } - - @Composable - private fun PropertyCard(item: ConfigProperty) { - val clickCallback = remember { mutableStateOf<ClickCallback?>(null) } - Card( - modifier = Modifier - .fillMaxWidth() - .clickable { - clickCallback.value?.invoke(true) - } - .padding(start = 10.dp, end = 10.dp, top = 5.dp, bottom = 5.dp) - ) { - Row( - modifier = Modifier - .fillMaxSize() - .padding(all = 4.dp), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Column( - modifier = Modifier - .align(Alignment.CenterVertically) - .weight(1f, fill = true) - .padding(all = 10.dp) - ) { - Text(text = manager.translation.propertyName(item), fontSize = 16.sp, fontWeight = FontWeight.Bold) - Text( - text = manager.translation.propertyDescription(item), - fontSize = 12.sp, - lineHeight = 15.sp - ) - } - - Column( - modifier = Modifier - .align(Alignment.CenterVertically) - .padding(all = 10.dp) - ) { - PropertyAction(item, registerClickCallback = { callback -> - clickCallback.value = callback - callback - }) - } - } - } - } - - @Composable - private fun PropertyContainer() { - val properties = remember { - val items by manager.config - items.properties.map { it.key to it.value } - } - - LazyColumn( - modifier = Modifier - .fillMaxHeight(), - verticalArrangement = Arrangement.Center - ) { - items(properties) { (key, value) -> - // Logger.debug("key: $key, value: $value") - } - } - } - - - @Composable - @Preview - override fun Content() { - val scope = rememberCoroutineScope() - val scaffoldState = rememberScaffoldState() - Scaffold( - snackbarHost = { SnackbarHost(scaffoldState.snackbarHostState) }, - floatingActionButton = { - FloatingActionButton( - onClick = { - //manager.config.writeConfig() - scope.launch { - scaffoldState.snackbarHostState.showSnackbar("Saved") - } - }, - containerColor = MaterialTheme.colors.primary, - contentColor = MaterialTheme.colors.onPrimary, - shape = RoundedCornerShape(16.dp), - ) { - Icon( - imageVector = Icons.Rounded.Save, - contentDescription = null - ) - } - }, - modifier = Modifier.fillMaxSize(), - content = { innerPadding -> - Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - ) { - Text( - text = "Features", - modifier = Modifier.padding(all = 10.dp), - fontSize = 20.sp - ) - PropertyContainer() - } - } - ) - } -}- \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/CallbackAlias.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/CallbackAlias.kt @@ -0,0 +1,4 @@ +package me.rhunk.snapenhance.manager.sections.features + +typealias ClickCallback = (Boolean) -> Unit +typealias RegisterClickCallback = (ClickCallback) -> ClickCallback+ \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt @@ -0,0 +1,202 @@ +package me.rhunk.snapenhance.manager.sections.features + +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.text.TextRange +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.dp +import me.rhunk.snapenhance.core.config.DataProcessors +import me.rhunk.snapenhance.core.config.PropertyPair + + +class Dialogs { + @Composable + fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) { + Card( + shape = MaterialTheme.shapes.medium, + modifier = Modifier + .padding(10.dp, 5.dp, 10.dp, 10.dp), + ) { + Column( + modifier = Modifier + .padding(10.dp, 10.dp, 10.dp, 10.dp) + .verticalScroll(ScrollState(0)), + ) { content() } + } + } + + @Composable + fun DefaultEntryText(text: String, modifier: Modifier = Modifier) { + Text( + text = text, + modifier = Modifier + .padding(10.dp, 10.dp, 10.dp, 10.dp) + .then(modifier) + ) + } + + @Composable + @Suppress("UNCHECKED_CAST") + fun UniqueSelectionDialog(property: PropertyPair<*>) { + val keys = (property.value.defaultValues as List<String>).toMutableList().apply { + add(0, "disabled") + } + + val selectedValue = remember { + mutableStateOf(property.value.getNullable()?.toString() ?: "disabled") + } + + DefaultDialogCard { + keys.forEachIndexed { index, item -> + fun select() { + selectedValue.value = item + property.value.setAny(if (index == 0) { + null + } else { + item + }) + } + + Row( + modifier = Modifier.clickable { select() }, + verticalAlignment = Alignment.CenterVertically + ) { + DefaultEntryText( + text = item, + modifier = Modifier.weight(1f) + ) + RadioButton( + selected = selectedValue.value == item, + onClick = { select() } + ) + } + } + } + } + + @Composable + fun KeyboardInputDialog(property: PropertyPair<*>, dismiss: () -> Unit = {}) { + val focusRequester = remember { FocusRequester() } + + DefaultDialogCard { + val fieldValue = remember { + mutableStateOf(property.value.get().toString().let { + TextFieldValue( + text = it, + selection = TextRange(it.length) + ) + }) + } + + TextField( + modifier = Modifier + .fillMaxWidth() + .padding(all = 10.dp) + .onGloballyPositioned { + focusRequester.requestFocus() + } + .focusRequester(focusRequester), + value = fieldValue.value, + onValueChange = { + fieldValue.value = it + }, + keyboardOptions = when (property.key.dataType.type) { + DataProcessors.Type.INTEGER -> { + KeyboardOptions(keyboardType = KeyboardType.Number) + } + else -> { + KeyboardOptions(keyboardType = KeyboardType.Text) + } + }, + singleLine = true + ) + + Row( + modifier = Modifier.padding(top = 10.dp).fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly, + ) { + Button(onClick = { dismiss() }) { + Text(text = "Cancel") + } + Button(onClick = { + if (property.key.dataType.type == DataProcessors.Type.INTEGER) { + runCatching { + property.value.setAny(fieldValue.value.text.toInt()) + }.onFailure { + property.value.setAny(0) + } + } else { + property.value.setAny(fieldValue.value.text) + } + dismiss() + }) { + Text(text = "Ok") + } + } + } + } + + @Composable + @Suppress("UNCHECKED_CAST") + fun MultipleSelectionDialog(property: PropertyPair<*>) { + val defaultItems = property.value.defaultValues as List<String> + val toggledStates = property.value.get() as MutableList<String> + DefaultDialogCard { + defaultItems.forEach { key -> + val state = remember { + mutableStateOf(toggledStates.contains(key)) + } + + fun toggle(value: Boolean? = null) { + state.value = value ?: !state.value + if (state.value) { + toggledStates.add(key) + } else { + toggledStates.remove(key) + } + } + + Row( + modifier = Modifier.clickable { toggle() }, + verticalAlignment = Alignment.CenterVertically + ) { + DefaultEntryText( + text = key, + modifier = Modifier + .weight(1f) + ) + Switch( + checked = state.value, + onCheckedChange = { + toggle(it) + } + ) + } + } + } + } +} diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt @@ -0,0 +1,293 @@ +package me.rhunk.snapenhance.manager.sections.features + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.OpenInNew +import androidx.compose.material.icons.rounded.Save +import androidx.compose.material3.Card +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.rememberBottomSheetScaffoldState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import androidx.navigation.navigation +import kotlinx.coroutines.launch +import me.rhunk.snapenhance.core.config.ConfigContainer +import me.rhunk.snapenhance.core.config.DataProcessors +import me.rhunk.snapenhance.core.config.PropertyPair +import me.rhunk.snapenhance.manager.Section + +class FeaturesSection : Section() { + private val dialogs by lazy { Dialogs() } + + companion object { + private const val MAIN_ROUTE = "root" + } + + @Composable + private fun PropertyAction(property: PropertyPair<*>, registerClickCallback: RegisterClickCallback) { + val showDialog = remember { mutableStateOf(false) } + val dialogComposable = remember { mutableStateOf<@Composable () -> Unit>({}) } + + fun registerDialogOnClickCallback() = registerClickCallback { + showDialog.value = true + } + + if (showDialog.value) { + Dialog( + onDismissRequest = { showDialog.value = false }, + properties = DialogProperties() + ) { + dialogComposable.value() + } + } + + val propertyValue = property.value + + when (val dataType = remember { property.key.dataType.type }) { + DataProcessors.Type.BOOLEAN -> { + val state = remember { mutableStateOf(propertyValue.get() as Boolean) } + Switch( + checked = state.value, + onCheckedChange = registerClickCallback { + state.value = state.value.not() + propertyValue.setAny(state.value) + } + ) + } + + DataProcessors.Type.STRING_UNIQUE_SELECTION -> { + registerDialogOnClickCallback() + + dialogComposable.value = { + dialogs.UniqueSelectionDialog(property) + } + + Text( + overflow = TextOverflow.Ellipsis, + maxLines = 1, + modifier = Modifier.widthIn(0.dp, 120.dp), + text = (propertyValue.getNullable() as? String) ?: "Disabled", + ) + } + + DataProcessors.Type.STRING_MULTIPLE_SELECTION, DataProcessors.Type.STRING, DataProcessors.Type.INTEGER -> { + dialogComposable.value = { + when (dataType) { + DataProcessors.Type.STRING_MULTIPLE_SELECTION -> { + dialogs.MultipleSelectionDialog(property) + } + DataProcessors.Type.STRING, DataProcessors.Type.INTEGER -> { + dialogs.KeyboardInputDialog(property) { showDialog.value = false } + } + else -> {} + } + } + + registerDialogOnClickCallback().let { { it.invoke(true) } }.also { + if (dataType == DataProcessors.Type.INTEGER) { + FilledIconButton(onClick = it) { + Text( + text = propertyValue.get().toString(), + modifier = Modifier.wrapContentWidth(), + overflow = TextOverflow.Ellipsis + ) + } + } else { + IconButton(onClick = it) { + Icon(Icons.Filled.OpenInNew, contentDescription = null) + } + } + } + } + else -> {} + } + + } + + @Composable + private fun PropertyCard(property: PropertyPair<*>) { + val clickCallback = remember { mutableStateOf<ClickCallback?>(null) } + Card( + modifier = Modifier + .fillMaxWidth() + .padding(start = 10.dp, end = 10.dp, top = 5.dp, bottom = 5.dp) + ) { + Row( + modifier = Modifier + .fillMaxSize() + .clickable { + clickCallback.value?.invoke(true) + } + .padding(all = 4.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Column( + modifier = Modifier + .align(Alignment.CenterVertically) + .weight(1f, fill = true) + .padding(all = 10.dp) + ) { + Text( + text = property.name, + fontSize = 16.sp, + fontWeight = FontWeight.Bold + ) + Text( + text = property.name, + fontSize = 12.sp, + lineHeight = 15.sp + ) + } + + when (property.key.dataType.type) { + DataProcessors.Type.CONTAINER -> { + clickCallback.value = { + navController.navigate("container/${property.name}") + } + } + else -> { + Column( + modifier = Modifier + .align(Alignment.CenterVertically) + .padding(all = 10.dp) + ) { + PropertyAction(property, registerClickCallback = { callback -> + clickCallback.value = callback + callback + }) + } + } + } + } + } + } + + + @OptIn(ExperimentalMaterial3Api::class) + @Composable + private fun Container( + containerName: String, + configContainer: ConfigContainer + ) { + val properties = remember { + configContainer.properties.map { PropertyPair(it.key, it.value) } + } + + val scope = rememberCoroutineScope() + val scaffoldState = rememberBottomSheetScaffoldState() + Scaffold( + snackbarHost = { SnackbarHost(scaffoldState.snackbarHostState) }, + modifier = Modifier.fillMaxSize(), + topBar = { + TopAppBar( + title = { + Text(text = containerName, textAlign = TextAlign.Center) + }, + navigationIcon = { + if (navController.currentBackStackEntry?.destination?.route != MAIN_ROUTE) { + IconButton(onClick = { navController.popBackStack() }) { + Icon(Icons.Filled.ArrowBack, contentDescription = null) + } + } + } + ) + }, + floatingActionButton = { + FloatingActionButton( + onClick = { + manager.config.writeConfig() + scope.launch { + scaffoldState.snackbarHostState.showSnackbar("Saved") + } + }, + modifier = Modifier.padding(25.dp), + containerColor = MaterialTheme.colors.primary, + contentColor = MaterialTheme.colors.onPrimary, + shape = RoundedCornerShape(16.dp), + ) { + Icon( + imageVector = Icons.Rounded.Save, + contentDescription = null + ) + } + }, + content = { innerPadding -> + LazyColumn( + modifier = Modifier.fillMaxHeight().padding(innerPadding), + verticalArrangement = Arrangement.Center + ) { + items(properties) { + PropertyCard(it) + } + } + } + ) + + } + + override fun build(navGraphBuilder: NavGraphBuilder) { + val allContainers by lazy { + val containers = mutableMapOf<String, ConfigContainer>() + fun queryContainerRecursive(container: ConfigContainer) { + container.properties.forEach { + if (it.key.dataType.type == DataProcessors.Type.CONTAINER) { + containers[it.key.name] = it.value.get() as ConfigContainer + queryContainerRecursive(it.value.get() as ConfigContainer) + } + } + } + queryContainerRecursive(manager.config.root) + containers + } + + navGraphBuilder.navigation(route = "features", startDestination = MAIN_ROUTE) { + composable(MAIN_ROUTE) { + Container(MAIN_ROUTE, manager.config.root) + } + + composable("container/{name}") { backStackEntry -> + backStackEntry.arguments?.getString("name")?.let { containerName -> + allContainers[containerName]?.let { + Container(containerName, it) + } + } + } + } + } +}+ \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt @@ -6,7 +6,6 @@ import android.content.Intent import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.result.contract.ActivityResultContracts -import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.core.config.PropertyValue import kotlin.system.exitProcess diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt @@ -14,7 +14,6 @@ import kotlinx.coroutines.asCoroutineDispatcher import me.rhunk.snapenhance.bridge.BridgeClient import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper import me.rhunk.snapenhance.core.config.ModConfig -import me.rhunk.snapenhance.core.config.impl.RootConfig import me.rhunk.snapenhance.data.MessageSender import me.rhunk.snapenhance.database.DatabaseAccess import me.rhunk.snapenhance.features.Feature diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt @@ -7,7 +7,6 @@ import android.content.Intent import android.os.Build import android.os.Environment import android.provider.Settings -import me.rhunk.snapenhance.bridge.wrapper.ConfigWrapper import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper import me.rhunk.snapenhance.download.DownloadTaskManager import kotlin.system.exitProcess @@ -18,7 +17,6 @@ import kotlin.system.exitProcess object SharedContext { lateinit var downloadTaskManager: DownloadTaskManager lateinit var translation: TranslationWrapper - lateinit var config: ConfigWrapper private fun askForStoragePermission(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { @@ -76,10 +74,6 @@ object SharedContext { loadFromContext(context) } } - if (!this::config.isInitialized) { - config = ConfigWrapper().apply { loadFromContext(context) } - } - //askForPermissions(context) } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt @@ -1,12 +1,10 @@ package me.rhunk.snapenhance.action import me.rhunk.snapenhance.ModContext -import me.rhunk.snapenhance.config.ConfigProperty import java.io.File abstract class AbstractAction( - val nameKey: String, - val dependsOnProperty: ConfigProperty? = null, + val nameKey: String ) { lateinit var context: ModContext diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/CheckForUpdates.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/CheckForUpdates.kt @@ -1,10 +1,9 @@ package me.rhunk.snapenhance.action.impl import me.rhunk.snapenhance.action.AbstractAction -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.impl.AutoUpdater -class CheckForUpdates : AbstractAction("action.check_for_updates", dependsOnProperty = ConfigProperty.AUTO_UPDATER) { +class CheckForUpdates : AbstractAction("action.check_for_updates") { override fun run() { context.executeAsync { runCatching { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt @@ -3,11 +3,10 @@ package me.rhunk.snapenhance.action.impl import android.content.Intent import android.os.Bundle import me.rhunk.snapenhance.action.AbstractAction -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.ui.map.MapActivity -class OpenMap: AbstractAction("action.open_map", dependsOnProperty = ConfigProperty.LOCATION_SPOOF) { +class OpenMap: AbstractAction("action.open_map") { override fun run() { context.runOnUiThread { val mapActivityIntent = Intent() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt @@ -10,10 +10,10 @@ import android.os.Handler import android.os.HandlerThread import android.os.IBinder import de.robv.android.xposed.XposedHelpers -import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.Logger.xposedLog import me.rhunk.snapenhance.ModContext import me.rhunk.snapenhance.bridge.types.BridgeFileType +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.data.LocalePair import java.util.concurrent.CompletableFuture import java.util.concurrent.Executors diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt @@ -1,73 +0,0 @@ -package me.rhunk.snapenhance.bridge.wrapper - -import android.content.Context -import com.google.gson.GsonBuilder -import com.google.gson.JsonObject -import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.bridge.BridgeClient -import me.rhunk.snapenhance.bridge.FileLoaderWrapper -import me.rhunk.snapenhance.bridge.types.BridgeFileType -import me.rhunk.snapenhance.config.ConfigAccessor -import me.rhunk.snapenhance.config.ConfigProperty - -class ConfigWrapper: ConfigAccessor() { - companion object { - private val gson = GsonBuilder().setPrettyPrinting().create() - } - - private val file = FileLoaderWrapper(BridgeFileType.CONFIG, "{}".toByteArray(Charsets.UTF_8)) - - fun load() { - ConfigProperty.sortedByCategory().forEach { key -> - set(key, key.valueContainer) - } - - if (!file.isFileExists()) { - writeConfig() - return - } - - runCatching { - loadConfig() - }.onFailure { - Logger.error("Failed to load config", it) - writeConfig() - } - } - - fun save() { - writeConfig() - } - - private fun loadConfig() { - val configContent = file.read() - - val configObject: JsonObject = gson.fromJson( - configContent.toString(Charsets.UTF_8), - JsonObject::class.java - ) - - entries().forEach { (key, value) -> - value.writeFrom(configObject.get(key.name)?.asString ?: value.read()) - } - } - - fun writeConfig() { - val configObject = JsonObject() - entries().forEach { (key, value) -> - configObject.addProperty(key.name, value.read()) - } - - file.write(gson.toJson(configObject).toByteArray(Charsets.UTF_8)) - } - - fun loadFromContext(context: Context) { - file.loadFromContext(context) - load() - } - - fun loadFromBridge(bridgeClient: BridgeClient) { - file.loadFromBridge(bridgeClient) - load() - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt @@ -5,7 +5,6 @@ import com.google.gson.JsonObject import com.google.gson.JsonParser import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.bridge.BridgeClient -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.LocalePair import java.util.Locale @@ -80,18 +79,6 @@ class TranslationWrapper { return translationMap[key] ?: key.also { Logger.debug("Missing translation for $key") } } - fun propertyName(property: ConfigProperty): String { - return get("property.${property.translationKey}.name") - } - - fun propertyDescription(property: ConfigProperty): String { - return get("property.${property.translationKey}.description") - } - - fun propertyOption(property: ConfigProperty, item: String): String { - return get(property.getOptionTranslationKey(item)) - } - fun format(key: String, vararg args: Pair<String, String>): String { return args.fold(get(key)) { acc, pair -> acc.replace("{${pair.first}}", pair.second) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigAccessor.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigAccessor.kt @@ -1,62 +0,0 @@ -package me.rhunk.snapenhance.config - -open class ConfigAccessor( - private val configMap: MutableMap<ConfigProperty, ConfigValue<*>> = mutableMapOf() -) { - fun bool(key: ConfigProperty): Boolean { - return get(key).value() as Boolean - } - - fun int(key: ConfigProperty): Int { - return get(key).value() as Int - } - - fun string(key: ConfigProperty): String { - return get(key).value() as String - } - - fun double(key: ConfigProperty): Double { - return get(key).value() as Double - } - - fun float(key: ConfigProperty): Float { - return get(key).value() as Float - } - - fun long(key: ConfigProperty): Long { - return get(key).value() as Long - } - - fun short(key: ConfigProperty): Short { - return get(key).value() as Short - } - - fun byte(key: ConfigProperty): Byte { - return get(key).value() as Byte - } - - fun char(key: ConfigProperty): Char { - return get(key).value() as Char - } - - @Suppress("UNCHECKED_CAST") - fun options(key: ConfigProperty): Map<String, Boolean> { - return get(key).value() as Map<String, Boolean> - } - - fun state(key: ConfigProperty): String { - return get(key).value() as String - } - - fun get(key: ConfigProperty): ConfigValue<*> { - return configMap[key]!! - } - - fun set(key: ConfigProperty, value: ConfigValue<*>) { - configMap[key] = value - } - - fun entries(): Set<Map.Entry<ConfigProperty, ConfigValue<*>>> { - return configMap.entries - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt @@ -1,14 +0,0 @@ -package me.rhunk.snapenhance.config - -enum class ConfigCategory( - val key: String, - val hidden: Boolean = false -) { - SPYING_PRIVACY("spying_privacy"), - MEDIA_MANAGEMENT("media_manager"), - UI_TWEAKS("ui_tweaks"), - UPDATES("updates"), - CAMERA("camera"), - EXPERIMENTAL_DEBUGGING("experimental_debugging"), - DEVICE_SPOOFER("device_spoofer", hidden = true) -} diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt @@ -1,417 +0,0 @@ -package me.rhunk.snapenhance.config - -import me.rhunk.snapenhance.config.impl.ConfigIntegerValue -import me.rhunk.snapenhance.config.impl.ConfigStateListValue -import me.rhunk.snapenhance.config.impl.ConfigStateSelection -import me.rhunk.snapenhance.config.impl.ConfigStateValue -import me.rhunk.snapenhance.config.impl.ConfigStringValue -import me.rhunk.snapenhance.data.NotificationType -import me.rhunk.snapenhance.features.impl.tweaks.CameraTweaks - -enum class ConfigProperty( - val translationKey: String, - val category: ConfigCategory, - val valueContainer: ConfigValue<*>, - val valueContainerTranslationKey: String? = null, - val shouldAppearInSettings: Boolean = true, - val disableValueLocalization: Boolean = false -) { - - //SPYING AND PRIVACY - MESSAGE_LOGGER("message_logger", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - PREVENT_READ_RECEIPTS( - "prevent_read_receipts", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - HIDE_BITMOJI_PRESENCE( - "hide_bitmoji_presence", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - BETTER_NOTIFICATIONS( - "better_notifications", - ConfigCategory.SPYING_PRIVACY, - ConfigStateListValue( - listOf("snap", "chat", "reply_button", "download_button"), - mutableMapOf( - "snap" to false, - "chat" to false, - "reply_button" to false, - "download_button" to false - ) - ) - ), - NOTIFICATION_BLACKLIST( - "notification_blacklist", - ConfigCategory.SPYING_PRIVACY, - ConfigStateListValue( - NotificationType.getIncomingValues().map { it.key }, - NotificationType.getIncomingValues().associate { it.key to false }.toMutableMap() - ), - valueContainerTranslationKey = "notifications", - ), - DISABLE_METRICS("disable_metrics", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - BLOCK_ADS("block_ads", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - UNLIMITED_SNAP_VIEW_TIME("unlimited_snap_view_time", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - PREVENT_SENDING_MESSAGES( - "prevent_sending_messages", - ConfigCategory.SPYING_PRIVACY, - ConfigStateListValue( - NotificationType.getOutgoingValues().map { it.key }, - NotificationType.getOutgoingValues().associate { it.key to false }.toMutableMap() - ), - valueContainerTranslationKey = "notifications", - ), - ANONYMOUS_STORY_VIEW( - "anonymous_story_view", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - HIDE_TYPING_NOTIFICATION( - "hide_typing_notification", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - - //MEDIA MANAGEMENT - SAVE_FOLDER( - "save_folder", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStringValue("", isFolderPath =true), - ), - AUTO_DOWNLOAD_OPTIONS( - "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( - "download_options", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateListValue( - listOf( - "allow_duplicate", - "create_user_folder", - "append_hash", - "append_date_time", - "append_type", - "append_username", - "merge_overlay" - ), - mutableMapOf( - "allow_duplicate" to false, - "create_user_folder" to true, - "append_hash" to true, - "append_date_time" to true, - "append_type" to false, - "append_username" to false, - "merge_overlay" to false, - ) - ) - ), - CHAT_DOWNLOAD_CONTEXT_MENU( - "chat_download_context_menu", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateValue(false) - ), - GALLERY_MEDIA_SEND_OVERRIDE( - "gallery_media_send_override", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateSelection( - listOf("OFF", "NOTE", "SNAP", "LIVE_SNAP"), - "OFF" - ) - ), - AUTO_SAVE_MESSAGES("auto_save_messages", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateListValue( - listOf("CHAT", "SNAP", "NOTE", "EXTERNAL_MEDIA", "STICKER") - ) - ), - - FORCE_MEDIA_SOURCE_QUALITY( - "force_media_source_quality", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateValue(false) - ), - DOWNLOAD_LOGGING( - "download_logging", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateListValue( - listOf("started", "success", "progress", "failure"), - mutableMapOf( - "started" to false, - "success" to true, - "progress" to false, - "failure" to true - ) - ) - ), - - //UI AND TWEAKS - ENABLE_FRIEND_FEED_MENU_BAR( - "enable_friend_feed_menu_bar", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - FRIEND_FEED_MENU_BUTTONS( - "friend_feed_menu_buttons", - ConfigCategory.UI_TWEAKS, - ConfigStateListValue( - listOf("auto_download_blacklist", "anti_auto_save", "stealth_mode", "conversation_info"), - mutableMapOf( - "auto_download_blacklist" to false, - "anti_auto_save" to false, - "stealth_mode" to true, - "conversation_info" to true - ) - ) - ), - FRIEND_FEED_MENU_POSITION("friend_feed_menu_buttons_position", - ConfigCategory.UI_TWEAKS, - ConfigIntegerValue(1) - ), - HIDE_UI_ELEMENTS( - "hide_ui_elements", - ConfigCategory.UI_TWEAKS, - ConfigStateListValue( - listOf("remove_voice_record_button", "remove_stickers_button", "remove_cognac_button", "remove_live_location_share_button", "remove_call_buttons", "remove_camera_borders"), - mutableMapOf( - "remove_voice_record_button" to false, - "remove_stickers_button" to false, - "remove_cognac_button" to false, - "remove_live_location_share_button" to false, - "remove_call_buttons" to false, - "remove_camera_borders" to false - ) - ) - ), - HIDE_STORY_SECTION( - "hide_story_section", - ConfigCategory.UI_TWEAKS, - ConfigStateListValue( - listOf("hide_friend_suggestions", "hide_friends", "hide_following", "hide_for_you"), - mutableMapOf( - "hide_friend_suggestions" to false, - "hide_friends" to false, - "hide_following" to false, - "hide_for_you" to false - ) - ) - ), - STORY_VIEWER_OVERRIDE("story_viewer_override", - ConfigCategory.UI_TWEAKS, - ConfigStateSelection( - listOf("OFF", "DISCOVER_PLAYBACK_SEEKBAR", "VERTICAL_STORY_VIEWER"), - "OFF" - ) - ), - STREAK_EXPIRATION_INFO( - "streak_expiration_info", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - DISABLE_SNAP_SPLITTING( - "disable_snap_splitting", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - DISABLE_VIDEO_LENGTH_RESTRICTION( - "disable_video_length_restriction", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - SNAPCHAT_PLUS("snapchat_plus", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - NEW_MAP_UI("new_map_ui", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - LOCATION_SPOOF( - "location_spoof", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - LATITUDE( - "latitude_value", - ConfigCategory.UI_TWEAKS, - ConfigStringValue("0.0000"), - shouldAppearInSettings = false - ), - LONGITUDE( - "longitude_value", - ConfigCategory.UI_TWEAKS, - ConfigStringValue("0.0000"), - shouldAppearInSettings = false - ), - MESSAGE_PREVIEW_LENGTH( - "message_preview_length", - ConfigCategory.UI_TWEAKS, - ConfigIntegerValue(20) - ), - UNLIMITED_CONVERSATION_PINNING( - "unlimited_conversation_pinning", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - DISABLE_SPOTLIGHT( - "disable_spotlight", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - ENABLE_APP_APPEARANCE( - "enable_app_appearance", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - STARTUP_PAGE_OVERRIDE( - "startup_page_override", - ConfigCategory.UI_TWEAKS, - ConfigStateSelection( - listOf( - "OFF", - "ngs_map_icon_container", - "ngs_chat_icon_container", - "ngs_camera_icon_container", - "ngs_community_icon_container", - "ngs_spotlight_icon_container", - "ngs_search_icon_container" - ), - "OFF" - ) - ), - DISABLE_GOOGLE_PLAY_DIALOGS( - "disable_google_play_dialogs", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - - //CAMERA - CAMERA_DISABLE( - "disable_camera", - ConfigCategory.CAMERA, - ConfigStateValue(false) - ), - IMMERSIVE_CAMERA_PREVIEW( - "immersive_camera_preview", - ConfigCategory.CAMERA, - ConfigStateValue(false) - ), - OVERRIDE_PREVIEW_RESOLUTION( - "preview_resolution", - ConfigCategory.CAMERA, - ConfigStateSelection( - CameraTweaks.resolutions, - "OFF" - ), - disableValueLocalization = true - ), - OVERRIDE_PICTURE_RESOLUTION( - "picture_resolution", - ConfigCategory.CAMERA, - ConfigStateSelection( - CameraTweaks.resolutions, - "OFF" - ), - disableValueLocalization = true - ), - FORCE_HIGHEST_FRAME_RATE( - "force_highest_frame_rate", - ConfigCategory.CAMERA, - ConfigStateValue(false) - ), - FORCE_CAMERA_SOURCE_ENCODING( - "force_camera_source_encoding", - ConfigCategory.CAMERA, - ConfigStateValue(false) - ), - - // UPDATES - AUTO_UPDATER( - "auto_updater", - ConfigCategory.UPDATES, - ConfigStateSelection( - listOf("DISABLED", "EVERY_LAUNCH", "DAILY", "WEEKLY"), - "DAILY" - ) - ), - - // EXPERIMENTAL DEBUGGING - APP_PASSCODE( - "app_passcode", - ConfigCategory.EXPERIMENTAL_DEBUGGING, - ConfigStringValue("", isHidden = true) - ), - APP_LOCK_ON_RESUME( - "app_lock_on_resume", - ConfigCategory.EXPERIMENTAL_DEBUGGING, - ConfigStateValue(false) - ), - INFINITE_STORY_BOOST( - "infinite_story_boost", - ConfigCategory.EXPERIMENTAL_DEBUGGING, - ConfigStateValue(false) - ), - MEO_PASSCODE_BYPASS( - "meo_passcode_bypass", - ConfigCategory.EXPERIMENTAL_DEBUGGING, - ConfigStateValue(false) - ), - AMOLED_DARK_MODE( - "amoled_dark_mode", - ConfigCategory.EXPERIMENTAL_DEBUGGING, - ConfigStateValue(false) - ), - UNLIMITED_MULTI_SNAP( - "unlimited_multi_snap", - ConfigCategory.EXPERIMENTAL_DEBUGGING, - ConfigStateValue(false) - ), - - //DEVICE SPOOFER - DEVICE_SPOOF( - "device_spoof", - ConfigCategory.DEVICE_SPOOFER, - ConfigStateValue(false) - ), - FINGERPRINT( - "device_fingerprint", - ConfigCategory.DEVICE_SPOOFER, - ConfigStringValue("") - ), - ANDROID_ID( - "android_id", - ConfigCategory.DEVICE_SPOOFER, - ConfigStringValue("") - ); - - fun getOptionTranslationKey(key: String) = "option.property.${valueContainerTranslationKey ?: translationKey}.$key" - - companion object { - fun sortedByCategory(): List<ConfigProperty> { - return values().sortedBy { it.category.ordinal } - } - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigValue.kt @@ -1,22 +0,0 @@ -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 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() - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigIntegerValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigIntegerValue.kt @@ -1,17 +0,0 @@ -package me.rhunk.snapenhance.config.impl - -import me.rhunk.snapenhance.config.ConfigValue - -class ConfigIntegerValue( - private var value: Int -) : ConfigValue<Int>() { - override fun value() = value - - override fun read(): String { - return value.toString() - } - - override fun write(value: String) { - this.value = value.toInt() - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateListValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateListValue.kt @@ -1,32 +0,0 @@ -package me.rhunk.snapenhance.config.impl - -import me.rhunk.snapenhance.config.ConfigValue - -class ConfigStateListValue( - private val keys: List<String>, - private var states: MutableMap<String, Boolean> = mutableMapOf() -) : ConfigValue<Map<String, Boolean>>() { - override fun value() = states - - fun setKey(key: String, state: Boolean) { - states[key] = state - onValueChanged() - } - - operator fun get(key: String) = states[key] ?: false - - override fun read(): String { - return keys.joinToString("|") { "$it:${states[it]}" } - } - - override fun write(value: String) { - value.split("|").forEach { - val (key, state) = it.split(":") - states[key] = state.toBoolean() - } - } - - override fun toString(): String { - return states.filter { it.value }.keys.joinToString(", ") { it } - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateSelection.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateSelection.kt @@ -1,22 +0,0 @@ -package me.rhunk.snapenhance.config.impl - -import me.rhunk.snapenhance.config.ConfigValue - -class ConfigStateSelection( - private val keys: List<String>, - private var state: String = "" -) : ConfigValue<String>() { - fun keys(): List<String> { - return keys - } - - override fun value() = state - - override fun read(): String { - return state - } - - override fun write(value: String) { - state = value - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateValue.kt @@ -1,17 +0,0 @@ -package me.rhunk.snapenhance.config.impl - -import me.rhunk.snapenhance.config.ConfigValue - -class ConfigStateValue( - private var value: Boolean -) : ConfigValue<Boolean>() { - override fun value() = value - - override fun read(): String { - return value.toString() - } - - override fun write(value: String) { - this.value = value.toBoolean() - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStringValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStringValue.kt @@ -1,21 +0,0 @@ -package me.rhunk.snapenhance.config.impl - -import me.rhunk.snapenhance.config.ConfigValue - -class ConfigStringValue( - private var value: String = "", - val isFolderPath: Boolean = false, - val isHidden: Boolean = false -) : ConfigValue<String>() { - override fun value() = value - - fun hiddenValue() = if (isHidden) value.map { '*' }.joinToString("") else value - - override fun read(): String { - return value - } - - override fun write(value: String) { - this.value = value - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.core.config import com.google.gson.JsonObject -import me.rhunk.snapenhance.Logger import kotlin.reflect.KProperty typealias ConfigParamsBuilder = ConfigParams.() -> Unit @@ -57,7 +56,7 @@ open class ConfigContainer( fun toJson(): JsonObject { val json = JsonObject() properties.forEach { (propertyKey, propertyValue) -> - val serializedValue = propertyValue.getNullable()?.let { propertyKey.dataProcessor.serializeAny(it) } + val serializedValue = propertyValue.getNullable()?.let { propertyKey.dataType.serializeAny(it) } json.add(propertyKey.name, serializedValue) } return json @@ -66,7 +65,7 @@ open class ConfigContainer( fun fromJson(json: JsonObject) { properties.forEach { (key, _) -> val jsonElement = json.get(key.name) ?: return@forEach - key.dataProcessor.deserializeAny(jsonElement)?.let { + key.dataType.deserializeAny(jsonElement)?.let { properties[key]?.setAny(it) } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt @@ -1,12 +1,20 @@ package me.rhunk.snapenhance.core.config -import me.rhunk.snapenhance.Logger import kotlin.reflect.KProperty + +data class PropertyPair<T>( + val key: PropertyKey<T>, + val value: PropertyValue<*> +) { + val name get() = key.name +} + class ConfigParams( var shouldTranslate: Boolean = false, var hidden: Boolean = false, - var isFolder: Boolean = false + var isFolder: Boolean = false, + val disabledKey: String? = null ) class PropertyValue<T>( @@ -34,7 +42,7 @@ class PropertyValue<T>( class PropertyKey<T>( val name: String, - val dataProcessor: DataProcessors.PropertyDataProcessor<T>, + val dataType: DataProcessors.PropertyDataProcessor<T>, val params: ConfigParams = ConfigParams(), ) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/DataProcessors.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/DataProcessors.kt @@ -65,7 +65,7 @@ object DataProcessors { type = Type.STRING_MULTIPLE_SELECTION, serialize = { JsonArray().apply { it.forEach { add(it) } } }, deserialize = { obj -> - obj.asJsonArray.map { it.asString } + obj.asJsonArray.map { it.asString }.toMutableList() }, ) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt @@ -5,7 +5,7 @@ import me.rhunk.snapenhance.data.NotificationType class Global : ConfigContainer() { val snapchatPlus = boolean("snapchat_plus") - val autoUpdater = unique("auto_updater", "DAILY","EVERY_LAUNCH", "DAILY", "WEEKLY") + val autoUpdater = unique("auto_updater", "EVERY_LAUNCH", "DAILY", "WEEKLY") val disableMetrics = boolean("disable_metrics") val disableVideoLengthRestrictions = boolean("disable_video_length_restrictions") val disableGooglePlayDialogs = boolean("disable_google_play_dialogs") diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/RootConfig.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/RootConfig.kt @@ -1,8 +1,6 @@ package me.rhunk.snapenhance.core.config.impl import me.rhunk.snapenhance.core.config.ConfigContainer -import me.rhunk.snapenhance.data.NotificationType -import me.rhunk.snapenhance.features.impl.tweaks.CameraTweaks class RootConfig : ConfigContainer() { val downloader = container("downloader", DownloaderConfig()) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt @@ -18,8 +18,6 @@ import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.DownloadCallback -import me.rhunk.snapenhance.bridge.wrapper.ConfigWrapper -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.core.config.ModConfig import me.rhunk.snapenhance.data.FileType import me.rhunk.snapenhance.download.data.DownloadMetadata diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl import android.annotation.SuppressLint -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage @@ -95,10 +94,6 @@ class ConfigEnumKeys : Feature("Config enum keys", loadParams = FeatureLoadParam } } - ConfigProperty.ENABLE_APP_APPEARANCE.valueContainer.addPropertyChangeListener { - context.softRestartApp(true) - } - val sharedPreferencesImpl = context.androidContext.classLoader.loadClass("android.app.SharedPreferencesImpl") sharedPreferencesImpl.methods.first { it.name == "getBoolean" }.hook(HookStage.BEFORE) { param -> diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt @@ -1,14 +1,13 @@ package me.rhunk.snapenhance.features.impl -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams +import me.rhunk.snapenhance.features.impl.spying.StealthMode import me.rhunk.snapenhance.hook.HookStage import me.rhunk.snapenhance.hook.Hooker import me.rhunk.snapenhance.hook.hook import me.rhunk.snapenhance.util.getObjectField -import me.rhunk.snapenhance.features.impl.spying.StealthMode; class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC or FeatureLoadParams.INIT_ASYNC or FeatureLoadParams.INIT_SYNC) { lateinit var conversationManager: Any diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt @@ -11,7 +11,6 @@ import me.rhunk.snapenhance.Constants.ARROYO_URL_KEY_PROTO_PATH import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.Logger.xposedLog import me.rhunk.snapenhance.bridge.DownloadCallback -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.FileType import me.rhunk.snapenhance.data.wrapper.impl.media.MediaInfo diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.content.res.TypedArray import android.graphics.drawable.ColorDrawable import me.rhunk.snapenhance.Constants -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl.experiments import android.annotation.SuppressLint -import android.app.AlertDialog import android.content.Context import android.os.Build import android.text.Editable @@ -9,7 +8,6 @@ import android.text.InputType import android.text.TextWatcher import android.view.inputmethod.InputMethodManager import android.widget.EditText -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.ui.ViewAppearanceHelper diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl.experiments import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.experiments -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.experiments -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.experiments -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt @@ -1,10 +1,8 @@ package me.rhunk.snapenhance.features.impl.privacy import de.robv.android.xposed.XposedHelpers -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams -import me.rhunk.snapenhance.hook.HookAdapter import me.rhunk.snapenhance.hook.HookStage import me.rhunk.snapenhance.hook.Hooker diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl.privacy import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.NotificationType import me.rhunk.snapenhance.data.wrapper.impl.MessageContent import me.rhunk.snapenhance.features.Feature diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.spying -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt @@ -4,7 +4,6 @@ import android.os.DeadObjectException import com.google.gson.JsonObject import com.google.gson.JsonParser 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 diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.spying -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl.tweaks import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.MessageState import me.rhunk.snapenhance.data.wrapper.impl.Message import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt @@ -5,7 +5,6 @@ import android.annotation.SuppressLint import android.content.ContextWrapper import android.content.pm.PackageManager import android.hardware.camera2.CameraManager -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.wrapper.impl.ScSize import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.tweaks -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt @@ -1,7 +1,5 @@ package me.rhunk.snapenhance.features.impl.tweaks -import android.app.AlertDialog -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.MessageSender import me.rhunk.snapenhance.data.wrapper.impl.MessageContent diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt @@ -2,7 +2,6 @@ package me.rhunk.snapenhance.features.impl.tweaks import android.app.AlertDialog import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl.tweaks import android.content.Intent -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.tweaks -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt @@ -14,7 +14,6 @@ import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedHelpers import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.MediaReferenceType import me.rhunk.snapenhance.data.wrapper.impl.Message diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.tweaks -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.tweaks -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 diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt @@ -5,7 +5,6 @@ import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import me.rhunk.snapenhance.Constants -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt @@ -8,7 +8,6 @@ import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import me.rhunk.snapenhance.Constants -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookAdapter diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.manager.impl -import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.ModContext import me.rhunk.snapenhance.action.AbstractAction import me.rhunk.snapenhance.action.impl.CheckForUpdates @@ -9,6 +8,7 @@ import me.rhunk.snapenhance.action.impl.ClearMessageLogger import me.rhunk.snapenhance.action.impl.ExportChatMessages import me.rhunk.snapenhance.action.impl.OpenMap import me.rhunk.snapenhance.action.impl.RefreshMappings +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.manager.Manager import kotlin.reflect.KClass diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ConfigManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ConfigManager.kt @@ -1,58 +0,0 @@ -package me.rhunk.snapenhance.manager.impl - -import com.google.gson.JsonObject -import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.ModContext -import me.rhunk.snapenhance.bridge.types.BridgeFileType -import me.rhunk.snapenhance.config.ConfigAccessor -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.manager.Manager -import java.nio.charset.StandardCharsets - -class ConfigManager( - private val context: ModContext -) : ConfigAccessor(), Manager { - - override fun init() { - ConfigProperty.sortedByCategory().forEach { key -> - set(key, key.valueContainer) - } - - if (!context.bridgeClient.isFileExists(BridgeFileType.CONFIG)) { - writeConfig() - return - } - - runCatching { - loadConfig() - }.onFailure { - Logger.xposedLog("Failed to load config", it) - writeConfig() - } - } - - private fun loadConfig() { - val configContent = context.bridgeClient.createAndReadFile( - BridgeFileType.CONFIG, - "{}".toByteArray(Charsets.UTF_8) - ) - val configObject: JsonObject = context.gson.fromJson( - String(configContent, StandardCharsets.UTF_8), - JsonObject::class.java - ) - entries().forEach { (key, value) -> - value.writeFrom(configObject.get(key.name)?.asString ?: value.read()) - } - } - - fun writeConfig() { - val configObject = JsonObject() - entries().forEach { (key, value) -> - configObject.addProperty(key.name, value.read()) - } - context.bridgeClient.writeFile( - BridgeFileType.CONFIG, - context.gson.toJson(configObject).toByteArray(Charsets.UTF_8) - ) - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.manager.impl -import android.app.AlertDialog import com.google.gson.JsonElement import com.google.gson.JsonParser import me.rhunk.snapenhance.Constants diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt @@ -1,87 +0,0 @@ -package me.rhunk.snapenhance.ui - -import android.app.Activity -import android.app.AlertDialog -import android.content.Context -import android.content.Intent -import android.widget.EditText -import android.widget.TextView -import android.widget.Toast -import me.rhunk.snapenhance.core.R -import me.rhunk.snapenhance.SharedContext -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.util.ActivityResultCallback -import kotlin.math.abs -import kotlin.random.Random - -class ItemHelper( - private val context : Context -) { - val positiveButtonText by lazy { - SharedContext.translation["button.ok"] - } - - val cancelButtonText by lazy { - SharedContext.translation["button.cancel"] - } - - fun longToast(message: String, context: Context) { - Toast.makeText(context, message, Toast.LENGTH_LONG).show() - } - - fun createTranslatedTextView(property: ConfigProperty, shouldTranslatePropertyValue: Boolean = true): TextView { - return object: TextView(context) { - override fun setText(text: CharSequence?, type: BufferType?) { - val newText = text?.takeIf { it.isNotEmpty() }?.let { - if (!shouldTranslatePropertyValue || property.disableValueLocalization) it - else SharedContext.translation[property.getOptionTranslationKey(it.toString())] - }?.let { - if (it.length > 20) { - "...${it.substring(it.length - 20)}" - } else { - it - } - } ?: "" - super.setTextColor(context.getColor(R.color.tertiaryText)) - super.setText(newText, type) - } - } - } - - fun askForValue(property: ConfigProperty, requestedInputType: Int, callback: (String) -> Unit) { - val editText = EditText(context).apply { - inputType = requestedInputType - setText(property.valueContainer.value().toString()) - } - AlertDialog.Builder(context) - .setTitle(SharedContext.translation["property.${property.translationKey}.name"]) - .setView(editText) - .setPositiveButton(positiveButtonText) { _, _ -> - callback(editText.text.toString()) - } - .setNegativeButton(cancelButtonText) { dialog, _ -> - dialog.cancel() - } - .show() - } - - fun askForFolder(activity: Activity, property: ConfigProperty, callback: (String) -> Unit): Pair<Int, ActivityResultCallback> { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - - val requestCode = abs(Random.nextInt()) - activity.startActivityForResult(intent, requestCode) - - return requestCode to let@{_, resultCode, data -> - if (resultCode != Activity.RESULT_OK) return@let - val uri = data?.data ?: return@let - val value = uri.toString() - activity.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - property.valueContainer.writeFrom(value) - callback(value) - } - } - - -} diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt @@ -1,279 +0,0 @@ -package me.rhunk.snapenhance.ui.config - -import android.app.Activity -import android.app.AlertDialog -import android.content.Intent -import android.content.res.ColorStateList -import android.os.Bundle -import android.text.Html -import android.text.InputType -import android.view.View -import android.view.ViewGroup -import android.widget.ImageButton -import android.widget.Switch -import android.widget.TextView -import android.widget.Toast -import me.rhunk.snapenhance.core.BuildConfig -import me.rhunk.snapenhance.core.R -import me.rhunk.snapenhance.SharedContext -import me.rhunk.snapenhance.config.ConfigCategory -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.config.impl.ConfigIntegerValue -import me.rhunk.snapenhance.config.impl.ConfigStateListValue -import me.rhunk.snapenhance.config.impl.ConfigStateSelection -import me.rhunk.snapenhance.config.impl.ConfigStateValue -import me.rhunk.snapenhance.config.impl.ConfigStringValue -import me.rhunk.snapenhance.ui.ItemHelper -import me.rhunk.snapenhance.util.ActivityResultCallback -import kotlin.system.exitProcess - - -class ConfigActivity : Activity() { - private val itemHelper = ItemHelper(this) - private val activityResultCallbacks = mutableMapOf<Int, ActivityResultCallback>() - - @Deprecated("Deprecated in Java") - @Suppress("DEPRECATION") - override fun onBackPressed() { - super.onBackPressed() - finish() - } - - override fun onDestroy() { - super.onDestroy() - SharedContext.config.writeConfig() - } - - override fun onPause() { - super.onPause() - SharedContext.config.writeConfig() - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - activityResultCallbacks[requestCode]?.invoke(requestCode, resultCode, data) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - SharedContext.ensureInitialized(this) - setContentView(R.layout.config_activity) - - findViewById<View>(R.id.title_bar).let { titleBar -> - titleBar.findViewById<TextView>(R.id.title).text = SharedContext.translation["config_activity.title"] - titleBar.findViewById<ImageButton>(R.id.back_button).visibility = View.GONE - } - - val propertyListLayout = findViewById<ViewGroup>(R.id.property_list) - - if (intent.getBooleanExtra("lspatched", false) || - applicationInfo.packageName != "me.rhunk.snapenhance" || - BuildConfig.DEBUG) { - propertyListLayout.addView( - layoutInflater.inflate( - R.layout.config_activity_debug_item, - propertyListLayout, - false - ).apply { - findViewById<TextView>(R.id.debug_item_content).apply { - text = Html.fromHtml( - "You are using a <u><b>debug/unofficial</b></u> build!\n" + - "Please consider downloading stable builds from <a href=\"https://github.com/rhunk/SnapEnhance\">GitHub</a>.", - Html.FROM_HTML_MODE_COMPACT - ) - movementMethod = android.text.method.LinkMovementMethod.getInstance() - } - }) - } - - //check if save folder is set - //TODO: first run activity - run { - val saveFolder = SharedContext.config.string(ConfigProperty.SAVE_FOLDER) - val itemHelper = ItemHelper(this) - - if (saveFolder.isEmpty() || !saveFolder.startsWith("content://")) { - AlertDialog.Builder(this) - .setTitle("Save folder") - .setMessage("Please select a folder where you want to save downloaded files.") - .setPositiveButton("Select") { _, _ -> - val (requestCode, callback) = itemHelper.askForFolder( - this, - ConfigProperty.SAVE_FOLDER - ) {} - activityResultCallbacks[requestCode] = { a1, a2, a3 -> - callback(a1, a2, a3) - Toast.makeText(this, "Save Folder set!", Toast.LENGTH_SHORT).show() - finish() - } - } - .setNegativeButton("Cancel") { _, _ -> - exitProcess(0) - } - .show() - } - } - - var currentCategory: ConfigCategory? = null - - SharedContext.config.entries().filter { !it.key.category.hidden }.forEach { (property, value) -> - val configItem = layoutInflater.inflate(R.layout.config_activity_item, propertyListLayout, false) - - fun addSeparator() { - //add separator - propertyListLayout.addView(View(this).apply { - layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1) - setBackgroundColor(getColor(R.color.tertiaryBackground)) - }) - } - - if (property.category != currentCategory) { - if(!property.shouldAppearInSettings) return@forEach - currentCategory = property.category - with(layoutInflater.inflate(R.layout.config_activity_item, propertyListLayout, false)) { - findViewById<TextView>(R.id.name).apply { - text = SharedContext.translation["category.${property.category.key}"] - textSize = 20f - typeface = typeface?.let { android.graphics.Typeface.create(it, android.graphics.Typeface.BOLD) } - } - propertyListLayout.addView(this) - } - addSeparator() - } - - if (!property.shouldAppearInSettings) return@forEach - - val propertyName = SharedContext.translation["property.${property.translationKey}.name"] - - configItem.findViewById<TextView>(R.id.name).text = propertyName - configItem.findViewById<TextView>(R.id.description).also { - it.text = SharedContext.translation["property.${property.translationKey}.description"] - it.visibility = if (it.text.isEmpty()) View.GONE else View.VISIBLE - } - - fun addValueView(view: View) { - configItem.findViewById<ViewGroup>(R.id.value).addView(view) - } - - when (value) { - is ConfigStateValue -> { - val switch = Switch(this) - switch.isChecked = value.value() - switch.trackTintList = ColorStateList( - arrayOf( - intArrayOf(android.R.attr.state_checked), - intArrayOf(-android.R.attr.state_checked) - ), - intArrayOf( - switch.highlightColor, - getColor(R.color.tertiaryBackground) - ) - ) - switch.setOnCheckedChangeListener { _, isChecked -> - value.writeFrom(isChecked.toString()) - } - configItem.setOnClickListener { switch.toggle() } - addValueView(switch) - } - is ConfigStringValue, is ConfigIntegerValue -> { - val textView = itemHelper.createTranslatedTextView(property, shouldTranslatePropertyValue = false).also { - it.text = value.value().toString() - } - configItem.setOnClickListener { - if (value is ConfigStringValue && value.isFolderPath) { - val (requestCode, callback) = itemHelper.askForFolder(this, property) { - value.writeFrom(it) - textView.text = value.value() - } - - activityResultCallbacks[requestCode] = callback - return@setOnClickListener - } - - if (value is ConfigIntegerValue) { - itemHelper.askForValue(property, InputType.TYPE_CLASS_NUMBER) { - try { - value.writeFrom(it) - textView.text = value.value().toString() - } catch (e: NumberFormatException) { - itemHelper.longToast(SharedContext.translation["config_activity.invalid_number_toast"], this) - } - } - return@setOnClickListener - } - itemHelper.askForValue(property, InputType.TYPE_CLASS_TEXT) { - value.writeFrom(it) - textView.text = value.value().toString() - } - } - addValueView(textView) - } - is ConfigStateListValue -> { - val textView = itemHelper.createTranslatedTextView(property, shouldTranslatePropertyValue = false) - val values = value.value() - - fun updateText() { - textView.text = SharedContext.translation.format("config_activity.selected_text", "count" to values.filter { it.value }.size.toString()) - } - - updateText() - - configItem.setOnClickListener { - AlertDialog.Builder(this) - .setTitle(propertyName) - .setPositiveButton(itemHelper.positiveButtonText) { _, _ -> - updateText() - } - .setMultiChoiceItems( - values.keys.map { - if (property.disableValueLocalization) it - else SharedContext.translation[property.getOptionTranslationKey(it)] - }.toTypedArray(), - values.map { it.value }.toBooleanArray() - ) { _, which, isChecked -> - value.setKey(values.keys.elementAt(which), isChecked) - } - .show() - } - - addValueView(textView) - } - is ConfigStateSelection -> { - val textView = itemHelper.createTranslatedTextView(property, shouldTranslatePropertyValue = true) - textView.text = value.value() - - configItem.setOnClickListener { - val builder = AlertDialog.Builder(this) - builder.setTitle(propertyName) - - builder.setSingleChoiceItems( - value.keys().toTypedArray().map { - if (property.disableValueLocalization) it - else SharedContext.translation[property.getOptionTranslationKey(it)] - }.toTypedArray(), - value.keys().indexOf(value.value()) - ) { _, which -> - value.writeFrom(value.keys()[which]) - } - - builder.setPositiveButton(itemHelper.positiveButtonText) { _, _ -> - textView.text = value.value() - } - - builder.show() - } - addValueView(textView) - } - } - - propertyListLayout.addView(configItem) - addSeparator() - } - - propertyListLayout.addView(layoutInflater.inflate(R.layout.config_activity_debug_item, propertyListLayout, false).apply { - findViewById<TextView>(R.id.debug_item_content).apply { - text = Html.fromHtml("Made by rhunk on <a href=\"https://github.com/rhunk/SnapEnhance\">GitHub</a>", Html.FROM_HTML_MODE_COMPACT) - movementMethod = android.text.method.LinkMovementMethod.getInstance() - } - }) - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.ui.download import android.app.AlertDialog -import android.content.Intent import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter @@ -9,11 +8,9 @@ import android.widget.ImageButton import android.widget.ListView import android.widget.TextView import android.widget.Toast -import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.types.BridgeFileType -import me.rhunk.snapenhance.ui.config.ConfigActivity -import me.rhunk.snapenhance.ui.spoof.DeviceSpooferActivity +import me.rhunk.snapenhance.core.R import java.io.File class ActionListAdapter( @@ -68,12 +65,6 @@ class DebugSettingsLayoutInflater( debugSettingsLayout.findViewById<ListView>(R.id.setting_page_list).apply { adapter = ActionListAdapter(activity, R.layout.debug_setting_item, mutableListOf<Pair<String, () -> Unit>>().apply { - add(SharedContext.translation["config_activity.title"] to { - activity.startActivity(Intent(activity, ConfigActivity::class.java)) - }) - add(SharedContext.translation["spoof_activity.title"] to { - activity.startActivity(Intent(activity, DeviceSpooferActivity::class.java)) - }) add(debugSettingsTranslation["clear_cache_title"] to { context.cacheDir.listFiles()?.forEach { it.deleteRecursively() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt @@ -23,8 +23,8 @@ import kotlinx.coroutines.job import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.SharedContext +import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.data.FileType import me.rhunk.snapenhance.download.data.PendingDownload import me.rhunk.snapenhance.download.enums.DownloadStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt @@ -15,10 +15,10 @@ import android.widget.ImageButton import android.widget.TextView import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView -import me.rhunk.snapenhance.core.BuildConfig -import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper +import me.rhunk.snapenhance.core.BuildConfig +import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.download.data.PendingDownload class DownloadManagerActivity : Activity() { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt @@ -10,7 +10,6 @@ import android.widget.Button import android.widget.LinearLayout import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.Constants.VIEW_INJECTED_CODE -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.impl.Messaging import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader import me.rhunk.snapenhance.features.impl.spying.MessageLogger diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt @@ -21,7 +21,6 @@ import android.widget.LinearLayout import android.widget.Switch import android.widget.Toast import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.wrapper.impl.FriendActionButton import me.rhunk.snapenhance.database.objects.ConversationMessage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt @@ -6,9 +6,7 @@ import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import android.widget.LinearLayout -import de.robv.android.xposed.XC_MethodHook.Unhook import me.rhunk.snapenhance.Constants -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.features.impl.Messaging diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt @@ -10,8 +10,8 @@ import android.widget.ScrollView import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader -import me.rhunk.snapenhance.ui.menu.AbstractMenu import me.rhunk.snapenhance.ui.ViewAppearanceHelper.applyTheme +import me.rhunk.snapenhance.ui.menu.AbstractMenu @SuppressLint("DiscouragedApi") class OperaContextActionMenu : AbstractMenu() { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt @@ -6,9 +6,8 @@ import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import android.widget.ImageView -import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.Constants -import me.rhunk.snapenhance.ui.config.ConfigActivity +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.ui.menu.AbstractMenu import java.io.File diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsMenu.kt @@ -1,26 +1,13 @@ package me.rhunk.snapenhance.ui.menu.impl import android.annotation.SuppressLint -import android.app.AlertDialog -import android.graphics.Color -import android.graphics.Typeface -import android.text.InputType import android.view.View import android.widget.Button -import android.widget.EditText -import android.widget.LinearLayout -import android.widget.Switch -import android.widget.TextView -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.config.impl.ConfigIntegerValue -import me.rhunk.snapenhance.config.impl.ConfigStateListValue -import me.rhunk.snapenhance.config.impl.ConfigStateSelection -import me.rhunk.snapenhance.config.impl.ConfigStateValue -import me.rhunk.snapenhance.config.impl.ConfigStringValue -import me.rhunk.snapenhance.ui.menu.AbstractMenu import me.rhunk.snapenhance.ui.ViewAppearanceHelper +import me.rhunk.snapenhance.ui.menu.AbstractMenu class SettingsMenu : AbstractMenu() { + /* @SuppressLint("ClickableViewAccessibility") private fun createCategoryTitle(key: String): TextView { val categoryText = TextView(context.androidContext) @@ -186,7 +173,7 @@ class SettingsMenu : AbstractMenu() { setPadding(0, 0, 0, thickness) setBackgroundColor(color) } - } + }*/ //TODO: quick settings @SuppressLint("SetTextI18n") @@ -194,9 +181,7 @@ class SettingsMenu : AbstractMenu() { val actions = context.actionManager.getActions().map { Pair(it) { val button = Button(viewModel.context) - button.text = (it.dependsOnProperty?.let { property -> - "["+context.translation["property.${property.translationKey}.name"] + "] " - }?: "") + context.translation[it.nameKey] + button.text = context.translation[it.nameKey] button.setOnClickListener { _ -> it.run() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt @@ -1,111 +0,0 @@ -package me.rhunk.snapenhance.ui.spoof - -import android.app.Activity -import android.content.res.ColorStateList -import android.os.Bundle -import android.text.InputType -import android.view.View -import android.view.ViewGroup -import android.widget.ImageButton -import android.widget.Switch -import android.widget.TextView -import me.rhunk.snapenhance.core.R -import me.rhunk.snapenhance.SharedContext -import me.rhunk.snapenhance.config.ConfigCategory -import me.rhunk.snapenhance.config.impl.ConfigIntegerValue -import me.rhunk.snapenhance.config.impl.ConfigStateValue -import me.rhunk.snapenhance.config.impl.ConfigStringValue -import me.rhunk.snapenhance.ui.ItemHelper - -class DeviceSpooferActivity: Activity() { - private val itemHelper = ItemHelper(this) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - SharedContext.ensureInitialized(this) - setContentView(R.layout.device_spoofer_activity) - - findViewById<TextView>(R.id.title).text = "Device Spoofer" - findViewById<ImageButton>(R.id.back_button).setOnClickListener { finish() } - val propertyListLayout = findViewById<ViewGroup>(R.id.spoof_property_list) - - SharedContext.config.entries().filter { it.key.category == ConfigCategory.DEVICE_SPOOFER }.forEach { (property, value) -> - val configItem = layoutInflater.inflate(R.layout.config_activity_item, propertyListLayout, false) - - val propertyName = SharedContext.translation["property.${property.translationKey}.name"] - - fun addSeparator() { - //add separator - propertyListLayout.addView(View(this).apply { - layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1) - setBackgroundColor(getColor(R.color.tertiaryBackground)) - }) - } - - configItem.findViewById<TextView>(R.id.name).text = propertyName - configItem.findViewById<TextView>(R.id.description).also { - it.text = SharedContext.translation["property.${property.translationKey}.description"] - it.visibility = if (it.text.isEmpty()) View.GONE else View.VISIBLE - } - - fun addValueView(view: View) { - configItem.findViewById<ViewGroup>(R.id.value).addView(view) - } - - when (value) { - is ConfigStateValue -> { - val switch = Switch(this) - switch.isChecked = value.value() - switch.trackTintList = ColorStateList( - arrayOf( - intArrayOf(android.R.attr.state_checked), - intArrayOf(-android.R.attr.state_checked) - ), - intArrayOf( - switch.highlightColor, - getColor(R.color.tertiaryBackground) - ) - ) - switch.setOnCheckedChangeListener { _, isChecked -> - value.writeFrom(isChecked.toString()) - } - configItem.setOnClickListener { switch.toggle() } - addValueView(switch) - } - is ConfigStringValue, is ConfigIntegerValue -> { - val textView = itemHelper.createTranslatedTextView(property, shouldTranslatePropertyValue = false).also { - it.text = value.value().toString() - } - configItem.setOnClickListener { - if (value is ConfigIntegerValue) { - itemHelper.askForValue(property, InputType.TYPE_CLASS_NUMBER) { - try { - value.writeFrom(it) - textView.text = value.value().toString() - } catch (e: NumberFormatException) { - itemHelper.longToast(SharedContext.translation["config_activity.invalid_number_toast"], this) - } - } - return@setOnClickListener - } - itemHelper.askForValue(property, InputType.TYPE_CLASS_TEXT) { - value.writeFrom(it) - textView.text = value.value().toString() - } - } - addValueView(textView) - } - } - - propertyListLayout.addView(configItem) - addSeparator() - } - } - - @Deprecated("Deprecated in Java") - @Suppress("DEPRECATION") - override fun onBackPressed() { - super.onBackPressed() - finish() - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt @@ -10,9 +10,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.withContext -import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.ModContext +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.FileType import me.rhunk.snapenhance.data.MediaReferenceType diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml @@ -1,10 +1,10 @@ [versions] -agp = "8.2.0-alpha13" +agp = "8.2.0-alpha14" androidx-material = "1.6.0-alpha02" junit = "4.13.2" kotlin = "1.8.22" kotlinx-coroutines-android = "1.7.2" -kotlin-reflect = "1.8.21" +kotlin-reflect = "1.8.22" material-icons-extended = "1.6.0-alpha03" navigation-compose = "2.6.0" recyclerview = "1.3.1"