暂存
This commit is contained in:
parent
0b20d1a01a
commit
6cf551aa6c
@ -5,7 +5,7 @@ public class EventWrapper {
|
|||||||
|
|
||||||
private EventType type;
|
private EventType type;
|
||||||
private Event event;
|
private Event event;
|
||||||
private int order; // 添加顺序编号(用于UI编号)
|
private int order;
|
||||||
|
|
||||||
public EventWrapper(EventType type, Event event, int order) {
|
public EventWrapper(EventType type, Event event, int order) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@ -13,7 +13,6 @@ public class EventWrapper {
|
|||||||
this.order = order;
|
this.order = order;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public EventType getType() {
|
public EventType getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import android.widget.TextView;
|
|||||||
public class PointEvent extends Event {
|
public class PointEvent extends Event {
|
||||||
private int x;
|
private int x;
|
||||||
private int y;
|
private int y;
|
||||||
private TextView view; // 悬浮窗上的控件
|
private TextView view;
|
||||||
private WindowManager.LayoutParams layoutParams;
|
private WindowManager.LayoutParams layoutParams;
|
||||||
|
|
||||||
public PointEvent(int id, int x, int y, TextView view, WindowManager.LayoutParams layoutParams) {
|
public PointEvent(int id, int x, int y, TextView view, WindowManager.LayoutParams layoutParams) {
|
||||||
|
|||||||
@ -10,7 +10,6 @@ public class SlideEvent extends Event {
|
|||||||
private PointEvent endPoint;
|
private PointEvent endPoint;
|
||||||
private ConnectingLineView lineView;
|
private ConnectingLineView lineView;
|
||||||
private WindowManager.LayoutParams lineParams;
|
private WindowManager.LayoutParams lineParams;
|
||||||
private long durationMillis; // 滑动时间
|
|
||||||
|
|
||||||
public SlideEvent(int id, PointEvent startPoint, PointEvent endPoint,
|
public SlideEvent(int id, PointEvent startPoint, PointEvent endPoint,
|
||||||
ConnectingLineView lineView, WindowManager.LayoutParams lineParams) {
|
ConnectingLineView lineView, WindowManager.LayoutParams lineParams) {
|
||||||
@ -53,11 +52,4 @@ public class SlideEvent extends Event {
|
|||||||
this.lineParams = lineParams;
|
this.lineParams = lineParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getDurationMillis() {
|
|
||||||
return durationMillis;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDurationMillis(long durationMillis) {
|
|
||||||
this.durationMillis = durationMillis;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -8,10 +8,11 @@ import androidx.room.RoomDatabase;
|
|||||||
|
|
||||||
import com.auto.clicker.autoclicker.MyApplication;
|
import com.auto.clicker.autoclicker.MyApplication;
|
||||||
|
|
||||||
@Database(entities = {TouchEventEntity.class}, version = MyApplication.DB_VERSION, exportSchema = false)
|
@Database(entities = {ScriptEntity.class,PointEventEntity.class,SlideEventEntity.class}, version = MyApplication.DB_VERSION, exportSchema = false)
|
||||||
public abstract class AppDatabase extends RoomDatabase {
|
public abstract class AppDatabase extends RoomDatabase {
|
||||||
|
public abstract ScriptEntityDao scriptDao();
|
||||||
public abstract TouchEventDao touchEventDao();
|
public abstract PointEventEntityDao pointEventEntityDao();
|
||||||
|
public abstract SlideEventEntityDao slideEventEntityDao();
|
||||||
|
|
||||||
private static volatile AppDatabase INSTANCE;
|
private static volatile AppDatabase INSTANCE;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,23 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package com.auto.clicker.autoclicker.room;
|
||||||
|
|
||||||
|
import androidx.room.Dao;
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
public interface PointEventEntityDao {
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package com.auto.clicker.autoclicker.room;
|
||||||
|
|
||||||
|
import androidx.room.Dao;
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
public interface SlideEventEntityDao {
|
||||||
|
}
|
||||||
@ -1,19 +0,0 @@
|
|||||||
package com.auto.clicker.autoclicker.room;
|
|
||||||
|
|
||||||
import androidx.room.Dao;
|
|
||||||
import androidx.room.Delete;
|
|
||||||
import androidx.room.Insert;
|
|
||||||
import androidx.room.Update;
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
public interface TouchEventDao {
|
|
||||||
|
|
||||||
@Insert
|
|
||||||
long insertEvent(TouchEventEntity event);
|
|
||||||
|
|
||||||
@Delete
|
|
||||||
void deleteEvent(TouchEventEntity event);
|
|
||||||
|
|
||||||
@Update
|
|
||||||
void updateEvent(TouchEventEntity event);
|
|
||||||
}
|
|
||||||
@ -1,116 +0,0 @@
|
|||||||
package com.auto.clicker.autoclicker.room;
|
|
||||||
|
|
||||||
import androidx.room.Entity;
|
|
||||||
import androidx.room.PrimaryKey;
|
|
||||||
|
|
||||||
@Entity(tableName = "touch_event")
|
|
||||||
public class TouchEventEntity {
|
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
|
||||||
private int id;
|
|
||||||
private int eventType;
|
|
||||||
private int orderIndex;
|
|
||||||
|
|
||||||
private int x;
|
|
||||||
private int y;
|
|
||||||
|
|
||||||
private Integer endX;
|
|
||||||
private Integer endY;
|
|
||||||
|
|
||||||
private int clickInterval;
|
|
||||||
private Integer slideDuration;
|
|
||||||
|
|
||||||
private int displayOrderNumber;
|
|
||||||
|
|
||||||
public TouchEventEntity(int eventType, int orderIndex, int x, int y, Integer endX, Integer endY, int clickInterval, Integer slideDuration, int displayOrderNumber) {
|
|
||||||
this.eventType = eventType;
|
|
||||||
this.orderIndex = orderIndex;
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.endX = endX;
|
|
||||||
this.endY = endY;
|
|
||||||
this.clickInterval = clickInterval;
|
|
||||||
this.slideDuration = slideDuration;
|
|
||||||
this.displayOrderNumber = displayOrderNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(int id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getEventType() {
|
|
||||||
return eventType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEventType(int eventType) {
|
|
||||||
this.eventType = eventType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOrderIndex() {
|
|
||||||
return orderIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOrderIndex(int orderIndex) {
|
|
||||||
this.orderIndex = orderIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getEndX() {
|
|
||||||
return endX;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEndX(Integer endX) {
|
|
||||||
this.endX = endX;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getEndY() {
|
|
||||||
return endY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEndY(Integer endY) {
|
|
||||||
this.endY = endY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getClickInterval() {
|
|
||||||
return clickInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClickInterval(int clickInterval) {
|
|
||||||
this.clickInterval = clickInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getSlideDuration() {
|
|
||||||
return slideDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSlideDuration(Integer slideDuration) {
|
|
||||||
this.slideDuration = slideDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDisplayOrderNumber() {
|
|
||||||
return displayOrderNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDisplayOrderNumber(int displayOrderNumber) {
|
|
||||||
this.displayOrderNumber = displayOrderNumber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
package com.auto.clicker.autoclicker.room.repository;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
|
|
||||||
import com.auto.clicker.autoclicker.room.AppDatabase;
|
|
||||||
import com.auto.clicker.autoclicker.room.TouchEventDao;
|
|
||||||
import com.auto.clicker.autoclicker.room.TouchEventEntity;
|
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
public class TouchEventRepository {
|
|
||||||
private final TouchEventDao dao;
|
|
||||||
private final ExecutorService executorService;
|
|
||||||
|
|
||||||
public TouchEventRepository(Application application) {
|
|
||||||
AppDatabase db = AppDatabase.getInstance(application);
|
|
||||||
this.dao = db.touchEventDao();
|
|
||||||
this.executorService = Executors.newSingleThreadExecutor();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void insertEvent(TouchEventEntity touchEventEntity){
|
|
||||||
executorService.execute(() -> dao.insertEvent(touchEventEntity));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deleteEvent(TouchEventEntity touchEventEntity){
|
|
||||||
executorService.execute(() -> dao.deleteEvent(touchEventEntity));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateEvent(TouchEventEntity touchEventEntity){
|
|
||||||
executorService.execute(() -> dao.updateEvent(touchEventEntity));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
package com.auto.clicker.autoclicker.room.viewmodel;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.lifecycle.AndroidViewModel;
|
|
||||||
|
|
||||||
import com.auto.clicker.autoclicker.room.repository.TouchEventRepository;
|
|
||||||
|
|
||||||
public class TouchEventViewModel extends AndroidViewModel {
|
|
||||||
private final TouchEventRepository repository;
|
|
||||||
|
|
||||||
public TouchEventViewModel(@NonNull Application application) {
|
|
||||||
super(application);
|
|
||||||
repository = new TouchEventRepository(application);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
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); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
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 java.util.List;
|
||||||
|
|
||||||
|
public class ScriptListAdapter extends RecyclerView.Adapter<ScriptListAdapter.ViewHolder> {
|
||||||
|
private List<ScriptEntity> scripts;
|
||||||
|
private LoadScriptView.OnScriptClickListener listener;
|
||||||
|
|
||||||
|
|
||||||
|
public ScriptListAdapter(List<ScriptEntity> scripts, LoadScriptView.OnScriptClickListener listener) {
|
||||||
|
this.scripts = scripts;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parentGroup, int viewType) {
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(parentGroup.getContext());
|
||||||
|
View itemLayout = inflater.inflate(R.layout.item_script, parentGroup, false);
|
||||||
|
return new ViewHolder(itemLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder itemHolder, int pos) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return scripts.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
public ViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,131 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -4,35 +4,133 @@ import android.content.Context;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import com.auto.clicker.autoclicker.R;
|
||||||
|
|
||||||
public class ConnectingLineView extends View {
|
public class ConnectingLineView extends View {
|
||||||
private float startX, startY, endX, endY;
|
|
||||||
private final Paint paint;
|
// 画笔,用于绘制连接带。设置为填充样式。
|
||||||
|
private final Paint connectionBandPaint;
|
||||||
|
|
||||||
|
// 连接带的宽度,单位 dp。
|
||||||
|
private final float CONNECTION_BAND_WIDTH_DP = 30f;
|
||||||
|
// 连接带在屏幕上的实际像素宽度。
|
||||||
|
private float connectionBandWidthPx;
|
||||||
|
|
||||||
|
// 绘制时,连接带的逻辑起始点和结束点(在 View 自身的坐标系中)。
|
||||||
|
private float drawStartX, drawStartY, drawEndX, drawEndY;
|
||||||
|
// 连接带的长度。
|
||||||
|
private float lineLength;
|
||||||
|
// 连接带的旋转角度(以度为单位)。
|
||||||
|
private float rotationAngle;
|
||||||
|
|
||||||
|
// 用于表示方向的 Drawable 图标。
|
||||||
|
private Drawable startArrowDrawable;
|
||||||
|
private Drawable endArrowDrawable;
|
||||||
|
|
||||||
public ConnectingLineView(Context context) {
|
public ConnectingLineView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
paint = new Paint();
|
// 将 dp 单位的宽度转换为像素单位,以便在 Canvas 上绘制。
|
||||||
paint.setColor(Color.RED);
|
connectionBandWidthPx = dpToPx(context, CONNECTION_BAND_WIDTH_DP);
|
||||||
paint.setAlpha(180);
|
|
||||||
paint.setStrokeWidth(5);
|
connectionBandPaint = new Paint();
|
||||||
paint.setAntiAlias(true);
|
|
||||||
paint.setStyle(Paint.Style.STROKE);
|
connectionBandPaint.setColor(Color.parseColor("#80184857"));
|
||||||
|
connectionBandPaint.setAntiAlias(true);
|
||||||
|
connectionBandPaint.setStyle(Paint.Style.FILL);
|
||||||
|
|
||||||
|
startArrowDrawable = ContextCompat.getDrawable(context, R.drawable.white_arrow_right);
|
||||||
|
endArrowDrawable = ContextCompat.getDrawable(context, R.drawable.white_arrow_right);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private float dpToPx(Context context, float dp) {
|
||||||
|
return dp * context.getResources().getDisplayMetrics().density;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setPoints(float startX, float startY, float endX, float endY) {
|
||||||
|
// 计算两点间的X和Y坐标差。
|
||||||
|
float deltaX = endX - startX;
|
||||||
|
float deltaY = endY - startY;
|
||||||
|
|
||||||
|
// 计算连接带的长度(两点间的直线距离)。
|
||||||
|
this.lineLength = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||||
|
|
||||||
|
// 计算连接带的旋转角度。
|
||||||
|
// Math.atan2(y, x) 返回的是从X轴正向到点(x,y)的弧度,范围是-PI到PI。
|
||||||
|
// Math.toDegrees 将弧度转换为度数。
|
||||||
|
this.rotationAngle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
|
||||||
|
|
||||||
|
this.drawStartX = startX;
|
||||||
|
this.drawStartY = startY;
|
||||||
|
this.drawEndX = endX;
|
||||||
|
this.drawEndY = endY;
|
||||||
|
|
||||||
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDraw(@NonNull Canvas canvas) {
|
protected void onDraw(@NonNull Canvas canvas) {
|
||||||
super.onDraw(canvas);
|
super.onDraw(canvas);
|
||||||
canvas.drawLine(startX, startY, endX, endY, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPoints(float startX, float startY, float endX, float endY) {
|
if (lineLength == 0) {
|
||||||
this.startX = startX;
|
return;
|
||||||
this.startY = startY;
|
}
|
||||||
this.endX = endX;
|
|
||||||
this.endY = endY;
|
// 保存当前画布的状态。
|
||||||
invalidate();
|
// 这允许我们对画布进行平移和旋转操作,而不会影响后续或外部的绘制。
|
||||||
|
canvas.save();
|
||||||
|
|
||||||
|
// 1. 平移画布到连接带的起始点。
|
||||||
|
// 这样,在绘制时,连接带的逻辑起始点就变成了 (0,0)。
|
||||||
|
canvas.translate(drawStartX, drawStartY);
|
||||||
|
|
||||||
|
// 2. 旋转画布。
|
||||||
|
// 围绕当前的画布原点(即连接带的起始点)旋转画布。
|
||||||
|
// 之后绘制的所有内容都会以这个角度显示。
|
||||||
|
canvas.rotate(rotationAngle);
|
||||||
|
|
||||||
|
// 3. 绘制连接带的矩形。
|
||||||
|
// RectF 定义了一个矩形。由于画布已经旋转和平移,
|
||||||
|
// 这里的 (0, -connectionBandWidthPx / 2) 是矩形的左上角,
|
||||||
|
// (lineLength, connectionBandWidthPx / 2) 是矩形的右下角。
|
||||||
|
// 这样矩形就会以当前 Y 轴为中心,从当前 X 轴原点(0)延伸到 lineLength。
|
||||||
|
RectF rect = new RectF(0, -connectionBandWidthPx / 2, lineLength, connectionBandWidthPx / 2);
|
||||||
|
canvas.drawRect(rect, connectionBandPaint);
|
||||||
|
|
||||||
|
// 4. 绘制起始方向图标。
|
||||||
|
if (startArrowDrawable != null) {
|
||||||
|
int arrowSize = (int) (connectionBandWidthPx / 2);
|
||||||
|
// 设置图标的边界。这里的坐标是相对于当前已旋转和平移的画布。
|
||||||
|
// 确保图标垂直居中于连接带。
|
||||||
|
startArrowDrawable.setBounds(
|
||||||
|
75,
|
||||||
|
(int) (-arrowSize / 2),// 垂直居中
|
||||||
|
75 + arrowSize,
|
||||||
|
(int) (arrowSize / 2)
|
||||||
|
);
|
||||||
|
startArrowDrawable.draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endArrowDrawable != null) {
|
||||||
|
int arrowSize = (int) (connectionBandWidthPx / 2);
|
||||||
|
endArrowDrawable.setBounds(
|
||||||
|
(int) (lineLength - arrowSize - 75),
|
||||||
|
(int) (-arrowSize / 2),
|
||||||
|
(int) (lineLength - 75),
|
||||||
|
(int) (arrowSize / 2)
|
||||||
|
);
|
||||||
|
endArrowDrawable.draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复画布到 save() 调用之前的状态。
|
||||||
|
canvas.restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,6 +25,7 @@ import android.widget.LinearLayout;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
@ -34,6 +35,7 @@ import com.auto.clicker.autoclicker.data.EventWrapper;
|
|||||||
import com.auto.clicker.autoclicker.data.PointEvent;
|
import com.auto.clicker.autoclicker.data.PointEvent;
|
||||||
import com.auto.clicker.autoclicker.data.PositionUpdater;
|
import com.auto.clicker.autoclicker.data.PositionUpdater;
|
||||||
import com.auto.clicker.autoclicker.data.SlideEvent;
|
import com.auto.clicker.autoclicker.data.SlideEvent;
|
||||||
|
import com.auto.clicker.autoclicker.dialog.ScriptManagerDialog;
|
||||||
import com.auto.clicker.autoclicker.service.AutoClickService;
|
import com.auto.clicker.autoclicker.service.AutoClickService;
|
||||||
import com.auto.clicker.autoclicker.service.ForegroundService;
|
import com.auto.clicker.autoclicker.service.ForegroundService;
|
||||||
import com.auto.clicker.autoclicker.util.PrefUtils;
|
import com.auto.clicker.autoclicker.util.PrefUtils;
|
||||||
@ -45,8 +47,8 @@ import java.util.List;
|
|||||||
|
|
||||||
public class FloatingViewManager {
|
public class FloatingViewManager {
|
||||||
private static final String TAG = "FloatingViewManager";
|
private static final String TAG = "FloatingViewManager";
|
||||||
private static final long DEBOUNCE_INTERVAL = 500;
|
public static final long DEBOUNCE_INTERVAL = 500;
|
||||||
private static final int TOUCH_POINT_SIZE = 100;
|
public static final int TOUCH_POINT_SIZE = 100;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final WindowManager windowManager;
|
private final WindowManager windowManager;
|
||||||
@ -62,6 +64,8 @@ public class FloatingViewManager {
|
|||||||
private boolean isMultipleRunning = false;
|
private boolean isMultipleRunning = false;
|
||||||
private long lastToggleTime = 0;
|
private long lastToggleTime = 0;
|
||||||
|
|
||||||
|
private boolean areEventsVisible = true;
|
||||||
|
|
||||||
public FloatingViewManager(Context context) {
|
public FloatingViewManager(Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||||
@ -69,7 +73,6 @@ public class FloatingViewManager {
|
|||||||
this.screenWidth = screenSize.x;
|
this.screenWidth = screenSize.x;
|
||||||
this.screenHeight = screenSize.y;
|
this.screenHeight = screenSize.y;
|
||||||
registerBroadcastReceiver();
|
registerBroadcastReceiver();
|
||||||
Log.d(TAG, "Screen size: " + screenWidth + "x" + screenHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showFloatingViews(int mode) throws SecurityException {
|
public void showFloatingViews(int mode) throws SecurityException {
|
||||||
@ -77,83 +80,18 @@ public class FloatingViewManager {
|
|||||||
throw new SecurityException("需要悬浮窗许可");
|
throw new SecurityException("需要悬浮窗许可");
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFloatingViews(); // 移除已有悬浮窗
|
removeFloatingViews();
|
||||||
showMultipleModeViews(mode); // 显示新模式悬浮窗
|
showMultipleModeViews(mode);
|
||||||
|
|
||||||
Log.d(TAG, "添加悬浮窗, 模式 = " + mode);
|
Log.d(TAG, "添加悬浮窗, 模式 = " + mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeFloatingViews() {
|
|
||||||
try {
|
|
||||||
// 移除点事件视图
|
|
||||||
for (EventWrapper wrapper : new ArrayList<>(runtimeEvents)) {
|
|
||||||
if (wrapper.getType() == EventWrapper.EventType.POINT) {
|
|
||||||
PointEvent pe = (PointEvent) wrapper.getEvent();
|
|
||||||
safeRemoveView(pe.getView());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移除滑动事件视图
|
|
||||||
for (EventWrapper wrapper : new ArrayList<>(runtimeEvents)) {
|
|
||||||
if (wrapper.getType() == EventWrapper.EventType.SLIDE) {
|
|
||||||
SlideEvent se = (SlideEvent) wrapper.getEvent();
|
|
||||||
safeRemoveView(se.getStartPoint().getView());
|
|
||||||
safeRemoveView(se.getEndPoint().getView());
|
|
||||||
safeRemoveView(se.getLineView());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runtimeEvents.clear();
|
|
||||||
|
|
||||||
if (multipleControlBarView != null) {
|
|
||||||
safeRemoveView(multipleControlBarView);
|
|
||||||
multipleControlBarView = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
isMultipleRunning = false;
|
|
||||||
Log.d(TAG, "悬浮窗已移除");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, "移除悬浮窗失败", e);
|
|
||||||
isMultipleRunning = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void safeRemoveView(View view) {
|
|
||||||
try {
|
|
||||||
windowManager.removeView(view);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, "移除视图失败: " + view, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void closeFloatingViews() {
|
|
||||||
// 停止自动点击服务
|
|
||||||
AutoClickService service = AutoClickService.getInstance();
|
|
||||||
if (service != null) {
|
|
||||||
service.stopClicking();
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "自动点击服务没有初始化");
|
|
||||||
Toast.makeText(context, "请同意无障碍服务权限", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTouchPointsBackground(R.drawable.ring_has_bg);
|
|
||||||
removeFloatingViews(); // 移除所有悬浮窗
|
|
||||||
|
|
||||||
context.stopService(new Intent(context, ForegroundService.class));
|
|
||||||
PrefUtils.setFloatingShown(context, 0);
|
|
||||||
|
|
||||||
// 发送浮窗关闭广播
|
|
||||||
Intent intent = new Intent("com.auto.autoclicker.FLOATING_WINDOW_STATE_CHANGED");
|
|
||||||
intent.putExtra("isShown", false);
|
|
||||||
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showMultipleModeViews(int mode) {
|
private void showMultipleModeViews(int mode) {
|
||||||
initMultipleTouchPointView(); // 初始化点击点数据
|
initMultipleTouchPointView();
|
||||||
initMultipleControlBar(); // 初始化控制栏视图
|
initMultipleControlBar();
|
||||||
|
|
||||||
if (multipleControlBarView != null) {
|
if (multipleControlBarView != null) {
|
||||||
windowManager.addView(multipleControlBarView, multipleControlBarParams); // 显示控制栏
|
windowManager.addView(multipleControlBarView, multipleControlBarParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,43 +105,9 @@ public class FloatingViewManager {
|
|||||||
multipleControlBarView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.multiple_control_bar, null);
|
multipleControlBarView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.multiple_control_bar, null);
|
||||||
multipleControlBarParams = createControlBarParams();
|
multipleControlBarParams = createControlBarParams();
|
||||||
|
|
||||||
setupDraggableView(multipleControlBarView, multipleControlBarParams, this::updateMoreControlBarPosition); // 可拖动
|
setupDraggableView(multipleControlBarView, multipleControlBarParams, this::updateMoreControlBarPosition);
|
||||||
|
|
||||||
setupControlButtons(multipleControlBarView); // 初始化按钮功能
|
setupControlButtons(multipleControlBarView);
|
||||||
}
|
|
||||||
|
|
||||||
private void setupControlButtons(LinearLayout controlBar) {
|
|
||||||
ImageView playButton = controlBar.findViewById(R.id.play_button);
|
|
||||||
ImageView addButton = controlBar.findViewById(R.id.add_button);
|
|
||||||
ImageView removeButton = controlBar.findViewById(R.id.remove_button);
|
|
||||||
ImageView slideButton = controlBar.findViewById(R.id.slide_button);
|
|
||||||
ImageView closeButton = controlBar.findViewById(R.id.close_button);
|
|
||||||
ImageView settingButton = controlBar.findViewById(R.id.settings_button);
|
|
||||||
|
|
||||||
// 按钮点击事件绑定
|
|
||||||
playButton.setOnClickListener(v -> toggleMultipleClicking());
|
|
||||||
addButton.setOnClickListener(v -> addPointEvent());
|
|
||||||
removeButton.setOnClickListener(v -> removeLastEvent());
|
|
||||||
slideButton.setOnClickListener(v -> addSlideEvent());
|
|
||||||
closeButton.setOnClickListener(v -> closeFloatingViews());
|
|
||||||
settingButton.setOnClickListener(v -> showInputDialog());
|
|
||||||
}
|
|
||||||
|
|
||||||
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 createControlBarParams() {
|
private WindowManager.LayoutParams createControlBarParams() {
|
||||||
@ -226,25 +130,115 @@ public class FloatingViewManager {
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
private WindowManager.LayoutParams createLineViewParams() {
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
// 创建连接线(滑动路径)的参数,全屏不可点击
|
private void setupDraggableView(View view, WindowManager.LayoutParams params, PositionUpdater positionUpdater) {
|
||||||
int overlayType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
|
view.setOnTouchListener(new View.OnTouchListener() {
|
||||||
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
|
private float lastX, lastY;
|
||||||
WindowManager.LayoutParams.TYPE_PHONE;
|
private float paramX, paramY;
|
||||||
|
private boolean isDragging = false;
|
||||||
|
|
||||||
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
|
@Override
|
||||||
WindowManager.LayoutParams.MATCH_PARENT,
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
WindowManager.LayoutParams.MATCH_PARENT,
|
if (isMultipleRunning) {
|
||||||
overlayType,
|
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
|
v.performClick();
|
||||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
|
return true;
|
||||||
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
|
}
|
||||||
PixelFormat.TRANSLUCENT
|
return false;
|
||||||
);
|
}
|
||||||
params.gravity = Gravity.TOP | Gravity.START;
|
|
||||||
params.x = 0;
|
switch (event.getAction()) {
|
||||||
params.y = 0;
|
case MotionEvent.ACTION_DOWN:
|
||||||
return params;
|
lastX = event.getRawX();
|
||||||
|
lastY = event.getRawY();
|
||||||
|
paramX = params.x;
|
||||||
|
paramY = params.y;
|
||||||
|
isDragging = true;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
if (isDragging) {
|
||||||
|
float dx = event.getRawX() - lastX;
|
||||||
|
float dy = event.getRawY() - lastY;
|
||||||
|
positionUpdater.update(paramX, paramY, dx, dy);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
isDragging = false;
|
||||||
|
v.performClick();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMoreControlBarPosition(float paramX, float paramY, float dx, float dy) {
|
||||||
|
Point constrainedPoint = ViewUtils.constrainToScreen(
|
||||||
|
paramX + dx, paramY + dy,
|
||||||
|
screenWidth - multipleControlBarView.getWidth(),
|
||||||
|
screenHeight - multipleControlBarView.getHeight());
|
||||||
|
multipleControlBarParams.x = constrainedPoint.x;
|
||||||
|
multipleControlBarParams.y = constrainedPoint.y;
|
||||||
|
windowManager.updateViewLayout(multipleControlBarView, multipleControlBarParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupControlButtons(LinearLayout controlBar) {
|
||||||
|
ImageView playButton = controlBar.findViewById(R.id.play_button);
|
||||||
|
ImageView addButton = controlBar.findViewById(R.id.add_button);
|
||||||
|
ImageView removeButton = controlBar.findViewById(R.id.remove_button);
|
||||||
|
ImageView slideButton = controlBar.findViewById(R.id.slide_button);
|
||||||
|
ImageView saveButton = controlBar.findViewById(R.id.save_button);
|
||||||
|
ImageView eyeButton = controlBar.findViewById(R.id.eye_button);
|
||||||
|
ImageView settingButton = controlBar.findViewById(R.id.settings_button);
|
||||||
|
ImageView closeButton = controlBar.findViewById(R.id.close_button);
|
||||||
|
|
||||||
|
playButton.setOnClickListener(v -> toggleMultipleClicking());
|
||||||
|
addButton.setOnClickListener(v -> addPointEvent());
|
||||||
|
removeButton.setOnClickListener(v -> removeLastEvent());
|
||||||
|
slideButton.setOnClickListener(v -> addSlideEvent());
|
||||||
|
saveButton.setOnClickListener(v -> {
|
||||||
|
ScriptManagerDialog dialog = new ScriptManagerDialog(context);
|
||||||
|
dialog.show(); // 显示弹窗
|
||||||
|
});
|
||||||
|
|
||||||
|
eyeButton.setOnClickListener(v -> {
|
||||||
|
areEventsVisible = !areEventsVisible;
|
||||||
|
toggleEventsVisibility(areEventsVisible);
|
||||||
|
if (areEventsVisible) {
|
||||||
|
eyeButton.setImageResource(R.drawable.eye);
|
||||||
|
} else {
|
||||||
|
eyeButton.setImageResource(R.drawable.hide_eye);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
settingButton.setOnClickListener(v -> showInputDialog());
|
||||||
|
closeButton.setOnClickListener(v -> closeFloatingViews());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleMultipleClicking() {
|
||||||
|
if (isDebounced()) return;
|
||||||
|
|
||||||
|
AutoClickService service = AutoClickService.getInstance();
|
||||||
|
if (service == null) {
|
||||||
|
handleMissingService(); // 无障碍服务未开启
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runtimeEvents.isEmpty()) {
|
||||||
|
Toast.makeText(context, "请添加一个触摸点或者滑动事件", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMultipleRunning) {
|
||||||
|
stopMultiClicking(service); // 停止点击
|
||||||
|
} else {
|
||||||
|
startMultiClicking(service); // 开始点击
|
||||||
|
}
|
||||||
|
|
||||||
|
isMultipleRunning = !isMultipleRunning;
|
||||||
|
Log.d(TAG, "多点点击服务状态: " + service.isClicking());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@ -319,6 +313,64 @@ public class FloatingViewManager {
|
|||||||
updateServicePositions();
|
updateServicePositions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void toggleEventsVisibility(boolean show) {
|
||||||
|
for (EventWrapper wrapper : new ArrayList<>(runtimeEvents)) {
|
||||||
|
// 获取事件的视图
|
||||||
|
View viewToToggle = null;
|
||||||
|
if (wrapper.getType() == EventWrapper.EventType.POINT) {
|
||||||
|
viewToToggle = ((PointEvent) wrapper.getEvent()).getView();
|
||||||
|
} else if (wrapper.getType() == EventWrapper.EventType.SLIDE) {
|
||||||
|
View startPointView = getView(show, wrapper);
|
||||||
|
|
||||||
|
WindowManager.LayoutParams startParams = (WindowManager.LayoutParams) startPointView.getLayoutParams();
|
||||||
|
if (show) {
|
||||||
|
startParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
|
||||||
|
} else {
|
||||||
|
startParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
|
||||||
|
}
|
||||||
|
windowManager.updateViewLayout(startPointView, startParams);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewToToggle != null) {
|
||||||
|
if (show) {
|
||||||
|
viewToToggle.setAlpha(1.0f);
|
||||||
|
} else {
|
||||||
|
viewToToggle.setAlpha(0.0f);
|
||||||
|
}
|
||||||
|
WindowManager.LayoutParams params = (WindowManager.LayoutParams) viewToToggle.getLayoutParams();
|
||||||
|
if (show) {
|
||||||
|
params.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
|
||||||
|
} else {
|
||||||
|
params.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
|
||||||
|
}
|
||||||
|
windowManager.updateViewLayout(viewToToggle, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static View getView(boolean show, EventWrapper wrapper) {
|
||||||
|
SlideEvent slideEvent = (SlideEvent) wrapper.getEvent();
|
||||||
|
// 假设你只隐藏/显示点,线也需要同步
|
||||||
|
View startPointView = slideEvent.getStartPoint().getView();
|
||||||
|
View endPointView = slideEvent.getEndPoint().getView();
|
||||||
|
View lineView = slideEvent.getLineView();
|
||||||
|
|
||||||
|
if (show) {
|
||||||
|
// 如果是显示,可能需要重新添加或设置可见性
|
||||||
|
// 这里假设你只是更新透明度,如果之前移除了,则需要重新addView
|
||||||
|
startPointView.setAlpha(1.0f);
|
||||||
|
endPointView.setAlpha(1.0f);
|
||||||
|
lineView.setAlpha(1.0f); // 如果线视图也需要隐藏/显示
|
||||||
|
} else {
|
||||||
|
startPointView.setAlpha(0.0f);
|
||||||
|
endPointView.setAlpha(0.0f);
|
||||||
|
lineView.setAlpha(0.0f); // 隐藏线视图
|
||||||
|
}
|
||||||
|
return startPointView;
|
||||||
|
}
|
||||||
|
|
||||||
private void removeLastEvent() {
|
private void removeLastEvent() {
|
||||||
if (isMultipleRunning) {
|
if (isMultipleRunning) {
|
||||||
Toast.makeText(context, "请先停止点击", Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, "请先停止点击", Toast.LENGTH_SHORT).show();
|
||||||
@ -337,6 +389,66 @@ public class FloatingViewManager {
|
|||||||
updateServicePositions();
|
updateServicePositions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void closeFloatingViews() {
|
||||||
|
// 停止自动点击服务
|
||||||
|
AutoClickService service = AutoClickService.getInstance();
|
||||||
|
if (service != null) {
|
||||||
|
service.stopClicking();
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "自动点击服务没有初始化");
|
||||||
|
Toast.makeText(context, "请同意无障碍服务权限", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTouchPointsBackground(R.drawable.ring_has_bg);
|
||||||
|
removeFloatingViews(); // 移除所有悬浮窗
|
||||||
|
|
||||||
|
context.stopService(new Intent(context, ForegroundService.class));
|
||||||
|
PrefUtils.setFloatingShown(context, 0);
|
||||||
|
|
||||||
|
// 发送浮窗关闭广播
|
||||||
|
Intent intent = new Intent("com.auto.autoclicker.FLOATING_WINDOW_STATE_CHANGED");
|
||||||
|
intent.putExtra("isShown", false);
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startMultiClicking(AutoClickService service) {
|
||||||
|
service.addMultipleClickEvents(runtimeEvents);
|
||||||
|
service.startClicking();
|
||||||
|
|
||||||
|
Log.d(TAG, "开始多点点击事件 " + runtimeEvents.size());
|
||||||
|
Toast.makeText(context, "多点点击开始", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
updateTouchPointsBackground(R.drawable.touch_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopMultiClicking(AutoClickService service) {
|
||||||
|
service.stopClicking();
|
||||||
|
Toast.makeText(context, "多点点击停止", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
updateTouchPointsBackground(R.drawable.ring_has_bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
private void removeEvent(Event event) {
|
private void removeEvent(Event event) {
|
||||||
try {
|
try {
|
||||||
// 判断类型并移除相关视图
|
// 判断类型并移除相关视图
|
||||||
@ -354,53 +466,47 @@ public class FloatingViewManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateServicePositions() {
|
public void removeFloatingViews() {
|
||||||
// 更新服务中记录的位置
|
try {
|
||||||
AutoClickService service = AutoClickService.getInstance();
|
// 移除点事件视图
|
||||||
if (service != null) {
|
for (EventWrapper wrapper : new ArrayList<>(runtimeEvents)) {
|
||||||
service.addMultipleClickEvents(runtimeEvents);
|
if (wrapper.getType() == EventWrapper.EventType.POINT) {
|
||||||
|
PointEvent pe = (PointEvent) wrapper.getEvent();
|
||||||
|
safeRemoveView(pe.getView());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除滑动事件视图
|
||||||
|
for (EventWrapper wrapper : new ArrayList<>(runtimeEvents)) {
|
||||||
|
if (wrapper.getType() == EventWrapper.EventType.SLIDE) {
|
||||||
|
SlideEvent se = (SlideEvent) wrapper.getEvent();
|
||||||
|
safeRemoveView(se.getStartPoint().getView());
|
||||||
|
safeRemoveView(se.getEndPoint().getView());
|
||||||
|
safeRemoveView(se.getLineView());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runtimeEvents.clear();
|
||||||
|
|
||||||
|
if (multipleControlBarView != null) {
|
||||||
|
safeRemoveView(multipleControlBarView);
|
||||||
|
multipleControlBarView = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
isMultipleRunning = false;
|
||||||
|
Log.d(TAG, "悬浮窗已移除");
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "移除悬浮窗失败", e);
|
||||||
|
isMultipleRunning = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toggleMultipleClicking() {
|
private void safeRemoveView(View view) {
|
||||||
if (isDebounced()) return;
|
try {
|
||||||
|
windowManager.removeView(view);
|
||||||
AutoClickService service = AutoClickService.getInstance();
|
} catch (Exception e) {
|
||||||
if (service == null) {
|
Log.w(TAG, "移除视图失败: " + view, e);
|
||||||
handleMissingService(); // 无障碍服务未开启
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runtimeEvents.isEmpty()) {
|
|
||||||
Toast.makeText(context, "请添加一个触摸点或者滑动事件", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMultipleRunning) {
|
|
||||||
stopMultiClicking(service); // 停止点击
|
|
||||||
} else {
|
|
||||||
startMultiClicking(service); // 开始点击
|
|
||||||
}
|
|
||||||
|
|
||||||
isMultipleRunning = !isMultipleRunning;
|
|
||||||
Log.d(TAG, "多点点击服务状态: " + service.isClicking());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startMultiClicking(AutoClickService service) {
|
|
||||||
service.addMultipleClickEvents(runtimeEvents);
|
|
||||||
service.startClicking();
|
|
||||||
|
|
||||||
Log.d(TAG, "开始多点点击事件 " + runtimeEvents.size());
|
|
||||||
Toast.makeText(context, "多点点击开始", Toast.LENGTH_SHORT).show();
|
|
||||||
|
|
||||||
updateTouchPointsBackground(R.drawable.touch_point);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopMultiClicking(AutoClickService service) {
|
|
||||||
service.stopClicking();
|
|
||||||
Toast.makeText(context, "多点点击停止", Toast.LENGTH_SHORT).show();
|
|
||||||
|
|
||||||
updateTouchPointsBackground(R.drawable.ring_has_bg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTouchPointsBackground(int resourceId) {
|
private void updateTouchPointsBackground(int resourceId) {
|
||||||
@ -437,51 +543,6 @@ public class FloatingViewManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
|
||||||
private void setupDraggableView(View view, WindowManager.LayoutParams params, PositionUpdater positionUpdater) {
|
|
||||||
view.setOnTouchListener(new View.OnTouchListener() {
|
|
||||||
private float lastX, lastY;
|
|
||||||
private float paramX, paramY;
|
|
||||||
private boolean isDragging = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
|
||||||
if (isMultipleRunning) {
|
|
||||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
|
||||||
v.performClick();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (event.getAction()) {
|
|
||||||
case MotionEvent.ACTION_DOWN:
|
|
||||||
lastX = event.getRawX();
|
|
||||||
lastY = event.getRawY();
|
|
||||||
paramX = params.x;
|
|
||||||
paramY = params.y;
|
|
||||||
isDragging = true;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case MotionEvent.ACTION_MOVE:
|
|
||||||
if (isDragging) {
|
|
||||||
float dx = event.getRawX() - lastX;
|
|
||||||
float dy = event.getRawY() - lastY;
|
|
||||||
positionUpdater.update(paramX, paramY, dx, dy);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MotionEvent.ACTION_UP:
|
|
||||||
isDragging = false;
|
|
||||||
v.performClick();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private void setupDraggablePoint(TextView point, WindowManager.LayoutParams params, PointEvent pointEvent) {
|
private void setupDraggablePoint(TextView point, WindowManager.LayoutParams params, PointEvent pointEvent) {
|
||||||
point.setOnTouchListener(new View.OnTouchListener() {
|
point.setOnTouchListener(new View.OnTouchListener() {
|
||||||
@ -575,16 +636,6 @@ public class FloatingViewManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMoreControlBarPosition(float paramX, float paramY, float dx, float dy) {
|
|
||||||
Point constrainedPoint = ViewUtils.constrainToScreen(
|
|
||||||
paramX + dx, paramY + dy,
|
|
||||||
screenWidth - multipleControlBarView.getWidth(),
|
|
||||||
screenHeight - multipleControlBarView.getHeight());
|
|
||||||
multipleControlBarParams.x = constrainedPoint.x;
|
|
||||||
multipleControlBarParams.y = constrainedPoint.y;
|
|
||||||
windowManager.updateViewLayout(multipleControlBarView, multipleControlBarParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateLinePosition(SlideEvent slideEvent) {
|
private void updateLinePosition(SlideEvent slideEvent) {
|
||||||
PointEvent start = slideEvent.getStartPoint();
|
PointEvent start = slideEvent.getStartPoint();
|
||||||
PointEvent end = slideEvent.getEndPoint();
|
PointEvent end = slideEvent.getEndPoint();
|
||||||
@ -609,6 +660,31 @@ public class FloatingViewManager {
|
|||||||
return touchPointView;
|
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 void updateServicePositions() {
|
||||||
|
// 更新服务中记录的位置
|
||||||
|
AutoClickService service = AutoClickService.getInstance();
|
||||||
|
if (service != null) {
|
||||||
|
service.addMultipleClickEvents(runtimeEvents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void showInputDialog() {
|
public void showInputDialog() {
|
||||||
EditText input = new EditText(context);
|
EditText input = new EditText(context);
|
||||||
input.setInputType(InputType.TYPE_CLASS_NUMBER);
|
input.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
|
|||||||
17
app/src/main/res/layout/dialog_script_container.xml
Normal file
17
app/src/main/res/layout/dialog_script_container.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<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">
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/tab_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@android:color/transparent"/>
|
||||||
|
|
||||||
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
|
android:id="@+id/view_pager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
6
app/src/main/res/layout/item_script.xml
Normal file
6
app/src/main/res/layout/item_script.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
32
app/src/main/res/layout/load_script_page.xml
Normal file
32
app/src/main/res/layout/load_script_page.xml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/script_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="250dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/close"
|
||||||
|
android:layout_marginBottom="10dp"/>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatButton
|
||||||
|
android:id="@+id/close"
|
||||||
|
android:layout_width="223dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginTop="107dp"
|
||||||
|
android:background="@drawable/btn_border_background"
|
||||||
|
android:backgroundTint="#0BC4FC"
|
||||||
|
android:text="Close"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_marginBottom="28dp"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
63
app/src/main/res/layout/save_script_page.xml
Normal file
63
app/src/main/res/layout/save_script_page.xml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
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"
|
||||||
|
android:textSize="15sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/script_name_input"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="18dp"
|
||||||
|
android:backgroundTint="#56757c"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="12sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tip" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatButton
|
||||||
|
android:id="@+id/save"
|
||||||
|
android:layout_width="223dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="@drawable/btn_border_background"
|
||||||
|
android:text="Save Script"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginTop="107dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
android:backgroundTint="#0BC4FC"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/script_name_input"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatButton
|
||||||
|
android:id="@+id/close"
|
||||||
|
android:layout_width="223dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="@drawable/btn_border_background"
|
||||||
|
android:text="Close"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/save"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
Loading…
Reference in New Issue
Block a user