xiongzhu 1 年間 前
コミット
ed26df9e1d

+ 1 - 1
app/build.gradle

@@ -24,7 +24,7 @@ android {
         applicationId "com.example.modifier"
         minSdk 29
         targetSdk 34
-        versionCode 145
+        versionCode 146
         versionName "1.0.1"
         archivesBaseName = "modifier-${versionCode}"
 

+ 142 - 0
app/schemas/com.example.modifier.data.AppDatabase/5.json

@@ -0,0 +1,142 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 5,
+    "identityHash": "0de21f8c407dfff0496469ab4e9dc19c",
+    "entities": [
+      {
+        "tableName": "BackupItem",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `createdAt` INTEGER NOT NULL, `numberId` INTEGER NOT NULL, `number` TEXT NOT NULL, `country` TEXT NOT NULL, `code` TEXT NOT NULL, `mcc` TEXT NOT NULL, `mnc` TEXT NOT NULL, `imei` TEXT NOT NULL, `imsi` TEXT NOT NULL, `iccid` TEXT NOT NULL, `sendCount` INTEGER NOT NULL, `path` TEXT NOT NULL, `lastUse` INTEGER NOT NULL, `type` TEXT NOT NULL, `stock` INTEGER NOT NULL, `carrierId` TEXT NOT NULL, `carrierName` TEXT NOT NULL, `retry` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "createdAt",
+            "columnName": "createdAt",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "numberId",
+            "columnName": "numberId",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "number",
+            "columnName": "number",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "country",
+            "columnName": "country",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "code",
+            "columnName": "code",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "mcc",
+            "columnName": "mcc",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "mnc",
+            "columnName": "mnc",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "imei",
+            "columnName": "imei",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "imsi",
+            "columnName": "imsi",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "iccid",
+            "columnName": "iccid",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "sendCount",
+            "columnName": "sendCount",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "path",
+            "columnName": "path",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "lastUse",
+            "columnName": "lastUse",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "type",
+            "columnName": "type",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "stock",
+            "columnName": "stock",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "carrierId",
+            "columnName": "carrierId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "carrierName",
+            "columnName": "carrierName",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "retry",
+            "columnName": "retry",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "views": [],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0de21f8c407dfff0496469ab4e9dc19c')"
+    ]
+  }
+}

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

@@ -2,27 +2,14 @@ package com.example.modifier.adapter
 
 import android.annotation.SuppressLint
 import android.content.Context
-import android.content.DialogInterface
-import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import android.widget.Toast
-import androidx.appcompat.content.res.AppCompatResources
 import androidx.recyclerview.widget.RecyclerView
-import com.example.modifier.R
-import com.example.modifier.baseTag
-import com.example.modifier.Utils
 import com.example.modifier.adapter.BackupAdapter.BackupViewHolder
+import com.example.modifier.baseTag
 import com.example.modifier.data.BackupItem
 import com.example.modifier.databinding.ItemBackupBinding
-import com.example.modifier.repo.BackupRepository
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import java.io.File
 import java.text.SimpleDateFormat
 import java.util.Date
 

+ 13 - 16
app/src/main/java/com/example/modifier/data/AppDatabase.kt

