功能添加
This commit is contained in:
parent
27727eaff0
commit
73e2478168
@ -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<EventWithPoints> events; // 注意这里我们用 EventWithPoints
|
||||
public List<EventWithPoints> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Solution> 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<SolutionWithEventsAndPoints> 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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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<EventWrapper> runtimeEvents, RepositoryCallback<Long> 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<List<Solution>> callback) {
|
||||
databaseWriteExecutor.execute(() -> {
|
||||
try {
|
||||
@ -128,6 +123,35 @@ public class EventRepository {
|
||||
});
|
||||
}
|
||||
|
||||
public void updateSolutionName(long solutionId, String newName, RepositoryCallback<Void> 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<Void> 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<SolutionWithEventsAndPoints> callback) {
|
||||
databaseWriteExecutor.execute(() -> {
|
||||
try {
|
||||
@ -140,34 +164,69 @@ public class EventRepository {
|
||||
});
|
||||
}
|
||||
|
||||
// public void deleteSolution(Solution solution, RepositoryCallback<Integer> 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<Void> 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<Void> 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<Void> callback) {
|
||||
|
||||
}
|
||||
|
||||
// 用于复制方案,需要保存一个新的方案以及其关联的事件和点
|
||||
public void saveSolutionWithEventsAndPoints(Solution solution, List<SolutionWithEventsAndPoints.EventWithPoints> eventsWithPoints, RepositoryCallback<Void> callback) {
|
||||
|
||||
}
|
||||
|
||||
// 获取某个方案及其所有事件和点的数据
|
||||
public void getSolutionWithEventsAndPoints(String solutionId, RepositoryCallback<SolutionWithEventsAndPoints> callback) {
|
||||
|
||||
public String exportSolutionToJson(SolutionWithEventsAndPoints data) {
|
||||
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().setPrettyPrinting().create();
|
||||
return gson.toJson(data);
|
||||
}
|
||||
|
||||
public interface RepositoryCallback<T> {
|
||||
|
||||
@ -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<List<Solution>>() {
|
||||
// 确保 FloatingViewManager 能够提供 EventRepository 实例
|
||||
// 假设 FloatingViewManager 内部管理着 EventRepository
|
||||
floatingViewManager.getEventRepository().getAllSolutions(new EventRepository.RepositoryCallback<List<Solution>>() {
|
||||
@Override
|
||||
public void onComplete(List<Solution> 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<Void>() {
|
||||
@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<SolutionWithEventsAndPoints>() {
|
||||
@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<SolutionWithEventsAndPoints.EventWithPoints> 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<Void>() {
|
||||
if (originalData != null) {
|
||||
floatingViewManager.getEventRepository().saveDuplicateSolution(originalData, new EventRepository.RepositoryCallback<Void>() {
|
||||
@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<SolutionWithEventsAndPoints>() {
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,14 @@ public class SolutionAdapter extends RecyclerView.Adapter<SolutionAdapter.Soluti
|
||||
public SolutionAdapter(List<Solution> solutionList, OnSolutionClickListener listener, OnOptionMenuClickListener optionMenuListener) {
|
||||
this.solutionList = solutionList;
|
||||
this.listener = listener;
|
||||
this.optionMenuListener = optionMenuListener; // 初始化
|
||||
this.optionMenuListener = optionMenuListener;
|
||||
}
|
||||
|
||||
// 新增构造函数,用于不需要选项菜单的情况
|
||||
public SolutionAdapter(List<Solution> solutionList, OnSolutionClickListener listener) {
|
||||
this.solutionList = solutionList;
|
||||
this.listener = listener;
|
||||
this.optionMenuListener = null; // 设置为 null,表示不需要选项菜单
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -68,12 +75,14 @@ public class SolutionAdapter extends RecyclerView.Adapter<SolutionAdapter.Soluti
|
||||
}
|
||||
});
|
||||
|
||||
// 为 option 按钮设置点击监听器
|
||||
holder.option.setOnClickListener(v -> {
|
||||
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
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="292dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_rounded_rect"
|
||||
android:backgroundTint="#164958"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="My Script 1"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/mode"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/mode"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="1-point Mode"
|
||||
android:textColor="@color/text_gray"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/play"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/solution_play"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/option"
|
||||
android:layout_marginEnd="32dp"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/option"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/script_option"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -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" />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user