x1ongzhu 1 год назад
Родитель
Сommit
65481c0fa0

+ 104 - 966
app/src/main/java/com/example/modifier/service/ModifierService.kt

@@ -131,7 +131,7 @@ import kotlin.time.Duration.Companion.minutes
 import kotlin.time.Duration.Companion.seconds
 
 @SuppressLint("SetTextI18n")
-class ModifierService : AccessibilityService(), Emitter.Listener {
+class ModifierService : AccessibilityService() {
     companion object {
         const val NAME: String = BuildConfig.APPLICATION_ID + ".service.ModifierService"
 
@@ -140,421 +140,32 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             private set
     }
 
-    private val handler = Handler(Looper.getMainLooper())
-
-    private val mSocketOpts = IO.Options()
-    private lateinit var mSocket: Socket
     private lateinit var binding: FloatingWindowBinding
 
-    private var currentTaskId = 0
-    private var lastSend = 0L
-
-    private val backupItemDao by lazy {
-        AppDatabase.getDatabase(this).itemDao()
-    }
-    private val appPreferencesRepository by lazy {
-        AppPreferencesRepository(this)
-    }
+    private val backupItemDao by lazy { AppDatabase.getDatabase(this).itemDao() }
+    private val appPreferencesRepository by lazy { AppPreferencesRepository(this) }
     private lateinit var appPreferences: StateFlow<AppPreferences>
-    private val appStateRepository: AppStateRepository by lazy {
-        AppStateRepository(this)
-    }
+    private val appStateRepository: AppStateRepository by lazy { AppStateRepository(this) }
     private lateinit var appState: StateFlow<AppState>
-    private val googleMessageStateRepository by lazy {
-        GoogleMessageStateRepository(this)
-    }
-    private var requestMode = 1;
-    private var currentActivity = ""
-    private val screenInspector by lazy {
-        ScreenInspector(this)
-    }
-    private val screenController by lazy {
-        ScreenController(this, screenInspector)
-    }
-    private val spoofedSimInfoRepository by lazy {
-        SpoofedSimInfoRepository(this)
-    }
+    private val googleMessageStateRepository by lazy { GoogleMessageStateRepository(this) }
+    private val screenInspector by lazy { ScreenInspector(this) }
+    private val screenController by lazy { ScreenController(this, screenInspector) }
+    private val spoofedSimInfoRepository by lazy { SpoofedSimInfoRepository(this) }
     private lateinit var spoofedSimInfo: StateFlow<SpoofedSimInfo>
     private val backupRepository by lazy {
-        BackupRepository(this, backupItemDao, spoofedSimInfoRepository)
-    }
-
-    fun connect() {
-        try {
-            if (this@ModifierService::mSocket.isInitialized) {
-                mSocket.disconnect()
-            }
-            mSocketOpts.query =
-                "model=${Build.MODEL}&name=${appPreferences.value.name}&id=${uniqueId}&version=${BuildConfig.VERSION_CODE}"
-            mSocketOpts.transports = arrayOf("websocket")
-            Log.i(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")
-                CoroutineScope(Dispatchers.IO).launch {
-                    delay(500)
-                    reportDeviceStatues()
-                }
-            }
-            mSocket.on(Socket.EVENT_DISCONNECT) {
-                Log.i(TAG, "Disconnected from server")
-            }
-            mSocket.on(Socket.EVENT_CONNECT_ERROR) { args ->
-                Log.i(TAG, "Connection error: " + args[0])
-                if (args[0] is Exception) {
-                    val e = args[0] as Exception
-                    e.printStackTrace()
-                }
-            }
-            mSocket.connect()
-        } catch (e: Exception) {
-            e.printStackTrace()
-        }
-    }
-
-    suspend fun init() {
-        Log.i("$TAG/AccessibilityService", "init")
-        appStateRepository.updateRuntimeFlags(preparing = true)
-        val hasRoot = run checkRoot@{
-            repeat(30) {
-                if (hasRootAccess()) {
-                    return@checkRoot true
-                }
-                delay(1000)
-            }
-            false
-        }
-        if (!hasRoot) {
-            System.exit(0)
-        }
-        CoroutineScope(coroutineContext).launch {
-            googleMessageStateRepository.startLogging()
-        }
-        if (isRebooted()) {
-            delay(2.minutes)
-        } else {
-            delay(5000)
-        }
-        optimize()
-        syncTime()
-        if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
-            killPhoneProcess(force = false)
-        }
-        appStateRepository.updateRuntimeFlags(preparing = false)
-
-        connect()
-        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() {
-                    CoroutineScope(Dispatchers.IO).launch {
-                        try {
-                            setBatteryLevel(100)
-                        } catch (e: Exception) {
-                            e.printStackTrace()
-                        }
-                    }
-                }
-            }, 0, 30.minutes.inWholeMilliseconds)
-        }
-    }
-
-    override fun onAccessibilityEvent(event: AccessibilityEvent) {
-        Log.d(
-            TAG,
-            "eventType: ${event.eventType}, packageName: ${event.packageName}, className: ${event.className}"
+        BackupRepository(
+            this,
+            backupItemDao,
+            spoofedSimInfoRepository
         )
-        if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
-            if (event.packageName != null && event.className != null) {
-                val componentName = ComponentName(
-                    event.packageName.toString(),
-                    event.className.toString()
-                )
-                try {
-                    packageManager.getActivityInfo(componentName, 0)
-                    currentActivity = componentName.flattenToShortString()
-                    Log.d(TAG, "Activity: $currentActivity")
-                } catch (_: Exception) {
-                }
-
-            }
-        }
-    }
-
-    override fun onInterrupt() {
-    }
-
-    override fun call(vararg args: Any) {
-        if (args.isNotEmpty()) {
-            Log.i(TAG, "Received message: " + args[0])
-            if (args[0] is JSONObject) {
-                val json = args[0] as JSONObject
-                val action = json.optString("action")
-                if ("send" == action) {
-                    val data = json.optJSONObject("data")
-                    if (data != null) {
-                        val to = data.optString("to")
-                        val body = data.optString("body")
-                        CoroutineScope(Dispatchers.IO).launch {
-                            send(
-                                to, body, TaskConfig(
-                                    rcsWait = 3000,
-                                    rcsInterval = 1000,
-                                    cleanCount = 10,
-                                    requestNumberInterval = 50,
-                                    checkConnection = true,
-                                    useBackup = false,
-                                    endToEndEncryption = true
-                                )
-                            )
-                        }
-                    }
-                } else if ("task" == action) {
-                    val taskAction = Json.decodeFromString<TaskAction>(json.toString())
-
-                    CoroutineScope(Dispatchers.IO).launch {
-                        runTask(taskAction)
-                    }
-                } else if ("installApk" == action) {
-                    val installApkAction = Json.decodeFromString<InstallApkAction>(json.toString())
-
-                    CoroutineScope(Dispatchers.IO).launch {
-                        installApk(installApkAction)
-                    }
-                }
-            }
-        }
     }
