diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8a4f149..a8db7c9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -27,7 +27,6 @@
-
-
multiClickPoints = new ArrayList<>();
+
private static final String TAG = "AutoClickService";
private static AutoClickService instance;
+ private final List multiClickPositions = new ArrayList<>();
+ private int currentClickIndex = 0;
+
private static final long MIN_CLICK_INTERVAL = 40;
private static final long MAX_CLICK_INTERVAL = 10000;
private static final int MIN_CLICK_DURATION = 50;
@@ -44,6 +52,10 @@ public class AutoClickService extends AccessibilityService {
logDebug("屏幕尺寸: " + screenWidth + "x" + screenHeight);
}
+ public static AutoClickService getInstance() {
+ return instance;
+ }
+
@Override
public void onServiceConnected() {
super.onServiceConnected();
@@ -56,25 +68,17 @@ public class AutoClickService extends AccessibilityService {
logDebug("无障碍服务中断");
}
- @Override
- public void onDestroy() {
- super.onDestroy();
- instance = null;
- stopClicking();
- Intent intent = new Intent("com.auto.autoclicker.SERVICE_DESTROYED");
- LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
- logDebug("无障碍服务已销毁");
- }
-
- public static AutoClickService getInstance() {
- return instance;
- }
-
public void setClickPosition(float x, float y) {
Point constrained = ViewUtils.constrainToScreen(x, y, screenWidth, screenHeight);
this.clickX = constrained.x;
this.clickY = constrained.y;
- logDebug("设置点击位置: 原始 (" + x + ", " + y + ") -> 修正 (" + clickX + ", " + clickY + ")");
+ logDebug("设置单点点击位置: 原始 (" + x + ", " + y + ") -> 修正 (" + clickX + ", " + clickY + ")");
+ }
+
+ public void setMultiClickPositions(List positions) {
+ this.multiClickPositions.clear();
+ this.multiClickPositions.addAll(positions);
+ logDebug("设置多点点击位置: " + positions.size() + " 个点");
}
public void setClickInterval(long interval) {
@@ -97,7 +101,12 @@ public class AutoClickService extends AccessibilityService {
public void startClicking() {
if (!isClicking) {
+ if (multiClickPositions.isEmpty() && (clickX == 0 && clickY == 0)) {
+ Log.w(TAG, "无有效点击位置,忽略开始点击");
+ return;
+ }
isClicking = true;
+ currentClickIndex = 0;
logDebug("开始自动点击");
performClick();
}
@@ -121,11 +130,22 @@ public class AutoClickService extends AccessibilityService {
return;
}
- logDebug("执行点击: (" + clickX + ", " + clickY + ")");
+ // 决定使用单点还是多点点击
+ Point clickPoint;
+ if (!multiClickPositions.isEmpty()) {
+ // 多点点击模式
+ clickPoint = multiClickPositions.get(currentClickIndex);
+ currentClickIndex = (currentClickIndex + 1) % multiClickPositions.size();
+ } else {
+ // 单点点击模式
+ clickPoint = new Point(clickX, clickY);
+ }
+
+ logDebug("执行点击: (" + clickPoint.x + ", " + clickPoint.y + ")");
flashTouchFeedback();
Path path = new Path();
- path.moveTo(clickX, clickY);
+ path.moveTo(clickPoint.x, clickPoint.y);
GestureDescription.StrokeDescription stroke =
new GestureDescription.StrokeDescription(path, 0, clickDuration);
GestureDescription gesture =
@@ -162,4 +182,14 @@ public class AutoClickService extends AccessibilityService {
private void logDebug(String message) {
Log.d(TAG, message);
}
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ instance = null;
+ stopClicking();
+ Intent intent = new Intent("com.auto.autoclicker.SERVICE_DESTROYED");
+ LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
+ logDebug("无障碍服务已销毁");
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/auto/autoclicker/FloatingViewManager.java b/app/src/main/java/com/auto/autoclicker/FloatingViewManager.java
index 4b36d9d..c1505fc 100644
--- a/app/src/main/java/com/auto/autoclicker/FloatingViewManager.java
+++ b/app/src/main/java/com/auto/autoclicker/FloatingViewManager.java
@@ -2,7 +2,6 @@ package com.auto.autoclicker;
import android.annotation.SuppressLint;
import android.app.Activity;
-import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -24,27 +23,45 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
+import androidx.appcompat.app.AlertDialog;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.auto.autoclicker.util.PrefUtils;
import com.auto.autoclicker.util.ScreenUtils;
import com.auto.autoclicker.util.ViewUtils;
+import java.util.ArrayList;
+import java.util.List;
+
public class FloatingViewManager {
private static final String TAG = "FloatingViewManager";
- private static final long DEBOUNCE_INTERVAL = 500;
private final Context context;
+
+ private static final long DEBOUNCE_INTERVAL = 500;
+
private final WindowManager windowManager;
- private View touchPointView;
- private LinearLayout controlBarView;
- private WindowManager.LayoutParams touchPointParams;
- private WindowManager.LayoutParams controlBarParams;
+
+ private final List moreTouchPointViews = new ArrayList<>();
+ private final List moreTouchPointParams = new ArrayList<>();
+
+ private View singleTouchPointView;
+ private WindowManager.LayoutParams singleTouchPointParams;
+
+ private LinearLayout singleControlBarView;
+ private LinearLayout moreControlBarView;
+
+ private WindowManager.LayoutParams singleControlBarParams;
+ private WindowManager.LayoutParams moreControlBarParams;
+
private float touchPointX = 500;
private float touchPointY = 500;
+
private boolean isClicking = false;
private boolean isFloatingViewsShown = false;
+
private long lastToggleTime = 0;
+
private final int screenWidth;
private final int screenHeight;
@@ -58,47 +75,61 @@ public class FloatingViewManager {
logDebug("屏幕尺寸: " + screenWidth + "x" + screenHeight);
}
- public void showFloatingViews() throws SecurityException {
+ private void registerBroadcast(Context context) {
+ LocalBroadcastManager.getInstance(context).registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if ("com.auto.autoclicker.FLASH_TOUCH_POINT".equals(intent.getAction())) {
+ flashTouchPoint();
+ }
+ }
+ }, new IntentFilter("com.auto.autoclicker.FLASH_TOUCH_POINT"));
+ }
+
+ public void showFloatingViews(int mode) throws SecurityException {
if (!Settings.canDrawOverlays(context)) {
throw new SecurityException("需要悬浮窗权限");
}
- if (isFloatingViewsShown) {
- logDebug("悬浮窗已存在,清理后重新创建");
- removeFloatingViews();
+ removeFloatingViews();
+
+ if (mode == 1) {
+ initSingleTouchPointView();
+ initSingleControlBar();
+
+ if (singleTouchPointView != null) {
+ windowManager.addView(singleTouchPointView, singleTouchPointParams);
+ }
+
+ if (singleControlBarView != null) {
+ windowManager.addView(singleControlBarView, singleControlBarParams);
+ }
+ } else if (mode == 2) {
+ initMoreTouchPointView();
+ initMoreControlBar();
+
+ if (moreTouchPointViews.size() == moreTouchPointParams.size()) {
+ for (int i = 0; i < moreTouchPointViews.size(); i++) {
+ windowManager.addView(moreTouchPointViews.get(i), moreTouchPointParams.get(i));
+ }
+ } else {
+ Log.e(TAG, "触摸点与布局参数数量不一致");
+ }
+
+
+ if (moreControlBarView != null) {
+ windowManager.addView(moreControlBarView, moreControlBarParams);
+ }
}
- initializeTouchPointView();
- initializeControlBarView();
-
- windowManager.addView(touchPointView, touchPointParams);
- windowManager.addView(controlBarView, controlBarParams);
isFloatingViewsShown = true;
- logDebug("悬浮窗已添加");
+ logDebug("悬浮窗已添加,模式 = " + mode);
}
- public void removeFloatingViews() {
- try {
- if (touchPointView != null) {
- windowManager.removeView(touchPointView);
- touchPointView = null;
- }
- if (controlBarView != null) {
- windowManager.removeView(controlBarView);
- controlBarView = null;
- }
- isFloatingViewsShown = false;
- logDebug("悬浮窗已移除");
- } catch (Exception e) {
- Log.e(TAG, "移除悬浮窗失败", e);
- isFloatingViewsShown = false;
- }
- }
-
- private void initializeTouchPointView() {
- touchPointView = new View(context);
- touchPointView.setBackgroundResource(R.drawable.un_touch_point);
- touchPointParams = new WindowManager.LayoutParams(
+ private void initSingleTouchPointView() {
+ singleTouchPointView = new View(context);
+ singleTouchPointView.setBackgroundResource(R.drawable.un_touch_point);
+ singleTouchPointParams = new WindowManager.LayoutParams(
100, 100,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
@@ -107,11 +138,11 @@ public class FloatingViewManager {
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT
);
- touchPointParams.gravity = Gravity.TOP | Gravity.START;
- touchPointParams.x = (int) touchPointX;
- touchPointParams.y = (int) touchPointY;
+ singleTouchPointParams.gravity = Gravity.TOP | Gravity.START;
+ singleTouchPointParams.x = (int) touchPointX;
+ singleTouchPointParams.y = (int) touchPointY;
- touchPointView.setOnTouchListener(new View.OnTouchListener() {
+ singleTouchPointView.setOnTouchListener(new View.OnTouchListener() {
private float lastX, lastY;
private float paramX, paramY;
@@ -121,8 +152,8 @@ public class FloatingViewManager {
case MotionEvent.ACTION_DOWN:
lastX = event.getRawX();
lastY = event.getRawY();
- paramX = touchPointParams.x;
- paramY = touchPointParams.y;
+ paramX = singleTouchPointParams.x;
+ paramY = singleTouchPointParams.y;
break;
case MotionEvent.ACTION_MOVE:
float dx = event.getRawX() - lastX;
@@ -131,7 +162,7 @@ public class FloatingViewManager {
AutoClickService service = AutoClickService.getInstance();
if (service != null) {
service.setClickPosition(touchPointX + 50, touchPointY + 50);
- logDebug("触摸点移动到: (" + touchPointX + ", " + touchPointY + ")");
+ logDebug("单点触摸点移动到: (" + touchPointX + ", " + touchPointY + ")");
}
break;
}
@@ -140,10 +171,16 @@ public class FloatingViewManager {
});
}
+ private void initMoreTouchPointView() {
+ moreTouchPointViews.clear();
+ moreTouchPointParams.clear();
+ addNewTouchPoint(100, 500);
+ }
+
@SuppressLint("ClickableViewAccessibility")
- private void initializeControlBarView() {
- controlBarView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.single_control_bar, null);
- controlBarParams = new WindowManager.LayoutParams(
+ private void initSingleControlBar() {
+ singleControlBarView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.single_control_bar, null);
+ singleControlBarParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
@@ -153,11 +190,11 @@ public class FloatingViewManager {
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT
);
- controlBarParams.gravity = Gravity.TOP | Gravity.START;
- controlBarParams.x = 0;
- controlBarParams.y = 200;
+ singleControlBarParams.gravity = Gravity.TOP | Gravity.START;
+ singleControlBarParams.x = 0;
+ singleControlBarParams.y = 200;
- controlBarView.setOnTouchListener(new View.OnTouchListener() {
+ singleControlBarView.setOnTouchListener(new View.OnTouchListener() {
private float lastX, lastY;
private float paramX, paramY;
@@ -167,13 +204,13 @@ public class FloatingViewManager {
case MotionEvent.ACTION_DOWN:
lastX = event.getRawX();
lastY = event.getRawY();
- paramX = controlBarParams.x;
- paramY = controlBarParams.y;
+ paramX = singleControlBarParams.x;
+ paramY = singleControlBarParams.y;
return true;
case MotionEvent.ACTION_MOVE:
float dx = event.getRawX() - lastX;
float dy = event.getRawY() - lastY;
- updateControlBarPosition(paramX, paramY, dx, dy);
+ updateSingleControlBarPosition(paramX, paramY, dx, dy);
return true;
case MotionEvent.ACTION_UP:
v.performClick();
@@ -183,84 +220,304 @@ public class FloatingViewManager {
}
});
- ImageView play = controlBarView.findViewById(R.id.play_button);
- ImageView close = controlBarView.findViewById(R.id.close_button);
- ImageView setting = controlBarView.findViewById(R.id.settings_button);
-
- play.setOnClickListener(v -> {
- if (!isDebounced()) return;
-
- AutoClickService service = AutoClickService.getInstance();
-
- if (service == null) {
- Log.e(TAG, "AutoClickService 未初始化");
- Toast.makeText(context, "请在设置中启用无障碍服务", Toast.LENGTH_LONG).show();
- Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
- return;
- }
-
- if (isClicking) {
- service.stopClicking();
- play.setBackgroundResource(R.drawable.play);
- touchPointView.setBackgroundResource(R.drawable.un_touch_point);
- Toast.makeText(context, "停止自动点击", Toast.LENGTH_SHORT).show();
- } else {
- service.setClickPosition(touchPointX, touchPointY);
- service.startClicking();
- play.setBackgroundResource(R.drawable.play);
- touchPointView.setBackgroundResource(R.drawable.touch_point);
- Toast.makeText(context, "开始自动点击", Toast.LENGTH_SHORT).show();
- }
-
- isClicking = !isClicking;
- logDebug("服务是否点击中: " + service.isClicking());
- });
-
- close.setOnClickListener(v -> {
- AutoClickService service = AutoClickService.getInstance();
-
- if (service != null) {
- service.stopClicking();
- } else {
- logDebug("AutoClickService 未初始化");
- Toast.makeText(context, "请启用无障碍服务", Toast.LENGTH_SHORT).show();
- }
-
- touchPointView.setBackgroundResource(R.drawable.un_touch_point);
- removeFloatingViews();
-
- context.stopService(new Intent(context, ForegroundService.class));
-
- PrefUtils.setFloatingShown(context, false);
-
- Intent intent = new Intent("com.auto.autoclicker.FLOATING_WINDOW_STATE_CHANGED");
- intent.putExtra("isShown", false);
- LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
-
- });
+ ImageView play = singleControlBarView.findViewById(R.id.play_button);
+ ImageView close = singleControlBarView.findViewById(R.id.close_button);
+ ImageView setting = singleControlBarView.findViewById(R.id.settings_button);
+ play.setOnClickListener(v -> toggleSingleClicking(play));
+ close.setOnClickListener(v -> closeFloatingViews());
setting.setOnClickListener(v -> showInputDialog());
+ }
+ @SuppressLint("ClickableViewAccessibility")
+ private void initMoreControlBar() {
+ moreControlBarView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.more_control_bar, null);
+ moreControlBarParams = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
+ WindowManager.LayoutParams.TYPE_PHONE,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT
+ );
+ moreControlBarParams.gravity = Gravity.TOP | Gravity.START;
+ moreControlBarParams.x = 0;
+ moreControlBarParams.y = 200;
+
+ moreControlBarView.setOnTouchListener(new View.OnTouchListener() {
+ private float lastX, lastY;
+ private float paramX, paramY;
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ lastX = event.getRawX();
+ lastY = event.getRawY();
+ paramX = moreControlBarParams.x;
+ paramY = moreControlBarParams.y;
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ float dx = event.getRawX() - lastX;
+ float dy = event.getRawY() - lastY;
+ updateMoreControlBarPosition(paramX, paramY, dx, dy);
+ return true;
+ case MotionEvent.ACTION_UP:
+ v.performClick();
+ return true;
+ }
+ return false;
+ }
+ });
+
+ ImageView addButton = moreControlBarView.findViewById(R.id.add_button);
+ ImageView removeButton = moreControlBarView.findViewById(R.id.remove_button);
+ ImageView playButton = moreControlBarView.findViewById(R.id.play_button);
+ ImageView closeButton = moreControlBarView.findViewById(R.id.close_button);
+ ImageView settingButton = moreControlBarView.findViewById(R.id.settings_button);
+
+ addButton.setOnClickListener(v -> addNewTouchPoint(100 + moreTouchPointViews.size() * 120, 500));
+ removeButton.setOnClickListener(v -> removeLastTouchPoint());
+ playButton.setOnClickListener(v -> toggleMoreClicking(playButton));
+ closeButton.setOnClickListener(v -> closeFloatingViews());
+ settingButton.setOnClickListener(v -> showInputDialog());
+ }
+
+ private void toggleSingleClicking(ImageView play) {
+ if (isDebounced()) return;
+
+ AutoClickService service = AutoClickService.getInstance();
+ if (service == null) {
+ Log.e(TAG, "AutoClickService 未初始化");
+ Toast.makeText(context, "请在设置中启用无障碍服务", Toast.LENGTH_LONG).show();
+ Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ return;
+ }
+
+ if (isClicking) {
+ service.stopClicking();
+ play.setBackgroundResource(R.drawable.play);
+ singleTouchPointView.setBackgroundResource(R.drawable.un_touch_point);
+ Toast.makeText(context, "停止自动点击", Toast.LENGTH_SHORT).show();
+ } else {
+ service.setClickPosition(touchPointX + 50, touchPointY + 50);
+ service.startClicking();
+ play.setBackgroundResource(R.drawable.pause);
+ singleTouchPointView.setBackgroundResource(R.drawable.touch_point);
+ Toast.makeText(context, "开始自动点击", Toast.LENGTH_SHORT).show();
+ }
+
+ isClicking = !isClicking;
+ logDebug("单点服务是否点击中: " + service.isClicking());
+ }
+
+ private void toggleMoreClicking(ImageView play) {
+ if (isDebounced()) return;
+
+ AutoClickService service = AutoClickService.getInstance();
+ if (service == null) {
+ Log.e(TAG, "AutoClickService 未初始化");
+ Toast.makeText(context, "请在设置中启用无障碍服务", Toast.LENGTH_LONG).show();
+ Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ return;
+ }
+
+ if (isClicking) {
+ service.stopClicking();
+ play.setBackgroundResource(R.drawable.play);
+ for (View point : moreTouchPointViews) {
+ point.setBackgroundResource(R.drawable.un_touch_point);
+ }
+ Toast.makeText(context, "停止多点自动点击", Toast.LENGTH_SHORT).show();
+ } else {
+ List positions = new ArrayList<>();
+ for (WindowManager.LayoutParams params : moreTouchPointParams) {
+ positions.add(new Point(params.x + 50, params.y + 50));
+ }
+ service.setMultiClickPositions(positions);
+ service.startClicking();
+ play.setBackgroundResource(R.drawable.pause);
+ for (View point : moreTouchPointViews) {
+ point.setBackgroundResource(R.drawable.touch_point);
+ }
+ Toast.makeText(context, "开始多点自动点击", Toast.LENGTH_SHORT).show();
+ }
+
+ isClicking = !isClicking;
+ logDebug("多点服务是否点击中: " + service.isClicking());
+ }
+
+ private void closeFloatingViews() {
+ AutoClickService service = AutoClickService.getInstance();
+ if (service != null) {
+ service.stopClicking();
+ } else {
+ logDebug("AutoClickService 未初始化");
+ Toast.makeText(context, "请启用无障碍服务", Toast.LENGTH_SHORT).show();
+ }
+
+ if (singleTouchPointView != null) {
+ singleTouchPointView.setBackgroundResource(R.drawable.un_touch_point);
+ }
+ for (View point : moreTouchPointViews) {
+ point.setBackgroundResource(R.drawable.un_touch_point);
+ }
+
+ removeFloatingViews();
+
+ context.stopService(new Intent(context, ForegroundService.class));
+ PrefUtils.setFloatingShown(context, 0);
+
+ Intent intent = new Intent("com.auto.autoclicker.FLOATING_WINDOW_STATE_CHANGED");
+ intent.putExtra("isShown", false);
+ LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
+ }
+
+ public void removeFloatingViews() {
+ try {
+ // 移除单点触摸视图
+ if (singleTouchPointView != null) {
+ try {
+ windowManager.removeView(singleTouchPointView);
+ } catch (Exception e) {
+ Log.w(TAG, "移除 singleTouchPointView 失败", e);
+ }
+ singleTouchPointView = null;
+ }
+
+ // 移除多点触摸视图
+ List pointsToRemove = new ArrayList<>(moreTouchPointViews);
+ for (View point : pointsToRemove) {
+ try {
+ windowManager.removeView(point);
+ moreTouchPointViews.remove(point); // 成功移除后从列表中删除
+ } catch (Exception e) {
+ Log.w(TAG, "移除 multiTouchPoint 失败", e);
+ }
+ }
+ moreTouchPointParams.clear(); // 清空参数列表
+
+ // 移除单点控制栏
+ if (singleControlBarView != null) {
+ try {
+ windowManager.removeView(singleControlBarView);
+ } catch (Exception e) {
+ Log.w(TAG, "移除 singleControlBarView 失败", e);
+ }
+ singleControlBarView = null;
+ }
+
+ // 移除多点控制栏
+ if (moreControlBarView != null) {
+ try {
+ windowManager.removeView(moreControlBarView);
+ } catch (Exception e) {
+ Log.w(TAG, "移除 moreControlBarView 失败", e);
+ }
+ moreControlBarView = null;
+ }
+
+ isFloatingViewsShown = false;
+ isClicking = false;
+ logDebug("悬浮窗已移除");
+ } catch (Exception e) {
+ Log.e(TAG, "移除悬浮窗失败", e);
+ isFloatingViewsShown = false;
+ isClicking = false;
+ }
+ }
+
+ private void addNewTouchPoint(int x, int y) {
+ View point = new View(context);
+ point.setBackgroundResource(R.drawable.un_touch_point);
+ WindowManager.LayoutParams params = createTouchPointLayoutParams(x, y);
+ windowManager.addView(point, params);
+ moreTouchPointViews.add(point);
+ moreTouchPointParams.add(params);
+
+ point.setOnTouchListener(new View.OnTouchListener() {
+ private float lastX, lastY, startX, startY;
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ lastX = event.getRawX();
+ lastY = event.getRawY();
+ startX = params.x;
+ startY = params.y;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ float dx = event.getRawX() - lastX;
+ float dy = event.getRawY() - lastY;
+ params.x = (int) (startX + dx);
+ params.y = (int) (startY + dy);
+ windowManager.updateViewLayout(v, params);
+ logDebug("多点触摸点移动到: (" + params.x + ", " + params.y + ")");
+ break;
+ }
+ return true;
+ }
+ });
+ }
+
+ private void removeLastTouchPoint() {
+ if (moreTouchPointViews.size() <= 1) {
+ Toast.makeText(context, "至少保留一个触摸点", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ int lastIndex = moreTouchPointViews.size() - 1;
+ View last = moreTouchPointViews.remove(lastIndex);
+ moreTouchPointParams.remove(lastIndex);
+ windowManager.removeView(last);
+ }
+
+ private WindowManager.LayoutParams createTouchPointLayoutParams(int x, int y) {
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ 100, 100,
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
+ WindowManager.LayoutParams.TYPE_PHONE,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT
+ );
+ params.gravity = Gravity.TOP | Gravity.START;
+ params.x = x;
+ params.y = y;
+ return params;
}
private void updateTouchPointPosition(float paramX, float paramY, float dx, float dy) {
Point constrainedPoint = ViewUtils.constrainToScreen(
- paramX + dx, paramY + dy, screenWidth - touchPointView.getWidth(), screenHeight - touchPointView.getHeight());
- touchPointParams.x = constrainedPoint.x;
- touchPointParams.y = constrainedPoint.y;
- touchPointX = touchPointParams.x;
- touchPointY = touchPointParams.y;
- windowManager.updateViewLayout(touchPointView, touchPointParams);
+ paramX + dx, paramY + dy, screenWidth - singleTouchPointView.getWidth(), screenHeight - singleTouchPointView.getHeight());
+ singleTouchPointParams.x = constrainedPoint.x;
+ singleTouchPointParams.y = constrainedPoint.y;
+ touchPointX = singleTouchPointParams.x;
+ touchPointY = singleTouchPointParams.y;
+ windowManager.updateViewLayout(singleTouchPointView, singleTouchPointParams);
}
- private void updateControlBarPosition(float paramX, float paramY, float dx, float dy) {
+ private void updateSingleControlBarPosition(float paramX, float paramY, float dx, float dy) {
Point constrainedPoint = ViewUtils.constrainToScreen(
- paramX + dx, paramY + dy, screenWidth - controlBarView.getWidth(), screenHeight - controlBarView.getHeight());
- controlBarParams.x = constrainedPoint.x;
- controlBarParams.y = constrainedPoint.y;
- windowManager.updateViewLayout(controlBarView, controlBarParams);
+ paramX + dx, paramY + dy, screenWidth - singleControlBarView.getWidth(), screenHeight - singleControlBarView.getHeight());
+ singleControlBarParams.x = constrainedPoint.x;
+ singleControlBarParams.y = constrainedPoint.y;
+ windowManager.updateViewLayout(singleControlBarView, singleControlBarParams);
+ }
+
+ private void updateMoreControlBarPosition(float paramX, float paramY, float dx, float dy) {
+ Point constrainedPoint = ViewUtils.constrainToScreen(
+ paramX + dx, paramY + dy, screenWidth - moreControlBarView.getWidth(), screenHeight - moreControlBarView.getHeight());
+ moreControlBarParams.x = constrainedPoint.x;
+ moreControlBarParams.y = constrainedPoint.y;
+ windowManager.updateViewLayout(moreControlBarView, moreControlBarParams);
}
public void showInputDialog() {
@@ -303,7 +560,6 @@ public class FloatingViewManager {
dialog.show();
- // 强制显示软键盘
input.requestFocus();
input.post(() -> {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -314,41 +570,42 @@ public class FloatingViewManager {
}
public void flashTouchPoint() {
- if (touchPointView == null) return;
-
- touchPointView.animate()
- .alpha(0.3f)
- .setDuration(100)
- .withEndAction(() ->
- touchPointView.animate()
- .alpha(1.0f)
- .setDuration(100)
- .start()
- )
- .start();
- }
-
- private void registerBroadcast(Context context) {
- LocalBroadcastManager.getInstance(context).registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if ("com.auto.autoclicker.FLASH_TOUCH_POINT".equals(intent.getAction())) {
- flashTouchPoint();
- }
- }
- }, new IntentFilter("com.auto.autoclicker.FLASH_TOUCH_POINT"));
+ if (singleTouchPointView != null) {
+ singleTouchPointView.animate()
+ .alpha(0.3f)
+ .setDuration(100)
+ .withEndAction(() ->
+ singleTouchPointView.animate()
+ .alpha(1.0f)
+ .setDuration(100)
+ .start()
+ )
+ .start();
+ }
+ for (View point : moreTouchPointViews) {
+ point.animate()
+ .alpha(0.3f)
+ .setDuration(100)
+ .withEndAction(() ->
+ point.animate()
+ .alpha(1.0f)
+ .setDuration(100)
+ .start()
+ )
+ .start();
+ }
}
private boolean isDebounced() {
long currentTime = System.currentTimeMillis();
if (currentTime - lastToggleTime < DEBOUNCE_INTERVAL) {
- return false;
+ return true;
}
lastToggleTime = currentTime;
- return true;
+ return false;
}
private void logDebug(String message) {
Log.d(TAG, message);
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/auto/autoclicker/ForegroundService.java b/app/src/main/java/com/auto/autoclicker/ForegroundService.java
index e7be063..e74de4b 100644
--- a/app/src/main/java/com/auto/autoclicker/ForegroundService.java
+++ b/app/src/main/java/com/auto/autoclicker/ForegroundService.java
@@ -1,5 +1,7 @@
package com.auto.autoclicker;
+import static com.auto.autoclicker.MainActivity.FLOATING_SINGLE;
+
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -33,11 +35,10 @@ public class ForegroundService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- Intent notificationIntent = new Intent(this, MainActivity.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(
- this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE
- );
+ int type = intent != null ? intent.getIntExtra("FLOATING_TYPE", FLOATING_SINGLE) : FLOATING_SINGLE;
+ Intent notificationIntent = new Intent(this, MainActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("自动点击运行中")
.setContentText("自动点击正在后台运行")
@@ -50,21 +51,20 @@ public class ForegroundService extends Service {
if (floatingViewManager != null && !isFloatingViewShown) {
try {
- floatingViewManager.showFloatingViews();
+ floatingViewManager.showFloatingViews(type);
isFloatingViewShown = true;
- Log.d(TAG, "悬浮窗已显示");
} catch (SecurityException e) {
Log.e(TAG, "未授予悬浮窗权限", e);
stopSelf();
}
} else if (floatingViewManager == null) {
- Log.e(TAG, "FloatingViewManager 未初始化");
stopSelf();
}
return START_STICKY;
}
-
+
+
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
diff --git a/app/src/main/java/com/auto/autoclicker/MainActivity.java b/app/src/main/java/com/auto/autoclicker/MainActivity.java
index 0723a45..cda9422 100644
--- a/app/src/main/java/com/auto/autoclicker/MainActivity.java
+++ b/app/src/main/java/com/auto/autoclicker/MainActivity.java
@@ -23,17 +23,27 @@ import com.auto.autoclicker.util.PrefUtils;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
+ private static final String ACTION_FLOATING_STATE_CHANGED = "com.auto.autoclicker.FLOATING_WINDOW_STATE_CHANGED";
+
private ActivityMainBinding binding;
+
+ public static final int FLOATING_NONE = 0;
+ public static final int FLOATING_SINGLE = 1;
+ public static final int FLOATING_MULTI = 2;
+
+ private Toast debounceToast;
+
private ActivityResultLauncher permissionLauncher;
+
private long lastClickTime = 0;
private static final long DEBOUNCE_INTERVAL = 500;
- private boolean isFloatingShown;
- private static final String ACTION_FLOATING_STATE_CHANGED = "com.auto.autoclicker.FLOATING_WINDOW_STATE_CHANGED";
+
+ private int isFloatingShown;
private final BroadcastReceiver floatingReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- isFloatingShown = intent.getBooleanExtra("isShown", false);
+ isFloatingShown = intent.getIntExtra("isShown", 0);
updateButtonText();
}
};
@@ -45,13 +55,14 @@ public class MainActivity extends AppCompatActivity {
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
- isFloatingShown = PrefUtils.isFloatingShown(this);
+ isFloatingShown = PrefUtils.getFloatingShown(this);
updateButtonText();
LocalBroadcastManager.getInstance(this).registerReceiver(
floatingReceiver, new IntentFilter(ACTION_FLOATING_STATE_CHANGED));
- binding.singleButton.setOnClickListener(v -> onToggleFloatingWindowClicked());
+ binding.singleButton.setOnClickListener(v -> onToggleFloatingWindowClicked(FLOATING_SINGLE));
+ binding.moreButton.setOnClickListener(v -> onToggleFloatingWindowClicked(FLOATING_MULTI));
permissionLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> checkPermissions());
@@ -120,24 +131,24 @@ public class MainActivity extends AppCompatActivity {
}
private void syncServiceState() {
- isFloatingShown = isServiceRunning();
+ isFloatingShown = getFloatingMode();
updateButtonText();
}
- private boolean isServiceRunning() {
+ private int getFloatingMode() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (ForegroundService.class.getName().equals(service.service.getClassName())) {
- return true;
+ return PrefUtils.getFloatingShown(this);
}
}
- return false;
+ return 0;
}
- public void onToggleFloatingWindowClicked() {
+ public void onToggleFloatingWindowClicked(int requestedType) {
long currentTime = System.currentTimeMillis();
if (currentTime - lastClickTime < DEBOUNCE_INTERVAL) {
- Toast.makeText(this, "点击太频繁", Toast.LENGTH_LONG).show();
+ showDebounceToast();
return;
}
lastClickTime = currentTime;
@@ -148,20 +159,28 @@ public class MainActivity extends AppCompatActivity {
return;
}
- if (isFloatingShown) {
+ if (isFloatingShown != FLOATING_NONE && isFloatingShown != requestedType) {
+ Toast.makeText(this, "已显示其他悬浮窗,请先关闭", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ if (isFloatingShown == requestedType) {
stopService(new Intent(this, ForegroundService.class));
AutoClickService.getInstance().stopClicking();
- isFloatingShown = false;
+ isFloatingShown = FLOATING_NONE;
logDebug("悬浮窗已经隐藏");
} else {
Intent serviceIntent = new Intent(this, ForegroundService.class);
+ serviceIntent.putExtra("FLOATING_TYPE", requestedType);
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent);
} else {
startService(serviceIntent);
}
- isFloatingShown = true;
- logDebug("悬浮窗已经显示");
+
+ isFloatingShown = requestedType;
+ logDebug("悬浮窗已显示: " + requestedType);
}
PrefUtils.setFloatingShown(this, isFloatingShown);
@@ -169,14 +188,14 @@ public class MainActivity extends AppCompatActivity {
updateButtonText();
}
- private void broadcastFloatingState(boolean isShown) {
+ private void broadcastFloatingState(int isShown) {
Intent intent = new Intent(ACTION_FLOATING_STATE_CHANGED);
intent.putExtra("isShown", isShown);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
private void updateButtonText() {
- binding.singleButton.setText(isFloatingShown ? "隐藏悬浮窗" : "显示悬浮窗");
+ binding.singleButton.setText(isFloatingShown == 1 ? "隐藏悬浮窗" : "显示悬浮窗");
}
@Override
@@ -185,6 +204,15 @@ public class MainActivity extends AppCompatActivity {
LocalBroadcastManager.getInstance(this).unregisterReceiver(floatingReceiver);
}
+ private void showDebounceToast() {
+ if (debounceToast == null) {
+ debounceToast = Toast.makeText(this, "点击太频繁", Toast.LENGTH_SHORT);
+ } else {
+ debounceToast.setText("点击太频繁");
+ }
+ debounceToast.show();
+ }
+
private void logDebug(String message) {
Log.d(TAG, message);
}
diff --git a/app/src/main/java/com/auto/autoclicker/util/PrefUtils.java b/app/src/main/java/com/auto/autoclicker/util/PrefUtils.java
index 6401cb4..f261634 100644
--- a/app/src/main/java/com/auto/autoclicker/util/PrefUtils.java
+++ b/app/src/main/java/com/auto/autoclicker/util/PrefUtils.java
@@ -6,13 +6,13 @@ public class PrefUtils {
private static final String PREF_NAME = "app_prefs";
private static final String KEY_FLOATING_SHOWN = "floating_window_shown";
- public static void setFloatingShown(Context context, boolean shown) {
+ public static void setFloatingShown(Context context, int shown) {
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
- .edit().putBoolean(KEY_FLOATING_SHOWN, shown).apply();
+ .edit().putInt(KEY_FLOATING_SHOWN, shown).apply();
}
- public static boolean isFloatingShown(Context context) {
+ public static int getFloatingShown(Context context) {
return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
- .getBoolean(KEY_FLOATING_SHOWN, false);
+ .getInt(KEY_FLOATING_SHOWN, 0);
}
}
diff --git a/app/src/main/res/drawable/pause.xml b/app/src/main/res/drawable/pause.xml
new file mode 100644
index 0000000..09624dd
--- /dev/null
+++ b/app/src/main/res/drawable/pause.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/layout/more_control_bar.xml b/app/src/main/res/layout/more_control_bar.xml
index e8ced22..53ac0dc 100644
--- a/app/src/main/res/layout/more_control_bar.xml
+++ b/app/src/main/res/layout/more_control_bar.xml
@@ -16,14 +16,14 @@
android:src="@drawable/play" />