This commit is contained in:
manchuwork
2025-11-01 08:14:58 +08:00
parent 16b5efb3c6
commit 7506144410
8 changed files with 315 additions and 13 deletions

View File

@@ -10,8 +10,11 @@ enum class ActionEvent {
QUERY_COMPANY_REGISTER("com.loveerror.bested.QUERY_COMPANY_REGISTER", "去全网查询",isDisplay = true),
QUERY_COMPANY_SEARCH_ACTION("com.loveerror.bested.QUERY_COMPANY_SEARCH_ACTION", "查询公司是否注册",isDisplay = true, menuWithText = listOf("companyNames")),
COORDINATE_CLICK("coordinate_click", "坐标点击", true),
UNKNOWN("com.loveerror.bested.UNKNOWN", "未知",isDisplay = false, enable = false);
constructor(event: String, menuName: String, isDisplay: Boolean = true, menuWithText: List<String> = emptyList(), enable: Boolean = true){
this.event = event
this.menuName = menuName

View File

@@ -14,6 +14,9 @@ import android.widget.PopupMenu
import android.app.Activity
import android.content.ContextWrapper
import android.widget.LinearLayout
import com.loveerror.bested.tool.CoordinateClickTool
import com.loveerror.bested.tool.CoordinateListener
import com.loveerror.bested.tool.CoordinateListenerImpl
class DragFloatingButton(context: Context) : View(context) {
private val paint = Paint().apply {
@@ -66,6 +69,7 @@ class DragFloatingButton(context: Context) : View(context) {
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
startX = event.rawX
@@ -116,8 +120,19 @@ class DragFloatingButton(context: Context) : View(context) {
}
MotionEvent.ACTION_UP -> {
// 只在特定条件下移除覆盖层
if (!isDragging) {
// 点击事件 - 显示菜单
val intent = android.content.Intent(ActionEvent.COORDINATE_CLICK.event).apply {
setPackage(context.packageName)
}
val coordinateListenerImpl =
CoordinateListenerImpl(event.x, event.y, intent, context = context)
coordinateClickTool.disableCoordinateClickMode(coordinateListener = coordinateListenerImpl)
showPopupMenu()
}
}
@@ -125,6 +140,13 @@ class DragFloatingButton(context: Context) : View(context) {
return true
}
// 使用新的 CoordinateClickTool
private val coordinateClickTool = CoordinateClickTool(context, windowManager)
private fun showPopupMenu() {
val popup = PopupMenu(context, this)
// 添加菜单项 use ActionEvent
@@ -261,14 +283,21 @@ class DragFloatingButton(context: Context) : View(context) {
private fun triggerActionEvent(actionEvent: ActionEvent, textInputs: MutableList<TextWithTag> = mutableListOf()) {
try {
val intent = android.content.Intent(actionEvent.event).apply {
setPackage(context.packageName)
when (actionEvent) {
ActionEvent.COORDINATE_CLICK -> {
coordinateClickTool.enableCoordinateClickMode()
}
else -> {
val intent = android.content.Intent(actionEvent.event).apply {
setPackage(context.packageName)
}
// 如果有文本输入添加到Intent中
for(textWithTag in textInputs){
intent.putExtra(textWithTag.tag, textWithTag.text)
}
context.sendBroadcast(intent)
}
}
// 如果有文本输入添加到Intent中
for(textWithTag in textInputs){
intent.putExtra(textWithTag.tag, textWithTag.text)
}
context.sendBroadcast(intent)
} catch (e: IllegalArgumentException) {
e.printStackTrace()
println("枚举值不存在: $actionEvent, ${e.toString()}")

View File

@@ -200,7 +200,7 @@ class NormalGroupCreateForm(private val rootNodeCallback: RootNodeCallback) {
// fillEditTextByHintText(rootNew,fieldMap["员工数"] ?: "1", "员工数", "请输入人数")
clickToOpenBottomSheetAddress(
root,
rootNodeCallback.getRootNodeReceived(),
"请点击此处打点",
"浙江省杭州市余杭区高顺路8号五常西溪软件园金牛座A座"
)
@@ -218,7 +218,7 @@ class NormalGroupCreateForm(private val rootNodeCallback: RootNodeCallback) {
address: String
) {
//
val triggerNode = NodeToolText.findNodeByText(root, triggerText)
val triggerNode = NodeToolText.findNodeByText(rootNodeCallback.getRootNodeReceived(), triggerText)
// 父级的可点击
if (triggerNode != null) {
@@ -235,7 +235,7 @@ class NormalGroupCreateForm(private val rootNodeCallback: RootNodeCallback) {
Thread.sleep(8000)
// 地图选址 按钮
val mapSearchButton = NodeToolText.findNodeByText(getRootInActiveWindow(), "地图选址")
val mapSearchButton = NodeToolText.findNodeByText(rootNodeCallback.getRootNodeReceived(), "地图选址")
if (mapSearchButton != null) {
AccessibilityTool.performClick(mapSearchButton)
println("已点击地图选址按钮")
@@ -351,7 +351,7 @@ class NormalGroupCreateForm(private val rootNodeCallback: RootNodeCallback) {
}
private fun checkTextViewWithRetry(): Boolean {
repeat(5) {
repeat(15) {
val textView = NodeToolText.findNodeByText(getRootInActiveWindow(), "宽带选址")
if (textView != null && textView.viewIdResourceName == "com.ZJMCCMCRM:id/title") {
return true // 找到匹配的控件

View File

@@ -13,6 +13,7 @@ import com.loveerror.bested.SelectAccessibility
import com.loveerror.bested.button.ActionEvent
import com.loveerror.bested.button.DragFloatingButton
import com.loveerror.bested.tool.AccessibilityTool
import com.loveerror.bested.tool.GestureTool
import com.loveerror.bested.tool.NodeToolText
import com.loveerror.bested.tool.RootNodeCallback
@@ -21,6 +22,7 @@ class ZjMccmCrm(private val rootNodeCallback: RootNodeCallback) {
private lateinit var selectAccessibility: SelectAccessibility
private var broadcastReceiver: BroadcastReceiver? = null
private val gestureTool: GestureTool = GestureTool(rootNodeCallback)
fun initialize(context: Context) {
val listOfActionEvent = ActionEvent.entries.filter { it.enable }
@@ -60,6 +62,12 @@ class ZjMccmCrm(private val rootNodeCallback: RootNodeCallback) {
searchCompanyActionAsync(intent,floatingButton)
}
ActionEvent.COORDINATE_CLICK.event -> {
val x = intent.getFloatExtra("x", 0f)
val y = intent.getFloatExtra("y", 0f)
gestureTool.performGestureClick(x, y)
}
else -> {
println("Received unknown action: ${intent.action}")
}

View File

@@ -0,0 +1,69 @@
// CoordinateClickTool.kt
package com.loveerror.bested.tool
import android.content.Context
import android.view.WindowManager
interface CoordinateListener {
fun onDisable()
}
class CoordinateListenerImpl(private val x: Float,private val y: Float,private val intent: android.content.Intent,private val context: Context): CoordinateListener {
override fun onDisable() {
intent.putExtra("x",x)
intent.putExtra("y",y)
context.sendBroadcast(intent)
return
}
}
class CoordinateClickTool(private val context: Context, private val windowManager: WindowManager) {
private var isCoordinateClickMode = false
private val overlayTool = OverlayTool(context, windowManager)
fun isCoordinateClickMode(): Boolean {
return isCoordinateClickMode
}
fun enableCoordinateClickMode() {
isCoordinateClickMode = true
if (!overlayTool.isOverlayVisible()) {
overlayTool.createOverlayView()
}
}
fun disableCoordinateClickMode(coordinateListener: CoordinateListener? = null) {
val canSend = isCoordinateClickMode
isCoordinateClickMode = false
overlayTool.removeOverlayView()
if(canSend){
coordinateListener?.onDisable()
}
}
fun toggleCoordinateClickMode() {
if (isCoordinateClickMode) {
disableCoordinateClickMode()
} else {
enableCoordinateClickMode()
}
}
// fun onTouchEvent(event: MotionEvent): Boolean {
//
// when (event.action) {
//
// MotionEvent.ACTION_UP -> {
// if(!isCoordinateClickMode){
// // 非拖拽且非坐标点击模式下移除覆盖层
// overlayTool.removeOverlayView()
// isCoordinateClickMode = false
// }
// }
// }
// return true
// }
}

View File

@@ -0,0 +1,76 @@
package com.loveerror.bested.tool
import android.accessibilityservice.GestureDescription
import android.graphics.Path
import android.os.Build
import android.view.accessibility.AccessibilityNodeInfo
import com.loveerror.bested.UIInspectorService
class GestureTool (private val rootNodeCallback: RootNodeCallback){
/**
* 根据屏幕坐标执行点击操作
* @param x X坐标
* @param y Y坐标
* @param rootNodeCallback 根节点回调接口
*/
fun performClickAtCoordinates(x: Float, y: Float) {
// 可以通过手势模拟实现坐标点击
performGestureClick(x, y)
}
/**
* 通过系统手势API执行坐标点击
* @param x X坐标
* @param y Y坐标
* @param rootNodeCallback 根节点回调接口
*/
fun performGestureClick(x: Float, y: Float) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// 使用手势API实现点击
// 这里需要获取AccessibilityService实例来执行手势
// 实现细节取决于具体如何获取AccessibilityService
// 需要通过AccessibilityService执行手势点击
rootNodeCallback.performGestureClick(x, y)
}
}
/**
* 在指定节点区域内执行相对坐标点击
* @param node 目标节点
* @param relativeX 相对于节点的X偏移
* @param relativeY 相对于节点的Y偏移
*/
fun performClickInNode(node: AccessibilityNodeInfo, relativeX: Int, relativeY: Int) {
val bounds = android.graphics.Rect()
node.getBoundsInScreen(bounds)
val clickX = bounds.left + relativeX
val clickY = bounds.top + relativeY
// 需要通过AccessibilityService执行手势点击
// 这里只是一个概念性实现实际需要获取AccessibilityService实例
// 需要通过AccessibilityService执行手势点击
performGestureClick(clickX.toFloat(), clickY.toFloat())
}
/**
* 点击节点下方指定偏移位置
* @param node 目标节点
* @param offsetDp 向下偏移的dp值
*/
fun clickBelowNode(node: AccessibilityNodeInfo, offsetDp: Float = 25f) {
val bounds = android.graphics.Rect()
node.getBoundsInScreen(bounds)
// 将dp转换为像素
val density = android.content.res.Resources.getSystem().displayMetrics.density
val pixelOffset = (offsetDp * density).toInt()
// 计算点击坐标(节点底部中心位置向下偏移)
val clickX = bounds.centerX()
val clickY = bounds.bottom + pixelOffset
// 需要通过AccessibilityService执行手势点击
performGestureClick(clickX.toFloat(), clickY.toFloat())
}
}

View File

@@ -0,0 +1,90 @@
package com.loveerror.bested.tool
import android.app.AlertDialog
import android.content.Context
import android.graphics.Color
import android.graphics.PixelFormat
import android.view.View
import android.view.WindowManager
// 创建新的 OverlayTool 类
class OverlayTool(private val context: Context, private val windowManager: WindowManager) {
private var overlayView: View? = null
// 创建半透明覆盖层的方法
fun createOverlayView(): View {
return View(context).apply {
// 设置半透明背景
setBackgroundColor(Color.argb(128, 0, 0, 0)) // 50%透明度的黑色
// 设置布局参数
val overlayParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT
)
// 添加到窗口管理器
windowManager.addView(this, overlayParams)
this.setOnTouchListener { v, event ->
removeOverlayView()
// 显示坐标弹窗
showCoordinateDialog(event.rawX, event.rawY)
return@setOnTouchListener false }
overlayView = this
}
}
private fun showCoordinateDialog(x: Float, y: Float) {
val message = "点击坐标:\nX: $x\nY: $y"
val dialog = AlertDialog.Builder(context)
.setTitle("坐标信息")
.setMessage(message)
.setPositiveButton("确定", null)
.create();
// .show()
// 设置对话框类型为系统级对话框
val window = dialog.window
window?.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY)
// 在对话框关闭后移除覆盖层
dialog.setOnCancelListener {
// isCoordinateClickMode = false
// removeOverlayView()
}
dialog.setOnDismissListener {
// isCoordinateClickMode = false
// removeOverlayView()
}
dialog.show()
}
// 移除覆盖层的方法
fun removeOverlayView() {
println("Attempting to remove overlay view, current overlayView: $overlayView")
overlayView?.let { view ->
try {
windowManager.removeView(view)
println("Overlay view removed successfully")
} catch (e: Exception) {
println("Error removing overlay view: ${e.message}")
}
overlayView = null
} ?: println("No overlay view to remove")
}
// 检查覆盖层是否存在
fun isOverlayVisible(): Boolean {
return overlayView != null
}
}

View File

@@ -1,10 +1,15 @@
package com.loveerror.bested.tool
import android.accessibilityservice.GestureDescription
import android.graphics.Path
import android.os.Build
import android.view.accessibility.AccessibilityNodeInfo
import com.loveerror.bested.UIInspectorService
fun interface RootNodeCallback {
interface RootNodeCallback {
fun getRootNodeReceived(): AccessibilityNodeInfo
fun performGestureClick(x: Float, y: Float)
}
class RootNodeCallbackImpl(private var uiInspectorService: UIInspectorService) : RootNodeCallback {
@@ -12,4 +17,26 @@ class RootNodeCallbackImpl(private var uiInspectorService: UIInspectorService) :
override fun getRootNodeReceived(): AccessibilityNodeInfo {
return uiInspectorService.rootInActiveWindow
}
/**
* 通过系统手势API执行坐标点击
* @param x X坐标
* @param y Y坐标
*/
override fun performGestureClick(x: Float, y: Float) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
try {
val path = Path()
path.moveTo(x, y)
val gestureBuilder = GestureDescription.Builder()
gestureBuilder.addStroke(GestureDescription.StrokeDescription(path, 0, 50))
val gesture = gestureBuilder.build()
uiInspectorService.dispatchGesture(gesture, null, null)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}