+    private lateinit var socketClient: SocketClient
+    private lateinit var taskRunner: TaskRunner
 
-    private suspend fun installApk(installApkAction: InstallApkAction) {
-        try {
-            val file = withContext(Dispatchers.IO) {
-                File.createTempFile("files", ".apk")
-            }
+    override fun onAccessibilityEvent(event: AccessibilityEvent) {}
 
-            HttpClient(OkHttp) {
-                HttpResponseValidator {
-                    validateResponse { response ->
-                        if (response.status.value !in 200..299) {
-                            throw ServerResponseException(
-                                response,
-                                "Error " + response.status.value.toString()
-                            )
-                        }
-                    }
-                }
-            }.prepareGet(installApkAction.data.apkUrl)
-                .execute { httpResponse ->
-                    val channel: ByteReadChannel = httpResponse.body()
-                    while (!channel.isClosedForRead) {
-                        val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
-                        while (!packet.isEmpty) {
-                            val bytes = packet.readBytes()
-                            file.appendBytes(bytes)
-                        }
-                    }
-                }
-            Log.i(TAG, "Apk file saved to ${file.path}")
-            shellRun("pm install -d -r ${file.path}")
-            mSocket.emit(
-                "callback",
-                JSONObject(
-                    Json.encodeToString(
-                        SocketCallback<String>(
-                            id = installApkAction.id,
-                            status = 0,
-                        )
-                    )
-                )
-            )
-        } catch (e: Exception) {
-            Log.e("Modifier", "Failed to install apk", e)
-            mSocket.emit(
-                "callback",
-                JSONObject(
-                    Json.encodeToString(
-                        SocketCallback<String>(
-                            id = installApkAction.id,
-                            status = -1,
-                            error = e.message
-                        )
-                    )
-                )
-            )
-        }
-    }
-
-    private suspend fun runTask(taskAction: TaskAction) {
-        if (appState.value.busy) {
-            mSocket.emit(
-                "callback",
-                JSONObject(
-                    Json.encodeToString(
-                        SocketCallback<String>(
-                            id = taskAction.id,
-                            status = -1,
-                            error = "another task is running"
-                        )
-                    )
-                )
-            )
-            return
-        }
-
-        try {
-            val taskConfig = taskAction.data.config
-            currentTaskId = taskAction.data.taskId
-            requestMode = if (taskAction.data.config.useBackup) 2 else 1
-
-            if (taskAction.data.config.checkConnection) {
-                appStateRepository.updateRuntimeFlags(checkingConnection = true)
-                if (!checkRcsAvailability()) {
-                    mSocket.emit(
-                        "callback",
-                        JSONObject(
-                            Json.encodeToString(
-                                SocketCallback<String>(
-                                    id = taskAction.id,
-                                    status = -1,
-                                    error = "RCS not available"
-                                )
-                            )
-                        )
-                    )
-                    requestNumber()
-                    appStateRepository.updateRuntimeFlags(checkingConnection = false)
-                    return
-                }
-                appStateRepository.updateRuntimeFlags(checkingConnection = false)
-            }
-
-            appStateRepository.updateRuntimeFlags(running = true)
-            val success = ArrayList<Int>()
-            val fail = ArrayList<Int>()
-            for (i in 0 until taskAction.data.tasks.size) {
-                val taskItem = taskAction.data.tasks[i]
-                try {
-                    if (send(
-                            taskItem.number,
-                            taskItem.message,
-                            taskConfig
-                        )
-                    ) {
-                        success.add(taskItem.id)
-                    } else {
-                        fail.add(taskItem.id)
-                    }
-                } catch (e: Exception) {
-                    Log.e(TAG, "runTaskError: ${e.message}", e)
-                    fail.add(taskItem.id)
-                }
-            }
-            shellRun(CMD_BACK)
-            mSocket.emit(
-                "callback",
-                JSONObject(
-                    Json.encodeToString(
-                        SocketCallback(
-                            id = taskAction.id,
-                            status = 0,
-                            data = TaskExecutionResult(success, fail)
-                        )
-                    )
-                )
-            )
-            if (taskConfig.requestNumberInterval in 1..appState.value.successNum) {
-                delay(3000)
-                requestNumber()
-            } else if (taskConfig.cleanCount in 1..appState.value.executedNum && !appPreferences.value.preventClean) {
-                delay(3000)
-                clearConv();
-                shellRun(CMD_MESSAGING_APP)
-                delay(3000)
-                appStateRepository.resetExecutedNum()
-            } else {
-                delay(2000)
-            }
-            appStateRepository.updateRuntimeFlags(running = false)
-        } catch (e: Exception) {
-            Log.e(TAG, "runTaskError: ${e.message}", e)
-            mSocket.emit(
-                "callback",
-                JSONObject(
-                    Json.encodeToString(
-                        SocketCallback<String>(
-                            id = taskAction.id,
-                            status = -1,
-                            error = e.message
-                        )
-                    )
-                )
-            )
-            appStateRepository.updateRuntimeFlags(running = false)
-        }
-    }
-
-    private suspend fun send(
-        to: String,
-        body: String,
-        taskConfig: TaskConfig
-    ): Boolean {
-        Log.i(TAG, "Sending SMS to $to: $body")
-        startActivity(smsIntent(to, body))
-        try {
-            Log.i(
-                TAG,
-                "Command executed successfully, waiting for app to open..."
-            )
-            delay(1000)
-            var success = false
-            var traverseResult = TraverseResult()
-            withTimeoutOrNull(taskConfig.rcsWait) {
-                while (true) {
-                    traverseResult = TraverseResult()
-                    screenInspector.traverseNode(traverseResult)
-                    if (traverseResult.isRcsCapable && traverseResult.sendBtn != null) {
-                        if (!taskConfig.endToEndEncryption || traverseResult.encrypted) {
-                            break
-                        }
-                    }
-                    delay(200)
-                }
-            }
-            if (traverseResult.isRcsCapable) {
-                if (traverseResult.sendBtn == null) {
-                    Log.i(TAG, "Send button not found")
-                } else {
-                    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")
-                        delay(taskConfig.rcsInterval - dt)
-                    }
-                    traverseResult.sendBtn!!.performAction(AccessibilityNodeInfo.ACTION_CLICK)
-                    lastSend = System.currentTimeMillis()
-                    success = true
-                }
-            } else {
-                Log.i(TAG, "RCS not detected")
-            }
-            appStateRepository.incrementExecutedNum(success)
-            Log.i(
-                TAG,
-                "executedNum: ${appState.value.executedNum}, successNum: ${appState.value.successNum}"
-            )
-            delay(1000)
-            return success
-        } catch (e: Exception) {
-            e.printStackTrace()
-        }
-        return false
+    override fun onInterrupt() {
+        Log.e("$TAG/AccessibilityService", "onInterrupt")
     }
 
     @SuppressLint("ClickableViewAccessibility")
