From e5f52aaba39cce6588ceb24661bf5c14a56edfe0 Mon Sep 17 00:00:00 2001 From: lihongwei Date: Wed, 23 Apr 2025 13:54:09 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0=E5=8D=95?= =?UTF-8?q?=E7=82=B9=E7=82=B9=E5=87=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 2 +- .../auto/autoclicker/AutoClickService.java | 156 ++++++------------ .../auto/autoclicker/FloatingViewManager.java | 123 +++++++------- .../auto/autoclicker/ForegroundService.java | 4 - .../com/auto/autoclicker/MainActivity.java | 70 +++----- .../com/auto/autoclicker/ScreenUtils.java | 14 ++ .../java/com/auto/autoclicker/ViewUtils.java | 13 ++ .../drawable/{shoot.xml => touch_point.xml} | 2 +- app/src/main/res/drawable/un_touch_point.xml | 9 + .../res/xml/accessibility_service_config.xml | 3 +- 10 files changed, 176 insertions(+), 220 deletions(-) create mode 100644 app/src/main/java/com/auto/autoclicker/ScreenUtils.java create mode 100644 app/src/main/java/com/auto/autoclicker/ViewUtils.java rename app/src/main/res/drawable/{shoot.xml => touch_point.xml} (94%) create mode 100644 app/src/main/res/drawable/un_touch_point.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ff0d066..23d746f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -8,7 +8,7 @@ android { defaultConfig { applicationId = "com.auto.autoclicker" - minSdk = 23 + minSdk = 24 targetSdk = 35 versionCode = 1 versionName = "1.0.0" diff --git a/app/src/main/java/com/auto/autoclicker/AutoClickService.java b/app/src/main/java/com/auto/autoclicker/AutoClickService.java index ca2b55b..134f5a7 100644 --- a/app/src/main/java/com/auto/autoclicker/AutoClickService.java +++ b/app/src/main/java/com/auto/autoclicker/AutoClickService.java @@ -2,119 +2,103 @@ package com.auto.autoclicker; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.GestureDescription; +import android.content.Intent; import android.graphics.Path; -import android.os.Build; +import android.graphics.Point; import android.os.Handler; import android.os.Looper; -import android.util.DisplayMetrics; import android.util.Log; import android.view.accessibility.AccessibilityEvent; -/** - * 无障碍服务,执行自动点击操作 - */ +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + public class AutoClickService extends AccessibilityService { private static final String TAG = "AutoClickService"; - private static volatile AutoClickService instance; + private static final long MIN_CLICK_INTERVAL = 100; + private static final long MAX_CLICK_INTERVAL = 10000; + private static final int MIN_CLICK_DURATION = 50; + private static final int MAX_CLICK_DURATION = 1000; private final Handler handler = new Handler(Looper.getMainLooper()); private boolean isClicking = false; private int clickX = 500; private int clickY = 500; - private long clickInterval = 1000; // 默认1秒间隔 - private int clickDuration = 200; // 默认200毫秒点击时长 - private int screenWidth, screenHeight; + private long clickInterval = 1000; + private int clickDuration = 200; + private int screenWidth; + private int screenHeight; @Override public void onCreate() { super.onCreate(); - instance = this; - // 获取屏幕尺寸 - DisplayMetrics metrics = getResources().getDisplayMetrics(); - screenWidth = metrics.widthPixels; - screenHeight = metrics.heightPixels; - Log.d(TAG, "屏幕尺寸: " + screenWidth + "x" + screenHeight); + Point screenSize = ScreenUtils.getScreenSize(this); + screenWidth = screenSize.x; + screenHeight = screenSize.y; + logDebug("屏幕尺寸: " + screenWidth + "x" + screenHeight); } public static AutoClickService getInstance() { - return instance; + return null; } - /** - * 设置点击位置 - */ public void setClickPosition(float x, float y) { - this.clickX = (int) Math.max(0, Math.min(x, screenWidth)); - this.clickY = (int) Math.max(0, Math.min(y, screenHeight)); - Log.d(TAG, "点击位置设置为: (" + clickX + ", " + clickY + ")"); - Log.d(TAG, "设置点击位置为: (" + clickX + ", " + clickY + "), 屏幕尺寸: " + screenWidth + "x" + screenHeight); + Point constrainedPoint = ViewUtils.constrainToScreen(x, y, screenWidth, screenHeight); + this.clickX = constrainedPoint.x; + this.clickY = constrainedPoint.y; + logDebug("设置点击位置: x=" + x + ", y=" + y + ", 修正后: (" + clickX + ", " + clickY + ")"); } - /** - * 设置点击间隔 - */ public void setClickInterval(long interval) { - if (interval > 0) { - this.clickInterval = interval; - Log.d(TAG, "点击间隔设置为: " + interval + "ms"); + if (interval < MIN_CLICK_INTERVAL || interval > MAX_CLICK_INTERVAL) { + Log.w(TAG, "点击间隔超出合理范围: " + interval + "ms"); + return; } + this.clickInterval = interval; + logDebug("点击间隔设置为: " + interval + "ms"); } - /** - * 设置点击时长 - */ public void setClickDuration(int duration) { - if (duration > 0) { - this.clickDuration = duration; - Log.d(TAG, "点击时长设置为: " + duration + "ms"); + if (duration < MIN_CLICK_DURATION || duration > MAX_CLICK_DURATION) { + Log.w(TAG, "点击时长超出合理范围: " + duration + "ms"); + return; } + this.clickDuration = duration; + logDebug("点击时长设置为: " + duration + "ms"); } - /** - * 开始自动点击 - */ public void startClicking() { if (!isClicking) { isClicking = true; performClick(); - Log.d(TAG, "开始点击: (" + clickX + ", " + clickY + ")"); + logDebug("开始点击: (" + clickX + ", " + clickY + ")"); } } - /** - * 停止自动点击 - */ public void stopClicking() { if (isClicking) { isClicking = false; handler.removeCallbacksAndMessages(null); - Log.d(TAG, "停止点击"); + logDebug("停止点击"); } } - /** - * 执行单次点击 - */ private void performClick() { - if (!isClicking || Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { - Log.w(TAG, "点击被跳过(未开始或系统版本不支持)"); + if (!isClicking) { + logDebug("点击被跳过(未开始)"); return; } + logDebug("执行点击: (" + clickX + ", " + clickY + ")"); Path path = new Path(); path.moveTo(clickX, clickY); - path.lineTo(clickX, clickY); // 创建一个点状路径 - GestureDescription.StrokeDescription stroke = - new GestureDescription.StrokeDescription(path, 0, 10); // 10ms 点击 - + new GestureDescription.StrokeDescription(path, 0, clickDuration); GestureDescription gesture = new GestureDescription.Builder().addStroke(stroke).build(); dispatchGesture(gesture, new GestureResultCallback() { @Override public void onCompleted(GestureDescription gestureDescription) { - super.onCompleted(gestureDescription); - Log.i(TAG, "点击完成: (" + clickX + ", " + clickY + ")"); + logDebug("点击完成: (" + clickX + ", " + clickY + ")"); if (isClicking) { handler.postDelayed(() -> performClick(), clickInterval); } @@ -122,78 +106,44 @@ public class AutoClickService extends AccessibilityService { @Override public void onCancelled(GestureDescription gestureDescription) { - super.onCancelled(gestureDescription); - Log.e(TAG, "点击取消(可能位置错误或界面变化): (" + clickX + ", " + clickY + ")"); + Log.e(TAG, "点击取消: (" + clickX + ", " + clickY + ")"); if (isClicking) { - handler.postDelayed(() -> performClick(), clickInterval + 300); // 稍微延迟重试 + handler.postDelayed(() -> performClick(), clickInterval + 300); } } }, null); } - public void testSingleClick(float x, float y) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { - Log.e(TAG, "设备不支持手势(需要 API 24+)"); - return; - } - - try { - Path path = new Path(); - path.moveTo(x, y); - path.lineTo(x, y); // 明确指定按下和抬起 - Log.d(TAG, "测试点击: (" + x + ", " + y + ")"); - - GestureDescription.StrokeDescription stroke = new GestureDescription.StrokeDescription( - path, 0, 100 - ); - GestureDescription gesture = new GestureDescription.Builder() - .addStroke(stroke) - .build(); - - boolean dispatched = dispatchGesture(gesture, new GestureResultCallback() { - @Override - public void onCompleted(GestureDescription gestureDescription) { - Log.v(TAG, "测试点击完成: (" + x + ", " + y + ")"); - } - - @Override - public void onCancelled(GestureDescription gestureDescription) { - Log.w(TAG, "测试点击取消: (" + x + ", " + y + ")"); - } - }, null); - - Log.d(TAG, "手势分发结果: " + (dispatched ? "成功" : "失败")); - } catch (Exception e) { - Log.e(TAG, "测试点击失败", e); - } - } - @Override public void onAccessibilityEvent(AccessibilityEvent event) { - // 不处理事件 } @Override public void onInterrupt() { stopClicking(); - Log.d(TAG, "无障碍服务中断"); + logDebug("无障碍服务中断"); } @Override protected void onServiceConnected() { super.onServiceConnected(); - Log.d(TAG, "无障碍服务已连接"); - } - - public boolean isClicking() { - return isClicking; + logDebug("无障碍服务已连接"); } @Override public void onDestroy() { super.onDestroy(); stopClicking(); - instance = null; - Log.d(TAG, "无障碍服务已销毁"); + Intent intent = new Intent("com.auto.autoclicker.SERVICE_DESTROYED"); + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + logDebug("无障碍服务已销毁"); + } + + public boolean isClicking() { + return isClicking; + } + + 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/FloatingViewManager.java b/app/src/main/java/com/auto/autoclicker/FloatingViewManager.java index 75d80e8..6fd8bef 100644 --- a/app/src/main/java/com/auto/autoclicker/FloatingViewManager.java +++ b/app/src/main/java/com/auto/autoclicker/FloatingViewManager.java @@ -1,11 +1,11 @@ package com.auto.autoclicker; import android.content.Context; -import android.graphics.Color; +import android.content.Intent; import android.graphics.PixelFormat; +import android.graphics.Point; import android.os.Build; import android.provider.Settings; -import android.util.DisplayMetrics; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -16,11 +16,9 @@ import android.widget.Button; import android.widget.LinearLayout; import android.widget.Toast; -/** - * 悬浮窗管理器,负责触摸点和控制栏的显示与交互 - */ public class FloatingViewManager { private static final String TAG = "FloatingViewManager"; + private static final long DEBOUNCE_INTERVAL = 500; private final Context context; private final WindowManager windowManager; private View touchPointView; @@ -30,32 +28,28 @@ public class FloatingViewManager { private float touchPointX = 500; private float touchPointY = 500; private boolean isClicking = false; - private int screenWidth, screenHeight; + private boolean isFloatingViewsShown = false; private long lastToggleTime = 0; - private static final long DEBOUNCE_INTERVAL = 500; - private static final int CONTROL_BAR_WIDTH = 200; // 假设控制栏宽度 - private static final int CONTROL_BAR_HEIGHT = 100; // 假设控制栏高度 + private final int screenWidth; + private final int screenHeight; public FloatingViewManager(Context context) { this.context = context; this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - screenWidth = metrics.widthPixels; - screenHeight = metrics.heightPixels; - Log.d(TAG, "屏幕尺寸: " + screenWidth + "x" + screenHeight); + Point screenSize = ScreenUtils.getScreenSize(context); + this.screenWidth = screenSize.x; + this.screenHeight = screenSize.y; + logDebug("屏幕尺寸: " + screenWidth + "x" + screenHeight); } - /** - * 显示悬浮窗(触摸点和控制栏) - */ public void showFloatingViews() throws SecurityException { if (!Settings.canDrawOverlays(context)) { throw new SecurityException("需要悬浮窗权限"); } - if (touchPointView != null || controlBarView != null) { - Log.w(TAG, "悬浮窗已存在,跳过创建"); - return; + if (isFloatingViewsShown) { + logDebug("悬浮窗已存在,清理后重新创建"); + removeFloatingViews(); } initializeTouchPointView(); @@ -63,15 +57,13 @@ public class FloatingViewManager { windowManager.addView(touchPointView, touchPointParams); windowManager.addView(controlBarView, controlBarParams); - Log.d(TAG, "悬浮窗已添加"); + isFloatingViewsShown = true; + logDebug("悬浮窗已添加"); } - /** - * 初始化触摸点视图 - */ private void initializeTouchPointView() { touchPointView = new View(context); - touchPointView.setBackgroundResource(R.drawable.shoot); // 使用触摸点图标 + touchPointView.setBackgroundResource(R.drawable.un_touch_point); touchPointParams = new WindowManager.LayoutParams( 100, 100, Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? @@ -81,7 +73,7 @@ public class FloatingViewManager { WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT ); - touchPointParams.gravity = Gravity.TOP | Gravity.LEFT; + touchPointParams.gravity = Gravity.TOP | Gravity.START; touchPointParams.x = (int) touchPointX; touchPointParams.y = (int) touchPointY; @@ -101,15 +93,11 @@ public class FloatingViewManager { case MotionEvent.ACTION_MOVE: float dx = event.getRawX() - lastX; float dy = event.getRawY() - lastY; - touchPointParams.x = (int) Math.max(0, Math.min(paramX + dx, screenWidth - 100)); - touchPointParams.y = (int) Math.max(0, Math.min(paramY + dy, screenHeight - 100)); - touchPointX = touchPointParams.x; - touchPointY = touchPointParams.y; - windowManager.updateViewLayout(touchPointView, touchPointParams); + updateTouchPointPosition(paramX, paramY, dx, dy); AutoClickService service = AutoClickService.getInstance(); if (service != null) { service.setClickPosition(touchPointX + 50, touchPointY + 50); - Log.d(TAG, "触摸点移动到: (" + touchPointX + ", " + touchPointY + ")"); + logDebug("触摸点移动到: (" + touchPointX + ", " + touchPointY + ")"); } break; } @@ -118,9 +106,16 @@ public class FloatingViewManager { }); } - /** - * 初始化控制栏视图 - */ + 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); + } + private void initializeControlBarView() { controlBarView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.control_bar, null); controlBarParams = new WindowManager.LayoutParams( @@ -133,11 +128,10 @@ public class FloatingViewManager { WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT ); - controlBarParams.gravity = Gravity.TOP | Gravity.LEFT; + controlBarParams.gravity = Gravity.TOP | Gravity.START; controlBarParams.x = 0; controlBarParams.y = 200; - // 替换 controlBarView 的 onTouchListener 和 toggleButton 的 OnClickListener controlBarView.setOnTouchListener(new View.OnTouchListener() { private float lastX, lastY; private float paramX, paramY; @@ -154,9 +148,7 @@ public class FloatingViewManager { case MotionEvent.ACTION_MOVE: float dx = event.getRawX() - lastX; float dy = event.getRawY() - lastY; - controlBarParams.x = (int) Math.max(0, Math.min(paramX + dx, screenWidth - CONTROL_BAR_WIDTH)); - controlBarParams.y = (int) Math.max(0, Math.min(paramY + dy, screenHeight - CONTROL_BAR_HEIGHT)); - windowManager.updateViewLayout(controlBarView, controlBarParams); + updateControlBarPosition(paramX, paramY, dx, dy); return true; } return false; @@ -165,50 +157,55 @@ public class FloatingViewManager { Button toggleButton = controlBarView.findViewById(R.id.toggle_button); toggleButton.setOnClickListener(v -> { - long currentTime = System.currentTimeMillis(); - if (currentTime - lastToggleTime < DEBOUNCE_INTERVAL) { + if (!isDebounced()) { return; } - lastToggleTime = currentTime; AutoClickService service = AutoClickService.getInstance(); if (service == null) { Log.e(TAG, "AutoClickService 未初始化"); - Toast.makeText(context, "请启用无障碍服务", Toast.LENGTH_SHORT).show(); + 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(); toggleButton.setText(R.string.start_click); - touchPointView.setBackgroundResource(R.drawable.shoot); + touchPointView.setBackgroundResource(R.drawable.un_touch_point); Toast.makeText(context, "停止自动点击", Toast.LENGTH_SHORT).show(); } else { service.setClickPosition(touchPointX + 50, touchPointY + 50); service.startClicking(); toggleButton.setText(R.string.stop_click); - touchPointView.setBackgroundColor(Color.GREEN); + touchPointView.setBackgroundResource(R.drawable.touch_point); Toast.makeText(context, "开始自动点击", Toast.LENGTH_SHORT).show(); } isClicking = !isClicking; - - // 测试单次点击(屏幕中央) - float testX = screenWidth / 2f; - float testY = screenHeight / 2f; - service.testSingleClick(testX, testY); - Toast.makeText(context, "测试点击: (" + testX + ", " + testY + ")", Toast.LENGTH_SHORT).show(); - - Log.d(TAG, "AutoClickService 实例:" + service); - Log.d(TAG, "服务是否点击中:" + service.isClicking()); - + logDebug("服务是否点击中: " + service.isClicking()); }); - } - /** - * 移除悬浮窗 - */ + private void updateControlBarPosition(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); + } + + private boolean isDebounced() { + long currentTime = System.currentTimeMillis(); + if (currentTime - lastToggleTime < DEBOUNCE_INTERVAL) { + return false; + } + lastToggleTime = currentTime; + return true; + } + public void removeFloatingViews() { try { if (touchPointView != null) { @@ -219,9 +216,15 @@ public class FloatingViewManager { windowManager.removeView(controlBarView); controlBarView = null; } - Log.d(TAG, "悬浮窗已移除"); + isFloatingViewsShown = false; + logDebug("悬浮窗已移除"); } catch (Exception e) { Log.e(TAG, "移除悬浮窗失败", e); + isFloatingViewsShown = 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 1d651c4..e20a5df 100644 --- a/app/src/main/java/com/auto/autoclicker/ForegroundService.java +++ b/app/src/main/java/com/auto/autoclicker/ForegroundService.java @@ -12,9 +12,6 @@ import android.util.Log; import androidx.core.app.NotificationCompat; -/** - * 前台服务,管理自动点击的悬浮窗和通知 - */ public class ForegroundService extends Service { private static final String TAG = "ForegroundService"; private static final String CHANNEL_ID = "AutoClickerChannel"; @@ -36,7 +33,6 @@ 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 diff --git a/app/src/main/java/com/auto/autoclicker/MainActivity.java b/app/src/main/java/com/auto/autoclicker/MainActivity.java index c23c05a..790828c 100644 --- a/app/src/main/java/com/auto/autoclicker/MainActivity.java +++ b/app/src/main/java/com/auto/autoclicker/MainActivity.java @@ -15,16 +15,13 @@ import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.appcompat.app.AppCompatActivity; -/** - * 主活动,负责权限检查和控制悬浮窗显示/隐藏 - */ + public class MainActivity extends AppCompatActivity { - private static final String TAG = "MainActivity"; private Button startButton; private ActivityResultLauncher permissionLauncher; private boolean isFloatingShown = false; private long lastClickTime = 0; - private static final long DEBOUNCE_INTERVAL = 500; // 防抖间隔(毫秒) + private static final long DEBOUNCE_INTERVAL = 500; @Override protected void onCreate(Bundle savedInstanceState) { @@ -34,22 +31,16 @@ public class MainActivity extends AppCompatActivity { startButton = findViewById(R.id.start_Button); startButton.setOnClickListener(v -> toggleFloatingWindow()); - // 初始化权限请求回调 permissionLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> checkPermissions()); - // 同步服务状态 syncServiceState(); checkPermissions(); } - /** - * 检查必要的权限:无障碍服务、悬浮窗、电池优化 - */ private void checkPermissions() { boolean allPermissionsGranted = true; - // 检查无障碍服务 - if (!isAccessibilityServiceEnabled()) { + if (isAccessibilityServiceEnabled()) { allPermissionsGranted = false; try { Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); @@ -60,8 +51,7 @@ public class MainActivity extends AppCompatActivity { } } - // 检查悬浮窗权限 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) { + if (!Settings.canDrawOverlays(this)) { allPermissionsGranted = false; try { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); @@ -72,30 +62,23 @@ public class MainActivity extends AppCompatActivity { } } - // 检查电池优化 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - if (!pm.isIgnoringBatteryOptimizations(getPackageName())) { - allPermissionsGranted = false; - try { - Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); - intent.setData(Uri.parse("package:" + getPackageName())); - permissionLauncher.launch(intent); - Toast.makeText(this, "请禁用电池优化", Toast.LENGTH_LONG).show(); - } catch (Exception e) { - Toast.makeText(this, "无法打开电池优化设置", Toast.LENGTH_LONG).show(); - } + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + if (!pm.isIgnoringBatteryOptimizations(getPackageName())) { + allPermissionsGranted = false; + try { + Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + intent.setData(Uri.parse("package:" + getPackageName())); + permissionLauncher.launch(intent); + Toast.makeText(this, "请禁用电池优化", Toast.LENGTH_LONG).show(); + } catch (Exception e) { + Toast.makeText(this, "无法打开电池优化设置", Toast.LENGTH_LONG).show(); } } - // 更新按钮状态 startButton.setEnabled(allPermissionsGranted); updateButtonText(); } - /** - * 检查无障碍服务是否启用 - */ private boolean isAccessibilityServiceEnabled() { String service = getPackageName() + "/" + AutoClickService.class.getCanonicalName(); try { @@ -106,47 +89,37 @@ public class MainActivity extends AppCompatActivity { String settingValue = Settings.Secure.getString( getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); - return settingValue != null && settingValue.contains(service); + return settingValue == null || !settingValue.contains(service); } } catch (Settings.SettingNotFoundException e) { e.printStackTrace(); } - return false; + return true; } - /** - * 同步服务状态(从通知栏进入时) - */ private void syncServiceState() { - isFloatingShown = isServiceRunning(ForegroundService.class); + isFloatingShown = isServiceRunning(); updateButtonText(); } - /** - * 检查服务是否运行 - */ - private boolean isServiceRunning(Class serviceClass) { + private boolean isServiceRunning() { ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { - if (serviceClass.getName().equals(service.service.getClassName())) { + if (ForegroundService.class.getName().equals(service.service.getClassName())) { return true; } } return false; } - /** - * 切换悬浮窗显示/隐藏 - */ private void toggleFloatingWindow() { - // 防抖处理 long currentTime = System.currentTimeMillis(); if (currentTime - lastClickTime < DEBOUNCE_INTERVAL) { return; } lastClickTime = currentTime; - if (!isAccessibilityServiceEnabled() || !Settings.canDrawOverlays(this)) { + if (isAccessibilityServiceEnabled() || !Settings.canDrawOverlays(this)) { Toast.makeText(this, "请授予所有必要权限", Toast.LENGTH_LONG).show(); checkPermissions(); return; @@ -169,9 +142,6 @@ public class MainActivity extends AppCompatActivity { updateButtonText(); } - /** - * 更新按钮文本 - */ private void updateButtonText() { startButton.setText(isFloatingShown ? R.string.hide_floating_window : R.string.show_floating_window); } diff --git a/app/src/main/java/com/auto/autoclicker/ScreenUtils.java b/app/src/main/java/com/auto/autoclicker/ScreenUtils.java new file mode 100644 index 0000000..5a847b4 --- /dev/null +++ b/app/src/main/java/com/auto/autoclicker/ScreenUtils.java @@ -0,0 +1,14 @@ +package com.auto.autoclicker; + +import android.content.Context; +import android.graphics.Point; +import android.util.DisplayMetrics; + + +public class ScreenUtils { + + public static Point getScreenSize(Context context) { + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + return new Point(metrics.widthPixels, metrics.heightPixels); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/autoclicker/ViewUtils.java b/app/src/main/java/com/auto/autoclicker/ViewUtils.java new file mode 100644 index 0000000..ac13359 --- /dev/null +++ b/app/src/main/java/com/auto/autoclicker/ViewUtils.java @@ -0,0 +1,13 @@ +package com.auto.autoclicker; + +import android.graphics.Point; + + +public class ViewUtils { + + public static Point constrainToScreen(float x, float y, int maxWidth, int maxHeight) { + int constrainedX = (int) Math.max(0, Math.min(x, maxWidth)); + int constrainedY = (int) Math.max(0, Math.min(y, maxHeight)); + return new Point(constrainedX, constrainedY); + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/shoot.xml b/app/src/main/res/drawable/touch_point.xml similarity index 94% rename from app/src/main/res/drawable/shoot.xml rename to app/src/main/res/drawable/touch_point.xml index 4d72eb8..53630de 100644 --- a/app/src/main/res/drawable/shoot.xml +++ b/app/src/main/res/drawable/touch_point.xml @@ -5,5 +5,5 @@ android:viewportHeight="1024"> + android:fillColor="#FF0057"/> diff --git a/app/src/main/res/drawable/un_touch_point.xml b/app/src/main/res/drawable/un_touch_point.xml new file mode 100644 index 0000000..8f80d5c --- /dev/null +++ b/app/src/main/res/drawable/un_touch_point.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/xml/accessibility_service_config.xml b/app/src/main/res/xml/accessibility_service_config.xml index 46c4d6d..88dd349 100644 --- a/app/src/main/res/xml/accessibility_service_config.xml +++ b/app/src/main/res/xml/accessibility_service_config.xml @@ -1,8 +1,9 @@ \ No newline at end of file