功能添加优化

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:theme="@style/Theme.AutoClicker"
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
android:name=".ui.activity.main.QuestionActivity"
android:exported="false" />

View File

@ -1,21 +1,29 @@
package com.auto.clicker.autoclicker.data;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
public class PointEvent extends Event {
private int id;
private int x;
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);
this.x = x;
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() {
@ -34,7 +42,7 @@ public class PointEvent extends Event {
this.y = y;
}
public View getView() {
public TextView getView() {
return view;
}

View File

@ -4,20 +4,27 @@ import android.view.WindowManager;
import com.auto.clicker.autoclicker.view.ConnectingLineView;
public class SlideEvent extends Event {
private int id;
private PointEvent startPoint;
private PointEvent endPoint;
private ConnectingLineView lineView;
private WindowManager.LayoutParams lineParams;
public SlideEvent(int id, PointEvent startPoint, PointEvent endPoint,
ConnectingLineView lineView, WindowManager.LayoutParams lineParams) {
private transient ConnectingLineView lineView;
private transient WindowManager.LayoutParams lineParams;
public SlideEvent(int id, PointEvent startPoint, PointEvent endPoint) {
super(id);
this.startPoint = startPoint;
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() {
@ -51,5 +58,4 @@ public class SlideEvent extends Event {
public void setLineParams(WindowManager.LayoutParams 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 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 ScriptEntityDao scriptDao();
public abstract PointEventEntityDao pointEventEntityDao();
public abstract SlideEventEntityDao slideEventEntityDao();
public abstract SolutionDao solutionDao();
public abstract TouchPointDao pointDao();
public abstract EventEntityDao eventEntityDao();
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) {
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);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
@ -257,7 +257,7 @@ public class AutoClickService extends AccessibilityService {
super.onDestroy();
instance = null;
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);
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.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.auto.clicker.autoclicker.R;
import com.auto.clicker.autoclicker.dialog.LoadScriptView;
import com.auto.clicker.autoclicker.room.ScriptEntity;
import com.auto.clicker.autoclicker.room.entity.Solution;
import java.util.List;
public class ScriptListAdapter extends RecyclerView.Adapter<ScriptListAdapter.ViewHolder> {
private List<ScriptEntity> scripts;
private LoadScriptView.OnScriptClickListener listener;
private List<Solution> solutionList;
public ScriptListAdapter(List<ScriptEntity> scripts, LoadScriptView.OnScriptClickListener listener) {
this.scripts = scripts;
this.listener = listener;
public ScriptListAdapter(List<Solution> solutionList) {
this.solutionList = solutionList;
}
@NonNull
@ -40,7 +34,7 @@ public class ScriptListAdapter extends RecyclerView.Adapter<ScriptListAdapter.Vi
@Override
public int getItemCount() {
return scripts.size();
return solutionList.size();
}
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.graphics.PixelFormat;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.EditText;
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.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 {
private static final String TAG = "FloatingTabDialogMgr";
private final Context originalContext; // 保存原始的 Service Context
private final Context context;
private final WindowManager windowManager;
private LinearLayout floatingDialogView;
private WindowManager.LayoutParams floatingDialogParams;
private Button tabOneButton;
private Button tabTwoButton;
private FrameLayout contentFrame;
private View pageOneView;
private View pageTwoView;
private Button saveTab;
private Button loadTab;
private View savePage;
private View loadPage;
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) {
this.originalContext = context; // 保存原始 Context
this.context = context;
this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
}
public void setFloatingViewManager(FloatingViewManager manager) {
this.floatingViewManager = manager;
}
public void showFloatingTabDialog() {
if (floatingDialogView != null) {
removeFloatingTabDialog(); // 如果已经显示先移除
removeFloatingTabDialog();
}
// --- 关键修改使用 ContextThemeWrapper 包裹 Service Context ---
Context themedContext = new ContextThemeWrapper(originalContext, R.style.Theme_AutoClicker);
// 或者如果你想用 Material3 的默认主题但你的 Theme_AutoClicker 已经继承了 Material3那用它就行
// Context themedContext = new ContextThemeWrapper(originalContext, R.style.Theme_Material3_DayNight_NoActionBar); // 也可以直接用这个
Context themedContext = new ContextThemeWrapper(context, R.style.Theme_AutoClicker);
floatingDialogView = (LinearLayout) LayoutInflater.from(themedContext).inflate(R.layout.floating_tab_dialog, null);
// --- 关键修改结束 ---
floatingDialogParams = createFloatingDialogParams(); // 创建悬浮窗参数
floatingDialogParams = createFloatingDialogParams();
saveTab = floatingDialogView.findViewById(R.id.tab_one_button);
loadTab = floatingDialogView.findViewById(R.id.tab_two_button);
savePage = floatingDialogView.findViewById(R.id.page_one_view);
loadPage = floatingDialogView.findViewById(R.id.page_two_view);
// 获取视图组件
tabOneButton = floatingDialogView.findViewById(R.id.tab_one_button);
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);
saveTab.setOnClickListener(v -> showPage(0));
loadTab.setOnClickListener(v -> showPage(1));
// 设置标签按钮的点击事件
tabOneButton.setOnClickListener(v -> showPage(0));
tabTwoButton.setOnClickListener(v -> showPage(1));
inputName = savePage.findViewById(R.id.script_name_input);
saveButton = savePage.findViewById(R.id.save);
closeButton = savePage.findViewById(R.id.close);
// 设置关闭按钮的点击事件
if (closeDialogButton != null) {
closeDialogButton.setOnClickListener(v -> {
removeFloatingTabDialog(); // 点击关闭按钮时移除弹窗
});
saveButton.setOnClickListener(v -> {
String solutionName = inputName.getText().toString().trim();
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();
}
}
// 默认显示第一个页面
showPage(0);
@Override
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 {
windowManager.addView(floatingDialogView, floatingDialogParams);
Log.d(TAG, "浮动标签页弹窗已显示");
} catch (WindowManager.BadTokenException e) {
Log.e(TAG, "无法添加浮动标签页弹窗,可能是权限问题或上下文无效", e);
Toast.makeText(context, "显示弹窗失败,请检查权限。", Toast.LENGTH_SHORT).show();
} catch (SecurityException e) {
Log.e(TAG, "需要悬浮窗权限才能显示浮动弹窗", e);
// 引导用户去开启权限如果你在 FloatingViewManager 中已经处理了这里可以省略
// Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
// Uri.parse("package:" + context.getPackageName()));
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// context.startActivity(intent);
Toast.makeText(context, "请授予悬浮窗权限以便显示。", Toast.LENGTH_SHORT).show();
}
}
private void showPage(int pageIndex) {
// 确保所有视图都已正确加载例如通过 inflated 后的 findViewById
if (pageOneView == null || pageTwoView == null) {
if (savePage == null || loadPage == null) {
Log.e(TAG, "页面视图未初始化!");
return;
}
// 隐藏所有页面
pageOneView.setVisibility(View.GONE);
pageTwoView.setVisibility(View.GONE);
savePage.setVisibility(View.GONE);
loadPage.setVisibility(View.GONE);
// 重置所有标签按钮的颜色/背景如果需要视觉反馈
// 注意这里使用 getContext().getResources().getColor() 是在 themedContext 的上下文中获取颜色
// 如果你的颜色定义在 themes.xml colors.xml 并且依赖于主题属性那么需要确保主题是正确的
tabOneButton.setTextColor(originalContext.getResources().getColor(android.R.color.darker_gray)); // originalContext themedContext
tabTwoButton.setTextColor(originalContext.getResources().getColor(android.R.color.darker_gray)); // 最好用 themedContext
// 重置Tab按钮颜色
saveTab.setTextColor(context.getResources().getColor(android.R.color.darker_gray));
loadTab.setTextColor(context.getResources().getColor(android.R.color.darker_gray));
// 显示选定页面并更新标签样式
switch (pageIndex) {
case 0:
pageOneView.setVisibility(View.VISIBLE);
tabOneButton.setTextColor(originalContext.getResources().getColor(android.R.color.black)); // 选中颜色
case 0: // 保存页面
savePage.setVisibility(View.VISIBLE);
saveTab.setTextColor(context.getResources().getColor(android.R.color.black));
// 在显示保存页面时尝试显示键盘
showKeyboard();
break;
case 1:
pageTwoView.setVisibility(View.VISIBLE);
tabTwoButton.setTextColor(originalContext.getResources().getColor(android.R.color.black)); // 选中颜色
case 1: // 加载页面
loadPage.setVisibility(View.VISIBLE);
loadTab.setTextColor(context.getResources().getColor(android.R.color.black));
loadSolutionsForList();
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() {
if (floatingDialogView != null) {
try {
windowManager.removeView(floatingDialogView);
floatingDialogView = null;
floatingDialogView = null; // 置空防止重复移除或空指针异常
Log.d(TAG, "浮动标签页弹窗已移除");
} catch (IllegalArgumentException e) {
Log.w(TAG, "尝试移除不存在的视图", e);
@ -131,20 +216,30 @@ public class FloatingTabDialogManager {
}
private WindowManager.LayoutParams createFloatingDialogParams() {
// 创建浮动弹窗的参数
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.WRAP_CONTENT, // 宽度自适应内容
WindowManager.LayoutParams.WRAP_CONTENT, // 高度自适应内容
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
overlayType,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | // 如果希望点击弹窗外部能穿透则保持
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, // 确保它能接收触摸事件
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT
);
params.gravity = Gravity.CENTER; // 居中显示
params.gravity = Gravity.CENTER;
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.PositionUpdater;
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.ForegroundService;
import com.auto.clicker.autoclicker.ui.activity.floating.TransparentFloatingActivity;
import com.auto.clicker.autoclicker.util.FloatingTabDialogManager;
import com.auto.clicker.autoclicker.util.PrefUtils;
import com.auto.clicker.autoclicker.util.ScreenUtils;
@ -71,6 +74,8 @@ public class FloatingViewManager {
private FloatingTabDialogManager floatingTabDialogManager;
private EventRepository eventRepository;
public FloatingViewManager(Context context) {
this.context = context;
this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@ -78,6 +83,8 @@ public class FloatingViewManager {
this.screenWidth = screenSize.x;
this.screenHeight = screenSize.y;
this.floatingTabDialogManager = new FloatingTabDialogManager(context);
this.floatingTabDialogManager.setFloatingViewManager(this);
this.eventRepository = new EventRepository(context.getApplicationContext());
registerBroadcastReceiver();
}
@ -266,7 +273,7 @@ public class FloatingViewManager {
}
// 创建点击点视图
TextView point = createTouchPointView(String.valueOf(eventOrderCounter));
TextView pointView = createTouchPointView(String.valueOf(eventOrderCounter));
// 设置位置居中
WindowManager.LayoutParams params = createTouchPointParams();
@ -274,15 +281,18 @@ public class FloatingViewManager {
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));
eventOrderCounter++;
Log.d(TAG, "addPoint runtimeEvents: " + runtimeEvents.size() + " eventOrderCounter :" + eventOrderCounter);
// 设置拖动功能并添加到窗口
setupDraggablePoint(point, params, pointEvent);
windowManager.addView(point, params);
setupDraggablePoint(pointView, params, pointEvent);
windowManager.addView(pointView, params);
updateServicePositions();
}
@ -294,37 +304,44 @@ public class FloatingViewManager {
}
// 创建起点
TextView startPoint = createTouchPointView(String.valueOf(eventOrderCounter));
TextView startPointView = createTouchPointView(String.valueOf(eventOrderCounter));
WindowManager.LayoutParams startParams = createTouchPointParams();
startParams.x = screenWidth / 2 - 100;
startParams.y = screenHeight / 2 - 50;
PointEvent startPointEvent = new PointEvent(eventOrderCounter, startParams.x, startParams.y, 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();
endParams.x = screenWidth / 2 + 100;
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);
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));
eventOrderCounter++;
Log.d(TAG, "addSlide runtimeEvents: " + runtimeEvents.size() + " eventOrderCounter :" + eventOrderCounter);
// 设置滑动点可拖动
setupDraggableSlidePoint(startPoint, startParams, startPointEvent, slideEvent);
setupDraggableSlidePoint(endPoint, endParams, endPointEvent, slideEvent);
setupDraggableSlidePoint(startPointView, startParams, startPointEvent, slideEvent);
setupDraggableSlidePoint(endPointView, endParams, endPointEvent, slideEvent);
// 添加视图到窗口
windowManager.addView(lineView, lineParams);
windowManager.addView(startPoint, startParams);
windowManager.addView(endPoint, endParams);
windowManager.addView(startPointView, startParams);
windowManager.addView(endPointView, endParams);
updateLinePosition(slideEvent); // 更新线的位置
updateServicePositions();
@ -335,9 +352,9 @@ public class FloatingViewManager {
// 获取事件的视图
View viewToToggle = null;
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) {
View startPointView = getView(show, wrapper);
View startPointView = getView(show, wrapper,context);
WindowManager.LayoutParams startParams = (WindowManager.LayoutParams) startPointView.getLayoutParams();
if (show) {
@ -367,12 +384,12 @@ public class FloatingViewManager {
}
@NonNull
private static View getView(boolean show, EventWrapper wrapper) {
private View getView(boolean show, EventWrapper wrapper,Context context) {
SlideEvent slideEvent = (SlideEvent) wrapper.getEvent();
// 假设你只隐藏/显示点线也需要同步
View startPointView = slideEvent.getStartPoint().getView();
View endPointView = slideEvent.getEndPoint().getView();
View lineView = slideEvent.getLineView();
View startPointView = createTouchPointView(String.valueOf(slideEvent.getStartPoint().getId()));
View endPointView = createTouchPointView(String.valueOf(slideEvent.getEndPoint().getId()));
View lineView = new ConnectingLineView(context);
if (show) {
// 如果是显示可能需要重新添加或设置可见性
@ -423,7 +440,7 @@ public class FloatingViewManager {
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);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
@ -788,13 +805,145 @@ public class FloatingViewManager {
new BroadcastReceiver() {
@Override
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);
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_height="wrap_content"
android:layout_marginStart="16dp"
android:text="@string/scripts"
android:text="@string/solutionList"
android:textColor="@color/white"
android:textSize="32sp"
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"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="321dp"
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">
<LinearLayout
android:id="@+id/tab_buttons_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:weightSum="2"> <Button
android:orientation="horizontal"
android:weightSum="2">
<Button
android:id="@+id/tab_one_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="选项卡一"
android:background="?attr/selectableItemBackground"
android:textColor="@color/black" /> <Button
android:text="选项卡一"
android:textColor="@color/black" />
<Button
android:id="@+id/tab_two_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="选项卡二"
android:background="?attr/selectableItemBackground"
android:textColor="@color/gray" /> <ImageView
android:id="@+id/close_dialog_button"
android:layout_width="30dp"
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>
android:text="选项卡二"
android:textColor="@color/gray" />
</LinearLayout>
<FrameLayout
android:id="@+id/content_frame"
@ -42,8 +40,14 @@
android:layout_height="match_parent"
android:layout_marginTop="8dp">
<include layout="@layout/save_script_page" android:id="@+id/page_one_view"/>
<include layout="@layout/load_script_page" android:id="@+id/page_two_view" android:visibility="gone"/>
<include
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>

View File

@ -117,7 +117,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="Save/Load scripts"
android:text="Save/Load solutionList"
android:textColor="#FFFFFF"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="@+id/text_play_stop"
@ -285,7 +285,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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"
android:textColor="@color/white"
android:textSize="14sp"
@ -396,7 +396,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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
\ninterval or duration"
android:textColor="@color/white"

View File

@ -43,7 +43,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="11dp"
android:text="Save/Load scripts"
android:text="Save/Load solutionList"
android:textColor="#FFFFFF"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="@+id/text_play_stop"
@ -211,7 +211,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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"
android:textColor="@color/white"
android:textSize="14sp"

View File

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

View File

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

View File

@ -6,19 +6,5 @@
<item name="android:windowBackground">@drawable/activity_background</item>
</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" />
</resources>