@@ -637,7 +248,16 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             appPreferences = appPreferencesRepository.stateFlow()
             appState = appStateRepository.stateFlow()
             spoofedSimInfo = spoofedSimInfoRepository.stateFlow()
-
+            taskRunner = TaskRunner(
+                this@ModifierService,
+                screenInspector,
+                screenController,
+                appStateRepository,
+                appPreferencesRepository,
+                spoofedSimInfoRepository,
+                googleMessageStateRepository,
+                backupRepository,
+            )
             launch {
                 appState.collect {
                     withContext(Dispatchers.Main) {
@@ -658,48 +278,105 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         when (it.requestNumberState) {
                             RequestNumberState.IDLE -> {
                                 binding.tvStatus.visibility = GONE
-                                binding.btnReq.text = ""
+                                binding.tvStatus.text = ""
                             }
 
                             RequestNumberState.RESET -> {
                                 binding.tvStatus.visibility = VISIBLE
-                                binding.btnReq.text = "Resetting GMS"
+                                binding.tvStatus.text = "Resetting GMS"
                             }
 
                             RequestNumberState.REQUEST -> {
                                 binding.tvStatus.visibility = VISIBLE
-                                binding.btnReq.text = "Requesting Number"
+                                binding.tvStatus.text = "Requesting Number"
                             }
 
                             RequestNumberState.OTP_1 -> {
                                 binding.tvStatus.visibility = VISIBLE
-                                binding.btnReq.text = "Waiting for OTP Sent"
+                                binding.tvStatus.text = "Waiting for OTP Sent"
                             }
 
                             RequestNumberState.OTP_2 -> {
                                 binding.tvStatus.visibility = VISIBLE
-                                binding.btnReq.text = "Waiting for OTP Received"
+                                binding.tvStatus.text = "Waiting for OTP Received"
                             }
 
                             RequestNumberState.CONFIG -> {
                                 binding.tvStatus.visibility = VISIBLE
-                                binding.btnReq.text = "Waiting for Configuration"
+                                binding.tvStatus.text = "Waiting for Configuration"
                             }
                         }
                     }
+
                     withContext(Dispatchers.IO) {
                         reportDeviceStatues()
                     }
                 }
             }
+
             launch {
                 appPreferences.collect {
                     withContext(Dispatchers.Main) {
                         binding.swSend.text = it.name
                     }
+                    if (this@ModifierService::socketClient.isInitialized) {
+                        socketClient.disconnect()
+                    }
+                    socketClient = SocketClient(
+                        appPreferences.value.server,
+                        appPreferences.value.name,
+                        taskRunner
+                    )
                 }
             }
