x1ongzhu 1 tahun lalu
induk
melakukan
877a738438
26 mengubah file dengan 697 tambahan dan 870 penghapusan
  1. 1 1
      app/src/main/AndroidManifest.xml
  2. 7 8
      app/src/main/java/com/example/modifier/Frida.kt
  3. 50 297
      app/src/main/java/com/example/modifier/Global.kt
  4. 0 42
      app/src/main/java/com/example/modifier/GlobalState.kt
  5. 2 2
      app/src/main/java/com/example/modifier/MainActivity.kt
  6. 0 83
      app/src/main/java/com/example/modifier/MsgAppModel.kt
  7. 2 0
      app/src/main/java/com/example/modifier/MyApplication.kt
  8. 0 76
      app/src/main/java/com/example/modifier/Utils.java
  9. 8 0
      app/src/main/java/com/example/modifier/constants/Miscellaneous.kt
  10. 3 1
      app/src/main/java/com/example/modifier/constants/Packages.kt
  11. 0 1
      app/src/main/java/com/example/modifier/data/SocketClient.kt
  12. 0 44
      app/src/main/java/com/example/modifier/model/Backup.java
  13. 1 1
      app/src/main/java/com/example/modifier/model/SimInfo.kt
  14. 4 5
      app/src/main/java/com/example/modifier/receiver/MyReceiver.kt
  15. 0 3
      app/src/main/java/com/example/modifier/service/Helpers.kt
  16. 120 102
      app/src/main/java/com/example/modifier/service/ModifierService.kt
  17. 2 1
      app/src/main/java/com/example/modifier/ui/login/LoginActivity.kt
  18. 3 4
      app/src/main/java/com/example/modifier/ui/login/LoginViewModel.kt
  19. 12 14
      app/src/main/java/com/example/modifier/ui/settings/SettingsFragment.kt
  20. 7 7
      app/src/main/java/com/example/modifier/ui/utils/UtilsFragment.kt
  21. 41 0
      app/src/main/java/com/example/modifier/utils/Clash.kt
  22. 80 0
      app/src/main/java/com/example/modifier/utils/GoogleMessage.kt
  23. 111 0
      app/src/main/java/com/example/modifier/utils/Package.kt
  24. 143 138
      app/src/main/java/com/example/modifier/utils/RcsHackTool.kt
  25. 43 0
      app/src/main/java/com/example/modifier/utils/Root.kt
  26. 57 40
      app/src/main/java/com/example/modifier/utils/System.kt

+ 1 - 1
app/src/main/AndroidManifest.xml

@@ -40,7 +40,7 @@
         android:usesCleartextTraffic="true"
         tools:targetApi="31">
         <receiver
-            android:name=".MyReceiver"
+            android:name=".receiver.MyReceiver"
             android:enabled="true"
             android:exported="true">
 

+ 7 - 8
app/src/main/java/com/example/modifier/Frida.kt

@@ -2,7 +2,8 @@ package com.example.modifier
 
 import android.util.Log
 import androidx.core.content.ContextCompat
-import com.example.modifier.utils.RcsHackTool
+import com.example.modifier.utils.createFakeSms
+import com.example.modifier.utils.shellRun
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
@@ -10,7 +11,6 @@ import kotlinx.coroutines.withContext
 import org.apache.commons.io.FileUtils
 import org.apache.commons.io.IOUtils
 import java.io.File
