x1ongzhu 1 жил өмнө
parent
commit
d07089a6d6

+ 15 - 1
app/src/main/java/com/example/modifier/Global.kt

@@ -1,6 +1,7 @@
 package com.example.modifier
 
 import android.content.Context
+import android.content.Intent
 import android.os.Build
 import android.util.Log
 import androidx.core.content.ContextCompat
@@ -356,7 +357,7 @@ object Global {
     }
 
     @JvmStatic
-    suspend fun sendSms(sender: String, msg: String) {
+    suspend fun sendSmsFrida(sender: String, msg: String) {
         val context = Utils.getContext()
         try {
             val dataDir = ContextCompat.getDataDir(context)
@@ -423,4 +424,17 @@ object Global {
             e.printStackTrace()
         }
     }
+
+    @JvmStatic
+    fun sendSmsIntent(sender: String, msg: String) {
+        val intent = Intent()
+        intent.setAction("com.example.modifier.sms")
+        intent.putExtra("sender", sender)
+        intent.putExtra(
+            "message",
+            msg
+        )
+        val context = Utils.getContext()
+        context.sendBroadcast(intent)
+    }
 }

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

@@ -45,9 +45,9 @@ class MainActivity : AppCompatActivity() {
                     }
                 }
                 Utils.runAsRoot(
-                    "settings put global window_animation_scale 0.0",
-                    "settings put global transition_animation_scale 0.0",
-                    "settings put global animator_duration_scale 0.0"
+                    "settings put global window_animation_scale 1.0",
+                    "settings put global transition_animation_scale 1.0",
+                    "settings put global animator_duration_scale 1.0"
                 )
                 if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
                     Global.killPhoneProcess()

+ 18 - 0
app/src/main/java/com/example/modifier/ShellCommands.kt

@@ -0,0 +1,18 @@
+package com.example.modifier
+
+const val CMD_MESSAGING_APP = "am start com.google.android.apps.messaging"
+
+const val CMD_RCS_SETTINGS_ACTIVITY =
+    "am start com.google.android.apps.messaging/com.google.android.apps.messaging.ui.appsettings.RcsSettingsActivity"
+
+const val CMD_MSG_SETTINGS_ACTIVITY =
+    "am start com.google.android.apps.messaging/com.google.android.apps.messaging.ui.appsettings.SettingsActivity"
+
+const val CMD_CONVERSATION_LIST_ACTIVITY =
+    "am start com.google.android.apps.messaging/.ui.ConversationListActivity"
+
+const val CMD_BACK = "input keyevent KEYCODE_BACK"
+
+const val CMD_HOME = "input keyevent KEYCODE_HOME"
+
+const val CMD_BACK_APP = "am start ${BuildConfig.APPLICATION_ID}/.MainActivity"

+ 10 - 0
app/src/main/java/com/example/modifier/TraverseResult.java