-            init()
+
+            appStateRepository.updateRuntimeFlags(preparing = true)
+            val hasRoot = run checkRoot@{
+                repeat(30) {
+                    if (hasRootAccess()) {
+                        return@checkRoot true
+                    }
+                    delay(1000)
+                }
+                false
+            }
+            if (!hasRoot) {
+                System.exit(0)
+            }
+            CoroutineScope(kotlin.coroutines.coroutineContext).launch {
+                googleMessageStateRepository.startLogging()
+            }
+            if (isRebooted()) {
+                delay(2.minutes)
+            } else {
+                delay(5000)
+            }
+            optimize()
+            syncTime()
+            if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
+                killPhoneProcess(force = false)
+            }
+            appStateRepository.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() {
+                        CoroutineScope(Dispatchers.IO).launch {
+                            try {
+                                setBatteryLevel(100)
+                            } catch (e: Exception) {
+                                e.printStackTrace()
+                            }
+                        }
+                    }
+                }, 0, 30.minutes.inWholeMilliseconds)
+            }
         }
         binding.swSend.setOnTouchListener(touchListener)
         binding.swSend.setOnCheckedChangeListener { buttonView: CompoundButton?, isChecked: Boolean ->
@@ -715,19 +392,20 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
 
         binding.btnReq.setOnClickListener {
             CoroutineScope(Dispatchers.IO).launch {
-                requestNumber(reset = false, noBackup = true)
+                taskRunner.requestNumber(reset = false, noBackup = true)
             }
         }
 
         binding.btnReset.setOnClickListener {
             binding.btnReset.isEnabled = false
             CoroutineScope(Dispatchers.IO).launch {
-                reset()
+                taskRunner.reset()
                 withContext(Dispatchers.Main) {
                     binding.btnReset.isEnabled = true
                 }
             }
         }
+
         binding.btnMore.setOnClickListener {
             showMenu(newContext, binding.btnMore, R.menu.more)
         }
@@ -746,7 +424,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     }
 
                     R.id.check_availability -> {
-                        checkRcsAvailability()
+                        taskRunner.checkRcsAvailability()
                     }
 
                     R.id.toggle_on -> {
@@ -796,561 +474,21 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
     }
 
     private fun reportDeviceStatues() {
-        if (this::mSocket.isInitialized) {
-            val data = JSONObject()
-            try {
-                data.put("action", "updateDevice")
-                val dataObj = JSONObject()
-                dataObj.put("canSend", appState.value.send)
-                dataObj.put("busy", appState.value.busy)
-                dataObj.put("currentCountry", spoofedSimInfo.value.country)
-                data.put("data", dataObj)
-                mSocket.emit("message", data)
-            } catch (e: JSONException) {
-                e.printStackTrace()
-            }
-        }
-    }
-
-    private suspend fun reset() {
-        if (isOldVersion(this)) {
-            withTimeout(1.hours) {
-                while (true) {
-                    delay(100)
-                    withContext(Dispatchers.Main) {
-                        binding.tvLog.text = "Waiting for RCS switch on..."
-                    }
-                    googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
-                    spoofedSimInfoRepository.mock()
-                    resetAll()
-                    var switchAppear = googleMessageStateRepository.waitForRcsState(
-                        arrayOf(RcsConfigureState.WAITING_FOR_TOS),
-                        2.minutes
-                    )?.let {
-                        it == RcsConfigureState.WAITING_FOR_TOS
-                    }
-                    if (switchAppear != true) {
-                        shellRun(
-                            PACKAGE_GMS.kill(), PACKAGE_MESSAGING.kill(), "sleep 1",
-                            CMD_MESSAGING_APP
-                        )
-                        switchAppear = googleMessageStateRepository.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..."
-                            )
-                            continue
-                        }
-                    }
-                    if (!screenController.toggleRcsSwitch(false)) {
-                        Log.e(TAG, "RCS switch not turned off, retrying...")
-                        continue
-                    }
-                    if (!screenController.toggleRcsSwitch(true)) {
-                        Log.e(TAG, "RCS switch not turned on, retrying...")
-                        continue
-                    }
-                    var resetSuccess = googleMessageStateRepository.waitForRcsState(
-                        arrayOf(
-                            RcsConfigureState.READY
-                        ), 30.seconds
-                    ).let { it == RcsConfigureState.READY }
-                    if (!resetSuccess) {
-                        screenController.toggleRcsSwitch(false)
-                        delay(1000)
-                        screenController.toggleRcsSwitch(true)
-                        resetSuccess = googleMessageStateRepository.waitForRcsState(
-                            arrayOf(
-                                RcsConfigureState.READY
-                            ), 1.minutes
-                        ).let { it == RcsConfigureState.READY }
-                    }
-                    Log.i(TAG, "waitForRcsState: $resetSuccess")
-                    appStateRepository.resetRequestedNum()
-                    if (resetSuccess) {
-                        delay(3000)
-                        break
-                    }
-                }
-            }
-        } else {
-            withTimeout(1.hours) {
-                while (true) {
-                    delay(100)
-                    withContext(Dispatchers.Main) {
-                        binding.tvLog.text = "Waiting for RCS switch on..."
-                    }
-                    googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
-                    spoofedSimInfoRepository.mock()
-                    resetAll()
-                    var switchAppear = googleMessageStateRepository.waitForRcsState(
-                        arrayOf(RcsConfigureState.WAITING_FOR_DEFAULT_ON),
-                        1.minutes
-                    )?.let {
-                        it == RcsConfigureState.WAITING_FOR_DEFAULT_ON
-                    }
-                    if (switchAppear != true) {
-                        shellRun(
-                            PACKAGE_GMS.kill(), PACKAGE_MESSAGING.kill(), "sleep 1",
-                            CMD_MESSAGING_APP
-                        )
-                        switchAppear = googleMessageStateRepository.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..."
-                            )
-                            continue
-                        }
-                    }
-                    val switchOn = screenController.toggleRcsSwitch(true)
-                    if (!switchOn) {
-                        Log.e(TAG, "RCS switch not turned on, retrying...")
-                        continue
-                    }
-                    var resetSuccess = googleMessageStateRepository.waitForRcsState(
-                        arrayOf(
-                            RcsConfigureState.READY
-                        ), 30.seconds
-                    ).let { it == RcsConfigureState.READY }
-                    if (!resetSuccess) {
-                        screenController.toggleRcsSwitch(false)
-                        delay(1000)
-                        screenController.toggleRcsSwitch(true)
-                        resetSuccess = googleMessageStateRepository.waitForRcsState(
-                            arrayOf(
-                                RcsConfigureState.READY
-                            ), 1.minutes
-                        ).let { it == RcsConfigureState.READY }
-                    }
-                    Log.i(TAG, "waitForRcsState: $resetSuccess")
-                    appStateRepository.resetRequestedNum()
-                    if (resetSuccess) {
-                        delay(3000)
-                        break
-                    }
-                }
-            }
-        }
-    }
-
-    private suspend fun requestNumber(
-        reset: Boolean = false,
-        noBackup: Boolean = false,
-        fresh: Boolean = false
-    ) {
-        appStateRepository.updateRuntimeFlags(suspended = false)
-        if (appPreferences.value.preventRequest) {
-            return
-        }
-        if (appState.value.requesting) {
-            return
-        }
-
-        appStateRepository.updateRuntimeFlags(requesting = true)
-
-        if (spoofedSimInfo.value.available) {
-            backupRepository.backup(
-                spoofedSimInfo = spoofedSimInfo.value,
-                type = "auto",
-                sendCount = appState.value.executedNum,
-                fresh = fresh
-            )
-        } else {
-            clearConv();
-        }
-
-        appStateRepository.incrementRequestedNum()
-        var requestSuccess = false
-        var retry = 0
-        var needRest = reset
-        withTimeoutOrNull(1.hours) {
-            while (true) {
-                delay(200)
-                needRest = needRest || retry > 2 || appState.value.requestedNum > 5
-                try {
-                    val device =
-                        ktorClient(appPreferences.value.server).get(DeviceApi.Id(id = uniqueId))
-                            .body<DeviceResponse>()
-                    if (isClashInstalled(applicationContext)) {
-
-                        val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
-                        if (TextUtils.isEmpty(device.clashProfile)) {
-                            prefs.edit()
-                                .remove("clash_profile")
-                                .apply()
-                            stopClash()
-                        } else {
-                            val oldProfile = prefs.getString("clash_profile", "")
-                            if (oldProfile != device.clashProfile) {
-                                prefs.edit()
-                                    .putString("clash_profile", device.clashProfile)
-                                    .apply()
-                                changeClashProfile(
-                                    device.pinCountry!!, Base64.encodeToString(
-                                        device.clashProfile!!.toByteArray(),
-                                        Base64.DEFAULT
-                                    )
-                                )
-                                delay(5000)
-                            }
-                        }
-                    }
-
-                    if (requestMode == 2 && !noBackup) {
-                        val backup = backupItemDao.findBackupForRestore(
-                            spoofedSimInfo.value.number,
-                            System.currentTimeMillis() - 2 * 24 * 60 * 60 * 1000
-                        )
-                        if (backup != null) {
-                            if (backupRepository.restore(backup)) {
-                                requestSuccess = true
-                                break
-                            } else {
-                                backupRepository.backup(
-                                    spoofedSimInfo = spoofedSimInfo.value,
-                                    type = "auto",
-                                    sendCount = 0
-                                )
-                                continue
-                            }
-                        }
-                    }
-
-                    if (needRest && !appPreferences.value.preventReset) {
-                        reset()
-                        retry = 0
-                        needRest = false
-                    }
-
-                    googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
-                    withContext(Dispatchers.Main) {
-                        binding.tvLog.text = "Requesting number..."
-                    }
-
-                    val req = RcsNumberRequest(
-                        deviceId = uniqueId,
-                        taskId = currentTaskId
-                    )
-                    if (!TextUtils.isEmpty(device.pinCountry)) {
-                        req.country = device.pinCountry
-                    }
-                    val response = ktorClient(appPreferences.value.server).put(
-                        RcsNumberApi()
-                    ) {
-                        contentType(ContentType.Application.Json)
-                        setBody(req)
-                        timeout {
-                            requestTimeoutMillis = 60 * 1000
-                            socketTimeoutMillis = 60 * 1000
-                        }
-                    }
-                    var rcsNumber = response.body<RcsNumberResponse>()
-                    Log.i(TAG, "requestNumber response: $rcsNumber")
-
-                    withContext(Dispatchers.Main) {
-                        binding.tvLog.text = "Requesting success, waiting for logs..."
-                    }
-
-                    spoofedSimInfoRepository.updateSpoofedSimInfo(
-                        SpoofedSimInfo(
-                            number = rcsNumber.number,
-                            mcc = rcsNumber.mcc,
-                            mnc = rcsNumber.mnc,
-                            iccid = genICCID(rcsNumber.mnc, rcsNumber.areaCode),
-                            imsi = genIMSI(rcsNumber.mcc + rcsNumber.mnc),
-                            imei = genIMEI(),
-                            country = rcsNumber.country,
-                            areaCode = rcsNumber.areaCode,
-                            available = false,
-                            carrierId = rcsNumber.carrierId,
-                            carrierName = rcsNumber.carrierName,
-                        )
-                    )
-
-                    shellRun(CMD_MESSAGING_APP)
-                    withContext(Dispatchers.Main) {
-                        binding.tvLog.text = "Waiting for logs..."
-                    }
-
-                    if (rcsNumber.expiryTime.isBefore(LocalDateTime.now())) {
-                        Log.e(TAG, "RCS number expired, retrying...")
-                        continue
-                    }
-                    var sendOtpTimeout = ChronoUnit.SECONDS.between(
-                        LocalDateTime.now(),
-                        rcsNumber.expiryTime
-                    ).seconds
-                    if (sendOtpTimeout < 60.seconds) {
-                        Log.e(TAG, "OTP timeout too short, retrying...")
-                        continue
-                    }
-                    if (sendOtpTimeout > 2.minutes) {
-                        sendOtpTimeout = 2.minutes
-                    }
-                    if (googleMessageStateRepository.waitForRcsState(
-                            arrayOf(RcsConfigureState.WAITING_FOR_OTP),
-                            sendOtpTimeout
-                        ) != RcsConfigureState.WAITING_FOR_OTP
-                    ) {
-                        if (!screenController.toggleRcsSwitch(true)) {
-                            needRest = true
-                        }
-                        if (RcsConfigureState.REPLAY_REQUEST == googleMessageStateRepository.rcsConfigureState.value) {
-                            Log.e(
-                                TAG,
-                                "REPLAY_REQUEST detected, may reset after 3 retry ($retry)"
-                            )
-                            retry++
-                        }
-                        Log.e(
-                            TAG,
-                            "RCS not entered waiting for OTP state, retrying..."
-                        )
-                        continue
-                    }
-
-                    launch {
-                        try {
-                            ktorClient(appPreferences.value.server).post(
-                                RcsNumberApi.Id.OtpState(
-                                    RcsNumberApi.Id(
-                                        RcsNumberApi(),
-                                        rcsNumber.id
-                                    )
-                                )
-                            )
-                        } catch (e: Exception) {
-                            Log.e(TAG, "Send OtpState Error: ${e.message}", e)
-                        }
-                    }
-
-                    if (rcsNumber.expiryTime.isBefore(LocalDateTime.now())) {
-                        Log.e(TAG, "RCS number expired, retrying...")
-                        continue
-                    }
-
-                    withTimeoutOrNull(60.seconds) {
-                        while (true) {
-                            try {
-                                rcsNumber =
-                                    ktorClient(appPreferences.value.server).get(RcsNumberApi.Id(id = rcsNumber.id))
-                                        .body<RcsNumberResponse>()
-                                Log.i(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}"
-                                )
-                            }
-                            delay(2.seconds)
-                        }
-                    }
-
-                    if (rcsNumber.status != RcsNumberResponse.STATUS_SUCCESS) {
-                        Log.e(TAG, "OTP not received, retrying...")
-                        continue
-                    }
-
-                    val match =
-                        Regex("Your Messenger verification code is G-(\\d{6})")
-                            .find(rcsNumber.message!!)
-                    if (match != null) {
-                        val otp = match.groupValues[1]
-                        Log.i(TAG, "OTP: $otp")
-                        val sender = "3538"
-                        val msg = "Your Messenger verification code is G-$otp"
-
-                        val configured = run configuring@{
-                            repeat(2) {
-                                spoofSmsIntent(sender, msg)
-                                val state =
-                                    googleMessageStateRepository.waitForRcsState(
-                                        arrayOf(
-                                            RcsConfigureState.CONFIGURED,
-                                            RcsConfigureState.RETRY
-                                        ), 60.seconds
-                                    )
-                                when (state) {
-                                    RcsConfigureState.CONFIGURED -> {
-                                        return@configuring true
-                                    }
-
-                                    RcsConfigureState.RETRY -> {
-                                        googleMessageStateRepository.waitForRcsState(
-                                            arrayOf(RcsConfigureState.WAITING_FOR_OTP),
-                                            60.seconds
-                                        )
-                                    }
-
-                                    else -> {
-                                        Log.e(
-                                            TAG,
-                                            "verifyOtp fail, retrying..."
-                                        )
-                                    }
-                                }
-                            }
-                            false
-                        }
-                        if (!configured) {
-                            Log.e(TAG, "RCS not configured, retrying...")
-                            continue
-                        } else {
-                            launch {
-                                try {
-                                    ktorClient(appPreferences.value.server).post(
-                                        RcsNumberApi.Id.Configured(
-                                            RcsNumberApi.Id(
-                                                RcsNumberApi(),
-                                                rcsNumber.id
-                                            )
-                                        )
-                                    )
-                                } catch (e: Exception) {
-                                    Log.e(
-                                        TAG,
-                                        "Send ConfiguredState Error: ${e.message}",
-                                        e
-                                    )
-                                }
-                            }
-                            requestSuccess = true
-                            break
-                        }
-                    }
-                } catch (e: Exception) {
-                    Log.e(TAG, "requestNumberError: ${e.message}", e)
-                }
-            }
-        }
-        if (requestSuccess) {
-            spoofedSimInfoRepository.updateSpoofedSimInfo(
-                spoofedSimInfo = spoofedSimInfo.value.copy(
-                    available = true
-                )
+        if (this::socketClient.isInitialized) {
+            socketClient.reportDeviceStatues(
+                appState.value.send,
+                appState.value.busy,
+                spoofedSimInfo.value.country
             )
-            appStateRepository.resetSuccessNum()
-            appStateRepository.resetExecutedNum()
-            Log.i(TAG, "requestNumber success")
-            delay(5000)
-            shellRun(PACKAGE_MESSAGING.kill(), "sleep 1", CMD_MESSAGING_APP)
-            delay(2000)
-        } else {
-            Log.e(TAG, "requestNumber failed")
-            appStateRepository.updateSend(false)
-            appStateRepository.updateRuntimeFlags(suspended = true)
         }
-        appStateRepository.updateRuntimeFlags(requesting = false)
     }
 
-    private suspend fun checkRcsConnectivity(): Boolean = run checkRcsConnection@{
-        repeat(3) {
-            Log.i(TAG, "Checking RCS status...")
-            shellRun(
-                CMD_CONVERSATION_LIST_ACTIVITY,
-                CMD_RCS_SETTINGS_ACTIVITY,
-                "sleep 1",
-            )
-            val res = TraverseResult()
-            screenInspector.traverseNode(res)
-            if (res.rcsConnectionStatus == RcsConnectionStatus.CONNECTED) {
-                Log.i(TAG, "RCS is connected")
-                shellRun(CMD_BACK)
-                return@checkRcsConnection true
-            } else {
-                Log.i(TAG, "RCS not connected, retrying...")
-            }
-            shellRun(CMD_BACK, "sleep ${it * 2}")
-        }
-        false
-    }
-
-    suspend fun checkRcsAvailability(): Boolean {
-        appStateRepository.updateRuntimeFlags(checkingConnection = true)
-        val availability = run checkAvailability@{
-            val rcsConnected = checkRcsConnectivity()
-            if (!rcsConnected) {
-                return@checkAvailability false
-            }
-
-            var config: SysConfigResponse
-            val checkRcsAvailabilityNumbers = mutableListOf<String>()
-            withTimeoutOrNull(60.seconds) {
-                try {
-                    config = ktorClient(appPreferences.value.server).get(
-                        SysConfigApi.Id(
-                            SysConfigApi(),
-                            "check_availability_numbers"
-                        )
-                    )
-                        .body<SysConfigResponse>()
-                    Log.i(TAG, "sysConfig response: $config")
-                    checkRcsAvailabilityNumbers.addAll(
-                        config.value.split(",").map { it.trim() })
-
-                } catch (exception: Exception) {
-                    Log.e(
-                        TAG,
-                        "sysConfig Error: ${exception.message}",
-                        exception
-                    )
-                }
-            }
-
-            if (checkRcsAvailabilityNumbers.isEmpty()) {
-                Log.e(TAG, "checkRcsAvailabilityNumbers is empty")
-                return true
-            }
-
-            checkRcsAvailabilityNumbers.forEach {
-                startActivity(smsIntent(it, ""))
-                val s = withTimeoutOrNull(5.seconds) {
-                    while (true) {
-                        val traverseResult = TraverseResult()
-                        screenInspector.traverseNode(traverseResult)
-                        if (traverseResult.isRcsCapable) {
-                            return@withTimeoutOrNull true
-                        } else {
-                            Log.i(
-                                TAG,
-                                "checkRcsAvailability: RCS not detected"
-                            )
-                        }
-                        delay(200)
-                    }
-                }
-                if (s == true) {
-                    Log.i(TAG, "checkRcsAvailability: $it success")
-                    delay(1000)
-                    return@checkAvailability true
-                }
-            }
-            false
-        }
-        appStateRepository.updateRuntimeFlags(checkingConnection = false)
-        return availability
-    }
 
     private suspend fun storeNumbers() {
-        repeat(100) {
-            requestNumber(reset = true, fresh = it > 0)
-            delay(5000)
-        }
+//        repeat(100) {
+//            requestNumber(reset = true, fresh = it > 0)
+//            delay(5000)
+//        }
     }
 
 }

