功能添加优化

This commit is contained in:
lihongwei 2025-06-23 18:04:51 +08:00
parent 6aa0ee4cb0
commit add6f3d1e0
40 changed files with 985 additions and 833 deletions

View File

@ -18,13 +18,6 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.AutoClicker" android:theme="@style/Theme.AutoClicker"
tools:targetApi="31"> tools:targetApi="31">
<activity
android:name=".ui.activity.floating.TransparentFloatingActivity"
android:theme="@style/Theme.TransparentOverlay"
android:launchMode="singleTop"
android:excludeFromRecents="true"
android:taskAffinity=""
android:exported="false" />
<activity <activity
android:name=".ui.activity.main.QuestionActivity" android:name=".ui.activity.main.QuestionActivity"
android:exported="false" /> android:exported="false" />

View File

@ -1,21 +1,29 @@
package com.auto.clicker.autoclicker.data; package com.auto.clicker.autoclicker.data;
import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.TextView; import android.widget.TextView;
public class PointEvent extends Event { public class PointEvent extends Event {
private int id;
private int x; private int x;
private int y; private int y;
private TextView view;
private WindowManager.LayoutParams layoutParams;
public PointEvent(int id, int x, int y, TextView view, WindowManager.LayoutParams layoutParams) { private transient TextView view;
private transient WindowManager.LayoutParams layoutParams;
public PointEvent(int id, int x, int y) {
super(id); super(id);
this.x = x; this.x = x;
this.y = y; this.y = y;
this.view = view; }
this.layoutParams = layoutParams;
@Override
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
} }
public int getX() { public int getX() {
@ -34,7 +42,7 @@ public class PointEvent extends Event {
this.y = y; this.y = y;
} }
public View getView() { public TextView getView() {
return view; return view;
} }

View File

@ -4,20 +4,27 @@ import android.view.WindowManager;
import com.auto.clicker.autoclicker.view.ConnectingLineView; import com.auto.clicker.autoclicker.view.ConnectingLineView;
public class SlideEvent extends Event { public class SlideEvent extends Event {
private int id;
private PointEvent startPoint; private PointEvent startPoint;
private PointEvent endPoint; private PointEvent endPoint;
private ConnectingLineView lineView;
private WindowManager.LayoutParams lineParams;
public SlideEvent(int id, PointEvent startPoint, PointEvent endPoint, private transient ConnectingLineView lineView;
ConnectingLineView lineView, WindowManager.LayoutParams lineParams) { private transient WindowManager.LayoutParams lineParams;
public SlideEvent(int id, PointEvent startPoint, PointEvent endPoint) {
super(id); super(id);
this.startPoint = startPoint; this.startPoint = startPoint;
this.endPoint = endPoint; this.endPoint = endPoint;
this.lineView = lineView; }
this.lineParams = lineParams;
@Override
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
} }
public PointEvent getStartPoint() { public PointEvent getStartPoint() {
@ -51,5 +58,4 @@ public class SlideEvent extends Event {
public void setLineParams(WindowManager.LayoutParams lineParams) { public void setLineParams(WindowManager.LayoutParams lineParams) {
this.lineParams = lineParams; this.lineParams = lineParams;
} }
} }

View File

@ -1,39 +0,0 @@
package com.auto.clicker.autoclicker.dialog;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.auto.clicker.autoclicker.R;
import com.auto.clicker.autoclicker.room.AppDatabase;
import com.auto.clicker.autoclicker.room.ScriptEntity;
import com.auto.clicker.autoclicker.ui.adapter.script.ScriptListAdapter;
import java.util.List;
public class LoadScriptView extends ConstraintLayout {
public interface OnScriptClickListener {
void onScriptSelected(ScriptEntity script);
}
public LoadScriptView(Context context, OnScriptClickListener listener) {
super(context);
LayoutInflater.from(context).inflate(R.layout.load_script_page, this, true);
RecyclerView recyclerView = findViewById(R.id.script_list);
recyclerView.setLayoutManager(new LinearLayoutManager(context));
new Thread(() -> {
List<ScriptEntity> scripts = AppDatabase.getInstance(context).scriptDao().getAllScripts();
new Handler(Looper.getMainLooper()).post(() -> {
recyclerView.setAdapter(new ScriptListAdapter(scripts, listener));
});
}).start();
}
}

View File

@ -1,35 +0,0 @@
package com.auto.clicker.autoclicker.dialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.widget.AppCompatButton;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.auto.clicker.autoclicker.R;
public class SaveScriptView extends ConstraintLayout {
public interface OnSaveListener {
void onSave(String scriptName);
}
public SaveScriptView(Context context, OnSaveListener listener) {
super(context);
LayoutInflater.from(context).inflate(R.layout.save_script_page, this, true);
EditText input = findViewById(R.id.script_name_input);
AppCompatButton saveBtn = findViewById(R.id.save);
saveBtn.setOnClickListener(v -> {
String name = input.getText().toString().trim();
if (!name.isEmpty()) {
listener.onSave(name);
} else {
Toast.makeText(context, "名称不能为空", Toast.LENGTH_SHORT).show();
}
});
}
}

View File

@ -1,87 +0,0 @@
package com.auto.clicker.autoclicker.dialog;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import androidx.viewpager2.widget.ViewPager2;
import com.auto.clicker.autoclicker.R;
import com.auto.clicker.autoclicker.room.ScriptEntity;
import com.auto.clicker.autoclicker.ui.adapter.dialog.ViewPagerAdapter;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import java.util.ArrayList;
import java.util.List;
public class ScriptManagerDialog {
private final WindowManager windowManager;
private final View rootView;
public ScriptManagerDialog(Context context) {
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
rootView = LayoutInflater.from(context).inflate(R.layout.dialog_script_container, null);
TabLayout tabLayout = rootView.findViewById(R.id.tab_layout);
ViewPager2 viewPager = rootView.findViewById(R.id.view_pager);
List<View> pages = new ArrayList<>();
pages.add(new SaveScriptView(context, this::onScriptSaved));
pages.add(new LoadScriptView(context, this::onScriptSelected));
viewPager.setAdapter(new ViewPagerAdapter(pages));
new TabLayoutMediator(tabLayout, viewPager,
(tab, position) -> tab.setText(position == 0 ? "保存脚本" : "加载脚本")).attach();
// 悬浮窗参数
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT
);
params.gravity = Gravity.CENTER;
windowManager.addView(rootView, params);
}
private void onScriptSaved(String name) {
// 插入数据库逻辑
}
private void onScriptSelected(ScriptEntity script) {
// 加载并恢复脚本逻辑
}
public void dismiss() {
windowManager.removeView(rootView);
}
public void show() {
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT
);
params.gravity = Gravity.CENTER;
try {
windowManager.addView(rootView, params);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -7,12 +7,18 @@ import androidx.room.Room;
import androidx.room.RoomDatabase; import androidx.room.RoomDatabase;
import com.auto.clicker.autoclicker.MyApplication; import com.auto.clicker.autoclicker.MyApplication;
import com.auto.clicker.autoclicker.room.dao.EventEntityDao;
import com.auto.clicker.autoclicker.room.dao.TouchPointDao;
import com.auto.clicker.autoclicker.room.dao.SolutionDao;
import com.auto.clicker.autoclicker.room.entity.EventEntity;
import com.auto.clicker.autoclicker.room.entity.TouchPoint;
import com.auto.clicker.autoclicker.room.entity.Solution;
@Database(entities = {ScriptEntity.class,PointEventEntity.class,SlideEventEntity.class}, version = MyApplication.DB_VERSION, exportSchema = false) @Database(entities = {Solution.class, TouchPoint.class, EventEntity.class}, version = MyApplication.DB_VERSION, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase { public abstract class AppDatabase extends RoomDatabase {
public abstract ScriptEntityDao scriptDao(); public abstract SolutionDao solutionDao();
public abstract PointEventEntityDao pointEventEntityDao(); public abstract TouchPointDao pointDao();
public abstract SlideEventEntityDao slideEventEntityDao(); public abstract EventEntityDao eventEntityDao();
private static volatile AppDatabase INSTANCE; private static volatile AppDatabase INSTANCE;

View File

@ -1,23 +0,0 @@
package com.auto.clicker.autoclicker.room;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
@Entity(tableName = "point_events",
foreignKeys = @ForeignKey(entity = ScriptEntity.class,
parentColumns = "scriptId",
childColumns = "scriptOwnerId",
onDelete = ForeignKey.CASCADE),
indices = {@Index("scriptOwnerId")})
public class PointEventEntity {
@PrimaryKey(autoGenerate = true)
public long pointId;
public int x;
public int y;
public int order;
public long scriptOwnerId;
}

View File

@ -1,7 +0,0 @@
package com.auto.clicker.autoclicker.room;
import androidx.room.Dao;
@Dao
public interface PointEventEntityDao {
}

View File

@ -1,14 +0,0 @@
package com.auto.clicker.autoclicker.room;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "scripts")
public class ScriptEntity {
@PrimaryKey(autoGenerate = true)
public long scriptId;
public String name;
public long createdTime;
}

View File

@ -1,29 +0,0 @@
package com.auto.clicker.autoclicker.room;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import java.util.List;
@Dao
public interface ScriptEntityDao {
@Insert
long insertScript(ScriptEntity script);
@Insert void insertPointEvents(List<PointEventEntity> points);
@Insert void insertSlideEvents(List<SlideEventEntity> slides);
@Transaction
@Query("SELECT * FROM scripts")
List<ScriptEntity> getAllScripts();
@Transaction
@Query("SELECT * FROM scripts WHERE scriptId = :scriptId")
ScriptWithEvents getScriptById(long scriptId);
@Delete
void deleteScript(ScriptEntity script);
}

View File

@ -1,18 +0,0 @@
package com.auto.clicker.autoclicker.room;
import androidx.room.Embedded;
import androidx.room.Relation;
import java.util.List;
public class ScriptWithEvents {
@Embedded
public ScriptEntity script;
@Relation(parentColumn = "scriptId", entityColumn = "scriptOwnerId")
public List<PointEventEntity> pointEvents;
@Relation(parentColumn = "scriptId", entityColumn = "scriptOwnerId")
public List<SlideEventEntity> slideEvents;
}

View File

@ -1,25 +0,0 @@
package com.auto.clicker.autoclicker.room;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
@Entity(tableName = "slide_events",
foreignKeys = @ForeignKey(entity = ScriptEntity.class,
parentColumns = "scriptId",
childColumns = "scriptOwnerId",
onDelete = ForeignKey.CASCADE),
indices = {@Index("scriptOwnerId")})
public class SlideEventEntity {
@PrimaryKey(autoGenerate = true)
public long slideId;
public int startX;
public int startY;
public int endX;
public int endY;
public int order;
public long scriptOwnerId;
}

View File

@ -1,7 +0,0 @@
package com.auto.clicker.autoclicker.room;
import androidx.room.Dao;
@Dao
public interface SlideEventEntityDao {
}

View File

@ -0,0 +1,50 @@
package com.auto.clicker.autoclicker.room;
import androidx.room.Embedded;
import androidx.room.Relation;
import com.auto.clicker.autoclicker.room.entity.EventEntity;
import com.auto.clicker.autoclicker.room.entity.TouchPoint;
import com.auto.clicker.autoclicker.room.entity.Solution;
import java.util.List;
// 这个 POJO 用于表示一个方案以及该方案下的所有事件及其关联的坐标点
public class SolutionWithEventsAndPoints {
@Embedded
public Solution solution;
@Relation(
parentColumn = "solutionId",
entityColumn = "solution_id",
entity = EventEntity.class
)
public List<EventWithPoints> events; // 注意这里我们用 EventWithPoints
// 内部类用于表示一个事件以及它关联的起点和终点
public static class EventWithPoints {
@Embedded
public EventEntity eventEntity;
@Relation(
parentColumn = "click_point_id",
entityColumn = "pointId",
entity = TouchPoint.class
)
public TouchPoint clickTouchPoint; // 单击事件的点
@Relation(
parentColumn = "slide_start_point_id",
entityColumn = "pointId",
entity = TouchPoint.class
)
public TouchPoint slideStartTouchPoint; // 滑动事件的起点
@Relation(
parentColumn = "slide_end_point_id",
entityColumn = "pointId",
entity = TouchPoint.class
)
public TouchPoint slideEndTouchPoint; // 滑动事件的终点
}
}

View File

@ -0,0 +1,23 @@
package com.auto.clicker.autoclicker.room.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import com.auto.clicker.autoclicker.room.entity.EventEntity;
import java.util.List;
@Dao
public interface EventEntityDao {
@Insert
long insertEventEntity(EventEntity eventEntity); // 返回新插入的 eventId
@Delete
int deleteEventEntity(EventEntity eventEntity);
// 查询某个方案下的所有事件实体按顺序排序
@Query("SELECT * FROM events WHERE solution_id = :solutionId ORDER BY order_in_solution ASC")
List<EventEntity> getEventsForSolution(long solutionId);
}

View File

@ -0,0 +1,44 @@
package com.auto.clicker.autoclicker.room.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Delete;
import androidx.room.Query;
import androidx.room.Transaction;
import androidx.room.Update;
import com.auto.clicker.autoclicker.room.SolutionWithEventsAndPoints;
import com.auto.clicker.autoclicker.room.entity.Solution;
import java.util.List;
@Dao
public interface SolutionDao {
@Insert
long insertSolution(Solution solution); // 返回新插入的 solutionId
@Query("SELECT * FROM solutions")
List<Solution> getAllSolutions();
@Query("SELECT * FROM solutions WHERE solutionId = :solutionId LIMIT 1")
Solution getSolutionById(long solutionId);
@Query("SELECT * FROM solutions WHERE solution_name = :solutionName LIMIT 1")
Solution getSolutionByName(String solutionName);
@Delete
int deleteSolution(Solution solution); // 返回删除的行数
@Update
int updateSolution(Solution solution); // 返回更新的行数
// SolutionDao 中添加
@Transaction // 表示这是一个事务操作确保数据一致性
@Query("SELECT * FROM solutions WHERE solutionId = :solutionId LIMIT 1")
SolutionWithEventsAndPoints getSolutionWithEventsAndPoints(long solutionId);
// 也可以获取所有方案及其事件
@Transaction
@Query("SELECT * FROM solutions")
List<SolutionWithEventsAndPoints> getAllSolutionsWithEventsAndPoints();
}

View File

@ -0,0 +1,24 @@
package com.auto.clicker.autoclicker.room.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import androidx.room.Delete;
import com.auto.clicker.autoclicker.room.entity.TouchPoint;
@Dao
public interface TouchPointDao {
@Insert
long insertPoint(TouchPoint touchPoint); // 返回新插入的 pointId
@Query("SELECT * FROM touchPoints WHERE pointId = :pointId LIMIT 1")
TouchPoint getPointById(long pointId);
@Delete
int deletePoint(TouchPoint touchPoint);
@Update
int updatePoint(TouchPoint touchPoint);
}

View File

@ -0,0 +1,129 @@
package com.auto.clicker.autoclicker.room.entity;
import static androidx.room.ForeignKey.CASCADE;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.PrimaryKey;
@Entity(tableName = "events",
foreignKeys = {
@ForeignKey(entity = Solution.class,
parentColumns = "solutionId",
childColumns = "solution_id",
onDelete = CASCADE),
@ForeignKey(entity = TouchPoint.class,
parentColumns = "pointId",
childColumns = "click_point_id",
onDelete = CASCADE),
@ForeignKey(entity = TouchPoint.class,
parentColumns = "pointId",
childColumns = "slide_start_point_id",
onDelete = CASCADE),
@ForeignKey(entity = TouchPoint.class,
parentColumns = "pointId",
childColumns = "slide_end_point_id",
onDelete = CASCADE)
})
public class EventEntity {
@PrimaryKey(autoGenerate = true)
public long eventId;
@ColumnInfo(name = "solution_id")
public long solutionId;
@ColumnInfo(name = "event_type")
public String eventType;
@ColumnInfo(name = "order_in_solution")
public int orderInSolution;
@ColumnInfo(name = "click_point_id")
public Long clickPointId;
@ColumnInfo(name = "slide_start_point_id")
public Long slideStartPointId;
@ColumnInfo(name = "slide_end_point_id")
public Long slideEndPointId;
@ColumnInfo(name = "duration")
public long duration;
public EventEntity(long solutionId, String eventType, int orderInSolution,
Long clickPointId, Long slideStartPointId, Long slideEndPointId, long duration) {
this.solutionId = solutionId;
this.eventType = eventType;
this.orderInSolution = orderInSolution;
this.clickPointId = clickPointId;
this.slideStartPointId = slideStartPointId;
this.slideEndPointId = slideEndPointId;
this.duration = duration;
}
public long getEventId() {
return eventId;
}
public void setEventId(long eventId) {
this.eventId = eventId;
}
public long getSolutionId() {
return solutionId;
}
public void setSolutionId(long solutionId) {
this.solutionId = solutionId;
}
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
public int getOrderInSolution() {
return orderInSolution;
}
public void setOrderInSolution(int orderInSolution) {
this.orderInSolution = orderInSolution;
}
public Long getClickPointId() {
return clickPointId;
}
public void setClickPointId(Long clickPointId) {
this.clickPointId = clickPointId;
}
public Long getSlideStartPointId() {
return slideStartPointId;
}
public void setSlideStartPointId(Long slideStartPointId) {
this.slideStartPointId = slideStartPointId;
}
public Long getSlideEndPointId() {
return slideEndPointId;
}
public void setSlideEndPointId(Long slideEndPointId) {
this.slideEndPointId = slideEndPointId;
}
public long getDuration() {
return duration;
}
public void setDuration(long duration) {
this.duration = duration;
}
}

View File

@ -0,0 +1,27 @@
package com.auto.clicker.autoclicker.room.entity;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "solutions")
public class Solution {
@PrimaryKey(autoGenerate = true)
public long solutionId;
@ColumnInfo(name = "solution_name")
public String solutionName;
public Solution(String solutionName) {
this.solutionName = solutionName;
}
public long getSolutionId() { return solutionId; }
public void setSolutionId(long solutionId) { this.solutionId = solutionId; }
public String getSolutionName() { return solutionName; }
public void setSolutionName(String solutionName) { this.solutionName = solutionName; }
}

View File

@ -0,0 +1,31 @@
package com.auto.clicker.autoclicker.room.entity;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "touchPoints")
public class TouchPoint {
@PrimaryKey(autoGenerate = true)
public long pointId;
@ColumnInfo(name = "x_coordinate")
public int x;
@ColumnInfo(name = "y_coordinate")
public int y;
public TouchPoint(int x, int y) {
this.x = x;
this.y = y;
}
public long getPointId() { return pointId; }
public void setPointId(long pointId) { this.pointId = pointId; }
public int getX() { return x; }
public void setX(int x) { this.x = x; }
public int getY() { return y; }
public void setY(int y) { this.y = y; }
}

View File

@ -0,0 +1,169 @@
package com.auto.clicker.autoclicker.room.repository;
import android.content.Context;
import com.auto.clicker.autoclicker.data.Event;
import com.auto.clicker.autoclicker.data.EventWrapper;
import com.auto.clicker.autoclicker.data.PointEvent;
import com.auto.clicker.autoclicker.data.SlideEvent;
import com.auto.clicker.autoclicker.room.AppDatabase;
import com.auto.clicker.autoclicker.room.SolutionWithEventsAndPoints;
import com.auto.clicker.autoclicker.room.dao.EventEntityDao;
import com.auto.clicker.autoclicker.room.dao.TouchPointDao;
import com.auto.clicker.autoclicker.room.dao.SolutionDao;
import com.auto.clicker.autoclicker.room.entity.EventEntity;
import com.auto.clicker.autoclicker.room.entity.TouchPoint;
import com.auto.clicker.autoclicker.room.entity.Solution;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.content.Context;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.util.Log; // Add Log import for debugging
public class EventRepository {
private final SolutionDao solutionDao;
private final TouchPointDao touchPointDao; // Renamed from PointDao
private final EventEntityDao eventEntityDao;
private final ExecutorService databaseWriteExecutor;
public EventRepository(Context context) {
AppDatabase db = AppDatabase.getInstance(context); // Updated to getInstance
this.solutionDao = db.solutionDao();
this.touchPointDao = db.pointDao(); // Assuming db.pointDao() returns TouchPointDao now
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
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
long eventEntityId = -1; // To store the result of event entity insertion
if (wrapper.getType() == EventWrapper.EventType.POINT && event instanceof PointEvent) {
PointEvent pointEvent = (PointEvent) event;
TouchPoint touchPoint = new TouchPoint(pointEvent.getX(), pointEvent.getY());
long pointId = touchPointDao.insertPoint(touchPoint);
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
wrapper.getType().name(),
order,
pointId, // clickPointId
null, // slideStartPointId (null for point event)
null, // slideEndPointId (null for point event)
0 // duration (0 for point event)
);
eventEntityId = eventEntityDao.insertEventEntity(eventEntity);
if (eventEntityId == -1)
throw new RuntimeException("Failed to insert EventEntity for PointEvent.");
} else if (wrapper.getType() == EventWrapper.EventType.SLIDE && event instanceof SlideEvent) {
SlideEvent slideEvent = (SlideEvent) event;
TouchPoint startTouchPoint = new TouchPoint(slideEvent.getStartPoint().getX(), slideEvent.getStartPoint().getY());
long startPointId = touchPointDao.insertPoint(startTouchPoint);
if (startPointId == -1)
throw new RuntimeException("Failed to insert start touch point for SlideEvent.");
TouchPoint endTouchPoint = new TouchPoint(slideEvent.getEndPoint().getX(), slideEvent.getEndPoint().getY());
long endPointId = touchPointDao.insertPoint(endTouchPoint);
if (endPointId == -1)
throw new RuntimeException("Failed to insert end touch point for SlideEvent.");
// 调用 EventEntity 的唯一构造函数将点击相关字段设为 null
EventEntity eventEntity = new EventEntity(
(int) solutionId,
wrapper.getType().name(),
order,
null,
startPointId,
endPointId,
0
);
eventEntityId = eventEntityDao.insertEventEntity(eventEntity);
if (eventEntityId == -1)
throw new RuntimeException("Failed to insert EventEntity for SlideEvent.");
}
}
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);
}
}
});
}
// --- 查询操作 ---
public void getAllSolutions(RepositoryCallback<List<Solution>> callback) {
databaseWriteExecutor.execute(() -> {
try {
List<Solution> solutions = solutionDao.getAllSolutions();
if (callback != null) callback.onComplete(solutions);
} catch (Exception e) {
Log.e("EventRepository", "Error getting all solutions: " + e.getMessage(), e);
if (callback != null) callback.onError(e);
}
});
}
public void getSolutionWithEventsAndPoints(long solutionId, RepositoryCallback<SolutionWithEventsAndPoints> callback) {
databaseWriteExecutor.execute(() -> {
try {
SolutionWithEventsAndPoints data = solutionDao.getSolutionWithEventsAndPoints(solutionId);
if (callback != null) callback.onComplete(data);
} catch (Exception e) {
Log.e("EventRepository", "Error getting solution with events and points: " + e.getMessage(), e);
if (callback != null) callback.onError(e);
}
});
}
// --- 删除操作 ---
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);
}
});
}
// 假设的回调接口可以根据需要自定义
public interface RepositoryCallback<T> {
void onComplete(T result);
void onError(Exception e);
}
}

