功能优化

This commit is contained in:
lihongwei 2025-06-25 15:01:57 +08:00
parent 30688c11c2
commit 6693521d11
6 changed files with 313 additions and 353 deletions

View File

@ -87,7 +87,7 @@ public class ForegroundService extends Service {
public void onDestroy() {
super.onDestroy();
if (floatingViewManager != null) {
floatingViewManager.removeFloatingViews();
floatingViewManager.removeAllFloatingViews();
isFloatingViewShown = false;
Log.d(TAG, "悬浮窗已移除");
}

View File

@ -1,4 +1,4 @@
package com.auto.clicker.autoclicker.ui.floating;
package com.auto.clicker.autoclicker.ui.adapter.floating;
import android.view.LayoutInflater;
import android.view.View;
@ -16,7 +16,6 @@ public class SolutionAdapter extends RecyclerView.Adapter<SolutionAdapter.Soluti
private List<Solution> solutionList;
private OnSolutionClickListener listener;
// 定义接口用于处理点击事件
public interface OnSolutionClickListener {
void onSolutionClick(Solution solution);
}
@ -30,7 +29,7 @@ public class SolutionAdapter extends RecyclerView.Adapter<SolutionAdapter.Soluti
@Override
public SolutionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(android.R.layout.simple_list_item_1, parent, false); // 可以自定义更复杂的item布局
.inflate(android.R.layout.simple_list_item_1, parent, false);
return new SolutionViewHolder(view);
}
@ -64,7 +63,6 @@ public class SolutionAdapter extends RecyclerView.Adapter<SolutionAdapter.Soluti
SolutionViewHolder(View itemView) {
super(itemView);
// simple_list_item_1 只有一个 TextView
solutionNameTextView = itemView.findViewById(android.R.id.text1);
}
}

View File

@ -0,0 +1,61 @@
package com.auto.clicker.autoclicker.util;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
public class DraggableHelper {
public interface DragCallback {
void onPositionChanged(int x, int y);
}
public static void makeDraggable(View view,
WindowManager.LayoutParams params,
WindowManager windowManager,
int screenWidth, int screenHeight,
DragCallback callback) {
view.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 = params.x;
paramY = params.y;
return true;
case MotionEvent.ACTION_MOVE:
float dx = event.getRawX() - lastX;
float dy = event.getRawY() - lastY;
int newX = (int) (paramX + dx);
int newY = (int) (paramY + dy);
newX = Math.max(0, Math.min(newX, screenWidth - view.getWidth()));
newY = Math.max(0, Math.min(newY, screenHeight - view.getHeight()));
params.x = newX;
params.y = newY;
windowManager.updateViewLayout(view, params);
if (callback != null) {
callback.onPositionChanged(newX, newY);
}
return true;
case MotionEvent.ACTION_UP:
v.performClick();
return true;
}
return false;
}
});
}
}

View File

