package com.example.modifier.service import android.accessibilityservice.AccessibilityService import android.accessibilityservice.GestureDescription import android.content.Intent import android.graphics.Path import android.graphics.Rect import android.provider.Settings import android.util.Log import android.view.accessibility.AccessibilityNodeInfo import com.draco.ladb.utils.ADB import com.example.modifier.TraverseResult import com.example.modifier.constants.CMD_BACK import com.example.modifier.constants.CMD_RCS_SETTINGS_ACTIVITY import com.example.modifier.utils.currentActivity import com.example.modifier.utils.findAllScrollable import com.example.modifier.utils.findFirstByDesc import com.example.modifier.utils.findFirstByIdAndText import com.example.modifier.utils.findFirstScrollable import com.example.modifier.utils.shellRun import kotlinx.coroutines.delay import kotlinx.coroutines.withTimeoutOrNull class ScreenController(val context: AccessibilityService, private val inspector: ScreenInspector) { companion object { private const val TAG = "ScreenController" } suspend fun toggleRcsSwitch(state: Boolean, retry: Int = 3): Boolean { val res = TraverseResult() shellRun(CMD_RCS_SETTINGS_ACTIVITY, "sleep 2") val success = run repeatBlock@{ repeat(retry) { res.rcsSwitch = null inspector.traverseNode(res) if (res.rcsSwitch == null) { shellRun(CMD_BACK, "sleep 0.5", CMD_RCS_SETTINGS_ACTIVITY, "sleep 1") } else { if (res.rcsSwitch!!.isChecked == state) { return@repeatBlock true } val rect = Rect() res.rcsSwitch!!.getBoundsInScreen(rect) if (state) { shellRun( "input tap ${rect.centerX()} ${rect.centerY()}", "sleep 1", ) val btn = context.rootInActiveWindow.findAccessibilityNodeInfosByViewId("android:id/button1") .firstOrNull() if (btn != null) { btn.performAction(AccessibilityNodeInfo.ACTION_CLICK) delay(1000) } while (currentActivity()?.contains("RcsSettingsActivity") == true || currentActivity()?.contains("TurnOffRcsActivity") == true ) { shellRun(CMD_BACK) delay(500) } shellRun(CMD_RCS_SETTINGS_ACTIVITY, "sleep 1") } else { shellRun( "input tap ${rect.centerX()} ${rect.centerY()}", "sleep 1", ) if (currentActivity()?.contains("TurnOffRcsActivity") == true) { inspector.traverseNode(res) if (res.closeBtn != null) { val rect1 = Rect() res.closeBtn!!.getBoundsInScreen(rect1) shellRun( "input tap ${rect1.centerX()} ${rect1.centerY()}", "sleep 1", ) } } else { context.rootInActiveWindow.findAccessibilityNodeInfosByViewId("android:id/button1") .firstOrNull()?.performAction(AccessibilityNodeInfo.ACTION_CLICK) } delay(1000) while (currentActivity()?.contains("RcsSettingsActivity") == true || currentActivity()?.contains("TurnOffRcsActivity") == true ) { shellRun(CMD_BACK) delay(500) } shellRun(CMD_RCS_SETTINGS_ACTIVITY, "sleep 1") } res.rcsSwitch = null inspector.traverseNode(res) if (res.rcsSwitch?.isChecked == state) { return@repeatBlock true } } } false } while (currentActivity()?.contains("RcsSettingsActivity") == true) { shellRun(CMD_BACK) delay(500) } return success } suspend fun adbPair(): Pair? { val i = Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS) i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) context.startActivity(i) delay(500) withTimeoutOrNull(10000) { while (true) { val node = context.rootInActiveWindow.findFirstByIdAndText( "android:id/title", "Wireless debugging" ) if (node != null) { Log.i(TAG, "found wireless debugging") delay(500) tapNode(node) delay(1000) return@withTimeoutOrNull context.rootInActiveWindow.findFirstByIdAndText( "android:id/title", "Pair device with pairing code" )?.let { Log.i(TAG, "found pair device with pairing code") it.parent.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK) delay(1000) val code = context.rootInActiveWindow.findAccessibilityNodeInfosByViewId( "com.android.settings:id/pairing_code" ).firstOrNull()?.text?.toString() val ip = context.rootInActiveWindow.findAccessibilityNodeInfosByViewId( "com.android.settings:id/ip_addr" ).firstOrNull()?.text?.toString() Log.i(TAG, "pairing code: $code, ip: $ip") if (code != null && ip != null) { return@let Pair(code, ip) } else { } } return@withTimeoutOrNull } context.rootInActiveWindow.findAllScrollable().forEach { Log.i(TAG, "scrolling ${it.viewIdResourceName}") it.performAction(AccessibilityNodeInfo.ACTION_FOCUS) it.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) } delay(500) } null } } fun tapNode(node: AccessibilityNodeInfo) { val rect = Rect() node.getBoundsInScreen(rect) val path = Path() path.moveTo(rect.centerX().toFloat(), rect.centerY().toFloat()) context.dispatchGesture( GestureDescription.Builder().addStroke( GestureDescription.StrokeDescription(path, 0, 100) ).build(), null, null ) } }