+ 48 - 5
app/src/main/java/com/example/modifier/service/SocketClient.kt

@@ -5,6 +5,7 @@ import android.util.Log
 import com.example.modifier.BuildConfig
 import com.example.modifier.TAG
 import com.example.modifier.model.InstallApkAction
+import com.example.modifier.model.SocketCallback
 import com.example.modifier.model.TaskAction
 import com.example.modifier.model.TaskConfig
 import com.example.modifier.serializer.Json
@@ -16,10 +17,15 @@ import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
+import kotlinx.serialization.encodeToString
 import org.json.JSONException
 import org.json.JSONObject
 
-class SocketClient(private val server: String, private val name: String) : Emitter.Listener {
+class SocketClient(
+    private val server: String,
+    private val name: String,
+    private val taskRunner: TaskRunner
+) : Emitter.Listener {
     private val tag = "$TAG/SocketClient"
     private val mSocketOpts: IO.Options = IO.Options()
     private val mSocket: Socket
@@ -42,6 +48,11 @@ class SocketClient(private val server: String, private val name: String) : Emitt
         }
     }
 
+    fun disconnect() {
+        mSocket.off()
+        mSocket.disconnect()
+    }
+
     override fun call(vararg args: Any?) {
         if (args.isNotEmpty()) {
             Log.i(TAG, "Received message: " + args[0])
@@ -54,7 +65,7 @@ class SocketClient(private val server: String, private val name: String) : Emitt
                         val to = data.optString("to")
                         val body = data.optString("body")
                         CoroutineScope(Dispatchers.IO).launch {
-                            send(
+                            taskRunner.send(
                                 to, body, TaskConfig(
                                     rcsWait = 3000,
                                     rcsInterval = 1000,
@@ -69,15 +80,39 @@ class SocketClient(private val server: String, private val name: String) : Emitt
                     }
                 } else if ("task" == action) {
                     val taskAction = Json.decodeFromString<TaskAction>(json.toString())
-
                     CoroutineScope(Dispatchers.IO).launch {
-                        runTask(taskAction)
+                        taskRunner.runTask(taskAction, onSuccess = { res ->
+                            emitEvent("callback", res)
+                        }, onError = { error ->
+                            emitEvent(
+                                "callback", SocketCallback<String>(
+                                    id = taskAction.id,
+                                    status = -1,
+                                    error = error.message
+                                )
+                            )
+                        })
                     }
                 } else if ("installApk" == action) {
                     val installApkAction = Json.decodeFromString<InstallApkAction>(json.toString())
 
                     CoroutineScope(Dispatchers.IO).launch {
-                        installApk(installApkAction)
+                        taskRunner.installApk(installApkAction, onSuccess = {
+                            emitEvent(
+                                "callback", SocketCallback<String>(
+                                    id = installApkAction.id,
+                                    status = 0,
+                                )
+                            )
+                        }, onError = { error ->
+                            emitEvent(
+                                "callback", SocketCallback<String>(
+                                    id = installApkAction.id,
+                                    status = -1,
+                                    error = error.message
+                                )
+                            )
+                        })
                     }
                 }
             }
@@ -115,4 +150,12 @@ class SocketClient(private val server: String, private val name: String) : Emitt
             e.printStackTrace()
         }
     }
+
+    fun emitEvent(event: String, data: Any) {
+        try {
+            mSocket.emit(event, JSONObject(Json.encodeToString(data)))
+        } catch (e: Exception) {
+            Log.e(tag, "emitEvent error", e)
+        }
+    }
 }

