|
|
@@ -2,10 +2,13 @@ package com.example.modifier.service
|
|
|
|
|
|
import android.accessibilityservice.AccessibilityService
|
|
|
import android.accessibilityservice.GestureDescription
|
|
|
+import android.content.ClipData
|
|
|
+import android.content.Context
|
|
|
import android.content.Intent
|
|
|
import android.graphics.Path
|
|
|
import android.graphics.Rect
|
|
|
import android.net.Uri
|
|
|
+import android.os.Bundle
|
|
|
import android.provider.Settings
|
|
|
import android.util.Log
|
|
|
import android.view.accessibility.AccessibilityNodeInfo
|
|
|
@@ -28,6 +31,7 @@ import com.example.modifier.utils.shellRun
|
|
|
import com.example.modifier.utils.smsIntent
|
|
|
import kotlinx.coroutines.delay
|
|
|
import kotlinx.coroutines.withTimeoutOrNull
|
|
|
+import java.io.File
|
|
|
|
|
|
class ScreenController(val context: AccessibilityService, private val inspector: ScreenInspector) {
|
|
|
|
|
|
@@ -222,7 +226,7 @@ class ScreenController(val context: AccessibilityService, private val inspector:
|
|
|
node.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
|
|
}
|
|
|
|
|
|
- suspend fun createGroup(numbers: Array<String>):Boolean {
|
|
|
+ suspend fun createGroup(numbers: Array<String>, timeout: Long): Boolean {
|
|
|
val intent = Intent(Intent.ACTION_SENDTO)
|
|
|
intent.data = Uri.parse("sms:")
|
|
|
intent.putExtra("exit_on_sent", true)
|
|
|
@@ -230,7 +234,7 @@ class ScreenController(val context: AccessibilityService, private val inspector:
|
|
|
intent.setPackage(PACKAGE_MESSAGING)
|
|
|
context.startActivity(intent)
|
|
|
delay(1000)
|
|
|
- val createGroup = context.rootInActiveWindow.findByText("Create group")
|
|
|
+ val createGroup = context.rootInActiveWindow.findByText(Regex("^Create group$"))
|
|
|
if (createGroup == null) {
|
|
|
Log.e(TAG, "not found Create group")
|
|
|
return false
|
|
|
@@ -240,40 +244,136 @@ class ScreenController(val context: AccessibilityService, private val inspector:
|
|
|
shellRun(*(numbers.flatMap {
|
|
|
listOf("input text $it", "input keyevent 66")
|
|
|
}).toTypedArray())
|
|
|
- val next = context.rootInActiveWindow.findByText("Next")
|
|
|
+ val next = context.rootInActiveWindow.findByText(Regex("^Next$"))
|
|
|
if (next == null) {
|
|
|
Log.e(TAG, "not found Next")
|
|
|
return false
|
|
|
}
|
|
|
next.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
|
|
delay(500)
|
|
|
- val done = context.rootInActiveWindow.findByText("Done")
|
|
|
+ val done = context.rootInActiveWindow.findByText(Regex("^Done$"))
|
|
|
if (done == null) {
|
|
|
Log.e(TAG, "not found Done")
|
|
|
return false
|
|
|
}
|
|
|
done.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
|
|
|
|
|
+ val traverseResult = TraverseResult()
|
|
|
+ withTimeoutOrNull(timeout) {
|
|
|
+ repeat(999) {
|
|
|
+ inspector.traverseNode(traverseResult)
|
|
|
+ if (traverseResult.isRcsCapable && traverseResult.sendBtn != null) {
|
|
|
+ return@withTimeoutOrNull
|
|
|
+ }
|
|
|
+ delay(200)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!traverseResult.isRcsCapable) {
|
|
|
+ Log.e(TAG, "RCS not capable")
|
|
|
+ }
|
|
|
+ return traverseResult.isRcsCapable
|
|
|
+ }
|
|
|
+
|
|
|
+ suspend fun groupSend(message: String?, img: File?): Boolean {
|
|
|
+ var res = TraverseResult()
|
|
|
+ run r1@{
|
|
|
+ repeat(10) {
|
|
|
+ if (it > 0) delay(200)
|
|
|
+ inspector.traverseNode(res)
|
|
|
+ if (res.inputField != null) {
|
|
|
+ return@r1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (res.inputField == null) {
|
|
|
+ Log.e(TAG, "not found input field")
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ res.inputField!!.performAction(AccessibilityNodeInfo.ACTION_FOCUS)
|
|
|
+ if (message != null) {
|
|
|
+ res.inputField!!.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, Bundle().apply {
|
|
|
+ putCharSequence(
|
|
|
+ AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
|
|
|
+ message
|
|
|
+ )
|
|
|
+ })
|
|
|
+ }
|
|
|
+ if (img != null) {
|
|
|
+ val uri = getUriForFile(
|
|
|
+ context,
|
|
|
+ "${BuildConfig.APPLICATION_ID}.fileprovider",
|
|
|
+ img
|
|
|
+ )
|
|
|
+ context.grantUriPermission(
|
|
|
+ PACKAGE_MESSAGING,
|
|
|
+ uri,
|
|
|
+ Intent.FLAG_GRANT_READ_URI_PERMISSION
|
|
|
+ )
|
|
|
+ val clipboardManager =
|
|
|
+ context.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
|
|
|
+ val clipData = ClipData.newUri(context.contentResolver, "Image", uri)
|
|
|
+ clipboardManager.setPrimaryClip(clipData)
|
|
|
+ delay(500)
|
|
|
+ res.inputField!!.performAction(AccessibilityNodeInfo.ACTION_PASTE)
|
|
|
+ }
|
|
|
+ delay(500)
|
|
|
+ run r2@{
|
|
|
+ repeat(10) {
|
|
|
+ if (it > 0) delay(200)
|
|
|
+ inspector.traverseNode(res)
|
|
|
+ if (res.sendBtn != null) {
|
|
|
+ return@r2
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (res.sendBtn == null) {
|
|
|
+ Log.e(TAG, "not found send button")
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ res.sendBtn!!.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
|
|
+ Log.i(TAG, "groupSend: click send button")
|
|
|
return true
|
|
|
}
|
|
|
|
|
|
suspend fun leaveGroup(): Boolean {
|
|
|
- val more = context.rootInActiveWindow.findFirstByDesc("More")
|
|
|
+ val actionBar = run r1@{
|
|
|
+ repeat(10) {
|
|
|
+ if (it > 0) delay(200)
|
|
|
+ context.rootInActiveWindow.findAccessibilityNodeInfosByViewId(
|
|
|
+ "com.google.android.apps.messaging:id/action_bar_root"
|
|
|
+ ).firstOrNull().let {
|
|
|
+ if (it != null) {
|
|
|
+ return@r1 it
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return@r1 null
|
|
|
+ }
|
|
|
+ if (actionBar == null) {
|
|
|
+ Log.e(TAG, "leaveGroup: not found action_bar_root")
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ val more = actionBar.findFirstByDesc(Regex("^More$", RegexOption.IGNORE_CASE))
|
|
|
if (more == null) {
|
|
|
Log.e(TAG, "leaveGroup: not found More")
|
|
|
return false
|
|
|
}
|
|
|
more.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
|
|
delay(500)
|
|
|
- val groupDetails = context.rootInActiveWindow.findByText("Group details")
|
|
|
+ val groupDetails =
|
|
|
+ context.rootInActiveWindow.findByText(Regex("^Group details$", RegexOption.IGNORE_CASE))
|
|
|
if (groupDetails == null) {
|
|
|
- inspector.traverseNode(TraverseResult(), true)
|
|
|
Log.e(TAG, "leaveGroup: not found Group details")
|
|
|
return false
|
|
|
}
|
|
|
- groupDetails.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
|
|
- groupDetails.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
|
|
+ groupDetails!!.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
|
|
delay(500)
|
|
|
+
|
|
|
+ context.rootInActiveWindow.findAllScrollable().forEach {
|
|
|
+ it.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)
|
|
|
+ }
|
|
|
+
|
|
|
val leave = context.rootInActiveWindow.findById("Leave group")
|
|
|
if (leave == null) {
|
|
|
Log.e(TAG, "leaveGroup: not found Leave group")
|
|
|
@@ -281,7 +381,7 @@ class ScreenController(val context: AccessibilityService, private val inspector:
|
|
|
}
|
|
|
leave.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
|
|
delay(500)
|
|
|
- val ok = context.rootInActiveWindow.findByText("OK")
|
|
|
+ val ok = context.rootInActiveWindow.findByText(Regex("^Cancel$"))
|
|
|
if (ok == null) {
|
|
|
Log.e(TAG, "not found OK")
|
|
|
return false
|