View File

@ -239,7 +239,7 @@ public class AutoClickService extends AccessibilityService {
} }
private void flashTouchFeedback(int index) { private void flashTouchFeedback(int index) {
Intent intent = new Intent("com.auto.autoclicker.FLASH_TOUCH_POINT"); Intent intent = new Intent("com.auto.clicker.autoclicker.FLASH_TOUCH_POINT");
intent.putExtra("index", index); intent.putExtra("index", index);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent); LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
} }
@ -257,7 +257,7 @@ public class AutoClickService extends AccessibilityService {
super.onDestroy(); super.onDestroy();
instance = null; instance = null;
stopClicking(); stopClicking();
Intent intent = new Intent("com.auto.autoclicker.SERVICE_DESTROYED"); Intent intent = new Intent("com.auto.clicker.autoclicker.SERVICE_DESTROYED");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent); LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
logDebug("无障碍服务已销毁"); logDebug("无障碍服务已销毁");
} }

View File

@ -1,109 +0,0 @@
package com.auto.clicker.autoclicker.ui.activity.floating; // 替换为你的实际包名
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater; // 新增
import android.view.View;
import android.view.WindowManager; // 新增用于设置 Window 属性
import android.widget.ImageView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; // 继续使用 AppCompatActivity
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.viewpager2.widget.ViewPager2; // 新增
import com.auto.clicker.autoclicker.R;
import com.auto.clicker.autoclicker.ui.adapter.floating.TabPagerAdapter;
import com.auto.clicker.autoclicker.ui.fragment.floating.LoadScriptFragment;
import com.auto.clicker.autoclicker.ui.fragment.floating.SaveScriptFragment;
import com.google.android.material.tabs.TabLayout; // 新增
import com.google.android.material.tabs.TabLayoutMediator; // 新增
public class TransparentFloatingActivity extends AppCompatActivity {
private static final String TAG = "TransparentFloatingAct";
public static final String ACTION_CLOSE_FLOATING_TAB_DIALOG = "com.auto.autoclicker.CLOSE_FLOATING_TAB_DIALOG";
// 不再需要 FloatingTabDialogManager 实例直接在这里处理 UI
private View dialogContentView; // 承载整个弹窗内容的 View
// 接收关闭指令的广播接收器
private BroadcastReceiver closeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_CLOSE_FLOATING_TAB_DIALOG.equals(intent.getAction())) {
Log.d(TAG, "收到关闭浮动标签页弹窗的广播");
finish(); // 关闭自身 Activity
}
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置 Activity 的窗口属性使其看起来像一个对话框
// 注意这里的设置可能与主题中的部分重复但可以作为额外的控制
WindowManager.LayoutParams params = getWindow().getAttributes();
params.gravity = android.view.Gravity.CENTER; // 确保居中
params.flags &= ~WindowManager.LayoutParams.FLAG_DIM_BEHIND; // 移除背景变暗如果主题没有完全实现
params.width = WindowManager.LayoutParams.WRAP_CONTENT; // 根据内容调整宽度
params.height = WindowManager.LayoutParams.WRAP_CONTENT; // 根据内容调整高度
getWindow().setAttributes(params);
getWindow().setBackgroundDrawableResource(android.R.color.transparent); // 确保背景透明
// 加载你的弹窗布局
setContentView(R.layout.floating_tab_dialog);
dialogContentView = findViewById(android.R.id.content); // 获取根视图如果你的布局没有特定ID
Log.d(TAG, "TransparentFloatingActivity onCreate");
// 初始化 TabLayout ViewPager2
TabLayout tabLayout = findViewById(R.id.tab_layout);
ViewPager2 viewPager = findViewById(R.id.view_pager);
TabPagerAdapter pagerAdapter = new TabPagerAdapter(this); // 传入当前的 Activity 作为 FragmentActivity
pagerAdapter.addFragment(new SaveScriptFragment(), "选项卡一");
pagerAdapter.addFragment(new LoadScriptFragment(), "选项卡二");
// 添加更多 Fragment...
viewPager.setAdapter(pagerAdapter);
new TabLayoutMediator(tabLayout, viewPager,
(tab, position) -> tab.setText(pagerAdapter.getPageTitle(position))
).attach();
// 假设你在 floating_tab_dialog.xml 中有一个关闭按钮ID close_dialog_button
ImageView closeDialogButton = findViewById(R.id.close_dialog_button);
if (closeDialogButton != null) {
closeDialogButton.setOnClickListener(v -> {
// 点击关闭按钮时直接结束这个 Activity
finish();
});
}
// 注册广播接收器
LocalBroadcastManager.getInstance(this).registerReceiver(closeReceiver,
new IntentFilter(ACTION_CLOSE_FLOATING_TAB_DIALOG));
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "TransparentFloatingActivity onDestroy");
// 注销广播接收器
LocalBroadcastManager.getInstance(this).unregisterReceiver(closeReceiver);
}
// 移除这个方法因为现在 Activity 自己会关闭
// public static void sendCloseBroadcast(Context context) {
// Intent intent = new Intent(ACTION_CLOSE_FLOATING_TAB_DIALOG);
// LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
// }
}

