diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index dee4fe07..102a13c1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
+
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt
index 92145ff7..e6b2300a 100644
--- a/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt
@@ -25,6 +25,7 @@ import me.rhunk.snapenhance.download.DownloadTaskManager
import me.rhunk.snapenhance.messaging.ModDatabase
import me.rhunk.snapenhance.messaging.StreaksReminder
import me.rhunk.snapenhance.scripting.RemoteScriptManager
+import me.rhunk.snapenhance.ui.overlay.SettingsOverlay
import me.rhunk.snapenhance.ui.manager.MainActivity
import me.rhunk.snapenhance.ui.manager.data.InstallationSummary
import me.rhunk.snapenhance.ui.manager.data.ModInfo
@@ -56,6 +57,7 @@ class RemoteSideContext(
val streaksReminder = StreaksReminder(this)
val log = LogManager(this)
val scriptManager = RemoteScriptManager(this)
+ val settingsOverlay = SettingsOverlay(this)
//used to load bitmoji selfies and download previews
val imageLoader by lazy {
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt b/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt
index 7cabff84..aa8b2d84 100644
--- a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt
@@ -187,5 +187,20 @@ class BridgeService : Service() {
}
override fun getScriptingInterface() = remoteSideContext.scriptManager
+ override fun openSettingsOverlay() {
+ runCatching {
+ remoteSideContext.settingsOverlay.show()
+ }.onFailure {
+ remoteSideContext.log.error("Failed to open settings overlay", it)
+ }
+ }
+
+ override fun closeSettingsOverlay() {
+ runCatching {
+ remoteSideContext.settingsOverlay.close()
+ }.onFailure {
+ remoteSideContext.log.error("Failed to close settings overlay", it)
+ }
+ }
}
}
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt
index f4d81e0f..81ddfa28 100644
--- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt
@@ -1,5 +1,6 @@
package me.rhunk.snapenhance.ui.manager.sections.features
+import android.content.Intent
import android.net.Uri
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.core.tween
@@ -65,7 +66,6 @@ import androidx.compose.ui.text.font.FontWeight
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.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
@@ -80,12 +80,9 @@ import me.rhunk.snapenhance.core.config.FeatureNotice
import me.rhunk.snapenhance.core.config.PropertyKey
import me.rhunk.snapenhance.core.config.PropertyPair
import me.rhunk.snapenhance.core.config.PropertyValue
+import me.rhunk.snapenhance.ui.manager.MainActivity
import me.rhunk.snapenhance.ui.manager.Section
-import me.rhunk.snapenhance.ui.util.ActivityLauncherHelper
-import me.rhunk.snapenhance.ui.util.AlertDialogs
-import me.rhunk.snapenhance.ui.util.chooseFolder
-import me.rhunk.snapenhance.ui.util.openFile
-import me.rhunk.snapenhance.ui.util.saveFile
+import me.rhunk.snapenhance.ui.util.*
@OptIn(ExperimentalMaterial3Api::class)
class FeaturesSection : Section() {
@@ -98,7 +95,7 @@ class FeaturesSection : Section() {
}
- private lateinit var activityLauncherHelper: ActivityLauncherHelper
+ private var activityLauncherHelper: ActivityLauncherHelper? = null
private val featuresRouteName by lazy { context.translation["manager.routes.features"] }
private lateinit var rememberScaffoldState: BottomSheetScaffoldState
@@ -143,6 +140,16 @@ class FeaturesSection : Section() {
activityLauncherHelper = ActivityLauncherHelper(context.activity!!)
}
+ private fun activityLauncher(block: ActivityLauncherHelper.() -> Unit) {
+ activityLauncherHelper?.let(block) ?: run {
+ //open manager if activity launcher is null
+ val intent = Intent(context.androidContext, MainActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ intent.putExtra("route", enumSection.route)
+ context.androidContext.startActivity(intent)
+ }
+ }
+
override fun build(navGraphBuilder: NavGraphBuilder) {
navGraphBuilder.navigation(route = enumSection.route, startDestination = MAIN_ROUTE) {
composable(MAIN_ROUTE) {
@@ -194,8 +201,10 @@ class FeaturesSection : Section() {
if (property.key.params.flags.contains(ConfigFlag.FOLDER)) {
IconButton(onClick = registerClickCallback {
- activityLauncherHelper.chooseFolder { uri ->
- propertyValue.setAny(uri)
+ activityLauncher {
+ chooseFolder { uri ->
+ propertyValue.setAny(uri)
+ }
}
}.let { { it.invoke(true) } }) {
Icon(Icons.Filled.FolderOpen, contentDescription = null)
@@ -478,24 +487,28 @@ class FeaturesSection : Section() {
val actions = remember {
mapOf(
"Export" to {
- activityLauncherHelper.saveFile("config.json", "application/json") { uri ->
- context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
- context.config.writeConfig()
- context.config.exportToString().byteInputStream().copyTo(it)
- context.shortToast("Config exported successfully!")
+ activityLauncher {
+ saveFile("config.json", "application/json") { uri ->
+ context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
+ context.config.writeConfig()
+ context.config.exportToString().byteInputStream().copyTo(it)
+ context.shortToast("Config exported successfully!")
+ }
}
}
},
"Import" to {
- activityLauncherHelper.openFile("application/json") { uri ->
- context.androidContext.contentResolver.openInputStream(Uri.parse(uri))?.use {
- runCatching {
- context.config.loadFromString(it.readBytes().toString(Charsets.UTF_8))
- }.onFailure {
- context.longToast("Failed to import config ${it.message}")
- return@use
+ activityLauncher {
+ openFile("application/json") { uri ->
+ context.androidContext.contentResolver.openInputStream(Uri.parse(uri))?.use {
+ runCatching {
+ context.config.loadFromString(it.readBytes().toString(Charsets.UTF_8))
+ }.onFailure {
+ context.longToast("Failed to import config ${it.message}")
+ return@use
+ }
+ context.shortToast("Config successfully loaded!")
}
- context.shortToast("Config successfully loaded!")
}
}
},
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSection.kt
index 5ec1f168..b12e5ecb 100644
--- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSection.kt
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSection.kt
@@ -4,32 +4,11 @@ import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.Image
import androidx.compose.foundation.ScrollState
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.RowScope
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.*
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.BugReport
-import androidx.compose.material.icons.filled.Language
-import androidx.compose.material.icons.filled.Map
-import androidx.compose.material.icons.filled.MoreVert
-import androidx.compose.material.icons.filled.OpenInNew
-import androidx.compose.material.icons.filled.ReceiptLong
-import androidx.compose.material.icons.filled.Refresh
-import androidx.compose.material3.Button
-import androidx.compose.material3.Card
-import androidx.compose.material3.CardDefaults
-import androidx.compose.material3.DropdownMenu
-import androidx.compose.material3.DropdownMenuItem
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedCard
-import androidx.compose.material3.Text
+import androidx.compose.material.icons.filled.*
+import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -63,8 +42,8 @@ class HomeSection : Section() {
companion object {
val cardMargin = 10.dp
const val HOME_ROOT = "home_root"
- const val DEBUG_SECTION_ROUTE = "home_debug"
const val LOGS_SECTION_ROUTE = "home_logs"
+ const val SETTINGS_SECTION_ROUTE = "home_settings"
}
private var installationSummary: InstallationSummary? = null
@@ -223,12 +202,12 @@ class HomeSection : Section() {
IconButton(onClick = {
navController.navigate(LOGS_SECTION_ROUTE)
}) {
- Icon(Icons.Filled.ReceiptLong, contentDescription = null)
+ Icon(Icons.Filled.BugReport, contentDescription = null)
}
IconButton(onClick = {
- navController.navigate(DEBUG_SECTION_ROUTE)
+ navController.navigate(SETTINGS_SECTION_ROUTE)
}) {
- Icon(Icons.Filled.BugReport, contentDescription = null)
+ Icon(Icons.Filled.Settings, contentDescription = null)
}
}
LOGS_SECTION_ROUTE -> {
@@ -290,8 +269,8 @@ class HomeSection : Section() {
composable(LOGS_SECTION_ROUTE) {
homeSubSection.LogsSection()
}
- composable(DEBUG_SECTION_ROUTE) {
- homeSubSection.DebugSection()
+ composable(SETTINGS_SECTION_ROUTE) {
+ SettingsSection().also { it.context = context }.Content()
}
}
}
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSubSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSubSection.kt
index 708c5a92..1fcf7cb7 100644
--- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSubSection.kt
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSubSection.kt
@@ -2,26 +2,14 @@ package me.rhunk.snapenhance.ui.manager.sections.home
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.horizontalScroll
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.defaultMinSize
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
-import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardDoubleArrowDown
import androidx.compose.material.icons.filled.KeyboardDoubleArrowUp
-import androidx.compose.material.icons.filled.OpenInNew
import androidx.compose.material.icons.outlined.BugReport
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.Report
@@ -29,17 +17,9 @@ import androidx.compose.material.icons.outlined.Warning
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.setValue
+import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
@@ -49,72 +29,18 @@ import androidx.compose.ui.text.font.FontWeight
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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-import me.rhunk.snapenhance.Constants
-import me.rhunk.snapenhance.core.logger.LogChannel
-import me.rhunk.snapenhance.core.logger.LogLevel
import me.rhunk.snapenhance.LogReader
import me.rhunk.snapenhance.RemoteSideContext
-import me.rhunk.snapenhance.action.EnumAction
-import me.rhunk.snapenhance.core.bridge.types.BridgeFileType
-import me.rhunk.snapenhance.manager.impl.ActionManager
-import me.rhunk.snapenhance.ui.util.AlertDialogs
+import me.rhunk.snapenhance.core.logger.LogChannel
+import me.rhunk.snapenhance.core.logger.LogLevel
class HomeSubSection(
private val context: RemoteSideContext
) {
- private val dialogs by lazy { AlertDialogs(context.translation) }
-
private lateinit var logListState: LazyListState
- @Composable
- private fun RowAction(title: String, requireConfirmation: Boolean = false, action: () -> Unit) {
- var confirmationDialog by remember {
- mutableStateOf(false)
- }
-
- fun takeAction() {
- if (requireConfirmation) {
- confirmationDialog = true
- } else {
- action()
- }
- }
-
- if (requireConfirmation && confirmationDialog) {
- Dialog(onDismissRequest = { confirmationDialog = false }) {
- dialogs.ConfirmDialog(title = "Are you sure?", onConfirm = {
- action()
- confirmationDialog = false
- }, onDismiss = {
- confirmationDialog = false
- })
- }
- }
-
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .height(65.dp)
- .clickable {
- takeAction()
- },
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Text(text = title, modifier = Modifier.padding(start = 26.dp))
- IconButton(onClick = { takeAction() }) {
- Icon(
- imageVector = Icons.Filled.OpenInNew,
- contentDescription = null,
- modifier = Modifier.size(24.dp)
- )
- }
- }
- }
-
@Composable
fun LogsSection() {
val coroutineScope = rememberCoroutineScope()
@@ -236,43 +162,4 @@ class HomeSubSection(
}
}
}
-
- private fun launchActionIntent(action: EnumAction) {
- val intent = context.androidContext.packageManager.getLaunchIntentForPackage(Constants.SNAPCHAT_PACKAGE_NAME)
- intent?.putExtra(ActionManager.ACTION_PARAMETER, action.key)
- context.androidContext.startActivity(intent)
- }
-
- @Composable
- private fun RowTitle(title: String) {
- Text(text = title, modifier = Modifier.padding(16.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold)
- }
-
- @Composable
- fun DebugSection() {
- Column(
- modifier = Modifier
- .fillMaxSize()
- .verticalScroll(ScrollState(0))
- ) {
- RowTitle(title = "Actions")
- EnumAction.values().forEach { enumAction ->
- RowAction(title = context.translation["actions.${enumAction.key}"]) {
- launchActionIntent(enumAction)
- }
- }
-
- RowTitle(title = "Clear Files")
- BridgeFileType.values().forEach { fileType ->
- RowAction(title = fileType.displayName, requireConfirmation = true) {
- runCatching {
- fileType.resolve(context.androidContext).delete()
- context.longToast("Deleted ${fileType.displayName}!")
- }.onFailure {
- context.longToast("Failed to delete ${fileType.displayName}!")
- }
- }
- }
- }
- }
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/SettingsSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/SettingsSection.kt
new file mode 100644
index 00000000..44d9a680
--- /dev/null
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/SettingsSection.kt
@@ -0,0 +1,117 @@
+package me.rhunk.snapenhance.ui.manager.sections.home
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.OpenInNew
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.window.Dialog
+import me.rhunk.snapenhance.Constants
+import me.rhunk.snapenhance.action.EnumAction
+import me.rhunk.snapenhance.core.bridge.types.BridgeFileType
+import me.rhunk.snapenhance.manager.impl.ActionManager
+import me.rhunk.snapenhance.ui.manager.Section
+import me.rhunk.snapenhance.ui.util.AlertDialogs
+
+class SettingsSection : Section() {
+ private val dialogs by lazy { AlertDialogs(context.translation) }
+
+ @Composable
+ private fun RowTitle(title: String) {
+ Text(text = title, modifier = Modifier.padding(16.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold)
+ }
+
+ @Composable
+ private fun RowAction(title: String, requireConfirmation: Boolean = false, action: () -> Unit) {
+ var confirmationDialog by remember {
+ mutableStateOf(false)
+ }
+
+ fun takeAction() {
+ if (requireConfirmation) {
+ confirmationDialog = true
+ } else {
+ action()
+ }
+ }
+
+ if (requireConfirmation && confirmationDialog) {
+ Dialog(onDismissRequest = { confirmationDialog = false }) {
+ dialogs.ConfirmDialog(title = "Are you sure?", onConfirm = {
+ action()
+ confirmationDialog = false
+ }, onDismiss = {
+ confirmationDialog = false
+ })
+ }
+ }
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(65.dp)
+ .clickable {
+ takeAction()
+ },
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(text = title, modifier = Modifier.padding(start = 26.dp))
+ IconButton(onClick = { takeAction() }) {
+ Icon(
+ imageVector = Icons.Filled.OpenInNew,
+ contentDescription = null,
+ modifier = Modifier.size(24.dp)
+ )
+ }
+ }
+ }
+
+ private fun launchActionIntent(action: EnumAction) {
+ val intent = context.androidContext.packageManager.getLaunchIntentForPackage(Constants.SNAPCHAT_PACKAGE_NAME)
+ intent?.putExtra(ActionManager.ACTION_PARAMETER, action.key)
+ context.androidContext.startActivity(intent)
+ }
+
+ @Composable
+ override fun Content() {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .verticalScroll(ScrollState(0))
+ ) {
+ RowTitle(title = "Actions")
+ EnumAction.values().forEach { enumAction ->
+ RowAction(title = context.translation["actions.${enumAction.key}"]) {
+ launchActionIntent(enumAction)
+ }
+ }
+
+ RowTitle(title = "Clear Files")
+ BridgeFileType.values().forEach { fileType ->
+ RowAction(title = fileType.displayName, requireConfirmation = true) {
+ runCatching {
+ fileType.resolve(context.androidContext).delete()
+ context.longToast("Deleted ${fileType.displayName}!")
+ }.onFailure {
+ context.longToast("Failed to delete ${fileType.displayName}!")
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/overlay/ComposeOverlay.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/overlay/ComposeOverlay.kt
new file mode 100644
index 00000000..146c4e69
--- /dev/null
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/overlay/ComposeOverlay.kt
@@ -0,0 +1,71 @@
+package me.rhunk.snapenhance.ui.overlay
+
+import android.content.Context
+import android.os.Bundle
+import androidx.activity.OnBackPressedDispatcher
+import androidx.activity.OnBackPressedDispatcherOwner
+import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.compose.runtime.Recomposer
+import androidx.compose.ui.platform.AndroidUiDispatcher
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.ui.platform.compositionContext
+import androidx.lifecycle.*
+import androidx.savedstate.SavedStateRegistry
+import androidx.savedstate.SavedStateRegistryController
+import androidx.savedstate.SavedStateRegistryOwner
+import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+// https://github.com/tberghuis/FloatingCountdownTimer/blob/master/app/src/main/java/xyz/tberghuis/floatingtimer/service/overlayViewFactory.kt
+fun overlayComposeView(service: Context) = ComposeView(service).apply {
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ val lifecycleOwner = OverlayLifecycleOwner().apply {
+ performRestore(null)
+ handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
+ }
+ setViewTreeLifecycleOwner(lifecycleOwner)
+ setViewTreeSavedStateRegistryOwner(lifecycleOwner)
+
+ val viewModelStore = ViewModelStore()
+ setViewTreeViewModelStoreOwner(object : ViewModelStoreOwner {
+ override val viewModelStore: ViewModelStore
+ get() = viewModelStore
+ })
+
+ val backPressedDispatcherOwner = OnBackPressedDispatcher()
+ setViewTreeOnBackPressedDispatcherOwner(object: OnBackPressedDispatcherOwner {
+ override val lifecycle: Lifecycle
+ get() = lifecycleOwner.lifecycle
+ override val onBackPressedDispatcher: OnBackPressedDispatcher
+ get() = backPressedDispatcherOwner
+ })
+
+ val coroutineContext = AndroidUiDispatcher.CurrentThread
+ val runRecomposeScope = CoroutineScope(coroutineContext)
+ val recomposer = Recomposer(coroutineContext)
+ compositionContext = recomposer
+ runRecomposeScope.launch {
+ recomposer.runRecomposeAndApplyChanges()
+ }
+}
+
+private class OverlayLifecycleOwner : SavedStateRegistryOwner {
+ private var mLifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
+ private var mSavedStateRegistryController: SavedStateRegistryController =
+ SavedStateRegistryController.create(this)
+ override val lifecycle: Lifecycle
+ get() = mLifecycleRegistry
+ override val savedStateRegistry: SavedStateRegistry
+ get() = mSavedStateRegistryController.savedStateRegistry
+ fun handleLifecycleEvent(event: Lifecycle.Event) {
+ mLifecycleRegistry.handleLifecycleEvent(event)
+ }
+ fun performRestore(savedState: Bundle?) {
+ mSavedStateRegistryController.performRestore(savedState)
+ }
+ fun performSave(outBundle: Bundle) {
+ mSavedStateRegistryController.performSave(outBundle)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/overlay/SettingsOverlay.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/overlay/SettingsOverlay.kt
new file mode 100644
index 00000000..c3878356
--- /dev/null
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/overlay/SettingsOverlay.kt
@@ -0,0 +1,133 @@
+package me.rhunk.snapenhance.ui.overlay
+
+import android.app.Dialog
+import android.content.Intent
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.net.Uri
+import android.provider.Settings
+import android.view.WindowManager
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.unit.dp
+import androidx.navigation.compose.rememberNavController
+import com.arthenica.ffmpegkit.Packages.getPackageName
+import me.rhunk.snapenhance.R
+import me.rhunk.snapenhance.RemoteSideContext
+import me.rhunk.snapenhance.ui.AppMaterialTheme
+import me.rhunk.snapenhance.ui.manager.EnumSection
+import me.rhunk.snapenhance.ui.manager.Navigation
+import me.rhunk.snapenhance.ui.manager.sections.features.FeaturesSection
+
+
+class SettingsOverlay(
+ private val context: RemoteSideContext
+) {
+ private lateinit var dialog: Dialog
+ private fun checkForPermissions(): Boolean {
+ if (!Settings.canDrawOverlays(context.androidContext)) {
+ val myIntent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
+ myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ myIntent.setData(Uri.parse("package:" + getPackageName()))
+ context.androidContext.startActivity(myIntent)
+ return false
+ }
+ return true
+ }
+
+ @Composable
+ private fun OverlayContent() {
+ val navHostController = rememberNavController()
+
+ /*navHostController.addOnDestinationChangedListener { _, destination, _ ->
+ dialog.setCancelable(destination.route == FeaturesSection.MAIN_ROUTE)
+ }*/
+
+ val navigation = remember {
+ Navigation(
+ context,
+ mapOf(
+ EnumSection.FEATURES to FeaturesSection().apply {
+ enumSection = EnumSection.FEATURES
+ context = this@SettingsOverlay.context
+ }
+ ),
+ navHostController
+ )
+ }
+
+ Scaffold(
+ containerColor = MaterialTheme.colorScheme.background,
+ topBar = { navigation.TopBar() }
+ ) { innerPadding ->
+ navigation.NavigationHost(
+ startDestination = EnumSection.FEATURES,
+ innerPadding = innerPadding
+ )
+ }
+ }
+
+ fun close() {
+ if (!::dialog.isInitialized || !dialog.isShowing) return
+ context.config.writeConfig()
+ dialog.dismiss()
+ }
+
+ fun show() {
+ if (!checkForPermissions()) {
+ return
+ }
+
+ if (::dialog.isInitialized && dialog.isShowing) {
+ return
+ }
+
+ context.androidContext.mainExecutor.execute {
+ dialog = Dialog(context.androidContext, R.style.FullscreenOverlayDialog)
+ dialog.window?.apply {
+ setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ setLayout(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.MATCH_PARENT,
+ )
+ clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY)
+ }
+
+ dialog.setContentView(
+ overlayComposeView(context.androidContext).apply {
+ setContent {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(10.dp)
+ .clip(shape = MaterialTheme.shapes.large),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ AppMaterialTheme {
+ OverlayContent()
+ }
+ }
+ }
+ }
+ )
+
+ dialog.setCancelable(true)
+ dialog.setOnDismissListener {
+ close()
+ }
+
+ dialog.show()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/AndroidDialogCustom.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/AndroidDialogCustom.kt
new file mode 100644
index 00000000..fa3f3384
--- /dev/null
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/AndroidDialogCustom.kt
@@ -0,0 +1,360 @@
+package me.rhunk.snapenhance.ui.util
+
+
+import android.content.Context
+import android.graphics.Outline
+import android.os.Build
+import android.view.*
+import androidx.activity.ComponentDialog
+import androidx.activity.addCallback
+import androidx.compose.runtime.*
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.R
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.platform.AbstractComposeView
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.platform.ViewRootForInspector
+import androidx.compose.ui.semantics.dialog
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.SecureFlagPolicy
+import androidx.core.view.WindowCompat
+import androidx.lifecycle.findViewTreeLifecycleOwner
+import androidx.lifecycle.findViewTreeViewModelStoreOwner
+import androidx.lifecycle.setViewTreeLifecycleOwner
+import androidx.lifecycle.setViewTreeViewModelStoreOwner
+import androidx.savedstate.findViewTreeSavedStateRegistryOwner
+import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import java.util.UUID
+import kotlin.math.roundToInt
+
+class DialogProperties constructor(
+ val dismissOnBackPress: Boolean = true,
+ val dismissOnClickOutside: Boolean = true,
+ val securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
+ val usePlatformDefaultWidth: Boolean = true,
+ val decorFitsSystemWindows: Boolean = true
+) {
+
+ constructor(
+ dismissOnBackPress: Boolean = true,
+ dismissOnClickOutside: Boolean = true,
+ securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
+ ) : this(
+ dismissOnBackPress = dismissOnBackPress,
+ dismissOnClickOutside = dismissOnClickOutside,
+ securePolicy = securePolicy,
+ usePlatformDefaultWidth = true,
+ decorFitsSystemWindows = true
+ )
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is DialogProperties) return false
+
+ if (dismissOnBackPress != other.dismissOnBackPress) return false
+ if (dismissOnClickOutside != other.dismissOnClickOutside) return false
+ if (securePolicy != other.securePolicy) return false
+ if (usePlatformDefaultWidth != other.usePlatformDefaultWidth) return false
+ if (decorFitsSystemWindows != other.decorFitsSystemWindows) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = dismissOnBackPress.hashCode()
+ result = 31 * result + dismissOnClickOutside.hashCode()
+ result = 31 * result + securePolicy.hashCode()
+ result = 31 * result + usePlatformDefaultWidth.hashCode()
+ result = 31 * result + decorFitsSystemWindows.hashCode()
+ return result
+ }
+}
+
+@Composable
+fun Dialog(
+ onDismissRequest: () -> Unit,
+ properties: DialogProperties = DialogProperties(),
+ content: @Composable () -> Unit
+) {
+ val view = LocalView.current
+ val density = LocalDensity.current
+ val layoutDirection = LocalLayoutDirection.current
+ val composition = rememberCompositionContext()
+ val currentContent by rememberUpdatedState(content)
+ val dialogId = rememberSaveable { UUID.randomUUID() }
+ val dialog = remember(view, density) {
+ DialogWrapper(
+ onDismissRequest,
+ properties,
+ view,
+ layoutDirection,
+ density,
+ dialogId
+ ).apply {
+ setContent(composition) {
+ // TODO(b/159900354): draw a scrim and add margins around the Compose Dialog, and
+ // consume clicks so they can't pass through to the underlying UI
+ DialogLayout(
+ Modifier.semantics { dialog() },
+ ) {
+ currentContent()
+ }
+ }
+ }
+ }
+
+ DisposableEffect(dialog) {
+ // Set the dialog's window type to TYPE_APPLICATION_OVERLAY so it's compatible with compose overlays
+ dialog.window?.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY)
+ dialog.show()
+
+ onDispose {
+ dialog.dismiss()
+ dialog.disposeComposition()
+ }
+ }
+
+ SideEffect {
+ dialog.updateParameters(
+ onDismissRequest = onDismissRequest,
+ properties = properties,
+ layoutDirection = layoutDirection
+ )
+ }
+}
+
+interface DialogWindowProvider {
+ val window: Window
+}
+
+@Suppress("ViewConstructor")
+private class DialogLayout(
+ context: Context,
+ override val window: Window
+) : AbstractComposeView(context), DialogWindowProvider {
+
+ private var content: @Composable () -> Unit by mutableStateOf({})
+
+ var usePlatformDefaultWidth = false
+
+ override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
+ private set
+
+ fun setContent(parent: CompositionContext, content: @Composable () -> Unit) {
+ setParentCompositionContext(parent)
+ this.content = content
+ shouldCreateCompositionOnAttachedToWindow = true
+ createComposition()
+ }
+
+ override fun measureChild(
+ child: View?,
+ parentWidthMeasureSpec: Int,
+ parentHeightMeasureSpec: Int
+ ) {
+
+ super.measureChild(child, parentWidthMeasureSpec, parentHeightMeasureSpec)
+ }
+
+ private val displayWidth: Int
+ get() {
+ val density = context.resources.displayMetrics.density
+ return (context.resources.configuration.screenWidthDp * density).roundToInt()
+ }
+
+ private val displayHeight: Int
+ get() {
+ val density = context.resources.displayMetrics.density
+ return (context.resources.configuration.screenHeightDp * density).roundToInt()
+ }
+
+ @Composable
+ override fun Content() {
+ content()
+ }
+}
+
+private class DialogWrapper(
+ private var onDismissRequest: () -> Unit,
+ private var properties: DialogProperties,
+ private val composeView: View,
+ layoutDirection: LayoutDirection,
+ density: Density,
+ dialogId: UUID
+) : ComponentDialog(
+ ContextThemeWrapper(
+ composeView.context,
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || properties.decorFitsSystemWindows) {
+ R.style.DialogWindowTheme
+ } else {
+ R.style.FloatingDialogWindowTheme
+ }
+ )
+),
+ ViewRootForInspector {
+
+ private val dialogLayout: DialogLayout
+
+ // On systems older than Android S, there is a bug in the surface insets matrix math used by
+ // elevation, so high values of maxSupportedElevation break accessibility services: b/232788477.
+ private val maxSupportedElevation = 8.dp
+
+ override val subCompositionView: AbstractComposeView get() = dialogLayout
+
+ private val defaultSoftInputMode: Int
+
+ init {
+ val window = window ?: error("Dialog has no window")
+ defaultSoftInputMode =
+ window.attributes.softInputMode and WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST
+ window.requestFeature(Window.FEATURE_NO_TITLE)
+ window.setBackgroundDrawableResource(android.R.color.transparent)
+ @OptIn(ExperimentalComposeUiApi::class)
+ WindowCompat.setDecorFitsSystemWindows(window, properties.decorFitsSystemWindows)
+ dialogLayout = DialogLayout(context, window).apply {
+ // Set unique id for AbstractComposeView. This allows state restoration for the state
+ // defined inside the Dialog via rememberSaveable()
+ setTag(R.id.compose_view_saveable_id_tag, "Dialog:$dialogId")
+ // Enable children to draw their shadow by not clipping them
+ clipChildren = false
+ // Allocate space for elevation
+ with(density) { elevation = maxSupportedElevation.toPx() }
+ // Simple outline to force window manager to allocate space for shadow.
+ // Note that the outline affects clickable area for the dismiss listener. In case of
+ // shapes like circle the area for dismiss might be to small (rectangular outline
+ // consuming clicks outside of the circle).
+ outlineProvider = object : ViewOutlineProvider() {
+ override fun getOutline(view: View, result: Outline) {
+ result.setRect(0, 0, view.width, view.height)
+ // We set alpha to 0 to hide the view's shadow and let the composable to draw
+ // its own shadow. This still enables us to get the extra space needed in the
+ // surface.
+ result.alpha = 0f
+ }
+ }
+ }
+
+ /**
+ * Disables clipping for [this] and all its descendant [ViewGroup]s until we reach a
+ * [DialogLayout] (the [ViewGroup] containing the Compose hierarchy).
+ */
+ fun ViewGroup.disableClipping() {
+ clipChildren = false
+ if (this is DialogLayout) return
+ for (i in 0 until childCount) {
+ (getChildAt(i) as? ViewGroup)?.disableClipping()
+ }
+ }
+
+ // Turn of all clipping so shadows can be drawn outside the window
+ (window.decorView as? ViewGroup)?.disableClipping()
+ setContentView(dialogLayout)
+ dialogLayout.setViewTreeLifecycleOwner(composeView.findViewTreeLifecycleOwner())
+ dialogLayout.setViewTreeViewModelStoreOwner(composeView.findViewTreeViewModelStoreOwner())
+ dialogLayout.setViewTreeSavedStateRegistryOwner(
+ composeView.findViewTreeSavedStateRegistryOwner()
+ )
+
+ // Initial setup
+ updateParameters(onDismissRequest, properties, layoutDirection)
+
+ // Due to how the onDismissRequest callback works
+ // (it enforces a just-in-time decision on whether to update the state to hide the dialog)
+ // we need to unconditionally add a callback here that is always enabled,
+ // meaning we'll never get a system UI controlled predictive back animation
+ // for these dialogs
+ onBackPressedDispatcher.addCallback(this) {
+ if (properties.dismissOnBackPress) {
+ onDismissRequest()
+ }
+ }
+ }
+
+ private fun setLayoutDirection(layoutDirection: LayoutDirection) {
+ dialogLayout.layoutDirection = when (layoutDirection) {
+ LayoutDirection.Ltr -> android.util.LayoutDirection.LTR
+ LayoutDirection.Rtl -> android.util.LayoutDirection.RTL
+ }
+ }
+
+ // TODO(b/159900354): Make the Android Dialog full screen and the scrim fully transparent
+
+ fun setContent(parentComposition: CompositionContext, children: @Composable () -> Unit) {
+ dialogLayout.setContent(parentComposition, children)
+ }
+
+ private fun setSecurePolicy(securePolicy: SecureFlagPolicy) {
+ }
+
+ fun updateParameters(
+ onDismissRequest: () -> Unit,
+ properties: DialogProperties,
+ layoutDirection: LayoutDirection
+ ) {
+ this.onDismissRequest = onDismissRequest
+ this.properties = properties
+ setSecurePolicy(properties.securePolicy)
+ setLayoutDirection(layoutDirection)
+ if (properties.usePlatformDefaultWidth && !dialogLayout.usePlatformDefaultWidth) {
+ // Undo fixed size in internalOnLayout, which would suppress size changes when
+ // usePlatformDefaultWidth is true.
+ window?.setLayout(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT
+ )
+ }
+ dialogLayout.usePlatformDefaultWidth = properties.usePlatformDefaultWidth
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+ @OptIn(ExperimentalComposeUiApi::class)
+ if (properties.decorFitsSystemWindows) {
+ window?.setSoftInputMode(defaultSoftInputMode)
+ } else {
+ @Suppress("DEPRECATION")
+ window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
+ }
+ }
+ }
+
+ fun disposeComposition() {
+ dialogLayout.disposeComposition()
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ val result = super.onTouchEvent(event)
+ if (result && properties.dismissOnClickOutside) {
+ onDismissRequest()
+ }
+
+ return result
+ }
+
+ override fun cancel() {
+ // Prevents the dialog from dismissing itself
+ return
+ }
+}
+
+@Composable
+private fun DialogLayout(
+ modifier: Modifier = Modifier,
+ content: @Composable () -> Unit
+) {
+ Layout(
+ content = content,
+ modifier = modifier
+ ) { measurables, constraints ->
+ val placeables = measurables.map { it.measure(constraints) }
+ val width = placeables.maxBy { it.width }.width
+ val height = placeables.maxBy { it.height }.height
+ layout(width, height) {
+ placeables.forEach { it.placeRelative(0, 0) }
+ }
+ }
+}
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..97d8bf2f
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/core/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl b/core/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl
index 68d8c465..2ce769cd 100644
--- a/core/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl
+++ b/core/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl
@@ -93,4 +93,8 @@ interface BridgeInterface {
oneway void passGroupsAndFriends(in List groups, in List friends);
IScripting getScriptingInterface();
+
+ void openSettingsOverlay();
+
+ void closeSettingsOverlay();
}
\ No newline at end of file
diff --git a/core/src/main/assets/lang/en_US.json b/core/src/main/assets/lang/en_US.json
index bdf2cad2..0c729b91 100644
--- a/core/src/main/assets/lang/en_US.json
+++ b/core/src/main/assets/lang/en_US.json
@@ -25,7 +25,7 @@
"downloads": "Downloads",
"features": "Features",
"home": "Home",
- "home_debug": "Debug",
+ "home_settings": "Settings",
"home_logs": "Logs",
"social": "Social",
"scripts": "Scripts"
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt
index 0b3df4bf..c6a4a8c0 100644
--- a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt
+++ b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt
@@ -16,12 +16,20 @@ import me.rhunk.snapenhance.core.messaging.MessagingFriendInfo
import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo
import me.rhunk.snapenhance.core.util.ktx.getApplicationInfoCompat
import me.rhunk.snapenhance.data.SnapClassCache
+import me.rhunk.snapenhance.hook.HookAdapter
import me.rhunk.snapenhance.hook.HookStage
import me.rhunk.snapenhance.hook.Hooker
import me.rhunk.snapenhance.hook.hook
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime
+private fun useMainActivity(hookAdapter: HookAdapter, block: Activity.() -> Unit) {
+ val activity = hookAdapter.thisObject() as Activity
+ if (!activity.packageName.equals(Constants.SNAPCHAT_PACKAGE_NAME)) return
+ block(activity)
+}
+
+
class SnapEnhance {
companion object {
lateinit var classLoader: ClassLoader
@@ -68,12 +76,18 @@ class SnapEnhance {
}
Activity::class.java.hook( "onCreate", HookStage.AFTER, { isBridgeInitialized }) {
- val activity = it.thisObject() as Activity
- if (!activity.packageName.equals(Constants.SNAPCHAT_PACKAGE_NAME)) return@hook
- val isMainActivityNotNull = appContext.mainActivity != null
- appContext.mainActivity = activity
- if (isMainActivityNotNull || !appContext.mappings.isMappingsLoaded()) return@hook
- onActivityCreate()
+ useMainActivity(it) {
+ val isMainActivityNotNull = appContext.mainActivity != null
+ appContext.mainActivity = this
+ if (isMainActivityNotNull || !appContext.mappings.isMappingsLoaded()) return@useMainActivity
+ onActivityCreate()
+ }
+ }
+
+ Activity::class.java.hook( "onPause", HookStage.AFTER, { isBridgeInitialized }) {
+ useMainActivity(it) {
+ appContext.bridgeClient.closeSettingsOverlay()
+ }
}
var activityWasResumed = false
@@ -81,17 +95,16 @@ class SnapEnhance {
//we need to reload the config when the app is resumed
//FIXME: called twice at first launch
Activity::class.java.hook("onResume", HookStage.AFTER, { isBridgeInitialized }) {
- val activity = it.thisObject() as Activity
- if (!activity.packageName.equals(Constants.SNAPCHAT_PACKAGE_NAME)) return@hook
+ useMainActivity(it) {
+ if (!activityWasResumed) {
+ activityWasResumed = true
+ return@useMainActivity
+ }
- if (!activityWasResumed) {
- activityWasResumed = true
- return@hook
+ appContext.actionManager.onNewIntent(this.intent)
+ appContext.reloadConfig()
+ syncRemote()
}
-
- appContext.actionManager.onNewIntent(activity.intent)
- appContext.reloadConfig()
- syncRemote()
}
}
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/bridge/BridgeClient.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/bridge/BridgeClient.kt
index b59c669c..6f5e4018 100644
--- a/core/src/main/kotlin/me/rhunk/snapenhance/core/bridge/BridgeClient.kt
+++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/bridge/BridgeClient.kt
@@ -140,4 +140,7 @@ class BridgeClient(
= service.setRule(targetUuid, type.key, state)
fun getScriptingInterface(): IScripting = service.getScriptingInterface()
+
+ fun openSettingsOverlay() = service.openSettingsOverlay()
+ fun closeSettingsOverlay() = service.closeSettingsOverlay()
}
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
index e31a05a3..00e87d5a 100644
--- 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
@@ -45,11 +45,12 @@ class SettingsGearInjector : AbstractMenu() {
isClickable = true
setOnClickListener {
- val intent = Intent().apply {
+ /* val intent = Intent().apply {
setClassName(BuildConfig.APPLICATION_ID, "me.rhunk.snapenhance.ui.manager.MainActivity")
putExtra("route", "features")
}
- context.startActivity(intent)
+ context.startActivity(intent)*/
+ this@SettingsGearInjector.context.bridgeClient.openSettingsOverlay()
}
parent.setOnTouchListener { _, event ->