xiongzhu 1 год назад
Родитель
Сommit
2850c37a95
21 измененных файлов с 228 добавлено и 212 удалено
  1. 2 0
      app/src/main/AndroidManifest.xml
  2. 3 2
      app/src/main/java/com/example/modifier/Frida.kt
  3. 4 2
      app/src/main/java/com/example/modifier/MyApplication.kt
  4. 3 17
      app/src/main/java/com/example/modifier/Utils.java
  5. 3 1
      app/src/main/java/com/example/modifier/adapter/BackupAdapter.kt
  6. 2 2
      app/src/main/java/com/example/modifier/repo/AppPrefsRepo.kt
  7. 2 1
      app/src/main/java/com/example/modifier/repo/AppStateRepo.kt
  8. 38 10
      app/src/main/java/com/example/modifier/repo/BackupRepository.kt
  9. 17 16
      app/src/main/java/com/example/modifier/repo/GmsgStateRepository.kt
  10. 9 11
      app/src/main/java/com/example/modifier/repo/SpoofedSimInfoRepo.kt
  11. 7 34
      app/src/main/java/com/example/modifier/service/ModifierService.kt
  12. 1 1
      app/src/main/java/com/example/modifier/service/ScreenController.kt
  13. 42 24
      app/src/main/java/com/example/modifier/service/SocketClient.kt
  14. 54 51
      app/src/main/java/com/example/modifier/service/TaskRunner.kt
  15. 5 2
      app/src/main/java/com/example/modifier/ui/backup/BackupFragment.kt
  16. 4 4
      app/src/main/java/com/example/modifier/ui/login/LoginViewModel.kt
  17. 6 3
      app/src/main/java/com/example/modifier/ui/settings/SettingsFragment.kt
  18. 9 11
      app/src/main/java/com/example/modifier/ui/utils/UtilsFragment.kt
  19. 2 2
      app/src/main/java/com/example/modifier/utils/Clash.kt
  20. 3 2
      app/src/main/java/com/example/modifier/utils/GoogleMessage.kt
  21. 12 16
      app/src/main/java/com/example/modifier/utils/System.kt

+ 2 - 0
app/src/main/AndroidManifest.xml

@@ -30,6 +30,8 @@
     <uses-permission
         android:name="android.permission.PACKAGE_USAGE_STATS"
         tools:ignore="ProtectedPermissions" />
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+
 
     <queries>
         <package android:name="com.github.metacubex.clash" />

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

@@ -3,6 +3,7 @@ package com.example.modifier
 import android.util.Log
 import androidx.core.content.ContextCompat
 import com.example.modifier.utils.createFakeSms
+import com.example.modifier.utils.getContext
 import com.example.modifier.utils.shellRun
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.coroutineScope
@@ -29,7 +30,7 @@ class Frida {
                 script = null
             }
 
-            val context = Utils.getContext()
+            val context = getContext()
             val dataDir = ContextCompat.getDataDir(context)
             Utils.copyAssetFolder(context.assets, "bin", File(dataDir, "bin").path)
             val binPath = File(dataDir, "bin/frida-inject-16.3.3-android-arm64").path
