diff --git a/app/src/main/java/com/auto/clicker/autoclicker/service/ForegroundService.java b/app/src/main/java/com/auto/clicker/autoclicker/service/ForegroundService.java index b1d88dc..520c614 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/service/ForegroundService.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/service/ForegroundService.java @@ -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, "悬浮窗已移除"); } diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/floating/SolutionAdapter.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/floating/SolutionAdapter.java similarity index 90% rename from app/src/main/java/com/auto/clicker/autoclicker/ui/floating/SolutionAdapter.java rename to app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/floating/SolutionAdapter.java index 560e326..2f7a07f 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/ui/floating/SolutionAdapter.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/floating/SolutionAdapter.java @@ -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 solutionList; private OnSolutionClickListener listener; - // 定义接口用于处理点击事件 public interface OnSolutionClickListener { void onSolutionClick(Solution solution); } @@ -30,7 +29,7 @@ public class SolutionAdapter extends RecyclerView.Adapter { + 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); + } +} + diff --git a/app/src/main/java/com/auto/clicker/autoclicker/util/FloatingTabDialogManager.java b/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingTabDialogManager.java similarity index 85% rename from app/src/main/java/com/auto/clicker/autoclicker/util/FloatingTabDialogManager.java rename to app/src/main/java/com/auto/clicker/autoclicker/view/FloatingTabDialogManager.java index e6b89d1..34db8b7 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/util/FloatingTabDialogManager.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingTabDialogManager.java @@ -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>() { @Override public void onComplete(List 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() diff --git a/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingViewManager.java b/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingViewManager.java index 04d780a..d642c97 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingViewManager.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingViewManager.java @@ -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; + } } \ No newline at end of file