-import java.io.InterruptedIOException
 import java.util.Base64
 
 class Frida {
@@ -18,7 +18,6 @@ class Frida {
     companion object {
         var p: Process? = null
         var script: File? = null
-        private val TAG = "FridaScript"
 
         suspend fun start() {
             if (p != null) {
@@ -43,7 +42,7 @@ class Frida {
             withContext(Dispatchers.IO) {
                 script = File.createTempFile("script", ".js")
                 FileUtils.writeStringToFile(script, scriptContent, "UTF-8")
-                Log.i(TAG, "start: $binPath -p $pid  -s $script")
+                Log.i(com.example.modifier.TAG, "start: $binPath -p $pid  -s $script")
 
                 p = Runtime.getRuntime().exec("su -M")
                 p!!.outputStream.bufferedWriter().use {
@@ -61,7 +60,7 @@ class Frida {
                                 .bufferedReader()
                                 .useLines { lines ->
                                     lines.forEach {
-                                        Log.i(TAG, it)
+                                        Log.i(com.example.modifier.TAG, it)
                                     }
                                 }
                         } catch (e: Exception) {
@@ -74,7 +73,7 @@ class Frida {
                                 .bufferedReader()
                                 .useLines { lines ->
                                     lines.forEach {
-                                        Log.e(TAG, it)
+                                        Log.e(com.example.modifier.TAG, it)
                                     }
                                 }
                         } catch (e: Exception) {
@@ -107,7 +106,7 @@ suspend fun sendSmsFrida(sender: String, msg: String) {
         Utils.copyAssetFolder(context.assets, "bin", File(dataDir, "bin").path)
         val binPath = File(dataDir, "bin/frida-inject-16.3.3-android-arm64").path
         val pduBase64 =
-            String(Base64.getEncoder().encode(RcsHackTool.createFakeSms(sender, msg)))
+            String(Base64.getEncoder().encode(createFakeSms(sender, msg)))
         Log.i("Modifier", "pduBase64: $pduBase64")
         val script = IOUtils.toString(context.assets.open("scripts/sms.js"), "UTF-8")
             .replace("{pduBase64}", pduBase64)
@@ -118,7 +117,7 @@ suspend fun sendSmsFrida(sender: String, msg: String) {
             FileUtils.writeStringToFile(tmpFile, script, "UTF-8")
         }
 
-        val pid = Utils.runAsRoot("pidof com.android.phone").trim()
+        val pid = shellRun("pidof com.android.phone").first.trim()
         if (!Regex("[0-9]+").matches(pid)) {
             return
         }

+ 50 - 297
app/src/main/java/com/example/modifier/Global.kt

@@ -10,22 +10,21 @@ import android.os.Build
 import android.telephony.SubscriptionManager
 import android.util.Log
 import androidx.core.content.ContextCompat
-import com.example.modifier.constants.CMD_HOME
 import com.example.modifier.constants.CMD_MESSAGING_APP
-import com.example.modifier.constants.CMD_START_PLAY_STORE
 import com.example.modifier.constants.PACKAGE_GMS
 import com.example.modifier.constants.PACKAGE_GSF
 import com.example.modifier.constants.PACKAGE_MESSAGING
 import com.example.modifier.data.BackupItem
 import com.example.modifier.data.BackupItemDao
-import com.example.modifier.extension.clear
 import com.example.modifier.extension.disable
 import com.example.modifier.extension.enable
-import com.example.modifier.extension.kill
-import com.example.modifier.extension.resume
-import com.example.modifier.extension.suspend
-import com.example.modifier.model.TelephonyConfig
+import com.example.modifier.model.SimInfo
 import com.example.modifier.serializer.Json
+import com.example.modifier.utils.clearConv
+import com.example.modifier.utils.hasPermission
+import com.example.modifier.utils.resumePackage
+import com.example.modifier.utils.shellRun
+import com.example.modifier.utils.suspendPackage
 import com.google.gson.Gson
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.delay
@@ -42,7 +41,7 @@ import java.util.Optional
 
 object Global {
     @JvmField
-    var telephonyConfig: TelephonyConfig = TelephonyConfig(
+    var simInfo: SimInfo = SimInfo(
         number = "",
         mcc = "",
         mnc = "",
@@ -56,8 +55,6 @@ object Global {
         carrierName = "T-Mobile"
     )
 
-    private const val TAG = "Modifier"
-
     @JvmStatic
     fun load() {
         val context = Utils.getContext()
@@ -66,48 +63,34 @@ object Global {
             if (file.exists()) {
                 val gson = Gson()
                 val json = FileUtils.readFileToString(file, "UTF-8")
-                telephonyConfig = gson.fromJson(json, TelephonyConfig::class.java)
+                simInfo = gson.fromJson(json, SimInfo::class.java)
             }
         } catch (e: Exception) {
             e.printStackTrace()
         }
     }
 
-    @JvmStatic
-    val servers: MutableSet<String>
-        get() {
-            val context = Utils.getContext()
-            val defServers: MutableSet<String> = HashSet()
-            defServers.add("http://47.98.225.28")
-            defServers.add("http://8.149.128.251")
-            defServers.add("http://192.168.6.215:3000")
-            defServers.add("http://192.168.50.135:3000")
-            val prefs =
-                context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE)
-            return defServers
-        }
-
     @JvmStatic
     fun save() {
         val context = Utils.getContext()
         val file = File(ContextCompat.getDataDir(context), "config.json")
-        FileUtils.write(file, Json.encodeToString(telephonyConfig), "UTF-8")
+        FileUtils.write(file, Json.encodeToString(simInfo), "UTF-8")
     }
 
     @SuppressLint("MissingPermission")
     @JvmStatic
-    suspend fun save(telephonyConfig: TelephonyConfig, suspend: Boolean? = true) {
-        Global.telephonyConfig.mcc = telephonyConfig.mcc
-        Global.telephonyConfig.mnc = telephonyConfig.mnc
-        Global.telephonyConfig.number = telephonyConfig.number
-        Global.telephonyConfig.country = telephonyConfig.country
-        Global.telephonyConfig.areaCode = telephonyConfig.areaCode
-        Global.telephonyConfig.iccid = telephonyConfig.iccid
-        Global.telephonyConfig.imei = telephonyConfig.imei
-        Global.telephonyConfig.imsi = telephonyConfig.imsi
-        Global.telephonyConfig.available = telephonyConfig.available
-        Global.telephonyConfig.carrierId = telephonyConfig.carrierId
-        Global.telephonyConfig.carrierName = telephonyConfig.carrierName
+    suspend fun save(simInfo: SimInfo, suspend: Boolean? = true) {
+        Global.simInfo.mcc = simInfo.mcc
+        Global.simInfo.mnc = simInfo.mnc
+        Global.simInfo.number = simInfo.number
+        Global.simInfo.country = simInfo.country
+        Global.simInfo.areaCode = simInfo.areaCode
+        Global.simInfo.iccid = simInfo.iccid
+        Global.simInfo.imei = simInfo.imei
+        Global.simInfo.imsi = simInfo.imsi
+        Global.simInfo.available = simInfo.available
+        Global.simInfo.carrierId = simInfo.carrierId
+        Global.simInfo.carrierName = simInfo.carrierName
 
         try {
             if (suspend == true) {
@@ -116,21 +99,21 @@ object Global {
             save()
 
             shellRun(
-                "setprop persist.spoof.mcc ${telephonyConfig.mcc}",
-                "setprop persist.spoof.mnc ${telephonyConfig.mnc}",
-                "setprop persist.spoof.number ${telephonyConfig.number}",
-                "setprop persist.spoof.country ${telephonyConfig.country}",
-                "setprop persist.spoof.iccid ${telephonyConfig.iccid}",
-                "setprop persist.spoof.imei ${telephonyConfig.imei}",
-                "setprop persist.spoof.imsi ${telephonyConfig.imsi}",
+                "setprop persist.spoof.mcc ${simInfo.mcc}",
+                "setprop persist.spoof.mnc ${simInfo.mnc}",
+                "setprop persist.spoof.number ${simInfo.number}",
+                "setprop persist.spoof.country ${simInfo.country}",
+                "setprop persist.spoof.iccid ${simInfo.iccid}",
+                "setprop persist.spoof.imei ${simInfo.imei}",
+                "setprop persist.spoof.imsi ${simInfo.imsi}",
                 "setprop persist.spoof.carrier.id ${
-                    telephonyConfig.carrierId.replace(
+                    simInfo.carrierId.replace(
                         "^\\W*\$".toRegex(),
                         "''"
                     )
                 }",
                 "setprop persist.spoof.carrier.name ${
-                    telephonyConfig.carrierName.replace(
+                    simInfo.carrierName.replace(
                         "^\\W*\$".toRegex(),
                         "''"
                     )
@@ -148,7 +131,7 @@ object Global {
                     if (info != null) {
                         val mcc = info.mccString
                         val mnc = info.mncString
-                        Log.i(TAG, "mccmnc spoofed: $mcc$mnc")
+                        Log.i(com.example.modifier.TAG, "mccmnc spoofed: $mcc$mnc")
                     }
                 }
             }
@@ -184,7 +167,7 @@ object Global {
                 .firstOrNull()
                 ?.let {
                     save(
-                        TelephonyConfig(
+                        SimInfo(
                             number = it,
                             mcc = "310",
                             mnc = "150",
@@ -210,7 +193,7 @@ object Global {
                 .firstOrNull()
                 ?.let {
                     save(
-                        TelephonyConfig(
+                        SimInfo(
                             number = it,
                             mcc = "310",
                             mnc = "240",
@@ -228,62 +211,6 @@ object Global {
         }
     }
 
-    @JvmStatic
-    suspend fun clear(gsf: Boolean, gms: Boolean, sms: Boolean) {
-        try {
-            suspendPackage(PACKAGE_GSF, PACKAGE_GMS, PACKAGE_MESSAGING)
-            val cmds: MutableList<String> = ArrayList()
-            // suspend
-            if (gsf) {
-                cmds.add(PACKAGE_GSF.suspend())
-                cmds.add(PACKAGE_GSF.kill())
-                cmds.add("echo 'gsf suspended'")
-            }
-            if (gms) {
-                cmds.add(PACKAGE_GMS.suspend())
-                cmds.add(PACKAGE_GMS.kill())
-                cmds.add("echo 'gms suspended'")
-            }
-            if (sms) {
-                cmds.add(PACKAGE_MESSAGING.suspend())
-                cmds.add(PACKAGE_MESSAGING.kill())
-                cmds.add("echo 'sms suspended'")
-            }
-            cmds.add("sleep 1")
-            // clear
-            if (gsf) {
-                cmds.add(PACKAGE_GSF.clear())
-                cmds.add("echo 'cleared gsf'")
-            }
-            if (gms) {
-                cmds.add(PACKAGE_GMS.clear())
-                cmds.add("echo 'cleared gms'")
-            }
-            if (sms) {
-                cmds.add(PACKAGE_MESSAGING.clear())
-                cmds.add("echo 'cleared sms'")
-            }
-            cmds.add("sleep 1")
-            // unsuspend
-            if (gsf) {
-                cmds.add(PACKAGE_GSF.resume())
-                cmds.add("echo 'gsf unsuspend'")
-            }
-            if (gms) {
-                cmds.add(PACKAGE_GMS.resume())
-                cmds.add("echo 'gms unsuspend'")
-            }
-            if (sms) {
-                cmds.add(PACKAGE_MESSAGING.resume())
-                cmds.add("echo 'sms unsuspend'")
-            }
-            cmds.add("sleep 1")
-            shellRun(*cmds.toTypedArray<String>())
-        } catch (e: Exception) {
-            e.printStackTrace()
-        }
-    }
-
 //    @JvmStatic
 //    fun stop(gsf: Boolean? = false, gms: Boolean? = false, sms: Boolean? = false) {
 //        try {
@@ -307,27 +234,7 @@ object Global {
 //        }
 //    }
 
-    @JvmStatic
-    suspend fun suspendPackage(vararg packages: String) {
-        packages.forEach {
-            shellRun(
-                it.suspend(),
-                it.kill()
-            )
-            delay(1000)
-        }
-    }
 
-    @JvmStatic
-    suspend fun resumePackage(vararg packages: String) {
-        packages.forEach {
-            shellRun(
-                it.resume(),
-                it.kill()
-            )
-            delay(1000)
-        }
-    }
 
     fun sqlite3path(): String {
         val context = Utils.getContext()
@@ -347,90 +254,6 @@ object Global {
         return file.path
     }
 
-    fun copyDB(): File {
-        val context = Utils.getContext()
-        val dataDir = ContextCompat.getDataDir(context)
-        val dbDir = File(dataDir, "providerDB")
-        if (!dbDir.exists()) {
-            Utils.copyAssetFolder(context.assets, "providerDB", dbDir.path)
-        }
-        return dbDir
-    }
-
-    @JvmStatic
-    suspend fun clearConv() {
-        val context = Utils.getContext()
-        if (context.getSharedPreferences("settings", Context.MODE_PRIVATE)
-                .getBoolean("do_not_clean", false)
-        ) {
-            return
-        }
-        val model = Build.MODEL.replace(" ", "_")
-        if (!(model == "SM-F707N" || model == "SM-F711B" || model == "Pixel_5")) {
-            return
-        }
-        try {
-            val sqlite3 = sqlite3path()
-            suspendPackage(PACKAGE_MESSAGING)
-            val dataDir = ContextCompat.getDataDir(context)
-            val providerDir = File(dataDir, "telephony_provider/$model")
-            if (!providerDir.exists()) {
-                Utils.copyAssetFolder(context.assets, "telephony_provider/$model", providerDir.path)
-            }
-
-            val cmds = mutableListOf<String>()
-            fun copyToData(file: File) {
-                val relative = file.path.replace(providerDir.path, "")
-                if (file.isDirectory) {
-                    file.listFiles()?.forEach {
-                        copyToData(it)
-                    }
-                } else {
-                    val dest = File("/data/data/com.android.providers.telephony/$relative")
-                    cmds.addAll(
-                        listOf(
-                            "cp -f ${file.path} ${dest.path}",
-                            "chmod 660 ${dest.path}",
-                            "chown radio:radio ${dest.path}"
-                        )
-                    )
-                }
-            }
-            copyToData(providerDir)
-            shellRun(
-                "$sqlite3 /data/data/com.google.android.apps.messaging/databases/bugle_db \"DELETE FROM conversations;\"",
-                "$sqlite3 /data/data/com.google.android.apps.messaging/databases/bugle_db \"DELETE FROM messages;\"",
-                *cmds.toTypedArray(),
-            )
-            resumePackage(PACKAGE_MESSAGING)
-        } catch (e: Exception) {
-            e.printStackTrace()
-        }
-    }
-
-    @JvmStatic
-    suspend fun resetAll() {
-        try {
-            clearConv()
-            shellRun(
-                PACKAGE_MESSAGING.suspend(),
-                PACKAGE_MESSAGING.kill(),
-                PACKAGE_MESSAGING.clear(),
-                PACKAGE_GSF.clear(),
-                PACKAGE_GMS.clear(),
-                "sleep 1",
-                CMD_START_PLAY_STORE,
-                "sleep 1",
-                CMD_HOME,
-                "sleep 10",
-                PACKAGE_GMS.clear(),
-                PACKAGE_MESSAGING.resume(),
-                CMD_MESSAGING_APP
-            )
-        } catch (e: Exception) {
-            e.printStackTrace()
-        }
-    }
 
     @JvmStatic
     suspend fun killPhoneProcess(force: Boolean = false): Boolean {
@@ -444,25 +267,25 @@ object Global {
                 repeat(3) {
                     val pid = shellRun("pidof com.android.phone").component1().trim()
                     if (!Regex("[0-9]+").matches(pid)) {
-                        Log.e(TAG, "killPhoneProcess: pid not found")
+                        Log.e(com.example.modifier.TAG, "killPhoneProcess: pid not found")
                         return true
                     }
-                    Log.i(TAG, "killPhoneProcess: pid=$pid")
+                    Log.i(com.example.modifier.TAG, "killPhoneProcess: pid=$pid")
                     shellRun("kill -9 $pid")
                     delay(1000)
 
                     val pidNew = shellRun("pidof com.android.phone").component1().trim()
                     if (pidNew == pid) {
-                        Log.e(TAG, "killPhoneProcess: failed to kill phone process")
+                        Log.e(com.example.modifier.TAG, "killPhoneProcess: failed to kill phone process")
                     } else {
-                        Log.i(TAG, "killPhoneProcess: success, new pid: $pidNew")
+                        Log.i(com.example.modifier.TAG, "killPhoneProcess: success, new pid: $pidNew")
                         shellRun("kill -9 $pid", "setprop phonekilled yes")
                         return true
                     }
                 }
             }
         } catch (e: Exception) {
-            Log.e(TAG, "Error Kill Phone", e)
+            Log.e(com.example.modifier.TAG, "Error Kill Phone", e)
         }
         return false
     }
@@ -487,15 +310,6 @@ object Global {
         return prefix + RandomStringUtils.randomNumeric(20 - prefix.length)
     }
 
-    @JvmStatic
-    suspend fun rebooted(): Boolean {
-        if (shellRun("getprop rebooted").component1().contains("yes")) {
-            return false
-        }
-        shellRun("setprop rebooted yes")
-        return true
-    }
-
     @SuppressLint("SdCardPath")
     @JvmStatic
     suspend fun backup(
@@ -557,24 +371,24 @@ object Global {
 
         val backup = BackupItem(
             createdAt = Date().time,
-            number = telephonyConfig.number,
-            code = telephonyConfig.areaCode,
-            country = telephonyConfig.country,
-            mcc = telephonyConfig.mcc,
-            mnc = telephonyConfig.mnc,
-            imei = telephonyConfig.imei,
-            imsi = telephonyConfig.imsi,
-            iccid = telephonyConfig.iccid,
+            number = simInfo.number,
+            code = simInfo.areaCode,
+            country = simInfo.country,
+            mcc = simInfo.mcc,
+            mnc = simInfo.mnc,
+            imei = simInfo.imei,
+            imsi = simInfo.imsi,
+            iccid = simInfo.iccid,
             path = dest.path,
             sendCount = sendCount,
             lastUse = Date().time,
             type = type,
             fresh = fresh,
-            carrierId = Optional.ofNullable(telephonyConfig.carrierId).orElse("1"),
-            carrierName = Optional.ofNullable(telephonyConfig.carrierName).orElse("T-Mobile")
+            carrierId = Optional.ofNullable(simInfo.carrierId).orElse("1"),
+            carrierName = Optional.ofNullable(simInfo.carrierName).orElse("T-Mobile")
         )
 
-        backupItemDao.findBackupForNumber(telephonyConfig.country, telephonyConfig.number)?.let {
+        backupItemDao.findBackupForNumber(simInfo.country, simInfo.number)?.let {
             File(it.path).deleteRecursively()
             backupItemDao.delete(it)
             backup.sendCount += it.sendCount
@@ -589,7 +403,7 @@ object Global {
     @JvmStatic
     suspend fun restore(backup: BackupItem): Boolean {
         save(
-            TelephonyConfig(
+            SimInfo(
                 number = backup.number,
                 mcc = backup.mcc,
                 mnc = backup.mnc,
@@ -649,67 +463,6 @@ object Global {
         return true
     }
 
-    @JvmStatic
-    suspend fun optimize() {
-        val context = Utils.getContext()
-        val packageManager = context.packageManager
-        val info = packageManager.getApplicationInfo("com.google.android.gms", 0)
-
-        shellRun(
-            "dumpsys deviceidle whitelist +com.google.android.apps.messaging",
-            "dumpsys deviceidle whitelist +${BuildConfig.APPLICATION_ID}",
-            "cmd netpolicy add restrict-background-blacklist ${info.uid}",
-            "settings put global window_animation_scale 0",
-            "settings put global transition_animation_scale 0",
-            "settings put global animator_duration_scale 0"
-        )
-    }
-
-    @JvmStatic
-    suspend fun setupSystem() {
-        syncTime()
-        optimize()
-    }
-
-    @JvmStatic
-    fun isClashInstalled(context: Context): Boolean {
-        val packageManager = context.packageManager
-        return try {
-            packageManager.getPackageInfo("com.github.metacubex.clash.meta", 0)
-            true
-        } catch (e: Exception) {
-            false
-        }
-    }
-
-    @JvmStatic
-    fun stopClash() {
-        try {
-            val context = Utils.getContext()
-            val intent = Intent()
-            intent.action = "com.github.metacubex.clash.meta.action.STOP_CLASH"
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-            context.startActivity(intent)
-        } catch (e: Exception) {
-            e.printStackTrace()
-        }
-    }
-
-    @JvmStatic
-    fun changeClashProfile(name: String, base64: String) {
-        try {
-            val context = Utils.getContext()
-            val intent = Intent()
-            intent.action = "com.github.metacubex.clash.meta.action.USE_PROFILE"
-            intent.putExtra("base64", base64)
-            intent.putExtra("name", name)
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-            context.startActivity(intent)
-        } catch (e: Exception) {
-            e.printStackTrace()
-        }
-    }
-
     @JvmStatic
     fun restartModifier() {
         val context = Utils.getContext()

+ 0 - 42
app/src/main/java/com/example/modifier/GlobalState.kt

@@ -1,42 +0,0 @@
-package com.example.modifier
-
-class GlobalState {
-    companion object {
-        var number: String = ""
-        var country: String = ""
-        var areaCode: String = ""
-        var mcc: String = ""
-        var mnc: String = ""
-        var imei: String = ""
-        var iccid: String = ""
-        var imsi: String = ""
-
-        var appCount: Int = 0
-        var appSuccess: Int = 0
-
-        var numberCount: Int = 0
-        var numberSuccess: Int = 0
-
-        var sessionCount: Int = 0
-        var sessionSuccess: Int = 0
-
-        fun increment(success: Boolean = false) {
-            appCount++
-            if (success) {
-                appSuccess++
-            }
-            numberCount++
-            if (success) {
-                numberSuccess++
-            }
-            sessionCount++
-            if (success) {
-                sessionSuccess++
-            }
-        }
-
-
-    }
-
-
-}

+ 2 - 2
app/src/main/java/com/example/modifier/MainActivity.kt

@@ -2,7 +2,6 @@ package com.example.modifier
 
 import android.content.DialogInterface
 import android.content.Intent
-import android.os.Build
 import android.os.Bundle
 import android.provider.Settings
 import androidx.activity.enableEdgeToEdge
@@ -10,6 +9,8 @@ import androidx.appcompat.app.AppCompatActivity
 import androidx.navigation.fragment.NavHostFragment
 import androidx.navigation.ui.NavigationUI.setupWithNavController
 import com.example.modifier.databinding.ActivityMainBinding
+import com.example.modifier.utils.enableAccessibility
+import com.example.modifier.utils.hasRootAccess
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -39,7 +40,6 @@ class MainActivity : AppCompatActivity() {
                     delay(1000);
                     finish();
                 }
-                Global.setupSystem()
             } else {
                 withContext(Dispatchers.Main) {
                     MaterialAlertDialogBuilder(this@MainActivity)

+ 0 - 83
app/src/main/java/com/example/modifier/MsgAppModel.kt

@@ -1,83 +0,0 @@
-package com.example.modifier
-
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.liveData
-import com.example.modifier.enums.RcsConfigureState
-import com.example.modifier.service.ModifierService
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
-import org.apache.commons.collections4.queue.CircularFifoQueue
-
-class MsgAppModel(private val modifierService: ModifierService) {
-
-    private val rcsConfigureState = MutableLiveData(RcsConfigureState.CONFIGURED)
-    private val logcat = liveData(Dispatchers.IO) {
-        try {
-            val logs = CircularFifoQueue<String>(128)
-            val p = Runtime.getRuntime().exec("su")
-            p.outputStream.bufferedWriter().use { writer ->
-                writer.write("logcat -c")
-                writer.newLine()
-                writer.flush()
-                writer.write("logcat BugleRcsEngine:D *:S -v time")
-                writer.newLine()
-                writer.flush()
-            }
-            p.inputStream
-                .bufferedReader()
-                .useLines { lines ->
-                    lines.forEach { line ->
-                        if (line.contains("destState=CheckPreconditionsState")) {
-                            rcsConfigureState.postValue(RcsConfigureState.NOT_CONFIGURED)
-                        } else if (line.contains("destState=ReadyState")) {
-                            rcsConfigureState.postValue(RcsConfigureState.READY)
-                        } else if (line.contains("destState=WaitingForOtpState")) {
-                            rcsConfigureState.postValue(RcsConfigureState.WAITING_FOR_OTP)
-                        } else if (line.contains("destState=VerifyOtpState")) {
-                            rcsConfigureState.postValue(RcsConfigureState.VERIFYING_OTP)
-                        } else if (line.contains("destState=ConfiguredState")) {
-                            rcsConfigureState.postValue(RcsConfigureState.CONFIGURED)
-                        } else if (line.contains("destState=WaitingForRcsDefaultOnState")) {
-                            rcsConfigureState.postValue(RcsConfigureState.WAITING_FOR_DEFAULT_ON)
-                        } else if (line.contains("destState=RetryState")) {
-                            rcsConfigureState.postValue(RcsConfigureState.RETRY)
-                        }
-                        Regex("(?<time>\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}.\\d{3}) I/BugleRcsEngine\\(\\W*\\d+\\): (?<log>.*)").matchEntire(
-                            line
-                        )?.apply {
-                            val time = groups["time"]?.value?.dropLast(4)
-                            val log = groups["log"]?.value
-                                ?.replace(Regex("\\[\\w+-\\w+-\\w+-\\w+-\\w+]"), "")
-                                ?.replace(Regex("\\[CONTEXT.*]"), "")
-                                ?.trim()
-
-                            if (time != null && log != null) {
-                                if (log.contains("destState=")) {
-                                    logs.add("$time: $log")
-                                    emit(logs.joinToString("\n"))
-                                    delay(100)
-                                    emit(logs.joinToString("\n"))
-                                }
-                            }
-                        }
-                    }
-                }
-        } catch (e: Exception) {
-            e.printStackTrace()
-        }
-    }
-
-    fun isRcsSwitchOn(): Boolean {
-        val nodes = modifierService.getNodes()
-        nodes.find {
-            it.text.contains("Turn on RCS chats") || it.text.contains("开启 RCS 聊天功能")
-        }?.let { node ->
-            modifierService.getNodes(node)
-                .find { "com.google.android.apps.messaging:id/switchWidget" == it.viewIdResourceName }
-                ?.let {
-                    return true
-                }
-        }
-        return false
-    }
-}

+ 2 - 0
app/src/main/java/com/example/modifier/MyApplication.kt

@@ -5,6 +5,8 @@ import com.example.modifier.data.AppContainer
 import com.example.modifier.data.AppDataContainer
 import dagger.hilt.android.HiltAndroidApp
 
+const val TAG = "Modifier"
+
 @HiltAndroidApp
 class MyApplication : Application() {
 

+ 0 - 76
app/src/main/java/com/example/modifier/Utils.java

@@ -1,49 +1,31 @@
 package com.example.modifier;
 
-import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accounts.AccountManager;
 import android.accounts.AuthenticatorDescription;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.accessibility.AccessibilityManager;
 
-import com.example.modifier.service.ModifierService;
 import com.google.android.material.button.MaterialButton;
 import com.google.android.material.progressindicator.CircularProgressIndicatorSpec;
 import com.google.android.material.progressindicator.IndeterminateDrawable;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.RandomStringUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.xmlpull.v1.XmlPullParser;
 
-import java.io.DataOutputStream;
 import java.io.File;
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.lang.reflect.Method;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Objects;
 
 public class Utils {
-    private static final String TAG = "Modifier";
 
     public static Context getContext() {
         try {
@@ -60,64 +42,6 @@ public class Utils {
         return null;
     }
 
-    public static String runAsRoot(String... cmds) throws IOException, InterruptedException {
-        Log.i(TAG, "Trying to run as root");
-        Process p = new ProcessBuilder("su", "-M").start();
-
-        StringBuilder res = new StringBuilder();
-        StringBuilder err = new StringBuilder();
-        new Thread(() -> {
-            try {
-                res.append(IOUtils.toString(p.getInputStream(), StandardCharsets.UTF_8));
-            } catch (IOException ignored) {
-            }
-        }).start();
-
-        new Thread(() -> {
-            try {
-                err.append(IOUtils.toString(p.getErrorStream(), StandardCharsets.UTF_8));
-            } catch (IOException ignored) {
-            }
-        }).start();
-
-        DataOutputStream outputStream = new DataOutputStream(p.getOutputStream());
-        for (String cmd : cmds) {
-            outputStream.writeBytes(cmd + "\n");
-            outputStream.flush();
-            Log.i(TAG, "Running command: " + cmd);
-        }
-        Thread.sleep(500);
-        outputStream.writeBytes("exit\n");
-        outputStream.flush();
-        p.waitFor();
-
-        Log.i(TAG, "Output: " + res);
-        if (err.length() > 0) {
-            Log.i(TAG, "Error: " + err);
-        }
-
-        return res.toString();
-    }
-
-    public static String getUniqueID() {
-        Context context = getContext();
-        Objects.requireNonNull(context);
-        SharedPreferences sharedPrefs = context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE);
-        String uniqueID = sharedPrefs.getString("unique_id", null);
-        if (StringUtils.isBlank(uniqueID)) {
-            try {
-                uniqueID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-        }
-        if (StringUtils.isBlank(uniqueID)) {
-            uniqueID = java.util.UUID.randomUUID().toString();
-        }
-        sharedPrefs.edit().putString("unique_id", uniqueID).apply();
-        return uniqueID;
-    }
-
     public static String generateIMEI() {
         int pos;
         int[] str = Arrays.stream(("35684610" + RandomStringUtils.randomNumeric(7)).split("")).mapToInt(Integer::parseInt).toArray();

+ 8 - 0
app/src/main/java/com/example/modifier/constants/Miscellaneous.kt

@@ -0,0 +1,8 @@
+package com.example.modifier.constants
+
+val servers = arrayOf(
+    "http://47.98.225.28",
+    "http://8.149.128.251",
+    "http://192.168.6.215:3000",
+    "http://192.168.50.135:3000"
+)

+ 3 - 1
app/src/main/java/com/example/modifier/constants/Packages.kt

@@ -6,4 +6,6 @@ const val PACKAGE_GSF = "com.google.android.gsf"
 
 const val PACKAGE_MESSAGING = "com.google.android.apps.messaging"
 
-const val PACKAGE_CLASH = "com.github.metacubex.clash.meta"
+const val PACKAGE_CLASH = "com.github.metacubex.clash.meta"
+
+const val PACKAGE_TELEPHONY = "com.android.providers.telephony"

+ 0 - 1
app/src/main/java/com/example/modifier/data/SocketClient.kt

@@ -3,7 +3,6 @@ package com.example.modifier.data
 import io.socket.client.IO
 import io.socket.client.Socket
 
-const val TAG = "SocketClient"
 class SocketClient {
     private val mSocketOpts = IO.Options()
     private lateinit var mSocket: Socket

+ 0 - 44
app/src/main/java/com/example/modifier/model/Backup.java

@@ -1,44 +0,0 @@
-package com.example.modifier.model;
-
-import com.example.modifier.model.TelephonyConfig;
-
-import java.util.Date;
-
-public class Backup {
-
-    private TelephonyConfig telephonyConfig;
-
-    private Date date;
-
-    private String path;
-
-    public Backup(TelephonyConfig telephonyConfig, Date date, String path) {
-        this.telephonyConfig = telephonyConfig;
-        this.date = date;
-        this.path = path;
-    }
-
-    public TelephonyConfig getConfig() {
-        return telephonyConfig;
-    }
-
-    public void setConfig(TelephonyConfig telephonyConfig) {
-        this.telephonyConfig = telephonyConfig;
-    }
-
-    public Date getDate() {
-        return date;
-    }
-
-    public void setDate(Date date) {
-        this.date = date;
-    }
-
-    public String getPath() {
-        return path;
-    }
-
-    public void setPath(String path) {
-        this.path = path;
-    }
-}

+ 1 - 1
app/src/main/java/com/example/modifier/model/TelephonyConfig.kt → app/src/main/java/com/example/modifier/model/SimInfo.kt

@@ -3,7 +3,7 @@ package com.example.modifier.model
 import kotlinx.serialization.Serializable
 
 @Serializable
-data class TelephonyConfig(
+data class SimInfo(
     var number: String,
     var mcc: String,
     var mnc: String,

+ 4 - 5
app/src/main/java/com/example/modifier/MyReceiver.kt → app/src/main/java/com/example/modifier/receiver/MyReceiver.kt

@@ -1,4 +1,4 @@
-package com.example.modifier
+package com.example.modifier.receiver
 
 import android.content.BroadcastReceiver
 import android.content.Context
@@ -7,6 +7,7 @@ import android.util.Log
 import android.widget.Toast
 import androidx.core.content.ContextCompat
 import com.example.modifier.data.AppDatabase
+import com.example.modifier.utils.shellRun
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -14,8 +15,6 @@ import java.io.File
 
 class MyReceiver : BroadcastReceiver() {
 
-    private val TAG = "MyReceiver"
-
     override fun onReceive(context: Context, intent: Intent) {
         when (intent.action) {
             "clear_backup" -> {
@@ -30,12 +29,12 @@ class MyReceiver : BroadcastReceiver() {
     private suspend fun clearBackup(context: Context) {
         val dao = AppDatabase.getDatabase(context).itemDao()
         dao.getAll().forEach {
-            Log.i(TAG, "Deleting ${it.id}: ${it.path}")
+            Log.i(com.example.modifier.TAG, "Deleting ${it.id}: ${it.path}")
             File(it.path).deleteRecursively()
             dao.delete(it)
         }
         ContextCompat.getExternalFilesDirs(context, "backup")[0].listFiles()?.forEach {
-            Log.i(TAG, "clearBackup: Deleting ${it.absolutePath}")
+            Log.i(com.example.modifier.TAG, "clearBackup: Deleting ${it.absolutePath}")
             shellRun("rm -rf ${it.absolutePath}")
         }
     }

+ 0 - 3
app/src/main/java/com/example/modifier/service/Helpers.kt

@@ -1,3 +0,0 @@
-package com.example.modifier.service
-
-

+ 120 - 102
app/src/main/java/com/example/modifier/service/ModifierService.kt

@@ -5,10 +5,8 @@ import android.accessibilityservice.AccessibilityServiceInfo
 import android.annotation.SuppressLint
 import android.content.ComponentName
 import android.content.Context
-import android.content.Intent
 import android.graphics.PixelFormat
 import android.graphics.Rect
-import android.net.Uri
 import android.os.Build
 import android.os.Handler
 import android.os.Looper
@@ -35,10 +33,7 @@ import androidx.lifecycle.liveData
 import com.example.modifier.BuildConfig
 import com.example.modifier.Global
 import com.example.modifier.Global.backup
-import com.example.modifier.Global.changeClashProfile
-import com.example.modifier.Global.resetAll
 import com.example.modifier.Global.restartModifier
-import com.example.modifier.Global.stopClash
 import com.example.modifier.R
 import com.example.modifier.TraverseResult
 import com.example.modifier.Utils
@@ -58,7 +53,7 @@ import com.example.modifier.databinding.FloatingWindowBinding
 import com.example.modifier.enums.RcsConfigureState
 import com.example.modifier.enums.RcsConnectionStatus
 import com.example.modifier.extension.kill
-import com.example.modifier.hasRootAccess
+import com.example.modifier.utils.hasRootAccess
 import com.example.modifier.http.ktorClient
 import com.example.modifier.http.api.DeviceApi
 import com.example.modifier.http.api.RcsNumberApi
@@ -72,9 +67,20 @@ import com.example.modifier.model.SocketCallback
 import com.example.modifier.model.TaskAction
 import com.example.modifier.model.TaskConfig
 import com.example.modifier.model.TaskExecutionResult
-import com.example.modifier.model.TelephonyConfig
+import com.example.modifier.model.SimInfo
 import com.example.modifier.serializer.Json
-import com.example.modifier.shellRun
+import com.example.modifier.utils.changeClashProfile
+import com.example.modifier.utils.clearConv
+import com.example.modifier.utils.isClashInstalled
+import com.example.modifier.utils.isOldVersion
+import com.example.modifier.utils.isRebooted
+import com.example.modifier.utils.optimize
+import com.example.modifier.utils.resetAll
+import com.example.modifier.utils.shellRun
+import com.example.modifier.utils.smsIntent
+import com.example.modifier.utils.stopClash
+import com.example.modifier.utils.syncTime
+import com.example.modifier.utils.uniqueId
 import com.google.android.material.color.DynamicColors
 import io.ktor.client.HttpClient
 import io.ktor.client.call.body
@@ -115,6 +121,7 @@ import java.time.LocalDateTime
 import java.time.temporal.ChronoUnit
 import java.util.Optional
 import java.util.Timer
+import java.util.TimerTask
 import java.util.concurrent.atomic.AtomicReference
 import kotlin.coroutines.resume
 import kotlin.math.max
@@ -127,8 +134,6 @@ import kotlin.time.Duration.Companion.seconds
 @SuppressLint("SetTextI18n")
 class ModifierService : AccessibilityService(), Emitter.Listener {
     companion object {
-        private const val TAG = "ModifierService1"
-
         const val NAME: String = BuildConfig.APPLICATION_ID + ".service.ModifierService"
 
         @JvmStatic
@@ -226,23 +231,23 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                 mSocket.disconnect()
             }
             mSocketOpts.query =
-                "model=${Build.MODEL}&name=${appPreferences.value.name}&id=${Utils.getUniqueID()}&version=${BuildConfig.VERSION_CODE}"
+                "model=${Build.MODEL}&name=${appPreferences.value.name}&id=${uniqueId}&version=${BuildConfig.VERSION_CODE}"
             mSocketOpts.transports = arrayOf("websocket")
-            Log.i(TAG, "Connection query: ${mSocketOpts.query}")
+            Log.i(com.example.modifier.TAG, "Connection query: ${mSocketOpts.query}")
             mSocket = IO.socket(appPreferences.value.server, mSocketOpts)
             mSocket.on("message", this@ModifierService)
             mSocket.on(Socket.EVENT_CONNECT) {
-                Log.i(TAG, "Connected to server")
+                Log.i(com.example.modifier.TAG, "Connected to server")
                 CoroutineScope(Dispatchers.IO).launch {
                     delay(500)
                     reportDeviceStatues()
                 }
             }
             mSocket.on(Socket.EVENT_DISCONNECT) {
-                Log.i(TAG, "Disconnected from server")
+                Log.i(com.example.modifier.TAG, "Disconnected from server")
             }
             mSocket.on(Socket.EVENT_CONNECT_ERROR) { args ->
-                Log.i(TAG, "Connection error: " + args[0])
+                Log.i(com.example.modifier.TAG, "Connection error: " + args[0])
                 if (args[0] is Exception) {
                     val e = args[0] as Exception
                     e.printStackTrace()
@@ -256,7 +261,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
 
     override fun onCreate() {
         super.onCreate()
-        Log.i(TAG, "Starting ModifierService")
+        Log.i(com.example.modifier.TAG, "Starting ModifierService")
         CoroutineScope(Dispatchers.IO).launch {
             appState = appStateRepository.getAppState()
             appPreferences = appPreferencesRepository.getAppPreferences()
@@ -273,12 +278,13 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             if (!hasRoot) {
                 System.exit(0)
             }
-            if (Global.rebooted()) {
+            if (isRebooted()) {
                 delay(2.minutes)
             } else {
                 delay(5000)
             }
-            Global.setupSystem()
+            optimize()
+            syncTime()
             if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
                 Global.killPhoneProcess(force = false)
             }
@@ -286,17 +292,22 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
 
             connect()
             val timer = Timer()
-            timer.schedule(object : java.util.TimerTask() {
+            timer.schedule(object : TimerTask() {
                 override fun run() {
                     reportDeviceStatues()
                 }
-            }, 0, 3000)
+            }, 0, 3.seconds.inWholeMilliseconds)
+            timer.schedule(object : TimerTask() {
+                override fun run() {
+
+                }
+            }, 0, 30.minutes.inWholeMilliseconds)
         }
     }
 
     override fun onAccessibilityEvent(event: AccessibilityEvent) {
         Log.d(
-            TAG,
+            com.example.modifier.TAG,
             "eventType: ${event.eventType}, packageName: ${event.packageName}, className: ${event.className}"
         )
         if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
@@ -308,7 +319,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                 try {
                     packageManager.getActivityInfo(componentName, 0)
                     currentActivity = componentName.flattenToShortString()
-                    Log.d(TAG, "Activity: $currentActivity")
+                    Log.d(com.example.modifier.TAG, "Activity: $currentActivity")
                 } catch (_: Exception) {
                 }
 
@@ -321,7 +332,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
 
     override fun call(vararg args: Any) {
         if (args.isNotEmpty()) {
-            Log.i(TAG, "Received message: " + args[0])
+            Log.i(com.example.modifier.TAG, "Received message: " + args[0])
             if (args[0] is JSONObject) {
                 val json = args[0] as JSONObject
                 val action = json.optString("action")
@@ -389,7 +400,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         }
                     }
                 }
-            Log.i(TAG, "Apk file saved to ${file.path}")
+            Log.i(com.example.modifier.TAG, "Apk file saved to ${file.path}")
             shellRun("pm install -d -r ${file.path}")
             mSocket.emit(
                 "callback",
@@ -480,7 +491,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         fail.add(taskItem.id)
                     }
                 } catch (e: Exception) {
-                    Log.e(TAG, "runTaskError: ${e.message}", e)
+                    Log.e(com.example.modifier.TAG, "runTaskError: ${e.message}", e)
                     fail.add(taskItem.id)
                 }
             }
@@ -500,9 +511,9 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             if (taskConfig.requestNumberInterval in 1..appState.value.successNum) {
                 delay(3000)
                 requestNumber()
-            } else if (taskConfig.cleanCount in 1..appState.value.executedNum) {
+            } else if (taskConfig.cleanCount in 1..appState.value.executedNum && !appPreferences.value.preventClean) {
                 delay(3000)
-                Global.clearConv();
+                clearConv();
                 shellRun(CMD_MESSAGING_APP)
                 delay(3000)
                 appStateRepository.resetExecutedNum()
@@ -511,7 +522,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             }
             appStateRepository.updateRuntimeFlags(running = false)
         } catch (e: Exception) {
-            Log.e(TAG, "runTaskError: ${e.message}", e)
+            Log.e(com.example.modifier.TAG, "runTaskError: ${e.message}", e)
             mSocket.emit(
                 "callback",
                 JSONObject(
@@ -528,25 +539,18 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         }
     }
 
-    private fun smsIntent(to: String, body: String): Intent {
-        val intent = Intent(Intent.ACTION_SENDTO)
-        intent.data = Uri.parse("sms:$to")
-        intent.putExtra("sms_body", body)
-        intent.putExtra("exit_on_sent", true)
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-        intent.setPackage("com.google.android.apps.messaging")
-        return intent
-    }
-
     private suspend fun send(
         to: String,
         body: String,
         taskConfig: TaskConfig
     ): Boolean {
-        Log.i(TAG, "Sending SMS to $to: $body")
+        Log.i(com.example.modifier.TAG, "Sending SMS to $to: $body")
         startActivity(smsIntent(to, body))
         try {
-            Log.i(TAG, "Command executed successfully, waiting for app to open...")
+            Log.i(
+                com.example.modifier.TAG,
+                "Command executed successfully, waiting for app to open..."
+            )
             delay(1000)
             var success = false
             var traverseResult = TraverseResult()
@@ -571,13 +575,13 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             }
             if (traverseResult.isRcsCapable) {
                 if (traverseResult.sendBtn == null) {
-                    Log.i(TAG, "Send button not found")
+                    Log.i(com.example.modifier.TAG, "Send button not found")
                 } else {
-                    Log.i(TAG, "Clicking send button")
+                    Log.i(com.example.modifier.TAG, "Clicking send button")
 
                     val dt = System.currentTimeMillis() - lastSend
                     if (taskConfig.rcsInterval > 0 && dt < taskConfig.rcsInterval) {
-                        Log.i(TAG, "Waiting for RCS interval")
+                        Log.i(com.example.modifier.TAG, "Waiting for RCS interval")
                         delay(taskConfig.rcsInterval - dt)
                     }
                     traverseResult.sendBtn!!.performAction(AccessibilityNodeInfo.ACTION_CLICK)
@@ -585,11 +589,11 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     success = true
                 }
             } else {
-                Log.i(TAG, "RCS not detected")
+                Log.i(com.example.modifier.TAG, "RCS not detected")
             }
             appStateRepository.incrementExecutedNum(success)
             Log.i(
-                TAG,
+                com.example.modifier.TAG,
                 "executedNum: ${appState.value.executedNum}, successNum: ${appState.value.successNum}"
             )
             delay(1000)
@@ -628,7 +632,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             .orElse(null)
         val id = node.viewIdResourceName
 
-        Log.d(TAG, "Node: class=$className, text=$text, name=$name, id=$id")
+        Log.d(com.example.modifier.TAG, "Node: class=$className, text=$text, name=$name, id=$id")
 
         if ("Compose:Draft:Send" == name) {
             result.sendBtn = node
@@ -735,7 +739,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         binding.root.post {
             maxX = width - binding.root.measuredWidth
             maxY = height - binding.root.measuredHeight
-            Log.i(TAG, "measured: $maxX, $maxY")
+            Log.i(com.example.modifier.TAG, "measured: $maxX, $maxY")
             layoutParams.x = maxX
             windowManager.updateViewLayout(mLayout, layoutParams)
         }
@@ -847,7 +851,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     }
 
                     R.id.clear_conv -> {
-                        Global.clearConv()
+                        clearConv()
                     }
 
                     R.id.store_numbers -> {
@@ -892,7 +896,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                 val dataObj = JSONObject()
                 dataObj.put("canSend", appState.value.send)
                 dataObj.put("busy", appState.value.busy)
-                dataObj.put("currentCountry", Global.telephonyConfig.country)
+                dataObj.put("currentCountry", Global.simInfo.country)
                 data.put("data", dataObj)
                 mSocket.emit("message", data)
             } catch (e: JSONException) {
@@ -921,7 +925,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
 
                     continuation.invokeOnCancellation {
                         handler.post {
-                            Log.i(TAG, "removeObserver")
+                            Log.i(com.example.modifier.TAG, "removeObserver")
                             rcsConfigureState.removeObserver(observer)
                         }
                     }
@@ -999,19 +1003,8 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         return success
     }
 
-    private fun isOldVersion(): Boolean {
-        val info = packageManager.getPackageInfo("com.google.android.apps.messaging", 0)
-        var oldVersion = false
-        if (info != null) {
-            if (info.versionCode < 170545910) {
-                oldVersion = true
-            }
-        }
-        return oldVersion
-    }
-
     private suspend fun reset() {
-        if (isOldVersion()) {
+        if (isOldVersion(this)) {
             withTimeout(1.hours) {
                 while (true) {
                     delay(100)
@@ -1039,16 +1032,19 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                             it == RcsConfigureState.WAITING_FOR_TOS
                         }
                         if (switchAppear != true) {
-                            Log.e(TAG, "RCS not entered default on state, retrying...")
+                            Log.e(
+                                com.example.modifier.TAG,
+                                "RCS not entered default on state, retrying..."
+                            )
                             continue
                         }
                     }
                     if (!toggleRcsSwitch(false)) {
-                        Log.e(TAG, "RCS switch not turned off, retrying...")
+                        Log.e(com.example.modifier.TAG, "RCS switch not turned off, retrying...")
                         continue
                     }
                     if (!toggleRcsSwitch(true)) {
-                        Log.e(TAG, "RCS switch not turned on, retrying...")
+                        Log.e(com.example.modifier.TAG, "RCS switch not turned on, retrying...")
                         continue
                     }
                     var resetSuccess = waitForRcsState(
@@ -1066,7 +1062,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                             ), 1.minutes
                         ).let { it == RcsConfigureState.READY }
                     }
-                    Log.i(TAG, "waitForRcsState: $resetSuccess")
+                    Log.i(com.example.modifier.TAG, "waitForRcsState: $resetSuccess")
                     appStateRepository.resetRequestedNum()
                     if (resetSuccess) {
                         delay(3000)
@@ -1102,13 +1098,16 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                             it == RcsConfigureState.WAITING_FOR_DEFAULT_ON
                         }
                         if (switchAppear != true) {
-                            Log.e(TAG, "RCS not entered default on state, retrying...")
+                            Log.e(
+                                com.example.modifier.TAG,
+                                "RCS not entered default on state, retrying..."
+                            )
                             continue
                         }
                     }
                     val switchOn = toggleRcsSwitch(true)
                     if (!switchOn) {
-                        Log.e(TAG, "RCS switch not turned on, retrying...")
+                        Log.e(com.example.modifier.TAG, "RCS switch not turned on, retrying...")
                         continue
                     }
                     var resetSuccess = waitForRcsState(
@@ -1126,7 +1125,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                             ), 1.minutes
                         ).let { it == RcsConfigureState.READY }
                     }
-                    Log.i(TAG, "waitForRcsState: $resetSuccess")
+                    Log.i(com.example.modifier.TAG, "waitForRcsState: $resetSuccess")
                     appStateRepository.resetRequestedNum()
                     if (resetSuccess) {
                         delay(3000)
@@ -1153,7 +1152,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
 
         appStateRepository.updateRuntimeFlags(requesting = true)
 
-        if (Global.telephonyConfig.available == true) {
+        if (Global.simInfo.available == true) {
             backup(
                 backupItemDao = backupItemDao,
                 type = "auto",
@@ -1161,7 +1160,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                 fresh = fresh
             )
         } else {
-            Global.clearConv();
+            clearConv();
         }
 
         appStateRepository.incrementRequestedNum()
@@ -1174,9 +1173,9 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                 needRest = needRest || retry > 2 || appState.value.requestedNum > 5
                 try {
                     val device =
-                        ktorClient(appPreferences.value.server).get(DeviceApi.Id(id = Utils.getUniqueID()))
+                        ktorClient(appPreferences.value.server).get(DeviceApi.Id(id = uniqueId))
                             .body<DeviceResponse>()
-                    if (Global.isClashInstalled(applicationContext)) {
+                    if (isClashInstalled(applicationContext)) {
 
                         val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
                         if (TextUtils.isEmpty(device.clashProfile)) {
@@ -1203,7 +1202,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
 
                     if (requestMode == 2 && !noBackup) {
                         val backup = backupItemDao.findBackupForRestore(
-                            Global.telephonyConfig.number,
+                            Global.simInfo.number,
                             System.currentTimeMillis() - 2 * 24 * 60 * 60 * 1000
                         )
                         if (backup != null) {
@@ -1229,7 +1228,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     }
 
                     val req = RcsNumberRequest(
-                        deviceId = Utils.getUniqueID(),
+                        deviceId = uniqueId,
                         taskId = currentTaskId
                     )
                     if (!TextUtils.isEmpty(device.pinCountry)) {
@@ -1246,14 +1245,14 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         }
                     }
                     var rcsNumber = response.body<RcsNumberResponse>()
-                    Log.i(TAG, "requestNumber response: $rcsNumber")
+                    Log.i(com.example.modifier.TAG, "requestNumber response: $rcsNumber")
 
                     withContext(Dispatchers.Main) {
                         binding.tvLog.text = "Requesting success, waiting for logs..."
                     }
 
                     Global.save(
-                        TelephonyConfig(
+                        SimInfo(
                             rcsNumber.number,
                             rcsNumber.mcc,
                             rcsNumber.mnc,
@@ -1276,7 +1275,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     }
 
                     if (rcsNumber.expiryTime.isBefore(LocalDateTime.now())) {
-                        Log.e(TAG, "RCS number expired, retrying...")
+                        Log.e(com.example.modifier.TAG, "RCS number expired, retrying...")
                         continue
                     }
                     var sendOtpTimeout = ChronoUnit.SECONDS.between(
@@ -1284,7 +1283,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         rcsNumber.expiryTime
                     ).seconds
                     if (sendOtpTimeout < 60.seconds) {
-                        Log.e(TAG, "OTP timeout too short, retrying...")
+                        Log.e(com.example.modifier.TAG, "OTP timeout too short, retrying...")
                         continue
                     }
                     if (sendOtpTimeout > 2.minutes) {
@@ -1299,10 +1298,16 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                             needRest = true
                         }
                         if (RcsConfigureState.REPLAY_REQUEST == rcsConfigureState.value) {
-                            Log.e(TAG, "REPLAY_REQUEST detected, may reset after 3 retry ($retry)")
+                            Log.e(
+                                com.example.modifier.TAG,
+                                "REPLAY_REQUEST detected, may reset after 3 retry ($retry)"
+                            )
                             retry++
                         }
-                        Log.e(TAG, "RCS not entered waiting for OTP state, retrying...")
+                        Log.e(
+                            com.example.modifier.TAG,
+                            "RCS not entered waiting for OTP state, retrying..."
+                        )
                         continue
                     }
 
@@ -1317,12 +1322,12 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                                 )
                             )
                         } catch (e: Exception) {
-                            Log.e(TAG, "Send OtpState Error: ${e.message}", e)
+                            Log.e(com.example.modifier.TAG, "Send OtpState Error: ${e.message}", e)
                         }
                     }
 
                     if (rcsNumber.expiryTime.isBefore(LocalDateTime.now())) {
-                        Log.e(TAG, "RCS number expired, retrying...")
+                        Log.e(com.example.modifier.TAG, "RCS number expired, retrying...")
                         continue
                     }
 
@@ -1332,19 +1337,22 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                                 rcsNumber =
                                     ktorClient(appPreferences.value.server).get(RcsNumberApi.Id(id = rcsNumber.id))
                                         .body<RcsNumberResponse>()
-                                Log.i(TAG, "wait for otp response: $rcsNumber")
+                                Log.i(com.example.modifier.TAG, "wait for otp response: $rcsNumber")
                                 if (rcsNumber.status == RcsNumberResponse.STATUS_SUCCESS || rcsNumber.status == RcsNumberResponse.STATUS_EXPIRED) {
                                     break
                                 }
                             } catch (exception: Exception) {
-                                Log.e(TAG, "wait for otp Error: ${exception.stackTrace}")
+                                Log.e(
+                                    com.example.modifier.TAG,
+                                    "wait for otp Error: ${exception.stackTrace}"
+                                )
                             }
                             delay(2.seconds)
                         }
                     }
 
                     if (rcsNumber.status != RcsNumberResponse.STATUS_SUCCESS) {
-                        Log.e(TAG, "OTP not received, retrying...")
+                        Log.e(com.example.modifier.TAG, "OTP not received, retrying...")
                         continue
                     }
 
@@ -1353,7 +1361,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                             .find(rcsNumber.message!!)
                     if (match != null) {
                         val otp = match.groupValues[1]
-                        Log.i(TAG, "OTP: $otp")
+                        Log.i(com.example.modifier.TAG, "OTP: $otp")
                         val sender = "3538"
                         val msg = "Your Messenger verification code is G-$otp"
 
@@ -1380,14 +1388,17 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                                     }
 
                                     else -> {
-                                        Log.e(TAG, "verifyOtp fail, retrying...")
+                                        Log.e(
+                                            com.example.modifier.TAG,
+                                            "verifyOtp fail, retrying..."
+                                        )
                                     }
                                 }
                             }
                             false
                         }
                         if (!configured) {
-                            Log.e(TAG, "RCS not configured, retrying...")
+                            Log.e(com.example.modifier.TAG, "RCS not configured, retrying...")
                             continue
                         } else {
                             launch {
@@ -1402,7 +1413,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                                     )
                                 } catch (e: Exception) {
                                     Log.e(
-                                        TAG,
+                                        com.example.modifier.TAG,
                                         "Send ConfiguredState Error: ${e.message}",
                                         e
                                     )
@@ -1413,21 +1424,21 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         }
                     }
                 } catch (e: Exception) {
-                    Log.e(TAG, "requestNumberError: ${e.message}", e)
+                    Log.e(com.example.modifier.TAG, "requestNumberError: ${e.message}", e)
                 }
             }
         }
         if (requestSuccess) {
-            Global.telephonyConfig.available = true
+            Global.simInfo.available = true
             Global.save()
             appStateRepository.resetSuccessNum()
             appStateRepository.resetExecutedNum()
-            Log.i(TAG, "requestNumber success")
+            Log.i(com.example.modifier.TAG, "requestNumber success")
             delay(5000)
             shellRun(PACKAGE_MESSAGING.kill(), "sleep 1", CMD_MESSAGING_APP)
             delay(2000)
         } else {
-            Log.e(TAG, "requestNumber failed")
+            Log.e(com.example.modifier.TAG, "requestNumber failed")
             appStateRepository.updateSend(false)
             withContext(Dispatchers.Main) {
                 binding.swSend.isChecked = false
@@ -1440,7 +1451,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
 
     private suspend fun checkRcsConnectivity(): Boolean = run checkRcsConnection@{
         repeat(3) {
-            Log.i(TAG, "Checking RCS status...")
+            Log.i(com.example.modifier.TAG, "Checking RCS status...")
             shellRun(
                 CMD_CONVERSATION_LIST_ACTIVITY,
                 CMD_RCS_SETTINGS_ACTIVITY,
@@ -1449,11 +1460,11 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             val res = TraverseResult()
             traverseNode(rootInActiveWindow, res)
             if (res.rcsConnectionStatus == RcsConnectionStatus.CONNECTED) {
-                Log.i(TAG, "RCS is connected")
+                Log.i(com.example.modifier.TAG, "RCS is connected")
                 shellRun(CMD_BACK)
                 return@checkRcsConnection true
             } else {
-                Log.i(TAG, "RCS not connected, retrying...")
+                Log.i(com.example.modifier.TAG, "RCS not connected, retrying...")
             }
             shellRun(CMD_BACK, "sleep ${it * 2}")
         }
@@ -1479,17 +1490,21 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         )
                     )
                         .body<SysConfigResponse>()
-                    Log.i(TAG, "sysConfig response: $config")
+                    Log.i(com.example.modifier.TAG, "sysConfig response: $config")
                     checkRcsAvailabilityNumbers.addAll(
                         config.value.split(",").map { it.trim() })
 
                 } catch (exception: Exception) {
-                    Log.e(TAG, "sysConfig Error: ${exception.message}", exception)
+                    Log.e(
+                        com.example.modifier.TAG,
+                        "sysConfig Error: ${exception.message}",
+                        exception
+                    )
                 }
             }
 
             if (checkRcsAvailabilityNumbers.isEmpty()) {
-                Log.e(TAG, "checkRcsAvailabilityNumbers is empty")
+                Log.e(com.example.modifier.TAG, "checkRcsAvailabilityNumbers is empty")
                 return true
             }
 
@@ -1503,13 +1518,16 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         if (traverseResult.isRcsCapable) {
                             return@withTimeoutOrNull true
                         } else {
-                            Log.i(TAG, "checkRcsAvailability: RCS not detected")
+                            Log.i(
+                                com.example.modifier.TAG,
+                                "checkRcsAvailability: RCS not detected"
+                            )
                         }
                         delay(200)
                     }
                 }
                 if (s == true) {
-                    Log.i(TAG, "checkRcsAvailability: $it success")
+                    Log.i(com.example.modifier.TAG, "checkRcsAvailability: $it success")
                     delay(1000)
                     return@checkAvailability true
                 }

+ 2 - 1
app/src/main/java/com/example/modifier/ui/login/LoginActivity.kt

@@ -12,6 +12,7 @@ import androidx.lifecycle.viewModelScope
 import com.example.modifier.Global
 import com.example.modifier.R
 import com.example.modifier.Utils
+import com.example.modifier.constants.servers
 import com.example.modifier.databinding.ActivityLoginBinding
 import com.example.modifier.model.ServerConfig
 import dagger.hilt.android.AndroidEntryPoint
@@ -28,7 +29,7 @@ class LoginActivity : AppCompatActivity() {
         binding.loginViewModel = loginViewModel
         binding.lifecycleOwner = this
 
-        binding.etServer.setSimpleItems(Global.servers.toTypedArray())
+        binding.etServer.setSimpleItems(servers)
         binding.btnLogin.setOnClickListener { v ->
             loginViewModel.serverConfig.value!!.url = binding.etServer.text.toString()
             loginViewModel.serverConfig.value!!.deviceLabel = binding.etDeviceLabel.text.toString()

+ 3 - 4
app/src/main/java/com/example/modifier/ui/login/LoginViewModel.kt

@@ -45,7 +45,6 @@ val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "se
 
 @HiltViewModel
 class LoginViewModel @Inject constructor() : ViewModel() {
-    private val TAG = "LoginViewModel"
 
     companion object {
         val URL = stringPreferencesKey("url")
@@ -88,7 +87,7 @@ class LoginViewModel @Inject constructor() : ViewModel() {
     }
 
     suspend fun login() {
-        Log.d(TAG, "login")
+        Log.d(com.example.modifier.TAG, "login")
         _uiState.value = LoginUiState.Loading
         val client = HttpClient(OkHttp) {
             defaultRequest {
@@ -110,12 +109,12 @@ class LoginViewModel @Inject constructor() : ViewModel() {
         when (response.status) {
             HttpStatusCode.OK -> {
                 val loginResponse = response.body<LoginResponse>()
-                Log.i(TAG, "response: $loginResponse")
+                Log.i(com.example.modifier.TAG, "response: $loginResponse")
             }
 
             else -> {
                 val errorResponse = response.body<ErrorResponse>()
-                Log.e(TAG, "error: $errorResponse")
+                Log.e(com.example.modifier.TAG, "error: $errorResponse")
             }
         }
     }

+ 12 - 14
app/src/main/java/com/example/modifier/ui/settings/SettingsFragment.kt

@@ -23,9 +23,9 @@ import androidx.fragment.app.Fragment
 import androidx.lifecycle.lifecycleScope
 import com.example.modifier.Global
 import com.example.modifier.Global.save
-import com.example.modifier.Global.servers
 import com.example.modifier.R
 import com.example.modifier.Utils
+import com.example.modifier.constants.servers
 import com.example.modifier.data.AppPreferences
 import com.example.modifier.data.AppPreferencesRepository
 import com.example.modifier.databinding.FragmentSettingsBinding
@@ -33,8 +33,9 @@ import com.example.modifier.http.ktorClient
 import com.example.modifier.http.api.RcsNumberApi
 import com.example.modifier.http.request.RcsNumberRequest
 import com.example.modifier.http.response.RcsNumberResponse
-import com.example.modifier.model.TelephonyConfig
+import com.example.modifier.model.SimInfo
 import com.example.modifier.service.ModifierService.Companion.instance
+import com.example.modifier.utils.uniqueId
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import io.ktor.client.call.body
 import io.ktor.client.plugins.resources.put
@@ -55,9 +56,10 @@ import org.apache.commons.validator.routines.UrlValidator
 import java.util.Objects
 import java.util.Optional
 
+
+
 @SuppressLint("SetTextI18n", "MissingPermission", "HardwareIds", "NewApi")
 class SettingsFragment : Fragment() {
-    private val TAG = "SettingsFragment"
 
     private lateinit var binding: FragmentSettingsBinding
     private lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
@@ -148,7 +150,7 @@ class SettingsFragment : Fragment() {
                 Toast.makeText(context, "Invalid server URL", Toast.LENGTH_SHORT).show()
                 return@setOnClickListener
             }
-            binding.etServer.setSimpleItems(servers.toTypedArray<String>())
+            binding.etServer.setSimpleItems(servers)
             lifecycleScope.launch {
                 appPreferencesRepository.updateServer(server)
                 appPreferencesRepository.updateName(binding.etDeviceLabel.text.toString())
@@ -177,11 +179,7 @@ class SettingsFragment : Fragment() {
                             RcsNumberApi()
                         ) {
                             contentType(ContentType.Application.Json)
-                            setBody(
-                                RcsNumberRequest(
-                                    deviceId = Utils.getUniqueID()
-                                )
-                            )
+                            setBody(RcsNumberRequest(deviceId = uniqueId))
                         }
                     } catch (e: Exception) {
                         withContext(Dispatchers.Main) {
@@ -198,7 +196,7 @@ class SettingsFragment : Fragment() {
                         }
                         return@req
                     }
-                    Log.i(TAG, "response: ${response.bodyAsText()}")
+                    Log.i(com.example.modifier.TAG, "response: ${response.bodyAsText()}")
                     val res = response.body<RcsNumberResponse>()
                     val number = res.number
                     val mcc = res.mcc
@@ -210,7 +208,7 @@ class SettingsFragment : Fragment() {
                         mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length)
                     val imei = Utils.generateIMEI()
                     save(
-                        TelephonyConfig(
+                        SimInfo(
                             number,
                             mcc,
                             mnc,
@@ -338,8 +336,8 @@ class SettingsFragment : Fragment() {
         withContext(Dispatchers.IO) {
             Global.load()
             withContext(Dispatchers.Main) {
-                binding.etServer.setSimpleItems(servers.toTypedArray<String>())
-                val telephonyConfig = Global.telephonyConfig
+                binding.etServer.setSimpleItems(servers)
+                val telephonyConfig = Global.simInfo
                 binding.etNumber.setText(telephonyConfig.number)
                 binding.etMcc.setText(telephonyConfig.mcc)
                 binding.etMnc.setText(telephonyConfig.mnc)
@@ -360,7 +358,7 @@ class SettingsFragment : Fragment() {
         lifecycleScope.launch {
             withContext(Dispatchers.IO) {
                 save(
-                    TelephonyConfig(
+                    SimInfo(
                         binding.etNumber.text.toString(),
                         binding.etMcc.text.toString(),
                         binding.etMnc.text.toString(),

+ 7 - 7
app/src/main/java/com/example/modifier/ui/utils/UtilsFragment.kt

@@ -11,11 +11,6 @@ import androidx.fragment.app.Fragment
 import androidx.lifecycle.lifecycleScope
 import com.example.modifier.Frida
 import com.example.modifier.Global
-import com.example.modifier.Global.clear
-import com.example.modifier.Global.clearConv
-import com.example.modifier.Global.resetAll
-import com.example.modifier.Global.resumePackage
-import com.example.modifier.Global.suspendPackage
 import com.example.modifier.R
 import com.example.modifier.Utils
 import com.example.modifier.constants.CMD_BACK_APP
@@ -31,8 +26,13 @@ import com.example.modifier.http.ktorClient
 import com.example.modifier.http.api.SysConfigApi
 import com.example.modifier.http.response.SysConfigResponse
 import com.example.modifier.service.ModifierService
-import com.example.modifier.shellRun
-import com.example.modifier.syncTime
+import com.example.modifier.utils.clear
+import com.example.modifier.utils.clearConv
+import com.example.modifier.utils.resetAll
+import com.example.modifier.utils.resumePackage
+import com.example.modifier.utils.shellRun
+import com.example.modifier.utils.suspendPackage
+import com.example.modifier.utils.syncTime
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import io.ktor.client.call.body
 import io.ktor.client.plugins.resources.get

+ 41 - 0
app/src/main/java/com/example/modifier/utils/Clash.kt

@@ -0,0 +1,41 @@
+package com.example.modifier.utils
+
+import android.content.Context
+import android.content.Intent
+import com.example.modifier.Utils
+
+fun isClashInstalled(context: Context): Boolean {
+    val packageManager = context.packageManager
+    return try {
+        packageManager.getPackageInfo("com.github.metacubex.clash.meta", 0)
+        true
+    } catch (e: Exception) {
+        false
+    }
+}
+
+fun stopClash() {
+    try {
+        val context = Utils.getContext()
+        val intent = Intent()
+        intent.action = "com.github.metacubex.clash.meta.action.STOP_CLASH"
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+        context.startActivity(intent)
+    } catch (e: Exception) {
+        e.printStackTrace()
+    }
+}
+
+fun changeClashProfile(name: String, base64: String) {
+    try {
+        val context = Utils.getContext()
+        val intent = Intent()
+        intent.action = "com.github.metacubex.clash.meta.action.USE_PROFILE"
+        intent.putExtra("base64", base64)
+        intent.putExtra("name", name)
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+        context.startActivity(intent)
+    } catch (e: Exception) {
+        e.printStackTrace()
+    }
+}

+ 80 - 0
app/src/main/java/com/example/modifier/utils/GoogleMessage.kt

@@ -0,0 +1,80 @@
+package com.example.modifier.utils
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Build
+import androidx.core.content.ContextCompat
+import com.example.modifier.Global.sqlite3path
+import com.example.modifier.Utils
+import com.example.modifier.constants.PACKAGE_MESSAGING
+import com.example.modifier.constants.PACKAGE_TELEPHONY
+import java.io.File
+
+fun smsIntent(to: String, body: String): Intent {
+    val intent = Intent(Intent.ACTION_SENDTO)
+    intent.data = Uri.parse("sms:$to")
+    intent.putExtra("sms_body", body)
+    intent.putExtra("exit_on_sent", true)
+    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+    intent.setPackage(PACKAGE_MESSAGING)
+    return intent
+}
+
+fun isOldVersion(context: Context): Boolean {
+    val info = context.packageManager.getPackageInfo(PACKAGE_MESSAGING, 0)
+    var oldVersion = false
+    if (info != null) {
+        if (info.versionCode < 170545910) {
+            oldVersion = true
+        }
+    }
+    return oldVersion
+}
+
+suspend fun clearConv() {
+    val context = Utils.getContext()
+    val model = Build.MODEL.replace(" ", "_")
+    if (!(model == "SM-F707N" || model == "SM-F711B" || model == "Pixel_5")) {
+        return
+    }
+    try {
+        val sqlite3 = sqlite3path()
+        suspendPackage(PACKAGE_MESSAGING)
+        val dataDir = ContextCompat.getDataDir(context)
+        val providerDir = File(dataDir, "telephony_provider/$model")
+        if (!providerDir.exists()) {
+            Utils.copyAssetFolder(context.assets, "telephony_provider/$model", providerDir.path)
+        }
+
+        val cmds = mutableListOf<String>()
+        @SuppressLint("SdCardPath")
+        fun copyToData(file: File) {
+            val relative = file.path.replace(providerDir.path, "")
+            if (file.isDirectory) {
+                file.listFiles()?.forEach {
+                    copyToData(it)
+                }
+            } else {
+                val dest = File("/data/data/$PACKAGE_TELEPHONY/$relative")
+                cmds.addAll(
+                    listOf(
+                        "cp -f ${file.path} ${dest.path}",
+                        "chmod 660 ${dest.path}",
+                        "chown radio:radio ${dest.path}"
+                    )
+                )
+            }
+        }
+        copyToData(providerDir)
+        shellRun(
+            "$sqlite3 /data/data/$PACKAGE_MESSAGING/bugle_db \"DELETE FROM conversations;\"",
+            "$sqlite3 /data/data/$PACKAGE_MESSAGING/databases/bugle_db \"DELETE FROM messages;\"",
+            *cmds.toTypedArray(),
+        )
+        resumePackage(PACKAGE_MESSAGING)
+    } catch (e: Exception) {
+        e.printStackTrace()
+    }
+}

+ 111 - 0
app/src/main/java/com/example/modifier/utils/Package.kt

@@ -0,0 +1,111 @@
+package com.example.modifier.utils
+
+import com.example.modifier.constants.CMD_HOME
+import com.example.modifier.constants.CMD_MESSAGING_APP
+import com.example.modifier.constants.CMD_START_PLAY_STORE
+import com.example.modifier.constants.PACKAGE_GMS
+import com.example.modifier.constants.PACKAGE_GSF
+import com.example.modifier.constants.PACKAGE_MESSAGING
+import com.example.modifier.extension.clear
+import com.example.modifier.extension.kill
+import com.example.modifier.extension.resume
+import com.example.modifier.extension.suspend
+import kotlinx.coroutines.delay
+
+suspend fun suspendPackage(vararg packages: String) {
+    packages.forEach {
+        shellRun(
+            it.suspend(),
+            it.kill()
+        )
+        delay(1000)
+    }
+}
+
+suspend fun resumePackage(vararg packages: String) {
+    packages.forEach {
+        shellRun(
+            it.resume(),
+            it.kill()
+        )
+        delay(1000)
+    }
+}
+
+suspend fun clear(gsf: Boolean, gms: Boolean, sms: Boolean) {
+    try {
+        suspendPackage(PACKAGE_GSF, PACKAGE_GMS, PACKAGE_MESSAGING)
+        val cmds: MutableList<String> = ArrayList()
+        // suspend
+        if (gsf) {
+            cmds.add(PACKAGE_GSF.suspend())
+            cmds.add(PACKAGE_GSF.kill())
+            cmds.add("echo 'gsf suspended'")
+        }
+        if (gms) {
+            cmds.add(PACKAGE_GMS.suspend())
+            cmds.add(PACKAGE_GMS.kill())
+            cmds.add("echo 'gms suspended'")
+        }
+        if (sms) {
+            cmds.add(PACKAGE_MESSAGING.suspend())
+            cmds.add(PACKAGE_MESSAGING.kill())
+            cmds.add("echo 'sms suspended'")
+        }
+        cmds.add("sleep 1")
+        // clear
+        if (gsf) {
+            cmds.add(PACKAGE_GSF.clear())
+            cmds.add("echo 'cleared gsf'")
+        }
+        if (gms) {
+            cmds.add(PACKAGE_GMS.clear())
+            cmds.add("echo 'cleared gms'")
+        }
+        if (sms) {
+            cmds.add(PACKAGE_MESSAGING.clear())
+            cmds.add("echo 'cleared sms'")
+        }
+        cmds.add("sleep 1")
+        // unsuspend
+        if (gsf) {
+            cmds.add(PACKAGE_GSF.resume())
+            cmds.add("echo 'gsf unsuspend'")
+        }
+        if (gms) {
+            cmds.add(PACKAGE_GMS.resume())
+            cmds.add("echo 'gms unsuspend'")
+        }
+        if (sms) {
+            cmds.add(PACKAGE_MESSAGING.resume())
+            cmds.add("echo 'sms unsuspend'")
+        }
+        cmds.add("sleep 1")
+        shellRun(*cmds.toTypedArray<String>())
+    } catch (e: Exception) {
+        e.printStackTrace()
+    }
+}
+
+suspend fun resetAll() {
+    try {
+        clearConv()
+        shellRun(
+            PACKAGE_MESSAGING.suspend(),
+            PACKAGE_MESSAGING.kill(),
+            PACKAGE_MESSAGING.clear(),
+            PACKAGE_GSF.clear(),
+            PACKAGE_GMS.clear(),
+            "sleep 1",
+            CMD_START_PLAY_STORE,
+            "sleep 1",
+            CMD_HOME,
+            "sleep 10",
+            PACKAGE_GMS.clear(),
+            PACKAGE_MESSAGING.resume(),
+            CMD_MESSAGING_APP
+        )
+    } catch (e: Exception) {
+        e.printStackTrace()
+    }
+}

+ 143 - 138
app/src/main/java/com/example/modifier/utils/RcsHackTool.kt

@@ -1,148 +1,153 @@
-package com.example.modifier.utils;
-
-import android.content.Context;
-import android.content.Intent;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.SmsManager;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-import com.google.gson.Gson;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.lang.reflect.Method;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-
-public class RcsHackTool {
-    private static final String TAG = "RcsHackTool";
-
-    // 获取对应的Intent数据
-    public static Intent createSmsIntent(String number, String body, int slot, int subId) {
-        SmsManager smsManager = SmsManager.getDefault();
-        // 防止短信过长
-        ArrayList<String> messages = smsManager.divideMessage(body);
-        int size = messages.size();
-        byte[][] objArray = new byte[size][];
-        for (int i = 0; i < size; ++i) {
-            byte[] pduu = createFakeSms(number, messages.get(i));
-            objArray[i] = pduu;
-        }
-        Intent intent = new Intent();
-        intent.setAction("android.provider.Telephony.SMS_DELIVER");
-
-        intent.putExtra("android.telephony.extra.SUBSCRIPTION_INDEX", subId);
-        intent.putExtra("messageId", Long.valueOf((int) Math.floor(Math.random() * 100000000)).longValue());
-        intent.putExtra("pdus", objArray);
-        intent.putExtra("format", "3gpp");
-        intent.putExtra("android.telephony.extra.SLOT_INDEX", slot);
-        intent.putExtra("phone", slot);
-        intent.putExtra("subscription", subId);
-        return intent;
+package com.example.modifier.utils
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.Intent
+import android.telephony.PhoneNumberUtils
+import android.telephony.SmsManager
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.util.Log
+import com.google.gson.Gson
+import java.io.ByteArrayOutputStream
+import java.io.IOException
+import java.io.UnsupportedEncodingException
+import java.nio.charset.StandardCharsets
+import java.util.Calendar
+import java.util.GregorianCalendar
+import kotlin.math.floor
+
+// 获取对应的Intent数据
+fun createSmsIntent(number: String, body: String?, slot: Int, subId: Int): Intent {
+    val smsManager = SmsManager.getDefault()
+    // 防止短信过长
+    val messages = smsManager.divideMessage(body)
+    val size = messages.size
+    val objArray = arrayOfNulls<ByteArray>(size)
+    for (i in 0 until size) {
+        val pduu = createFakeSms(number, messages[i])
+        objArray[i] = pduu
     }
-
-    public static Intent createSmsIntent(Context context, String number, String body) {
-        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-        SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
-        final int simCount = subscriptionManager.getActiveSubscriptionInfoCountMax();
-        int subId = 0;
-        int slot = 0;
-        for (int i = 0; i < simCount; i++) {
-            SubscriptionInfo subInfo = subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i);
-            if (subInfo != null) {
-                Log.d(TAG, "subId: " + subInfo.getSubscriptionId());
-                subId = subInfo.getSubscriptionId();
-                slot = subInfo.getSimSlotIndex();
-                break;
-            }
+    val intent = Intent()
+    intent.setAction("android.provider.Telephony.SMS_DELIVER")
+
+    intent.putExtra("android.telephony.extra.SUBSCRIPTION_INDEX", subId)
+    intent.putExtra(
+        "messageId", floor(Math.random() * 100000000)
+            .toInt().toLong()
+    )
+    intent.putExtra("pdus", objArray)
+    intent.putExtra("format", "3gpp")
+    intent.putExtra("android.telephony.extra.SLOT_INDEX", slot)
+    intent.putExtra("phone", slot)
+    intent.putExtra("subscription", subId)
+    return intent
+}
+
+@SuppressLint("MissingPermission")
+fun createSmsIntent(context: Context, number: String, body: String?): Intent {
+    val telephonyManager =
+        context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
+    val subscriptionManager = context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
+    val simCount = subscriptionManager.activeSubscriptionInfoCountMax
+    var subId = 0
+    var slot = 0
+    for (i in 0 until simCount) {
+        val subInfo = subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i)
+        if (subInfo != null) {
+            Log.d(com.example.modifier.TAG, "subId: " + subInfo.subscriptionId)
+            subId = subInfo.subscriptionId
+            slot = subInfo.simSlotIndex
+            break
         }
-
-        return createSmsIntent(number, body, slot, subId);
     }
 
-    // 创建pdu
-    public static byte[] createFakeSms(String sender, String body) {
-        byte[] pdu = null;
-        byte[] scBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD("0000000000");
-        byte[] senderBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(sender);
-        int lsmcs = scBytes.length;
-        // 时间处理,包括年月日时分秒以及时区和夏令时
-        byte[] dateBytes = new byte[7];
-        Calendar calendar = new GregorianCalendar();
-        dateBytes[0] = reverseByte((byte) (calendar.get(Calendar.YEAR)));
-        dateBytes[1] = reverseByte((byte) (calendar.get(Calendar.MONTH) + 1));
-        dateBytes[2] = reverseByte((byte) (calendar.get(Calendar.DAY_OF_MONTH)));
-        dateBytes[3] = reverseByte((byte) (calendar.get(Calendar.HOUR_OF_DAY)));
-        dateBytes[4] = reverseByte((byte) (calendar.get(Calendar.MINUTE)));
-        dateBytes[5] = reverseByte((byte) (calendar.get(Calendar.SECOND)));
-        dateBytes[6] = reverseByte((byte) ((calendar.get(Calendar.ZONE_OFFSET)
-                + calendar.get(Calendar.DST_OFFSET)) / (60 * 1000 * 15)));
+    return createSmsIntent(number, body, slot, subId)
+}
+
+// 创建pdu
+fun createFakeSms(sender: String, body: String): ByteArray? {
+    var pdu: ByteArray? = null
+    val scBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD("0000000000")
+    val senderBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(sender)
+    val lsmcs = scBytes.size
+    // 时间处理,包括年月日时分秒以及时区和夏令时
+    val dateBytes = ByteArray(7)
+    val calendar: Calendar = GregorianCalendar()
+    dateBytes[0] = reverseByte(calendar[Calendar.YEAR].toByte())
+    dateBytes[1] = reverseByte((calendar[Calendar.MONTH] + 1).toByte())
+    dateBytes[2] = reverseByte(calendar[Calendar.DAY_OF_MONTH].toByte())
+    dateBytes[3] = reverseByte(calendar[Calendar.HOUR_OF_DAY].toByte())
+    dateBytes[4] = reverseByte(calendar[Calendar.MINUTE].toByte())
+    dateBytes[5] = reverseByte(calendar[Calendar.SECOND].toByte())
+    dateBytes[6] = reverseByte(
+        ((calendar[Calendar.ZONE_OFFSET]
+                + calendar[Calendar.DST_OFFSET]) / (60 * 1000 * 15)).toByte()
+    )
+    try {
+        val bo = ByteArrayOutputStream()
+        bo.write(lsmcs) // 短信服务中心长度
+        bo.write(scBytes) // 短信服务中心号码
+        bo.write(0x04)
+        bo.write(sender.length.toByte().toInt()) // 发送方号码长度
+        bo.write(senderBytes) // 发送方号码
+        bo.write(0x00) // 协议标示,00为普通GSM,点对点方式
         try {
-            ByteArrayOutputStream bo = new ByteArrayOutputStream();
-            bo.write(lsmcs);// 短信服务中心长度
-            bo.write(scBytes);// 短信服务中心号码
-            bo.write(0x04);
-            bo.write((byte) sender.length());// 发送方号码长度
-            bo.write(senderBytes);// 发送方号码
-            bo.write(0x00);// 协议标示,00为普通GSM,点对点方式
-            try {
-                String className = "com.android.internal.telephony.GsmAlphabet";
-                Class<?> clazz = Class.forName(className);
-                Method method = clazz.getMethod("stringToGsm7BitPacked", new Class[]{String.class});
-                method.setAccessible(true);
-                byte[] bodybytes = (byte[]) method.invoke(null, body);
-
-                bo.write(0x00); // encoding: 0 for default 7bit
-                bo.write(dateBytes);
-                bo.write(bodybytes);
-            } catch (Exception e) {
-                // 下面是UCS-2编码的处理,中文短信就需要用此种方式
-                byte[] bodyBytes = encodeUCS2(body, null);
-                bo.write(0x08); // encoding: 8 for UCS-2
-                bo.write(dateBytes);
-                bo.write(bodyBytes);// 其中encodeUCS2是从系统中复制过来的,并不是我写的
-                // 源码具体位置在
-                // frameworks/base/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
-            }
-
-            pdu = bo.toByteArray();
-        } catch (IOException e) {
+            val className = "com.android.internal.telephony.GsmAlphabet"
+            val clazz = Class.forName(className)
+            val method = clazz.getMethod(
+                "stringToGsm7BitPacked", *arrayOf<Class<*>>(
+                    String::class.java
+                )
+            )
+            method.isAccessible = true
+            val bodybytes = method.invoke(null, body) as ByteArray
+
+            bo.write(0x00) // encoding: 0 for default 7bit
+            bo.write(dateBytes)
+            bo.write(bodybytes)
+        } catch (e: Exception) {
+            // 下面是UCS-2编码的处理,中文短信就需要用此种方式
+            val bodyBytes = encodeUCS2(body, null)
+            bo.write(0x08) // encoding: 8 for UCS-2
+            bo.write(dateBytes)
+            bo.write(bodyBytes) // 其中encodeUCS2是从系统中复制过来的,并不是我写的
+            // 源码具体位置在
+            // frameworks/base/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
         }
-        return pdu;
-    }
 
-    private static byte reverseByte(byte b) {
-        return (byte) ((b & 0xF0) >> 4 | (b & 0x0F) << 4);
+        pdu = bo.toByteArray()
+    } catch (e: IOException) {
     }
-
-    private static byte[] encodeUCS2(String message, byte[] header) throws UnsupportedEncodingException {
-        byte[] userData, textPart;
-        textPart = message.getBytes(StandardCharsets.UTF_16BE);
-
-        if (header != null) {
-            // Need 1 byte for UDHL
-            userData = new byte[header.length + textPart.length + 1];
-
-            userData[0] = (byte) header.length;
-            System.arraycopy(header, 0, userData, 1, header.length);
-            System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
-        } else {
-            userData = textPart;
-        }
-        byte[] ret = new byte[userData.length + 1];
-        ret[0] = (byte) (userData.length & 0xff);
-        System.arraycopy(userData, 0, ret, 1, userData.length);
-        return ret;
-    }
-
-    public static String toGson(Object obj) {
-        Gson gson = new Gson();
-        return gson.toJson(obj);
+    return pdu
+}
+
+private fun reverseByte(b: Byte): Byte {
+    return ((b.toInt() and 0xF0) shr 4 or ((b.toInt() and 0x0F) shl 4)).toByte()
+}
+
+@Throws(UnsupportedEncodingException::class)
+private fun encodeUCS2(message: String, header: ByteArray?): ByteArray {
+    val userData: ByteArray
+    val textPart = message.toByteArray(StandardCharsets.UTF_16BE)
+
+    if (header != null) {
+        // Need 1 byte for UDHL
+        userData = ByteArray(header.size + textPart.size + 1)
+
+        userData[0] = header.size.toByte()
+        System.arraycopy(header, 0, userData, 1, header.size)
+        System.arraycopy(textPart, 0, userData, header.size + 1, textPart.size)
+    } else {
+        userData = textPart
     }
+    val ret = ByteArray(userData.size + 1)
+    ret[0] = (userData.size and 0xff).toByte()
+    System.arraycopy(userData, 0, ret, 1, userData.size)
+    return ret
+}
+
+fun toGson(obj: Any?): String {
+    val gson = Gson()
+    return gson.toJson(obj)
 }

+ 43 - 0
app/src/main/java/com/example/modifier/utils/Root.kt

@@ -0,0 +1,43 @@
+package com.example.modifier.utils
+
+import android.util.Log
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+suspend fun shellRun(vararg commands: String): Pair<String, String> {
+    var output = ""
+    var error = ""
+    Log.i(com.example.modifier.TAG, "shellRun: \t${commands.joinToString("\n\t\t\t")}")
+    withContext(Dispatchers.IO) {
+        val p = ProcessBuilder("su", "-M").start()
+        p.outputStream.bufferedWriter().use { writer ->
+            commands.forEach { command ->
+                writer.write(command)
+                writer.newLine()
+                writer.flush()
+            }
+        }
+        coroutineScope {
+            launch {
+                p.inputStream.bufferedReader().useLines {
+                    it.forEach {
+                        output += it + "\n"
+                        Log.i(com.example.modifier.TAG, "shellRunOut: $it")
+                    }
+                }
+            }
+            launch {
+                p.errorStream.bufferedReader().useLines {
+                    it.forEach {
+                        error += it + "\n"
+                        Log.e(com.example.modifier.TAG, "shellRunErr: $it")
+                    }
+                }
+            }
+        }
+        p.waitFor()
+    }
+    return Pair(output, error)
+}

+ 57 - 40
app/src/main/java/com/example/modifier/Helpers.kt → app/src/main/java/com/example/modifier/utils/System.kt

@@ -1,61 +1,51 @@
-package com.example.modifier
+package com.example.modifier.utils
 
 import android.accessibilityservice.AccessibilityServiceInfo
+import android.annotation.SuppressLint
 import android.content.Context
 import android.content.pm.PackageManager
+import android.provider.Settings
 import android.text.TextUtils
 import android.util.Log
 import android.view.accessibility.AccessibilityManager
 import androidx.core.app.ActivityCompat
+import com.example.modifier.BuildConfig
+import com.example.modifier.Utils
 import com.example.modifier.http.ktorClient
 import com.example.modifier.service.ModifierService
 import io.ktor.client.request.head
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
+import org.apache.commons.lang3.StringUtils
 import java.time.ZoneId
 import java.time.ZonedDateTime
 import java.time.format.DateTimeFormatter
 import java.util.Locale
+import java.util.Objects
+import java.util.UUID
 
-private const val TAG = "Modifier"
-suspend fun shellRun(vararg commands: String): Pair<String, String> {
-    var output = ""
-    var error = ""
-    Log.i(TAG, "shellRun: \t${commands.joinToString("\n\t\t\t")}")
-    withContext(Dispatchers.IO) {
-        val p = ProcessBuilder("su", "-M").start()
-        p.outputStream.bufferedWriter().use { writer ->
-            commands.forEach { command ->
-                writer.write(command)
-                writer.newLine()
-                writer.flush()
+val uniqueId: String
+    @SuppressLint("HardwareIds")
+    get() {
+        val context = Utils.getContext()
+        Objects.requireNonNull(context)
+        val sharedPrefs =
+            context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE)
+        var uniqueID = sharedPrefs.getString("unique_id", "")!!
+        if (uniqueID.isBlank()) {
+            try {
+                uniqueID =
+                    Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
+            } catch (e: java.lang.Exception) {
+                e.printStackTrace()
             }
         }
-        coroutineScope {
-            launch {
-                p.inputStream.bufferedReader().useLines {
-                    it.forEach {
-                        output += it + "\n"
-                        Log.i(TAG, "shellRunOut: $it")
-                    }
-                }
-            }
-            launch {
-                p.errorStream.bufferedReader().useLines {
-                    it.forEach {
-                        error += it + "\n"
-                        Log.e(TAG, "shellRunErr: $it")
-                    }
-                }
-            }
+        if (StringUtils.isBlank(uniqueID)) {
+            uniqueID = UUID.randomUUID().toString()
         }
-        p.waitFor()
+        sharedPrefs.edit().putString("unique_id", uniqueID).apply()
+        return uniqueID
     }
-    return Pair(output, error)
-}
 
+@SuppressLint("PrivateApi")
 fun getContext(): Context? {
     try {
         val activityThreadClass = Class.forName("android.app.ActivityThread")
@@ -99,7 +89,7 @@ fun isAccessibilityEnabled(): Boolean {
 
     for (enabledService in enabledServices) {
         Log.i(
-            TAG,
+            com.example.modifier.TAG,
             "Enabled service: " + enabledService.resolveInfo.serviceInfo.packageName + "/" + enabledService.resolveInfo.serviceInfo.name
         )
         val enabledServiceInfo = enabledService.resolveInfo.serviceInfo
@@ -142,6 +132,21 @@ suspend fun enableOverlay() {
     }
 }
 
+suspend fun optimize() {
+    val context = Utils.getContext()
+    val packageManager = context.packageManager
+    val info = packageManager.getApplicationInfo("com.google.android.gms", 0)
+
+    shellRun(
+        "dumpsys deviceidle whitelist +com.google.android.apps.messaging",
+        "dumpsys deviceidle whitelist +${BuildConfig.APPLICATION_ID}",
+        "cmd netpolicy add restrict-background-blacklist ${info.uid}",
+        "settings put global window_animation_scale 0",
+        "settings put global transition_animation_scale 0",
+        "settings put global animator_duration_scale 0"
+    )
+}
+
 suspend fun syncTime() {
     try {
         Log.i("Modifier", "syncTime: start")
@@ -154,7 +159,7 @@ suspend fun syncTime() {
         // convert to Asia/Shanghai
         val dateInZone = date.withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
         Log.i(
-            TAG,
+            com.example.modifier.TAG,
             "CurrentTime from Baidu: ${dateInZone.format(DateTimeFormatter.ISO_DATE_TIME)}"
         )
         shellRun(
@@ -165,6 +170,18 @@ suspend fun syncTime() {
             "date \"${dateInZone.format(DateTimeFormatter.ofPattern("MMddHHmmyyyy.ss"))}\""
         )
     } catch (e: Exception) {
-        Log.e(TAG, "Error SyncTime", e)
+        Log.e(com.example.modifier.TAG, "Error SyncTime", e)
     }
+}
+
+suspend fun isRebooted(): Boolean {
+    if (shellRun("getprop rebooted").component1().contains("yes")) {
+        return false
+    }
+    shellRun("setprop rebooted yes")
+    return true
+}
+
+suspend fun setBatteryLevel(level: Int) {
+    shellRun("dumpsys battery set level $level")
 }