mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-29 21:10:20 +02:00
feat(core): sif status
- add InAppOverlay custom composable
This commit is contained in:
parent
43f1a305a2
commit
0ac142f46c
@ -60,6 +60,7 @@ class FeatureManager(
|
|||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
register(
|
register(
|
||||||
|
SecurityFeatures(),
|
||||||
EndToEndEncryption(),
|
EndToEndEncryption(),
|
||||||
ScopeSync(),
|
ScopeSync(),
|
||||||
PreventMessageListAutoScroll(),
|
PreventMessageListAutoScroll(),
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
package me.rhunk.snapenhance.core.features.impl
|
||||||
|
|
||||||
|
import android.system.Os
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
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.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import me.rhunk.snapenhance.core.features.Feature
|
||||||
|
|
||||||
|
class SecurityFeatures : Feature("Security Features") {
|
||||||
|
private fun transact(option: Int) = Os.prctl(option, 0, 0, 0, 0)
|
||||||
|
|
||||||
|
private val token by lazy {
|
||||||
|
runCatching { transact(0) }.getOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getStatus() = token?.run {
|
||||||
|
transact(this).toString(2).padStart(32, '0').count { it == '1' }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun init() {
|
||||||
|
token // pre init token
|
||||||
|
|
||||||
|
context.inAppOverlay.addCustomComposable {
|
||||||
|
var statusText by remember {
|
||||||
|
mutableStateOf("")
|
||||||
|
}
|
||||||
|
var textColor by remember {
|
||||||
|
mutableStateOf(Color.Red)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
while (true) {
|
||||||
|
val status = getStatus()
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (status == null || status == 0) {
|
||||||
|
textColor = Color.Red
|
||||||
|
statusText = "sif not loaded. Can't get status"
|
||||||
|
} else {
|
||||||
|
textColor = Color.Green
|
||||||
|
statusText = "sif = $status"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay(1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = statusText,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomCenter)
|
||||||
|
.background(Color.Black, shape = RoundedCornerShape(5.dp))
|
||||||
|
.padding(3.dp),
|
||||||
|
fontSize = 10.sp,
|
||||||
|
color = textColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,6 @@ import android.content.Intent
|
|||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.widget.FrameLayout
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
@ -26,12 +25,12 @@ import kotlinx.coroutines.withContext
|
|||||||
import me.rhunk.snapenhance.common.data.FileType
|
import me.rhunk.snapenhance.common.data.FileType
|
||||||
import me.rhunk.snapenhance.common.ui.AppMaterialTheme
|
import me.rhunk.snapenhance.common.ui.AppMaterialTheme
|
||||||
import me.rhunk.snapenhance.common.ui.createComposeAlertDialog
|
import me.rhunk.snapenhance.common.ui.createComposeAlertDialog
|
||||||
import me.rhunk.snapenhance.common.ui.createComposeView
|
|
||||||
import me.rhunk.snapenhance.common.util.ktx.toParcelFileDescriptor
|
import me.rhunk.snapenhance.common.util.ktx.toParcelFileDescriptor
|
||||||
import me.rhunk.snapenhance.common.util.snap.MediaDownloaderHelper
|
import me.rhunk.snapenhance.common.util.snap.MediaDownloaderHelper
|
||||||
import me.rhunk.snapenhance.core.event.events.impl.ActivityResultEvent
|
import me.rhunk.snapenhance.core.event.events.impl.ActivityResultEvent
|
||||||
import me.rhunk.snapenhance.core.event.events.impl.AddViewEvent
|
import me.rhunk.snapenhance.core.event.events.impl.AddViewEvent
|
||||||
import me.rhunk.snapenhance.core.features.Feature
|
import me.rhunk.snapenhance.core.features.Feature
|
||||||
|
import me.rhunk.snapenhance.core.ui.CustomComposable
|
||||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||||
import me.rhunk.snapenhance.core.util.hook.hook
|
import me.rhunk.snapenhance.core.util.hook.hook
|
||||||
import me.rhunk.snapenhance.core.util.ktx.getId
|
import me.rhunk.snapenhance.core.util.ktx.getId
|
||||||
@ -507,24 +506,26 @@ class AccountSwitcher: Feature("Account Switcher") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findClass("com.snap.identity.loginsignup.ui.LoginSignupActivity").apply {
|
val switchButtonComposable: CustomComposable = {
|
||||||
hook("onPostCreate", HookStage.AFTER) {
|
Row(
|
||||||
context.mainActivity!!.findViewById<FrameLayout>(android.R.id.content).addView(createComposeView(context.mainActivity!!) {
|
modifier = Modifier
|
||||||
Row(
|
.fillMaxWidth()
|
||||||
modifier = Modifier.fillMaxWidth(),
|
.align(Alignment.TopStart),
|
||||||
) {
|
) {
|
||||||
Button(
|
Button(
|
||||||
onClick = { showManagementPopup() },
|
onClick = { showManagementPopup() },
|
||||||
modifier = Modifier.padding(16.dp)
|
modifier = Modifier.padding(16.dp)
|
||||||
) {
|
) {
|
||||||
Text("Switch Account")
|
Text("Switch Account")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.apply {
|
}
|
||||||
layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT).apply {
|
|
||||||
gravity = android.view.Gravity.TOP or android.view.Gravity.START
|
onNextActivityCreate { activity ->
|
||||||
}
|
if (!activity.componentName.className.endsWith("LoginSignupActivity")) return@onNextActivityCreate
|
||||||
})
|
context.inAppOverlay.addCustomComposable(switchButtonComposable)
|
||||||
|
onNextActivityCreate {
|
||||||
|
context.inAppOverlay.removeCustomComposable(switchButtonComposable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,16 @@
|
|||||||
package me.rhunk.snapenhance.core.features.impl.experiments
|
package me.rhunk.snapenhance.core.features.impl.experiments
|
||||||
|
|
||||||
import android.app.Activity
|
import androidx.compose.foundation.layout.*
|
||||||
import android.view.View
|
|
||||||
import android.widget.FrameLayout
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.BugReport
|
import androidx.compose.material.icons.filled.BugReport
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.material3.FilledIconButton
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextField
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
@ -29,9 +19,7 @@ import androidx.compose.ui.unit.sp
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.rhunk.snapenhance.common.bridge.FileHandleScope
|
import me.rhunk.snapenhance.common.bridge.FileHandleScope
|
||||||
import me.rhunk.snapenhance.common.bridge.toWrapper
|
import me.rhunk.snapenhance.common.bridge.toWrapper
|
||||||
import me.rhunk.snapenhance.common.ui.AppMaterialTheme
|
|
||||||
import me.rhunk.snapenhance.common.ui.createComposeAlertDialog
|
import me.rhunk.snapenhance.common.ui.createComposeAlertDialog
|
||||||
import me.rhunk.snapenhance.common.ui.createComposeView
|
|
||||||
import me.rhunk.snapenhance.core.features.Feature
|
import me.rhunk.snapenhance.core.features.Feature
|
||||||
import me.rhunk.snapenhance.core.features.impl.downloader.MediaDownloader
|
import me.rhunk.snapenhance.core.features.impl.downloader.MediaDownloader
|
||||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||||
@ -101,35 +89,6 @@ class ComposerHooks: Feature("ComposerHooks") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val composerConsoleTag = Random.nextLong().toString()
|
|
||||||
|
|
||||||
private fun injectConsole(activity: Activity) {
|
|
||||||
val root = activity.findViewById<FrameLayout>(android.R.id.content) ?: run {
|
|
||||||
context.log.warn("Unable to find root view. Can't inject console.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
root.post {
|
|
||||||
if (root.findViewWithTag<View>(composerConsoleTag) != null) return@post
|
|
||||||
root.addView(createComposeView(root.context) {
|
|
||||||
AppMaterialTheme {
|
|
||||||
FilledIconButton(
|
|
||||||
onClick = {
|
|
||||||
composerConsole.show()
|
|
||||||
},
|
|
||||||
modifier = Modifier.padding(top = 100.dp, end = 16.dp)
|
|
||||||
) {
|
|
||||||
Icon(Icons.Default.BugReport, contentDescription = "Debug Console")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.apply {
|
|
||||||
tag = composerConsoleTag
|
|
||||||
layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT).apply {
|
|
||||||
gravity = android.view.Gravity.TOP or android.view.Gravity.END
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun newComposerFunction(block: ComposerMarshaller.() -> Boolean): Any? {
|
private fun newComposerFunction(block: ComposerMarshaller.() -> Boolean): Any? {
|
||||||
val composerFunctionClass = findClass("com.snap.composer.callable.ComposerFunction")
|
val composerFunctionClass = findClass("com.snap.composer.callable.ComposerFunction")
|
||||||
return Proxy.newProxyInstance(
|
return Proxy.newProxyInstance(
|
||||||
@ -223,9 +182,16 @@ class ComposerHooks: Feature("ComposerHooks") {
|
|||||||
|
|
||||||
loadHooks()
|
loadHooks()
|
||||||
|
|
||||||
onNextActivityCreate { activity ->
|
if (config.composerConsole.get()) {
|
||||||
if (config.composerConsole.get()) {
|
context.inAppOverlay.addCustomComposable {
|
||||||
injectConsole(activity)
|
FilledIconButton(
|
||||||
|
onClick = {
|
||||||
|
composerConsole.show()
|
||||||
|
},
|
||||||
|
modifier = Modifier.align(Alignment.TopEnd).padding(top = 100.dp, end = 16.dp)
|
||||||
|
) {
|
||||||
|
Icon(Icons.Default.BugReport, contentDescription = "Debug Console")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,8 @@ import me.rhunk.snapenhance.core.util.ktx.isDarkTheme
|
|||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
typealias CustomComposable = @Composable BoxScope.() -> Unit
|
||||||
|
|
||||||
class InAppOverlay(
|
class InAppOverlay(
|
||||||
private val context: ModContext
|
private val context: ModContext
|
||||||
) {
|
) {
|
||||||
@ -113,6 +115,7 @@ class InAppOverlay(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val toasts = mutableStateListOf<Toast>()
|
private val toasts = mutableStateListOf<Toast>()
|
||||||
|
private val customComposables = mutableStateListOf<CustomComposable>()
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@ -180,6 +183,10 @@ class InAppOverlay(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customComposables.forEach {
|
||||||
|
it()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,6 +211,14 @@ class InAppOverlay(
|
|||||||
injectOverlay(activity)
|
injectOverlay(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addCustomComposable(composable: CustomComposable) {
|
||||||
|
customComposables.add(composable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeCustomComposable(composable: CustomComposable) {
|
||||||
|
customComposables.remove(composable)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DurationProgress(
|
private fun DurationProgress(
|
||||||
duration: Int,
|
duration: Int,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user