feat(shake menu): add more options
This commit is contained in:
parent
c9ecf551bf
commit
0eb28c094d
4 changed files with 75 additions and 215 deletions
3
.idea/deploymentTargetSelector.xml
generated
3
.idea/deploymentTargetSelector.xml
generated
|
|
@ -5,9 +5,6 @@
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
<SelectionState runConfigName="KettuXposed.app">
|
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
|
||||||
</SelectionState>
|
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
|
|
@ -1,4 +1,3 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ class Main : Module(), IXposedHookLoadPackage, IXposedHookZygoteInit {
|
||||||
FixResourcesModule,
|
FixResourcesModule,
|
||||||
BlockDeepLinksTrackingModule,
|
BlockDeepLinksTrackingModule,
|
||||||
BlockCrashReportingModule,
|
BlockCrashReportingModule,
|
||||||
LogBoxModule(),
|
LogBoxModule,
|
||||||
CacheModule,
|
CacheModule,
|
||||||
FontsModule,
|
FontsModule,
|
||||||
ThemesModule,
|
ThemesModule,
|
||||||
|
|
|
||||||
|
|
@ -1,70 +1,82 @@
|
||||||
package io.github.revenge.xposed.modules
|
package io.github.revenge.xposed.modules
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
|
||||||
import cocobo1.pupu.xposed.BuildConfig
|
|
||||||
import io.github.revenge.xposed.Module
|
|
||||||
import io.github.revenge.xposed.Utils
|
|
||||||
import io.github.revenge.xposed.Utils.Companion.reloadApp
|
|
||||||
import io.github.revenge.xposed.Utils.Log
|
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Intent
|
import android.content.Context
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import de.robv.android.xposed.XposedHelpers
|
import de.robv.android.xposed.XposedHelpers
|
||||||
|
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||||
|
import io.github.revenge.xposed.Module
|
||||||
|
import io.github.revenge.xposed.Utils.Companion.reloadApp
|
||||||
|
import io.github.revenge.xposed.Utils.Log
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import org.json.JSONArray
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
class LogBoxModule : Module() {
|
object LogBoxModule : Module() {
|
||||||
lateinit var packageParam: XC_LoadPackage.LoadPackageParam
|
lateinit var packageParam: XC_LoadPackage.LoadPackageParam
|
||||||
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||||||
|
|
||||||
override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) = with(packageParam) {
|
override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) = with(packageParam) {
|
||||||
this@LogBoxModule.packageParam = packageParam
|
this@LogBoxModule.packageParam = packageParam
|
||||||
|
|
||||||
val dcdReactNativeHostClass = classLoader.loadClass("com.discord.bridge.DCDReactNativeHost")
|
try {
|
||||||
val getUseDeveloperSupportMethod = dcdReactNativeHostClass.methods.first { it.name == "getUseDeveloperSupport" }
|
val dcdReactNativeHostClass = classLoader.loadClass("com.discord.bridge.DCDReactNativeHost")
|
||||||
|
val getUseDeveloperSupportMethod = dcdReactNativeHostClass.methods.first { it.name == "getUseDeveloperSupport" }
|
||||||
getUseDeveloperSupportMethod.hook {
|
|
||||||
before {
|
getUseDeveloperSupportMethod.hook {
|
||||||
result = true
|
before {
|
||||||
|
result = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("Failed to hook DCDReactNativeHost: ${e.message}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
listOf(
|
listOf(
|
||||||
"com.facebook.react.devsupport.BridgeDevSupportManager",
|
"com.facebook.react.devsupport.BridgeDevSupportManager",
|
||||||
"com.facebook.react.devsupport.BridgelessDevSupportManager"
|
"com.facebook.react.devsupport.BridgelessDevSupportManager"
|
||||||
).mapNotNull { packageParam.classLoader.safeLoadClass(it) }.forEach {
|
).mapNotNull { packageParam.classLoader.safeLoadClass(it) }.forEach {
|
||||||
hookDevSupportManager(it)
|
hookDevSupportManager(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
return@with
|
return@with
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onContext(context: Context) {
|
||||||
|
}
|
||||||
|
|
||||||
private fun hookDevSupportManager(clazz: Class<*>) {
|
private fun hookDevSupportManager(clazz: Class<*>) {
|
||||||
val showDevOptionsDialogMethod = clazz.methods.first { it.name == "showDevOptionsDialog" }
|
try {
|
||||||
|
val handleReloadJSMethod = clazz.methods.first { it.name == "handleReloadJS" }
|
||||||
showDevOptionsDialogMethod.hook {
|
handleReloadJSMethod.hook {
|
||||||
before {
|
before {
|
||||||
try {
|
reloadApp()
|
||||||
val context = getContextFromDevSupport(clazz, thisObject)
|
|
||||||
if (context != null) {
|
|
||||||
showRecoveryMenu(context)
|
|
||||||
}
|
|
||||||
result = null
|
result = null
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("Failed to show recovery menu: ${e.message}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val showDevOptionsDialogMethod = clazz.methods.first { it.name == "showDevOptionsDialog" }
|
||||||
|
showDevOptionsDialogMethod.hook {
|
||||||
|
before {
|
||||||
|
try {
|
||||||
|
val context = getContextFromDevSupport(clazz, thisObject)
|
||||||
|
if (context != null) {
|
||||||
|
showRecoveryMenu(context)
|
||||||
|
}
|
||||||
|
result = null // Ignore the original dev menu
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("Failed to show recovery menu: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("Failed to hook DevSupportManager methods on class ${clazz.name}: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getContextFromDevSupport(clazz: Class<*>, instance: Any?): Context? {
|
private fun getContextFromDevSupport(clazz: Class<*>, instance: Any?): Context? {
|
||||||
return try {
|
return try {
|
||||||
val mReactInstanceDevHelperField = XposedHelpers.findField(clazz, "mReactInstanceDevHelper")
|
val mReactInstanceDevHelperField = XposedHelpers.findField(clazz, "mReactInstanceDevHelper")
|
||||||
|
|
@ -76,44 +88,14 @@ class LogBoxModule : Module() {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reloadReactNative(context: Context) {
|
|
||||||
try {
|
|
||||||
val reactInstanceManagerClass = packageParam.classLoader.loadClass("com.facebook.react.ReactInstanceManager")
|
|
||||||
val reactApplicationClass = packageParam.classLoader.loadClass("com.facebook.react.ReactApplication")
|
|
||||||
|
|
||||||
val app = context.applicationContext
|
|
||||||
|
|
||||||
if (reactApplicationClass.isInstance(app)) {
|
|
||||||
val getReactNativeHostMethod = reactApplicationClass.methods.first { it.name == "getReactNativeHost" }
|
|
||||||
val reactNativeHost = getReactNativeHostMethod.invoke(app)
|
|
||||||
|
|
||||||
val getReactInstanceManagerMethod = reactNativeHost.javaClass.methods.first { it.name == "getReactInstanceManager" }
|
|
||||||
val reactInstanceManager = getReactInstanceManagerMethod.invoke(reactNativeHost)
|
|
||||||
|
|
||||||
if (reactInstanceManager != null) {
|
|
||||||
val reloadMethod = reactInstanceManagerClass.methods.first { it.name == "recreateReactContextInBackground" }
|
|
||||||
reloadMethod.invoke(reactInstanceManager)
|
|
||||||
} else {
|
|
||||||
showError(context, "Error", "Could not get React Instance Manager")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showError(context, "Error", "Application is not a React Application")
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("Failed to Reload App: ${e.message}")
|
|
||||||
showError(context, "Failed to reload", "Error: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showRecoveryMenu(context: Context) {
|
private fun showRecoveryMenu(context: Context) {
|
||||||
val options = arrayOf(
|
val options = arrayOf(
|
||||||
if (isSafeModeEnabled(context)) "Disable Safe Mode" else "Enable Safe Mode",
|
if (isSafeModeEnabled(context)) "Disable Safe Mode" else "Enable Safe Mode",
|
||||||
"Refetch Bundle",
|
"Reset Bundle",
|
||||||
"Load Custom Bundle",
|
"Reload App (Full Relaunch)"
|
||||||
"Reload App"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
AlertDialog.Builder(context)
|
AlertDialog.Builder(context)
|
||||||
.setTitle("KettuXposed Recovery Menu")
|
.setTitle("KettuXposed Recovery Menu")
|
||||||
.setItems(options) { _, which ->
|
.setItems(options) { _, which ->
|
||||||
|
|
@ -122,16 +104,15 @@ class LogBoxModule : Module() {
|
||||||
.setNegativeButton("Cancel", null)
|
.setNegativeButton("Cancel", null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMenuSelection(context: Context, index: Int) {
|
private fun handleMenuSelection(context: Context, index: Int) {
|
||||||
when (index) {
|
when (index) {
|
||||||
0 -> toggleSafeMode(context)
|
0 -> toggleSafeMode(context)
|
||||||
1 -> confirmAction(context, "refetch the bundle") { refetchBundle(context) }
|
1 -> confirmAction(context, "reset bundle") { resetBundle(context) }
|
||||||
2 -> loadCustomBundle(context)
|
2 -> reloadApp()
|
||||||
3 -> reloadApp()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun confirmAction(context: Context, actionText: String, action: () -> Unit) {
|
private fun confirmAction(context: Context, actionText: String, action: () -> Unit) {
|
||||||
AlertDialog.Builder(context)
|
AlertDialog.Builder(context)
|
||||||
.setTitle("Confirm Action")
|
.setTitle("Confirm Action")
|
||||||
|
|
@ -140,12 +121,12 @@ class LogBoxModule : Module() {
|
||||||
.setNegativeButton("Cancel", null)
|
.setNegativeButton("Cancel", null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSafeModeEnabled(context: Context): Boolean {
|
private fun isSafeModeEnabled(context: Context): Boolean {
|
||||||
return try {
|
return try {
|
||||||
val settingsFile = File(context.filesDir, "vd_mmkv/VENDETTA_SETTINGS")
|
val settingsFile = File(context.filesDir, "vd_mmkv/VENDETTA_SETTINGS")
|
||||||
if (!settingsFile.exists()) return false
|
if (!settingsFile.exists()) return false
|
||||||
|
|
||||||
val json = JSONObject(settingsFile.readText())
|
val json = JSONObject(settingsFile.readText())
|
||||||
json.optJSONObject("safeMode")?.optBoolean("enabled", false) ?: false
|
json.optJSONObject("safeMode")?.optBoolean("enabled", false) ?: false
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
@ -153,26 +134,26 @@ class LogBoxModule : Module() {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toggleSafeMode(context: Context) {
|
private fun toggleSafeMode(context: Context) {
|
||||||
try {
|
try {
|
||||||
val settingsFile = File(context.filesDir, "vd_mmkv/VENDETTA_SETTINGS")
|
val settingsFile = File(context.filesDir, "vd_mmkv/VENDETTA_SETTINGS")
|
||||||
val themeFile = File(context.filesDir, "vd_mmkv/VENDETTA_THEMES")
|
val themeFile = File(context.filesDir, "vd_mmkv/VENDETTA_THEMES")
|
||||||
|
|
||||||
settingsFile.parentFile?.mkdirs()
|
settingsFile.parentFile?.mkdirs()
|
||||||
|
|
||||||
val settings = if (settingsFile.exists()) {
|
val settings = if (settingsFile.exists()) {
|
||||||
JSONObject(settingsFile.readText())
|
JSONObject(settingsFile.readText())
|
||||||
} else {
|
} else {
|
||||||
JSONObject()
|
JSONObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
val safeMode = settings.optJSONObject("safeMode") ?: JSONObject()
|
val safeMode = settings.optJSONObject("safeMode") ?: JSONObject()
|
||||||
val currentState = safeMode.optBoolean("enabled", false)
|
val currentState = safeMode.optBoolean("enabled", false)
|
||||||
val newState = !currentState
|
val newState = !currentState
|
||||||
|
|
||||||
safeMode.put("enabled", newState)
|
safeMode.put("enabled", newState)
|
||||||
|
|
||||||
if (newState && themeFile.exists()) {
|
if (newState && themeFile.exists()) {
|
||||||
val theme = JSONObject(themeFile.readText())
|
val theme = JSONObject(themeFile.readText())
|
||||||
val themeId = theme.optString("id")
|
val themeId = theme.optString("id")
|
||||||
|
|
@ -181,12 +162,11 @@ class LogBoxModule : Module() {
|
||||||
themeFile.delete()
|
themeFile.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.put("safeMode", safeMode)
|
settings.put("safeMode", safeMode)
|
||||||
settingsFile.writeText(settings.toString())
|
settingsFile.writeText(settings.toString())
|
||||||
|
|
||||||
Toast.makeText(context, "Safe Mode ${if (newState) "Enabled" else "Disabled"}", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Safe Mode ${if (newState) "Enabled" else "Disabled"}", Toast.LENGTH_SHORT).show()
|
||||||
|
|
||||||
reloadApp()
|
reloadApp()
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
@ -194,40 +174,20 @@ class LogBoxModule : Module() {
|
||||||
showError(context, "Failed to toggle safe mode", e.message)
|
showError(context, "Failed to toggle safe mode", e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refetchBundle(context: Context) {
|
|
||||||
try {
|
|
||||||
val pyoncordDir = getPyoncordDirectory(context)
|
|
||||||
val bundleFile = File(pyoncordDir, "bundle.js")
|
|
||||||
val backupFile = File(pyoncordDir, "bundle.js.backup")
|
|
||||||
|
|
||||||
if (bundleFile.exists()) {
|
|
||||||
backupFile.delete()
|
|
||||||
bundleFile.renameTo(backupFile)
|
|
||||||
Log.e("Bundle moved to backup")
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadApp()
|
|
||||||
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("Error refetching bundle: ${e.message}")
|
|
||||||
showError(context, "Failed to refetch bundle", e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resetBundle(context: Context) {
|
private fun resetBundle(context: Context) {
|
||||||
try {
|
try {
|
||||||
val pyoncordDir = getPyoncordDirectory(context)
|
val pyoncordDir = getPyoncordDirectory(context)
|
||||||
val bundleFile = File(pyoncordDir, "bundle.js")
|
val bundleFile = File(pyoncordDir, "bundle.js")
|
||||||
|
val backupFile = File(pyoncordDir, "bundle.js.backup")
|
||||||
val configFile = File(pyoncordDir, "loader_config.json")
|
val configFile = File(pyoncordDir, "loader_config.json")
|
||||||
|
|
||||||
bundleFile.delete()
|
bundleFile.delete()
|
||||||
|
backupFile.delete()
|
||||||
// Reset config
|
|
||||||
if (configFile.exists()) {
|
if (configFile.exists()) {
|
||||||
val config = JSONObject(configFile.readText())
|
val config = JSONObject(configFile.readText())
|
||||||
config.put("customLoadUrlEnabled", false)
|
config.put("customLoadUrlEnabled", false)
|
||||||
config.put("customLoadUrl", "http://localhost:4040/kettu.js")
|
|
||||||
configFile.writeText(config.toString())
|
configFile.writeText(config.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,100 +197,7 @@ class LogBoxModule : Module() {
|
||||||
showError(context, "Failed to reset bundle", e.message)
|
showError(context, "Failed to reset bundle", e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadCustomBundle(context: Context) {
|
|
||||||
val input = EditText(context).apply {
|
|
||||||
hint = "http://localhost:4040/kettu.js"
|
|
||||||
inputType = android.text.InputType.TYPE_TEXT_VARIATION_URI
|
|
||||||
}
|
|
||||||
|
|
||||||
AlertDialog.Builder(context)
|
|
||||||
.setTitle("Load Custom Bundle")
|
|
||||||
.setMessage("Enter the URL for your custom bundle:")
|
|
||||||
.setView(input)
|
|
||||||
.setPositiveButton("Load") { _, _ ->
|
|
||||||
val urlString = input.text.toString().trim()
|
|
||||||
if (urlString.isEmpty()) {
|
|
||||||
showError(context, "Invalid URL", "Please enter a URL")
|
|
||||||
return@setPositiveButton
|
|
||||||
}
|
|
||||||
|
|
||||||
validateAndLoadBundle(context, urlString)
|
|
||||||
}
|
|
||||||
.setNegativeButton("Cancel", null)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun validateAndLoadBundle(context: Context, urlString: String) {
|
|
||||||
scope.launch(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
val url = URL(urlString)
|
|
||||||
if (url.protocol != "http" && url.protocol != "https") {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
showError(context, "Invalid URL", "URL must start with http:// or https://")
|
|
||||||
}
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val connection = url.openConnection() as HttpURLConnection
|
|
||||||
connection.requestMethod = "GET"
|
|
||||||
connection.connectTimeout = 10000
|
|
||||||
connection.readTimeout = 10000
|
|
||||||
|
|
||||||
val responseCode = connection.responseCode
|
|
||||||
if (responseCode != 200) {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
showError(context, "Error", "Server returned error $responseCode")
|
|
||||||
}
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val contentType = connection.contentType
|
|
||||||
if (contentType != null && !contentType.contains("javascript") && !contentType.contains("text")) {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
showError(context, "Invalid Content", "URL must point to a JavaScript file")
|
|
||||||
}
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save custom bundle URL
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
setCustomBundleURL(context, urlString)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
showError(context, "Connection Error", "Could not reach URL: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setCustomBundleURL(context: Context, url: String) {
|
|
||||||
try {
|
|
||||||
val pyoncordDir = getPyoncordDirectory(context)
|
|
||||||
val configFile = File(pyoncordDir, "loader_config.json")
|
|
||||||
|
|
||||||
val config = if (configFile.exists()) {
|
|
||||||
JSONObject(configFile.readText())
|
|
||||||
} else {
|
|
||||||
JSONObject()
|
|
||||||
}
|
|
||||||
|
|
||||||
config.put("customLoadUrlEnabled", true)
|
|
||||||
config.put("customLoadUrl", url)
|
|
||||||
configFile.writeText(config.toString())
|
|
||||||
|
|
||||||
File(pyoncordDir, "bundle.js").delete()
|
|
||||||
|
|
||||||
Toast.makeText(context, "Custom bundle set", Toast.LENGTH_SHORT).show()
|
|
||||||
|
|
||||||
reloadApp()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("Error setting custom bundle URL: ${e.message}")
|
|
||||||
showError(context, "Failed to save configuration", e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getPyoncordDirectory(context: Context): File {
|
private fun getPyoncordDirectory(context: Context): File {
|
||||||
val dir = File(context.filesDir, "pyoncord")
|
val dir = File(context.filesDir, "pyoncord")
|
||||||
if (!dir.exists()) {
|
if (!dir.exists()) {
|
||||||
|
|
@ -338,7 +205,7 @@ class LogBoxModule : Module() {
|
||||||
}
|
}
|
||||||
return dir
|
return dir
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showError(context: Context, title: String, message: String?) {
|
private fun showError(context: Context, title: String, message: String?) {
|
||||||
AlertDialog.Builder(context)
|
AlertDialog.Builder(context)
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
|
|
@ -346,7 +213,4 @@ class LogBoxModule : Module() {
|
||||||
.setPositiveButton("OK", null)
|
.setPositiveButton("OK", null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onContext(context: Context) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Loading…
Add table
Reference in a new issue