From add6f3d1e0691811a13fc8ad9e55cca979656e17 Mon Sep 17 00:00:00 2001 From: lihongwei Date: Mon, 23 Jun 2025 18:04:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=B7=BB=E5=8A=A0=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 7 - .../clicker/autoclicker/data/PointEvent.java | 22 +- .../clicker/autoclicker/data/SlideEvent.java | 22 +- .../autoclicker/dialog/LoadScriptView.java | 39 ---- .../autoclicker/dialog/SaveScriptView.java | 35 --- .../dialog/ScriptManagerDialog.java | 87 ------- .../clicker/autoclicker/room/AppDatabase.java | 14 +- .../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 - .../room/SolutionWithEventsAndPoints.java | 50 ++++ .../autoclicker/room/dao/EventEntityDao.java | 23 ++ .../autoclicker/room/dao/SolutionDao.java | 44 ++++ .../autoclicker/room/dao/TouchPointDao.java | 24 ++ .../autoclicker/room/entity/EventEntity.java | 129 ++++++++++ .../autoclicker/room/entity/Solution.java | 27 +++ .../autoclicker/room/entity/TouchPoint.java | 31 +++ .../room/repository/EventRepository.java | 169 ++++++++++++++ .../autoclicker/service/AutoClickService.java | 4 +- .../floating/TransparentFloatingActivity.java | 109 --------- .../ui/adapter/dialog/ViewPagerAdapter.java | 35 --- .../ui/adapter/floating/TabPagerAdapter.java | 39 ---- .../ui/adapter/script/ScriptListAdapter.java | 16 +- .../ui/floating/SolutionAdapter.java | 71 ++++++ .../util/FloatingTabDialogManager.java | 221 +++++++++++++----- .../autoclicker/util/ScriptConverter.java | 48 ---- .../autoclicker/util/ScriptRestorer.java | 131 ----------- .../autoclicker/view/FloatingViewManager.java | 197 ++++++++++++++-- app/src/main/res/layout/activity_scripts.xml | 2 +- .../layout/activity_transparent_floating.xml | 10 - .../main/res/layout/floating_tab_dialog.xml | 60 ++--- .../main/res/layout/fragment_how_multi.xml | 6 +- .../main/res/layout/fragment_how_single.xml | 4 +- app/src/main/res/layout/save_script_page.xml | 1 - app/src/main/res/values/strings.xml | 4 +- app/src/main/res/values/themes.xml | 14 -- 40 files changed, 985 insertions(+), 833 deletions(-) delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/dialog/LoadScriptView.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/dialog/SaveScriptView.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/dialog/ScriptManagerDialog.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/PointEventEntity.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/PointEventEntityDao.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/ScriptEntity.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/ScriptEntityDao.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/ScriptWithEvents.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/SlideEventEntity.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/SlideEventEntityDao.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/SolutionWithEventsAndPoints.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/dao/EventEntityDao.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/dao/SolutionDao.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/dao/TouchPointDao.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/entity/EventEntity.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/entity/Solution.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/entity/TouchPoint.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/room/repository/EventRepository.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/ui/activity/floating/TransparentFloatingActivity.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/dialog/ViewPagerAdapter.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/floating/TabPagerAdapter.java create mode 100644 app/src/main/java/com/auto/clicker/autoclicker/ui/floating/SolutionAdapter.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/util/ScriptConverter.java delete mode 100644 app/src/main/java/com/auto/clicker/autoclicker/util/ScriptRestorer.java delete mode 100644 app/src/main/res/layout/activity_transparent_floating.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 00db0e7..78207e4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,13 +18,6 @@ android:supportsRtl="true" android:theme="@style/Theme.AutoClicker" tools:targetApi="31"> - 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 76702b5..e180cb7 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 @@ -1,21 +1,29 @@ package com.auto.clicker.autoclicker.data; -import android.view.View; import android.view.WindowManager; import android.widget.TextView; public class PointEvent extends Event { + private int id; private int x; private int y; - private TextView view; - private WindowManager.LayoutParams layoutParams; - public PointEvent(int id, int x, int y, TextView view, WindowManager.LayoutParams layoutParams) { + private transient TextView view; + private transient WindowManager.LayoutParams layoutParams; + + public PointEvent(int id, int x, int y) { super(id); this.x = x; this.y = y; - this.view = view; - this.layoutParams = layoutParams; + } + + @Override + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; } public int getX() { @@ -34,7 +42,7 @@ public class PointEvent extends Event { this.y = y; } - public View getView() { + public TextView getView() { return view; } 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 a1adaa1..1f0cce5 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 @@ -4,20 +4,27 @@ import android.view.WindowManager; import com.auto.clicker.autoclicker.view.ConnectingLineView; - public class SlideEvent extends Event { + private int id; private PointEvent startPoint; private PointEvent endPoint; - private ConnectingLineView lineView; - private WindowManager.LayoutParams lineParams; - public SlideEvent(int id, PointEvent startPoint, PointEvent endPoint, - ConnectingLineView lineView, WindowManager.LayoutParams lineParams) { + private transient ConnectingLineView lineView; + private transient WindowManager.LayoutParams lineParams; + + public SlideEvent(int id, PointEvent startPoint, PointEvent endPoint) { super(id); this.startPoint = startPoint; this.endPoint = endPoint; - this.lineView = lineView; - this.lineParams = lineParams; + } + + @Override + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; } public PointEvent getStartPoint() { @@ -51,5 +58,4 @@ public class SlideEvent extends Event { public void setLineParams(WindowManager.LayoutParams lineParams) { this.lineParams = lineParams; } - } \ 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 deleted file mode 100644 index 29b70f7..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/dialog/LoadScriptView.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.auto.clicker.autoclicker.dialog; - -import android.content.Context; -import android.os.Handler; -import android.os.Looper; -import android.view.LayoutInflater; - -import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.auto.clicker.autoclicker.R; -import com.auto.clicker.autoclicker.room.AppDatabase; -import com.auto.clicker.autoclicker.room.ScriptEntity; -import com.auto.clicker.autoclicker.ui.adapter.script.ScriptListAdapter; - -import java.util.List; - -public class LoadScriptView extends ConstraintLayout { - public interface OnScriptClickListener { - void onScriptSelected(ScriptEntity script); - } - - public LoadScriptView(Context context, OnScriptClickListener listener) { - super(context); - LayoutInflater.from(context).inflate(R.layout.load_script_page, this, true); - - RecyclerView recyclerView = findViewById(R.id.script_list); - recyclerView.setLayoutManager(new LinearLayoutManager(context)); - - new Thread(() -> { - List 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 deleted file mode 100644 index c0dce6d..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/dialog/SaveScriptView.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.auto.clicker.autoclicker.dialog; - -import android.content.Context; -import android.view.LayoutInflater; -import android.widget.EditText; -import android.widget.Toast; - -import androidx.appcompat.widget.AppCompatButton; -import androidx.constraintlayout.widget.ConstraintLayout; - -import com.auto.clicker.autoclicker.R; - -public class SaveScriptView extends ConstraintLayout { - public interface OnSaveListener { - void onSave(String scriptName); - } - - public SaveScriptView(Context context, OnSaveListener listener) { - super(context); - LayoutInflater.from(context).inflate(R.layout.save_script_page, this, true); - - EditText input = findViewById(R.id.script_name_input); - AppCompatButton saveBtn = findViewById(R.id.save); - - saveBtn.setOnClickListener(v -> { - String name = input.getText().toString().trim(); - if (!name.isEmpty()) { - listener.onSave(name); - } else { - Toast.makeText(context, "名称不能为空", Toast.LENGTH_SHORT).show(); - } - }); - } -} - 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 deleted file mode 100644 index b9666e4..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/dialog/ScriptManagerDialog.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.auto.clicker.autoclicker.dialog; - -import android.content.Context; -import android.graphics.PixelFormat; -import android.os.Build; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowManager; - -import androidx.viewpager2.widget.ViewPager2; - -import com.auto.clicker.autoclicker.R; -import com.auto.clicker.autoclicker.room.ScriptEntity; -import com.auto.clicker.autoclicker.ui.adapter.dialog.ViewPagerAdapter; -import com.google.android.material.tabs.TabLayout; -import com.google.android.material.tabs.TabLayoutMediator; - -import java.util.ArrayList; -import java.util.List; - -public class ScriptManagerDialog { - private final WindowManager windowManager; - private final View rootView; - - public ScriptManagerDialog(Context context) { - windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - - rootView = LayoutInflater.from(context).inflate(R.layout.dialog_script_container, null); - TabLayout tabLayout = rootView.findViewById(R.id.tab_layout); - ViewPager2 viewPager = rootView.findViewById(R.id.view_pager); - - List 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 ecbe19c..a0d21c5 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 @@ -7,12 +7,18 @@ import androidx.room.Room; import androidx.room.RoomDatabase; import com.auto.clicker.autoclicker.MyApplication; +import com.auto.clicker.autoclicker.room.dao.EventEntityDao; +import com.auto.clicker.autoclicker.room.dao.TouchPointDao; +import com.auto.clicker.autoclicker.room.dao.SolutionDao; +import com.auto.clicker.autoclicker.room.entity.EventEntity; +import com.auto.clicker.autoclicker.room.entity.TouchPoint; +import com.auto.clicker.autoclicker.room.entity.Solution; -@Database(entities = {ScriptEntity.class,PointEventEntity.class,SlideEventEntity.class}, version = MyApplication.DB_VERSION, exportSchema = false) +@Database(entities = {Solution.class, TouchPoint.class, EventEntity.class}, version = MyApplication.DB_VERSION, exportSchema = false) public abstract class AppDatabase extends RoomDatabase { - public abstract ScriptEntityDao scriptDao(); - public abstract PointEventEntityDao pointEventEntityDao(); - public abstract SlideEventEntityDao slideEventEntityDao(); + public abstract SolutionDao solutionDao(); + public abstract TouchPointDao pointDao(); + public abstract EventEntityDao eventEntityDao(); private static volatile AppDatabase INSTANCE; 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 deleted file mode 100644 index 94ddeae..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/PointEventEntity.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.auto.clicker.autoclicker.room; - -import androidx.room.Entity; -import androidx.room.ForeignKey; -import androidx.room.Index; -import androidx.room.PrimaryKey; - -@Entity(tableName = "point_events", - foreignKeys = @ForeignKey(entity = ScriptEntity.class, - parentColumns = "scriptId", - childColumns = "scriptOwnerId", - onDelete = ForeignKey.CASCADE), - indices = {@Index("scriptOwnerId")}) -public class PointEventEntity { - @PrimaryKey(autoGenerate = true) - public long pointId; - - public int x; - public int y; - public int order; - public long scriptOwnerId; -} - 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 deleted file mode 100644 index 574a48a..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/PointEventEntityDao.java +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index de63b05..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/ScriptEntity.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.auto.clicker.autoclicker.room; - -import androidx.room.Entity; -import androidx.room.PrimaryKey; - -@Entity(tableName = "scripts") -public class ScriptEntity { - @PrimaryKey(autoGenerate = true) - public long scriptId; - - public String name; - public long createdTime; -} - 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 deleted file mode 100644 index 39decfd..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/ScriptEntityDao.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.auto.clicker.autoclicker.room; - -import androidx.room.Dao; -import androidx.room.Delete; -import androidx.room.Insert; -import androidx.room.Query; -import androidx.room.Transaction; - -import java.util.List; - -@Dao -public interface ScriptEntityDao { - @Insert - long insertScript(ScriptEntity script); - @Insert void insertPointEvents(List 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 deleted file mode 100644 index f6fd9e7..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/ScriptWithEvents.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.auto.clicker.autoclicker.room; - -import androidx.room.Embedded; -import androidx.room.Relation; - -import java.util.List; - -public class ScriptWithEvents { - @Embedded - public ScriptEntity script; - - @Relation(parentColumn = "scriptId", entityColumn = "scriptOwnerId") - public List 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 deleted file mode 100644 index c624a54..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/SlideEventEntity.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.auto.clicker.autoclicker.room; - -import androidx.room.Entity; -import androidx.room.ForeignKey; -import androidx.room.Index; -import androidx.room.PrimaryKey; - -@Entity(tableName = "slide_events", - foreignKeys = @ForeignKey(entity = ScriptEntity.class, - parentColumns = "scriptId", - childColumns = "scriptOwnerId", - onDelete = ForeignKey.CASCADE), - indices = {@Index("scriptOwnerId")}) -public class SlideEventEntity { - @PrimaryKey(autoGenerate = true) - public long slideId; - - public int startX; - public int startY; - public int endX; - public int endY; - - public int order; - public long scriptOwnerId; -} \ 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 deleted file mode 100644 index 2f48edf..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/room/SlideEventEntityDao.java +++ /dev/null @@ -1,7 +0,0 @@ -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/SolutionWithEventsAndPoints.java b/app/src/main/java/com/auto/clicker/autoclicker/room/SolutionWithEventsAndPoints.java new file mode 100644 index 0000000..a73f03f --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/SolutionWithEventsAndPoints.java @@ -0,0 +1,50 @@ +package com.auto.clicker.autoclicker.room; + +import androidx.room.Embedded; +import androidx.room.Relation; + +import com.auto.clicker.autoclicker.room.entity.EventEntity; +import com.auto.clicker.autoclicker.room.entity.TouchPoint; +import com.auto.clicker.autoclicker.room.entity.Solution; + +import java.util.List; + +// 这个 POJO 用于表示一个方案,以及该方案下的所有事件及其关联的坐标点 +public class SolutionWithEventsAndPoints { + @Embedded + public Solution solution; + + @Relation( + parentColumn = "solutionId", + entityColumn = "solution_id", + entity = EventEntity.class + ) + public List events; // 注意这里我们用 EventWithPoints + + // 内部类,用于表示一个事件以及它关联的起点和终点 + public static class EventWithPoints { + @Embedded + public EventEntity eventEntity; + + @Relation( + parentColumn = "click_point_id", + entityColumn = "pointId", + entity = TouchPoint.class + ) + public TouchPoint clickTouchPoint; // 单击事件的点 + + @Relation( + parentColumn = "slide_start_point_id", + entityColumn = "pointId", + entity = TouchPoint.class + ) + public TouchPoint slideStartTouchPoint; // 滑动事件的起点 + + @Relation( + parentColumn = "slide_end_point_id", + entityColumn = "pointId", + entity = TouchPoint.class + ) + public TouchPoint slideEndTouchPoint; // 滑动事件的终点 + } +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/dao/EventEntityDao.java b/app/src/main/java/com/auto/clicker/autoclicker/room/dao/EventEntityDao.java new file mode 100644 index 0000000..e879ffa --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/dao/EventEntityDao.java @@ -0,0 +1,23 @@ +package com.auto.clicker.autoclicker.room.dao; + +import androidx.room.Dao; +import androidx.room.Delete; +import androidx.room.Insert; +import androidx.room.Query; + +import com.auto.clicker.autoclicker.room.entity.EventEntity; + +import java.util.List; + +@Dao +public interface EventEntityDao { + @Insert + long insertEventEntity(EventEntity eventEntity); // 返回新插入的 eventId + + @Delete + int deleteEventEntity(EventEntity eventEntity); + + // 查询某个方案下的所有事件实体,按顺序排序 + @Query("SELECT * FROM events WHERE solution_id = :solutionId ORDER BY order_in_solution ASC") + List getEventsForSolution(long solutionId); +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/dao/SolutionDao.java b/app/src/main/java/com/auto/clicker/autoclicker/room/dao/SolutionDao.java new file mode 100644 index 0000000..a898205 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/dao/SolutionDao.java @@ -0,0 +1,44 @@ +package com.auto.clicker.autoclicker.room.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Delete; +import androidx.room.Query; +import androidx.room.Transaction; +import androidx.room.Update; + +import com.auto.clicker.autoclicker.room.SolutionWithEventsAndPoints; +import com.auto.clicker.autoclicker.room.entity.Solution; + +import java.util.List; + +@Dao +public interface SolutionDao { + @Insert + long insertSolution(Solution solution); // 返回新插入的 solutionId + + @Query("SELECT * FROM solutions") + List getAllSolutions(); + + @Query("SELECT * FROM solutions WHERE solutionId = :solutionId LIMIT 1") + Solution getSolutionById(long solutionId); + + @Query("SELECT * FROM solutions WHERE solution_name = :solutionName LIMIT 1") + Solution getSolutionByName(String solutionName); + + @Delete + int deleteSolution(Solution solution); // 返回删除的行数 + + @Update + int updateSolution(Solution solution); // 返回更新的行数 + + // 在 SolutionDao 中添加 + @Transaction // 表示这是一个事务操作,确保数据一致性 + @Query("SELECT * FROM solutions WHERE solutionId = :solutionId LIMIT 1") + SolutionWithEventsAndPoints getSolutionWithEventsAndPoints(long solutionId); + + // 也可以获取所有方案及其事件 + @Transaction + @Query("SELECT * FROM solutions") + List getAllSolutionsWithEventsAndPoints(); +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/dao/TouchPointDao.java b/app/src/main/java/com/auto/clicker/autoclicker/room/dao/TouchPointDao.java new file mode 100644 index 0000000..f16fc3a --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/dao/TouchPointDao.java @@ -0,0 +1,24 @@ +package com.auto.clicker.autoclicker.room.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; +import androidx.room.Delete; + +import com.auto.clicker.autoclicker.room.entity.TouchPoint; + +@Dao +public interface TouchPointDao { + @Insert + long insertPoint(TouchPoint touchPoint); // 返回新插入的 pointId + + @Query("SELECT * FROM touchPoints WHERE pointId = :pointId LIMIT 1") + TouchPoint getPointById(long pointId); + + @Delete + int deletePoint(TouchPoint touchPoint); + + @Update + int updatePoint(TouchPoint touchPoint); +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/entity/EventEntity.java b/app/src/main/java/com/auto/clicker/autoclicker/room/entity/EventEntity.java new file mode 100644 index 0000000..a8cc319 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/entity/EventEntity.java @@ -0,0 +1,129 @@ +package com.auto.clicker.autoclicker.room.entity; + +import static androidx.room.ForeignKey.CASCADE; + +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.PrimaryKey; + +@Entity(tableName = "events", + foreignKeys = { + @ForeignKey(entity = Solution.class, + parentColumns = "solutionId", + childColumns = "solution_id", + onDelete = CASCADE), + @ForeignKey(entity = TouchPoint.class, + parentColumns = "pointId", + childColumns = "click_point_id", + onDelete = CASCADE), + @ForeignKey(entity = TouchPoint.class, + parentColumns = "pointId", + childColumns = "slide_start_point_id", + onDelete = CASCADE), + @ForeignKey(entity = TouchPoint.class, + parentColumns = "pointId", + childColumns = "slide_end_point_id", + onDelete = CASCADE) + }) +public class EventEntity { + @PrimaryKey(autoGenerate = true) + public long eventId; + + @ColumnInfo(name = "solution_id") + public long solutionId; + + @ColumnInfo(name = "event_type") + public String eventType; + + @ColumnInfo(name = "order_in_solution") + public int orderInSolution; + + @ColumnInfo(name = "click_point_id") + public Long clickPointId; + + @ColumnInfo(name = "slide_start_point_id") + public Long slideStartPointId; + + @ColumnInfo(name = "slide_end_point_id") + public Long slideEndPointId; + + @ColumnInfo(name = "duration") + public long duration; + + public EventEntity(long solutionId, String eventType, int orderInSolution, + Long clickPointId, Long slideStartPointId, Long slideEndPointId, long duration) { + this.solutionId = solutionId; + this.eventType = eventType; + this.orderInSolution = orderInSolution; + this.clickPointId = clickPointId; + this.slideStartPointId = slideStartPointId; + this.slideEndPointId = slideEndPointId; + this.duration = duration; + } + + public long getEventId() { + return eventId; + } + + public void setEventId(long eventId) { + this.eventId = eventId; + } + + public long getSolutionId() { + return solutionId; + } + + public void setSolutionId(long solutionId) { + this.solutionId = solutionId; + } + + public String getEventType() { + return eventType; + } + + public void setEventType(String eventType) { + this.eventType = eventType; + } + + public int getOrderInSolution() { + return orderInSolution; + } + + public void setOrderInSolution(int orderInSolution) { + this.orderInSolution = orderInSolution; + } + + public Long getClickPointId() { + return clickPointId; + } + + public void setClickPointId(Long clickPointId) { + this.clickPointId = clickPointId; + } + + public Long getSlideStartPointId() { + return slideStartPointId; + } + + public void setSlideStartPointId(Long slideStartPointId) { + this.slideStartPointId = slideStartPointId; + } + + public Long getSlideEndPointId() { + return slideEndPointId; + } + + public void setSlideEndPointId(Long slideEndPointId) { + this.slideEndPointId = slideEndPointId; + } + + public long getDuration() { + return duration; + } + + public void setDuration(long duration) { + this.duration = duration; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/entity/Solution.java b/app/src/main/java/com/auto/clicker/autoclicker/room/entity/Solution.java new file mode 100644 index 0000000..a09fb4e --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/entity/Solution.java @@ -0,0 +1,27 @@ +package com.auto.clicker.autoclicker.room.entity; + +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "solutions") +public class Solution { + @PrimaryKey(autoGenerate = true) + public long solutionId; + + @ColumnInfo(name = "solution_name") + public String solutionName; + + public Solution(String solutionName) { + this.solutionName = solutionName; + } + + public long getSolutionId() { return solutionId; } + public void setSolutionId(long solutionId) { this.solutionId = solutionId; } + public String getSolutionName() { return solutionName; } + public void setSolutionName(String solutionName) { this.solutionName = solutionName; } +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/entity/TouchPoint.java b/app/src/main/java/com/auto/clicker/autoclicker/room/entity/TouchPoint.java new file mode 100644 index 0000000..4d66646 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/entity/TouchPoint.java @@ -0,0 +1,31 @@ +package com.auto.clicker.autoclicker.room.entity; + +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.PrimaryKey; + + + +@Entity(tableName = "touchPoints") +public class TouchPoint { + @PrimaryKey(autoGenerate = true) + public long pointId; + + @ColumnInfo(name = "x_coordinate") + public int x; + + @ColumnInfo(name = "y_coordinate") + public int y; + + public TouchPoint(int x, int y) { + this.x = x; + this.y = y; + } + + public long getPointId() { return pointId; } + public void setPointId(long pointId) { this.pointId = pointId; } + public int getX() { return x; } + public void setX(int x) { this.x = x; } + public int getY() { return y; } + public void setY(int y) { this.y = y; } +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/repository/EventRepository.java b/app/src/main/java/com/auto/clicker/autoclicker/room/repository/EventRepository.java new file mode 100644 index 0000000..316f595 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/repository/EventRepository.java @@ -0,0 +1,169 @@ +package com.auto.clicker.autoclicker.room.repository; + +import android.content.Context; + +import com.auto.clicker.autoclicker.data.Event; +import com.auto.clicker.autoclicker.data.EventWrapper; +import com.auto.clicker.autoclicker.data.PointEvent; +import com.auto.clicker.autoclicker.data.SlideEvent; +import com.auto.clicker.autoclicker.room.AppDatabase; +import com.auto.clicker.autoclicker.room.SolutionWithEventsAndPoints; +import com.auto.clicker.autoclicker.room.dao.EventEntityDao; +import com.auto.clicker.autoclicker.room.dao.TouchPointDao; +import com.auto.clicker.autoclicker.room.dao.SolutionDao; +import com.auto.clicker.autoclicker.room.entity.EventEntity; +import com.auto.clicker.autoclicker.room.entity.TouchPoint; +import com.auto.clicker.autoclicker.room.entity.Solution; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import android.content.Context; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import android.util.Log; // Add Log import for debugging + +public class EventRepository { + private final SolutionDao solutionDao; + private final TouchPointDao touchPointDao; // Renamed from PointDao + private final EventEntityDao eventEntityDao; + private final ExecutorService databaseWriteExecutor; + + public EventRepository(Context context) { + AppDatabase db = AppDatabase.getInstance(context); // Updated to getInstance + this.solutionDao = db.solutionDao(); + this.touchPointDao = db.pointDao(); // Assuming db.pointDao() returns TouchPointDao now + this.eventEntityDao = db.eventEntityDao(); + this.databaseWriteExecutor = Executors.newFixedThreadPool(2); + } + + // --- 插入操作 --- + public void insertSolutionWithEvents(Solution solution, List runtimeEvents, RepositoryCallback callback) { + databaseWriteExecutor.execute(() -> { + long solutionId = -1; // Initialize solutionId outside try-catch for broader scope + try { + // 1. 插入 Solution + solutionId = solutionDao.insertSolution(solution); + if (solutionId == -1) { + throw new RuntimeException("Failed to insert solution."); + } + + // 2. 遍历 runtimeEvents,插入 TouchPoint 和 EventEntity + for (EventWrapper wrapper : runtimeEvents) { + Event event = wrapper.getEvent(); + int order = wrapper.getOrder(); // Using order from EventWrapper + + long eventEntityId = -1; // To store the result of event entity insertion + + if (wrapper.getType() == EventWrapper.EventType.POINT && event instanceof PointEvent) { + PointEvent pointEvent = (PointEvent) event; + TouchPoint touchPoint = new TouchPoint(pointEvent.getX(), pointEvent.getY()); + long pointId = touchPointDao.insertPoint(touchPoint); + if (pointId == -1) + throw new RuntimeException("Failed to insert touch point for PointEvent."); + + // 调用 EventEntity 的唯一构造函数,将滑动相关字段设为 null 或默认值 + EventEntity eventEntity = new EventEntity( + (int) solutionId, // Ensure type compatibility if solutionId is long + wrapper.getType().name(), + order, + pointId, // clickPointId + null, // slideStartPointId (null for point event) + null, // slideEndPointId (null for point event) + 0 // duration (0 for point event) + ); + eventEntityId = eventEntityDao.insertEventEntity(eventEntity); + if (eventEntityId == -1) + throw new RuntimeException("Failed to insert EventEntity for PointEvent."); + + } else if (wrapper.getType() == EventWrapper.EventType.SLIDE && event instanceof SlideEvent) { + SlideEvent slideEvent = (SlideEvent) event; + TouchPoint startTouchPoint = new TouchPoint(slideEvent.getStartPoint().getX(), slideEvent.getStartPoint().getY()); + long startPointId = touchPointDao.insertPoint(startTouchPoint); + if (startPointId == -1) + throw new RuntimeException("Failed to insert start touch point for SlideEvent."); + + TouchPoint endTouchPoint = new TouchPoint(slideEvent.getEndPoint().getX(), slideEvent.getEndPoint().getY()); + long endPointId = touchPointDao.insertPoint(endTouchPoint); + if (endPointId == -1) + throw new RuntimeException("Failed to insert end touch point for SlideEvent."); + + // 调用 EventEntity 的唯一构造函数,将点击相关字段设为 null + EventEntity eventEntity = new EventEntity( + (int) solutionId, + wrapper.getType().name(), + order, + null, + startPointId, + endPointId, + 0 + ); + eventEntityId = eventEntityDao.insertEventEntity(eventEntity); + if (eventEntityId == -1) + throw new RuntimeException("Failed to insert EventEntity for SlideEvent."); + } + } + if (callback != null) { + // Post the result back to the main thread if needed for UI updates + // For simplicity, directly calling callback here; in a real app, use Handler or LiveData + callback.onComplete(solutionId); + } + } catch (Exception e) { + Log.e("EventRepository", "Error inserting solution with events: " + e.getMessage(), e); + // Consider rolling back the transaction if any part fails + if (callback != null) { + callback.onError(e); + } + } + }); + } + + // --- 查询操作 --- + public void getAllSolutions(RepositoryCallback> callback) { + databaseWriteExecutor.execute(() -> { + try { + List solutions = solutionDao.getAllSolutions(); + if (callback != null) callback.onComplete(solutions); + } catch (Exception e) { + Log.e("EventRepository", "Error getting all solutions: " + e.getMessage(), e); + if (callback != null) callback.onError(e); + } + }); + } + + public void getSolutionWithEventsAndPoints(long solutionId, RepositoryCallback callback) { + databaseWriteExecutor.execute(() -> { + try { + SolutionWithEventsAndPoints data = solutionDao.getSolutionWithEventsAndPoints(solutionId); + if (callback != null) callback.onComplete(data); + } catch (Exception e) { + Log.e("EventRepository", "Error getting solution with events and points: " + e.getMessage(), e); + if (callback != null) callback.onError(e); + } + }); + } + + // --- 删除操作 --- + public void deleteSolution(Solution solution, RepositoryCallback callback) { + databaseWriteExecutor.execute(() -> { + try { + int rowsAffected = solutionDao.deleteSolution(solution); + if (callback != null) callback.onComplete(rowsAffected); + } catch (Exception e) { + Log.e("EventRepository", "Error deleting solution: " + e.getMessage(), e); + if (callback != null) callback.onError(e); + } + }); + } + + // 假设的回调接口,可以根据需要自定义 + public interface RepositoryCallback { + void onComplete(T result); + + void onError(Exception e); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/service/AutoClickService.java b/app/src/main/java/com/auto/clicker/autoclicker/service/AutoClickService.java index 409df0e..eb2b247 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/service/AutoClickService.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/service/AutoClickService.java @@ -239,7 +239,7 @@ public class AutoClickService extends AccessibilityService { } private void flashTouchFeedback(int index) { - Intent intent = new Intent("com.auto.autoclicker.FLASH_TOUCH_POINT"); + Intent intent = new Intent("com.auto.clicker.autoclicker.FLASH_TOUCH_POINT"); intent.putExtra("index", index); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } @@ -257,7 +257,7 @@ public class AutoClickService extends AccessibilityService { super.onDestroy(); instance = null; stopClicking(); - Intent intent = new Intent("com.auto.autoclicker.SERVICE_DESTROYED"); + Intent intent = new Intent("com.auto.clicker.autoclicker.SERVICE_DESTROYED"); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); logDebug("无障碍服务已销毁"); } diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/floating/TransparentFloatingActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/floating/TransparentFloatingActivity.java deleted file mode 100644 index 3ea9ef9..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/floating/TransparentFloatingActivity.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.auto.clicker.autoclicker.ui.activity.floating; // 替换为你的实际包名 - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; // 新增 -import android.view.View; -import android.view.WindowManager; // 新增,用于设置 Window 属性 -import android.widget.ImageView; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; // 继续使用 AppCompatActivity -import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import androidx.viewpager2.widget.ViewPager2; // 新增 - -import com.auto.clicker.autoclicker.R; -import com.auto.clicker.autoclicker.ui.adapter.floating.TabPagerAdapter; -import com.auto.clicker.autoclicker.ui.fragment.floating.LoadScriptFragment; -import com.auto.clicker.autoclicker.ui.fragment.floating.SaveScriptFragment; -import com.google.android.material.tabs.TabLayout; // 新增 -import com.google.android.material.tabs.TabLayoutMediator; // 新增 - - -public class TransparentFloatingActivity extends AppCompatActivity { - - private static final String TAG = "TransparentFloatingAct"; - public static final String ACTION_CLOSE_FLOATING_TAB_DIALOG = "com.auto.autoclicker.CLOSE_FLOATING_TAB_DIALOG"; - - // 不再需要 FloatingTabDialogManager 实例,直接在这里处理 UI - private View dialogContentView; // 承载整个弹窗内容的 View - - // 接收关闭指令的广播接收器 - private BroadcastReceiver closeReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (ACTION_CLOSE_FLOATING_TAB_DIALOG.equals(intent.getAction())) { - Log.d(TAG, "收到关闭浮动标签页弹窗的广播"); - finish(); // 关闭自身 Activity - } - } - }; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // 设置 Activity 的窗口属性,使其看起来像一个对话框 - // 注意:这里的设置可能与主题中的部分重复,但可以作为额外的控制 - WindowManager.LayoutParams params = getWindow().getAttributes(); - params.gravity = android.view.Gravity.CENTER; // 确保居中 - params.flags &= ~WindowManager.LayoutParams.FLAG_DIM_BEHIND; // 移除背景变暗(如果主题没有完全实现) - params.width = WindowManager.LayoutParams.WRAP_CONTENT; // 根据内容调整宽度 - params.height = WindowManager.LayoutParams.WRAP_CONTENT; // 根据内容调整高度 - getWindow().setAttributes(params); - getWindow().setBackgroundDrawableResource(android.R.color.transparent); // 确保背景透明 - - // 加载你的弹窗布局 - setContentView(R.layout.floating_tab_dialog); - dialogContentView = findViewById(android.R.id.content); // 获取根视图(如果你的布局没有特定ID) - - Log.d(TAG, "TransparentFloatingActivity onCreate"); - - // 初始化 TabLayout 和 ViewPager2 - TabLayout tabLayout = findViewById(R.id.tab_layout); - ViewPager2 viewPager = findViewById(R.id.view_pager); - - TabPagerAdapter pagerAdapter = new TabPagerAdapter(this); // 传入当前的 Activity 作为 FragmentActivity - pagerAdapter.addFragment(new SaveScriptFragment(), "选项卡一"); - pagerAdapter.addFragment(new LoadScriptFragment(), "选项卡二"); - // 添加更多 Fragment... - - viewPager.setAdapter(pagerAdapter); - - new TabLayoutMediator(tabLayout, viewPager, - (tab, position) -> tab.setText(pagerAdapter.getPageTitle(position)) - ).attach(); - - // 假设你在 floating_tab_dialog.xml 中有一个关闭按钮,ID 为 close_dialog_button - ImageView closeDialogButton = findViewById(R.id.close_dialog_button); - if (closeDialogButton != null) { - closeDialogButton.setOnClickListener(v -> { - // 点击关闭按钮时,直接结束这个 Activity - finish(); - }); - } - - - // 注册广播接收器 - LocalBroadcastManager.getInstance(this).registerReceiver(closeReceiver, - new IntentFilter(ACTION_CLOSE_FLOATING_TAB_DIALOG)); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - Log.d(TAG, "TransparentFloatingActivity onDestroy"); - // 注销广播接收器 - LocalBroadcastManager.getInstance(this).unregisterReceiver(closeReceiver); - } - - // 移除这个方法,因为现在 Activity 自己会关闭 - // public static void sendCloseBroadcast(Context context) { - // Intent intent = new Intent(ACTION_CLOSE_FLOATING_TAB_DIALOG); - // LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - // } -} \ No newline at end of file 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 deleted file mode 100644 index 6a72e17..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/dialog/ViewPagerAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.auto.clicker.autoclicker.ui.adapter.dialog; - -import android.view.View; -import android.view.ViewGroup; - -import androidx.recyclerview.widget.RecyclerView; - -import java.util.List; - -public class ViewPagerAdapter extends RecyclerView.Adapter { - 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/floating/TabPagerAdapter.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/floating/TabPagerAdapter.java deleted file mode 100644 index ca7f354..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/floating/TabPagerAdapter.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.auto.clicker.autoclicker.ui.adapter.floating; - -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.viewpager2.adapter.FragmentStateAdapter; - -import java.util.ArrayList; -import java.util.List; - -public class TabPagerAdapter extends FragmentStateAdapter { - - private final List fragmentList = new ArrayList<>(); - private final List fragmentTitleList = new ArrayList<>(); - - public TabPagerAdapter(@NonNull FragmentActivity fragmentActivity) { // 保持接收 FragmentActivity - super(fragmentActivity); - } - - public void addFragment(Fragment fragment, String title) { - fragmentList.add(fragment); - fragmentTitleList.add(title); - } - - @NonNull - @Override - public Fragment createFragment(int position) { - return fragmentList.get(position); - } - - @Override - public int getItemCount() { - return fragmentList.size(); - } - - public CharSequence getPageTitle(int position) { - return fragmentTitleList.get(position); - } -} 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 index 2caf32c..bf92cd8 100644 --- 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 @@ -3,26 +3,20 @@ package com.auto.clicker.autoclicker.ui.adapter.script; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.auto.clicker.autoclicker.R; -import com.auto.clicker.autoclicker.dialog.LoadScriptView; -import com.auto.clicker.autoclicker.room.ScriptEntity; +import com.auto.clicker.autoclicker.room.entity.Solution; import java.util.List; public class ScriptListAdapter extends RecyclerView.Adapter { - private List scripts; - private LoadScriptView.OnScriptClickListener listener; + private List solutionList; - - public ScriptListAdapter(List scripts, LoadScriptView.OnScriptClickListener listener) { - this.scripts = scripts; - this.listener = listener; + public ScriptListAdapter(List solutionList) { + this.solutionList = solutionList; } @NonNull @@ -40,7 +34,7 @@ public class ScriptListAdapter extends RecyclerView.Adapter { + + private List solutionList; + private OnSolutionClickListener listener; + + // 定义接口用于处理点击事件 + public interface OnSolutionClickListener { + void onSolutionClick(Solution solution); + } + + public SolutionAdapter(List solutionList, OnSolutionClickListener listener) { + this.solutionList = solutionList; + this.listener = listener; + } + + @NonNull + @Override + public SolutionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(android.R.layout.simple_list_item_1, parent, false); // 可以自定义更复杂的item布局 + return new SolutionViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull SolutionViewHolder holder, int position) { + Solution solution = solutionList.get(position); + holder.solutionNameTextView.setText(solution.getSolutionName()); + + holder.itemView.setOnClickListener(v -> { + if (listener != null) { + listener.onSolutionClick(solution); + } + }); + } + + @Override + public int getItemCount() { + return solutionList.size(); + } + + public void setSolutions(List newSolutions) { + this.solutionList.clear(); + if (newSolutions != null) { + this.solutionList.addAll(newSolutions); + } + notifyDataSetChanged(); + } + + static class SolutionViewHolder extends RecyclerView.ViewHolder { + TextView solutionNameTextView; + + SolutionViewHolder(View itemView) { + super(itemView); + // simple_list_item_1 只有一个 TextView + solutionNameTextView = itemView.findViewById(android.R.id.text1); + } + } +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/util/FloatingTabDialogManager.java b/app/src/main/java/com/auto/clicker/autoclicker/util/FloatingTabDialogManager.java index e4256ad..9448247 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/util/FloatingTabDialogManager.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/util/FloatingTabDialogManager.java @@ -3,126 +3,211 @@ package com.auto.clicker.autoclicker.util; import android.content.Context; import android.graphics.PixelFormat; import android.os.Build; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; import android.widget.Button; -import android.widget.FrameLayout; -import android.widget.ImageView; +import android.widget.EditText; import android.widget.LinearLayout; +import android.widget.Toast; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.auto.clicker.autoclicker.R; +import com.auto.clicker.autoclicker.room.entity.Solution; +import com.auto.clicker.autoclicker.room.repository.EventRepository; +import com.auto.clicker.autoclicker.ui.floating.SolutionAdapter; +import com.auto.clicker.autoclicker.view.FloatingViewManager; + +import java.util.ArrayList; +import java.util.List; public class FloatingTabDialogManager { private static final String TAG = "FloatingTabDialogMgr"; - private final Context originalContext; // 保存原始的 Service Context + private final Context context; private final WindowManager windowManager; private LinearLayout floatingDialogView; private WindowManager.LayoutParams floatingDialogParams; - private Button tabOneButton; - private Button tabTwoButton; - private FrameLayout contentFrame; - private View pageOneView; - private View pageTwoView; + private Button saveTab; + private Button loadTab; + private View savePage; + private View loadPage; + + private FloatingViewManager floatingViewManager; + + private EditText inputName; + private Button saveButton; + private Button closeButton; + + private RecyclerView loadRecyclerview; + private SolutionAdapter solutionsAdapter; + private List solutionList = new ArrayList<>(); public FloatingTabDialogManager(Context context) { - this.originalContext = context; // 保存原始 Context + this.context = context; this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); } + public void setFloatingViewManager(FloatingViewManager manager) { + this.floatingViewManager = manager; + } + public void showFloatingTabDialog() { if (floatingDialogView != null) { - removeFloatingTabDialog(); // 如果已经显示,先移除 + removeFloatingTabDialog(); } - // --- 关键修改:使用 ContextThemeWrapper 包裹 Service Context --- - Context themedContext = new ContextThemeWrapper(originalContext, R.style.Theme_AutoClicker); - // 或者如果你想用 Material3 的默认主题,但你的 Theme_AutoClicker 已经继承了 Material3,那用它就行 - // Context themedContext = new ContextThemeWrapper(originalContext, R.style.Theme_Material3_DayNight_NoActionBar); // 也可以直接用这个 + Context themedContext = new ContextThemeWrapper(context, R.style.Theme_AutoClicker); floatingDialogView = (LinearLayout) LayoutInflater.from(themedContext).inflate(R.layout.floating_tab_dialog, null); - // --- 关键修改结束 --- + floatingDialogParams = createFloatingDialogParams(); // 创建悬浮窗参数 - floatingDialogParams = createFloatingDialogParams(); + saveTab = floatingDialogView.findViewById(R.id.tab_one_button); + loadTab = floatingDialogView.findViewById(R.id.tab_two_button); + savePage = floatingDialogView.findViewById(R.id.page_one_view); + loadPage = floatingDialogView.findViewById(R.id.page_two_view); - // 获取视图组件 - tabOneButton = floatingDialogView.findViewById(R.id.tab_one_button); - tabTwoButton = floatingDialogView.findViewById(R.id.tab_two_button); - contentFrame = floatingDialogView.findViewById(R.id.content_frame); - pageOneView = floatingDialogView.findViewById(R.id.page_one_view); - pageTwoView = floatingDialogView.findViewById(R.id.page_two_view); - ImageView closeDialogButton = floatingDialogView.findViewById(R.id.close_dialog_button); + saveTab.setOnClickListener(v -> showPage(0)); + loadTab.setOnClickListener(v -> showPage(1)); - // 设置标签按钮的点击事件 - tabOneButton.setOnClickListener(v -> showPage(0)); - tabTwoButton.setOnClickListener(v -> showPage(1)); + inputName = savePage.findViewById(R.id.script_name_input); + saveButton = savePage.findViewById(R.id.save); + closeButton = savePage.findViewById(R.id.close); - // 设置关闭按钮的点击事件 - if (closeDialogButton != null) { - closeDialogButton.setOnClickListener(v -> { - removeFloatingTabDialog(); // 点击关闭按钮时,移除弹窗 - }); - } + saveButton.setOnClickListener(v -> { + String solutionName = inputName.getText().toString().trim(); - // 默认显示第一个页面 - showPage(0); + if (floatingViewManager != null) { + floatingViewManager.saveCurrentEventsAsSolution(solutionName, new EventRepository.RepositoryCallback() { + @Override + public void onComplete(Long solutionId) { + if (solutionId != -1) { + Toast.makeText(context, "方案 '" + solutionName + "' 保存成功!", Toast.LENGTH_SHORT).show(); + removeFloatingTabDialog(); + } else { + Toast.makeText(context, "保存方案失败。", Toast.LENGTH_SHORT).show(); + } + } + + @Override + public void onError(Exception e) { + Log.e(TAG, "保存方案失败: " + e.getMessage(), e); + Toast.makeText(context, "保存方案失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + } + }); + // ✅ 只在验证通过时才显示 + Toast.makeText(context, "正在保存方案...", Toast.LENGTH_SHORT).show(); + } + }); + + closeButton.setOnClickListener(v -> removeFloatingTabDialog()); + + loadRecyclerview = loadPage.findViewById(R.id.script_list); + loadRecyclerview.setLayoutManager(new LinearLayoutManager(context)); + + solutionsAdapter = new SolutionAdapter(solutionList, solution -> { + if (floatingViewManager != null) { + floatingViewManager.loadSolution(solution.solutionId); + Toast.makeText(context, "正在加载方案: " + solution.getSolutionName(), Toast.LENGTH_SHORT).show(); + } else { + Log.e(TAG, "FloatingViewManager 未设置!无法加载方案。"); + Toast.makeText(context, "加载功能不可用,请联系开发者。", Toast.LENGTH_SHORT).show(); + } + removeFloatingTabDialog(); + }); + loadRecyclerview.setAdapter(solutionsAdapter); + + showPage(0); // 默认显示保存页面 + + loadSolutionsForList(); // 加载方案列表,以便切换到加载页面时可以直接显示 try { windowManager.addView(floatingDialogView, floatingDialogParams); Log.d(TAG, "浮动标签页弹窗已显示"); } catch (WindowManager.BadTokenException e) { Log.e(TAG, "无法添加浮动标签页弹窗,可能是权限问题或上下文无效", e); + Toast.makeText(context, "显示弹窗失败,请检查权限。", Toast.LENGTH_SHORT).show(); } catch (SecurityException e) { Log.e(TAG, "需要悬浮窗权限才能显示浮动弹窗", e); - // 引导用户去开启权限,如果你在 FloatingViewManager 中已经处理了,这里可以省略 - // Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, - // Uri.parse("package:" + context.getPackageName())); - // intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - // context.startActivity(intent); + Toast.makeText(context, "请授予悬浮窗权限以便显示。", Toast.LENGTH_SHORT).show(); } } private void showPage(int pageIndex) { - // 确保所有视图都已正确加载(例如,通过 inflated 后的 findViewById) - if (pageOneView == null || pageTwoView == null) { + if (savePage == null || loadPage == null) { Log.e(TAG, "页面视图未初始化!"); return; } // 隐藏所有页面 - pageOneView.setVisibility(View.GONE); - pageTwoView.setVisibility(View.GONE); + savePage.setVisibility(View.GONE); + loadPage.setVisibility(View.GONE); - // 重置所有标签按钮的颜色/背景(如果需要视觉反馈) - // 注意:这里使用 getContext().getResources().getColor() 是在 themedContext 的上下文中获取颜色 - // 如果你的颜色定义在 themes.xml 或 colors.xml 中,并且依赖于主题属性,那么需要确保主题是正确的 - tabOneButton.setTextColor(originalContext.getResources().getColor(android.R.color.darker_gray)); // 用 originalContext 或 themedContext - tabTwoButton.setTextColor(originalContext.getResources().getColor(android.R.color.darker_gray)); // 最好用 themedContext + // 重置Tab按钮颜色 + saveTab.setTextColor(context.getResources().getColor(android.R.color.darker_gray)); + loadTab.setTextColor(context.getResources().getColor(android.R.color.darker_gray)); - // 显示选定页面并更新标签样式 switch (pageIndex) { - case 0: - pageOneView.setVisibility(View.VISIBLE); - tabOneButton.setTextColor(originalContext.getResources().getColor(android.R.color.black)); // 选中颜色 + case 0: // 保存页面 + savePage.setVisibility(View.VISIBLE); + saveTab.setTextColor(context.getResources().getColor(android.R.color.black)); + // 在显示保存页面时尝试显示键盘 + showKeyboard(); break; - case 1: - pageTwoView.setVisibility(View.VISIBLE); - tabTwoButton.setTextColor(originalContext.getResources().getColor(android.R.color.black)); // 选中颜色 + case 1: // 加载页面 + loadPage.setVisibility(View.VISIBLE); + loadTab.setTextColor(context.getResources().getColor(android.R.color.black)); + loadSolutionsForList(); break; - // 添加更多页面 } } + private void loadSolutionsForList() { + if (floatingViewManager == null) { + Log.e(TAG, "FloatingViewManager 未设置,无法加载方案列表。"); + // 确保UI操作在主线程 + new Handler(Looper.getMainLooper()).post(() -> + Toast.makeText(context, "加载方案列表功能不可用。", Toast.LENGTH_SHORT).show() + ); + return; + } + floatingViewManager.getAllSolutions(new EventRepository.RepositoryCallback>() { + @Override + public void onComplete(List solutions) { + // 将 UI 更新操作(包括更新适配器和显示Toast)切换到主线程 + new Handler(Looper.getMainLooper()).post(() -> { + solutionsAdapter.setSolutions(solutions); + if (solutions.isEmpty()) { + Toast.makeText(context, "没有可加载的方案。", Toast.LENGTH_SHORT).show(); + } + }); + } + + @Override + public void onError(Exception e) { + // 将 UI 更新操作(显示Toast)切换到主线程 + new Handler(Looper.getMainLooper()).post(() -> { + Log.e(TAG, "加载方案列表失败: " + e.getMessage(), e); + Toast.makeText(context, "加载方案列表失败。", Toast.LENGTH_SHORT).show(); + }); + } + }); + } + public void removeFloatingTabDialog() { if (floatingDialogView != null) { try { windowManager.removeView(floatingDialogView); - floatingDialogView = null; + floatingDialogView = null; // 置空,防止重复移除或空指针异常 Log.d(TAG, "浮动标签页弹窗已移除"); } catch (IllegalArgumentException e) { Log.w(TAG, "尝试移除不存在的视图", e); @@ -131,20 +216,30 @@ public class FloatingTabDialogManager { } private WindowManager.LayoutParams createFloatingDialogParams() { - // 创建浮动弹窗的参数 int overlayType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_PHONE; WindowManager.LayoutParams params = new WindowManager.LayoutParams( - WindowManager.LayoutParams.WRAP_CONTENT, // 宽度自适应内容 - WindowManager.LayoutParams.WRAP_CONTENT, // 高度自适应内容 + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT, overlayType, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | // 如果希望点击弹窗外部能穿透,则保持 - WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, // 确保它能接收触摸事件 + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSLUCENT ); - params.gravity = Gravity.CENTER; // 居中显示 + params.gravity = Gravity.CENTER; return params; } + + private void showKeyboard() { + if (inputName != null) { + inputName.requestFocus(); // 确保输入框获取焦点 + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + // 尝试强制显示键盘 + imm.showSoftInput(inputName, InputMethodManager.SHOW_IMPLICIT); + } + } + } } \ No newline at end of file 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 deleted file mode 100644 index 67c6c4f..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/util/ScriptConverter.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.auto.clicker.autoclicker.util; - -import com.auto.clicker.autoclicker.data.EventWrapper; -import com.auto.clicker.autoclicker.data.PointEvent; -import com.auto.clicker.autoclicker.data.SlideEvent; -import com.auto.clicker.autoclicker.room.PointEventEntity; -import com.auto.clicker.autoclicker.room.SlideEventEntity; - -import java.util.ArrayList; -import java.util.List; - -public class ScriptConverter { - - public static List 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 deleted file mode 100644 index 2d704f9..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/util/ScriptRestorer.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.auto.clicker.autoclicker.util; - -import static com.auto.clicker.autoclicker.view.FloatingViewManager.TOUCH_POINT_SIZE; - -import android.content.Context; -import android.graphics.Color; -import android.graphics.PixelFormat; -import android.os.Build; -import android.view.Gravity; -import android.view.WindowManager; -import android.widget.TextView; - -import com.auto.clicker.autoclicker.R; -import com.auto.clicker.autoclicker.data.EventWrapper; -import com.auto.clicker.autoclicker.data.PointEvent; -import com.auto.clicker.autoclicker.data.SlideEvent; -import com.auto.clicker.autoclicker.room.PointEventEntity; -import com.auto.clicker.autoclicker.room.ScriptWithEvents; -import com.auto.clicker.autoclicker.room.SlideEventEntity; -import com.auto.clicker.autoclicker.view.ConnectingLineView; - -import java.util.ArrayList; -import java.util.List; - -public class ScriptRestorer { - - private final Context context; - private final int screenWidth; - private final int screenHeight; - - public ScriptRestorer(Context context, int screenWidth, int screenHeight) { - this.context = context; - this.screenWidth = screenWidth; - this.screenHeight = screenHeight; - } - - public List 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/FloatingViewManager.java b/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingViewManager.java index 08b0e18..5f4a4d4 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 @@ -36,10 +36,13 @@ import com.auto.clicker.autoclicker.data.EventWrapper; import com.auto.clicker.autoclicker.data.PointEvent; import com.auto.clicker.autoclicker.data.PositionUpdater; import com.auto.clicker.autoclicker.data.SlideEvent; -import com.auto.clicker.autoclicker.dialog.ScriptManagerDialog; +import com.auto.clicker.autoclicker.room.SolutionWithEventsAndPoints; +import com.auto.clicker.autoclicker.room.entity.EventEntity; +import com.auto.clicker.autoclicker.room.entity.Solution; +import com.auto.clicker.autoclicker.room.entity.TouchPoint; +import com.auto.clicker.autoclicker.room.repository.EventRepository; import com.auto.clicker.autoclicker.service.AutoClickService; import com.auto.clicker.autoclicker.service.ForegroundService; -import com.auto.clicker.autoclicker.ui.activity.floating.TransparentFloatingActivity; import com.auto.clicker.autoclicker.util.FloatingTabDialogManager; import com.auto.clicker.autoclicker.util.PrefUtils; import com.auto.clicker.autoclicker.util.ScreenUtils; @@ -71,6 +74,8 @@ public class FloatingViewManager { private FloatingTabDialogManager floatingTabDialogManager; + private EventRepository eventRepository; + public FloatingViewManager(Context context) { this.context = context; this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); @@ -78,6 +83,8 @@ public class FloatingViewManager { this.screenWidth = screenSize.x; this.screenHeight = screenSize.y; this.floatingTabDialogManager = new FloatingTabDialogManager(context); + this.floatingTabDialogManager.setFloatingViewManager(this); + this.eventRepository = new EventRepository(context.getApplicationContext()); registerBroadcastReceiver(); } @@ -266,7 +273,7 @@ public class FloatingViewManager { } // 创建点击点视图 - TextView point = createTouchPointView(String.valueOf(eventOrderCounter)); + TextView pointView = createTouchPointView(String.valueOf(eventOrderCounter)); // 设置位置居中 WindowManager.LayoutParams params = createTouchPointParams(); @@ -274,15 +281,18 @@ public class FloatingViewManager { params.y = screenHeight / 2 - TOUCH_POINT_SIZE / 2; // 包装为事件对象 - PointEvent pointEvent = new PointEvent(eventOrderCounter, params.x, params.y, point, params); + PointEvent pointEvent = new PointEvent(eventOrderCounter, params.x, params.y); + pointEvent.setView(pointView); + pointEvent.setLayoutParams(params); + runtimeEvents.add(new EventWrapper(EventWrapper.EventType.POINT, pointEvent, eventOrderCounter)); eventOrderCounter++; Log.d(TAG, "addPoint runtimeEvents: " + runtimeEvents.size() + " eventOrderCounter :" + eventOrderCounter); // 设置拖动功能并添加到窗口 - setupDraggablePoint(point, params, pointEvent); - windowManager.addView(point, params); + setupDraggablePoint(pointView, params, pointEvent); + windowManager.addView(pointView, params); updateServicePositions(); } @@ -294,37 +304,44 @@ public class FloatingViewManager { } // 创建起点 - TextView startPoint = createTouchPointView(String.valueOf(eventOrderCounter)); + TextView startPointView = createTouchPointView(String.valueOf(eventOrderCounter)); WindowManager.LayoutParams startParams = createTouchPointParams(); startParams.x = screenWidth / 2 - 100; startParams.y = screenHeight / 2 - 50; - PointEvent startPointEvent = new PointEvent(eventOrderCounter, startParams.x, startParams.y, startPoint, startParams); + PointEvent startPointEvent = new PointEvent(eventOrderCounter, startParams.x, startParams.y); + startPointEvent.setView(startPointView); + startPointEvent.setLayoutParams(startParams); // 创建终点 - TextView endPoint = createTouchPointView(String.valueOf(eventOrderCounter)); + TextView endPointView = createTouchPointView(String.valueOf(eventOrderCounter)); WindowManager.LayoutParams endParams = createTouchPointParams(); endParams.x = screenWidth / 2 + 100; endParams.y = screenHeight / 2 - 50; - PointEvent endPointEvent = new PointEvent(eventOrderCounter, endParams.x, endParams.y, endPoint, endParams); + PointEvent endPointEvent = new PointEvent(eventOrderCounter, endParams.x, endParams.y); + startPointEvent.setView(endPointView); + startPointEvent.setLayoutParams(endParams); // 创建滑动连线 ConnectingLineView lineView = new ConnectingLineView(context); WindowManager.LayoutParams lineParams = createLineViewParams(); - SlideEvent slideEvent = new SlideEvent(eventOrderCounter, startPointEvent, endPointEvent, lineView, lineParams); + SlideEvent slideEvent = new SlideEvent(eventOrderCounter, startPointEvent, endPointEvent); + slideEvent.setLineView(lineView); + slideEvent.setLineParams(lineParams); + runtimeEvents.add(new EventWrapper(EventWrapper.EventType.SLIDE, slideEvent, eventOrderCounter)); eventOrderCounter++; Log.d(TAG, "addSlide runtimeEvents: " + runtimeEvents.size() + " eventOrderCounter :" + eventOrderCounter); // 设置滑动点可拖动 - setupDraggableSlidePoint(startPoint, startParams, startPointEvent, slideEvent); - setupDraggableSlidePoint(endPoint, endParams, endPointEvent, slideEvent); + setupDraggableSlidePoint(startPointView, startParams, startPointEvent, slideEvent); + setupDraggableSlidePoint(endPointView, endParams, endPointEvent, slideEvent); // 添加视图到窗口 windowManager.addView(lineView, lineParams); - windowManager.addView(startPoint, startParams); - windowManager.addView(endPoint, endParams); + windowManager.addView(startPointView, startParams); + windowManager.addView(endPointView, endParams); updateLinePosition(slideEvent); // 更新线的位置 updateServicePositions(); @@ -335,9 +352,9 @@ public class FloatingViewManager { // 获取事件的视图 View viewToToggle = null; if (wrapper.getType() == EventWrapper.EventType.POINT) { - viewToToggle = ((PointEvent) wrapper.getEvent()).getView(); + viewToToggle = createTouchPointView(String.valueOf(wrapper.getEvent().getId())); } else if (wrapper.getType() == EventWrapper.EventType.SLIDE) { - View startPointView = getView(show, wrapper); + View startPointView = getView(show, wrapper,context); WindowManager.LayoutParams startParams = (WindowManager.LayoutParams) startPointView.getLayoutParams(); if (show) { @@ -367,12 +384,12 @@ public class FloatingViewManager { } @NonNull - private static View getView(boolean show, EventWrapper wrapper) { + private View getView(boolean show, EventWrapper wrapper,Context context) { SlideEvent slideEvent = (SlideEvent) wrapper.getEvent(); // 假设你只隐藏/显示点,线也需要同步 - View startPointView = slideEvent.getStartPoint().getView(); - View endPointView = slideEvent.getEndPoint().getView(); - View lineView = slideEvent.getLineView(); + View startPointView = createTouchPointView(String.valueOf(slideEvent.getStartPoint().getId())); + View endPointView = createTouchPointView(String.valueOf(slideEvent.getEndPoint().getId())); + View lineView = new ConnectingLineView(context); if (show) { // 如果是显示,可能需要重新添加或设置可见性 @@ -423,7 +440,7 @@ public class FloatingViewManager { PrefUtils.setFloatingShown(context, 0); // 发送浮窗关闭广播 - Intent intent = new Intent("com.auto.autoclicker.FLOATING_WINDOW_STATE_CHANGED"); + Intent intent = new Intent("com.auto.clicker.autoclicker.FLOATING_WINDOW_STATE_CHANGED"); intent.putExtra("isShown", false); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); } @@ -788,13 +805,145 @@ public class FloatingViewManager { new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if ("com.auto.autoclicker.FLASH_TOUCH_POINT".equals(intent.getAction())) { + if ("com.auto.clicker.autoclicker.FLASH_TOUCH_POINT".equals(intent.getAction())) { int index = intent.getIntExtra("index", -1); flashTouchPoint(index); } } }, - new IntentFilter("com.auto.autoclicker.FLASH_TOUCH_POINT") + new IntentFilter("com.auto.clicker.autoclicker.FLASH_TOUCH_POINT") ); } + + public void saveCurrentEventsAsSolution(String solutionName, EventRepository.RepositoryCallback callback) { + if (solutionName == null || solutionName.trim().isEmpty()) { + Toast.makeText(context, "方案名称不能为空", Toast.LENGTH_SHORT).show(); + // 在这里调用回调的 onError,或者 onComplete 返回一个表示失败的值 + // 确保 FloatingTabDialogManager 收到反馈 + if (callback != null) { + callback.onComplete(-1L); // 返回一个表示失败的值 + } + return; + } + if (runtimeEvents.isEmpty()) { + Toast.makeText(context, "当前没有事件可以保存", Toast.LENGTH_SHORT).show(); + if (callback != null) { + callback.onComplete(-1L); // 返回一个表示失败的值 + } + return; + } + + Solution newSolution = new Solution(solutionName); + eventRepository.insertSolutionWithEvents(newSolution, runtimeEvents, new EventRepository.RepositoryCallback() { + @Override + public void onComplete(Long solutionId) { + // 成功保存,Toast 可以在这里显示,也可以在 FloatingTabDialogManager 中显示 + // 为了职责分离,建议在这里显示一个简单的"保存成功" Toast,然后让 FloatingTabDialogManager 决定是否关闭 + // Toast.makeText(context, "方案 '" + solutionName + "' 保存成功!", Toast.LENGTH_SHORT).show(); + // 保存失败,Toast 可以在这里显示 + // Toast.makeText(context, "保存方案失败。", Toast.LENGTH_SHORT).show(); + if (callback != null) { + callback.onComplete(solutionId); // 将结果传递给调用者 + } + } + + @Override + public void onError(Exception e) { + Log.e(TAG, "保存方案失败: " + e.getMessage(), e); + Toast.makeText(context, "保存方案失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + if (callback != null) { + callback.onError(e); // 将错误传递给调用者 + } + } + }); + } + + public void loadSolution(long solutionId) { + eventRepository.getSolutionWithEventsAndPoints(solutionId, new EventRepository.RepositoryCallback() { + @Override + public void onComplete(SolutionWithEventsAndPoints solutionData) { + if (solutionData != null && solutionData.events != null) { + removeFloatingViews(); + runtimeEvents.clear(); + eventOrderCounter = 1; + + for (SolutionWithEventsAndPoints.EventWithPoints eventWithPoints : solutionData.events) { + EventEntity eventEntity = eventWithPoints.eventEntity; + EventWrapper.EventType type = EventWrapper.EventType.valueOf(eventEntity.eventType); + + if (type == EventWrapper.EventType.POINT) { + TouchPoint clickPoint = eventWithPoints.clickTouchPoint; + if (clickPoint != null) { + PointEvent pointEvent = new PointEvent(eventEntity.orderInSolution, clickPoint.x, clickPoint.y); + TextView pointView = createTouchPointView(String.valueOf(pointEvent.getId())); + WindowManager.LayoutParams params = createTouchPointParams(); + params.x = pointEvent.getX(); + params.y = pointEvent.getY(); + pointView.setLayoutParams(params); + pointEvent.setView(pointView); + setupDraggablePoint(pointView, params, pointEvent); + windowManager.addView(pointView, params); + runtimeEvents.add(new EventWrapper(type, pointEvent, pointEvent.getId())); + } + } else if (type == EventWrapper.EventType.SLIDE) { + TouchPoint startPointData = eventWithPoints.slideStartTouchPoint; + TouchPoint endPointData = eventWithPoints.slideEndTouchPoint; + if (startPointData != null && endPointData != null) { + PointEvent startPointEvent = new PointEvent(eventEntity.orderInSolution, startPointData.x, startPointData.y); + PointEvent endPointEvent = new PointEvent(eventEntity.orderInSolution, endPointData.x, endPointData.y); + + TextView startPointView = createTouchPointView(String.valueOf(startPointEvent.getId())); + WindowManager.LayoutParams startParams = createTouchPointParams(); + startParams.x = startPointEvent.getX(); + startParams.y = startPointEvent.getY(); + startPointView.setLayoutParams(startParams); + + TextView endPointView = createTouchPointView(String.valueOf(endPointEvent.getId())); + WindowManager.LayoutParams endParams = createTouchPointParams(); + endParams.x = endPointEvent.getX(); + endParams.y = endPointEvent.getY(); + endPointView.setLayoutParams(endParams); + + ConnectingLineView lineView = new ConnectingLineView(context); + WindowManager.LayoutParams lineParams = createLineViewParams(); + + SlideEvent slideEvent = new SlideEvent(eventEntity.orderInSolution, startPointEvent, endPointEvent); + + setupDraggableSlidePoint(startPointView, startParams, startPointEvent, slideEvent); + setupDraggableSlidePoint(endPointView, endParams, endPointEvent, slideEvent); + + windowManager.addView(lineView, lineParams); + windowManager.addView(startPointView, startParams); + windowManager.addView(endPointView, endParams); + + updateLinePosition(slideEvent); + runtimeEvents.add(new EventWrapper(type, slideEvent, slideEvent.getId())); + } + } + if (eventEntity.orderInSolution >= eventOrderCounter) { + eventOrderCounter = eventEntity.orderInSolution + 1; + } + } + updateServicePositions(); + Toast.makeText(context, "方案 '" + solutionData.solution.solutionName + "' 加载成功!", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(context, "加载方案失败或方案不存在。", Toast.LENGTH_SHORT).show(); + } + } + + @Override + public void onError(Exception e) { + Log.e(TAG, "加载方案失败: " + e.getMessage(), e); + Toast.makeText(context, "加载方案失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + } + }); + } + + public void getAllSolutions(EventRepository.RepositoryCallback> callback) { + if (eventRepository != null) { + eventRepository.getAllSolutions(callback); + } else { + callback.onError(new IllegalStateException("EventRepository not initialized.")); + } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_scripts.xml b/app/src/main/res/layout/activity_scripts.xml index 828f499..0cbfcd9 100644 --- a/app/src/main/res/layout/activity_scripts.xml +++ b/app/src/main/res/layout/activity_scripts.xml @@ -22,7 +22,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" - android:text="@string/scripts" + android:text="@string/solutionList" android:textColor="@color/white" android:textSize="32sp" android:textStyle="bold" diff --git a/app/src/main/res/layout/activity_transparent_floating.xml b/app/src/main/res/layout/activity_transparent_floating.xml deleted file mode 100644 index 1ba554e..0000000 --- a/app/src/main/res/layout/activity_transparent_floating.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/floating_tab_dialog.xml b/app/src/main/res/layout/floating_tab_dialog.xml index 89045e9..ad98c26 100644 --- a/app/src/main/res/layout/floating_tab_dialog.xml +++ b/app/src/main/res/layout/floating_tab_dialog.xml @@ -1,40 +1,38 @@