View File

@ -1,35 +0,0 @@
package com.auto.clicker.autoclicker.ui.adapter.dialog;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.ViewHolder> {
private final List<View> views;
public ViewPagerAdapter(List<View> views) {
this.views = views;
}
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(views.get(viewType));
}
@Override public void onBindViewHolder(ViewHolder holder, int position) {}
@Override public int getItemCount() {
return views.size();
}
@Override public int getItemViewType(int position) {
return position;
}
static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) { super(itemView); }
}
}

View File

@ -1,39 +0,0 @@
package com.auto.clicker.autoclicker.ui.adapter.floating;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import java.util.ArrayList;
import java.util.List;
public class TabPagerAdapter extends FragmentStateAdapter {
private final List<Fragment> fragmentList = new ArrayList<>();
private final List<String> fragmentTitleList = new ArrayList<>();
public TabPagerAdapter(@NonNull FragmentActivity fragmentActivity) { // 保持接收 FragmentActivity
super(fragmentActivity);
}
public void addFragment(Fragment fragment, String title) {
fragmentList.add(fragment);
fragmentTitleList.add(title);
}
@NonNull
@Override
public Fragment createFragment(int position) {
return fragmentList.get(position);
}
@Override
public int getItemCount() {
return fragmentList.size();
}
public CharSequence getPageTitle(int position) {
return fragmentTitleList.get(position);
}
}