+ 66 - 7
app/src/main/java/com/example/modifier/service/TaskRunner.kt

@@ -27,6 +27,8 @@ import com.example.modifier.http.request.RcsNumberRequest
 import com.example.modifier.http.response.DeviceResponse
 import com.example.modifier.http.response.RcsNumberResponse
 import com.example.modifier.http.response.SysConfigResponse
+import com.example.modifier.model.InstallApkAction
+import com.example.modifier.model.SocketCallback
 import com.example.modifier.model.SpoofedSimInfo
 import com.example.modifier.model.TaskAction
 import com.example.modifier.model.TaskConfig
@@ -36,6 +38,7 @@ import com.example.modifier.repo.AppStateRepository
 import com.example.modifier.repo.BackupRepository
 import com.example.modifier.repo.GoogleMessageStateRepository
 import com.example.modifier.repo.SpoofedSimInfoRepository
+import com.example.modifier.serializer.Json
 import com.example.modifier.utils.changeClashProfile
 import com.example.modifier.utils.clearConv
 import com.example.modifier.utils.genICCID
@@ -49,21 +52,33 @@ import com.example.modifier.utils.smsIntent
 import com.example.modifier.utils.spoofSmsIntent
 import com.example.modifier.utils.stopClash
 import com.example.modifier.utils.uniqueId