@ -0,0 +1,166 @@
package com.auto.clicker.autoclicker.util;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Build;
import android.util.Log;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.TextView;
import com.auto.clicker.autoclicker.R;
import com.auto.clicker.autoclicker.data.PointEvent;
import com.auto.clicker.autoclicker.data.SlideEvent;
import com.auto.clicker.autoclicker.view.ConnectingLineView;
public class EventViewBinder {
public interface OnPositionChangedCallback {
void onPositionChanged(int x, int y);
}
public static void bindPoint(Context context, WindowManager windowManager,
PointEvent pointEvent, int touchPointSize,
int screenWidth, int screenHeight,
OnPositionChangedCallback callback,int order) {
TextView pointView = createTouchPointView(context, String.valueOf(order));
Log.d("bind","id: " + order);
WindowManager.LayoutParams params = createTouchPointParams(context, touchPointSize);
params.x = pointEvent.getX();
params.y = pointEvent.getY();
pointEvent.setView(pointView);
pointEvent.setLayoutParams(params);
DraggableHelper.makeDraggable(pointView, params, windowManager,
screenWidth, screenHeight,
(x, y) -> {
pointEvent.setX(x);
pointEvent.setY(y);
if (callback != null) callback.onPositionChanged(x, y);
});
windowManager.addView(pointView, params);
}
public static void bindSlide(Context context, WindowManager windowManager,
SlideEvent slideEvent, int touchPointSize,
int screenWidth, int screenHeight,
OnPositionChangedCallback callback,int order) {
PointEvent start = slideEvent.getStartPoint();
PointEvent end = slideEvent.getEndPoint();
TextView startView = createTouchPointView(context, String.valueOf(order));
TextView endView = createTouchPointView(context, String.valueOf(order));
WindowManager.LayoutParams startParams = createTouchPointParams(context, touchPointSize);
WindowManager.LayoutParams endParams = createTouchPointParams(context, touchPointSize);
startParams.x = start.getX();
startParams.y = start.getY();
endParams.x = end.getX();
endParams.y = end.getY();
start.setView(startView);
start.setLayoutParams(startParams);
end.setView(endView);
end.setLayoutParams(endParams);
ConnectingLineView lineView = new ConnectingLineView(context);
WindowManager.LayoutParams lineParams = createLineViewParams(context);
slideEvent.setLineView(lineView);
slideEvent.setLineParams(lineParams);
// 拖动起点
DraggableHelper.makeDraggable(startView, startParams, windowManager,
screenWidth, screenHeight,
(x, y) -> {
start.setX(x);
start.setY(y);
updateLinePosition(slideEvent, touchPointSize);
if (callback != null) callback.onPositionChanged(x, y);
});
// 拖动终点
DraggableHelper.makeDraggable(endView, endParams, windowManager,
screenWidth, screenHeight,
(x, y) -> {
end.setX(x);
end.setY(y);
updateLinePosition(slideEvent, touchPointSize);
if (callback != null) callback.onPositionChanged(x, y);
});
windowManager.addView(lineView, lineParams);
windowManager.addView(startView, startParams);
windowManager.addView(endView, endParams);
updateLinePosition(slideEvent, touchPointSize);
}
public static TextView createTouchPointView(Context context, String label) {
TextView touchPointView = new TextView(context);
touchPointView.setBackgroundResource(R.drawable.ring_has_bg);
touchPointView.setGravity(Gravity.CENTER);
touchPointView.setText(label);
touchPointView.setTextColor(Color.WHITE);
touchPointView.setTextSize(19);
return touchPointView;
}
private static WindowManager.LayoutParams createTouchPointParams(Context context, int size) {
int overlayType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_PHONE;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
size, size,
overlayType,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT
);
params.gravity = Gravity.TOP | Gravity.START;
return params;
}
private static WindowManager.LayoutParams createLineViewParams(Context context) {
int overlayType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_PHONE;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
overlayType,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT
);
params.gravity = Gravity.TOP | Gravity.START;
params.x = 0;
params.y = 0;
return params;
}
private static void updateLinePosition(SlideEvent slideEvent, int touchPointSize) {
PointEvent start = slideEvent.getStartPoint();
PointEvent end = slideEvent.getEndPoint();
ConnectingLineView lineView = slideEvent.getLineView();
if (lineView == null) return;
float startCenterX = start.getX() + (float) touchPointSize / 2;
float startCenterY = start.getY() + (float) touchPointSize / 2;
float endCenterX = end.getX() + (float) touchPointSize / 2;
float endCenterY = end.getY() + (float) touchPointSize / 2;
lineView.setPoints(startCenterX, startCenterY, endCenterX, endCenterY);
}
}

View File

