This commit is contained in:
lihongwei 2025-06-23 10:10:14 +08:00
parent 0b20d1a01a
commit 6cf551aa6c
28 changed files with 1090 additions and 442 deletions

View File

@ -5,7 +5,7 @@ public class EventWrapper {
private EventType type;
private Event event;
private int order; // 添加顺序编号用于UI编号
private int order;
public EventWrapper(EventType type, Event event, int order) {
this.type = type;
@ -13,7 +13,6 @@ public class EventWrapper {
this.order = order;
}
public EventType getType() {
return type;
}

View File

@ -7,7 +7,7 @@ import android.widget.TextView;
public class PointEvent extends Event {
private int x;
private int y;
private TextView view; // 悬浮窗上的控件
private TextView view;
private WindowManager.LayoutParams layoutParams;
public PointEvent(int id, int x, int y, TextView view, WindowManager.LayoutParams layoutParams) {

View File

@ -10,7 +10,6 @@ public class SlideEvent extends Event {
private PointEvent endPoint;
private ConnectingLineView lineView;
private WindowManager.LayoutParams lineParams;
private long durationMillis; // 滑动时间
public SlideEvent(int id, PointEvent startPoint, PointEvent endPoint,
ConnectingLineView lineView, WindowManager.LayoutParams lineParams) {
@ -53,11 +52,4 @@ public class SlideEvent extends Event {
this.lineParams = lineParams;
}
public long getDurationMillis() {
return durationMillis;
}
public void setDurationMillis(long durationMillis) {
this.durationMillis = durationMillis;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
});
}
}

View File

@ -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();
}
}
}

View File

@ -8,10 +8,11 @@ import androidx.room.RoomDatabase;
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 TouchEventDao touchEventDao();
public abstract ScriptEntityDao scriptDao();
public abstract PointEventEntityDao pointEventEntityDao();
public abstract SlideEventEntityDao slideEventEntityDao();
private static volatile AppDatabase INSTANCE;

View File

@ -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;
}

View File

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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

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

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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); }
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -4,35 +4,133 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import com.auto.clicker.autoclicker.R;
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) {
super(context);
paint = new Paint();
paint.setColor(Color.RED);
paint.setAlpha(180);
paint.setStrokeWidth(5);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
// dp 单位的宽度转换为像素单位以便在 Canvas 上绘制
connectionBandWidthPx = dpToPx(context, CONNECTION_BAND_WIDTH_DP);
connectionBandPaint = new Paint();
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
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(startX, startY, endX, endY, paint);
}
public void setPoints(float startX, float startY, float endX, float endY) {
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
invalidate();
if (lineLength == 0) {
return;
}
// 保存当前画布的状态
// 这允许我们对画布进行平移和旋转操作而不会影响后续或外部的绘制
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();
}
}

View File

@ -25,6 +25,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
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.PositionUpdater;
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.ForegroundService;
import com.auto.clicker.autoclicker.util.PrefUtils;
@ -45,8 +47,8 @@ import java.util.List;
public class FloatingViewManager {
private static final String TAG = "FloatingViewManager";
private static final long DEBOUNCE_INTERVAL = 500;
private static final int TOUCH_POINT_SIZE = 100;
public static final long DEBOUNCE_INTERVAL = 500;
public static final int TOUCH_POINT_SIZE = 100;
private final Context context;
private final WindowManager windowManager;
@ -62,6 +64,8 @@ public class FloatingViewManager {
private boolean isMultipleRunning = false;
private long lastToggleTime = 0;
private boolean areEventsVisible = true;
public FloatingViewManager(Context context) {
this.context = context;
this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@ -69,7 +73,6 @@ public class FloatingViewManager {
this.screenWidth = screenSize.x;
this.screenHeight = screenSize.y;
registerBroadcastReceiver();
Log.d(TAG, "Screen size: " + screenWidth + "x" + screenHeight);
}
public void showFloatingViews(int mode) throws SecurityException {
@ -77,83 +80,18 @@ public class FloatingViewManager {
throw new SecurityException("需要悬浮窗许可");
}
removeFloatingViews(); // 移除已有悬浮窗
showMultipleModeViews(mode); // 显示新模式悬浮窗
removeFloatingViews();
showMultipleModeViews(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) {
initMultipleTouchPointView(); // 初始化点击点数据
initMultipleControlBar(); // 初始化控制栏视图
initMultipleTouchPointView();
initMultipleControlBar();
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);
multipleControlBarParams = createControlBarParams();
setupDraggableView(multipleControlBarView, multipleControlBarParams, this::updateMoreControlBarPosition); // 可拖动
setupDraggableView(multipleControlBarView, multipleControlBarParams, this::updateMoreControlBarPosition);
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;
setupControlButtons(multipleControlBarView);
}
private WindowManager.LayoutParams createControlBarParams() {
@ -226,25 +130,115 @@ public class FloatingViewManager {
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;
@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;
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;
@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;
}
});
}
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")
@ -319,6 +313,64 @@ public class FloatingViewManager {
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() {
if (isMultipleRunning) {
Toast.makeText(context, "请先停止点击", Toast.LENGTH_SHORT).show();
@ -337,6 +389,66 @@ public class FloatingViewManager {
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) {
try {
// 判断类型并移除相关视图
@ -354,53 +466,47 @@ public class FloatingViewManager {
}
}
private void updateServicePositions() {
// 更新服务中记录的位置
AutoClickService service = AutoClickService.getInstance();
if (service != null) {
service.addMultipleClickEvents(runtimeEvents);
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 toggleMultipleClicking() {
if (isDebounced()) return;
AutoClickService service = AutoClickService.getInstance();
if (service == null) {
handleMissingService(); // 无障碍服务未开启
return;
private void safeRemoveView(View view) {
try {
windowManager.removeView(view);
} catch (Exception e) {
Log.w(TAG, "移除视图失败: " + view, e);
}
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) {
@ -437,51 +543,6 @@ public class FloatingViewManager {
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")
private void setupDraggablePoint(TextView point, WindowManager.LayoutParams params, PointEvent pointEvent) {
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) {
PointEvent start = slideEvent.getStartPoint();
PointEvent end = slideEvent.getEndPoint();
@ -609,6 +660,31 @@ public class FloatingViewManager {
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() {
EditText input = new EditText(context);
input.setInputType(InputType.TYPE_CLASS_NUMBER);

View 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>

View 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>

View 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>

View 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>