@@ -11,37 +11,33 @@ import androidx.sqlite.db.SupportSQLiteDatabase
 
 val MIGRATION_1_2 = object : Migration(1, 2) {
     override fun migrate(db: SupportSQLiteDatabase) {
-        db.execSQL(
-            "ALTER TABLE BackupItem ADD COLUMN fresh INTEGER NOT NULL DEFAULT 0"
-        )
+        db.execSQL("ALTER TABLE BackupItem ADD COLUMN fresh INTEGER NOT NULL DEFAULT 0")
     }
 }
 
 val MIGRATION_2_3 = object : Migration(2, 3) {
     override fun migrate(db: SupportSQLiteDatabase) {
-        db.execSQL(
-            "ALTER TABLE BackupItem ADD COLUMN carrierId TEXT NOT NULL DEFAULT ''"
-        )
-        db.execSQL(
-            "ALTER TABLE BackupItem ADD COLUMN carrierName TEXT NOT NULL DEFAULT ''"
-        )
+        db.execSQL("ALTER TABLE BackupItem ADD COLUMN carrierId TEXT NOT NULL DEFAULT ''")
+        db.execSQL("ALTER TABLE BackupItem ADD COLUMN carrierName TEXT NOT NULL DEFAULT ''")
     }
 }
 
 val MIGRATION_3_4 = object : Migration(3, 4) {
     override fun migrate(db: SupportSQLiteDatabase) {
-        db.execSQL(
-            "ALTER TABLE BackupItem ADD COLUMN numberId INTEGER NOT NULL DEFAULT 0"
-        )
-        db.execSQL(
-            "ALTER TABLE BackupItem RENAME COLUMN fresh TO stock"
-        )
+        db.execSQL("ALTER TABLE BackupItem ADD COLUMN numberId INTEGER NOT NULL DEFAULT 0")
+        db.execSQL("ALTER TABLE BackupItem RENAME COLUMN fresh TO stock")
+    }
+}
+
+val MIGRATION_4_5 = object : Migration(4, 5) {
+    override fun migrate(db: SupportSQLiteDatabase) {
+        db.execSQL("ALTER TABLE BackupItem ADD COLUMN retry INTEGER NOT NULL DEFAULT 0")
     }
 }
 
 @Database(
     entities = [BackupItem::class],
-    version = 4,
+    version = 5,
     exportSchema = true,
 )
 @TypeConverters(Converters::class)
@@ -60,6 +56,7 @@ abstract class AppDatabase : RoomDatabase() {
                     .addMigrations(MIGRATION_1_2)
                     .addMigrations(MIGRATION_2_3)
                     .addMigrations(MIGRATION_3_4)
+                    .addMigrations(MIGRATION_4_5)
                     .build()
                     .also { Instance = it }
             }

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

@@ -23,5 +23,6 @@ data class BackupItem(
     val type: String,
     var stock: Int,
     val carrierId: String,
-    val carrierName: String
+    val carrierName: String,
+    var retry: Int
 )

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

@@ -38,4 +38,7 @@ interface BackupItemDao {
 
     @Query("SELECT * FROM backupitem WHERE number != :number AND stock = 1 order by createdAt limit 1")
     suspend fun findBackupForRestore(number: String): BackupItem?
+
+    @Query("SELECT * FROM backupitem WHERE createdAt BETWEEN :start AND :end")
+    suspend fun findBackupsBetween(start: Long, end: Long): List<BackupItem>
 }

+ 20 - 0
app/src/main/java/com/example/modifier/model/CleanBackupAction.kt

@@ -0,0 +1,20 @@
+package com.example.modifier.model
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+class CleanBackupAction : BaseAction {
+    companion object {
+        const val NAME = "cleanBackup"
+    }
+
+    val data: CleanBackupActionData
+
+    constructor(id: String, data: CleanBackupActionData) : super(id, NAME) {
+        this.data = data
+    }
+}
+
+@Serializable
+data class CleanBackupActionData(val start: Long, val end: Long) {
+}

+ 45 - 12
app/src/main/java/com/example/modifier/repo/BackupRepository.kt

@@ -24,10 +24,17 @@ import com.example.modifier.service.ModifierService
 import com.example.modifier.utils.clearConv
 import com.example.modifier.utils.getContext
 import com.example.modifier.utils.shellRun
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 import java.io.File
 import java.util.Date
 import java.util.Optional
