From 6cf551aa6cf873e33c443efcb03bdc4c9a586103 Mon Sep 17 00:00:00 2001 From: lihongwei Date: Mon, 23 Jun 2025 10:10:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9A=82=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../autoclicker/data/EventWrapper.java | 3 +- .../clicker/autoclicker/data/PointEvent.java | 2 +- .../clicker/autoclicker/data/SlideEvent.java | 8 - .../autoclicker/dialog/LoadScriptView.java | 39 ++ .../autoclicker/dialog/SaveScriptView.java | 35 ++ .../dialog/ScriptManagerDialog.java | 87 +++ .../clicker/autoclicker/room/AppDatabase.java | 7 +- .../autoclicker/room/PointEventEntity.java | 23 + .../autoclicker/room/PointEventEntityDao.java | 7 + .../autoclicker/room/ScriptEntity.java | 14 + .../autoclicker/room/ScriptEntityDao.java | 29 + .../autoclicker/room/ScriptWithEvents.java | 18 + .../autoclicker/room/SlideEventEntity.java | 25 + .../autoclicker/room/SlideEventEntityDao.java | 7 + .../autoclicker/room/TouchEventDao.java | 19 - .../autoclicker/room/TouchEventEntity.java | 116 ---- .../room/repository/TouchEventRepository.java | 34 -- .../room/viewmodel/TouchEventViewModel.java | 18 - .../ui/adapter/dialog/ViewPagerAdapter.java | 35 ++ .../ui/adapter/script/ScriptListAdapter.java | 53 ++ .../autoclicker/util/ScriptConverter.java | 48 ++ .../autoclicker/util/ScriptRestorer.java | 131 +++++ .../autoclicker/view/ConnectingLineView.java | 130 ++++- .../autoclicker/view/FloatingViewManager.java | 526 ++++++++++-------- .../res/layout/dialog_script_container.xml | 17 + app/src/main/res/layout/item_script.xml | 6 + app/src/main/res/layout/load_script_page.xml | 32 ++ app/src/main/res/layout/save_script_page.xml | 63 +++ 28 files changed, 1090 insertions(+), 442 deletions(-) create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/dialog/LoadScriptView.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/dialog/SaveScriptView.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/dialog/ScriptManagerDialog.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/PointEventEntity.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/PointEventEntityDao.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/ScriptEntity.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/ScriptEntityDao.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/ScriptWithEvents.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/SlideEventEntity.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/SlideEventEntityDao.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventDao.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventEntity.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/repository/TouchEventRepository.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/viewmodel/TouchEventViewModel.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/dialog/ViewPagerAdapter.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/script/ScriptListAdapter.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/util/ScriptConverter.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/util/ScriptRestorer.java create mode 100644 app/src/main/res/layout/dialog_script_container.xml create mode 100644 app/src/main/res/layout/item_script.xml create mode 100644 app/src/main/res/layout/load_script_page.xml create mode 100644 app/src/main/res/layout/save_script_page.xml diff --git a/app/src/main/java/com/auto/clicker/autoclicker/data/EventWrapper.java b/app/src/main/java/com/auto/clicker/autoclicker/data/EventWrapper.java index 93ee581..7e503d9 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/data/EventWrapper.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/data/EventWrapper.java @@ -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; } diff --git a/app/src/main/java/com/auto/clicker/autoclicker/data/PointEvent.java b/app/src/main/java/com/auto/clicker/autoclicker/data/PointEvent.java index ade4d60..76702b5 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/data/PointEvent.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/data/PointEvent.java @@ -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) { diff --git a/app/src/main/java/com/auto/clicker/autoclicker/data/SlideEvent.java b/app/src/main/java/com/auto/clicker/autoclicker/data/SlideEvent.java index 1e9c527..a1adaa1 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/data/SlideEvent.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/data/SlideEvent.java @@ -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; - } } \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/dialog/LoadScriptView.java b/app/src/main/java/com/auto/clicker/autoclicker/dialog/LoadScriptView.java new file mode 100644 index 0000000..29b70f7 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/dialog/LoadScriptView.java @@ -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 scripts = AppDatabase.getInstance(context).scriptDao().getAllScripts(); + new Handler(Looper.getMainLooper()).post(() -> { + recyclerView.setAdapter(new ScriptListAdapter(scripts, listener)); + }); + }).start(); + } +} + diff --git a/app/src/main/java/com/auto/clicker/autoclicker/dialog/SaveScriptView.java b/app/src/main/java/com/auto/clicker/autoclicker/dialog/SaveScriptView.java new file mode 100644 index 0000000..c0dce6d --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/dialog/SaveScriptView.java @@ -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(); + } + }); + } +} + diff --git a/app/src/main/java/com/auto/clicker/autoclicker/dialog/ScriptManagerDialog.java b/app/src/main/java/com/auto/clicker/autoclicker/dialog/ScriptManagerDialog.java new file mode 100644 index 0000000..b9666e4 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/dialog/ScriptManagerDialog.java @@ -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 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(); + } + } +} + diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/AppDatabase.java b/app/src/main/java/com/auto/clicker/autoclicker/room/AppDatabase.java index f98997f..ecbe19c 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/AppDatabase.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/AppDatabase.java @@ -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; diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/PointEventEntity.java b/app/src/main/java/com/auto/clicker/autoclicker/room/PointEventEntity.java new file mode 100644 index 0000000..94ddeae --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/PointEventEntity.java @@ -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; +} + diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/PointEventEntityDao.java b/app/src/main/java/com/auto/clicker/autoclicker/room/PointEventEntityDao.java new file mode 100644 index 0000000..574a48a --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/PointEventEntityDao.java @@ -0,0 +1,7 @@ +package com.auto.clicker.autoclicker.room; + +import androidx.room.Dao; + +@Dao +public interface PointEventEntityDao { +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/ScriptEntity.java b/app/src/main/java/com/auto/clicker/autoclicker/room/ScriptEntity.java new file mode 100644 index 0000000..de63b05 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/ScriptEntity.java @@ -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; +} + diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/ScriptEntityDao.java b/app/src/main/java/com/auto/clicker/autoclicker/room/ScriptEntityDao.java new file mode 100644 index 0000000..39decfd --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/ScriptEntityDao.java @@ -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 points); + @Insert void insertSlideEvents(List slides); + + @Transaction + @Query("SELECT * FROM scripts") + List getAllScripts(); + + @Transaction + @Query("SELECT * FROM scripts WHERE scriptId = :scriptId") + ScriptWithEvents getScriptById(long scriptId); + + @Delete + void deleteScript(ScriptEntity script); +} + diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/ScriptWithEvents.java b/app/src/main/java/com/auto/clicker/autoclicker/room/ScriptWithEvents.java new file mode 100644 index 0000000..f6fd9e7 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/ScriptWithEvents.java @@ -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 pointEvents; + + @Relation(parentColumn = "scriptId", entityColumn = "scriptOwnerId") + public List slideEvents; +} + diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/SlideEventEntity.java b/app/src/main/java/com/auto/clicker/autoclicker/room/SlideEventEntity.java new file mode 100644 index 0000000..c624a54 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/SlideEventEntity.java @@ -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; +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/SlideEventEntityDao.java b/app/src/main/java/com/auto/clicker/autoclicker/room/SlideEventEntityDao.java new file mode 100644 index 0000000..2f48edf --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/SlideEventEntityDao.java @@ -0,0 +1,7 @@ +package com.auto.clicker.autoclicker.room; + +import androidx.room.Dao; + +@Dao +public interface SlideEventEntityDao { +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventDao.java b/app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventDao.java deleted file mode 100644 index c92221c..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventDao.java +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventEntity.java b/app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventEntity.java deleted file mode 100644 index e13911f..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventEntity.java +++ /dev/null @@ -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; - } -} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/repository/TouchEventRepository.java b/app/src/main/java/com/auto/clicker/autoclicker/room/repository/TouchEventRepository.java deleted file mode 100644 index 03996d1..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/repository/TouchEventRepository.java +++ /dev/null @@ -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)); - } - -} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/viewmodel/TouchEventViewModel.java b/app/src/main/java/com/auto/clicker/autoclicker/room/viewmodel/TouchEventViewModel.java deleted file mode 100644 index b715061..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/viewmodel/TouchEventViewModel.java +++ /dev/null @@ -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); - } - -} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/dialog/ViewPagerAdapter.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/dialog/ViewPagerAdapter.java new file mode 100644 index 0000000..6a72e17 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/dialog/ViewPagerAdapter.java @@ -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 { + private final List views; + + public ViewPagerAdapter(List 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); } + } +} + diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/script/ScriptListAdapter.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/script/ScriptListAdapter.java new file mode 100644 index 0000000..2caf32c --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/script/ScriptListAdapter.java @@ -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 { + private List scripts; + private LoadScriptView.OnScriptClickListener listener; + + + public ScriptListAdapter(List 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); + + } + } +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/util/ScriptConverter.java b/app/src/main/java/com/auto/clicker/autoclicker/util/ScriptConverter.java new file mode 100644 index 0000000..67c6c4f --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/util/ScriptConverter.java @@ -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 toPointEventEntities(List runtimeEvents, long scriptId) { + List 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 toSlideEventEntities(List runtimeEvents, long scriptId) { + List 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; + } +} + diff --git a/app/src/main/java/com/auto/clicker/autoclicker/util/ScriptRestorer.java b/app/src/main/java/com/auto/clicker/autoclicker/util/ScriptRestorer.java new file mode 100644 index 0000000..2d704f9 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/util/ScriptRestorer.java @@ -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 restoreRuntimeEvents(ScriptWithEvents script) { + List 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; + } +} + diff --git a/app/src/main/java/com/auto/clicker/autoclicker/view/ConnectingLineView.java b/app/src/main/java/com/auto/clicker/autoclicker/view/ConnectingLineView.java index 02e85ee..1997b75 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/view/ConnectingLineView.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/view/ConnectingLineView.java @@ -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(); } } \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingViewManager.java b/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingViewManager.java index 390582d..afeab2a 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingViewManager.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingViewManager.java @@ -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); diff --git a/app/src/main/res/layout/dialog_script_container.xml b/app/src/main/res/layout/dialog_script_container.xml new file mode 100644 index 0000000..3200dbd --- /dev/null +++ b/app/src/main/res/layout/dialog_script_container.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/app/src/main/res/layout/item_script.xml b/app/src/main/res/layout/item_script.xml new file mode 100644 index 0000000..77d9ef6 --- /dev/null +++ b/app/src/main/res/layout/item_script.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/load_script_page.xml b/app/src/main/res/layout/load_script_page.xml new file mode 100644 index 0000000..d7c3353 --- /dev/null +++ b/app/src/main/res/layout/load_script_page.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/app/src/main/res/layout/save_script_page.xml b/app/src/main/res/layout/save_script_page.xml new file mode 100644 index 0000000..1ae0ac2 --- /dev/null +++ b/app/src/main/res/layout/save_script_page.xml @@ -0,0 +1,63 @@ + + + + + + + + + + +