feat(core): sif status

- add InAppOverlay custom composable
This commit is contained in:
rhunk 2024-08-11 17:42:42 +02:00
parent 43f1a305a2
commit 0ac142f46c
5 changed files with 124 additions and 67 deletions

View File

@ -60,6 +60,7 @@ class FeatureManager(
fun init() { fun init() {
register( register(
SecurityFeatures(),
EndToEndEncryption(), EndToEndEncryption(),
ScopeSync(), ScopeSync(),
PreventMessageListAutoScroll(), PreventMessageListAutoScroll(),

View File

@ -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
)
}
}
}

View File

@ -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)
} }
} }
} }

View File

@ -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")
}
} }
} }

View File

@ -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,