+import kotlin.coroutines.coroutineContext
+import kotlin.time.Duration.Companion.minutes
 import kotlin.time.Duration.Companion.seconds
 
 class BackupRepository(
@@ -100,7 +107,8 @@ class BackupRepository(
             type = type,
             stock = stock,
             carrierId = Optional.ofNullable(spoofedSimInfo.carrierId).orElse("1"),
-            carrierName = Optional.ofNullable(spoofedSimInfo.carrierName).orElse("T-Mobile")
+            carrierName = Optional.ofNullable(spoofedSimInfo.carrierName).orElse("T-Mobile"),
+            retry = 0
         )
 
         dao.findBackupForNumber(spoofedSimInfo.country, spoofedSimInfo.number)?.let {
@@ -116,7 +124,6 @@ class BackupRepository(
 
     @SuppressLint("SdCardPath")
     suspend fun restore(backup: BackupItem): Boolean {
-        AppStateRepo.instance.updateRuntimeFlags(reqState = ReqState.RESTORE)
         val spoofedSimInfoRepo = SpoofedSimInfoRepo.instance
         val simInfo = SpoofedSimInfo(
             numberId = backup.numberId,
@@ -209,6 +216,7 @@ class BackupRepository(
             if (backup.stock != 1) {
                 return@isSuccess true
             }
+
             if (taskRunner == null) return@isSuccess false
             if (!taskRunner.screenController.toggleRcsSwitch(true)) {
                 shellRun(
@@ -222,11 +230,26 @@ class BackupRepository(
                     return@isSuccess false
                 }
             }
+
+            val job = CoroutineScope(coroutineContext).launch {
+                taskRunner.gmsgStateRepository.rcsConfigureState.collect {
+                    if (it == RcsConfigureState.RETRY) {
+                        shellRun(
+                            PACKAGE_GMS.kill(),
+                            "sleep 5",
+                            PACKAGE_MESSAGING.kill(),
+                            CMD_MESSAGING_APP
+                        )
+                    }
+                }
+            }
+
             val state = taskRunner.gmsgStateRepository.waitForRcsState(
-                arrayOf(RcsConfigureState.CONFIGURED), 120.seconds
+                arrayOf(RcsConfigureState.CONFIGURED), 5.minutes
             )
-            if (state != RcsConfigureState.CONFIGURED) return@isSuccess false
-            true
+            job.cancel()
+            if (state == RcsConfigureState.CONFIGURED) return@isSuccess true
+            false
         }
 
         if (success) {
@@ -236,17 +259,22 @@ class BackupRepository(
                 suspend = false
             )
             if (backup.stock == 1) {
-//                backup.stock = 2
-//                dao.update(backup)
+                backup.stock = 2
+                dao.update(backup)
                 RcsNumberApi.updateStockFlag(backup.numberId, 2)
             }
             return true
         } else {
             Log.i(TAG, "restore: failed")
             if (backup.stock == 1) {
-//                backup.stock = 3
-//                dao.update(backup)
-                RcsNumberApi.updateStockFlag(backup.numberId, 3)
+                backup.retry++
+                if (backup.retry >= 3) {
+                    backup.stock = 3
+                    dao.update(backup)
+                    RcsNumberApi.updateStockFlag(backup.numberId, 3)
+                } else {
+                    dao.update(backup)
+                }
             }
         }
         return false
@@ -260,7 +288,12 @@ class BackupRepository(
         return dao.findBackupForRestore(number)
     }
 
-    suspend fun update(backupItem: BackupItem) {
-        dao.update(backupItem)
+    suspend fun cleanBackup(start: Long, end: Long) {
+        dao.findBackupsBetween(start, end).forEach {
+            runCatching {
+                File(it.path).deleteRecursively()
+            }
+            dao.delete(it)
+        }
     }
 }

+ 26 - 9
app/src/main/java/com/example/modifier/service/SocketClient.kt

@@ -7,6 +7,7 @@ import com.example.modifier.baseTag
 import com.example.modifier.enums.ReqState
 import com.example.modifier.model.CancelStoreNumberAction
 import com.example.modifier.model.CheckPifAction
+import com.example.modifier.model.CleanBackupAction
 import com.example.modifier.model.InstallApkAction
 import com.example.modifier.model.ResumeAction
 import com.example.modifier.model.RunScriptAction
@@ -251,6 +252,22 @@ class SocketClient(
                     socketCallback(id = id, status = -1, error = e.message)
                 }
             }
+
+            CleanBackupAction.NAME -> {
+                try {
+                    val cleanBackupAction = Json.decodeFromString<CleanBackupAction>(jsonStr)
+                    CoroutineScope(Dispatchers.IO).launch {
+                        taskRunner.cleanBackup(
+                            cleanBackupAction.data.start,
+                            cleanBackupAction.data.end
+                        )
+                    }
+                    socketCallback(id = id, status = 0, data = "OK")
+                } catch (e: Exception) {
+                    Log.e(TAG, "checkPif error", e)
+                    socketCallback(id = id, status = -1, error = e.message)
+                }
+            }
         }
     }
 
@@ -269,7 +286,7 @@ class SocketClient(
             return
         }
         val data = JSONObject()
-        try {
+        runCatching {
             data.put("action", "updateDevice")
             val dataObj = JSONObject()
             dataObj.put("canSend", AppStateRepo.instance.appState.value.send)
@@ -284,13 +301,13 @@ class SocketClient(
             dataObj.put("storing", AppStateRepo.instance.appState.value.storing)
             data.put("data", dataObj)
             mSocket.emit("message", data)
-        } catch (e: JSONException) {
-            e.printStackTrace()
+        }.onFailure { e ->
+            Log.e(TAG, "reportDeviceStatues error", e)
         }
     }
 
     private fun socketCallback(id: String, data: TaskExecutionResult) {
-        try {
+        runCatching {
             mSocket.emit(
                 "callback", JSONObject(
                     Json.encodeToString(
@@ -302,13 +319,13 @@ class SocketClient(
                     )
                 )
             )
-        } catch (e: Exception) {
+        }.onFailure { e ->
             Log.e(TAG, "emitEvent error", e)
         }
     }
 
     private fun socketCallback(id: String, data: RunScriptResult) {
-        try {
+        runCatching {
             mSocket.emit(
                 "callback", JSONObject(
                     Json.encodeToString(
@@ -320,7 +337,7 @@ class SocketClient(
                     )
                 )
             )
-        } catch (e: Exception) {
+        }.onFailure { e ->
             Log.e(TAG, "emitEvent error", e)
         }
     }
@@ -331,7 +348,7 @@ class SocketClient(
         data: String? = null,
         error: String? = null
     ) {
-        try {
+        runCatching {
             mSocket.emit(
                 "callback", JSONObject(
                     Json.encodeToString(
@@ -344,7 +361,7 @@ class SocketClient(
                     )
                 )
             )
-        } catch (e: Exception) {
+        }.onFailure { e ->
             Log.e(TAG, "emitEvent error", e)
         }
     }

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

@@ -508,13 +508,10 @@ class TaskRunner(
                             spoofedSimInfoRepo.spoofedSimInfo.value.number
                         )
                         if (backup != null) {
+                            AppStateRepo.instance.updateRuntimeFlags(reqState = ReqState.RESTORE)
                             if (backupRepository.restore(backup)) {
-                                backup.stock = 2
-                                backupRepository.update(backup)
                                 return@withTimeoutOrNull true
                             } else {
-                                backup.stock = 3
-                                backupRepository.update(backup)
                                 continue
                             }
                         }
@@ -713,11 +710,13 @@ class TaskRunner(
             try {
                 appStateRepo.updateRuntimeFlags(storing = true)
                 if (spoofedSimInfoRepo.spoofedSimInfo.value.available && appStateRepo.appState.value.successNum <= 5) {
+                    screenController.toggleRcsSwitch(false)
                     backupRepository.backup(
                         type = "auto",
                         sendCount = appStateRepo.appState.value.successNum,
                         stock = 1
                     )
+                    screenController.toggleRcsSwitch(true)
                 }
                 var success = true
                 repeat(num) {
@@ -780,4 +779,8 @@ class TaskRunner(
             storeNumberJob = null
         }
     }
+
+    suspend fun cleanBackup(start: Long, end: Long) {
+        backupRepository.cleanBackup(start, end)
+    }
 }

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

@@ -22,6 +22,8 @@ import com.example.modifier.baseTag
 import com.example.modifier.data.AppDatabase
 import com.example.modifier.data.BackupItem
 import com.example.modifier.databinding.FragmentBackupBinding
+import com.example.modifier.enums.ReqState
+import com.example.modifier.repo.AppStateRepo
 import com.example.modifier.repo.BackupRepository
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import kotlinx.coroutines.CoroutineScope
@@ -80,11 +82,13 @@ class BackupFragment : Fragment() {
                             dialog?.dismiss()
                             progressDialog.show()
                             CoroutineScope(Dispatchers.IO).launch {
+                                AppStateRepo.instance.updateRuntimeFlags(reqState = ReqState.RESTORE)
                                 try {
                                     backupRepository.restore(backup)
                                 } catch (e: Exception) {
                                     e.printStackTrace()
                                 }
+                                AppStateRepo.instance.updateRuntimeFlags(reqState = ReqState.NONE)
                                 withContext(Dispatchers.Main) {
                                     progressDialog.dismiss()
                                     Toast.makeText(