@ -1,4 +1,4 @@
package com.auto.clicker.autoclicker.util;
package com.auto.clicker.autoclicker.view;
import android.content.Context;
import android.graphics.PixelFormat;
@ -23,8 +23,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.auto.clicker.autoclicker.R;
import com.auto.clicker.autoclicker.room.entity.Solution;
import com.auto.clicker.autoclicker.room.repository.EventRepository;
import com.auto.clicker.autoclicker.ui.floating.SolutionAdapter;
import com.auto.clicker.autoclicker.view.FloatingViewManager;
import com.auto.clicker.autoclicker.ui.adapter.floating.SolutionAdapter;
import java.util.ArrayList;
import java.util.List;
@ -69,7 +68,7 @@ public class FloatingTabDialogManager {
Context themedContext = new ContextThemeWrapper(context, R.style.Theme_AutoClicker);
floatingDialogView = (LinearLayout) LayoutInflater.from(themedContext).inflate(R.layout.floating_tab_dialog, null);
floatingDialogParams = createFloatingDialogParams(); // 创建悬浮窗参数
floatingDialogParams = createFloatingDialogParams();
saveTab = floatingDialogView.findViewById(R.id.tab_one_button);
loadTab = floatingDialogView.findViewById(R.id.tab_two_button);
@ -105,7 +104,6 @@ public class FloatingTabDialogManager {
}
});
// 这个可能也是在后台线程保险起见也用主线程
showToastOnUi(context, "正在保存方案...");
}
@ -128,9 +126,9 @@ public class FloatingTabDialogManager {
});
loadRecyclerview.setAdapter(solutionsAdapter);
showPage(0); // 默认显示保存页面
showPage(0);
loadSolutionsForList(); // 加载方案列表以便切换到加载页面时可以直接显示
loadSolutionsForList();
try {
windowManager.addView(floatingDialogView, floatingDialogParams);
@ -150,22 +148,18 @@ public class FloatingTabDialogManager {
return;
}
// 隐藏所有页面
savePage.setVisibility(View.GONE);
loadPage.setVisibility(View.GONE);
// 重置Tab按钮颜色
saveTab.setTextColor(context.getResources().getColor(android.R.color.darker_gray));
loadTab.setTextColor(context.getResources().getColor(android.R.color.darker_gray));
switch (pageIndex) {
case 0: // 保存页面
case 0:
savePage.setVisibility(View.VISIBLE);
saveTab.setTextColor(context.getResources().getColor(android.R.color.black));
// 在显示保存页面时尝试显示键盘
showKeyboard();
break;
case 1: // 加载页面
case 1:
loadPage.setVisibility(View.VISIBLE);
loadTab.setTextColor(context.getResources().getColor(android.R.color.black));
loadSolutionsForList();
@ -176,7 +170,6 @@ public class FloatingTabDialogManager {
private void loadSolutionsForList() {
if (floatingViewManager == null) {
Log.e(TAG, "FloatingViewManager 未设置,无法加载方案列表。");
// 确保UI操作在主线程
new Handler(Looper.getMainLooper()).post(() ->
Toast.makeText(context, "加载方案列表功能不可用。", Toast.LENGTH_SHORT).show()
);
@ -185,7 +178,6 @@ public class FloatingTabDialogManager {
floatingViewManager.getAllSolutions(new EventRepository.RepositoryCallback<List<Solution>>() {
@Override
public void onComplete(List<Solution> solutions) {
// UI 更新操作包括更新适配器和显示Toast切换到主线程
new Handler(Looper.getMainLooper()).post(() -> {
solutionsAdapter.setSolutions(solutions);
if (solutions.isEmpty()) {
@ -196,7 +188,6 @@ public class FloatingTabDialogManager {
@Override
public void onError(Exception e) {
// UI 更新操作显示Toast切换到主线程
new Handler(Looper.getMainLooper()).post(() -> {
Log.e(TAG, "加载方案列表失败: " + e.getMessage(), e);
Toast.makeText(context, "加载方案列表失败。", Toast.LENGTH_SHORT).show();
@ -209,7 +200,7 @@ public class FloatingTabDialogManager {
if (floatingDialogView != null) {
try {
windowManager.removeView(floatingDialogView);
floatingDialogView = null; // 置空防止重复移除或空指针异常
floatingDialogView = null;
Log.d(TAG, "浮动标签页弹窗已移除");
} catch (IllegalArgumentException e) {
Log.w(TAG, "尝试移除不存在的视图", e);
@ -234,17 +225,6 @@ public class FloatingTabDialogManager {
return params;
}
private void showKeyboard() {
if (inputName != null) {
inputName.requestFocus(); // 确保输入框获取焦点
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
// 尝试强制显示键盘
imm.showSoftInput(inputName, InputMethodManager.SHOW_IMPLICIT);
}
}
}
public static void showToastOnUi(Context context, String msg) {
new Handler(Looper.getMainLooper()).post(() ->
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()

View File

@ -1,15 +1,12 @@
package com.auto.clicker.autoclicker.view;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@ -25,7 +22,6 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
@ -45,7 +41,7 @@ import com.auto.clicker.autoclicker.room.entity.TouchPoint;
import com.auto.clicker.autoclicker.room.repository.EventRepository;
import com.auto.clicker.autoclicker.service.AutoClickService;
import com.auto.clicker.autoclicker.service.ForegroundService;
import com.auto.clicker.autoclicker.util.FloatingTabDialogManager;
import com.auto.clicker.autoclicker.util.EventViewBinder;
import com.auto.clicker.autoclicker.util.PrefUtils;
import com.auto.clicker.autoclicker.util.ScreenUtils;
import com.auto.clicker.autoclicker.util.ViewUtils;
@ -74,9 +70,9 @@ public class FloatingViewManager {
private boolean areEventsVisible = true;
private FloatingTabDialogManager floatingTabDialogManager;
private final FloatingTabDialogManager floatingTabDialogManager;
private EventRepository eventRepository;
private final EventRepository eventRepository;
public FloatingViewManager(Context context) {
this.context = context;
@ -95,7 +91,7 @@ public class FloatingViewManager {
throw new SecurityException("需要悬浮窗许可");
}
removeFloatingViews();
removeAllFloatingViews();
showMultipleModeViews(mode);
Log.d(TAG, "添加悬浮窗, 模式 = " + mode);
@ -115,7 +111,6 @@ public class FloatingViewManager {
eventOrderCounter = 1;
}
@SuppressLint("ClickableViewAccessibility")
private void initMultipleControlBar() {
multipleControlBarView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.multiple_control_bar, null);
multipleControlBarParams = createControlBarParams();
@ -145,7 +140,6 @@ public class FloatingViewManager {
return params;
}
@SuppressLint("ClickableViewAccessibility")
private void setupDraggableView(View view, WindowManager.LayoutParams params, PositionUpdater positionUpdater) {
view.setOnTouchListener(new View.OnTouchListener() {
private float lastX, lastY;
@ -216,18 +210,7 @@ public class FloatingViewManager {
slideButton.setOnClickListener(v -> addSlideEvent());
saveButton.setOnClickListener(v -> {
// 直接调用 FloatingTabDialogManager 来显示悬浮弹窗
// 记得要检查悬浮窗权限
if (!Settings.canDrawOverlays(context)) {
Toast.makeText(context, "需要悬浮窗权限才能显示弹窗", Toast.LENGTH_LONG).show();
// 引导用户去开启权限
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + context.getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} else {
floatingTabDialogManager.showFloatingTabDialog();
}
floatingTabDialogManager.showFloatingTabDialog();
});
eyeButton.setOnClickListener(v -> {
@ -239,6 +222,7 @@ public class FloatingViewManager {
eyeButton.setImageResource(R.drawable.hide_eye);
}
});
settingButton.setOnClickListener(v -> showInputDialog());
closeButton.setOnClickListener(v -> closeFloatingViews());
}
@ -248,7 +232,7 @@ public class FloatingViewManager {
AutoClickService service = AutoClickService.getInstance();
if (service == null) {
handleMissingService(); // 无障碍服务未开启
handleMissingService();
return;
}
@ -258,95 +242,48 @@ public class FloatingViewManager {
}
if (isMultipleRunning) {
stopMultiClicking(service); // 停止点击
stopMultiClicking(service);
} else {
startMultiClicking(service); // 开始点击
startMultiClicking(service);
}
isMultipleRunning = !isMultipleRunning;
Log.d(TAG, "多点点击服务状态: " + service.isClicking());
}
@SuppressLint("ClickableViewAccessibility")
private void addPointEvent() {
if (isMultipleRunning) {
Toast.makeText(context, "请停止点击", Toast.LENGTH_SHORT).show();
return;
}
// 创建点击点视图
TextView pointView = createTouchPointView(String.valueOf(eventOrderCounter));
PointEvent pointEvent = new PointEvent(eventOrderCounter, screenWidth / 2 - TOUCH_POINT_SIZE / 2,
screenHeight / 2 - TOUCH_POINT_SIZE / 2);
// 设置位置居中
WindowManager.LayoutParams params = createTouchPointParams();
params.x = screenWidth / 2 - TOUCH_POINT_SIZE / 2;
params.y = screenHeight / 2 - TOUCH_POINT_SIZE / 2;
// 包装为事件对象
PointEvent pointEvent = new PointEvent(eventOrderCounter, params.x, params.y);
pointEvent.setView(pointView);
pointEvent.setLayoutParams(params);
EventViewBinder.bindPoint(context, windowManager, pointEvent,
TOUCH_POINT_SIZE, screenWidth, screenHeight,
(x, y) -> updateServicePositions(), eventOrderCounter);
runtimeEvents.add(new EventWrapper(EventWrapper.EventType.POINT, pointEvent, eventOrderCounter));
eventOrderCounter++;
Log.d(TAG, "addPoint runtimeEvents: " + runtimeEvents.size() + " eventOrderCounter :" + eventOrderCounter);
// 设置拖动功能并添加到窗口
setupDraggablePoint(pointView, params, pointEvent);
windowManager.addView(pointView, params);
updateServicePositions();
}
@SuppressLint("ClickableViewAccessibility")
private void addSlideEvent() {
if (isMultipleRunning) {
Toast.makeText(context, "请先停止点击", Toast.LENGTH_SHORT).show();
return;
}
// 创建起点
TextView startPointView = createTouchPointView(String.valueOf(eventOrderCounter));
WindowManager.LayoutParams startParams = createTouchPointParams();
startParams.x = screenWidth / 2 - 100;
startParams.y = screenHeight / 2 - 50;
PointEvent startPointEvent = new PointEvent(eventOrderCounter, startParams.x, startParams.y);
startPointEvent.setView(startPointView);
startPointEvent.setLayoutParams(startParams);
PointEvent startPoint = new PointEvent(eventOrderCounter, screenWidth / 2 - 100, screenHeight / 2 - 50);
PointEvent endPoint = new PointEvent(eventOrderCounter, screenWidth / 2 + 100, screenHeight / 2 - 50);
SlideEvent slideEvent = new SlideEvent(eventOrderCounter, startPoint, endPoint);
// 创建终点
TextView endPointView = createTouchPointView(String.valueOf(eventOrderCounter));
WindowManager.LayoutParams endParams = createTouchPointParams();
endParams.x = screenWidth / 2 + 100;
endParams.y = screenHeight / 2 - 50;
PointEvent endPointEvent = new PointEvent(eventOrderCounter, endParams.x, endParams.y);
startPointEvent.setView(endPointView);
startPointEvent.setLayoutParams(endParams);
// 创建滑动连线
ConnectingLineView lineView = new ConnectingLineView(context);
WindowManager.LayoutParams lineParams = createLineViewParams();
SlideEvent slideEvent = new SlideEvent(eventOrderCounter, startPointEvent, endPointEvent);
slideEvent.setLineView(lineView);
slideEvent.setLineParams(lineParams);
EventViewBinder.bindSlide(context, windowManager, slideEvent,
TOUCH_POINT_SIZE, screenWidth, screenHeight,
(x, y) -> updateServicePositions(), eventOrderCounter);
runtimeEvents.add(new EventWrapper(EventWrapper.EventType.SLIDE, slideEvent, eventOrderCounter));
eventOrderCounter++;
Log.d(TAG, "addSlide runtimeEvents: " + runtimeEvents.size() + " eventOrderCounter :" + eventOrderCounter);
// 设置滑动点可拖动
setupDraggableSlidePoint(startPointView, startParams, startPointEvent, slideEvent);
setupDraggableSlidePoint(endPointView, endParams, endPointEvent, slideEvent);
// 添加视图到窗口
windowManager.addView(lineView, lineParams);
windowManager.addView(startPointView, startParams);
windowManager.addView(endPointView, endParams);
updateLinePosition(slideEvent); // 更新线的位置
updateServicePositions();
}
public void toggleEventsVisibility(boolean show) {
@ -354,9 +291,9 @@ public class FloatingViewManager {
// 获取事件的视图
View viewToToggle = null;
if (wrapper.getType() == EventWrapper.EventType.POINT) {
viewToToggle = createTouchPointView(String.valueOf(wrapper.getEvent().getId()));
viewToToggle = EventViewBinder.createTouchPointView(context, String.valueOf(wrapper.getEvent().getId()));
} else if (wrapper.getType() == EventWrapper.EventType.SLIDE) {
View startPointView = getView(show, wrapper,context);
View startPointView = getView(show, wrapper, context);
WindowManager.LayoutParams startParams = (WindowManager.LayoutParams) startPointView.getLayoutParams();
if (show) {
@ -386,23 +323,20 @@ public class FloatingViewManager {
}
@NonNull
private View getView(boolean show, EventWrapper wrapper,Context context) {
private View getView(boolean show, EventWrapper wrapper, Context context) {
SlideEvent slideEvent = (SlideEvent) wrapper.getEvent();
// 假设你只隐藏/显示点线也需要同步
View startPointView = createTouchPointView(String.valueOf(slideEvent.getStartPoint().getId()));
View endPointView = createTouchPointView(String.valueOf(slideEvent.getEndPoint().getId()));
View startPointView = EventViewBinder.createTouchPointView(context, String.valueOf(slideEvent.getStartPoint().getId()));
View endPointView = EventViewBinder.createTouchPointView(context, String.valueOf(slideEvent.getEndPoint().getId()));
View lineView = new ConnectingLineView(context);
if (show) {
// 如果是显示可能需要重新添加或设置可见性
// 这里假设你只是更新透明度如果之前移除了则需要重新addView
startPointView.setAlpha(1.0f);
endPointView.setAlpha(1.0f);
lineView.setAlpha(1.0f); // 如果线视图也需要隐藏/显示
lineView.setAlpha(1.0f);
} else {
startPointView.setAlpha(0.0f);
endPointView.setAlpha(0.0f);
lineView.setAlpha(0.0f); // 隐藏线视图
lineView.setAlpha(0.0f);
}
return startPointView;
}
@ -418,7 +352,6 @@ public class FloatingViewManager {
return;
}
// 移除最后一个事件
Event lastEvent = runtimeEvents.remove(runtimeEvents.size() - 1).getEvent();
removeEvent(lastEvent);
eventOrderCounter--;
@ -426,7 +359,6 @@ public class FloatingViewManager {
}
private void closeFloatingViews() {
// 停止自动点击服务
AutoClickService service = AutoClickService.getInstance();
if (service != null) {
service.stopClicking();
@ -435,13 +367,11 @@ public class FloatingViewManager {
Toast.makeText(context, "请同意无障碍服务权限", Toast.LENGTH_SHORT).show();
}
updateTouchPointsBackground(R.drawable.ring_has_bg);
removeFloatingViews(); // 移除所有悬浮窗
removeAllFloatingViews();
context.stopService(new Intent(context, ForegroundService.class));
PrefUtils.setFloatingShown(context, 0);
// 发送浮窗关闭广播
Intent intent = new Intent("com.auto.clicker.autoclicker.FLOATING_WINDOW_STATE_CHANGED");
intent.putExtra("isShown", false);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
@ -464,30 +394,8 @@ public class FloatingViewManager {
updateTouchPointsBackground(R.drawable.ring_has_bg);
}
private WindowManager.LayoutParams createLineViewParams() {
// 创建连接线滑动路径的参数全屏不可点击
int overlayType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_PHONE;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
overlayType,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT
);
params.gravity = Gravity.TOP | Gravity.START;
params.x = 0;
params.y = 0;
return params;
}
private void removeEvent(Event event) {
try {
// 判断类型并移除相关视图
if (event instanceof PointEvent) {
PointEvent pointEvent = (PointEvent) event;
windowManager.removeView(pointEvent.getView());
@ -502,42 +410,48 @@ public class FloatingViewManager {
}
}
public void removeFloatingViews() {
public void removeAllFloatingViews() {
removeAllEventViews();
removeMultipleControlBarView();
if (floatingTabDialogManager != null) {
floatingTabDialogManager.removeFloatingTabDialog();
}
Log.d(TAG, "所有浮动视图(包括控制栏和事件)已移除");
}
public void removeAllEventViews() {
try {
// 移除点事件视图
for (EventWrapper wrapper : new ArrayList<>(runtimeEvents)) {
if (wrapper.getType() == EventWrapper.EventType.POINT) {
PointEvent pe = (PointEvent) wrapper.getEvent();
safeRemoveView(pe.getView());
}
}
// 移除滑动事件视图
for (EventWrapper wrapper : new ArrayList<>(runtimeEvents)) {
if (wrapper.getType() == EventWrapper.EventType.SLIDE) {
} else if (wrapper.getType() == EventWrapper.EventType.SLIDE) {
SlideEvent se = (SlideEvent) wrapper.getEvent();
safeRemoveView(se.getStartPoint().getView());
safeRemoveView(se.getEndPoint().getView());
safeRemoveView(se.getLineView());
}
}
runtimeEvents.clear();
Log.d(TAG, "所有事件悬浮窗已移除");
} catch (Exception e) {
Log.e(TAG, "移除事件悬浮窗失败", e);
}
}
public void removeMultipleControlBarView() {
try {
if (multipleControlBarView != null) {
safeRemoveView(multipleControlBarView);
multipleControlBarView = null;
isMultipleRunning = false;
Log.d(TAG, "多选控制栏悬浮窗已移除");
}
if (floatingTabDialogManager != null) {
floatingTabDialogManager.removeFloatingTabDialog();
}
isMultipleRunning = false;
Log.d(TAG, "悬浮窗已移除");
} catch (Exception e) {
Log.e(TAG, "移除悬浮窗失败", e);
isMultipleRunning = false;
Log.e(TAG, "移除多选控制栏悬浮窗失败", e);
}
}
@ -574,156 +488,7 @@ public class FloatingViewManager {
context.startActivity(intent);
}
private boolean isDebounced() {
long currentTime = System.currentTimeMillis();
if (currentTime - lastToggleTime < DEBOUNCE_INTERVAL) {
return true;
}
lastToggleTime = currentTime;
return false;
}
@SuppressLint("ClickableViewAccessibility")
private void setupDraggablePoint(TextView point, WindowManager.LayoutParams params, PointEvent pointEvent) {
point.setOnTouchListener(new View.OnTouchListener() {
private float lastX, lastY;
private float paramX, paramY;
@Override
public boolean onTouch(View v, MotionEvent event) {
if (isMultipleRunning) {
if (event.getAction() == MotionEvent.ACTION_UP) {
v.performClick();
return true;
}
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = event.getRawX();
lastY = event.getRawY();
paramX = params.x;
paramY = params.y;
return true;
case MotionEvent.ACTION_MOVE:
float dx = event.getRawX() - lastX;
float dy = event.getRawY() - lastY;
Point constrainedPoint = ViewUtils.constrainToScreen(
paramX + dx, paramY + dy,
screenWidth - point.getWidth(), screenHeight - point.getHeight());
params.x = constrainedPoint.x;
params.y = constrainedPoint.y;
pointEvent.setX(params.x);
pointEvent.setY(params.y);
windowManager.updateViewLayout(point, params);
updateServicePositions();
return true;
case MotionEvent.ACTION_UP:
v.performClick();
return true;
}
return false;
}
});
}
@SuppressLint("ClickableViewAccessibility")
private void setupDraggableSlidePoint(TextView point, WindowManager.LayoutParams params,
PointEvent pointEvent, SlideEvent slideEvent) {
point.setOnTouchListener(new View.OnTouchListener() {
private float lastX, lastY;
private float paramX, paramY;
@Override
public boolean onTouch(View v, MotionEvent event) {
if (isMultipleRunning) {
if (event.getAction() == MotionEvent.ACTION_UP) {
v.performClick();
return true;
}
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = event.getRawX();
lastY = event.getRawY();
paramX = params.x;
paramY = params.y;
return true;
case MotionEvent.ACTION_MOVE:
float dx = event.getRawX() - lastX;
float dy = event.getRawY() - lastY;
Point constrainedPoint = ViewUtils.constrainToScreen(
paramX + dx, paramY + dy,
screenWidth - point.getWidth(), screenHeight - point.getHeight());
params.x = constrainedPoint.x;
params.y = constrainedPoint.y;
pointEvent.setX(params.x);
pointEvent.setY(params.y);
windowManager.updateViewLayout(point, params);
updateLinePosition(slideEvent);
updateServicePositions();
return true;
case MotionEvent.ACTION_UP:
v.performClick();
return true;
}
return false;
}
});
}
private void updateLinePosition(SlideEvent slideEvent) {
PointEvent start = slideEvent.getStartPoint();
PointEvent end = slideEvent.getEndPoint();
ConnectingLineView lineView = slideEvent.getLineView();
if (lineView == null) {
Log.e(TAG, "updateLinePosition: lineView is null, cannot update line");
return;
}
float startCenterX = start.getX() + (float) TOUCH_POINT_SIZE / 2;
float startCenterY = start.getY() + (float) TOUCH_POINT_SIZE / 2;
float endCenterX = end.getX() + (float) TOUCH_POINT_SIZE / 2;
float endCenterY = end.getY() + (float) TOUCH_POINT_SIZE / 2;
lineView.setPoints(startCenterX, startCenterY, endCenterX, endCenterY);
}
private TextView createTouchPointView(String label) {
// 创建一个圆形点击点视图
TextView touchPointView = new TextView(context);
touchPointView.setBackgroundResource(R.drawable.ring_has_bg);
touchPointView.setGravity(Gravity.CENTER);
touchPointView.setText(label);
touchPointView.setTextColor(Color.WHITE);
touchPointView.setTextSize(19);
return touchPointView;
}
private WindowManager.LayoutParams createTouchPointParams() {
// 创建点击点的布局参数
int overlayType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_PHONE;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
TOUCH_POINT_SIZE, TOUCH_POINT_SIZE,
overlayType,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT
);
params.gravity = Gravity.TOP | Gravity.START;
return params;
}
private void updateServicePositions() {
// 更新服务中记录的位置
AutoClickService service = AutoClickService.getInstance();
if (service != null) {
service.addMultipleClickEvents(runtimeEvents);
@ -871,7 +636,7 @@ public class FloatingViewManager {
public void onComplete(SolutionWithEventsAndPoints solutionData) {
new Handler(Looper.getMainLooper()).post(() -> {
if (solutionData != null && solutionData.events != null) {
removeFloatingViews();
removeAllEventViews();
runtimeEvents.clear();
eventOrderCounter = 1;
@ -883,50 +648,31 @@ public class FloatingViewManager {
TouchPoint clickPoint = eventWithPoints.clickTouchPoint;
if (clickPoint != null) {
PointEvent pointEvent = new PointEvent(eventEntity.orderInSolution, clickPoint.x, clickPoint.y);
TextView pointView = createTouchPointView(String.valueOf(pointEvent.getId()));
WindowManager.LayoutParams params = createTouchPointParams();
params.x = pointEvent.getX();
params.y = pointEvent.getY();
pointView.setLayoutParams(params);
pointEvent.setView(pointView);
setupDraggablePoint(pointView, params, pointEvent);
windowManager.addView(pointView, params);
EventViewBinder.bindPoint(context, windowManager,
pointEvent,
TOUCH_POINT_SIZE,
screenWidth, screenHeight,
(x, y) -> updateServicePositions(), eventEntity.orderInSolution);
runtimeEvents.add(new EventWrapper(type, pointEvent, pointEvent.getId()));
}
} else if (type == EventWrapper.EventType.SLIDE) {
TouchPoint startPointData = eventWithPoints.slideStartTouchPoint;
TouchPoint endPointData = eventWithPoints.slideEndTouchPoint;
if (startPointData != null && endPointData != null) {
PointEvent startPointEvent = new PointEvent(eventEntity.orderInSolution, startPointData.x, startPointData.y);
PointEvent endPointEvent = new PointEvent(eventEntity.orderInSolution, endPointData.x, endPointData.y);
TextView startPointView = createTouchPointView(String.valueOf(startPointEvent.getId()));
WindowManager.LayoutParams startParams = createTouchPointParams();
startParams.x = startPointEvent.getX();
startParams.y = startPointEvent.getY();
startPointView.setLayoutParams(startParams);
TextView endPointView = createTouchPointView(String.valueOf(endPointEvent.getId()));
WindowManager.LayoutParams endParams = createTouchPointParams();
endParams.x = endPointEvent.getX();
endParams.y = endPointEvent.getY();
endPointView.setLayoutParams(endParams);
ConnectingLineView lineView = new ConnectingLineView(context);
WindowManager.LayoutParams lineParams = createLineViewParams();
SlideEvent slideEvent = new SlideEvent(eventEntity.orderInSolution, startPointEvent, endPointEvent);
slideEvent.setLineView(lineView);
slideEvent.setLineParams(lineParams);
setupDraggableSlidePoint(startPointView, startParams, startPointEvent, slideEvent);
setupDraggableSlidePoint(endPointView, endParams, endPointEvent, slideEvent);
EventViewBinder.bindSlide(context, windowManager,
slideEvent,
TOUCH_POINT_SIZE,
screenWidth, screenHeight,
(x, y) -> updateServicePositions(), eventEntity.orderInSolution);
windowManager.addView(lineView, lineParams);
windowManager.addView(startPointView, startParams);
windowManager.addView(endPointView, endParams);
updateLinePosition(slideEvent);
runtimeEvents.add(new EventWrapper(type, slideEvent, slideEvent.getId()));
}
}
@ -960,4 +706,13 @@ public class FloatingViewManager {
callback.onError(new IllegalStateException("EventRepository not initialized."));
}
}
private boolean isDebounced() {
long currentTime = System.currentTimeMillis();
if (currentTime - lastToggleTime < DEBOUNCE_INTERVAL) {
return true;
}
lastToggleTime = currentTime;
return false;
}
}