优化现有实现

This commit is contained in:
lihongwei 2025-05-06 14:15:16 +08:00
parent 9643d74864
commit c7d8087b3e
14 changed files with 547 additions and 830 deletions

View File

@ -12,10 +12,14 @@ import android.view.accessibility.AccessibilityEvent;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.auto.autoclicker.data.Event;
import com.auto.autoclicker.data.EventWrapper;
import com.auto.autoclicker.data.PointEvent;
import com.auto.autoclicker.data.SlideEvent;
import com.auto.autoclicker.util.ScreenUtils; import com.auto.autoclicker.util.ScreenUtils;
import com.auto.autoclicker.util.ViewUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; import java.util.List;
public class AutoClickService extends AccessibilityService { public class AutoClickService extends AccessibilityService {
@ -29,15 +33,14 @@ public class AutoClickService extends AccessibilityService {
private static final int MIN_SLIDE_DURATION = 100; private static final int MIN_SLIDE_DURATION = 100;
private static final int MAX_SLIDE_DURATION = 2000; private static final int MAX_SLIDE_DURATION = 2000;
private final List<Event> eventQueue = new ArrayList<>(); private List<EventWrapper> runtimeEvents = new ArrayList<>();
private final Handler handler = new Handler(Looper.getMainLooper()); private final Handler handler = new Handler(Looper.getMainLooper());
private boolean isRunning = false; private boolean isRunning = false;
private long clickInterval = 1000; private long clickInterval = 1000;
private int clickDuration = 200; private int clickDuration = 200;
private int slideDuration = 500; private int slideDuration = 500;
private int screenWidth;
private int screenHeight;
private int currentEventIndex = 0; private int currentEventIndex = 0;
@Override @Override
@ -45,8 +48,8 @@ public class AutoClickService extends AccessibilityService {
super.onCreate(); super.onCreate();
instance = this; instance = this;
Point screenSize = ScreenUtils.getScreenSize(this); Point screenSize = ScreenUtils.getScreenSize(this);
screenWidth = screenSize.x; int screenWidth = screenSize.x;
screenHeight = screenSize.y; int screenHeight = screenSize.y;
logDebug("屏幕尺寸: " + screenWidth + "x" + screenHeight); logDebug("屏幕尺寸: " + screenWidth + "x" + screenHeight);
} }
@ -66,50 +69,16 @@ public class AutoClickService extends AccessibilityService {
logDebug("无障碍服务中断"); logDebug("无障碍服务中断");
} }
public void setClickPosition(float x, float y) {
Point constrained = ViewUtils.constrainToScreen(x, y, screenWidth, screenHeight);
clearEvents();
eventQueue.add(new ClickEvent(constrained.x, constrained.y, -1));
logDebug("设置单点点击位置: 原始 (" + x + ", " + y + ") -> 修正 (" + constrained.x + ", " + constrained.y + ")");
}
public void clearEvents() { public void clearEvents() {
eventQueue.clear(); runtimeEvents.clear();
logDebug("清除所有事件"); logDebug("清除所有事件");
} }
public void addClickEvent(int x, int y, int index) { public void addMultipleClickEvents(List<EventWrapper> eventWrapperList) {
Point constrained = ViewUtils.constrainToScreen(x, y, screenWidth, screenHeight);
eventQueue.add(new ClickEvent(constrained.x, constrained.y, index));
logDebug("添加点击事件 #" + index + ": (" + constrained.x + ", " + constrained.y + ")");
}
public void addMultipleClickEvents(List<Point> positions) {
clearEvents(); clearEvents();
for (int i = 0; i < positions.size(); i++) {
Point p = positions.get(i);
addClickEvent(p.x, p.y, i);
}
logDebug("设置多点点击事件: " + positions.size() + " 个点");
}
public void addSlideEvent(int startX, int startY, int endX, int endY) { runtimeEvents = new ArrayList<>(eventWrapperList);
Point constrainedStart = ViewUtils.constrainToScreen(startX, startY, screenWidth, screenHeight); logDebug("设置多点点击事件: " + eventWrapperList.size() + " 个点");
Point constrainedEnd = ViewUtils.constrainToScreen(endX, endY, screenWidth, screenHeight);
eventQueue.add(new SlideMultipleEvent(
constrainedStart.x, constrainedStart.y,
constrainedEnd.x, constrainedEnd.y));
logDebug("添加滑动事件: 从 (" + constrainedStart.x + ", " + constrainedStart.y +
") 到 (" + constrainedEnd.x + ", " + constrainedEnd.y + ")");
}
public void addMultipleSlideEvents(List<Slide> slides) {
for (Slide slide : slides) {
addSlideEvent(slide.startX, slide.startY, slide.endX, slide.endY);
}
logDebug("添加多个滑动事件: " + slides.size() + "");
} }
public void setClickInterval(long interval) { public void setClickInterval(long interval) {
@ -141,15 +110,15 @@ public class AutoClickService extends AccessibilityService {
public void startClicking() { public void startClicking() {
if (!isRunning) { if (!isRunning) {
if (eventQueue.isEmpty()) { if (runtimeEvents.isEmpty()) {
Log.w(TAG, "无事件队列,忽略开始"); Log.w(TAG, "无事件队列,忽略开始");
return; return;
} }
isRunning = true; isRunning = true;
currentEventIndex = 0; currentEventIndex = 0;
logDebug("开始执行事件队列 - 共 " + eventQueue.size() + " 个事件"); logDebug("开始执行事件队列 - 共 " + runtimeEvents.size() + " 个事件");
executeNextEvent(); executeEventsByType();
} }
} }
@ -165,46 +134,40 @@ public class AutoClickService extends AccessibilityService {
return isRunning; return isRunning;
} }
private void executeNextEvent() { private void executeEventsByType() {
if (!isRunning || eventQueue.isEmpty()) { if (!isRunning || runtimeEvents.isEmpty()) {
logDebug("跳过执行:服务未运行或事件队列为空"); logDebug("跳过执行:服务未运行或事件队列为空");
return; return;
} }
Event event = eventQueue.get(currentEventIndex); runtimeEvents.sort(Comparator.comparingInt(EventWrapper::getOrder));
if (event instanceof ClickEvent) { if (currentEventIndex >= runtimeEvents.size()) {
ClickEvent clickEvent = (ClickEvent) event; currentEventIndex = 0;
logDebug("即将执行点击事件: (" + clickEvent.getX() + ", " + clickEvent.getY() + "), index=" + clickEvent.getAdditionalInfo()); }
} else if (event instanceof SlideMultipleEvent) {
SlideMultipleEvent slideMultipleEvent = (SlideMultipleEvent) event; EventWrapper wrapper = runtimeEvents.get(currentEventIndex);
logDebug("即将执行滑动事件: 从 (" + slideMultipleEvent.startX + ", " + slideMultipleEvent.startY + ") 到 (" + slideMultipleEvent.endX + ", " + slideMultipleEvent.endY + ")"); Event event = wrapper.getEvent();
if (wrapper.getType() == EventWrapper.EventType.POINT && event instanceof PointEvent) {
performSingleClick((PointEvent) event);
} else if (wrapper.getType() == EventWrapper.EventType.SLIDE && event instanceof SlideEvent) {
performSlide((SlideEvent) event);
} else { } else {
logDebug("未知事件类型: " + event.getClass().getSimpleName()); logDebug("未知或不匹配的事件类型,跳过执行: " + wrapper.getType());
currentEventIndex++;
handler.postDelayed(this::executeEventsByType, clickInterval);
}
} }
if (event instanceof ClickEvent) { private void performSingleClick(PointEvent pointEvent) {
ClickEvent clickEvent = (ClickEvent) event; int x = pointEvent.getX();
Point clickPoint = new Point(clickEvent.getX(), clickEvent.getY()); int y = pointEvent.getY();
performSingleClick(clickPoint, clickEvent.getAdditionalInfo());
} else if (event instanceof SlideMultipleEvent) {
SlideMultipleEvent slideMultipleEvent = (SlideMultipleEvent) event;
performSlide(slideMultipleEvent);
} else {
logDebug("未知事件类型,跳过执行: " + event.getClass().getSimpleName());
// 也可以选择继续执行下一个
handler.postDelayed(this::executeNextEvent, clickInterval);
}
// 准备下一个事件 logDebug("执行点击: (" + x + ", " + y + ")");
currentEventIndex = (currentEventIndex + 1) % eventQueue.size();
}
private void performSingleClick(Point clickPoint, int index) {
logDebug("执行点击: (" + clickPoint.x + ", " + clickPoint.y + ")");
Path path = new Path(); Path path = new Path();
path.moveTo(clickPoint.x, clickPoint.y); path.moveTo(x, y);
GestureDescription.StrokeDescription stroke = GestureDescription.StrokeDescription stroke =
new GestureDescription.StrokeDescription(path, 0, clickDuration); new GestureDescription.StrokeDescription(path, 0, clickDuration);
GestureDescription gesture = GestureDescription gesture =
@ -215,8 +178,10 @@ public class AutoClickService extends AccessibilityService {
public void onCompleted(GestureDescription gestureDescription) { public void onCompleted(GestureDescription gestureDescription) {
logDebug("点击完成"); logDebug("点击完成");
if (isRunning) { if (isRunning) {
handler.postDelayed(() -> executeNextEvent(), clickInterval); int feedbackIndex = currentEventIndex;
flashTouchFeedback(index); currentEventIndex++;
handler.postDelayed(() -> executeEventsByType(), clickInterval);
flashTouchFeedback(feedbackIndex);
} }
} }
@ -224,19 +189,26 @@ public class AutoClickService extends AccessibilityService {
public void onCancelled(GestureDescription gestureDescription) { public void onCancelled(GestureDescription gestureDescription) {
Log.e(TAG, "点击被取消"); Log.e(TAG, "点击被取消");
if (isRunning) { if (isRunning) {
handler.postDelayed(() -> executeNextEvent(), clickInterval + 300); currentEventIndex++;
handler.postDelayed(() -> executeEventsByType(), clickInterval + 300);
} }
} }
}, null); }, null);
} }
private void performSlide(SlideMultipleEvent slide) { private void performSlide(SlideEvent slideEvent) {
logDebug("执行滑动: 从 (" + slide.startX + ", " + slide.startY + PointEvent start = slideEvent.getStartPoint();
") 到 (" + slide.endX + ", " + slide.endY + ")"); PointEvent end = slideEvent.getEndPoint();
int startX = start.getX();
int startY = start.getY();
int endX = end.getX();
int endY = end.getY();
logDebug("执行滑动: 从 (" + startX + ", " + startY + ") 到 (" + endX + ", " + endY + ")");
Path path = new Path(); Path path = new Path();
path.moveTo(slide.startX, slide.startY); path.moveTo(startX, startY);
path.lineTo(slide.endX, slide.endY); path.lineTo(endX, endY);
GestureDescription.StrokeDescription stroke = GestureDescription.StrokeDescription stroke =
new GestureDescription.StrokeDescription(path, 0, slideDuration); new GestureDescription.StrokeDescription(path, 0, slideDuration);
GestureDescription gesture = GestureDescription gesture =
@ -247,7 +219,10 @@ public class AutoClickService extends AccessibilityService {
public void onCompleted(GestureDescription gestureDescription) { public void onCompleted(GestureDescription gestureDescription) {
logDebug("滑动完成"); logDebug("滑动完成");
if (isRunning) { if (isRunning) {
handler.postDelayed(() -> executeNextEvent(), clickInterval); int feedbackIndex = currentEventIndex;
currentEventIndex++;
handler.postDelayed(() -> executeEventsByType(), clickInterval);
flashTouchFeedback(feedbackIndex);
} }
} }
@ -255,7 +230,8 @@ public class AutoClickService extends AccessibilityService {
public void onCancelled(GestureDescription gestureDescription) { public void onCancelled(GestureDescription gestureDescription) {
Log.e(TAG, "滑动被取消"); Log.e(TAG, "滑动被取消");
if (isRunning) { if (isRunning) {
handler.postDelayed(() -> executeNextEvent(), clickInterval + 300); currentEventIndex++;
handler.postDelayed(() -> executeEventsByType(), clickInterval + 300);
} }
} }
}, null); }, null);

View File

@ -1,19 +0,0 @@
package com.auto.autoclicker;
public class ClickEvent extends Event {
private int additionalInfo;
public ClickEvent(int x, int y, int additionalInfo) {
super(additionalInfo, x, y);
this.additionalInfo = additionalInfo;
}
public int getAdditionalInfo() {
return additionalInfo;
}
public void setAdditionalInfo(int additionalInfo) {
this.additionalInfo = additionalInfo;
}
}

View File

@ -1,26 +0,0 @@
package com.auto.autoclicker;
public class Event {
protected int id;
protected int x;
protected int y;
public Event(int id, int x, int y) {
this.id = id;
this.x = x;
this.y = y;
}
// Getter 方法
public int getId() {
return id;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}

View File

@ -64,7 +64,6 @@ public class ForegroundService extends Service {
return START_STICKY; return START_STICKY;
} }
private void createNotificationChannel() { private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel( NotificationChannel channel = new NotificationChannel(

View File

@ -1,31 +0,0 @@
package com.auto.autoclicker;
import android.view.WindowManager;
import android.widget.TextView;
public class PointEvent extends Event {
private TextView view;
private WindowManager.LayoutParams params;
public PointEvent(int id, int x, int y, TextView view, WindowManager.LayoutParams params) {
super(id, x, y);
this.view = view;
this.params = params;
}
public TextView getView() {
return view;
}
public WindowManager.LayoutParams getParams() {
return params;
}
public void setView(TextView view) {
this.view = view;
}
public void setParams(WindowManager.LayoutParams params) {
this.params = params;
}
}

View File

@ -1,16 +0,0 @@
package com.auto.autoclicker;
public class Slide {
public final int startX;
public final int startY;
public final int endX;
public final int endY;
public Slide(int startX, int startY, int endX, int endY) {
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
}
}

View File

@ -1,26 +0,0 @@
package com.auto.autoclicker;
public class SlideMultipleEvent extends Event {
public int startX;
public int startY;
public int endX;
public int endY;
public SlideMultipleEvent(int startX, int startY, int endX, int endY) {
super(-1, startX, startY);
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
}
public SlideMultipleEvent(int id, int startX, int startY, int endX, int endY) {
super(id, startX, startY);
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
}
}

View File

@ -0,0 +1,13 @@
package com.auto.autoclicker.data;
public abstract class Event {
protected int id;
public Event(int id) {
this.id = id;
}
public int getId() {
return id;
}
}

View File

@ -0,0 +1,40 @@
package com.auto.autoclicker.data;
public class EventWrapper {
public enum EventType { POINT, SLIDE }
private EventType type;
private Event event;
private int order; // 添加顺序编号用于UI编号
public EventWrapper(EventType type, Event event, int order) {
this.type = type;
this.event = event;
this.order = order;
}
public EventType getType() {
return type;
}
public void setType(EventType type) {
this.type = type;
}
public Event getEvent() {
return event;
}
public void setEvent(Event event) {
this.event = event;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}

View File

@ -0,0 +1,52 @@
package com.auto.autoclicker.data;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
public class PointEvent extends Event {
private int x;
private int y;
private TextView view; // 悬浮窗上的控件
private WindowManager.LayoutParams layoutParams;
public PointEvent(int id, int x, int y, TextView view, WindowManager.LayoutParams layoutParams) {
super(id);
this.x = x;
this.y = y;
this.view = view;
this.layoutParams = layoutParams;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public View getView() {
return view;
}
public void setView(TextView view) {
this.view = view;
}
public WindowManager.LayoutParams getLayoutParams() {
return layoutParams;
}
public void setLayoutParams(WindowManager.LayoutParams layoutParams) {
this.layoutParams = layoutParams;
}
}

View File

@ -0,0 +1,5 @@
package com.auto.autoclicker.data;
public interface PositionUpdater {
void update(float paramX, float paramY, float dx, float dy);
}

View File

@ -1,16 +1,19 @@
package com.auto.autoclicker; package com.auto.autoclicker.data;
import android.view.WindowManager; import android.view.WindowManager;
import com.auto.autoclicker.ConnectingLineView;
public class SlideEvent extends Event { public class SlideEvent extends Event {
private PointEvent startPoint; private PointEvent startPoint;
private PointEvent endPoint; private PointEvent endPoint;
private ConnectingLineView lineView; private ConnectingLineView lineView;
private WindowManager.LayoutParams lineParams; private WindowManager.LayoutParams lineParams;
private long durationMillis; // 滑动时间
public SlideEvent(int id, PointEvent startPoint, PointEvent endPoint, public SlideEvent(int id, PointEvent startPoint, PointEvent endPoint,
ConnectingLineView lineView, WindowManager.LayoutParams lineParams) { ConnectingLineView lineView, WindowManager.LayoutParams lineParams) {
super(id, 0, 0); super(id);
this.startPoint = startPoint; this.startPoint = startPoint;
this.endPoint = endPoint; this.endPoint = endPoint;
this.lineView = lineView; this.lineView = lineView;
@ -21,32 +24,39 @@ public class SlideEvent extends Event {
return startPoint; return startPoint;
} }
public PointEvent getEndPoint() {
return endPoint;
}
public ConnectingLineView getLineView() {
return lineView;
}
public WindowManager.LayoutParams getLineParams() {
return lineParams;
}
public void setStartPoint(PointEvent startPoint) { public void setStartPoint(PointEvent startPoint) {
this.startPoint = startPoint; this.startPoint = startPoint;
} }
public PointEvent getEndPoint() {
return endPoint;
}
public void setEndPoint(PointEvent endPoint) { public void setEndPoint(PointEvent endPoint) {
this.endPoint = endPoint; this.endPoint = endPoint;
} }
public ConnectingLineView getLineView() {
return lineView;
}
public void setLineView(ConnectingLineView lineView) { public void setLineView(ConnectingLineView lineView) {
this.lineView = lineView; this.lineView = lineView;
} }
public WindowManager.LayoutParams getLineParams() {
return lineParams;
}
public void setLineParams(WindowManager.LayoutParams lineParams) { public void setLineParams(WindowManager.LayoutParams lineParams) {
this.lineParams = lineParams; this.lineParams = lineParams;
} }
public long getDurationMillis() {
return durationMillis;
} }
public void setDurationMillis(long durationMillis) {
this.durationMillis = durationMillis;
}
}

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/rounded_background"
android:gravity="center"
android:orientation="vertical"
android:padding="8dp">
<ImageView
android:id="@+id/play_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginBottom="8dp"
android:src="@drawable/play" />
<ImageView
android:id="@+id/file_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginBottom="8dp"
android:src="@drawable/save" />
<ImageView
android:id="@+id/eye_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginBottom="8dp"
android:src="@drawable/eye" />
<ImageView
android:id="@+id/settings_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginBottom="8dp"
android:src="@drawable/setting" />
<ImageView
android:id="@+id/close_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/cancel" />
</LinearLayout>