|
|
@@ -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)
|
|
|
+// }
|
|
|
}
|
|
|
|
|
|
}
|