@@ -11,6 +11,8 @@ public class TraverseResult {
 
     private AccessibilityNodeInfo rcsSwitch;
 
+    private boolean rcsConnected;
+
 
     public boolean isRcsCapable() {
         return rcsCapable;
@@ -35,4 +37,12 @@ public class TraverseResult {
     public void setRcsSwitch(AccessibilityNodeInfo rcsSwitch) {
         this.rcsSwitch = rcsSwitch;
     }
+
+    public boolean isRcsConnected() {
+        return rcsConnected;
+    }
+
+    public void setRcsConnected(boolean rcsConnected) {
+        this.rcsConnected = rcsConnected;
+    }
 }

+ 1 - 1
app/src/main/java/com/example/modifier/http/api/SysConfigApi.kt

@@ -6,6 +6,6 @@ import io.ktor.resources.Resource
 class SysConfigApi {
 
     @Resource("{id}")
-    class Id(val parent: SysConfigApi = SysConfigApi(), val id: Long) {
+    class Id(val parent: SysConfigApi = SysConfigApi(), val id: String) {
     }
 }

+ 3 - 0
app/src/main/java/com/example/modifier/http/response/SysConfigResponse.kt

@@ -1,4 +1,7 @@
 package com.example.modifier.http.response
 
+import kotlinx.serialization.Serializable
+
+@Serializable
 data class SysConfigResponse(val id: Int, val name: String, val value: String) {
 }

+ 95 - 25
app/src/main/java/com/example/modifier/service/ModifierService.kt

@@ -31,7 +31,12 @@ import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.Observer
 import androidx.lifecycle.liveData
 import com.example.modifier.BuildConfig
-import com.example.modifier.Frida
+import com.example.modifier.CMD_BACK
+import com.example.modifier.CMD_CONVERSATION_LIST_ACTIVITY
+import com.example.modifier.CMD_HOME
+import com.example.modifier.CMD_MESSAGING_APP
+import com.example.modifier.CMD_MSG_SETTINGS_ACTIVITY
+import com.example.modifier.CMD_RCS_SETTINGS_ACTIVITY
 import com.example.modifier.Global
 import com.example.modifier.Global.load
 import com.example.modifier.Global.resetAll
@@ -42,8 +47,10 @@ import com.example.modifier.databinding.FloatingWindowBinding
 import com.example.modifier.enums.RcsConfigureState
 import com.example.modifier.http.KtorClient
 import com.example.modifier.http.api.RcsNumberApi
+import com.example.modifier.http.api.SysConfigApi
 import com.example.modifier.http.request.RcsNumberRequest
 import com.example.modifier.http.response.RcsNumberResponse
+import com.example.modifier.http.response.SysConfigResponse
 import com.example.modifier.model.TelephonyConfig
 import com.google.android.material.color.DynamicColors
 import io.ktor.client.call.body
@@ -57,9 +64,6 @@ import io.socket.client.Socket
 import io.socket.emitter.Emitter
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.async
-import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.launch
@@ -347,14 +351,18 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         }
     }
 
-    private fun send(to: String, body: String, rcsWait: Long): Boolean {
-        Log.i(TAG, "Sending SMS to $to: $body")
+    private fun smsIntent(to: String, body: String): Intent {
         val intent = Intent(Intent.ACTION_SENDTO)
-        intent.setData(Uri.parse("sms:$to"))
+        intent.data = Uri.parse("sms:$to")
         intent.putExtra("sms_body", body)
         intent.putExtra("exit_on_sent", true)
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-        startActivity(intent)
+        return intent
+    }
+
+    private fun send(to: String, body: String, rcsWait: Long): 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...")
             val f = mExecutor.schedule<Boolean>(
@@ -363,7 +371,6 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     val ts = System.currentTimeMillis()
                     while (System.currentTimeMillis() - ts < rcsWait) {
                         val root = rootInActiveWindow
-                        val packageName = root.packageName.toString()
                         val traverseResult = TraverseResult()
                         traverseNode(root, traverseResult)
                         if (traverseResult.isRcsCapable) {
@@ -458,6 +465,12 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             findSwitch(node.parent.parent)
         }
 
+        if ("com.google.android.apps.messaging:id/rcs_sim_status_status_text" == id) {
+            if (text.lowercase().contains("connected") || text.lowercase().contains("已连接")) {
+                result.isRcsConnected = true
+            }
+        }
+
         if (node.childCount != 0) {
             for (i in 0 until node.childCount) {
                 traverseNode(node.getChild(i), result)
@@ -573,7 +586,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         }
         binding.btnInspect.setOnClickListener {
             CoroutineScope(Dispatchers.IO).launch {
-                traverseNode(rootInActiveWindow, TraverseResult())
+                checkRcsAvailability()
             }
         }
 
@@ -630,9 +643,6 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
     }
 
     private suspend fun requestNumber() {
-        CoroutineScope(Dispatchers.IO).async {
-            Frida.start()
-        }
         requestNumberCount++
         requesting.postValue(true)
         val result = withTimeoutOrNull(1.hours) {
@@ -786,17 +796,12 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                 if (match != null) {
                     val otp = match.groupValues[1]
                     Log.i(TAG, "OTP: $otp")
-                    Global.sendSms(
-                        "3538",
-                        "Your Messenger verification code is G-$otp"
-                    )
+                    val sender = "3538"
+                    val msg = "Your Messenger verification code is G-$otp"
 
                     val configured = run a@{
                         repeat(2) {
-                            Global.sendSms(
-                                "3538",
-                                "Your Messenger verification code is G-$otp"
-                            )
+                            Global.sendSmsIntent(sender, msg)
                             val state =
                                 waitForRcsState(
                                     arrayOf(
@@ -835,10 +840,75 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         } else {
             Log.e(TAG, "requestNumber failed")
         }
-        try {
-            Frida.stop()
-        } catch (e: Exception) {
-            Log.wtf(TAG, "stop frida failed", e)
+    }
+
+    suspend fun checkRcsAvailability(): Boolean {
+        var rcsConnected = false
+        repeat(3) {
+            Utils.runAsRoot(
+                CMD_CONVERSATION_LIST_ACTIVITY,
+                "sleep 2",
+                CMD_RCS_SETTINGS_ACTIVITY,
+                "sleep 1",
+            )
+            val res = TraverseResult()
+            traverseNode(rootInActiveWindow, res)
+            if (res.isRcsConnected) {
+                rcsConnected = true
+                return@repeat
+            }
+            Utils.runAsRoot(CMD_HOME, "sleep 1")
         }
+        if (!rcsConnected) {
+            return false
+        }
+
+        var config: SysConfigResponse
+        val checkRcsAvailabilityNumbers = mutableListOf<String>()
+        withTimeoutOrNull(60.seconds) {
+            while (true) {
+                try {
+                    config = KtorClient.get(
+                        SysConfigApi.Id(
+                            SysConfigApi(),
+                            "check_availability_numbers"
+                        )
+                    )
+                        .body<SysConfigResponse>()
+                    Log.i(TAG, "sysConfig response: $config")
+                    checkRcsAvailabilityNumbers.addAll(config.value.split(",").map { it.trim() })
+                    break
+                } catch (exception: Exception) {
+                    Log.e(TAG, "sysConfig Error: ${exception.message}", exception)
+                }
+                delay(1.seconds)
+            }
+        }
+
+        checkRcsAvailabilityNumbers.forEach {
+            startActivity(smsIntent(it, ""))
+            val s = withTimeoutOrNull(3.seconds) {
+                while (true) {
+                    val root = rootInActiveWindow
+                    val traverseResult = TraverseResult()
+                    traverseNode(root, traverseResult)
+                    if (traverseResult.isRcsCapable) {
+                        return@withTimeoutOrNull true
+                    } else {
+                        Log.i(TAG, "RCS not detected")
+                    }
+                    try {
+                        delay(200)
+                    } catch (e: InterruptedException) {
+                        e.printStackTrace()
+                    }
+                }
+            }
+            if (s == true) {
+                Log.i(TAG, "checkRcsAvailability: $it success")
+                return true
+            }
+        }
+        return false
     }
 }

+ 30 - 3
app/src/main/java/com/example/modifier/ui/utils/UtilsFragment.kt

@@ -1,6 +1,5 @@
 package com.example.modifier.ui.utils
 
-import android.content.Intent
 import android.os.Bundle
 import android.os.Handler
 import android.os.Looper
@@ -9,6 +8,7 @@ import android.view.View
 import android.view.ViewGroup
 import android.widget.Toast
 import androidx.fragment.app.Fragment
+import com.example.modifier.CMD_BACK_APP
 import com.example.modifier.Frida
 import com.example.modifier.Global
 import com.example.modifier.Global.clear
@@ -19,6 +19,8 @@ import com.example.modifier.Global.unsuspend
 import com.example.modifier.R
 import com.example.modifier.Utils
 import com.example.modifier.databinding.FragmentUtilsBinding
+import com.example.modifier.service.ModifierService
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.delay
@@ -58,7 +60,7 @@ class UtilsFragment : Fragment() {
 
                 val otp = binding.etOtp.text.toString()
                 withContext(Dispatchers.IO) {
-                    Global.sendSms("3538", "Your Messenger verification code is G-$otp")
+                    Global.sendSmsIntent("3538", "Your Messenger verification code is G-$otp")
                 }
 
                 binding.btnSend.setIconResource(R.drawable.ic_done)
@@ -83,12 +85,37 @@ class UtilsFragment : Fragment() {
                         handler.postDelayed({
                             binding.btnClearConv.isEnabled = true
                             binding.btnClearConv.icon = null
-                            binding.btnClearConv.text = "Clear Conversations"
+                            binding.btnClearConv.text = "Clear Msg"
                         }, 1500)
                     }
                 }
             }
         }
+
+        binding.btnCheckA10y.setOnClickListener {
+            Utils.makeLoadingButton(context, binding.btnCheckA10y)
+            CoroutineScope(Dispatchers.Main).launch {
+                var a10y: Boolean? = null
+                withContext(Dispatchers.IO) {
+                    a10y = ModifierService.instance?.checkRcsAvailability()
+                    Utils.runAsRoot(CMD_BACK_APP)
+                }
+                if (isAdded) {
+                    MaterialAlertDialogBuilder(requireContext())
+                        .setMessage("RCS is ${if (a10y == true) "available" else "not available"}")
+                        .setPositiveButton("OK") { _, _ -> }
+                        .show()
+                }
+
+                binding.btnCheckA10y.setIconResource(R.drawable.ic_done)
+                binding.btnCheckA10y.text = "OK"
+                delay(1500L)
+                binding.btnCheckA10y.isEnabled = true
+                binding.btnCheckA10y.icon = null
+                binding.btnCheckA10y.text = "Check A10y"
+            }
+        }
+
         binding.btnSuspend.setOnClickListener {
             binding.btnSuspend.isEnabled = false
             Utils.makeLoadingButton(context, binding.btnSuspend)

+ 18 - 4
app/src/main/res/layout/fragment_utils.xml

@@ -185,11 +185,25 @@
                             android:textSize="14sp"
                             android:textStyle="bold" />
 
-                        <com.google.android.material.button.MaterialButton
-                            android:id="@+id/btn_clear_conv"
+                        <LinearLayout
                             android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
-                            android:text="Clear Conversations" />
+                            android:orientation="horizontal">
+
+                            <com.google.android.material.button.MaterialButton
+                                android:id="@+id/btn_clear_conv"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:text="Clear Msg" />
+
+                            <com.google.android.material.button.MaterialButton
+                                android:id="@+id/btn_check_a10y"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:layout_marginLeft="8dp"
+                                android:text="Check A10y" />
+                        </LinearLayout>
+
 
                         <LinearLayout
 
@@ -208,7 +222,7 @@
                                 android:id="@+id/btn_stop_frida"
                                 android:layout_width="wrap_content"
                                 android:layout_height="wrap_content"
-                                android:layout_marginLeft="16dp"
+                                android:layout_marginLeft="8dp"
                                 android:text="Stop Frida" />
                         </LinearLayout>
                     </LinearLayout>