功能添加优化
This commit is contained in:
parent
6aa0ee4cb0
commit
add6f3d1e0
@ -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" />
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
package com.auto.clicker.autoclicker.room;
|
||||
|
||||
import androidx.room.Dao;
|
||||
|
||||
@Dao
|
||||
public interface PointEventEntityDao {
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package com.auto.clicker.autoclicker.room;
|
||||
|
||||
import androidx.room.Dao;
|
||||
|
||||
@Dao
|
||||
public interface SlideEventEntityDao {
|
||||
}
|
||||
@ -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; // 滑动事件的终点
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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; }
|
||||
}
|
||||
@ -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; }
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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("无障碍服务已销毁");
|
||||
}
|
||||
|
||||
@ -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);
|
||||
// }
|
||||
}
|
||||
@ -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); }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
|
||||
// 默认显示第一个页面
|
||||
showPage(0);
|
||||
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
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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."));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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"
|
||||
|
||||
@ -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>
|
||||
@ -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: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: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: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:background="?attr/selectableItemBackground"
|
||||
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:background="?attr/selectableItemBackground"
|
||||
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>
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
Loading…
Reference in New Issue
Block a user