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

+ 0 - 23
app/src/main/java/com/example/modifier/Global.kt

@@ -41,12 +41,6 @@ import java.util.Optional
 
 
 object Global {
-    @JvmField
-    var serverUrl: String = ""
-
-    @JvmField
-    var name: String? = ""
-
     @JvmField
     var telephonyConfig: TelephonyConfig = TelephonyConfig(
         number = "",
@@ -67,9 +61,6 @@ object Global {
     @JvmStatic
     fun load() {
         val context = Utils.getContext()
-        val prefs = context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE)
-        serverUrl = prefs.getString("server", "http://47.98.225.28") ?: ""
-        name = prefs.getString("name", Build.DEVICE)
         try {
             val file = File(ContextCompat.getDataDir(context), "config.json")
             if (file.exists()) {
@@ -96,20 +87,6 @@ object Global {
             return defServers
         }
 
-    @JvmStatic
-    fun saveServer(server: String, name: String?) {
-        serverUrl = server
-        Global.name = name
-        val context = Utils.getContext()
-        val prefs = context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE)
-        val servers = servers
-        servers.add(server)
-        prefs.edit().putStringSet("servers", servers)
-            .putString("server", server)
-            .putString("name", name)
-            .apply()
-    }
-
     @JvmStatic
     fun save() {
         val context = Utils.getContext()

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

@@ -7,7 +7,7 @@ import android.text.TextUtils
 import android.util.Log
 import android.view.accessibility.AccessibilityManager
 import androidx.core.app.ActivityCompat
-import com.example.modifier.http.KtorClient
+import com.example.modifier.http.ktorClient
 import com.example.modifier.service.ModifierService
 import io.ktor.client.request.head
 import kotlinx.coroutines.Dispatchers
@@ -145,7 +145,7 @@ suspend fun enableOverlay() {
 suspend fun syncTime() {
     try {
         Log.i("Modifier", "syncTime: start")
-        val response = KtorClient.head("http://www.baidu.com")
+        val response = ktorClient("").head("http://www.baidu.com")
         val dateHeader = response.headers["Date"]
         val date = ZonedDateTime.parse(
             dateHeader,

+ 2 - 1
app/src/main/java/com/example/modifier/data/AppPreferences.kt

@@ -5,5 +5,6 @@ data class AppPreferences(
     val id: String,
     val name: String,
     val preventClean: Boolean,
-    val preventRequest: Boolean
+    val preventRequest: Boolean,
+    val preventReset: Boolean
 )

+ 25 - 10
app/src/main/java/com/example/modifier/data/AppPreferencesRepository.kt

@@ -4,15 +4,19 @@ import android.annotation.SuppressLint
 import android.content.Context
 import android.os.Build
 import android.provider.Settings
-import androidx.datastore.core.DataStore
-import androidx.datastore.preferences.core.Preferences
 import androidx.datastore.preferences.core.booleanPreferencesKey
 import androidx.datastore.preferences.core.edit
 import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import com.example.modifier.BuildConfig
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlin.coroutines.coroutineContext
+
+val Context.appPreferencesDataStore by preferencesDataStore(name = "${BuildConfig.APPLICATION_ID}.appPreferences")
 
 class AppPreferencesRepository(
-    private val dataStore: DataStore<Preferences>,
     private val context: Context
 ) {
 
@@ -22,51 +26,62 @@ class AppPreferencesRepository(
         val NAME = stringPreferencesKey("name")
         val PREVENT_CLEAN = booleanPreferencesKey("prevent_clean")
         val PREVENT_REQUEST = booleanPreferencesKey("prevent_request")
+        val PREVENT_RESET = booleanPreferencesKey("prevent_reset")
     }
 
     @SuppressLint("HardwareIds")
-    val appPreferencesFlow = dataStore.data.map {
+    val appPreferencesFlow = context.appPreferencesDataStore.data.map {
         val server = it[PreferencesKeys.SERVER] ?: "http://47.98.225.28"
         var id = it[PreferencesKeys.ID] ?: ""
         if (id.isEmpty()) {
             id = Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
-            dataStore.edit { preferences ->
+            context.appPreferencesDataStore.edit { preferences ->
                 preferences[PreferencesKeys.ID] = id
             }
         }
         val name = it[PreferencesKeys.NAME] ?: Build.DEVICE
         val preventClean = it[PreferencesKeys.PREVENT_CLEAN] ?: false
         val preventRequest = it[PreferencesKeys.PREVENT_REQUEST] ?: false
+        val preventReset = it[PreferencesKeys.PREVENT_RESET] ?: false
         AppPreferences(
             server = server,
             id = id,
             name = name,
             preventClean = preventClean,
-            preventRequest = preventRequest
+            preventRequest = preventRequest,
+            preventReset = preventReset
         )
     }
 
+    suspend fun getAppPreferences() = appPreferencesFlow.stateIn(CoroutineScope(coroutineContext))
+
     suspend fun updateServer(server: String) {
-        dataStore.edit { preferences ->
+        context.appPreferencesDataStore.edit { preferences ->
             preferences[PreferencesKeys.SERVER] = server
         }
     }
 
     suspend fun updateName(name: String) {
-        dataStore.edit { preferences ->
+        context.appPreferencesDataStore.edit { preferences ->
             preferences[PreferencesKeys.NAME] = name
         }
     }
 
     suspend fun updatePreventClean(preventClean: Boolean) {
-        dataStore.edit { preferences ->
+        context.appPreferencesDataStore.edit { preferences ->
             preferences[PreferencesKeys.PREVENT_CLEAN] = preventClean
         }
     }
 
     suspend fun updatePreventRequest(preventRequest: Boolean) {
-        dataStore.edit { preferences ->
+        context.appPreferencesDataStore.edit { preferences ->
             preferences[PreferencesKeys.PREVENT_REQUEST] = preventRequest
         }
     }
+
+    suspend fun updatePreventReset(preventReset: Boolean) {
+        context.appPreferencesDataStore.edit { preferences ->
+            preferences[PreferencesKeys.PREVENT_RESET] = preventReset
+        }
+    }
 }

+ 37 - 13
app/src/main/java/com/example/modifier/data/AppStateRepository.kt

@@ -1,18 +1,22 @@
 package com.example.modifier.data
 
 import android.content.Context
-import androidx.datastore.core.DataStore
-import androidx.datastore.preferences.core.Preferences
 import androidx.datastore.preferences.core.booleanPreferencesKey
 import androidx.datastore.preferences.core.edit
 import androidx.datastore.preferences.core.intPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.asFlow
+import com.example.modifier.BuildConfig
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+import kotlin.coroutines.coroutineContext
+
+val Context.appStateDataStore by preferencesDataStore(name = "${BuildConfig.APPLICATION_ID}.appState")
 
 class AppStateRepository(
-    private val dataStore: DataStore<Preferences>,
-    private val context: Context
+    val context: Context
 ) {
 
     private object PreferencesKeys {
@@ -24,8 +28,8 @@ class AppStateRepository(
 
     private val appRuntimeFlags = MutableLiveData(AppRuntimeFlags())
 
-    val appStateFlow =
-        dataStore.data.combine(appRuntimeFlags.asFlow()) { preferences, runtimeFlags ->
+    private val appStateFlow =
+        context.appStateDataStore.data.combine(appRuntimeFlags.asFlow()) { preferences, runtimeFlags ->
             AppState(
                 send = preferences[PreferencesKeys.SEND] ?: false,
                 executedNum = preferences[PreferencesKeys.EXECUTED_NUM] ?: 0,
@@ -39,8 +43,10 @@ class AppStateRepository(
             )
         }
 
+    suspend fun getAppState() = appStateFlow.stateIn(CoroutineScope(coroutineContext))
+
     suspend fun incrementExecutedNum(success: Boolean) {
-        dataStore.edit { preferences ->
+        context.appStateDataStore.edit { preferences ->
             val executedNum = (preferences[PreferencesKeys.EXECUTED_NUM] ?: 0) + 1
             preferences[PreferencesKeys.EXECUTED_NUM] = executedNum
             if (success) {
@@ -51,23 +57,41 @@ class AppStateRepository(
     }
 
     suspend fun incrementRequestedNum() {
-        dataStore.edit { preferences ->
+        context.appStateDataStore.edit { preferences ->
             val requestedNum = (preferences[PreferencesKeys.REQUESTED_NUM] ?: 0) + 1
             preferences[PreferencesKeys.REQUESTED_NUM] = requestedNum
         }
     }
 
+    suspend fun resetExecutedNum() {
+        context.appStateDataStore.edit { preferences ->
+            preferences[PreferencesKeys.EXECUTED_NUM] = 0
+        }
+    }
+
+    suspend fun resetSuccessNum() {
+        context.appStateDataStore.edit { preferences ->
+            preferences[PreferencesKeys.SUCCESS_NUM] = 0
+        }
+    }
+
+    suspend fun resetRequestedNum() {
+        context.appStateDataStore.edit { preferences ->
+            preferences[PreferencesKeys.REQUESTED_NUM] = 0
+        }
+    }
+
     suspend fun updateSend(send: Boolean) {
-        dataStore.edit { preferences ->
+        context.appStateDataStore.edit { preferences ->
             preferences[PreferencesKeys.SEND] = send
         }
     }
 
     fun updateRuntimeFlags(
-        running: Boolean?,
-        requesting: Boolean?,
-        preparing: Boolean?,
-        checkingConnection: Boolean?
+        running: Boolean? = null,
+        requesting: Boolean? = null,
+        preparing: Boolean? = null,
+        checkingConnection: Boolean? = null
     ) {
         val value = appRuntimeFlags.value ?: AppRuntimeFlags()
         appRuntimeFlags.postValue(

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

@@ -21,7 +21,7 @@ interface BackupItemDao {
     @Query("SELECT * from backupitem WHERE id = :id")
     suspend fun getItem(id: Int): BackupItem?
 
-    @Query("SELECT * FROM backupitem ORDER BY id DESC")
+    @Query("SELECT * FROM backupitem ORDER BY id")
     suspend fun getAll(): List<BackupItem>
 
     @Query("SELECT * FROM backupitem WHERE country = :country AND number = :number limit 1")

+ 9 - 5
app/src/main/java/com/example/modifier/http/KtorClient.kt

@@ -1,6 +1,8 @@
 package com.example.modifier.http
 
-import com.example.modifier.Global
+import android.util.Log
+import com.example.modifier.Utils
+import com.example.modifier.data.AppPreferencesRepository
 import com.example.modifier.http.response.ErrorResponse
 import io.ktor.client.HttpClient
 import io.ktor.client.call.body
@@ -21,6 +23,9 @@ import io.ktor.client.plugins.resources.Resources
 import io.ktor.client.utils.unwrapCancellationException
 import io.ktor.serialization.kotlinx.json.json
 import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.last
+import kotlinx.coroutines.runBlocking
 import kotlinx.serialization.ExperimentalSerializationApi
 import kotlinx.serialization.json.Json
 
@@ -32,11 +37,10 @@ private fun Throwable.isTimeoutException(): Boolean {
 }
 
 @OptIn(ExperimentalSerializationApi::class)
-val KtorClient = HttpClient(OkHttp) {
+fun ktorClient(baseUrl: String) = HttpClient(OkHttp) {
     defaultRequest {
-        Global.load()
-        url(Global.serverUrl.endsWith("/").let {
-            if (it) Global.serverUrl else "${Global.serverUrl}/"
+        url(baseUrl.endsWith("/").let {
+            if (it) baseUrl else "$baseUrl/"
         })
     }
     install(HttpSend) {

+ 103 - 179
app/src/main/java/com/example/modifier/service/ModifierService.kt

@@ -29,10 +29,6 @@ import android.widget.FrameLayout
 import androidx.annotation.MenuRes
 import androidx.appcompat.widget.PopupMenu
 import androidx.core.content.ContextCompat
-import androidx.datastore.core.DataStore
-import androidx.datastore.preferences.core.Preferences
-import androidx.datastore.preferences.preferencesDataStore
-import androidx.lifecycle.MediatorLiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.Observer
 import androidx.lifecycle.liveData
@@ -40,7 +36,6 @@ import com.example.modifier.BuildConfig
 import com.example.modifier.Global
 import com.example.modifier.Global.backup
 import com.example.modifier.Global.changeClashProfile
-import com.example.modifier.Global.load
 import com.example.modifier.Global.resetAll
 import com.example.modifier.Global.restartModifier
 import com.example.modifier.Global.stopClash
@@ -54,7 +49,9 @@ import com.example.modifier.constants.CMD_RCS_SETTINGS_ACTIVITY
 import com.example.modifier.constants.PACKAGE_GMS
 import com.example.modifier.constants.PACKAGE_MESSAGING
 import com.example.modifier.data.AppDatabase
+import com.example.modifier.data.AppPreferences
 import com.example.modifier.data.AppPreferencesRepository
+import com.example.modifier.data.AppState
 import com.example.modifier.data.AppStateRepository
 import com.example.modifier.data.BackupItemDao
 import com.example.modifier.databinding.FloatingWindowBinding
@@ -62,7 +59,7 @@ import com.example.modifier.enums.RcsConfigureState
 import com.example.modifier.enums.RcsConnectionStatus
 import com.example.modifier.extension.kill
 import com.example.modifier.hasRootAccess
-import com.example.modifier.http.KtorClient
+import com.example.modifier.http.ktorClient
 import com.example.modifier.http.api.DeviceApi
 import com.example.modifier.http.api.RcsNumberApi
 import com.example.modifier.http.api.SysConfigApi
@@ -73,6 +70,7 @@ import com.example.modifier.http.response.SysConfigResponse
 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.model.TaskExecutionResult
 import com.example.modifier.model.TelephonyConfig
 import com.example.modifier.serializer.Json
@@ -86,7 +84,6 @@ 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.retry
 import io.ktor.client.plugins.timeout
 import io.ktor.client.request.prepareGet
 import io.ktor.client.request.setBody
@@ -101,6 +98,7 @@ import io.socket.emitter.Emitter
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.suspendCancellableCoroutine
@@ -118,7 +116,6 @@ import java.time.temporal.ChronoUnit
 import java.util.Optional
 import java.util.Timer
 import java.util.concurrent.atomic.AtomicReference
-import java.util.concurrent.locks.ReentrantLock
 import kotlin.coroutines.resume
 import kotlin.math.max
 import kotlin.math.min
@@ -127,10 +124,6 @@ import kotlin.time.Duration.Companion.hours
 import kotlin.time.Duration.Companion.minutes
 import kotlin.time.Duration.Companion.seconds
 
-
-val Context.appPreferencesDataStore by preferencesDataStore(name = "${BuildConfig.APPLICATION_ID}.appPreferences")
-val Context.appStateDataStore by preferencesDataStore(name = "${BuildConfig.APPLICATION_ID}.appState")
-
 @SuppressLint("SetTextI18n")
 class ModifierService : AccessibilityService(), Emitter.Listener {
     companion object {
@@ -149,71 +142,10 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
     private lateinit var mSocket: Socket
     private lateinit var binding: FloatingWindowBinding
 
-    val lock = ReentrantLock()
-
-    private var canSend: Boolean
-        get() {
-            return getSharedPreferences(
-                BuildConfig.APPLICATION_ID,
-                MODE_PRIVATE
-            ).getBoolean("canSend", false)
-        }
-        set(value) {
-            getSharedPreferences(BuildConfig.APPLICATION_ID, MODE_PRIVATE).edit()
-                .putBoolean("canSend", value).apply()
-            reportDeviceStatues()
-        }
-
-    private var counter = 0
-    private var cleanCount = 0
-    private var lastSend = 0L
-    private var rcsInterval = 0L
-    private var requestNumberInterval = 0L
-    private val running = MutableLiveData(false)
-    private val requesting = MutableLiveData(false)
-    private val preparing = MutableLiveData(false)
-    private val checkingConnection = MutableLiveData(false)
     private var currentTaskId = 0
-
-    private var busy = MediatorLiveData<Boolean>().apply {
-        addSource(running) {
-            value = it || requesting.value!!
-        }
-        addSource(requesting) {
-            value = it || running.value!!
-        }
-        addSource(preparing) {
-            value = it || running.value!!
-        }
-        addSource(checkingConnection) {
-            value = it || running.value!!
-        }
-        value =
-            requesting.value!! || running.value!! || preparing.value!! || checkingConnection.value!!
-    }
+    private var lastSend = 0L
 
     private val rcsConfigureState = MutableLiveData(RcsConfigureState.CONFIGURED)
-
-    private var sendCount: Int
-        get() {
-            return getSharedPreferences(BuildConfig.APPLICATION_ID, MODE_PRIVATE)
-                .getInt("sendCount", 0)
-        }
-        set(value) {
-            getSharedPreferences(BuildConfig.APPLICATION_ID, MODE_PRIVATE).edit()
-                .putInt("sendCount", value).apply()
-        }
-
-    private var requestNumberCount: Int
-        get() {
-            return getSharedPreferences(BuildConfig.APPLICATION_ID, MODE_PRIVATE)
-                .getInt("requestNumberCount", 0)
-        }
-        set(value) {
-            getSharedPreferences(BuildConfig.APPLICATION_ID, MODE_PRIVATE).edit()
-                .putInt("requestNumberCount", value).apply()
-        }
-
     private val logcat = liveData(Dispatchers.IO) {
         try {
             val logs = CircularFifoQueue<String>(128)
@@ -278,29 +210,26 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         AppDatabase.getDatabase(this).itemDao()
     }
     private val appPreferencesRepository: AppPreferencesRepository by lazy {
-        AppPreferencesRepository(appPreferencesDataStore, this)
+        AppPreferencesRepository(this)
     }
+    private lateinit var appPreferences: StateFlow<AppPreferences>
     private val appStateRepository: AppStateRepository by lazy {
-        AppStateRepository(appStateDataStore, this)
+        AppStateRepository(this)
     }
-
+    private lateinit var appState: StateFlow<AppState>
     private var requestMode = 1;
     private var currentActivity = ""
 
     fun connect() {
         try {
-            load()
-            if (this@ModifierService::binding.isInitialized) {
-                binding.swSend.text = Global.name
-            }
             if (this@ModifierService::mSocket.isInitialized) {
                 mSocket.disconnect()
             }
             mSocketOpts.query =
-                "model=${Build.MODEL}&name=${Global.name}&id=${Utils.getUniqueID()}&version=${BuildConfig.VERSION_CODE}"
+                "model=${Build.MODEL}&name=${appPreferences.value.name}&id=${Utils.getUniqueID()}&version=${BuildConfig.VERSION_CODE}"
             mSocketOpts.transports = arrayOf("websocket")
             Log.i(TAG, "Connection query: ${mSocketOpts.query}")
-            mSocket = IO.socket(Global.serverUrl, mSocketOpts)
+            mSocket = IO.socket(appPreferences.value.server, mSocketOpts)
             mSocket.on("message", this@ModifierService)
             mSocket.on(Socket.EVENT_CONNECT) {
                 Log.i(TAG, "Connected to server")
@@ -325,12 +254,13 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         }
     }
 
-
     override fun onCreate() {
         super.onCreate()
         Log.i(TAG, "Starting ModifierService")
         CoroutineScope(Dispatchers.IO).launch {
-            preparing.postValue(true)
+            appState = appStateRepository.getAppState()
+            appPreferences = appPreferencesRepository.getAppPreferences()
+            appStateRepository.updateRuntimeFlags(preparing = true)
             val hasRoot = run checkRoot@{
                 repeat(30) {
                     if (hasRootAccess()) {
@@ -352,15 +282,16 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
                 Global.killPhoneProcess(force = false)
             }
-            preparing.postValue(false)
+            appStateRepository.updateRuntimeFlags(preparing = false)
+
+            connect()
+            val timer = Timer()
+            timer.schedule(object : java.util.TimerTask() {
+                override fun run() {
+                    reportDeviceStatues()
+                }
+            }, 0, 3000)
         }
-        connect()
-        val timer = Timer()
-        timer.schedule(object : java.util.TimerTask() {
-            override fun run() {
-                reportDeviceStatues()
-            }
-        }, 0, 3000)
     }
 
     override fun onAccessibilityEvent(event: AccessibilityEvent) {
@@ -400,7 +331,17 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         val to = data.optString("to")
                         val body = data.optString("body")
                         CoroutineScope(Dispatchers.IO).launch {
-                            send(to, body, 2000)
+                            send(
+                                to, body, TaskConfig(
+                                    rcsWait = 3000,
+                                    rcsInterval = 1000,
+                                    cleanCount = 10,
+                                    requestNumberInterval = 50,
+                                    checkConnection = true,
+                                    useBackup = false,
+                                    endToEndEncryption = true
+                                )
+                            )
                         }
                     }
                 } else if ("task" == action) {
@@ -479,7 +420,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
     }
 
     private suspend fun runTask(taskAction: TaskAction) {
-        if (checkingConnection.value!! || running.value!! || preparing.value!! || requesting.value!!) {
+        if (appState.value.busy) {
             mSocket.emit(
                 "callback",
                 JSONObject(
@@ -496,15 +437,12 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         }
 
         try {
-            val rcsWait = taskAction.data.config.rcsWait
-            cleanCount = taskAction.data.config.cleanCount
-            rcsInterval = taskAction.data.config.rcsInterval
-            requestNumberInterval = taskAction.data.config.requestNumberInterval
+            val taskConfig = taskAction.data.config
             currentTaskId = taskAction.data.taskId
             requestMode = if (taskAction.data.config.useBackup) 2 else 1
 
             if (taskAction.data.config.checkConnection) {
-                checkingConnection.postValue(true)
+                appStateRepository.updateRuntimeFlags(checkingConnection = true)
                 if (!checkRcsAvailability()) {
                     mSocket.emit(
                         "callback",
@@ -519,13 +457,13 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         )
                     )
                     requestNumber()
-                    checkingConnection.postValue(false)
+                    appStateRepository.updateRuntimeFlags(checkingConnection = false)
                     return
                 }
-                checkingConnection.postValue(false)
+                appStateRepository.updateRuntimeFlags(checkingConnection = false)
             }
 
-            running.postValue(true)
+            appStateRepository.updateRuntimeFlags(running = true)
             val success = ArrayList<Int>()
             val fail = ArrayList<Int>()
             for (i in 0 until taskAction.data.tasks.size) {
@@ -534,8 +472,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     if (send(
                             taskItem.number,
                             taskItem.message,
-                            rcsWait,
-                            taskAction.data.config.endToEndEncryption
+                            taskConfig
                         )
                     ) {
                         success.add(taskItem.id)
@@ -546,7 +483,6 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     Log.e(TAG, "runTaskError: ${e.message}", e)
                     fail.add(taskItem.id)
                 }
-                updateCountUI()
             }
             shellRun(CMD_BACK)
             mSocket.emit(
@@ -561,19 +497,19 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     )
                 )
             )
-            if (requestNumberInterval in 1..sendCount) {
+            if (taskConfig.requestNumberInterval in 1..appState.value.successNum) {
                 delay(3000)
                 requestNumber()
-            } else if (cleanCount in 1..counter) {
+            } else if (taskConfig.cleanCount in 1..appState.value.executedNum) {
                 delay(3000)
                 Global.clearConv();
                 shellRun(CMD_MESSAGING_APP)
                 delay(3000)
-                counter = 0
+                appStateRepository.resetExecutedNum()
             } else {
                 delay(2000)
             }
-            running.postValue(false)
+            appStateRepository.updateRuntimeFlags(running = false)
         } catch (e: Exception) {
             Log.e(TAG, "runTaskError: ${e.message}", e)
             mSocket.emit(
@@ -588,7 +524,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     )
                 )
             )
-            running.postValue(false)
+            appStateRepository.updateRuntimeFlags(running = false)
         }
     }
 
@@ -605,8 +541,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
     private suspend fun send(
         to: String,
         body: String,
-        rcsWait: Long,
-        endToEndEncryption: Boolean = false
+        taskConfig: TaskConfig
     ): Boolean {
         Log.i(TAG, "Sending SMS to $to: $body")
         startActivity(smsIntent(to, body))
@@ -615,14 +550,14 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             delay(1000)
             var success = false
             var traverseResult = TraverseResult()
-            withTimeoutOrNull(rcsWait) {
+            withTimeoutOrNull(taskConfig.rcsWait) {
                 while (true) {
                     val root = rootInActiveWindow
                     traverseResult = TraverseResult()
                     traverseNode(root, traverseResult)
                     if (traverseResult.isRcsCapable) {
                         if (traverseResult.sendBtn != null) {
-                            if (endToEndEncryption) {
+                            if (taskConfig.endToEndEncryption) {
                                 if (traverseResult.encrypted) {
                                     break
                                 }
@@ -641,22 +576,21 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     Log.i(TAG, "Clicking send button")
 
                     val dt = System.currentTimeMillis() - lastSend
-                    if (rcsInterval > 0 && dt < rcsInterval) {
+                    if (taskConfig.rcsInterval > 0 && dt < taskConfig.rcsInterval) {
                         Log.i(TAG, "Waiting for RCS interval")
-                        delay(rcsInterval - dt)
+                        delay(taskConfig.rcsInterval - dt)
                     }
                     traverseResult.sendBtn!!.performAction(AccessibilityNodeInfo.ACTION_CLICK)
                     lastSend = System.currentTimeMillis()
                     success = true
-                    sendCount++
                 }
             } else {
                 Log.i(TAG, "RCS not detected")
             }
-            counter++
+            appStateRepository.incrementExecutedNum(success)
             Log.i(
                 TAG,
-                "sendCount: $sendCount, Counter: $counter, cleanCount: $cleanCount, requestNumberInterval: $requestNumberInterval"
+                "executedNum: ${appState.value.executedNum}, successNum: ${appState.value.successNum}"
             )
             delay(1000)
             return success
@@ -791,7 +725,6 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         val newContext = DynamicColors.wrapContextIfAvailable(applicationContext, R.style.AppTheme)
         val inflater = LayoutInflater.from(newContext)
         binding = FloatingWindowBinding.inflate(inflater, mLayout, true)
-        binding.swSend.text = Global.name
         binding.tvVersion.text = "v${BuildConfig.VERSION_CODE}"
         windowManager.addView(mLayout, layoutParams)
 
@@ -841,40 +774,40 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             }
             false
         }
-        binding.swSend.setOnTouchListener(touchListener)
 
-        binding.swConnect.isChecked = true
-        binding.swConnect.setOnCheckedChangeListener { buttonView: CompoundButton?, isChecked: Boolean ->
-            if (isChecked) {
-                connect()
-            } else {
-                if (this::mSocket.isInitialized) {
-                    mSocket.disconnect()
+        CoroutineScope(Dispatchers.Main).launch {
+            appState.collect {
+                binding.swSend.isChecked = it.send
+                binding.btnReq.isEnabled = !it.requesting
+                binding.tvCount.text = "${it.successNum} / ${it.executedNum}"
+                withContext(Dispatchers.IO) {
+                    reportDeviceStatues()
                 }
             }
         }
-
-        binding.swSend.isChecked = canSend
+        CoroutineScope(Dispatchers.Main).launch {
+            appPreferences.collect {
+                binding.swSend.text = it.name
+            }
+        }
+        binding.swSend.setOnTouchListener(touchListener)
         binding.swSend.setOnCheckedChangeListener { buttonView: CompoundButton?, isChecked: Boolean ->
-            canSend = isChecked
+            CoroutineScope(Dispatchers.IO).launch {
+                appStateRepository.updateSend(isChecked)
+            }
         }
 
         logcat.observeForever {
             binding.tvLog.text = it
             binding.scroll.fullScroll(View.FOCUS_DOWN)
         }
-        requesting.observeForever {
-            binding.btnReq.isEnabled = !it
-        }
 
         binding.btnReq.setOnClickListener {
             CoroutineScope(Dispatchers.IO).launch {
                 requestNumber(reset = false, noBackup = true)
             }
         }
-//        binding.btnInspect.setOnClickListener {
-//            traverseNode(rootInActiveWindow, TraverseResult())
-//        }
+
         binding.btnReset.setOnClickListener {
             binding.btnReset.isEnabled = false
             CoroutineScope(Dispatchers.IO).launch {
@@ -887,10 +820,6 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         binding.btnMore.setOnClickListener {
             showMenu(newContext, binding.btnMore, R.menu.more)
         }
-        busy.observeForever {
-            reportDeviceStatues()
-        }
-        updateCountUI()
     }
 
     private fun showMenu(context: Context, v: View, @MenuRes menuRes: Int) {
@@ -934,9 +863,11 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     }
 
                     R.id.reset_counter -> {
-                        sendCount = 0
-                        counter = 0
-                        updateCountUI()
+                        CoroutineScope(Dispatchers.IO).launch {
+                            appStateRepository.resetExecutedNum()
+                            appStateRepository.resetSuccessNum()
+                            appStateRepository.resetRequestedNum()
+                        }
                     }
                 }
 
@@ -953,18 +884,14 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         popup.show()
     }
 
-    private fun updateCountUI() {
-        binding.tvCount.text = "${sendCount} / ${counter}"
-    }
-
     private fun reportDeviceStatues() {
         if (this::mSocket.isInitialized) {
             val data = JSONObject()
             try {
                 data.put("action", "updateDevice")
                 val dataObj = JSONObject()
-                dataObj.put("canSend", canSend)
-                dataObj.put("busy", busy.value)
+                dataObj.put("canSend", appState.value.send)
+                dataObj.put("busy", appState.value.busy)
                 dataObj.put("currentCountry", Global.telephonyConfig.country)
                 data.put("data", dataObj)
                 mSocket.emit("message", data)
@@ -1140,7 +1067,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         ).let { it == RcsConfigureState.READY }
                     }
                     Log.i(TAG, "waitForRcsState: $resetSuccess")
-                    requestNumberCount = 0
+                    appStateRepository.resetRequestedNum()
                     if (resetSuccess) {
                         delay(3000)
                         break
@@ -1200,7 +1127,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         ).let { it == RcsConfigureState.READY }
                     }
                     Log.i(TAG, "waitForRcsState: $resetSuccess")
-                    requestNumberCount = 0
+                    appStateRepository.resetRequestedNum()
                     if (resetSuccess) {
                         delay(3000)
                         break
@@ -1217,39 +1144,38 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
     ) {
         val color = ContextCompat.getColorStateList(binding.root.context, R.color.btn_color)
         binding.btnReq.backgroundTintList = color
-        if (getSharedPreferences("settings", Context.MODE_PRIVATE)
-                .getBoolean("do_not_request", false)
-        ) {
+        if (appPreferences.value.preventRequest) {
             return
         }
-        if (requesting.value!!) {
+        if (appState.value.requesting) {
             return
         }
 
-        requesting.postValue(true)
+        appStateRepository.updateRuntimeFlags(requesting = true)
 
         if (Global.telephonyConfig.available == true) {
             backup(
                 backupItemDao = backupItemDao,
                 type = "auto",
-                sendCount = sendCount,
+                sendCount = appState.value.executedNum,
                 fresh = fresh
             )
         } else {
             Global.clearConv();
         }
 
-        requestNumberCount++
+        appStateRepository.incrementRequestedNum()
         var requestSuccess = false
         var retry = 0
         var needRest = reset
         withTimeoutOrNull(1.hours) {
             while (true) {
                 delay(200)
-                needRest = needRest || retry > 2 || requestNumberCount > 5
+                needRest = needRest || retry > 2 || appState.value.requestedNum > 5
                 try {
-                    val device = KtorClient.get(DeviceApi.Id(id = Utils.getUniqueID()))
-                        .body<DeviceResponse>()
+                    val device =
+                        ktorClient(appPreferences.value.server).get(DeviceApi.Id(id = Utils.getUniqueID()))
+                            .body<DeviceResponse>()
                     if (Global.isClashInstalled(applicationContext)) {
 
                         val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
@@ -1291,10 +1217,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         }
                     }
 
-                    if (needRest &&
-                        !getSharedPreferences("settings", Context.MODE_PRIVATE)
-                            .getBoolean("do_not_reset", false)
-                    ) {
+                    if (needRest && !appPreferences.value.preventReset) {
                         reset()
                         retry = 0
                         needRest = false
@@ -1312,7 +1235,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     if (!TextUtils.isEmpty(device.pinCountry)) {
                         req.country = device.pinCountry
                     }
-                    val response = KtorClient.put(
+                    val response = ktorClient(appPreferences.value.server).put(
                         RcsNumberApi()
                     ) {
                         contentType(ContentType.Application.Json)
@@ -1385,7 +1308,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
 
                     launch {
                         try {
-                            KtorClient.post(
+                            ktorClient(appPreferences.value.server).post(
                                 RcsNumberApi.Id.OtpState(
                                     RcsNumberApi.Id(
                                         RcsNumberApi(),
@@ -1406,8 +1329,9 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     withTimeoutOrNull(60.seconds) {
                         while (true) {
                             try {
-                                rcsNumber = KtorClient.get(RcsNumberApi.Id(id = rcsNumber.id))
-                                    .body<RcsNumberResponse>()
+                                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
@@ -1468,7 +1392,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         } else {
                             launch {
                                 try {
-                                    KtorClient.post(
+                                    ktorClient(appPreferences.value.server).post(
                                         RcsNumberApi.Id.Configured(
                                             RcsNumberApi.Id(
                                                 RcsNumberApi(),
@@ -1496,22 +1420,22 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         if (requestSuccess) {
             Global.telephonyConfig.available = true
             Global.save()
-            sendCount = 0
-            counter = 0
+            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")
-            canSend = false
+            appStateRepository.updateSend(false)
             withContext(Dispatchers.Main) {
                 binding.swSend.isChecked = false
                 binding.btnReq.backgroundTintList =
                     ContextCompat.getColorStateList(binding.root.context, R.color.btn_color_error)
             }
         }
-        requesting.postValue(false)
+        appStateRepository.updateRuntimeFlags(requesting = false)
     }
 
     private suspend fun checkRcsConnectivity(): Boolean = run checkRcsConnection@{
@@ -1537,7 +1461,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
     }
 
     suspend fun checkRcsAvailability(): Boolean {
-        checkingConnection.postValue(true)
+        appStateRepository.updateRuntimeFlags(checkingConnection = true)
         val availability = run checkAvailability@{
             val rcsConnected = checkRcsConnectivity()
             if (!rcsConnected) {
@@ -1548,7 +1472,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             val checkRcsAvailabilityNumbers = mutableListOf<String>()
             withTimeoutOrNull(60.seconds) {
                 try {
-                    config = KtorClient.get(
+                    config = ktorClient(appPreferences.value.server).get(
                         SysConfigApi.Id(
                             SysConfigApi(),
                             "check_availability_numbers"
@@ -1592,7 +1516,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             }
             false
         }
-        checkingConnection.postValue(false)
+        appStateRepository.updateRuntimeFlags(checkingConnection = false)
         return availability
     }
 

+ 0 - 6
app/src/main/java/com/example/modifier/ui/backup/BackupFragment.kt

@@ -1,6 +1,5 @@
 package com.example.modifier.ui.backup
 
-import android.annotation.SuppressLint
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
@@ -12,21 +11,16 @@ import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
 import androidx.recyclerview.widget.LinearLayoutManager
 import com.example.modifier.Global
-import com.example.modifier.MyApplication
 import com.example.modifier.R
 import com.example.modifier.Utils
 import com.example.modifier.adapter.BackupAdapter
-import com.example.modifier.data.AppContainer
 import com.example.modifier.data.AppDatabase
 import com.example.modifier.data.BackupItem
 import com.example.modifier.data.BackupItemDao
 import com.example.modifier.databinding.FragmentBackupBinding
-import com.example.modifier.service.ModifierService
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
-import java.time.LocalDateTime
-import java.util.Date
 
 class BackupFragment : Fragment() {
     private lateinit var binding: FragmentBackupBinding

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

@@ -6,8 +6,6 @@ import android.content.Context
 import android.content.DialogInterface
 import android.content.pm.PackageManager
 import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
 import android.telephony.SmsManager
 import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
@@ -21,47 +19,36 @@ import androidx.activity.result.ActivityResultLauncher
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.core.app.ActivityCompat
 import androidx.core.content.ContextCompat
-import androidx.datastore.core.DataStore
-import androidx.datastore.preferences.core.Preferences
-import androidx.datastore.preferences.core.stringPreferencesKey
-import androidx.datastore.preferences.preferencesDataStore
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.lifecycleScope
-import com.example.modifier.BuildConfig
 import com.example.modifier.Global
 import com.example.modifier.Global.save
-import com.example.modifier.Global.saveServer
 import com.example.modifier.Global.servers
 import com.example.modifier.R
 import com.example.modifier.Utils
+import com.example.modifier.data.AppPreferences
+import com.example.modifier.data.AppPreferencesRepository
 import com.example.modifier.databinding.FragmentSettingsBinding
-import com.example.modifier.http.KtorClient
+import com.example.modifier.http.ktorClient
 import com.example.modifier.http.api.RcsNumberApi
 import com.example.modifier.http.request.RcsNumberRequest
 import com.example.modifier.http.response.RcsNumberResponse
 import com.example.modifier.model.TelephonyConfig
-import com.example.modifier.service.ModifierService
-import com.example.modifier.service.ModifierService.Companion
 import com.example.modifier.service.ModifierService.Companion.instance
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import io.ktor.client.call.body
 import io.ktor.client.plugins.resources.put
-import io.ktor.client.plugins.retry
 import io.ktor.client.request.setBody
 import io.ktor.client.statement.HttpResponse
 import io.ktor.client.statement.bodyAsText
 import io.ktor.http.ContentType
 import io.ktor.http.contentType
-import io.ktor.http.isSuccess
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.last
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
-import kotlinx.coroutines.withTimeout
-import kotlinx.coroutines.withTimeoutOrNull
 import org.apache.commons.lang3.RandomStringUtils
 import org.apache.commons.lang3.StringUtils
 import org.apache.commons.validator.routines.UrlValidator
@@ -74,7 +61,10 @@ class SettingsFragment : Fragment() {
 
     private lateinit var binding: FragmentSettingsBinding
     private lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
-    private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = BuildConfig.APPLICATION_ID)
+    private val appPreferencesRepository: AppPreferencesRepository by lazy {
+        AppPreferencesRepository(requireContext())
+    }
+    private lateinit var appPreferences: StateFlow<AppPreferences>
 
     init {
         Log.i("SettingsFragment", "SettingsFragment")
@@ -82,6 +72,9 @@ class SettingsFragment : Fragment() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+        CoroutineScope(Dispatchers.IO).launch {
+            appPreferences = appPreferencesRepository.getAppPreferences()
+        }
         requestPermissionLauncher =
             registerForActivityResult(
                 ActivityResultContracts.RequestPermission()
@@ -102,6 +95,7 @@ class SettingsFragment : Fragment() {
         lifecycleScope.launch {
             loadConfigs()
         }
+
     }
 
     override fun onCreateView(
@@ -154,13 +148,11 @@ class SettingsFragment : Fragment() {
                 Toast.makeText(context, "Invalid server URL", Toast.LENGTH_SHORT).show()
                 return@setOnClickListener
             }
-            saveServer(server, binding.etDeviceLabel.text.toString())
             binding.etServer.setSimpleItems(servers.toTypedArray<String>())
-
-            val modifierService = instance
-            modifierService?.connect()
-
             lifecycleScope.launch {
+                appPreferencesRepository.updateServer(server)
+                appPreferencesRepository.updateName(binding.etDeviceLabel.text.toString())
+                instance?.connect()
                 Utils.makeLoadingButton(context, binding.btnServer)
                 binding.btnServer.isEnabled = false
                 delay(500)
@@ -181,7 +173,7 @@ class SettingsFragment : Fragment() {
                 withContext(Dispatchers.IO) req@{
                     val response: HttpResponse
                     try {
-                        response = KtorClient.put(
+                        response = ktorClient(appPreferences.value.server).put(
                             RcsNumberApi()
                         ) {
                             contentType(ContentType.Application.Json)
@@ -241,18 +233,20 @@ class SettingsFragment : Fragment() {
             }
         }
 
-        val prefs = requireContext().getSharedPreferences("settings", Context.MODE_PRIVATE)
-        binding.switchClean.isChecked = prefs.getBoolean("do_not_clean", false)
         binding.switchClean.setOnCheckedChangeListener { buttonView, isChecked ->
-            prefs.edit().putBoolean("do_not_clean", isChecked).apply()
+            CoroutineScope(Dispatchers.IO).launch {
+                appPreferencesRepository.updatePreventClean(isChecked)
+            }
         }
-        binding.switchRequest.isChecked = prefs.getBoolean("do_not_request", false)
         binding.switchRequest.setOnCheckedChangeListener { buttonView, isChecked ->
-            prefs.edit().putBoolean("do_not_request", isChecked).apply()
+            CoroutineScope(Dispatchers.IO).launch {
+                appPreferencesRepository.updatePreventRequest(isChecked)
+            }
         }
-        binding.swReset.isChecked = prefs.getBoolean("do_not_reset", false)
         binding.swReset.setOnCheckedChangeListener { buttonView, isChecked ->
-            prefs.edit().putBoolean("do_not_reset", isChecked).apply()
+            CoroutineScope(Dispatchers.IO).launch {
+                appPreferencesRepository.updatePreventReset(isChecked)
+            }
         }
 
         binding.btnCheck.setOnClickListener {
@@ -328,12 +322,13 @@ class SettingsFragment : Fragment() {
         }
         lifecycleScope.launch {
             loadConfigs()
-
-            val server = requireContext().dataStore.data.map {
-                it[stringPreferencesKey("server")]
+            appPreferencesRepository.getAppPreferences().collect {
+                binding.etServer.setText(it.server)
+                binding.etDeviceLabel.setText(it.name)
+                binding.switchClean.isChecked = it.preventClean
+                binding.switchRequest.isChecked = it.preventRequest
+                binding.swReset.isChecked = it.preventReset
             }
-
-            Log.i("xxxxxxserver", "server: ${server.first()}")
         }
 
         return binding.root
@@ -343,9 +338,7 @@ class SettingsFragment : Fragment() {
         withContext(Dispatchers.IO) {
             Global.load()
             withContext(Dispatchers.Main) {
-                binding.etServer.setText(Global.serverUrl)
                 binding.etServer.setSimpleItems(servers.toTypedArray<String>())
-                binding.etDeviceLabel.setText(Global.name)
                 val telephonyConfig = Global.telephonyConfig
                 binding.etNumber.setText(telephonyConfig.number)
                 binding.etMcc.setText(telephonyConfig.mcc)

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

@@ -22,10 +22,12 @@ import com.example.modifier.constants.CMD_BACK_APP
 import com.example.modifier.constants.PACKAGE_GMS
 import com.example.modifier.constants.PACKAGE_GSF
 import com.example.modifier.constants.PACKAGE_MESSAGING
+import com.example.modifier.data.AppPreferences
+import com.example.modifier.data.AppPreferencesRepository
 import com.example.modifier.databinding.DialogUpdateBinding
 import com.example.modifier.databinding.FragmentUtilsBinding
 import com.example.modifier.extension.kill
-import com.example.modifier.http.KtorClient
+import com.example.modifier.http.ktorClient
 import com.example.modifier.http.api.SysConfigApi
 import com.example.modifier.http.response.SysConfigResponse
 import com.example.modifier.service.ModifierService
@@ -43,15 +45,23 @@ import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import java.io.File
 
 class UtilsFragment : Fragment() {
     private lateinit var binding: FragmentUtilsBinding
+    private val appPreferencesRepository: AppPreferencesRepository by lazy {
+        AppPreferencesRepository(requireContext())
+    }
+    private lateinit var appPreferences: StateFlow<AppPreferences>
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+        CoroutineScope(Dispatchers.IO).launch {
+            appPreferences = appPreferencesRepository.getAppPreferences()
+        }
     }
 
     override fun onCreateView(
@@ -239,7 +249,7 @@ class UtilsFragment : Fragment() {
         binding.btnUpdateModifier.setOnClickListener {
             lifecycleScope.launch {
                 try {
-                    val config = KtorClient.get(SysConfigApi.Id(SysConfigApi(), "modifier_apk"))
+                    val config = ktorClient(appPreferences.value.server).get(SysConfigApi.Id(SysConfigApi(), "modifier_apk"))
                         .body<SysConfigResponse>()
                     installApk(config.value)
                 } catch (e: Exception) {
@@ -251,7 +261,7 @@ class UtilsFragment : Fragment() {
         binding.btnUpdateMessage.setOnClickListener {
             lifecycleScope.launch {
                 try {
-                    val config = KtorClient.get(SysConfigApi.Id(SysConfigApi(), "message_apk"))
+                    val config = ktorClient(appPreferences.value.server).get(SysConfigApi.Id(SysConfigApi(), "message_apk"))
                         .body<SysConfigResponse>()
                     installApk(config.value)
                 } catch (e: Exception) {
@@ -263,7 +273,7 @@ class UtilsFragment : Fragment() {
         binding.btnUpdateGms.setOnClickListener {
             lifecycleScope.launch {
                 try {
-                    val config = KtorClient.get(SysConfigApi.Id(SysConfigApi(), "gms_apk"))
+                    val config = ktorClient(appPreferences.value.server).get(SysConfigApi.Id(SysConfigApi(), "gms_apk"))
                         .body<SysConfigResponse>()
                     installApk(config.value)
                 } catch (e: Exception) {
@@ -361,7 +371,7 @@ class UtilsFragment : Fragment() {
             try {
                 val file = File.createTempFile("files", ".apk")
 
-                KtorClient.prepareGet(url)
+                ktorClient(appPreferences.value.server).prepareGet(url)
                     .execute { httpResponse ->
                         val channel: ByteReadChannel = httpResponse.body()
                         while (!channel.isClosedForRead) {