diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/SolutionWithEventsAndPoints.java b/app/src/main/java/com/auto/clicker/autoclicker/room/SolutionWithEventsAndPoints.java index a73f03f..0bc5aa7 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/SolutionWithEventsAndPoints.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/SolutionWithEventsAndPoints.java @@ -9,7 +9,6 @@ import com.auto.clicker.autoclicker.room.entity.Solution; import java.util.List; -// 这个 POJO 用于表示一个方案,以及该方案下的所有事件及其关联的坐标点 public class SolutionWithEventsAndPoints { @Embedded public Solution solution; @@ -19,9 +18,8 @@ public class SolutionWithEventsAndPoints { entityColumn = "solution_id", entity = EventEntity.class ) - public List events; // 注意这里我们用 EventWithPoints + public List events; - // 内部类,用于表示一个事件以及它关联的起点和终点 public static class EventWithPoints { @Embedded public EventEntity eventEntity; @@ -31,20 +29,20 @@ public class SolutionWithEventsAndPoints { entityColumn = "pointId", entity = TouchPoint.class ) - public TouchPoint clickTouchPoint; // 单击事件的点 + public TouchPoint clickTouchPoint; @Relation( parentColumn = "slide_start_point_id", entityColumn = "pointId", entity = TouchPoint.class ) - public TouchPoint slideStartTouchPoint; // 滑动事件的起点 + public TouchPoint slideStartTouchPoint; @Relation( parentColumn = "slide_end_point_id", entityColumn = "pointId", entity = TouchPoint.class ) - public TouchPoint slideEndTouchPoint; // 滑动事件的终点 + public TouchPoint slideEndTouchPoint; } } diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/dao/SolutionDao.java b/app/src/main/java/com/auto/clicker/autoclicker/room/dao/SolutionDao.java index a898205..9230d35 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/dao/SolutionDao.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/dao/SolutionDao.java @@ -15,7 +15,7 @@ import java.util.List; @Dao public interface SolutionDao { @Insert - long insertSolution(Solution solution); // 返回新插入的 solutionId + long insertSolution(Solution solution); @Query("SELECT * FROM solutions") List getAllSolutions(); @@ -27,18 +27,22 @@ public interface SolutionDao { Solution getSolutionByName(String solutionName); @Delete - int deleteSolution(Solution solution); // 返回删除的行数 + int deleteSolution(Solution solution); @Update - int updateSolution(Solution solution); // 返回更新的行数 + int updateSolution(Solution solution); - // 在 SolutionDao 中添加 - @Transaction // 表示这是一个事务操作,确保数据一致性 + @Transaction @Query("SELECT * FROM solutions WHERE solutionId = :solutionId LIMIT 1") SolutionWithEventsAndPoints getSolutionWithEventsAndPoints(long solutionId); - // 也可以获取所有方案及其事件 @Transaction @Query("SELECT * FROM solutions") List getAllSolutionsWithEventsAndPoints(); + + @Query("UPDATE solutions SET solution_name = :newName WHERE solutionId = :solutionId") + int updateSolutionName(long solutionId, String newName); + + @Query("DELETE FROM solutions WHERE solutionId = :solutionId") + void deleteSolutionById(long solutionId); } diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/entity/EventEntity.java b/app/src/main/java/com/auto/clicker/autoclicker/room/entity/EventEntity.java index a8cc319..5ef3401 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/entity/EventEntity.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/entity/EventEntity.java @@ -7,6 +7,8 @@ import androidx.room.Entity; import androidx.room.ForeignKey; import androidx.room.PrimaryKey; +import com.google.gson.annotations.Expose; + @Entity(tableName = "events", foreignKeys = { @ForeignKey(entity = Solution.class, @@ -27,27 +29,35 @@ import androidx.room.PrimaryKey; onDelete = CASCADE) }) public class EventEntity { + @Expose @PrimaryKey(autoGenerate = true) public long eventId; + @Expose @ColumnInfo(name = "solution_id") public long solutionId; + @Expose @ColumnInfo(name = "event_type") public String eventType; + @Expose @ColumnInfo(name = "order_in_solution") public int orderInSolution; + @Expose @ColumnInfo(name = "click_point_id") public Long clickPointId; + @Expose @ColumnInfo(name = "slide_start_point_id") public Long slideStartPointId; + @Expose @ColumnInfo(name = "slide_end_point_id") public Long slideEndPointId; + @Expose @ColumnInfo(name = "duration") public long duration; diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/entity/Solution.java b/app/src/main/java/com/auto/clicker/autoclicker/room/entity/Solution.java index da92f39..e5e9180 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/entity/Solution.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/entity/Solution.java @@ -8,14 +8,20 @@ import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.PrimaryKey; +import com.google.gson.annotations.Expose; + @Entity(tableName = "solutions") public class Solution { + + @Expose @PrimaryKey(autoGenerate = true) public long solutionId; + @Expose @ColumnInfo(name = "solution_name") public String solutionName; + @Expose @ColumnInfo(name = "mode") public int mode; diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/entity/TouchPoint.java b/app/src/main/java/com/auto/clicker/autoclicker/room/entity/TouchPoint.java index 4d66646..68f7242 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/entity/TouchPoint.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/entity/TouchPoint.java @@ -4,16 +4,21 @@ import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.PrimaryKey; +import com.google.gson.annotations.Expose; @Entity(tableName = "touchPoints") public class TouchPoint { + + @Expose @PrimaryKey(autoGenerate = true) public long pointId; + @Expose @ColumnInfo(name = "x_coordinate") public int x; + @Expose @ColumnInfo(name = "y_coordinate") public int y; diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/repository/EventRepository.java b/app/src/main/java/com/auto/clicker/autoclicker/room/repository/EventRepository.java index 0d84d61..e006279 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/repository/EventRepository.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/repository/EventRepository.java @@ -3,6 +3,8 @@ package com.auto.clicker.autoclicker.room.repository; import android.content.Context; import android.util.Log; +import androidx.room.Transaction; + import com.auto.clicker.autoclicker.data.Event; import com.auto.clicker.autoclicker.data.EventWrapper; import com.auto.clicker.autoclicker.data.PointEvent; @@ -15,6 +17,8 @@ import com.auto.clicker.autoclicker.room.dao.TouchPointDao; import com.auto.clicker.autoclicker.room.entity.EventEntity; import com.auto.clicker.autoclicker.room.entity.Solution; import com.auto.clicker.autoclicker.room.entity.TouchPoint; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import java.util.List; import java.util.concurrent.ExecutorService; @@ -27,30 +31,27 @@ public class EventRepository { private final ExecutorService databaseWriteExecutor; public EventRepository(Context context) { - AppDatabase db = AppDatabase.getInstance(context); // Updated to getInstance + AppDatabase db = AppDatabase.getInstance(context); this.solutionDao = db.solutionDao(); this.touchPointDao = db.pointDao(); this.eventEntityDao = db.eventEntityDao(); this.databaseWriteExecutor = Executors.newFixedThreadPool(2); } - // --- 插入操作 --- public void insertSolutionWithEvents(Solution solution, List runtimeEvents, RepositoryCallback callback) { databaseWriteExecutor.execute(() -> { - long solutionId = -1; // Initialize solutionId outside try-catch for broader scope + long solutionId = -1; try { - // 1. 插入 Solution solutionId = solutionDao.insertSolution(solution); if (solutionId == -1) { throw new RuntimeException("Failed to insert solution."); } - // 2. 遍历 runtimeEvents,插入 TouchPoint 和 EventEntity for (EventWrapper wrapper : runtimeEvents) { Event event = wrapper.getEvent(); - int order = wrapper.getOrder(); // Using order from EventWrapper + int order = wrapper.getOrder(); - long eventEntityId = -1; // To store the result of event entity insertion + long eventEntityId; if (wrapper.getType() == EventWrapper.EventType.POINT && event instanceof PointEvent) { PointEvent pointEvent = (PointEvent) event; @@ -59,15 +60,14 @@ public class EventRepository { if (pointId == -1) throw new RuntimeException("Failed to insert touch point for PointEvent."); - // 调用 EventEntity 的唯一构造函数,将滑动相关字段设为 null 或默认值 EventEntity eventEntity = new EventEntity( - (int) solutionId, // Ensure type compatibility if solutionId is long + solutionId, // 使用 long 类型的 solutionId wrapper.getType().name(), order, - pointId, // clickPointId - null, // slideStartPointId (null for point event) - null, // slideEndPointId (null for point event) - 0 // duration (0 for point event) + pointId, + null, + null, + 0 ); eventEntityId = eventEntityDao.insertEventEntity(eventEntity); if (eventEntityId == -1) @@ -85,15 +85,14 @@ public class EventRepository { if (endPointId == -1) throw new RuntimeException("Failed to insert end touch point for SlideEvent."); - // 调用 EventEntity 的唯一构造函数,将点击相关字段设为 null EventEntity eventEntity = new EventEntity( - (int) solutionId, + solutionId, // 使用 long 类型的 solutionId wrapper.getType().name(), order, null, startPointId, endPointId, - 0 + 0 // duration for slide events, you might want to get this from SlideEvent ); eventEntityId = eventEntityDao.insertEventEntity(eventEntity); if (eventEntityId == -1) @@ -101,13 +100,10 @@ public class EventRepository { } } if (callback != null) { - // Post the result back to the main thread if needed for UI updates - // For simplicity, directly calling callback here; in a real app, use Handler or LiveData callback.onComplete(solutionId); } } catch (Exception e) { Log.e("EventRepository", "Error inserting solution with events: " + e.getMessage(), e); - // Consider rolling back the transaction if any part fails if (callback != null) { callback.onError(e); } @@ -115,7 +111,6 @@ public class EventRepository { }); } - // --- 查询操作 --- public void getAllSolutions(RepositoryCallback> callback) { databaseWriteExecutor.execute(() -> { try { @@ -128,6 +123,35 @@ public class EventRepository { }); } + public void updateSolutionName(long solutionId, String newName, RepositoryCallback callback) { + databaseWriteExecutor.execute(() -> { + try { + int updatedRows = solutionDao.updateSolutionName(solutionId, newName); + if (updatedRows > 0) { + if (callback != null) callback.onComplete(null); + } else { + throw new RuntimeException("Failed to update solution name, solution not found."); + } + } catch (Exception e) { + Log.e("EventRepository", "Error updating solution name: " + e.getMessage(), e); + if (callback != null) callback.onError(e); + } + }); + } + + @Transaction + public void deleteSolution(long solutionId, RepositoryCallback callback) { + databaseWriteExecutor.execute(() -> { + try { + solutionDao.deleteSolutionById(solutionId); + if (callback != null) callback.onComplete(null); + } catch (Exception e) { + Log.e("EventRepository", "Error deleting solution: " + e.getMessage(), e); + if (callback != null) callback.onError(e); + } + }); + } + public void getSolutionWithEventsAndPoints(long solutionId, RepositoryCallback callback) { databaseWriteExecutor.execute(() -> { try { @@ -140,34 +164,69 @@ public class EventRepository { }); } -// public void deleteSolution(Solution solution, RepositoryCallback callback) { -// databaseWriteExecutor.execute(() -> { -// try { -// int rowsAffected = solutionDao.deleteSolution(solution); -// if (callback != null) callback.onComplete(rowsAffected); -// } catch (Exception e) { -// Log.e("EventRepository", "Error deleting solution: " + e.getMessage(), e); -// if (callback != null) callback.onError(e); -// } -// }); -// } + @Transaction + public void saveDuplicateSolution(SolutionWithEventsAndPoints originalData, RepositoryCallback callback) { + databaseWriteExecutor.execute(() -> { + try { + // 创建新的 Solution 实例 + Solution newSolution = new Solution(originalData.solution.solutionName + " (1)", originalData.solution.mode); + long newSolutionId = solutionDao.insertSolution(newSolution); - public void updateSolutionName(long solutionId, String newName, RepositoryCallback callback) { + if (newSolutionId == -1) { + throw new RuntimeException("Failed to duplicate solution: Could not insert new Solution."); + } + for (SolutionWithEventsAndPoints.EventWithPoints eventWithPoints : originalData.events) { + Long newClickPointId = null; + if (eventWithPoints.clickTouchPoint != null) { + TouchPoint newClickPoint = new TouchPoint(eventWithPoints.clickTouchPoint.x, eventWithPoints.clickTouchPoint.y); + newClickPointId = touchPointDao.insertPoint(newClickPoint); + if (newClickPointId == -1) + throw new RuntimeException("Failed to duplicate click point."); + } + + Long newSlideStartPointId = null; + if (eventWithPoints.slideStartTouchPoint != null) { + TouchPoint newSlideStartPoint = new TouchPoint(eventWithPoints.slideStartTouchPoint.x, eventWithPoints.slideStartTouchPoint.y); + newSlideStartPointId = touchPointDao.insertPoint(newSlideStartPoint); + if (newSlideStartPointId == -1) + throw new RuntimeException("Failed to duplicate slide start point."); + } + + Long newSlideEndPointId = null; + if (eventWithPoints.slideEndTouchPoint != null) { + TouchPoint newSlideEndPoint = new TouchPoint(eventWithPoints.slideEndTouchPoint.x, eventWithPoints.slideEndTouchPoint.y); + newSlideEndPointId = touchPointDao.insertPoint(newSlideEndPoint); + if (newSlideEndPointId == -1) + throw new RuntimeException("Failed to duplicate slide end point."); + } + + // 创建新的 EventEntity 实例 + EventEntity newEventEntity = new EventEntity( + newSolutionId, + eventWithPoints.eventEntity.eventType, + eventWithPoints.eventEntity.orderInSolution, + newClickPointId, + newSlideStartPointId, + newSlideEndPointId, + eventWithPoints.eventEntity.duration + ); + long newEventEntityId = eventEntityDao.insertEventEntity(newEventEntity); + if (newEventEntityId == -1) { + throw new RuntimeException("Failed to duplicate event entity."); + } + } + if (callback != null) callback.onComplete(null); + } catch (Exception e) { + Log.e("EventRepository", "Error duplicating solution: " + e.getMessage(), e); + if (callback != null) callback.onError(e); + } + }); } - public void deleteSolution(long solutionId, RepositoryCallback callback) { - - } - - // 用于复制方案,需要保存一个新的方案以及其关联的事件和点 - public void saveSolutionWithEventsAndPoints(Solution solution, List eventsWithPoints, RepositoryCallback callback) { - - } - - // 获取某个方案及其所有事件和点的数据 - public void getSolutionWithEventsAndPoints(String solutionId, RepositoryCallback callback) { - + public String exportSolutionToJson(SolutionWithEventsAndPoints data) { + Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().setPrettyPrinting().create(); + return gson.toJson(data); } public interface RepositoryCallback { diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/ScriptsActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/ScriptsActivity.java index 5f8d7c8..b478618 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/ScriptsActivity.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/ScriptsActivity.java @@ -1,6 +1,5 @@ package com.auto.clicker.autoclicker.ui.activity.menu; -import android.app.AlertDialog; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -12,6 +11,8 @@ import android.widget.EditText; import android.widget.Toast; import androidx.activity.EdgeToEdge; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; @@ -21,28 +22,24 @@ import androidx.recyclerview.widget.LinearLayoutManager; import com.auto.clicker.autoclicker.R; import com.auto.clicker.autoclicker.databinding.ActivityScriptsBinding; import com.auto.clicker.autoclicker.room.SolutionWithEventsAndPoints; -import com.auto.clicker.autoclicker.room.entity.EventEntity; import com.auto.clicker.autoclicker.room.entity.Solution; -import com.auto.clicker.autoclicker.room.entity.TouchPoint; import com.auto.clicker.autoclicker.room.repository.EventRepository; import com.auto.clicker.autoclicker.ui.adapter.floating.SolutionAdapter; import com.auto.clicker.autoclicker.ui.dialog.CreateModeSelectionDialog; import com.auto.clicker.autoclicker.view.FloatingViewManager; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; -import java.util.UUID; public class ScriptsActivity extends AppCompatActivity implements SolutionAdapter.OnOptionMenuClickListener { private ActivityScriptsBinding binding; private SolutionAdapter solutionsAdapter; private FloatingViewManager floatingViewManager; + private String tempExportJson; private static final String TAG = "ScriptsActivity"; - private static final int CREATE_FILE_REQUEST_CODE = 1; // 用于导出文件的请求码 + private static final int CREATE_FILE_REQUEST_CODE = 1; @Override protected void onCreate(Bundle savedInstanceState) { @@ -57,11 +54,12 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte }); binding.scriptsRecyclerView.setLayoutManager(new LinearLayoutManager(this)); - // 传入 this 作为 OnOptionMenuClickListener 的实现 + // 将 this 作为 OnSolutionClickListener 和 OnOptionMenuClickListener 传入 solutionsAdapter = new SolutionAdapter(new ArrayList<>(), this::onSolutionSelected, this); binding.scriptsRecyclerView.setAdapter(solutionsAdapter); - floatingViewManager = new FloatingViewManager(this); + // 确保 FloatingViewManager 在 Activity 创建时被初始化 + floatingViewManager = new FloatingViewManager(this); // 如果 FloatingViewManager 依赖 Context,这里需要传递 binding.btnBack.setOnClickListener(v -> finish()); @@ -97,7 +95,9 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte return; } - floatingViewManager.getAllSolutions(new EventRepository.RepositoryCallback>() { + // 确保 FloatingViewManager 能够提供 EventRepository 实例 + // 假设 FloatingViewManager 内部管理着 EventRepository + floatingViewManager.getEventRepository().getAllSolutions(new EventRepository.RepositoryCallback>() { @Override public void onComplete(List solutions) { new Handler(Looper.getMainLooper()).post(() -> { @@ -116,7 +116,7 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte new Handler(Looper.getMainLooper()).post(() -> { Log.e(TAG, "加载方案列表失败: " + e.getMessage(), e); Toast.makeText(ScriptsActivity.this, "加载方案列表失败。", Toast.LENGTH_SHORT).show(); - updateLayout(true); // 加载失败也显示无方案视图 + updateLayout(true); }); } }); @@ -145,29 +145,23 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte } } - // --- 实现 SolutionAdapter.OnOptionMenuClickListener 接口的方法 --- - @Override public void onRenameClick(Solution solution) { - // 实现重命名逻辑 showRenameDialog(solution); } @Override public void onDuplicateClick(Solution solution) { - // 实现复制逻辑 duplicateSolution(solution); } @Override public void onDeleteClick(Solution solution) { - // 实现删除逻辑 showDeleteConfirmationDialog(solution); } @Override public void onExportClick(Solution solution) { - // 实现导出逻辑 exportSolution(solution); } @@ -182,7 +176,6 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte builder.setPositiveButton("确定", (dialog, which) -> { String newName = input.getText().toString().trim(); if (!newName.isEmpty() && !newName.equals(solution.getSolutionName())) { - // 调用 EventRepository 更新方案名称 floatingViewManager.getEventRepository().updateSolutionName(solution.getSolutionId(), newName, new EventRepository.RepositoryCallback() { @Override public void onComplete(Void result) { @@ -202,6 +195,8 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte }); } else if (newName.isEmpty()) { Toast.makeText(ScriptsActivity.this, "方案名称不能为空。", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(ScriptsActivity.this, "新名称与原名称相同。", Toast.LENGTH_SHORT).show(); } }); builder.setNegativeButton("取消", (dialog, which) -> dialog.cancel()); @@ -212,60 +207,12 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte floatingViewManager.getEventRepository().getSolutionWithEventsAndPoints(originalSolution.getSolutionId(), new EventRepository.RepositoryCallback() { @Override public void onComplete(SolutionWithEventsAndPoints originalData) { - if (originalData != null && originalData.solution != null) { - // 创建新的 Solution 对象 - Solution newSolution = new Solution(); - newSolution.setSolutionId(UUID.randomUUID().toString()); // 生成新的ID - newSolution.setSolutionName(originalSolution.getSolutionName() + " (1)"); - newSolution.setMode(originalSolution.getMode()); - - // 复制事件和点(深拷贝) - List newEventsWithPoints = new ArrayList<>(); - if (originalData.events != null) { - for (SolutionWithEventsAndPoints.EventWithPoints originalEvent : originalData.events) { - SolutionWithEventsAndPoints.EventWithPoints newEvent = new SolutionWithEventsAndPoints.EventWithPoints(); - // 复制 EventEntity - newEvent.eventEntity = new EventEntity(); - newEvent.eventEntity.eventId = UUID.randomUUID().toString(); // 新的事件ID - newEvent.eventEntity.solutionId = newSolution.getSolutionId(); // 关联新的方案ID - newEvent.eventEntity.eventType = originalEvent.eventEntity.eventType; - newEvent.eventEntity.orderInSolution = originalEvent.eventEntity.orderInSolution; - - // 复制 TouchPoint - if (originalEvent.clickTouchPoint != null) { - newEvent.clickTouchPoint = new TouchPoint(); - newEvent.clickTouchPoint.pointId = UUID.randomUUID().toString(); // 新的点ID - newEvent.clickTouchPoint.eventId = newEvent.eventEntity.eventId; // 关联新的事件ID - newEvent.clickTouchPoint.x = originalEvent.clickTouchPoint.x; - newEvent.clickTouchPoint.y = originalEvent.clickTouchPoint.y; - newEvent.clickTouchPoint.pointOrder = originalEvent.clickTouchPoint.pointOrder; - } - if (originalEvent.slideStartTouchPoint != null) { - newEvent.slideStartTouchPoint = new TouchPoint(); - newEvent.slideStartTouchPoint.pointId = UUID.randomUUID().toString(); // 新的点ID - newEvent.slideStartTouchPoint.eventId = newEvent.eventEntity.eventId; // 关联新的事件ID - newEvent.slideStartTouchPoint.x = originalEvent.slideStartTouchPoint.x; - newEvent.slideStartTouchPoint.y = originalEvent.slideStartTouchPoint.y; - newEvent.slideStartTouchPoint.pointOrder = originalEvent.slideStartTouchPoint.pointOrder; - } - if (originalEvent.slideEndTouchPoint != null) { - newEvent.slideEndTouchPoint = new TouchPoint(); - newEvent.slideEndTouchPoint.pointId = UUID.randomUUID().toString(); // 新的点ID - newEvent.slideEndTouchPoint.eventId = newEvent.eventEntity.eventId; // 关联新的事件ID - newEvent.slideEndTouchPoint.x = originalEvent.slideEndTouchPoint.x; - newEvent.slideEndTouchPoint.y = originalEvent.slideEndTouchPoint.y; - newEvent.slideEndTouchPoint.pointOrder = originalEvent.slideEndTouchPoint.pointOrder; - } - newEventsWithPoints.add(newEvent); - } - } - - // 保存新的方案及其事件和点 - floatingViewManager.getEventRepository().saveSolutionWithEventsAndPoints(newSolution, newEventsWithPoints, new EventRepository.RepositoryCallback() { + if (originalData != null) { + floatingViewManager.getEventRepository().saveDuplicateSolution(originalData, new EventRepository.RepositoryCallback() { @Override public void onComplete(Void result) { new Handler(Looper.getMainLooper()).post(() -> { - Toast.makeText(ScriptsActivity.this, "方案 '" + originalSolution.getSolutionName() + "' 已复制为 '" + newSolution.getSolutionName() + "'", Toast.LENGTH_SHORT).show(); + Toast.makeText(ScriptsActivity.this, "方案 '" + originalSolution.getSolutionName() + "' 已复制成功!", Toast.LENGTH_SHORT).show(); loadSolutions(); // 刷新列表 }); } @@ -279,9 +226,9 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte } }); } else { - new Handler(Looper.getMainLooper()).post(() -> - Toast.makeText(ScriptsActivity.this, "无法获取原始方案数据。", Toast.LENGTH_SHORT).show() - ); + new Handler(Looper.getMainLooper()).post(() -> { + Toast.makeText(ScriptsActivity.this, "无法找到原始方案数据进行复制。", Toast.LENGTH_SHORT).show(); + }); } } @@ -289,17 +236,16 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte public void onError(Exception e) { new Handler(Looper.getMainLooper()).post(() -> { Log.e(TAG, "获取原始方案数据失败: " + e.getMessage(), e); - Toast.makeText(ScriptsActivity.this, "复制方案失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + Toast.makeText(ScriptsActivity.this, "获取原始方案数据失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); }); } }); } - private void showDeleteConfirmationDialog(final Solution solution) { new AlertDialog.Builder(this) .setTitle("删除方案") - .setMessage("确定要删除方案 '" + solution.getSolutionName() + "' 吗?") + .setMessage("确定要删除方案 '" + solution.getSolutionName() + "' 吗?\n此操作不可撤销!") .setPositiveButton("删除", (dialog, which) -> deleteSolution(solution)) .setNegativeButton("取消", null) .show(); @@ -328,26 +274,20 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte private void exportSolution(final Solution solution) { floatingViewManager.getEventRepository().getSolutionWithEventsAndPoints(solution.getSolutionId(), new EventRepository.RepositoryCallback() { @Override - public void onComplete(SolutionWithEventsAndPoints solutionData) { - if (solutionData != null) { - new Handler(Looper.getMainLooper()).post(() -> { - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - String jsonString = gson.toJson(solutionData); - + public void onComplete(SolutionWithEventsAndPoints data) { + if (data != null) { + tempExportJson = floatingViewManager.getEventRepository().exportSolutionToJson(data); + if (tempExportJson != null && !tempExportJson.isEmpty()) { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("application/json"); // 导出为 JSON 文件 + intent.setType("application/json"); intent.putExtra(Intent.EXTRA_TITLE, solution.getSolutionName() + ".json"); startActivityForResult(intent, CREATE_FILE_REQUEST_CODE); - - // 将 JSON 字符串保存到临时变量或直接传递 - // 在 onActivityResult 中处理保存 - tempExportJson = jsonString; // 临时保存待导出的JSON数据 - }); + } else { + Toast.makeText(ScriptsActivity.this, "无法生成导出数据。", Toast.LENGTH_SHORT).show(); + } } else { - new Handler(Looper.getMainLooper()).post(() -> - Toast.makeText(ScriptsActivity.this, "获取方案数据失败,无法导出。", Toast.LENGTH_SHORT).show() - ); + Toast.makeText(ScriptsActivity.this, "无法获取方案数据进行导出。", Toast.LENGTH_SHORT).show(); } } @@ -355,17 +295,14 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte public void onError(Exception e) { new Handler(Looper.getMainLooper()).post(() -> { Log.e(TAG, "获取方案数据失败: " + e.getMessage(), e); - Toast.makeText(ScriptsActivity.this, "导出方案失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + Toast.makeText(ScriptsActivity.this, "获取方案数据失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); }); } }); } - // 临时变量,用于存储待导出的JSON数据 - private String tempExportJson; - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == CREATE_FILE_REQUEST_CODE && resultCode == RESULT_OK) { if (data != null && data.getData() != null) { @@ -385,6 +322,8 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte } else { Toast.makeText(this, "没有可导出的数据。", Toast.LENGTH_SHORT).show(); } + } else { + Toast.makeText(this, "文件创建操作被取消或数据无效。", Toast.LENGTH_SHORT).show(); } } } diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/floating/SolutionAdapter.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/floating/SolutionAdapter.java index a49f29e..545d6be 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/floating/SolutionAdapter.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/floating/SolutionAdapter.java @@ -41,7 +41,14 @@ public class SolutionAdapter extends RecyclerView.Adapter solutionList, OnSolutionClickListener listener, OnOptionMenuClickListener optionMenuListener) { this.solutionList = solutionList; this.listener = listener; - this.optionMenuListener = optionMenuListener; // 初始化 + this.optionMenuListener = optionMenuListener; + } + + // 新增构造函数,用于不需要选项菜单的情况 + public SolutionAdapter(List solutionList, OnSolutionClickListener listener) { + this.solutionList = solutionList; + this.listener = listener; + this.optionMenuListener = null; // 设置为 null,表示不需要选项菜单 } @NonNull @@ -68,12 +75,14 @@ public class SolutionAdapter extends RecyclerView.Adapter { - if (optionMenuListener != null) { + if (optionMenuListener != null) { + holder.option.setVisibility(View.VISIBLE); // 显示按钮 + holder.option.setOnClickListener(v -> { holder.showOptionMenu(solution, optionMenuListener); // 调用方法显示菜单 - } - }); + }); + } else { + holder.option.setVisibility(View.GONE); // 隐藏按钮 + } } @Override diff --git a/app/src/main/res/layout/item_script.xml b/app/src/main/res/layout/item_script.xml deleted file mode 100644 index 3f57b4d..0000000 --- a/app/src/main/res/layout/item_script.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_solution.xml b/app/src/main/res/layout/item_solution.xml index 1c86ff1..8354f60 100644 --- a/app/src/main/res/layout/item_solution.xml +++ b/app/src/main/res/layout/item_solution.xml @@ -48,6 +48,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/script_option" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" />