@@ -100,7 +101,7 @@ class Frida {
 }
 
 suspend fun sendSmsFrida(sender: String, msg: String) {
-    val context = Utils.getContext()
+    val context = getContext()
     try {
         val dataDir = ContextCompat.getDataDir(context)
         Utils.copyAssetFolder(context.assets, "bin", File(dataDir, "bin").path)

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

@@ -11,7 +11,9 @@ const val baseTag = "Modifier"
 
 @HiltAndroidApp
 class MyApplication : Application() {
-    private val tag = "$baseTag/MyApplication"
+    companion object {
+        private const val TAG = "$baseTag/MyApplication"
+    }
 
     lateinit var container: AppContainer
 
@@ -20,6 +22,6 @@ class MyApplication : Application() {
         //        DynamicColors.applyToActivitiesIfAvailable(this);
         container = AppDataContainer(this)
 
-        Log.i(tag, "server=${AppPrefsRepo.instance.appPrefs.value.server}")
+        Log.i(TAG, "server=${AppPrefsRepo.instance.appPrefs.value.server}")
     }
 }

+ 3 - 17
app/src/main/java/com/example/modifier/Utils.java

@@ -1,5 +1,7 @@
 package com.example.modifier;
 
+import static com.example.modifier.utils.SystemKt.getContext;
+
 import android.accounts.AccountManager;
 import android.accounts.AuthenticatorDescription;
 import android.content.Context;
@@ -8,6 +10,7 @@ import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.util.Log;
 
 import com.google.android.material.button.MaterialButton;
 import com.google.android.material.progressindicator.CircularProgressIndicatorSpec;
@@ -27,23 +30,6 @@ import java.util.Arrays;
 
 public class Utils {
 
-    public static Context getContext() {
-        try {
-            Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
-            Method currentActivityThreadMethod = activityThreadClass.getMethod("currentActivityThread");
-            currentActivityThreadMethod.setAccessible(true);
-            Object currentActivityThread = currentActivityThreadMethod.invoke(null);
-            Method getApplicationMethod = activityThreadClass.getMethod("getApplication");
-            getApplicationMethod.setAccessible(true);
-            return (Context) getApplicationMethod.invoke(currentActivityThread);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
-
-
-
     public static void makeLoadingButton(Context context, MaterialButton button) {
         CircularProgressIndicatorSpec spec = new CircularProgressIndicatorSpec(context, null, 0,
                 com.google.android.material.R.style.Widget_Material3_CircularProgressIndicator_ExtraSmall);

+ 3 - 1
app/src/main/java/com/example/modifier/adapter/BackupAdapter.kt

@@ -33,7 +33,9 @@ class BackupAdapter(
     private val onItemClickListener: OnItemClickListener
 ) :
     RecyclerView.Adapter<BackupViewHolder>() {
-    private val tag = "$baseTag/BackupAdapter"
+    companion object {
+        private const val TAG = "$baseTag/BackupAdapter"
+    }
 
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BackupViewHolder {
         val binding = ItemBackupBinding.inflate(

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

@@ -9,8 +9,8 @@ import androidx.datastore.preferences.core.edit
 import androidx.datastore.preferences.core.stringPreferencesKey
 import androidx.datastore.preferences.preferencesDataStore
 import com.example.modifier.BuildConfig
-import com.example.modifier.Utils
 import com.example.modifier.data.AppPreferences
+import com.example.modifier.utils.getContext
 import com.example.modifier.utils.uniqueId
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -23,7 +23,7 @@ val Context.appPreferencesDataStore by preferencesDataStore(name = "${BuildConfi
 class AppPrefsRepo private constructor(private val context: Context) {
 
     companion object {
-        val instance by lazy { AppPrefsRepo(Utils.getContext()) }
+        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { AppPrefsRepo(getContext()) }
     }
 
     private object PreferencesKeys {

+ 2 - 1
app/src/main/java/com/example/modifier/repo/AppStateRepo.kt

@@ -12,6 +12,7 @@ import com.example.modifier.baseTag
 import com.example.modifier.data.AppRuntimeFlags
 import com.example.modifier.data.AppState
 import com.example.modifier.enums.RequestNumberState
+import com.example.modifier.utils.getContext
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -25,7 +26,7 @@ class AppStateRepo private constructor(private val context: Context) {
 
     companion object {
         private const val TAG = "${baseTag}/AppStateRepo"
-        val instance by lazy { AppStateRepo(Utils.getContext()) }
+        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { AppStateRepo(getContext()) }
     }
 
     private object PreferencesKeys {

+ 38 - 10
app/src/main/java/com/example/modifier/repo/BackupRepository.kt

@@ -2,8 +2,8 @@ package com.example.modifier.repo
 
 import android.annotation.SuppressLint
 import android.content.Context
+import android.util.Log
 import androidx.core.content.ContextCompat
-import com.example.modifier.Utils
 import com.example.modifier.baseTag
 import com.example.modifier.constants.CMD_MESSAGING_APP
 import com.example.modifier.constants.CMD_RCS_SETTINGS_ACTIVITY
@@ -12,6 +12,7 @@ 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.enums.RcsConfigureState
 import com.example.modifier.extension.clear
 import com.example.modifier.extension.disable
 import com.example.modifier.extension.enable
@@ -20,23 +21,28 @@ import com.example.modifier.http.api.RcsNumberApi
 import com.example.modifier.model.SpoofedSimInfo
 import com.example.modifier.service.ModifierService
 import com.example.modifier.utils.clearConv
+import com.example.modifier.utils.getContext
 import com.example.modifier.utils.shellRun
 import kotlinx.coroutines.delay
 import java.io.File
 import java.util.Date
 import java.util.Optional
+import kotlin.time.Duration.Companion.seconds
 
 class BackupRepository(
     private val context: Context,
     private val dao: BackupItemDao,
 ) {
-    private val tag = "$baseTag/BackupRepository"
+    companion object {
+        private const val TAG = "$baseTag/BackupRepository"
+    }
+
     suspend fun backup(type: String, sendCount: Int, stock: Int = 0): BackupItem {
         val spoofedSimInfoRepo = SpoofedSimInfoRepo.instance
         clearConv()
         delay(3000)
 //        ModifierService.instance!!.toggleRcsSwitch(false)
-        val context = Utils.getContext()
+        val context = getContext()
         val dest = File(
             ContextCompat.getExternalFilesDirs(context, "backup")[0],
             System.currentTimeMillis().toString()
@@ -131,7 +137,7 @@ class BackupRepository(
         )
 
         shellRun(PACKAGE_MESSAGING.clear(), "sleep 2", PACKAGE_MESSAGING.disable())
-        for (pkg in listOf(PACKAGE_MESSAGING)) {
+        for (pkg in packages) {
             for (item in listOf("data.tar.gz", "data_de.tar.gz", "data_ext.tar.gz")) {
                 val file = File("${backup.path}/$pkg/$item")
                 if (!file.exists()) {
@@ -153,9 +159,9 @@ class BackupRepository(
         val taskRunner = ModifierService.instance?.taskRunner
         shellRun(
 //            "sed -i 's/<boolean name=\"enable_rcs\" value=\"true\" \\/>/<boolean name=\"enable_rcs\" value=\"false\" \\/>/g' /data/data/com.google.android.apps.messaging/shared_prefs/bugle.xml",
-            PACKAGE_GSF.clear(),
+            PACKAGE_GSF.kill(),
             "sleep 1",
-            PACKAGE_GMS.clear(),
+            PACKAGE_GMS.kill(),
             "settings put secure location_mode 0",
             "pm revoke com.google.android.gms android.permission.GET_ACCOUNTS",
             "pm revoke com.google.android.gms android.permission.SYSTEM_ALERT_WINDOW",
@@ -183,7 +189,7 @@ class BackupRepository(
             "pm revoke com.google.android.gms android.permission.WRITE_CALL_LOG",
             "pm revoke com.google.android.gms android.permission.BODY_SENSORS",
             "pm revoke com.google.android.gms android.permission.DUMP",
-            "sleep 30",
+//            "sleep 30",
             PACKAGE_MESSAGING.enable(),
             "sleep 1",
             CMD_MESSAGING_APP,
@@ -196,11 +202,32 @@ class BackupRepository(
             "sleep 5",
         )
 
-        val success = taskRunner != null
-                && taskRunner.screenController.toggleRcsSwitch(true)
-                && taskRunner.checkRcsA10y()
+        val success = run isSuccess@{
+            if (backup.stock != 1) {
+                return@isSuccess true
+            }
+            if (taskRunner == null) return@isSuccess false
+            if (!taskRunner.screenController.toggleRcsSwitch(true)) {
+                shellRun(
+                    PACKAGE_GMS.kill(),
+                    PACKAGE_MESSAGING.kill(),
+                    "sleep 5",
+                    CMD_MESSAGING_APP
+                )
+                delay(5000)
+                if (!taskRunner.screenController.toggleRcsSwitch(true)) {
+                    return@isSuccess false
+                }
+            }
+            val state = taskRunner.gmsgStateRepository.waitForRcsState(
+                arrayOf(RcsConfigureState.CONFIGURED), 120.seconds
+            )
+            if (state != RcsConfigureState.CONFIGURED) return@isSuccess false
+            true
+        }
 
         if (success) {
+            Log.i(TAG, "restore: success")
             spoofedSimInfoRepo.updateSpoofedSimInfo(
                 spoofedSimInfo = simInfo.copy(available = true),
                 suspend = false
@@ -212,6 +239,7 @@ class BackupRepository(
             }
             return true
         } else {
+            Log.i(TAG, "restore: failed")
             if (backup.stock == 1) {
 //                backup.stock = 3
 //                dao.update(backup)

+ 17 - 16
app/src/main/java/com/example/modifier/repo/GoogleMessageStateRepository.kt → app/src/main/java/com/example/modifier/repo/GmsgStateRepository.kt

@@ -1,37 +1,38 @@
 package com.example.modifier.repo
 
-import android.content.Context
-import android.os.Handler
-import android.os.Looper
 import android.util.Log
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.Observer
 import com.example.modifier.baseTag
 import com.example.modifier.enums.RcsConfigureState
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.cancel
-import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.cancellable
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.isActive
-import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withTimeoutOrNull
 import org.apache.commons.collections4.queue.CircularFifoQueue
-import kotlin.coroutines.resume
 import kotlin.time.Duration
 
-class GoogleMessageStateRepository(context: Context) {
-    private val tag = "$baseTag/GoogleMessageStateRepository"
-    private val handler = Handler(Looper.getMainLooper())
+class GmsgStateRepository() {
     val logs = MutableStateFlow("")
     val rcsConfigureState = MutableStateFlow(RcsConfigureState.CONFIGURED)
 
-    suspend fun startLogging() {
+    companion object {
+        private const val TAG = "$baseTag/GoogleMessageStateRepository"
+    }
+
+    init {
+        CoroutineScope(Dispatchers.IO).launch {
+            startLogging()
+        }
+    }
+
+    private suspend fun startLogging() {
         try {
             val logsCache = CircularFifoQueue<String>(128)
-            val p = Runtime.getRuntime().exec("su")
+            val p = withContext(Dispatchers.IO) {
+                Runtime.getRuntime().exec("su")
+            }
             p.outputStream.bufferedWriter().use { writer ->
                 writer.write("logcat -c")
                 writer.newLine()

+ 9 - 11
app/src/main/java/com/example/modifier/repo/SpoofedSimInfoRepo.kt

@@ -19,6 +19,7 @@ import com.example.modifier.model.SpoofedSimInfo
 import com.example.modifier.utils.genICCID
 import com.example.modifier.utils.genIMEI
 import com.example.modifier.utils.genIMSI
+import com.example.modifier.utils.getContext
 import com.example.modifier.utils.hasPermission
 import com.example.modifier.utils.isOldVersion
 import com.example.modifier.utils.resumePackage
@@ -28,17 +29,15 @@ import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
-import kotlin.coroutines.coroutineContext
 
 val Context.simInfoDataStore by preferencesDataStore(name = "${BuildConfig.APPLICATION_ID}.simInfo")
 
 class SpoofedSimInfoRepo private constructor(private val context: Context) {
-    private val tag = "$baseTag/SpoofedSimInfoRepository"
 
     companion object {
-        val instance by lazy { SpoofedSimInfoRepo(Utils.getContext()) }
+        private const val TAG = "$baseTag/SpoofedSimInfoRepository"
+        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { SpoofedSimInfoRepo(getContext()) }
     }
 
     private object PreferencesKeys {
@@ -143,7 +142,7 @@ class SpoofedSimInfoRepo private constructor(private val context: Context) {
                 "setprop persist.spoof.carrier.name '${spoofedSimInfo.carrierName}'",
             )
 
-            val context = Utils.getContext()
+            val context = getContext()
             val subscriptionManager: SubscriptionManager =
                 context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
 
@@ -154,7 +153,7 @@ class SpoofedSimInfoRepo private constructor(private val context: Context) {
                     if (info != null) {
                         val mcc = info.mccString
                         val mnc = info.mncString
-                        Log.i(tag, "mccmnc spoofed: $mcc$mnc")
+                        Log.i(TAG, "mccmnc spoofed: $mcc$mnc")
                     }
                 }
             }
@@ -162,13 +161,13 @@ class SpoofedSimInfoRepo private constructor(private val context: Context) {
                 resumePackage(PACKAGE_GMS, PACKAGE_MESSAGING)
             }
         } catch (e: Exception) {
-            e.printStackTrace()
+            Log.e(TAG, "Error updateSpoofedSimInfo: ${e.message}", e)
         }
     }
 
     suspend fun mock() {
         if (isOldVersion(context)) {
-            val content = Utils.getContext().assets.open("us_numbers.txt").bufferedReader().use {
+            val content = getContext().assets.open("us_numbers.txt").bufferedReader().use {
                 it.readText()
             }
             // get random number
@@ -195,9 +194,8 @@ class SpoofedSimInfoRepo private constructor(private val context: Context) {
                     )
                 }
         } else {
-            val content = Utils.getContext().assets.open("us_numbers.txt").bufferedReader().use {
-                it.readText()
-            }
+            val content = getContext().assets.open("us_numbers.txt")
+                .bufferedReader().use { it.readText() }
             // get random number
             content.split("\n")
                 .filter { it.isNotBlank() }

+ 7 - 34
app/src/main/java/com/example/modifier/service/ModifierService.kt

@@ -23,8 +23,8 @@ import androidx.appcompat.widget.PopupMenu
 import androidx.core.content.ContextCompat
 import com.example.modifier.BuildConfig
 import com.example.modifier.R
-import com.example.modifier.baseTag
 import com.example.modifier.TraverseResult
+import com.example.modifier.baseTag
 import com.example.modifier.data.AppDatabase
 import com.example.modifier.databinding.FloatingWindowBinding
 import com.example.modifier.enums.RequestNumberState
@@ -32,7 +32,7 @@ import com.example.modifier.http.api.RcsNumberApi
 import com.example.modifier.repo.AppPrefsRepo
 import com.example.modifier.repo.AppStateRepo
 import com.example.modifier.repo.BackupRepository
-import com.example.modifier.repo.GoogleMessageStateRepository
+import com.example.modifier.repo.GmsgStateRepository
 import com.example.modifier.repo.SpoofedSimInfoRepo
 import com.example.modifier.utils.clearConv
 import com.example.modifier.utils.hasRootAccess
@@ -56,7 +56,7 @@ import java.util.concurrent.atomic.AtomicReference
 import kotlin.math.max
 import kotlin.math.min
 import kotlin.time.Duration.Companion.minutes
-import kotlin.time.Duration.Companion.seconds
+
 
 @SuppressLint("SetTextI18n")
 class ModifierService : AccessibilityService() {
@@ -72,7 +72,7 @@ class ModifierService : AccessibilityService() {
     private val backupItemDao by lazy { AppDatabase.getDatabase(this).itemDao() }
     private val appPrefsRepo = AppPrefsRepo.instance
     private val appStateRepo = AppStateRepo.instance
-    private val googleMessageStateRepository by lazy { GoogleMessageStateRepository(this) }
+    private val gmsgStateRepository by lazy { GmsgStateRepository() }
     private val screenInspector by lazy { ScreenInspector(this) }
     private val screenController by lazy { ScreenController(this, screenInspector) }
     private val spoofedSimInfoRepo = SpoofedSimInfoRepo.instance
@@ -171,7 +171,7 @@ class ModifierService : AccessibilityService() {
                 appStateRepo,
                 appPrefsRepo,
                 spoofedSimInfoRepo,
-                googleMessageStateRepository,
+                gmsgStateRepository,
                 backupRepository,
             )
             launch {
@@ -233,10 +233,6 @@ class ModifierService : AccessibilityService() {
                             }
                         }
                     }
-
-                    withContext(Dispatchers.IO) {
-                        reportDeviceStatues()
-                    }
                 }
             }
 
@@ -248,12 +244,7 @@ class ModifierService : AccessibilityService() {
                     if (this@ModifierService::socketClient.isInitialized) {
                         socketClient.disconnect()
                     }
-                    socketClient = SocketClient(
-                        appPrefsRepo.appPrefs.value.id,
-                        appPrefsRepo.appPrefs.value.server,
-                        appPrefsRepo.appPrefs.value.name,
-                        taskRunner
-                    )
+                    socketClient = SocketClient(taskRunner)
                 }
             }
             launch {
@@ -264,7 +255,7 @@ class ModifierService : AccessibilityService() {
                 }
             }
             launch {
-                googleMessageStateRepository.logs.debounce(200).collect {
+                gmsgStateRepository.logs.debounce(200).collect {
                     withContext(Dispatchers.Main) {
                         binding.tvLog.text = it
                         delay(100)
@@ -286,9 +277,6 @@ class ModifierService : AccessibilityService() {
             if (!hasRoot) {
                 System.exit(0)
             }
-            CoroutineScope(kotlin.coroutines.coroutineContext).launch {
-                googleMessageStateRepository.startLogging()
-            }
             if (isRebooted()) {
                 delay(2.minutes)
             } else {
@@ -302,11 +290,6 @@ class ModifierService : AccessibilityService() {
             appStateRepo.updateRuntimeFlags(preparing = false)
 
             val timer = Timer()
-            timer.schedule(object : TimerTask() {
-                override fun run() {
-                    reportDeviceStatues()
-                }
-            }, 0, 3.seconds.inWholeMilliseconds)
             if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
                 timer.schedule(object : TimerTask() {
                     override fun run() {
@@ -418,16 +401,6 @@ class ModifierService : AccessibilityService() {
         popup.show()
     }
 
-    private fun reportDeviceStatues() {
-        if (this::socketClient.isInitialized) {
-            socketClient.reportDeviceStatues(
-                appStateRepo.appState.value.send,
-                appStateRepo.appState.value.busy,
-                spoofedSimInfoRepo.spoofedSimInfo.value.country
-            )
-        }
-    }
-
     private suspend fun storeNumbers() {
         appStateRepo.updateRuntimeFlags(storing = true)
         var success = true

+ 1 - 1
app/src/main/java/com/example/modifier/service/ScreenController.kt

@@ -10,7 +10,7 @@ import com.example.modifier.utils.currentActivity
 import com.example.modifier.utils.shellRun
 import kotlinx.coroutines.delay
 
-class ScreenController(val context: AccessibilityService, val inspector: ScreenInspector) {
+class ScreenController(val context: AccessibilityService, private val inspector: ScreenInspector) {
 
     suspend fun toggleRcsSwitch(state: Boolean, retry: Int = 3): Boolean {
         val res = TraverseResult()

+ 42 - 24
app/src/main/java/com/example/modifier/service/SocketClient.kt

@@ -12,6 +12,9 @@ import com.example.modifier.model.TaskAction
 import com.example.modifier.model.TaskConfig
 import com.example.modifier.model.TaskExecutionResult
 import com.example.modifier.model.UpdateDeviceAction
+import com.example.modifier.repo.AppPrefsRepo
+import com.example.modifier.repo.AppStateRepo
+import com.example.modifier.repo.SpoofedSimInfoRepo
 import com.example.modifier.serializer.Json
 import com.example.modifier.utils.getIPAddress
 import io.socket.client.IO
@@ -24,38 +27,48 @@ import kotlinx.coroutines.launch
 import kotlinx.serialization.encodeToString
 import org.json.JSONException
 import org.json.JSONObject
+import java.util.Timer
+import java.util.TimerTask
+import kotlin.concurrent.schedule
+import kotlin.time.Duration.Companion.seconds
 
 class SocketClient(
-    private val id: String,
-    private val server: String,
-    private val name: String,
     private val taskRunner: TaskRunner
 ) : Emitter.Listener {
-    private val tag = "$baseTag/SocketClient"
+    companion object {
+        private const val TAG = "$baseTag/SocketClient"
+    }
+
     private val mSocketOpts: IO.Options = IO.Options()
     private val mSocket: Socket
 
     init {
+        val appPrefs = AppPrefsRepo.instance.appPrefs.value
         mSocketOpts.query =
-            "model=${Build.MODEL}&name=${name}&id=$id&version=${BuildConfig.VERSION_CODE}&ip=${
+            "model=${Build.MODEL}&name=${appPrefs.name}&id=${appPrefs.id}&version=${BuildConfig.VERSION_CODE}&ip=${
                 getIPAddress().joinToString(",")
             }}"
         mSocketOpts.transports = arrayOf("websocket")
 
-        mSocket = IO.socket(server, mSocketOpts)
+        mSocket = IO.socket(appPrefs.server, mSocketOpts)
 
         mSocket.on("message", this)
         mSocket.on(Socket.EVENT_CONNECT) { this.onConnected(it) }
         mSocket.on(Socket.EVENT_DISCONNECT) { this.onDisconnected(it) }
         mSocket.on(Socket.EVENT_CONNECT_ERROR) {
             it.forEach {
-                Log.e(tag, "Connection error", it as Throwable)
+                Log.e(TAG, "Connection error", it as Throwable)
             }
         }
         try {
             mSocket.connect()
         } catch (e: Exception) {
-            Log.e(tag, "connect error", e)
+            Log.e(TAG, "connect error", e)
+        }
+
+        val timer = Timer()
+        timer.schedule(0, 3000) {
+            reportDeviceStatues()
         }
     }
 
@@ -66,7 +79,7 @@ class SocketClient(
 
     override fun call(vararg args: Any?) {
         if (args.isNotEmpty()) {
-            Log.i(tag, "Received message: " + args[0])
+            Log.i(TAG, "Received message: " + args[0])
             if (args[0] is JSONObject) {
                 val json = args[0] as JSONObject
                 val action = json.optString("action")
@@ -105,7 +118,7 @@ class SocketClient(
                             })
                         }
                     } catch (e: Exception) {
-                        Log.e(tag, "taskAction error", e)
+                        Log.e(TAG, "taskAction error", e)
                         socketCallback(id = id, status = -1, error = e.message)
                     }
                 } else if ("installApk" == action) {
@@ -124,7 +137,7 @@ class SocketClient(
                             })
                         }
                     } catch (e: Exception) {
-                        Log.e(tag, "installApk error", e)
+                        Log.e(TAG, "installApk error", e)
                         socketCallback(id = id, status = -1, error = e.message)
                     }
                 } else if ("runScript" == action) {
@@ -143,7 +156,7 @@ class SocketClient(
                             })
                         }
                     } catch (e: Exception) {
-                        Log.e(tag, "runScript error", e)
+                        Log.e(TAG, "runScript error", e)
                         socketCallback(id = id, status = -1, error = e.message)
                     }
                 } else if ("updateDevice" == action) {
@@ -162,7 +175,7 @@ class SocketClient(
                             })
                         }
                     } catch (e: Exception) {
-                        Log.e(tag, "updateDevice error", e)
+                        Log.e(TAG, "updateDevice error", e)
                         socketCallback(id = id, status = -1, error = e.message)
                     }
                 }
@@ -170,25 +183,30 @@ class SocketClient(
         }
     }
 
-    fun onConnected(vararg args: Any) {
-        Log.i(tag, "Connected to server")
+    private fun onConnected(vararg args: Any) {
+        Log.i(TAG, "Connected to server")
         CoroutineScope(Dispatchers.IO).launch {
             delay(500)
+            reportDeviceStatues()
         }
     }
 
-    fun onDisconnected(vararg args: Any) {}
-
+    private fun onDisconnected(vararg args: Any) {}
 
-    fun reportDeviceStatues(canSend: Boolean, busy: Boolean, currentCountry: String) {
+    fun reportDeviceStatues() {
+        if (!mSocket.connected()) {
+            return
+        }
         val data = JSONObject()
         try {
             data.put("action", "updateDevice")
             val dataObj = JSONObject()
-            dataObj.put("canSend", canSend)
-            dataObj.put("busy", busy)
-            dataObj.put("currentCountry", currentCountry)
+            dataObj.put("canSend", AppStateRepo.instance.appState.value.send)
+            dataObj.put("busy", AppStateRepo.instance.appState.value.busy)
+            dataObj.put("currentCountry", SpoofedSimInfoRepo.instance.spoofedSimInfo.value.country)
             dataObj.put("ip", getIPAddress().joinToString(","))
+            dataObj.put("requesting", AppStateRepo.instance.appState.value.requesting)
+            dataObj.put("suspended", AppStateRepo.instance.appState.value.suspended)
             data.put("data", dataObj)
             mSocket.emit("message", data)
         } catch (e: JSONException) {
@@ -210,7 +228,7 @@ class SocketClient(
                 )
             )
         } catch (e: Exception) {
-            Log.e(tag, "emitEvent error", e)
+            Log.e(TAG, "emitEvent error", e)
         }
     }
 
@@ -228,7 +246,7 @@ class SocketClient(
                 )
             )
         } catch (e: Exception) {
-            Log.e(tag, "emitEvent error", e)
+            Log.e(TAG, "emitEvent error", e)
         }
     }
 
@@ -252,7 +270,7 @@ class SocketClient(
                 )
             )
         } catch (e: Exception) {
-            Log.e(tag, "emitEvent error", e)
+            Log.e(TAG, "emitEvent error", e)
         }
     }
 }

+ 54 - 51
app/src/main/java/com/example/modifier/service/TaskRunner.kt

@@ -32,7 +32,7 @@ import com.example.modifier.model.UpdateDeviceAction
 import com.example.modifier.repo.AppPrefsRepo
 import com.example.modifier.repo.AppStateRepo
 import com.example.modifier.repo.BackupRepository
-import com.example.modifier.repo.GoogleMessageStateRepository
+import com.example.modifier.repo.GmsgStateRepository
 import com.example.modifier.repo.SpoofedSimInfoRepo
 import com.example.modifier.utils.clearConv
 import com.example.modifier.utils.genICCID
@@ -75,10 +75,13 @@ class TaskRunner(
     private val appStateRepo: AppStateRepo,
     private val appPrefsRepo: AppPrefsRepo,
     private val spoofedSimInfoRepo: SpoofedSimInfoRepo,
-    private val googleMessageStateRepository: GoogleMessageStateRepository,
+    val gmsgStateRepository: GmsgStateRepository,
     private val backupRepository: BackupRepository
 ) {
-    private val tag = "$baseTag/TaskRunner"
+    companion object {
+        private const val TAG = "$baseTag/TaskRunner"
+    }
+
     private var lastSend = 0L
     private var currentTaskId = 0
     private var requestMode = 1
@@ -88,10 +91,10 @@ class TaskRunner(
         body: String,
         taskConfig: TaskConfig
     ): Boolean {
-        Log.i(tag, "Sending SMS to $to: $body")
+        Log.i(TAG, "Sending SMS to $to: $body")
         context.startActivity(smsIntent(to, body))
         try {
-            Log.i(tag, "Command executed successfully, waiting for app to open...")
+            Log.i(TAG, "Command executed successfully, waiting for app to open...")
             delay(1000)
             var success = false
             var traverseResult = TraverseResult()
@@ -119,15 +122,15 @@ class TaskRunner(
             }
             if (traverseResult.isRcsCapable) {
                 if (traverseResult.sendBtn == null) {
-                    Log.i(tag, "Send button not found")
+                    Log.i(TAG, "Send button not found")
                 } else if (taskConfig.e2ee == 2 && !traverseResult.encrypted) {
-                    Log.i(tag, "E2EE not detected")
+                    Log.i(TAG, "E2EE not detected")
                 } else {
-                    Log.i(tag, "Clicking send button")
+                    Log.i(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(TAG, "Waiting for RCS interval")
                         delay(taskConfig.rcsInterval - dt)
                     }
                     traverseResult.sendBtn!!.performAction(AccessibilityNodeInfo.ACTION_CLICK)
@@ -135,11 +138,11 @@ class TaskRunner(
                     success = true
                 }
             } else {
-                Log.i(tag, "RCS not detected")
+                Log.i(TAG, "RCS not detected")
             }
             appStateRepo.incrementExecutedNum(success)
             Log.i(
-                tag,
+                TAG,
                 "executedNum: ${appStateRepo.appState.value.executedNum}, successNum: ${appStateRepo.appState.value.successNum}"
             )
             delay(1000)
@@ -188,7 +191,7 @@ class TaskRunner(
                         fail.add(taskItem.id)
                     }
                 } catch (e: Exception) {
-                    Log.e(tag, "runTaskError: ${e.message}", e)
+                    Log.e(TAG, "runTaskError: ${e.message}", e)
                     fail.add(taskItem.id)
                 }
             }
@@ -216,7 +219,7 @@ class TaskRunner(
                 appStateRepo.updateRuntimeFlags(checkingConnection = false)
             }
         } catch (e: Exception) {
-            Log.e(tag, "runTaskError: ${e.message}", e)
+            Log.e(TAG, "runTaskError: ${e.message}", e)
             onError(e)
             appStateRepo.updateRuntimeFlags(running = false)
         }
@@ -228,10 +231,10 @@ class TaskRunner(
                 while (true) {
                     delay(100)
                     appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.RESET)
-                    googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
+                    gmsgStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
                     spoofedSimInfoRepo.mock()
                     resetAll()
-                    var switchAppear = googleMessageStateRepository.waitForRcsState(
+                    var switchAppear = gmsgStateRepository.waitForRcsState(
                         arrayOf(RcsConfigureState.WAITING_FOR_TOS),
                         2.minutes
                     )?.let {
@@ -242,26 +245,26 @@ class TaskRunner(
                             PACKAGE_GMS.kill(), PACKAGE_MESSAGING.kill(), "sleep 1",
                             CMD_MESSAGING_APP
                         )
-                        switchAppear = googleMessageStateRepository.waitForRcsState(
+                        switchAppear = gmsgStateRepository.waitForRcsState(
                             arrayOf(RcsConfigureState.WAITING_FOR_TOS),
                             5.minutes
                         )?.let {
                             it == RcsConfigureState.WAITING_FOR_TOS
                         }
                         if (switchAppear != true) {
-                            Log.e(tag, "RCS not entered default on state, retrying...")
+                            Log.e(TAG, "RCS not entered default on state, retrying...")
                             continue
                         }
                     }
                     if (!screenController.toggleRcsSwitch(false)) {
-                        Log.e(tag, "RCS switch not turned off, retrying...")
+                        Log.e(TAG, "RCS switch not turned off, retrying...")
                         continue
                     }
                     if (!screenController.toggleRcsSwitch(true)) {
-                        Log.e(tag, "RCS switch not turned on, retrying...")
+                        Log.e(TAG, "RCS switch not turned on, retrying...")
                         continue
                     }
-                    var resetSuccess = googleMessageStateRepository.waitForRcsState(
+                    var resetSuccess = gmsgStateRepository.waitForRcsState(
                         arrayOf(
                             RcsConfigureState.READY
                         ), 30.seconds
@@ -270,13 +273,13 @@ class TaskRunner(
                         screenController.toggleRcsSwitch(false)
                         delay(1000)
                         screenController.toggleRcsSwitch(true)
-                        resetSuccess = googleMessageStateRepository.waitForRcsState(
+                        resetSuccess = gmsgStateRepository.waitForRcsState(
                             arrayOf(
                                 RcsConfigureState.READY
                             ), 1.minutes
                         ).let { it == RcsConfigureState.READY }
                     }
-                    Log.i(tag, "waitForRcsState: $resetSuccess")
+                    Log.i(TAG, "waitForRcsState: $resetSuccess")
                     appStateRepo.resetRequestedNum()
                     if (resetSuccess) {
                         delay(3000)
@@ -289,10 +292,10 @@ class TaskRunner(
                 while (true) {
                     delay(100)
                     appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.RESET)
-                    googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
+                    gmsgStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
                     spoofedSimInfoRepo.mock()
                     resetAll()
-                    var switchAppear = googleMessageStateRepository.waitForRcsState(
+                    var switchAppear = gmsgStateRepository.waitForRcsState(
                         arrayOf(RcsConfigureState.WAITING_FOR_DEFAULT_ON),
                         1.minutes
                     )?.let {
@@ -303,23 +306,23 @@ class TaskRunner(
                             PACKAGE_GMS.kill(), PACKAGE_MESSAGING.kill(), "sleep 1",
                             CMD_MESSAGING_APP
                         )
-                        switchAppear = googleMessageStateRepository.waitForRcsState(
+                        switchAppear = gmsgStateRepository.waitForRcsState(
                             arrayOf(RcsConfigureState.WAITING_FOR_DEFAULT_ON),
                             2.minutes
                         )?.let {
                             it == RcsConfigureState.WAITING_FOR_DEFAULT_ON
                         }
                         if (switchAppear != true) {
-                            Log.e(tag, "RCS not entered default on state, retrying...")
+                            Log.e(TAG, "RCS not entered default on state, retrying...")
                             continue
                         }
                     }
                     val switchOn = screenController.toggleRcsSwitch(true)
                     if (!switchOn) {
-                        Log.e(tag, "RCS switch not turned on, retrying...")
+                        Log.e(TAG, "RCS switch not turned on, retrying...")
                         continue
                     }
-                    var resetSuccess = googleMessageStateRepository.waitForRcsState(
+                    var resetSuccess = gmsgStateRepository.waitForRcsState(
                         arrayOf(
                             RcsConfigureState.READY
                         ), 30.seconds
@@ -328,13 +331,13 @@ class TaskRunner(
                         screenController.toggleRcsSwitch(false)
                         delay(1000)
                         screenController.toggleRcsSwitch(true)
-                        resetSuccess = googleMessageStateRepository.waitForRcsState(
+                        resetSuccess = gmsgStateRepository.waitForRcsState(
                             arrayOf(
                                 RcsConfigureState.READY
                             ), 1.minutes
                         ).let { it == RcsConfigureState.READY }
                     }
-                    Log.i(tag, "waitForRcsState: $resetSuccess")
+                    Log.i(TAG, "waitForRcsState: $resetSuccess")
                     appStateRepo.resetRequestedNum()
                     if (resetSuccess) {
                         delay(1000)
@@ -347,14 +350,14 @@ class TaskRunner(
 
     suspend fun requestNumberAtomic() {
         appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.REQUEST)
-        googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
+        gmsgStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
         val device = DeviceApi.getDevice(appPrefsRepo.appPrefs.value.id)
         val rcsNumber = RcsNumberApi.getRcsNumber(
             appPrefsRepo.appPrefs.value.id,
             currentTaskId,
             device.pinCountry
         )
-        Log.i(tag, "requestNumber response: $rcsNumber")
+        Log.i(TAG, "requestNumber response: $rcsNumber")
 
         appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.OTP_1)
 
@@ -401,7 +404,7 @@ class TaskRunner(
         if (sendOtpTimeout > 2.minutes) {
             sendOtpTimeout = 2.minutes
         }
-        if (googleMessageStateRepository.waitForRcsState(
+        if (gmsgStateRepository.waitForRcsState(
                 arrayOf(RcsConfigureState.WAITING_FOR_OTP),
                 sendOtpTimeout
             ) != RcsConfigureState.WAITING_FOR_OTP
@@ -410,7 +413,7 @@ class TaskRunner(
             if (!screenController.toggleRcsSwitch(true)) {
                 throw RequestNumberException(ErrorCode.CODE_RCS_TOGGLED_OFF)
             }
-            if (RcsConfigureState.REPLAY_REQUEST == googleMessageStateRepository.rcsConfigureState.value) {
+            if (RcsConfigureState.REPLAY_REQUEST == gmsgStateRepository.rcsConfigureState.value) {
                 throw RequestNumberException(ErrorCode.CODE_REPLAY_RETRY)
             }
             throw RequestNumberException(ErrorCode.CODE_OTP_NOT_SENT)
@@ -432,7 +435,7 @@ class TaskRunner(
             repeat(2) {
                 injectOTP(otp)
                 val state =
-                    googleMessageStateRepository.waitForRcsState(
+                    gmsgStateRepository.waitForRcsState(
                         arrayOf(
                             RcsConfigureState.CONFIGURED,
                             RcsConfigureState.RETRY
@@ -444,7 +447,7 @@ class TaskRunner(
                     }
 
                     RcsConfigureState.RETRY -> {
-                        googleMessageStateRepository.waitForRcsState(
+                        gmsgStateRepository.waitForRcsState(
                             arrayOf(RcsConfigureState.WAITING_FOR_OTP),
                             60.seconds
                         )
@@ -520,9 +523,9 @@ class TaskRunner(
                     return@withTimeoutOrNull true
                 } catch (e: Exception) {
                     if (e is RequestNumberException) {
-                        Log.e(tag, "requestNumberError: ${e.message}")
+                        Log.e(TAG, "requestNumberError: ${e.message}")
                     } else {
-                        Log.e(tag, "requestNumberError: ${e.message}", e)
+                        Log.e(TAG, "requestNumberError: ${e.message}", e)
                     }
                 }
             }
@@ -535,9 +538,9 @@ class TaskRunner(
             )
             appStateRepo.resetSuccessNum()
             appStateRepo.resetExecutedNum()
-            Log.i(tag, "requestNumber success")
+            Log.i(TAG, "requestNumber success")
         } else {
-            Log.e(tag, "requestNumber failed")
+            Log.e(TAG, "requestNumber failed")
             appStateRepo.updateSend(false)
         }
         appStateRepo.updateRuntimeFlags(
@@ -549,7 +552,7 @@ class TaskRunner(
 
     private suspend fun checkRcsConnectivity(): Boolean = run checkRcsConnection@{
         repeat(3) {
-            Log.i(tag, "Checking RCS status...")
+            Log.i(TAG, "Checking RCS status...")
             shellRun(
                 CMD_CONVERSATION_LIST_ACTIVITY,
                 CMD_RCS_SETTINGS_ACTIVITY,
@@ -558,11 +561,11 @@ class TaskRunner(
             val res = TraverseResult()
             screenInspector.traverseNode(res)
             if (res.rcsConnectionStatus == RcsConnectionStatus.CONNECTED) {
-                Log.i(tag, "RCS is connected")
+                Log.i(TAG, "RCS is connected")
                 shellRun(CMD_BACK)
                 return@checkRcsConnection true
             } else {
-                Log.i(tag, "RCS not connected, retrying...")
+                Log.i(TAG, "RCS not connected, retrying...")
             }
             shellRun(CMD_BACK, "sleep ${it * 2}")
         }
@@ -579,14 +582,14 @@ class TaskRunner(
                         val config = ktorClient
                             .get(SysConfigApi.Id(SysConfigApi(), "check_availability_numbers"))
                             .body<SysConfigResponse>()
-                        Log.i(tag, "sysConfig response: $config")
+                        Log.i(TAG, "sysConfig response: $config")
                         checkRcsA10yNumbers.addAll(config.value.split(",").map { it.trim() })
                     } catch (exception: Exception) {
-                        Log.e(tag, "sysConfig Error: ${exception.message}", exception)
+                        Log.e(TAG, "sysConfig Error: ${exception.message}", exception)
                     }
 
                     if (checkRcsA10yNumbers.isEmpty()) {
-                        Log.e(tag, "checkRcsA10yNumbers is empty")
+                        Log.e(TAG, "checkRcsA10yNumbers is empty")
                         return@checkA10y true
                     }
 
@@ -599,7 +602,7 @@ class TaskRunner(
                             if (traverseResult.isRcsCapable) {
                                 return@checkA10y true
                             } else {
-                                Log.i(tag, "checkRcsA10y: RCS not detected")
+                                Log.i(TAG, "checkRcsA10y: RCS not detected")
                             }
                         }
                     }
@@ -643,12 +646,12 @@ class TaskRunner(
                         }
                     }
                 }
-            Log.i(tag, "Apk file saved to ${file.path}")
+            Log.i(TAG, "Apk file saved to ${file.path}")
             shellRun("pm install -d -r ${file.path}")
             onSuccess()
             file.delete()
         } catch (e: Exception) {
-            Log.e(tag, "Failed to install apk", e)
+            Log.e(TAG, "Failed to install apk", e)
             onError(e)
         }
     }
@@ -662,7 +665,7 @@ class TaskRunner(
             val (out, err) = shellRun(*installApkAction.data.script.split("\n").toTypedArray())
             onSuccess(out, err)
         } catch (e: Exception) {
-            Log.e(tag, "Failed to run script", e)
+            Log.e(TAG, "Failed to run script", e)
             onError(e)
         }
     }
@@ -681,7 +684,7 @@ class TaskRunner(
             }
             onSuccess()
         } catch (e: Exception) {
-            Log.e(tag, "Failed to update device", e)
+            Log.e(TAG, "Failed to update device", e)
             onError(e)
         }
     }

+ 5 - 2
app/src/main/java/com/example/modifier/ui/backup/BackupFragment.kt

@@ -33,7 +33,10 @@ import kotlinx.coroutines.withContext
 import java.io.File
 
 class BackupFragment : Fragment() {
-    private val tag = "$baseTag/BackupFragment"
+    companion object {
+        private const val TAG = "$baseTag/BackupFragment"
+    }
+
     private lateinit var binding: FragmentBackupBinding
     private val list = mutableListOf<BackupItem>()
     private lateinit var adapter: BackupAdapter
@@ -174,7 +177,7 @@ class BackupFragment : Fragment() {
         }
         lifecycleScope.launch {
             searchText.asFlow().debounce(500).collect {
-                Log.i(tag, "searchText: $it")
+                Log.i(TAG, "searchText: $it")
                 refresh(it)
             }
         }

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

@@ -40,6 +40,7 @@ val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "se
 class LoginViewModel @Inject constructor() : ViewModel() {
 
     companion object {
+        private const val TAG = "$baseTag/LoginViewModel"
         val URL = stringPreferencesKey("url")
         val USERNAME = stringPreferencesKey("username")
         val PASSWORD = stringPreferencesKey("password")
@@ -47,7 +48,6 @@ class LoginViewModel @Inject constructor() : ViewModel() {
         val TOKEN = stringPreferencesKey("token")
     }
 
-    private val tag = "$baseTag/LoginViewModel"
     private val _uiState = MutableStateFlow<LoginUiState>(LoginUiState.Normal)
     val uiState: StateFlow<LoginUiState> = _uiState.asStateFlow()
     val serverConfig = MutableLiveData<ServerConfig>()
@@ -81,7 +81,7 @@ class LoginViewModel @Inject constructor() : ViewModel() {
     }
 
     suspend fun login() {
-        Log.d(tag, "login")
+        Log.d(TAG, "login")
         _uiState.value = LoginUiState.Loading
         val client = HttpClient(OkHttp) {
             defaultRequest {
@@ -103,12 +103,12 @@ class LoginViewModel @Inject constructor() : ViewModel() {
         when (response.status) {
             HttpStatusCode.OK -> {
                 val loginResponse = response.body<LoginResponse>()
-                Log.i(tag, "response: $loginResponse")
+                Log.i(TAG, "response: $loginResponse")
             }
 
             else -> {
                 val errorResponse = response.body<ErrorResponse>()
-                Log.e(tag, "error: $errorResponse")
+                Log.e(TAG, "error: $errorResponse")
             }
         }
     }

+ 6 - 3
app/src/main/java/com/example/modifier/ui/settings/SettingsFragment.kt

@@ -57,7 +57,10 @@ import java.util.Optional
 
 @SuppressLint("SetTextI18n", "MissingPermission", "HardwareIds", "NewApi")
 class SettingsFragment : Fragment() {
-    private val tag = "$baseTag/SettingsFragment"
+    companion object {
+        private const val TAG = "$baseTag/SettingsFragment"
+    }
+
     private lateinit var binding: FragmentSettingsBinding
     private lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
     private val appPrefsRepo = AppPrefsRepo.instance
@@ -82,7 +85,7 @@ class SettingsFragment : Fragment() {
                     // decision.
                 }
             }
-        Log.i(tag, "SettingsFragment.onCreate")
+        Log.i(TAG, "SettingsFragment.onCreate")
 
 
     }
@@ -180,7 +183,7 @@ class SettingsFragment : Fragment() {
                             }
                             return@req
                         }
-                        Log.i(tag, "response: ${response.bodyAsText()}")
+                        Log.i(TAG, "response: ${response.bodyAsText()}")
                         val res = response.body<RcsNumberResponse>()
                         val number = res.number
                         val mcc = res.mcc

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

@@ -50,14 +50,12 @@ import kotlinx.coroutines.withContext
 import java.io.File
 
 class UtilsFragment : Fragment() {
-    private val tag = "$baseTag/UtilsFragment"
-    private lateinit var binding: FragmentUtilsBinding
-    private lateinit var appPreferences: StateFlow<AppPreferences>
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
+    companion object {
+        private const val TAG = "$baseTag/UtilsFragment"
     }
 
+    private lateinit var binding: FragmentUtilsBinding
+
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
@@ -228,7 +226,7 @@ class UtilsFragment : Fragment() {
                         .body<SysConfigResponse>()
                     installApk(config.value)
                 } catch (e: Exception) {
-                    Log.e(tag, "Failed to get message apk", e)
+                    Log.e(TAG, "Failed to get message apk", e)
                 }
             }
         }
@@ -245,7 +243,7 @@ class UtilsFragment : Fragment() {
                         .body<SysConfigResponse>()
                     installApk(config.value)
                 } catch (e: Exception) {
-                    Log.e(tag, "Failed to get message apk", e)
+                    Log.e(TAG, "Failed to get message apk", e)
                 }
             }
         }
@@ -262,7 +260,7 @@ class UtilsFragment : Fragment() {
                         .body<SysConfigResponse>()
                     installApk(config.value)
                 } catch (e: Exception) {
-                    Log.e(tag, "Failed to get gms apk", e)
+                    Log.e(TAG, "Failed to get gms apk", e)
                 }
             }
         }
@@ -374,10 +372,10 @@ class UtilsFragment : Fragment() {
                             }
                         }
                     }
-                Log.i(tag, "A file saved to ${file.path}")
+                Log.i(TAG, "A file saved to ${file.path}")
                 shellRun("pm install -d -r ${file.path}")
             } catch (e: Exception) {
-                Log.e(tag, "Failed to download apk", e)
+                Log.e(TAG, "Failed to download apk", e)
 
             }
 

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

@@ -16,7 +16,7 @@ fun isClashInstalled(context: Context): Boolean {
 
 fun stopClash() {
     try {
-        val context = Utils.getContext()
+        val context = getContext()
         val intent = Intent()
         intent.action = "com.github.metacubex.clash.meta.action.STOP_CLASH"
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -28,7 +28,7 @@ fun stopClash() {
 
 fun changeClashProfile(name: String, base64: String) {
     try {
-        val context = Utils.getContext()
+        val context = getContext()
         val intent = Intent()
         intent.action = "com.github.metacubex.clash.meta.action.USE_PROFILE"
         intent.putExtra("base64", base64)

+ 3 - 2
app/src/main/java/com/example/modifier/utils/GoogleMessage.kt

@@ -19,7 +19,7 @@ fun injectOTP(otp: String) {
         "message",
         "Your Messenger verification code is G-$otp"
     )
-    val context = Utils.getContext()
+    val context = getContext()
     context.sendBroadcast(intent)
 }
 
@@ -45,7 +45,7 @@ fun isOldVersion(context: Context): Boolean {
 }
 
 suspend fun clearConv() {
-    val context = Utils.getContext()
+    val context = getContext()
     val model = Build.MODEL.replace(" ", "_")
     if (!(model == "SM-F707N" || model == "SM-F711B" || model == "Pixel_5")) {
         return
@@ -60,6 +60,7 @@ suspend fun clearConv() {
         }
 
         val cmds = mutableListOf<String>()
+
         @SuppressLint("SdCardPath")
         fun copyToData(file: File) {
             val relative = file.path.replace(providerDir.path, "")

+ 12 - 16
app/src/main/java/com/example/modifier/utils/System.kt

@@ -59,19 +59,14 @@ val uniqueId: String
     }
 
 @SuppressLint("PrivateApi")
-fun getContext(): Context? {
-    try {
-        val activityThreadClass = Class.forName("android.app.ActivityThread")
-        val currentActivityThreadMethod = activityThreadClass.getMethod("currentActivityThread")
-        currentActivityThreadMethod.isAccessible = true
-        val currentActivityThread = currentActivityThreadMethod.invoke(null)
-        val getApplicationMethod = activityThreadClass.getMethod("getApplication")
-        getApplicationMethod.isAccessible = true
-        return getApplicationMethod.invoke(currentActivityThread) as Context
-    } catch (e: java.lang.Exception) {
-        e.printStackTrace()
-    }
-    return null
+fun getContext(): Context {
+    val activityThreadClass = Class.forName("android.app.ActivityThread")
+    val currentActivityThreadMethod = activityThreadClass.getMethod("currentActivityThread")
+    currentActivityThreadMethod.isAccessible = true
+    val currentActivityThread = currentActivityThreadMethod.invoke(null)
+    val getApplicationMethod = activityThreadClass.getMethod("getApplication")
+    getApplicationMethod.isAccessible = true
+    return getApplicationMethod.invoke(currentActivityThread) as Context
 }
 
 suspend fun hasRootAccess(): Boolean {
@@ -146,7 +141,7 @@ suspend fun enableOverlay() {
 }
 
 suspend fun optimize() {
-    val context = Utils.getContext()
+    val context = getContext()
     val packageManager = context.packageManager
     val info = packageManager.getApplicationInfo("com.google.android.gms", 0)
 
@@ -154,6 +149,7 @@ suspend fun optimize() {
         "dumpsys deviceidle whitelist +com.google.android.apps.messaging",
         "dumpsys deviceidle whitelist +${BuildConfig.APPLICATION_ID}",
         "cmd netpolicy add restrict-background-blacklist ${info.uid}",
+        "pm grant ${BuildConfig.APPLICATION_ID} android.permission.POST_NOTIFICATIONS",
     )
     if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
         shellRun(
@@ -248,7 +244,7 @@ suspend fun currentActivity(): String? {
 }
 
 fun sqlite3path(): String {
-    val context = Utils.getContext()
+    val context = getContext()
     val dataDir = ContextCompat.getDataDir(context)
     val binDir = File(dataDir, "bin")
     val dbDir = File(dataDir, "providerDB")
@@ -266,7 +262,7 @@ fun sqlite3path(): String {
 }
 
 fun restartSelf() {
-    val context = Utils.getContext()
+    val context = getContext()
     val mStartActivity = Intent(context, MainActivity::class.java)
     val mPendingIntentId = 123456
     val mPendingIntent = PendingIntent.getActivity(