View File

@ -3,26 +3,20 @@ package com.auto.clicker.autoclicker.ui.adapter.script;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.auto.clicker.autoclicker.R; import com.auto.clicker.autoclicker.R;
import com.auto.clicker.autoclicker.dialog.LoadScriptView; import com.auto.clicker.autoclicker.room.entity.Solution;
import com.auto.clicker.autoclicker.room.ScriptEntity;
import java.util.List; import java.util.List;
public class ScriptListAdapter extends RecyclerView.Adapter<ScriptListAdapter.ViewHolder> { public class ScriptListAdapter extends RecyclerView.Adapter<ScriptListAdapter.ViewHolder> {
private List<ScriptEntity> scripts; private List<Solution> solutionList;
private LoadScriptView.OnScriptClickListener listener;
public ScriptListAdapter(List<Solution> solutionList) {
public ScriptListAdapter(List<ScriptEntity> scripts, LoadScriptView.OnScriptClickListener listener) { this.solutionList = solutionList;
this.scripts = scripts;
this.listener = listener;
} }
@NonNull @NonNull
@ -40,7 +34,7 @@ public class ScriptListAdapter extends RecyclerView.Adapter<ScriptListAdapter.Vi
@Override @Override
public int getItemCount() { public int getItemCount() {
return scripts.size(); return solutionList.size();
} }
static class ViewHolder extends RecyclerView.ViewHolder { static class ViewHolder extends RecyclerView.ViewHolder {

View File

@ -0,0 +1,71 @@
package com.auto.clicker.autoclicker.ui.floating;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.auto.clicker.autoclicker.room.entity.Solution;
import java.util.List;
public class SolutionAdapter extends RecyclerView.Adapter<SolutionAdapter.SolutionViewHolder> {
private List<Solution> solutionList;
private OnSolutionClickListener listener;
// 定义接口用于处理点击事件
public interface OnSolutionClickListener {
void onSolutionClick(Solution solution);
}
public SolutionAdapter(List<Solution> solutionList, OnSolutionClickListener listener) {
this.solutionList = solutionList;
this.listener = listener;
}
@NonNull
@Override
public SolutionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(android.R.layout.simple_list_item_1, parent, false); // 可以自定义更复杂的item布局
return new SolutionViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull SolutionViewHolder holder, int position) {
Solution solution = solutionList.get(position);
holder.solutionNameTextView.setText(solution.getSolutionName());
holder.itemView.setOnClickListener(v -> {
if (listener != null) {
listener.onSolutionClick(solution);
}
});
}
@Override
public int getItemCount() {
return solutionList.size();
}
public void setSolutions(List<Solution> newSolutions) {
this.solutionList.clear();
if (newSolutions != null) {
this.solutionList.addAll(newSolutions);
}
notifyDataSetChanged();
}
static class SolutionViewHolder extends RecyclerView.ViewHolder {
TextView solutionNameTextView;
SolutionViewHolder(View itemView) {
super(itemView);
// simple_list_item_1 只有一个 TextView
solutionNameTextView = itemView.findViewById(android.R.id.text1);
}
}
}

View File

@ -3,126 +3,211 @@ package com.auto.clicker.autoclicker.util;
import android.content.Context; import android.content.Context;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.os.Build; import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log; import android.util.Log;
import android.view.ContextThemeWrapper; import android.view.ContextThemeWrapper;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button; import android.widget.Button;
import android.widget.FrameLayout; import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.auto.clicker.autoclicker.R; import com.auto.clicker.autoclicker.R;
import com.auto.clicker.autoclicker.room.entity.Solution;
import com.auto.clicker.autoclicker.room.repository.EventRepository;
import com.auto.clicker.autoclicker.ui.floating.SolutionAdapter;
import com.auto.clicker.autoclicker.view.FloatingViewManager;
import java.util.ArrayList;
import java.util.List;
public class FloatingTabDialogManager { public class FloatingTabDialogManager {
private static final String TAG = "FloatingTabDialogMgr"; private static final String TAG = "FloatingTabDialogMgr";
private final Context originalContext; // 保存原始的 Service Context private final Context context;
private final WindowManager windowManager; private final WindowManager windowManager;
private LinearLayout floatingDialogView; private LinearLayout floatingDialogView;
private WindowManager.LayoutParams floatingDialogParams; private WindowManager.LayoutParams floatingDialogParams;
private Button tabOneButton; private Button saveTab;
private Button tabTwoButton; private Button loadTab;
private FrameLayout contentFrame; private View savePage;
private View pageOneView; private View loadPage;
private View pageTwoView;
private FloatingViewManager floatingViewManager;
private EditText inputName;
private Button saveButton;
private Button closeButton;
private RecyclerView loadRecyclerview;
private SolutionAdapter solutionsAdapter;
private List<Solution> solutionList = new ArrayList<>();
public FloatingTabDialogManager(Context context) { public FloatingTabDialogManager(Context context) {
this.originalContext = context; // 保存原始 Context this.context = context;
this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
} }
public void setFloatingViewManager(FloatingViewManager manager) {
this.floatingViewManager = manager;
}
public void showFloatingTabDialog() { public void showFloatingTabDialog() {
if (floatingDialogView != null) { if (floatingDialogView != null) {
removeFloatingTabDialog(); // 如果已经显示先移除 removeFloatingTabDialog();
} }
// --- 关键修改使用 ContextThemeWrapper 包裹 Service Context --- Context themedContext = new ContextThemeWrapper(context, R.style.Theme_AutoClicker);
Context themedContext = new ContextThemeWrapper(originalContext, R.style.Theme_AutoClicker);
// 或者如果你想用 Material3 的默认主题但你的 Theme_AutoClicker 已经继承了 Material3那用它就行
// Context themedContext = new ContextThemeWrapper(originalContext, R.style.Theme_Material3_DayNight_NoActionBar); // 也可以直接用这个
floatingDialogView = (LinearLayout) LayoutInflater.from(themedContext).inflate(R.layout.floating_tab_dialog, null); floatingDialogView = (LinearLayout) LayoutInflater.from(themedContext).inflate(R.layout.floating_tab_dialog, null);
// --- 关键修改结束 --- floatingDialogParams = createFloatingDialogParams(); // 创建悬浮窗参数
floatingDialogParams = createFloatingDialogParams(); saveTab = floatingDialogView.findViewById(R.id.tab_one_button);
loadTab = floatingDialogView.findViewById(R.id.tab_two_button);
savePage = floatingDialogView.findViewById(R.id.page_one_view);
loadPage = floatingDialogView.findViewById(R.id.page_two_view);
// 获取视图组件 saveTab.setOnClickListener(v -> showPage(0));
tabOneButton = floatingDialogView.findViewById(R.id.tab_one_button); loadTab.setOnClickListener(v -> showPage(1));
tabTwoButton = floatingDialogView.findViewById(R.id.tab_two_button);
contentFrame = floatingDialogView.findViewById(R.id.content_frame);
pageOneView = floatingDialogView.findViewById(R.id.page_one_view);
pageTwoView = floatingDialogView.findViewById(R.id.page_two_view);
ImageView closeDialogButton = floatingDialogView.findViewById(R.id.close_dialog_button);
// 设置标签按钮的点击事件 inputName = savePage.findViewById(R.id.script_name_input);
tabOneButton.setOnClickListener(v -> showPage(0)); saveButton = savePage.findViewById(R.id.save);
tabTwoButton.setOnClickListener(v -> showPage(1)); closeButton = savePage.findViewById(R.id.close);
// 设置关闭按钮的点击事件 saveButton.setOnClickListener(v -> {
if (closeDialogButton != null) { String solutionName = inputName.getText().toString().trim();
closeDialogButton.setOnClickListener(v -> {
removeFloatingTabDialog(); // 点击关闭按钮时移除弹窗 if (floatingViewManager != null) {
}); floatingViewManager.saveCurrentEventsAsSolution(solutionName, new EventRepository.RepositoryCallback<Long>() {
@Override
public void onComplete(Long solutionId) {
if (solutionId != -1) {
Toast.makeText(context, "方案 '" + solutionName + "' 保存成功!", Toast.LENGTH_SHORT).show();
removeFloatingTabDialog();
} else {
Toast.makeText(context, "保存方案失败。", Toast.LENGTH_SHORT).show();
}
} }
// 默认显示第一个页面 @Override
showPage(0); public void onError(Exception e) {
Log.e(TAG, "保存方案失败: " + e.getMessage(), e);
Toast.makeText(context, "保存方案失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
// 只在验证通过时才显示
Toast.makeText(context, "正在保存方案...", Toast.LENGTH_SHORT).show();
}
});
closeButton.setOnClickListener(v -> removeFloatingTabDialog());
loadRecyclerview = loadPage.findViewById(R.id.script_list);
loadRecyclerview.setLayoutManager(new LinearLayoutManager(context));
solutionsAdapter = new SolutionAdapter(solutionList, solution -> {
if (floatingViewManager != null) {
floatingViewManager.loadSolution(solution.solutionId);
Toast.makeText(context, "正在加载方案: " + solution.getSolutionName(), Toast.LENGTH_SHORT).show();
} else {
Log.e(TAG, "FloatingViewManager 未设置!无法加载方案。");
Toast.makeText(context, "加载功能不可用,请联系开发者。", Toast.LENGTH_SHORT).show();
}
removeFloatingTabDialog();
});
loadRecyclerview.setAdapter(solutionsAdapter);
showPage(0); // 默认显示保存页面
loadSolutionsForList(); // 加载方案列表以便切换到加载页面时可以直接显示
try { try {
windowManager.addView(floatingDialogView, floatingDialogParams); windowManager.addView(floatingDialogView, floatingDialogParams);
Log.d(TAG, "浮动标签页弹窗已显示"); Log.d(TAG, "浮动标签页弹窗已显示");
} catch (WindowManager.BadTokenException e) { } catch (WindowManager.BadTokenException e) {
Log.e(TAG, "无法添加浮动标签页弹窗,可能是权限问题或上下文无效", e); Log.e(TAG, "无法添加浮动标签页弹窗,可能是权限问题或上下文无效", e);
Toast.makeText(context, "显示弹窗失败,请检查权限。", Toast.LENGTH_SHORT).show();
} catch (SecurityException e) { } catch (SecurityException e) {
Log.e(TAG, "需要悬浮窗权限才能显示浮动弹窗", e); Log.e(TAG, "需要悬浮窗权限才能显示浮动弹窗", e);
// 引导用户去开启权限如果你在 FloatingViewManager 中已经处理了这里可以省略 Toast.makeText(context, "请授予悬浮窗权限以便显示。", Toast.LENGTH_SHORT).show();
// Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
// Uri.parse("package:" + context.getPackageName()));
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// context.startActivity(intent);
} }
} }
private void showPage(int pageIndex) { private void showPage(int pageIndex) {
// 确保所有视图都已正确加载例如通过 inflated 后的 findViewById if (savePage == null || loadPage == null) {
if (pageOneView == null || pageTwoView == null) {
Log.e(TAG, "页面视图未初始化!"); Log.e(TAG, "页面视图未初始化!");
return; return;
} }
// 隐藏所有页面 // 隐藏所有页面
pageOneView.setVisibility(View.GONE); savePage.setVisibility(View.GONE);
pageTwoView.setVisibility(View.GONE); loadPage.setVisibility(View.GONE);
// 重置所有标签按钮的颜色/背景如果需要视觉反馈 // 重置Tab按钮颜色
// 注意这里使用 getContext().getResources().getColor() 是在 themedContext 的上下文中获取颜色 saveTab.setTextColor(context.getResources().getColor(android.R.color.darker_gray));
// 如果你的颜色定义在 themes.xml colors.xml 并且依赖于主题属性那么需要确保主题是正确的 loadTab.setTextColor(context.getResources().getColor(android.R.color.darker_gray));
tabOneButton.setTextColor(originalContext.getResources().getColor(android.R.color.darker_gray)); // originalContext themedContext
tabTwoButton.setTextColor(originalContext.getResources().getColor(android.R.color.darker_gray)); // 最好用 themedContext
// 显示选定页面并更新标签样式
switch (pageIndex) { switch (pageIndex) {
case 0: case 0: // 保存页面
pageOneView.setVisibility(View.VISIBLE); savePage.setVisibility(View.VISIBLE);
tabOneButton.setTextColor(originalContext.getResources().getColor(android.R.color.black)); // 选中颜色 saveTab.setTextColor(context.getResources().getColor(android.R.color.black));
// 在显示保存页面时尝试显示键盘
showKeyboard();
break; break;
case 1: case 1: // 加载页面
pageTwoView.setVisibility(View.VISIBLE); loadPage.setVisibility(View.VISIBLE);
tabTwoButton.setTextColor(originalContext.getResources().getColor(android.R.color.black)); // 选中颜色 loadTab.setTextColor(context.getResources().getColor(android.R.color.black));
loadSolutionsForList();
break; break;
// 添加更多页面
} }
} }
private void loadSolutionsForList() {
if (floatingViewManager == null) {
Log.e(TAG, "FloatingViewManager 未设置,无法加载方案列表。");
// 确保UI操作在主线程
new Handler(Looper.getMainLooper()).post(() ->
Toast.makeText(context, "加载方案列表功能不可用。", Toast.LENGTH_SHORT).show()
);
return;
}
floatingViewManager.getAllSolutions(new EventRepository.RepositoryCallback<List<Solution>>() {
@Override
public void onComplete(List<Solution> solutions) {
// UI 更新操作包括更新适配器和显示Toast切换到主线程
new Handler(Looper.getMainLooper()).post(() -> {
solutionsAdapter.setSolutions(solutions);
if (solutions.isEmpty()) {
Toast.makeText(context, "没有可加载的方案。", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onError(Exception e) {
// UI 更新操作显示Toast切换到主线程
new Handler(Looper.getMainLooper()).post(() -> {
Log.e(TAG, "加载方案列表失败: " + e.getMessage(), e);
Toast.makeText(context, "加载方案列表失败。", Toast.LENGTH_SHORT).show();
});
}
});
}
public void removeFloatingTabDialog() { public void removeFloatingTabDialog() {
if (floatingDialogView != null) { if (floatingDialogView != null) {
try { try {
windowManager.removeView(floatingDialogView); windowManager.removeView(floatingDialogView);
floatingDialogView = null; floatingDialogView = null; // 置空防止重复移除或空指针异常
Log.d(TAG, "浮动标签页弹窗已移除"); Log.d(TAG, "浮动标签页弹窗已移除");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
Log.w(TAG, "尝试移除不存在的视图", e); Log.w(TAG, "尝试移除不存在的视图", e);
@ -131,20 +216,30 @@ public class FloatingTabDialogManager {
} }
private WindowManager.LayoutParams createFloatingDialogParams() { private WindowManager.LayoutParams createFloatingDialogParams() {
// 创建浮动弹窗的参数
int overlayType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? int overlayType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_PHONE; WindowManager.LayoutParams.TYPE_PHONE;
WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT, // 宽度自适应内容 WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT, // 高度自适应内容 WindowManager.LayoutParams.WRAP_CONTENT,
overlayType, overlayType,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | // 如果希望点击弹窗外部能穿透则保持 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, // 确保它能接收触摸事件 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT PixelFormat.TRANSLUCENT
); );
params.gravity = Gravity.CENTER; // 居中显示 params.gravity = Gravity.CENTER;
return params; return params;
} }
private void showKeyboard() {
if (inputName != null) {
inputName.requestFocus(); // 确保输入框获取焦点
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
// 尝试强制显示键盘
imm.showSoftInput(inputName, InputMethodManager.SHOW_IMPLICIT);
}
}
}
} }

View File

@ -1,48 +0,0 @@
package com.auto.clicker.autoclicker.util;
import com.auto.clicker.autoclicker.data.EventWrapper;
import com.auto.clicker.autoclicker.data.PointEvent;
import com.auto.clicker.autoclicker.data.SlideEvent;
import com.auto.clicker.autoclicker.room.PointEventEntity;
import com.auto.clicker.autoclicker.room.SlideEventEntity;
import java.util.ArrayList;
import java.util.List;
public class ScriptConverter {
public static List<PointEventEntity> toPointEventEntities(List<EventWrapper> runtimeEvents, long scriptId) {
List<PointEventEntity> result = new ArrayList<>();
for (EventWrapper wrapper : runtimeEvents) {
if (wrapper.getType() == EventWrapper.EventType.POINT) {
PointEvent point = (PointEvent) wrapper.getEvent();
PointEventEntity entity = new PointEventEntity();
entity.x = point.getX();
entity.y = point.getY();
entity.order = wrapper.getOrder();
entity.scriptOwnerId = scriptId;
result.add(entity);
}
}
return result;
}
public static List<SlideEventEntity> toSlideEventEntities(List<EventWrapper> runtimeEvents, long scriptId) {
List<SlideEventEntity> result = new ArrayList<>();
for (EventWrapper wrapper : runtimeEvents) {
if (wrapper.getType() == EventWrapper.EventType.SLIDE) {
SlideEvent slide = (SlideEvent) wrapper.getEvent();
SlideEventEntity entity = new SlideEventEntity();
entity.startX = slide.getStartPoint().getX();
entity.startY = slide.getStartPoint().getY();
entity.endX = slide.getEndPoint().getX();
entity.endY = slide.getEndPoint().getY();
entity.order = wrapper.getOrder();
entity.scriptOwnerId = scriptId;
result.add(entity);
}
}
return result;
}
}

View File

@ -1,131 +0,0 @@
package com.auto.clicker.autoclicker.util;
import static com.auto.clicker.autoclicker.view.FloatingViewManager.TOUCH_POINT_SIZE;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Build;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.TextView;
import com.auto.clicker.autoclicker.R;
import com.auto.clicker.autoclicker.data.EventWrapper;
import com.auto.clicker.autoclicker.data.PointEvent;
import com.auto.clicker.autoclicker.data.SlideEvent;
import com.auto.clicker.autoclicker.room.PointEventEntity;
import com.auto.clicker.autoclicker.room.ScriptWithEvents;
import com.auto.clicker.autoclicker.room.SlideEventEntity;
import com.auto.clicker.autoclicker.view.ConnectingLineView;
import java.util.ArrayList;
import java.util.List;
public class ScriptRestorer {
private final Context context;
private final int screenWidth;
private final int screenHeight;
public ScriptRestorer(Context context, int screenWidth, int screenHeight) {
this.context = context;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
}
public List<EventWrapper> restoreRuntimeEvents(ScriptWithEvents script) {
List<EventWrapper> events = new ArrayList<>();
int counter = 0;
// 还原点事件
for (PointEventEntity entity : script.pointEvents) {
TextView view = createTouchPointView(String.valueOf(entity.order));
WindowManager.LayoutParams params = createTouchPointParams();
params.x = entity.x;
params.y = entity.y;
PointEvent pointEvent = new PointEvent(entity.order, entity.x, entity.y, view, params);
events.add(new EventWrapper(EventWrapper.EventType.POINT, pointEvent, entity.order));
}
// 还原滑动事件
for (SlideEventEntity entity : script.slideEvents) {
// 创建起点和终点
TextView startView = createTouchPointView(String.valueOf(entity.order));
TextView endView = createTouchPointView(String.valueOf(entity.order));
WindowManager.LayoutParams startParams = createTouchPointParams();
WindowManager.LayoutParams endParams = createTouchPointParams();
startParams.x = entity.startX;
startParams.y = entity.startY;
endParams.x = entity.endX;
endParams.y = entity.endY;
PointEvent startEvent = new PointEvent(entity.order, entity.startX, entity.startY, startView, startParams);
PointEvent endEvent = new PointEvent(entity.order, entity.endX, entity.endY, endView, endParams);
// 创建连接线
ConnectingLineView lineView = new ConnectingLineView(context);
WindowManager.LayoutParams lineParams = createLineViewParams();
SlideEvent slideEvent = new SlideEvent(entity.order, startEvent, endEvent, lineView, lineParams);
events.add(new EventWrapper(EventWrapper.EventType.SLIDE, slideEvent, entity.order));
}
return events;
}
// 复制你的原始方法
private TextView createTouchPointView(String label) {
// 创建一个圆形点击点视图
TextView touchPointView = new TextView(context);
touchPointView.setBackgroundResource(R.drawable.ring_has_bg);
touchPointView.setGravity(Gravity.CENTER);
touchPointView.setText(label);
touchPointView.setTextColor(Color.WHITE);
touchPointView.setTextSize(19);
return touchPointView;
}
private WindowManager.LayoutParams createTouchPointParams() {
// 创建点击点的布局参数
int overlayType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_PHONE;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
TOUCH_POINT_SIZE, TOUCH_POINT_SIZE,
overlayType,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT
);
params.gravity = Gravity.TOP | Gravity.START;
return params;
}
private WindowManager.LayoutParams createLineViewParams() {
// 创建连接线滑动路径的参数全屏不可点击
int overlayType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_PHONE;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
overlayType,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT
);
params.gravity = Gravity.TOP | Gravity.START;
params.x = 0;
params.y = 0;
return params;
}
}

View File

@ -36,10 +36,13 @@ import com.auto.clicker.autoclicker.data.EventWrapper;
import com.auto.clicker.autoclicker.data.PointEvent; import com.auto.clicker.autoclicker.data.PointEvent;
import com.auto.clicker.autoclicker.data.PositionUpdater; import com.auto.clicker.autoclicker.data.PositionUpdater;
import com.auto.clicker.autoclicker.data.SlideEvent; import com.auto.clicker.autoclicker.data.SlideEvent;
import com.auto.clicker.autoclicker.dialog.ScriptManagerDialog; 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.service.AutoClickService; import com.auto.clicker.autoclicker.service.AutoClickService;
import com.auto.clicker.autoclicker.service.ForegroundService; import com.auto.clicker.autoclicker.service.ForegroundService;
import com.auto.clicker.autoclicker.ui.activity.floating.TransparentFloatingActivity;
import com.auto.clicker.autoclicker.util.FloatingTabDialogManager; import com.auto.clicker.autoclicker.util.FloatingTabDialogManager;
import com.auto.clicker.autoclicker.util.PrefUtils; import com.auto.clicker.autoclicker.util.PrefUtils;
import com.auto.clicker.autoclicker.util.ScreenUtils; import com.auto.clicker.autoclicker.util.ScreenUtils;
@ -71,6 +74,8 @@ public class FloatingViewManager {
private FloatingTabDialogManager floatingTabDialogManager; private FloatingTabDialogManager floatingTabDialogManager;
private EventRepository eventRepository;
public FloatingViewManager(Context context) { public FloatingViewManager(Context context) {
this.context = context; this.context = context;
this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@ -78,6 +83,8 @@ public class FloatingViewManager {
this.screenWidth = screenSize.x; this.screenWidth = screenSize.x;
this.screenHeight = screenSize.y; this.screenHeight = screenSize.y;
this.floatingTabDialogManager = new FloatingTabDialogManager(context); this.floatingTabDialogManager = new FloatingTabDialogManager(context);
this.floatingTabDialogManager.setFloatingViewManager(this);
this.eventRepository = new EventRepository(context.getApplicationContext());
registerBroadcastReceiver(); registerBroadcastReceiver();
} }
@ -266,7 +273,7 @@ public class FloatingViewManager {
} }
// 创建点击点视图 // 创建点击点视图
TextView point = createTouchPointView(String.valueOf(eventOrderCounter)); TextView pointView = createTouchPointView(String.valueOf(eventOrderCounter));
// 设置位置居中 // 设置位置居中
WindowManager.LayoutParams params = createTouchPointParams(); WindowManager.LayoutParams params = createTouchPointParams();
@ -274,15 +281,18 @@ public class FloatingViewManager {
params.y = screenHeight / 2 - TOUCH_POINT_SIZE / 2; params.y = screenHeight / 2 - TOUCH_POINT_SIZE / 2;
// 包装为事件对象 // 包装为事件对象
PointEvent pointEvent = new PointEvent(eventOrderCounter, params.x, params.y, point, params); PointEvent pointEvent = new PointEvent(eventOrderCounter, params.x, params.y);
pointEvent.setView(pointView);
pointEvent.setLayoutParams(params);
runtimeEvents.add(new EventWrapper(EventWrapper.EventType.POINT, pointEvent, eventOrderCounter)); runtimeEvents.add(new EventWrapper(EventWrapper.EventType.POINT, pointEvent, eventOrderCounter));
eventOrderCounter++; eventOrderCounter++;
Log.d(TAG, "addPoint runtimeEvents: " + runtimeEvents.size() + " eventOrderCounter :" + eventOrderCounter); Log.d(TAG, "addPoint runtimeEvents: " + runtimeEvents.size() + " eventOrderCounter :" + eventOrderCounter);
// 设置拖动功能并添加到窗口 // 设置拖动功能并添加到窗口
setupDraggablePoint(point, params, pointEvent); setupDraggablePoint(pointView, params, pointEvent);
windowManager.addView(point, params); windowManager.addView(pointView, params);
updateServicePositions(); updateServicePositions();
} }
@ -294,37 +304,44 @@ public class FloatingViewManager {
} }
// 创建起点 // 创建起点
TextView startPoint = createTouchPointView(String.valueOf(eventOrderCounter)); TextView startPointView = createTouchPointView(String.valueOf(eventOrderCounter));
WindowManager.LayoutParams startParams = createTouchPointParams(); WindowManager.LayoutParams startParams = createTouchPointParams();
startParams.x = screenWidth / 2 - 100; startParams.x = screenWidth / 2 - 100;
startParams.y = screenHeight / 2 - 50; startParams.y = screenHeight / 2 - 50;
PointEvent startPointEvent = new PointEvent(eventOrderCounter, startParams.x, startParams.y, startPoint, startParams); PointEvent startPointEvent = new PointEvent(eventOrderCounter, startParams.x, startParams.y);
startPointEvent.setView(startPointView);
startPointEvent.setLayoutParams(startParams);
// 创建终点 // 创建终点
TextView endPoint = createTouchPointView(String.valueOf(eventOrderCounter)); TextView endPointView = createTouchPointView(String.valueOf(eventOrderCounter));
WindowManager.LayoutParams endParams = createTouchPointParams(); WindowManager.LayoutParams endParams = createTouchPointParams();
endParams.x = screenWidth / 2 + 100; endParams.x = screenWidth / 2 + 100;
endParams.y = screenHeight / 2 - 50; endParams.y = screenHeight / 2 - 50;
PointEvent endPointEvent = new PointEvent(eventOrderCounter, endParams.x, endParams.y, endPoint, endParams); PointEvent endPointEvent = new PointEvent(eventOrderCounter, endParams.x, endParams.y);
startPointEvent.setView(endPointView);
startPointEvent.setLayoutParams(endParams);
// 创建滑动连线 // 创建滑动连线
ConnectingLineView lineView = new ConnectingLineView(context); ConnectingLineView lineView = new ConnectingLineView(context);
WindowManager.LayoutParams lineParams = createLineViewParams(); WindowManager.LayoutParams lineParams = createLineViewParams();
SlideEvent slideEvent = new SlideEvent(eventOrderCounter, startPointEvent, endPointEvent, lineView, lineParams); SlideEvent slideEvent = new SlideEvent(eventOrderCounter, startPointEvent, endPointEvent);
slideEvent.setLineView(lineView);
slideEvent.setLineParams(lineParams);
runtimeEvents.add(new EventWrapper(EventWrapper.EventType.SLIDE, slideEvent, eventOrderCounter)); runtimeEvents.add(new EventWrapper(EventWrapper.EventType.SLIDE, slideEvent, eventOrderCounter));
eventOrderCounter++; eventOrderCounter++;
Log.d(TAG, "addSlide runtimeEvents: " + runtimeEvents.size() + " eventOrderCounter :" + eventOrderCounter); Log.d(TAG, "addSlide runtimeEvents: " + runtimeEvents.size() + " eventOrderCounter :" + eventOrderCounter);
// 设置滑动点可拖动 // 设置滑动点可拖动
setupDraggableSlidePoint(startPoint, startParams, startPointEvent, slideEvent); setupDraggableSlidePoint(startPointView, startParams, startPointEvent, slideEvent);
setupDraggableSlidePoint(endPoint, endParams, endPointEvent, slideEvent); setupDraggableSlidePoint(endPointView, endParams, endPointEvent, slideEvent);
// 添加视图到窗口 // 添加视图到窗口
windowManager.addView(lineView, lineParams); windowManager.addView(lineView, lineParams);
windowManager.addView(startPoint, startParams); windowManager.addView(startPointView, startParams);
windowManager.addView(endPoint, endParams); windowManager.addView(endPointView, endParams);
updateLinePosition(slideEvent); // 更新线的位置 updateLinePosition(slideEvent); // 更新线的位置
updateServicePositions(); updateServicePositions();
@ -335,9 +352,9 @@ public class FloatingViewManager {
// 获取事件的视图 // 获取事件的视图
View viewToToggle = null; View viewToToggle = null;
if (wrapper.getType() == EventWrapper.EventType.POINT) { if (wrapper.getType() == EventWrapper.EventType.POINT) {
viewToToggle = ((PointEvent) wrapper.getEvent()).getView(); viewToToggle = createTouchPointView(String.valueOf(wrapper.getEvent().getId()));
} else if (wrapper.getType() == EventWrapper.EventType.SLIDE) { } else if (wrapper.getType() == EventWrapper.EventType.SLIDE) {
View startPointView = getView(show, wrapper); View startPointView = getView(show, wrapper,context);
WindowManager.LayoutParams startParams = (WindowManager.LayoutParams) startPointView.getLayoutParams(); WindowManager.LayoutParams startParams = (WindowManager.LayoutParams) startPointView.getLayoutParams();
if (show) { if (show) {
@ -367,12 +384,12 @@ public class FloatingViewManager {
} }
@NonNull @NonNull
private static View getView(boolean show, EventWrapper wrapper) { private View getView(boolean show, EventWrapper wrapper,Context context) {
SlideEvent slideEvent = (SlideEvent) wrapper.getEvent(); SlideEvent slideEvent = (SlideEvent) wrapper.getEvent();
// 假设你只隐藏/显示点线也需要同步 // 假设你只隐藏/显示点线也需要同步
View startPointView = slideEvent.getStartPoint().getView(); View startPointView = createTouchPointView(String.valueOf(slideEvent.getStartPoint().getId()));
View endPointView = slideEvent.getEndPoint().getView(); View endPointView = createTouchPointView(String.valueOf(slideEvent.getEndPoint().getId()));
View lineView = slideEvent.getLineView(); View lineView = new ConnectingLineView(context);
if (show) { if (show) {
// 如果是显示可能需要重新添加或设置可见性 // 如果是显示可能需要重新添加或设置可见性
@ -423,7 +440,7 @@ public class FloatingViewManager {
PrefUtils.setFloatingShown(context, 0); PrefUtils.setFloatingShown(context, 0);
// 发送浮窗关闭广播 // 发送浮窗关闭广播
Intent intent = new Intent("com.auto.autoclicker.FLOATING_WINDOW_STATE_CHANGED"); Intent intent = new Intent("com.auto.clicker.autoclicker.FLOATING_WINDOW_STATE_CHANGED");
intent.putExtra("isShown", false); intent.putExtra("isShown", false);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent); LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
} }
@ -788,13 +805,145 @@ public class FloatingViewManager {
new BroadcastReceiver() { new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if ("com.auto.autoclicker.FLASH_TOUCH_POINT".equals(intent.getAction())) { if ("com.auto.clicker.autoclicker.FLASH_TOUCH_POINT".equals(intent.getAction())) {
int index = intent.getIntExtra("index", -1); int index = intent.getIntExtra("index", -1);
flashTouchPoint(index); flashTouchPoint(index);
} }
} }
}, },
new IntentFilter("com.auto.autoclicker.FLASH_TOUCH_POINT") new IntentFilter("com.auto.clicker.autoclicker.FLASH_TOUCH_POINT")
); );
} }
public void saveCurrentEventsAsSolution(String solutionName, EventRepository.RepositoryCallback<Long> callback) {
if (solutionName == null || solutionName.trim().isEmpty()) {
Toast.makeText(context, "方案名称不能为空", Toast.LENGTH_SHORT).show();
// 在这里调用回调的 onError或者 onComplete 返回一个表示失败的值
// 确保 FloatingTabDialogManager 收到反馈
if (callback != null) {
callback.onComplete(-1L); // 返回一个表示失败的值
}
return;
}
if (runtimeEvents.isEmpty()) {
Toast.makeText(context, "当前没有事件可以保存", Toast.LENGTH_SHORT).show();
if (callback != null) {
callback.onComplete(-1L); // 返回一个表示失败的值
}
return;
}
Solution newSolution = new Solution(solutionName);
eventRepository.insertSolutionWithEvents(newSolution, runtimeEvents, new EventRepository.RepositoryCallback<Long>() {
@Override
public void onComplete(Long solutionId) {
// 成功保存Toast 可以在这里显示也可以在 FloatingTabDialogManager 中显示
// 为了职责分离建议在这里显示一个简单的"保存成功" Toast然后让 FloatingTabDialogManager 决定是否关闭
// Toast.makeText(context, "方案 '" + solutionName + "' 保存成功!", Toast.LENGTH_SHORT).show();
// 保存失败Toast 可以在这里显示
// Toast.makeText(context, "保存方案失败。", Toast.LENGTH_SHORT).show();
if (callback != null) {
callback.onComplete(solutionId); // 将结果传递给调用者
}
}
@Override
public void onError(Exception e) {
Log.e(TAG, "保存方案失败: " + e.getMessage(), e);
Toast.makeText(context, "保存方案失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
if (callback != null) {
callback.onError(e); // 将错误传递给调用者
}
}
});
}
public void loadSolution(long solutionId) {
eventRepository.getSolutionWithEventsAndPoints(solutionId, new EventRepository.RepositoryCallback<SolutionWithEventsAndPoints>() {
@Override
public void onComplete(SolutionWithEventsAndPoints solutionData) {
if (solutionData != null && solutionData.events != null) {
removeFloatingViews();
runtimeEvents.clear();
eventOrderCounter = 1;
for (SolutionWithEventsAndPoints.EventWithPoints eventWithPoints : solutionData.events) {
EventEntity eventEntity = eventWithPoints.eventEntity;
EventWrapper.EventType type = EventWrapper.EventType.valueOf(eventEntity.eventType);
if (type == EventWrapper.EventType.POINT) {
TouchPoint clickPoint = eventWithPoints.clickTouchPoint;
if (clickPoint != null) {
PointEvent pointEvent = new PointEvent(eventEntity.orderInSolution, clickPoint.x, clickPoint.y);
TextView pointView = createTouchPointView(String.valueOf(pointEvent.getId()));
WindowManager.LayoutParams params = createTouchPointParams();
params.x = pointEvent.getX();
params.y = pointEvent.getY();
pointView.setLayoutParams(params);
pointEvent.setView(pointView);
setupDraggablePoint(pointView, params, pointEvent);
windowManager.addView(pointView, params);
runtimeEvents.add(new EventWrapper(type, pointEvent, pointEvent.getId()));
}
} else if (type == EventWrapper.EventType.SLIDE) {
TouchPoint startPointData = eventWithPoints.slideStartTouchPoint;
TouchPoint endPointData = eventWithPoints.slideEndTouchPoint;
if (startPointData != null && endPointData != null) {
PointEvent startPointEvent = new PointEvent(eventEntity.orderInSolution, startPointData.x, startPointData.y);
PointEvent endPointEvent = new PointEvent(eventEntity.orderInSolution, endPointData.x, endPointData.y);
TextView startPointView = createTouchPointView(String.valueOf(startPointEvent.getId()));
WindowManager.LayoutParams startParams = createTouchPointParams();
startParams.x = startPointEvent.getX();
startParams.y = startPointEvent.getY();
startPointView.setLayoutParams(startParams);
TextView endPointView = createTouchPointView(String.valueOf(endPointEvent.getId()));
WindowManager.LayoutParams endParams = createTouchPointParams();
endParams.x = endPointEvent.getX();
endParams.y = endPointEvent.getY();
endPointView.setLayoutParams(endParams);
ConnectingLineView lineView = new ConnectingLineView(context);
WindowManager.LayoutParams lineParams = createLineViewParams();
SlideEvent slideEvent = new SlideEvent(eventEntity.orderInSolution, startPointEvent, endPointEvent);
setupDraggableSlidePoint(startPointView, startParams, startPointEvent, slideEvent);
setupDraggableSlidePoint(endPointView, endParams, endPointEvent, slideEvent);
windowManager.addView(lineView, lineParams);
windowManager.addView(startPointView, startParams);
windowManager.addView(endPointView, endParams);
updateLinePosition(slideEvent);
runtimeEvents.add(new EventWrapper(type, slideEvent, slideEvent.getId()));
}
}
if (eventEntity.orderInSolution >= eventOrderCounter) {
eventOrderCounter = eventEntity.orderInSolution + 1;
}
}
updateServicePositions();
Toast.makeText(context, "方案 '" + solutionData.solution.solutionName + "' 加载成功!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "加载方案失败或方案不存在。", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onError(Exception e) {
Log.e(TAG, "加载方案失败: " + e.getMessage(), e);
Toast.makeText(context, "加载方案失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
public void getAllSolutions(EventRepository.RepositoryCallback<List<Solution>> callback) {
if (eventRepository != null) {
eventRepository.getAllSolutions(callback);
} else {
callback.onError(new IllegalStateException("EventRepository not initialized."));
}
}
} }

View File

@ -22,7 +22,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:text="@string/scripts" android:text="@string/solutionList"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="32sp" android:textSize="32sp"
android:textStyle="bold" android:textStyle="bold"

View File

@ -1,10 +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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.activity.floating.TransparentFloatingActivity">
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,40 +1,38 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="321dp" android:layout_width="321dp"
android:layout_height="386dp" android:layout_height="386dp"
android:background="@drawable/rounded_edittext_dotted_background" android:background="@drawable/rounded_edittext_background"
android:backgroundTint="@color/bg_blue"
android:orientation="vertical"
android:padding="8dp"> android:padding="8dp">
<LinearLayout <LinearLayout
android:id="@+id/tab_buttons_container" android:id="@+id/tab_buttons_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical" android:gravity="center_vertical"
android:weightSum="2"> <Button android:orientation="horizontal"
android:weightSum="2">
<Button
android:id="@+id/tab_one_button" android:id="@+id/tab_one_button"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="选项卡一"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:textColor="@color/black" /> <Button android:text="选项卡一"
android:textColor="@color/black" />
<Button
android:id="@+id/tab_two_button" android:id="@+id/tab_two_button"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="选项卡二"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:textColor="@color/gray" /> <ImageView android:text="选项卡二"
android:id="@+id/close_dialog_button" android:textColor="@color/gray" />
android:layout_width="30dp" </LinearLayout>
android:layout_height="30dp"
android:src="@android:drawable/ic_menu_close_clear_cancel"
android:clickable="true"
android:focusable="true"
android:contentDescription="关闭弹窗"
android:layout_marginStart="8dp" /> </LinearLayout>
<FrameLayout <FrameLayout
android:id="@+id/content_frame" android:id="@+id/content_frame"
@ -42,8 +40,14 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="8dp"> android:layout_marginTop="8dp">
<include layout="@layout/save_script_page" android:id="@+id/page_one_view"/> <include
<include layout="@layout/load_script_page" android:id="@+id/page_two_view" android:visibility="gone"/> android:id="@+id/page_one_view"
layout="@layout/save_script_page" />
<include
android:id="@+id/page_two_view"
layout="@layout/load_script_page"
android:visibility="gone" />
</FrameLayout> </FrameLayout>

View File

@ -117,7 +117,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="Save/Load scripts" android:text="Save/Load solutionList"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="14sp" android:textSize="14sp"
app:layout_constraintStart_toStartOf="@+id/text_play_stop" app:layout_constraintStart_toStartOf="@+id/text_play_stop"
@ -285,7 +285,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="72dp" android:layout_marginTop="72dp"
android:text="Drag the action point to move it android:text="Drag the action touchPoint to move it
\nanywhere, click to edit the interval" \nanywhere, click to edit the interval"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="14sp" android:textSize="14sp"
@ -396,7 +396,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="25dp" android:layout_marginTop="25dp"
android:text="Drag the start/end point of swipe route to move android:text="Drag the start/end touchPoint of swipe route to move
\nit anywhere, click on either of the two to edit the \nit anywhere, click on either of the two to edit the
\ninterval or duration" \ninterval or duration"
android:textColor="@color/white" android:textColor="@color/white"

View File

@ -43,7 +43,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="11dp" android:layout_marginTop="11dp"
android:text="Save/Load scripts" android:text="Save/Load solutionList"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="14sp" android:textSize="14sp"
app:layout_constraintStart_toStartOf="@+id/text_play_stop" app:layout_constraintStart_toStartOf="@+id/text_play_stop"
@ -211,7 +211,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="87dp" android:layout_marginTop="87dp"
android:text="Drag the action point to move android:text="Drag the action touchPoint to move
\nit anywhere, click to edit the" \nit anywhere, click to edit the"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="14sp" android:textSize="14sp"

View File

@ -9,7 +9,6 @@
android:id="@+id/tip" android:id="@+id/tip"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="19dp"
android:text="Enter the Script name and hit android:text="Enter the Script name and hit
\nsave button" \nsave button"
android:textColor="@color/text_gray" android:textColor="@color/text_gray"

View File

@ -16,8 +16,8 @@
<string name="other">Other</string> <string name="other">Other</string>
<string name="title">Title</string> <string name="title">Title</string>
<string name="subtitle">Subtitle</string> <string name="subtitle">Subtitle</string>
<string name="scripts">Scripts</string> <string name="solutionList">Scripts</string>
<string name="no_scripts_text">Your saved scripts \nwill be shown here</string> <string name="no_scripts_text">Your saved solutionList \nwill be shown here</string>
<string name="create_now">Create Now</string> <string name="create_now">Create Now</string>
<string name="import_scripts">Import</string> <string name="import_scripts">Import</string>
<string name="accessibility">Accessibility</string> <string name="accessibility">Accessibility</string>

View File

@ -6,19 +6,5 @@
<item name="android:windowBackground">@drawable/activity_background</item> <item name="android:windowBackground">@drawable/activity_background</item>
</style> </style>
<style name="Theme.TransparentOverlay" parent="Theme.Material3.DayNight.Dialog">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:gravity">center</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
</style>
<style name="Theme.AutoClicker" parent="Base.Theme.AutoClicker" /> <style name="Theme.AutoClicker" parent="Base.Theme.AutoClicker" />
</resources> </resources>