diff --git a/app/src/main/java/com/auto/clicker/autoclicker/service/AutoClickService.java b/app/src/main/java/com/auto/clicker/autoclicker/service/AutoClickService.java index 942da98..8eda466 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/service/AutoClickService.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/service/AutoClickService.java @@ -30,8 +30,6 @@ public class AutoClickService extends AccessibilityService { private static final long MIN_CLICK_INTERVAL = 40; 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 static final int MIN_SLIDE_DURATION = 100; private static final int MAX_SLIDE_DURATION = 2000; @@ -109,15 +107,6 @@ public class AutoClickService extends AccessibilityService { logDebug("设置点击间隔: " + interval + "ms"); } - public void setClickDuration(int duration) { - if (duration < MIN_CLICK_DURATION || duration > MAX_CLICK_DURATION) { - Log.w(TAG, "点击时长超出范围: " + duration + "ms"); - return; - } - this.clickDuration = duration; - logDebug("设置点击时长: " + duration + "ms"); - } - public void setSlideDuration(int duration) { if (duration < MIN_SLIDE_DURATION || duration > MAX_SLIDE_DURATION) { Log.w(TAG, "滑动时长超出范围: " + duration + "ms"); @@ -127,11 +116,6 @@ public class AutoClickService extends AccessibilityService { logDebug("设置滑动时长: " + duration + "ms"); } - /** - * 设置事件队列的循环次数。 - * - * @param count 循环次数。设置为0表示无限循环,设置为1表示不循环(执行一次)。 - */ public void setLoopCount(int count) { if (count < 0) { Log.w(TAG, "循环次数不能为负数,已设置为默认值 1"); @@ -140,18 +124,12 @@ public class AutoClickService extends AccessibilityService { this.loopCount = count; } logDebug("设置循环次数: " + loopCount + " 次"); - // 如果设置了循环次数,则重置循环时间为0,确保两种模式不冲突 if (this.loopCount > 0) { this.loopTimeMillis = 0; logDebug("已重置循环时间为 0ms"); } } - /** - * 设置事件队列的循环总时间。 - * - * @param timeMillis 循环总时间,单位毫秒。设置为0表示不按时间循环。 - */ public void setLoopTime(long timeMillis) { if (timeMillis < 0) { Log.w(TAG, "循环时间不能为负数,已设置为默认值 0ms"); @@ -160,7 +138,6 @@ public class AutoClickService extends AccessibilityService { this.loopTimeMillis = timeMillis; } logDebug("设置循环时间: " + loopTimeMillis + "ms"); - // 如果设置了循环时间,则重置循环次数为0,确保两种模式不冲突 if (this.loopTimeMillis > 0) { this.loopCount = 0; logDebug("已重置循环次数为 0"); @@ -203,10 +180,9 @@ public class AutoClickService extends AccessibilityService { EventViewBinder.setAllTouchPointsDraggable(true); - // 重置循环状态 - currentEventIndex = 0; // 停止时重置事件索引 - currentLoop = 0; // 停止时重置循环计数 - startTimeMillis = 0; // 停止时重置开始时间 + currentEventIndex = 0; + currentLoop = 0; + startTimeMillis = 0; logDebug("停止执行事件"); } @@ -224,29 +200,26 @@ public class AutoClickService extends AccessibilityService { if (runtimeEvents.isEmpty()) { logDebug("跳过执行:事件队列为空"); - stopClicking(); // 如果事件队列为空,则停止服务 + stopClicking(); return; } - // 在每一轮循环开始前排序,确保顺序正确 runtimeEvents.sort(Comparator.comparingInt(EventWrapper::getOrder)); - // 检查是否完成了一轮事件列表 if (currentEventIndex >= runtimeEvents.size()) { - currentEventIndex = 0; // 重置事件索引,准备开始新一轮循环 - currentLoop++; // 完成一轮,增加循环计数 + currentEventIndex = 0; + currentLoop++; logDebug("完成第 " + currentLoop + " 轮事件执行。"); - // 判断是否继续循环 boolean continueLoop = false; - if (loopCount > 0) { // 按次数循环模式 + if (loopCount > 0) { if (currentLoop < loopCount) { continueLoop = true; logDebug("继续第 " + (currentLoop + 1) + " 轮循环 (共 " + loopCount + " 轮)"); } else { logDebug("已达到设定的循环次数 (" + loopCount + " 次),停止循环。"); } - } else if (loopTimeMillis > 0) { // 按时间循环模式 + } else if (loopTimeMillis > 0) { long elapsedTime = System.currentTimeMillis() - startTimeMillis; if (elapsedTime < loopTimeMillis) { continueLoop = true; @@ -254,13 +227,13 @@ public class AutoClickService extends AccessibilityService { } else { logDebug("已达到设定的循环时间 (" + (loopTimeMillis / 1000) + "s),停止循环。"); } - } else { // 无限循环模式 (loopCount == 0 && loopTimeMillis == 0) + } else { continueLoop = true; logDebug("无限循环模式,继续下一轮循环。"); } if (!continueLoop) { - stopClicking(); // 不再继续循环,则停止服务 + stopClicking(); return; } } @@ -274,8 +247,8 @@ public class AutoClickService extends AccessibilityService { performSlide((SlideEvent) event); } else { logDebug("未知或不匹配的事件类型,跳过执行: " + wrapper.getType()); - currentEventIndex++; // 跳过当前事件 - handler.postDelayed(this::executeEventsByType, clickInterval); // 立即尝试下一个事件 + currentEventIndex++; + handler.postDelayed(this::executeEventsByType, clickInterval); } } diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/setting/ActionFragment.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/setting/ActionFragment.java index cc08bfb..fca7817 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/setting/ActionFragment.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/setting/ActionFragment.java @@ -4,6 +4,7 @@ import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.text.InputType; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -12,18 +13,35 @@ import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatButton; import androidx.fragment.app.Fragment; import com.auto.clicker.autoclicker.R; import com.auto.clicker.autoclicker.databinding.FragmentActionBinding; +import com.auto.clicker.autoclicker.service.AutoClickService; public class ActionFragment extends Fragment { + private static final String TAG = "ActionFragment"; private FragmentActionBinding binding; private static final int TYPE_INTERVAL = 0; private static final int TYPE_SWIPE_DURATION = 1; private static final int TYPE_REPEAT = 2; + private enum UnitType { + MS, SEC, MIN, INFINITELY, DURATION, CYCLE, UNKNOWN + } + + private UnitType currentIntervalUnit = UnitType.MS; + private UnitType currentSwipeDurationUnit = UnitType.MS; + private UnitType currentRepeatUnit = UnitType.INFINITELY; + private String currentIntervalValue = ""; + private String currentDurationValue = ""; + private String currentRepeatValue = ""; + private boolean isRepeatSwitchChecked = false; + + private AppCompatButton saveSettingsButton; + @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -33,38 +51,61 @@ public class ActionFragment extends Fragment { binding.unitSelectorContainer.setOnClickListener(v -> showUnitSelection(v, TYPE_SWIPE_DURATION)); setupRepeatMode(); - setupAdvancedMode(); + restoreUIState(); + + saveSettingsButton = binding.save; + saveSettingsButton.setOnClickListener(v -> { + applySettingsToService(); + Toast.makeText(getContext(), "设置已保存并应用!", Toast.LENGTH_SHORT).show(); + }); return binding.getRoot(); } + @Override + public void onResume() { + super.onResume(); + restoreUIState(); + } + private void setupRepeatMode() { + binding.repeatSwitch.setChecked(isRepeatSwitchChecked); + updateRepeatUI(isRepeatSwitchChecked); + binding.repeatSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (isChecked) { - binding.repeatInput.setVisibility(View.VISIBLE); - binding.repeatSelectorContainer.setVisibility(View.VISIBLE); + isRepeatSwitchChecked = isChecked; + updateRepeatUI(isChecked); + }); + + binding.repeatSelectorContainer.setOnClickListener(v -> showUnitSelection(v, TYPE_REPEAT)); + } + + private void updateRepeatUI(boolean isChecked) { + if (isChecked) { + binding.repeatInput.setVisibility(View.VISIBLE); + binding.repeatSelectorContainer.setVisibility(View.VISIBLE); + + if (currentRepeatUnit == UnitType.INFINITELY) { binding.repeatSelectedUnit.setText(getString(R.string.infinitely_unit)); binding.etRepeatValue.setText(""); binding.etRepeatValue.setEnabled(false); binding.etRepeatValue.setInputType(InputType.TYPE_NULL); - } else { - binding.repeatInput.setVisibility(View.GONE); - binding.repeatSelectorContainer.setVisibility(View.GONE); + } else if (currentRepeatUnit == UnitType.DURATION) { + binding.repeatSelectedUnit.setText(getString(R.string.duration_unit)); + binding.etRepeatValue.setText(currentRepeatValue); + binding.etRepeatValue.setEnabled(true); + binding.etRepeatValue.setInputType(InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_TIME); + } else if (currentRepeatUnit == UnitType.CYCLE) { + binding.repeatSelectedUnit.setText(getString(R.string.cycle_unit)); + binding.etRepeatValue.setText(currentRepeatValue); + binding.etRepeatValue.setEnabled(true); + binding.etRepeatValue.setInputType(InputType.TYPE_CLASS_NUMBER); } - }); - - if (!binding.repeatSwitch.isChecked()) { + } else { binding.repeatInput.setVisibility(View.GONE); binding.repeatSelectorContainer.setVisibility(View.GONE); - } else { - binding.repeatSelectedUnit.setText(getString(R.string.infinitely_unit)); - binding.etRepeatValue.setText(""); - binding.etRepeatValue.setEnabled(false); - binding.etRepeatValue.setInputType(InputType.TYPE_NULL); } - - binding.repeatSelectorContainer.setOnClickListener(v -> showUnitSelection(v, TYPE_REPEAT)); } private void setupAdvancedMode() { @@ -72,9 +113,9 @@ public class ActionFragment extends Fragment { binding.advancedSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { if (isChecked) { - Toast.makeText(getContext(), "Anti-Detection mode is ON", Toast.LENGTH_SHORT).show(); + Toast.makeText(getContext(), "防检测模式已开启", Toast.LENGTH_SHORT).show(); } else { - Toast.makeText(getContext(), "Anti-Detection mode is OFF", Toast.LENGTH_SHORT).show(); + Toast.makeText(getContext(), "防检测模式已关闭", Toast.LENGTH_SHORT).show(); } }); } @@ -98,132 +139,251 @@ public class ActionFragment extends Fragment { TextView tvDuration = popupView.findViewById(R.id.tv_unit_duration); TextView tvCycle = popupView.findViewById(R.id.tv_unit_cycle); + tvMs.setVisibility(View.GONE); + tvSec.setVisibility(View.GONE); + tvMin.setVisibility(View.GONE); + tvInfinitely.setVisibility(View.GONE); + tvDuration.setVisibility(View.GONE); + tvCycle.setVisibility(View.GONE); switch (type) { case TYPE_INTERVAL: tvMs.setVisibility(View.VISIBLE); tvSec.setVisibility(View.VISIBLE); tvMin.setVisibility(View.VISIBLE); - tvInfinitely.setVisibility(View.GONE); - tvDuration.setVisibility(View.GONE); - tvCycle.setVisibility(View.GONE); break; case TYPE_SWIPE_DURATION: tvMs.setVisibility(View.VISIBLE); tvSec.setVisibility(View.VISIBLE); - tvMin.setVisibility(View.GONE); - tvInfinitely.setVisibility(View.GONE); - tvDuration.setVisibility(View.GONE); - tvCycle.setVisibility(View.GONE); break; case TYPE_REPEAT: - tvMs.setVisibility(View.GONE); - tvSec.setVisibility(View.GONE); - tvMin.setVisibility(View.GONE); tvInfinitely.setVisibility(View.VISIBLE); tvDuration.setVisibility(View.VISIBLE); tvCycle.setVisibility(View.VISIBLE); break; } - if (type == TYPE_INTERVAL || type == TYPE_SWIPE_DURATION) { - tvMs.setOnClickListener(v -> { - String unitText = getString(R.string.milliseconds_unit); - if (type == TYPE_INTERVAL) { - binding.intervalSelectedUnit.setText(unitText); - showInputValue("Interval", binding.etIntervalValue.getText().toString(), unitText); - } else { - binding.tvSelectedUnit.setText(unitText); - showInputValue("Swipe Duration", binding.etDurationValue.getText().toString(), unitText); - } + tvMs.setOnClickListener(v -> { + UnitType selectedUnitType = UnitType.MS; + String unitText = getString(R.string.milliseconds_unit); + updateUIAndSaveSettings(type, selectedUnitType, unitText); + popupWindow.dismiss(); + }); + + tvSec.setOnClickListener(v -> { + UnitType selectedUnitType = UnitType.SEC; + String unitText = getString(R.string.seconds_unit); + updateUIAndSaveSettings(type, selectedUnitType, unitText); + popupWindow.dismiss(); + }); + + if (type == TYPE_INTERVAL) { + tvMin.setOnClickListener(v -> { + UnitType selectedUnitType = UnitType.MIN; + String unitText = getString(R.string.minutes_unit); + updateUIAndSaveSettings(type, selectedUnitType, unitText); popupWindow.dismiss(); }); - - tvSec.setOnClickListener(v -> { - String unitText = getString(R.string.seconds_unit); - if (type == TYPE_INTERVAL) { - binding.intervalSelectedUnit.setText(unitText); - showInputValue("Interval", binding.etIntervalValue.getText().toString(), unitText); - } else { - binding.tvSelectedUnit.setText(unitText); - showInputValue("Swipe Duration", binding.etDurationValue.getText().toString(), unitText); - } - popupWindow.dismiss(); - }); - - if (type == TYPE_INTERVAL) { - tvMin.setOnClickListener(v -> { - String unitText = getString(R.string.minutes_unit); - binding.intervalSelectedUnit.setText(unitText); - popupWindow.dismiss(); - showInputValue("Interval", binding.etIntervalValue.getText().toString(), unitText); - }); - } } if (type == TYPE_REPEAT) { tvInfinitely.setOnClickListener(v -> { - binding.repeatSelectedUnit.setText(getString(R.string.infinitely_unit)); - binding.etRepeatValue.setText(""); - binding.etRepeatValue.setEnabled(false); - binding.etRepeatValue.setInputType(InputType.TYPE_NULL); + UnitType selectedUnitType = UnitType.INFINITELY; + String unitText = getString(R.string.infinitely_unit); + updateUIAndSaveSettings(type, selectedUnitType, unitText); + updateRepeatUI(true); popupWindow.dismiss(); - showInputValue("Repeat", binding.etRepeatValue.getText().toString(), getString(R.string.infinitely_unit)); }); tvDuration.setOnClickListener(v -> { - binding.repeatSelectedUnit.setText(getString(R.string.duration_unit)); - binding.etRepeatValue.setText(""); - binding.etRepeatValue.setEnabled(true); - binding.etRepeatValue.setInputType(InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_TIME); // 设置为时间格式 + UnitType selectedUnitType = UnitType.DURATION; + String unitText = getString(R.string.duration_unit); + updateUIAndSaveSettings(type, selectedUnitType, unitText); + updateRepeatUI(true); popupWindow.dismiss(); - showInputValue("Repeat", binding.etRepeatValue.getText().toString(), getString(R.string.duration_unit)); }); tvCycle.setOnClickListener(v -> { - binding.repeatSelectedUnit.setText(getString(R.string.cycle_unit)); - binding.etRepeatValue.setText(""); - binding.etRepeatValue.setEnabled(true); - binding.etRepeatValue.setInputType(InputType.TYPE_CLASS_NUMBER); + UnitType selectedUnitType = UnitType.CYCLE; + String unitText = getString(R.string.cycle_unit); + updateUIAndSaveSettings(type, selectedUnitType, unitText); + updateRepeatUI(true); popupWindow.dismiss(); - showInputValue("Repeat", binding.etRepeatValue.getText().toString(), getString(R.string.cycle_unit)); }); } popupWindow.showAsDropDown(anchorView); } - private void showInputValue(String type, String value, String unit) { - String message = type + " 值: " + (value.isEmpty() ? "未输入" : value) + ", 单位: " + unit; - Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); + private void updateUIAndSaveSettings(int type, UnitType selectedUnitType, String unitText) { + switch (type) { + case TYPE_INTERVAL: + binding.intervalSelectedUnit.setText(unitText); + currentIntervalUnit = selectedUnitType; + break; + case TYPE_SWIPE_DURATION: + binding.tvSelectedUnit.setText(unitText); + currentSwipeDurationUnit = selectedUnitType; + break; + case TYPE_REPEAT: + binding.repeatSelectedUnit.setText(unitText); + currentRepeatUnit = selectedUnitType; + break; + } + saveCurrentInputValues(); + showInputValue(type, getEditTextValue(type), unitText); + } - if (type.equals("Interval")) { - long intervalValue = value.isEmpty() ? 0 : Long.parseLong(value); - // 根据 unit 进行单位转换或保存 Interval 值 - // 例如:if (unit.equals(getString(R.string.seconds_unit))) { ... } - } else if (type.equals("Swipe Duration")) { - long durationValue = value.isEmpty() ? 0 : Long.parseLong(value); - // 根据 unit 进行单位转换或保存 Swipe Duration 值 - } else if (type.equals("Repeat")) { - if (unit.equals(getString(R.string.infinitely_unit))) { - // Repeat 为无限次,不需要处理输入值 - } else if (unit.equals(getString(R.string.duration_unit))) { - // Repeat 为时长,处理时间格式的输入 - // 您需要在这里添加解析时间格式的逻辑 - String durationValue = value.isEmpty() ? "0" : value; - // long parsedDuration = parseDurationString(durationValue); // 自定义方法解析时间 - Toast.makeText(getContext(), "Repeat Duration: " + durationValue, Toast.LENGTH_SHORT).show(); - } else if (unit.equals(getString(R.string.cycle_unit))) { - // Repeat 为次数,处理整数输入 - int repeatCount = value.isEmpty() ? 0 : Integer.parseInt(value); - Toast.makeText(getContext(), "Repeat Cycles: " + repeatCount, Toast.LENGTH_SHORT).show(); + private String getEditTextValue(int type) { + switch (type) { + case TYPE_INTERVAL: return binding.etIntervalValue.getText().toString(); + case TYPE_SWIPE_DURATION: return binding.etDurationValue.getText().toString(); + case TYPE_REPEAT: return binding.etRepeatValue.getText().toString(); + default: return ""; + } + } + + private void showInputValue(int type, String value, String unitText) { + String typeName = ""; + switch (type) { + case TYPE_INTERVAL: typeName = "点击间隔"; break; + case TYPE_SWIPE_DURATION: typeName = "滑动时长"; break; + case TYPE_REPEAT: typeName = "循环模式"; break; + } + String message = typeName + " 值: " + (value.isEmpty() ? "未输入" : value) + ", 单位: " + unitText; + Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); + } + + private void saveCurrentInputValues() { + currentIntervalValue = binding.etIntervalValue.getText().toString(); + currentDurationValue = binding.etDurationValue.getText().toString(); + currentRepeatValue = binding.etRepeatValue.getText().toString(); + isRepeatSwitchChecked = binding.repeatSwitch.isChecked(); + } + + private void restoreUIState() { + binding.etIntervalValue.setText(currentIntervalValue); + binding.intervalSelectedUnit.setText(getUnitString(currentIntervalUnit)); + + binding.etDurationValue.setText(currentDurationValue); + binding.tvSelectedUnit.setText(getUnitString(currentSwipeDurationUnit)); + + binding.repeatSwitch.setChecked(isRepeatSwitchChecked); + updateRepeatUI(isRepeatSwitchChecked); + + if (isRepeatSwitchChecked) { + binding.repeatSelectedUnit.setText(getUnitString(currentRepeatUnit)); + } + } + + private String getUnitString(UnitType unitType) { + switch (unitType) { + case MS: return getString(R.string.milliseconds_unit); + case SEC: return getString(R.string.seconds_unit); + case MIN: return getString(R.string.minutes_unit); + case INFINITELY: return getString(R.string.infinitely_unit); + case DURATION: return getString(R.string.duration_unit); + case CYCLE: return getString(R.string.cycle_unit); + default: return ""; + } + } + + public void applySettingsToService() { + saveCurrentInputValues(); + + AutoClickService service = AutoClickService.getInstance(); + if (service == null) { + Toast.makeText(getContext(), "自动点击服务未运行,无法应用设置。", Toast.LENGTH_SHORT).show(); + Log.e(TAG, "AutoClickService 实例为空,无法应用设置。"); + return; + } + + long intervalValue = 0; + if (!currentIntervalValue.isEmpty()) { + try { + intervalValue = Long.parseLong(currentIntervalValue); + if (currentIntervalUnit == UnitType.SEC) { + intervalValue *= 1000; + } else if (currentIntervalUnit == UnitType.MIN) { + intervalValue *= 60 * 1000; + } + service.setClickInterval(intervalValue); + Log.d(TAG, "应用点击间隔到服务: " + intervalValue + "ms"); + } catch (NumberFormatException e) { + Log.e(TAG, "点击间隔值解析错误: " + currentIntervalValue, e); + Toast.makeText(getContext(), "点击间隔值无效,请输入有效数字。", Toast.LENGTH_SHORT).show(); } + } else { + Log.d(TAG, "点击间隔值为空,未应用到服务。"); + } + + long slideDurationValue = 0; + if (!currentDurationValue.isEmpty()) { + try { + slideDurationValue = Long.parseLong(currentDurationValue); + if (currentSwipeDurationUnit == UnitType.SEC) { + slideDurationValue *= 1000; + } + service.setSlideDuration((int) slideDurationValue); + Log.d(TAG, "应用滑动时长到服务: " + slideDurationValue + "ms"); + } catch (NumberFormatException e) { + Log.e(TAG, "滑动时长值解析错误: " + currentDurationValue, e); + Toast.makeText(getContext(), "滑动时长值无效,请输入有效数字。", Toast.LENGTH_SHORT).show(); + } + } else { + Log.d(TAG, "滑动时长值为空,未应用到服务。"); + } + + if (isRepeatSwitchChecked) { + if (currentRepeatUnit == UnitType.INFINITELY) { + service.setLoopCount(0); + Log.d(TAG, "应用重复模式: 无限次。"); + } else if (currentRepeatUnit == UnitType.DURATION) { + long repeatDurationMillis = 0; + if (!currentRepeatValue.isEmpty()) { + try { + repeatDurationMillis = Long.parseLong(currentRepeatValue); + service.setLoopTime(repeatDurationMillis); + Log.d(TAG, "应用重复时长: " + repeatDurationMillis + "ms。"); + } catch (NumberFormatException e) { + Log.e(TAG, "重复时长值解析错误: " + currentRepeatValue, e); + Toast.makeText(getContext(), "重复时长值无效,请输入有效数字或正确的时间格式。", Toast.LENGTH_SHORT).show(); + service.setLoopTime(0); + } + } else { + service.setLoopTime(0); + Log.d(TAG, "重复时长值为空,取消时间循环。"); + } + service.setLoopCount(0); + } else if (currentRepeatUnit == UnitType.CYCLE) { + int repeatCount = 0; + if (!currentRepeatValue.isEmpty()) { + try { + repeatCount = Integer.parseInt(currentRepeatValue); + service.setLoopCount(repeatCount); + Log.d(TAG, "应用重复次数: " + repeatCount + "次。"); + } catch (NumberFormatException e) { + Log.e(TAG, "重复次数值解析错误: " + currentRepeatValue, e); + Toast.makeText(getContext(), "重复次数值无效,请输入有效数字。", Toast.LENGTH_SHORT).show(); + service.setLoopCount(1); + } + } else { + service.setLoopCount(1); + Log.d(TAG, "重复次数值为空,设置为执行一次。"); + } + service.setLoopTime(0); + } + } else { + service.setLoopCount(1); + service.setLoopTime(0); + Log.d(TAG, "重复开关关闭,服务设置为执行一次。"); } } @Override public void onDestroyView() { super.onDestroyView(); + saveCurrentInputValues(); binding = null; } } \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/util/EventViewBinder.java b/app/src/main/java/com/auto/clicker/autoclicker/util/EventViewBinder.java index 9f4991a..0506c75 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/util/EventViewBinder.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/util/EventViewBinder.java @@ -22,14 +22,12 @@ import java.util.Map; public class EventViewBinder { - // 存储每个视图及其对应的 OnTouchListener 实例,用于启用/禁用拖动 private static final Map activeDragListeners = new HashMap<>(); public interface OnPositionChangedCallback { void onPositionChanged(int x, int y); } - // --- bindPoint 方法 --- public static void bindPoint(Context context, WindowManager windowManager, PointEvent pointEvent, int touchPointSize, int screenWidth, int screenHeight, @@ -47,7 +45,6 @@ public class EventViewBinder { pointView.setTextSize(TypedValue.COMPLEX_UNIT_SP, initialTextSize); - // 创建并保存 OnTouchListener View.OnTouchListener touchListener = createTouchListener( pointView, params, windowManager, screenWidth, screenHeight, (x, y) -> { @@ -57,12 +54,11 @@ public class EventViewBinder { }); pointView.setOnTouchListener(touchListener); - activeDragListeners.put(pointView, touchListener); // 保存监听器到 Map + activeDragListeners.put(pointView, touchListener); windowManager.addView(pointView, params); } - // --- bindSlide 方法 --- public static void bindSlide(Context context, WindowManager windowManager, SlideEvent slideEvent, int touchPointSize, int screenWidth, int screenHeight, @@ -126,15 +122,13 @@ public class EventViewBinder { updateLinePosition(slideEvent, touchPointSize); } - // --- 从 DraggableHelper 移动过来的 OnTouchListener 创建逻辑 --- - // 这个方法创建并返回一个 View.OnTouchListener 实例 private static View.OnTouchListener createTouchListener( View view, WindowManager.LayoutParams params, WindowManager windowManager, - int screenWidth, int screenHeight, DraggableHelper.DragCallback callback) { // 仍然使用 DraggableHelper.DragCallback 接口 + int screenWidth, int screenHeight, DraggableHelper.DragCallback callback) { return new View.OnTouchListener() { private float lastX, lastY; private float paramX, paramY; - private boolean isDragging = false; // 标记是否正在拖动 + private boolean isDragging = false; @Override public boolean onTouch(View v, MotionEvent event) { @@ -144,7 +138,7 @@ public class EventViewBinder { lastY = event.getRawY(); paramX = params.x; paramY = params.y; - isDragging = false; // 重置拖动状态 + isDragging = false; return true; case MotionEvent.ACTION_MOVE: @@ -169,7 +163,7 @@ public class EventViewBinder { return true; case MotionEvent.ACTION_UP: - if (!isDragging) { // 如果没有拖动,才视为点击 + if (!isDragging) { v.performClick(); } return true; @@ -179,42 +173,25 @@ public class EventViewBinder { }; } - // --- 新增方法:统一控制所有触摸点的拖动状态 --- public static void setAllTouchPointsDraggable(boolean draggable) { for (Map.Entry entry : activeDragListeners.entrySet()) { View view = entry.getKey(); View.OnTouchListener listener = entry.getValue(); if (draggable) { - // 启用拖动:重新设置保存的监听器 view.setOnTouchListener(listener); } else { - // 禁用拖动:将监听器设置为 null,阻止触摸事件 view.setOnTouchListener(null); } } } - // --- 新增方法:移除不再需要的视图及其监听器 --- - public static void removeBoundView(WindowManager windowManager, View view) { - if (view != null) { - try { - windowManager.removeView(view); - activeDragListeners.remove(view); // 从 Map 中移除 - Log.d("EventViewBinder", "移除视图并清理监听器: " + view.getTag()); - } catch (IllegalArgumentException e) { - Log.e("EventViewBinder", "尝试移除不存在的视图: " + e.getMessage()); - } - } - } - - // --- 辅助方法 (保持不变) --- 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.setTag(label); // 添加一个Tag,方便日志识别 + touchPointView.setTag(label); return touchPointView; } @@ -245,7 +222,7 @@ public class EventViewBinder { overlayType, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | - WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, // 线条通常不可触摸 + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT ); params.gravity = Gravity.TOP | Gravity.START; diff --git a/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingSettingDialogManager.java b/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingSettingDialogManager.java index 7dd2ff4..ab88f27 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingSettingDialogManager.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingSettingDialogManager.java @@ -96,7 +96,6 @@ public class FloatingSettingDialogManager { unitSelectorContainer.setOnClickListener(v -> showUnitSelection(v, TYPE_SWIPE_DURATION)); setupRepeatMode(); - // --- 恢复UI状态 --- restoreUIState(); // floatingDialogView.setOnTouchListener((v, event) -> { @@ -179,8 +178,8 @@ public class FloatingSettingDialogManager { overlayType, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL // 允许将触摸事件传递到其下方窗口 - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, // 监听外部触摸事件 + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, PixelFormat.TRANSLUCENT ); params.gravity = Gravity.CENTER; @@ -195,7 +194,7 @@ public class FloatingSettingDialogManager { } repeatSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - isRepeatSwitchChecked = isChecked; // 保存开关状态 + isRepeatSwitchChecked = isChecked; updateRepeatUI(isChecked); }); @@ -206,16 +205,14 @@ public class FloatingSettingDialogManager { if (isChecked) { repeatInput.setVisibility(View.VISIBLE); repeatSelectorContainer.setVisibility(View.VISIBLE); - // 只有当单位是“无限次”时才禁用输入框 if (currentRepeatUnit.equals(context.getString(R.string.infinitely_unit)) || repeatSelectedUnit.getText().toString().equals(context.getString(R.string.infinitely_unit))) { etRepeatValue.setText(""); etRepeatValue.setEnabled(false); etRepeatValue.setInputType(InputType.TYPE_NULL); - repeatSelectedUnit.setText(context.getString(R.string.infinitely_unit)); // 确保显示无限次 + repeatSelectedUnit.setText(context.getString(R.string.infinitely_unit)); } else { etRepeatValue.setEnabled(true); - etRepeatValue.setInputType(InputType.TYPE_CLASS_NUMBER); // 默认设置为数字输入 - // 恢复之前的输入值和单位 + etRepeatValue.setInputType(InputType.TYPE_CLASS_NUMBER); etRepeatValue.setText(currentRepeatValue); repeatSelectedUnit.setText(currentRepeatUnit.isEmpty() ? context.getString(R.string.cycle_unit) : currentRepeatUnit); } @@ -383,8 +380,6 @@ public class FloatingSettingDialogManager { showToastOnUi(context, "点击间隔值无效,请输入有效数字。"); } } else { - // 如果为空,设置一个默认值或不设置,这取决于你的业务逻辑 - // service.setClickInterval(默认值); Log.d(TAG, "点击间隔值为空,未应用到服务。"); } @@ -402,12 +397,9 @@ public class FloatingSettingDialogManager { showToastOnUi(context, "滑动时长值无效,请输入有效数字。"); } } else { - // 如果为空,设置一个默认值或不设置 - // service.setSlideDuration(默认值); Log.d(TAG, "滑动时长值为空,未应用到服务。"); } - // 应用重复设置 if (isRepeatSwitchChecked) { if (currentRepeatUnit.equals(context.getString(R.string.infinitely_unit))) { service.setLoopCount(0); // 无限循环 @@ -417,8 +409,6 @@ public class FloatingSettingDialogManager { if (!currentRepeatValue.isEmpty()) { try { repeatDurationMillis = Long.parseLong(currentRepeatValue); - // 根据你的UI单位,这里可能需要进一步转换,例如用户输入的是秒 - // repeatDurationMillis *= 1000L; service.setLoopTime(repeatDurationMillis); Log.d(TAG, "应用重复时长: " + repeatDurationMillis + "ms。"); } catch (NumberFormatException e) { 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 77e517b..58c89dc 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 @@ -24,7 +24,6 @@ import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; -import androidx.annotation.NonNull; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.auto.clicker.autoclicker.R; @@ -87,6 +86,13 @@ public class FloatingViewManager { private static final int DEFAULT_CONTROL_BAR_WIDTH_DP = 50; private ImageView playButton; + private ImageView addButton; + private ImageView removeButton; + private ImageView slideButton; + private ImageView saveButton; + private ImageView eyeButton; + private ImageView settingButton; + private ImageView closeButton; public FloatingViewManager(Context context) { this.context = context; @@ -145,7 +151,7 @@ public class FloatingViewManager { multipleControlBarParams.height = WindowManager.LayoutParams.WRAP_CONTENT; setupDraggableView(multipleControlBarView, multipleControlBarParams, this::updateMoreControlBarPosition); - setupControlButtons(multipleControlBarView,mode); + setupControlButtons(multipleControlBarView, mode); updateControlBarImageViews(multipleControlBarView, this.controlBarWidth); } @@ -223,17 +229,17 @@ public class FloatingViewManager { windowManager.updateViewLayout(multipleControlBarView, multipleControlBarParams); } - private void setupControlButtons(LinearLayout controlBar,int mode) { + private void setupControlButtons(LinearLayout controlBar, int mode) { playButton = controlBar.findViewById(R.id.play_button); - ImageView addButton = controlBar.findViewById(R.id.add_button); - ImageView removeButton = controlBar.findViewById(R.id.remove_button); - ImageView slideButton = controlBar.findViewById(R.id.slide_button); - ImageView saveButton = controlBar.findViewById(R.id.save_button); - ImageView eyeButton = controlBar.findViewById(R.id.eye_button); - ImageView settingButton = controlBar.findViewById(R.id.settings_button); - ImageView closeButton = controlBar.findViewById(R.id.close_button); + addButton = controlBar.findViewById(R.id.add_button); + removeButton = controlBar.findViewById(R.id.remove_button); + slideButton = controlBar.findViewById(R.id.slide_button); + saveButton = controlBar.findViewById(R.id.save_button); + eyeButton = controlBar.findViewById(R.id.eye_button); + settingButton = controlBar.findViewById(R.id.settings_button); + closeButton = controlBar.findViewById(R.id.close_button); - if (mode == 1){ + if (mode == 1) { addButton.setVisibility(View.GONE); removeButton.setVisibility(View.GONE); slideButton.setVisibility(View.GONE); @@ -336,59 +342,90 @@ public class FloatingViewManager { public void toggleEventsVisibility(boolean show) { for (EventWrapper wrapper : new ArrayList<>(runtimeEvents)) { - // 获取事件的视图 - View viewToToggle = null; + WindowManager.LayoutParams params = null; + if (wrapper.getType() == EventWrapper.EventType.POINT) { - viewToToggle = EventViewBinder.createTouchPointView(context, String.valueOf(wrapper.getEvent().getId())); + PointEvent pointEvent = (PointEvent) wrapper.getEvent(); + View pointView = pointEvent.getView(); + + if (pointView != null) { + params = (WindowManager.LayoutParams) pointView.getLayoutParams(); + if (params != null) { + if (show) { + pointView.setAlpha(1.0f); + params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + } else { + pointView.setAlpha(0.0f); + params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + } + windowManager.updateViewLayout(pointView, params); + } + } + } else if (wrapper.getType() == EventWrapper.EventType.SLIDE) { - View startPointView = getView(show, wrapper, context); + SlideEvent slideEvent = (SlideEvent) wrapper.getEvent(); - WindowManager.LayoutParams startParams = (WindowManager.LayoutParams) startPointView.getLayoutParams(); - if (show) { - startParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - } else { - startParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + View startPointView = slideEvent.getStartPoint().getView(); + if (startPointView != null) { + params = (WindowManager.LayoutParams) startPointView.getLayoutParams(); + if (params != null) { + if (show) { + startPointView.setAlpha(1.0f); + params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + } else { + startPointView.setAlpha(0.0f); + params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + } + windowManager.updateViewLayout(startPointView, params); + } } - windowManager.updateViewLayout(startPointView, startParams); - continue; - } - if (viewToToggle != null) { - if (show) { - viewToToggle.setAlpha(1.0f); - } else { - viewToToggle.setAlpha(0.0f); + View endPointView = slideEvent.getEndPoint().getView(); + if (endPointView != null) { + params = (WindowManager.LayoutParams) endPointView.getLayoutParams(); + if (params != null) { + if (show) { + endPointView.setAlpha(1.0f); + params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + } else { + endPointView.setAlpha(0.0f); + params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + } + windowManager.updateViewLayout(endPointView, params); + } } - WindowManager.LayoutParams params = (WindowManager.LayoutParams) viewToToggle.getLayoutParams(); - if (show) { - params.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - } else { - params.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + + View lineView = slideEvent.getLineView(); + if (lineView != null) { + params = (WindowManager.LayoutParams) lineView.getLayoutParams(); + if (params != null) { + if (show) { + lineView.setAlpha(1.0f); + params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + } else { + lineView.setAlpha(0.0f); + params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + } + windowManager.updateViewLayout(lineView, params); + } } - windowManager.updateViewLayout(viewToToggle, params); } } } - @NonNull - private View getView(boolean show, EventWrapper wrapper, Context context) { - SlideEvent slideEvent = (SlideEvent) wrapper.getEvent(); - 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) { - startPointView.setAlpha(1.0f); - endPointView.setAlpha(1.0f); - lineView.setAlpha(1.0f); - } else { - startPointView.setAlpha(0.0f); - endPointView.setAlpha(0.0f); - lineView.setAlpha(0.0f); - } - return startPointView; - } - private void removeLastEvent() { if (isMultipleRunning) { Toast.makeText(context, "请先停止点击", Toast.LENGTH_SHORT).show(); @@ -433,6 +470,8 @@ public class FloatingViewManager { Toast.makeText(context, "多点点击开始", Toast.LENGTH_SHORT).show(); updateTouchPointsBackground(R.drawable.touch_point); + + setControlButtonsEnabled(false); } private void stopMultiClicking(AutoClickService service) { @@ -440,6 +479,25 @@ public class FloatingViewManager { Toast.makeText(context, "多点点击停止", Toast.LENGTH_SHORT).show(); updateTouchPointsBackground(R.drawable.ring_has_bg); + + setControlButtonsEnabled(true); + } + + private void setControlButtonsEnabled(boolean enabled) { + if (addButton != null) addButton.setEnabled(enabled); + if (removeButton != null) removeButton.setEnabled(enabled); + if (slideButton != null) slideButton.setEnabled(enabled); + if (saveButton != null) saveButton.setEnabled(enabled); + if (eyeButton != null) eyeButton.setEnabled(enabled); + if (settingButton != null) settingButton.setEnabled(enabled); + + float alpha = enabled ? 1.0f : 0.5f; + if (addButton != null) addButton.setAlpha(alpha); + if (removeButton != null) removeButton.setAlpha(alpha); + if (slideButton != null) slideButton.setAlpha(alpha); + if (saveButton != null) saveButton.setAlpha(alpha); + if (eyeButton != null) eyeButton.setAlpha(alpha); + if (settingButton != null) settingButton.setAlpha(alpha); } private void removeEvent(Event event) { @@ -606,7 +664,7 @@ public class FloatingViewManager { return; } - Solution newSolution = new Solution(solutionName,mode); + Solution newSolution = new Solution(solutionName, mode); eventRepository.insertSolutionWithEvents(newSolution, runtimeEvents, new EventRepository.RepositoryCallback() { @Override public void onComplete(Long solutionId) { @@ -782,7 +840,7 @@ public class FloatingViewManager { } if (slideEvent.getEndPoint().getView() != null && slideEvent.getEndPoint().getLayoutParams() != null) { - TextView endView = slideEvent.getEndPoint().getView(); + TextView endView = slideEvent.getEndPoint().getView(); WindowManager.LayoutParams endParams = slideEvent.getEndPoint().getLayoutParams(); endParams.width = touchPointSize; endParams.height = touchPointSize; diff --git a/app/src/main/res/layout/fragment_action.xml b/app/src/main/res/layout/fragment_action.xml index 8318a37..f4902f8 100644 --- a/app/src/main/res/layout/fragment_action.xml +++ b/app/src/main/res/layout/fragment_action.xml @@ -14,11 +14,12 @@ @@ -112,6 +113,18 @@ + + + app:layout_constraintTop_toBottomOf="@id/tv_interval_range_hint" /> @@ -199,6 +213,17 @@ + + diff --git a/app/src/main/res/layout/single_control_bar.xml b/app/src/main/res/layout/single_control_bar.xml deleted file mode 100644 index 0eaefb6..0000000 --- a/app/src/main/res/layout/single_control_bar.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file