+import io.ktor.client.HttpClient
 import io.ktor.client.call.body
+import io.ktor.client.engine.okhttp.OkHttp
+import io.ktor.client.plugins.HttpResponseValidator
+import io.ktor.client.plugins.ServerResponseException
 import io.ktor.client.plugins.resources.get
 import io.ktor.client.plugins.resources.post
 import io.ktor.client.plugins.resources.put
 import io.ktor.client.plugins.timeout
+import io.ktor.client.request.prepareGet
 import io.ktor.client.request.setBody
 import io.ktor.http.ContentType
 import io.ktor.http.contentType
+import io.ktor.utils.io.ByteReadChannel
+import io.ktor.utils.io.core.isEmpty
+import io.ktor.utils.io.core.readBytes
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withTimeout
 import kotlinx.coroutines.withTimeoutOrNull
+import kotlinx.serialization.encodeToString
+import org.json.JSONObject
+import java.io.File
 import java.time.LocalDateTime
 import java.time.temporal.ChronoUnit
 import kotlin.time.Duration.Companion.hours
@@ -95,7 +110,7 @@ class TaskRunner(
         }
     }
 
-    private suspend fun send(
+    suspend fun send(
         to: String,
         body: String,
         taskConfig: TaskConfig
@@ -153,7 +168,7 @@ class TaskRunner(
         return false
     }
 
-    private suspend fun runTask(
+    suspend fun runTask(
         taskAction: TaskAction,
         onSuccess: (TaskExecutionResult) -> Unit,
         onError: (Exception) -> Unit
@@ -222,7 +237,7 @@ class TaskRunner(
         }
     }
 
-    private suspend fun reset() {
+    suspend fun reset() {
         if (isOldVersion(context)) {
             withTimeout(1.hours) {
                 while (true) {
@@ -351,7 +366,7 @@ class TaskRunner(
         }
     }
 
-    private suspend fun requestNumber(
+    suspend fun requestNumber(
         reset: Boolean = false,
         noBackup: Boolean = false,
         fresh: Boolean = false
@@ -381,7 +396,7 @@ class TaskRunner(
         var requestSuccess = false
         var retry = 0
         var needRest = reset
-        withTimeoutOrNull(1.hours) {
+        withTimeoutOrNull(2.hours) {
             while (true) {
                 delay(200)
                 needRest = needRest || retry > 2 || appState.value.requestedNum > 5
@@ -655,9 +670,12 @@ class TaskRunner(
         } else {
             Log.e(TAG, "requestNumber failed")
             appStateRepository.updateSend(false)
-            appStateRepository.updateRuntimeFlags(suspended = true)
         }
-        appStateRepository.updateRuntimeFlags(requesting = false, requestNumberState = RequestNumberState.IDLE)
+        appStateRepository.updateRuntimeFlags(
+            requesting = false,
+            requestNumberState = RequestNumberState.IDLE,
+            suspended = !requestSuccess
+        )
     }
 
     private suspend fun checkRcsConnectivity(): Boolean = run checkRcsConnection@{
@@ -747,4 +765,45 @@ class TaskRunner(
         appStateRepository.updateRuntimeFlags(checkingConnection = false)
         return availability
     }
+
+    suspend fun installApk(
+        installApkAction: InstallApkAction,
+        onSuccess: () -> Unit,
+        onError: (Exception) -> Unit
+    ) {
+        try {
+            val file = withContext(Dispatchers.IO) {
+                File.createTempFile("files", ".apk")
+            }
+
+            HttpClient(OkHttp) {
+                HttpResponseValidator {
+                    validateResponse { response ->
+                        if (response.status.value !in 200..299) {
+                            throw ServerResponseException(
+                                response,
+                                "Error " + response.status.value.toString()
+                            )
+                        }
+                    }
+                }
+            }.prepareGet(installApkAction.data.apkUrl)
+                .execute { httpResponse ->
+                    val channel: ByteReadChannel = httpResponse.body()
+                    while (!channel.isClosedForRead) {
+                        val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
+                        while (!packet.isEmpty) {
+                            val bytes = packet.readBytes()
+                            file.appendBytes(bytes)
+                        }
+                    }
+                }
+            Log.i(TAG, "Apk file saved to ${file.path}")
+            shellRun("pm install -d -r ${file.path}")
+            onSuccess()
+        } catch (e: Exception) {
+            Log.e("Modifier", "Failed to install apk", e)
+            onError(e)
+        }
+    }
 }

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

@@ -79,10 +79,6 @@ class SettingsFragment : Fragment() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        CoroutineScope(Dispatchers.IO).launch {
-            appPreferences = appPreferencesRepository.stateFlow()
-            spoofedSimInfo = spoofedSimInfoRepository.stateFlow()
-        }
         requestPermissionLauncher =
             registerForActivityResult(
                 ActivityResultContracts.RequestPermission()
@@ -97,6 +93,8 @@ class SettingsFragment : Fragment() {
                 }
             }
         Log.i(TAG, "SettingsFragment.onCreate")
+
+
     }
 
     override fun onCreateView(
@@ -152,7 +150,6 @@ class SettingsFragment : Fragment() {
                 lifecycleScope.launch {
                     appPreferencesRepository.updateServer(server)
                     appPreferencesRepository.updateName(binding.etDeviceLabel.text.toString())
-                    instance?.connect()
                     Utils.makeLoadingButton(context, binding.btnServer)
                     binding.btnServer.isEnabled = false
                     delay(500)
@@ -315,7 +312,10 @@ class SettingsFragment : Fragment() {
 
             }
         }
+
         viewLifecycleOwner.lifecycleScope.launch {
+            appPreferences = appPreferencesRepository.stateFlow()
+            spoofedSimInfo = spoofedSimInfoRepository.stateFlow()
             launch {
                 appPreferencesRepository.stateFlow().collect {
                     Log.i(TAG, "appPreferencesRepository.collect")

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

@@ -115,30 +115,6 @@ class UtilsFragment : Fragment() {
             }
         }
 
-        binding.btnCheckA10y.setOnClickListener {
-            Utils.makeLoadingButton(context, binding.btnCheckA10y)
-            lifecycleScope.launch {
-                var a10y: Boolean? = null
-                withContext(Dispatchers.IO) {
-                    a10y = ModifierService.instance?.checkRcsAvailability()
-                    shellRun(CMD_BACK_APP)
-                }
-                if (isAdded) {
-                    MaterialAlertDialogBuilder(requireContext())
-                        .setMessage("RCS is ${if (a10y == true) "available" else "not available"}")
-                        .setPositiveButton("OK") { _, _ -> }
-                        .show()
-                }
-
-                binding.btnCheckA10y.setIconResource(R.drawable.ic_done)
-                binding.btnCheckA10y.text = "OK"
-                delay(1500L)
-                binding.btnCheckA10y.isEnabled = true
-                binding.btnCheckA10y.icon = null
-                binding.btnCheckA10y.text = "Check A10y"
-            }
-        }
-
         binding.btnSuspend.setOnClickListener {
             binding.btnSuspend.isEnabled = false
             Utils.makeLoadingButton(context, binding.btnSuspend)

+ 0 - 6
app/src/main/res/layout/fragment_utils.xml

@@ -196,12 +196,6 @@
                                 android:layout_height="wrap_content"
                                 android:text="Clear Msg" />
 
-                            <com.google.android.material.button.MaterialButton
-                                android:id="@+id/btn_check_a10y"
-                                android:layout_width="wrap_content"
-                                android:layout_height="wrap_content"
-                                android:layout_marginLeft="8dp"
-                                android:text="Check A10y" />
                         </LinearLayout>