diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e03c3e2..00db0e7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -18,6 +18,13 @@
android:supportsRtl="true"
android:theme="@style/Theme.AutoClicker"
tools:targetApi="31">
+
@@ -72,11 +79,11 @@
+
-
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/floating/TabPagerAdapter.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/floating/TabPagerAdapter.java
new file mode 100644
index 0000000..ca7f354
--- /dev/null
+++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/floating/TabPagerAdapter.java
@@ -0,0 +1,39 @@
+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/fragment/floating/LoadScriptFragment.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/floating/LoadScriptFragment.java
new file mode 100644
index 0000000..49f1444
--- /dev/null
+++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/floating/LoadScriptFragment.java
@@ -0,0 +1,66 @@
+package com.auto.clicker.autoclicker.ui.fragment.floating;
+
+import android.os.Bundle;
+
+import androidx.fragment.app.Fragment;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.auto.clicker.autoclicker.R;
+
+/**
+ * A simple {@link Fragment} subclass.
+ * Use the {@link LoadScriptFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
+public class LoadScriptFragment extends Fragment {
+
+ // TODO: Rename parameter arguments, choose names that match
+ // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
+ private static final String ARG_PARAM1 = "param1";
+ private static final String ARG_PARAM2 = "param2";
+
+ // TODO: Rename and change types of parameters
+ private String mParam1;
+ private String mParam2;
+
+ public LoadScriptFragment() {
+ // Required empty public constructor
+ }
+
+ /**
+ * Use this factory method to create a new instance of
+ * this fragment using the provided parameters.
+ *
+ * @param param1 Parameter 1.
+ * @param param2 Parameter 2.
+ * @return A new instance of fragment LoadScriptFragment.
+ */
+ // TODO: Rename and change types and number of parameters
+ public static LoadScriptFragment newInstance(String param1, String param2) {
+ LoadScriptFragment fragment = new LoadScriptFragment();
+ Bundle args = new Bundle();
+ args.putString(ARG_PARAM1, param1);
+ args.putString(ARG_PARAM2, param2);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getArguments() != null) {
+ mParam1 = getArguments().getString(ARG_PARAM1);
+ mParam2 = getArguments().getString(ARG_PARAM2);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_load_script, container, false);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/floating/SaveScriptFragment.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/floating/SaveScriptFragment.java
new file mode 100644
index 0000000..a2f1471
--- /dev/null
+++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/floating/SaveScriptFragment.java
@@ -0,0 +1,66 @@
+package com.auto.clicker.autoclicker.ui.fragment.floating;
+
+import android.os.Bundle;
+
+import androidx.fragment.app.Fragment;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.auto.clicker.autoclicker.R;
+
+/**
+ * A simple {@link Fragment} subclass.
+ * Use the {@link SaveScriptFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
+public class SaveScriptFragment extends Fragment {
+
+ // TODO: Rename parameter arguments, choose names that match
+ // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
+ private static final String ARG_PARAM1 = "param1";
+ private static final String ARG_PARAM2 = "param2";
+
+ // TODO: Rename and change types of parameters
+ private String mParam1;
+ private String mParam2;
+
+ public SaveScriptFragment() {
+ // Required empty public constructor
+ }
+
+ /**
+ * Use this factory method to create a new instance of
+ * this fragment using the provided parameters.
+ *
+ * @param param1 Parameter 1.
+ * @param param2 Parameter 2.
+ * @return A new instance of fragment SaveScriptFragment.
+ */
+ // TODO: Rename and change types and number of parameters
+ public static SaveScriptFragment newInstance(String param1, String param2) {
+ SaveScriptFragment fragment = new SaveScriptFragment();
+ Bundle args = new Bundle();
+ args.putString(ARG_PARAM1, param1);
+ args.putString(ARG_PARAM2, param2);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getArguments() != null) {
+ mParam1 = getArguments().getString(ARG_PARAM1);
+ mParam2 = getArguments().getString(ARG_PARAM2);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_save_script, container, false);
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..e4256ad
--- /dev/null
+++ b/app/src/main/java/com/auto/clicker/autoclicker/util/FloatingTabDialogManager.java
@@ -0,0 +1,150 @@
+package com.auto.clicker.autoclicker.util;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.os.Build;
+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.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.auto.clicker.autoclicker.R;
+
+public class FloatingTabDialogManager {
+
+ private static final String TAG = "FloatingTabDialogMgr";
+ private final Context originalContext; // 保存原始的 Service 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;
+
+ public FloatingTabDialogManager(Context context) {
+ this.originalContext = context; // 保存原始 Context
+ this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ }
+
+ public void showFloatingTabDialog() {
+ if (floatingDialogView != null) {
+ 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); // 也可以直接用这个
+
+ floatingDialogView = (LinearLayout) LayoutInflater.from(themedContext).inflate(R.layout.floating_tab_dialog, null);
+ // --- 关键修改结束 ---
+
+ floatingDialogParams = createFloatingDialogParams();
+
+ // 获取视图组件
+ 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);
+
+ // 设置标签按钮的点击事件
+ tabOneButton.setOnClickListener(v -> showPage(0));
+ tabTwoButton.setOnClickListener(v -> showPage(1));
+
+ // 设置关闭按钮的点击事件
+ if (closeDialogButton != null) {
+ closeDialogButton.setOnClickListener(v -> {
+ removeFloatingTabDialog(); // 点击关闭按钮时,移除弹窗
+ });
+ }
+
+ // 默认显示第一个页面
+ showPage(0);
+
+ try {
+ windowManager.addView(floatingDialogView, floatingDialogParams);
+ Log.d(TAG, "浮动标签页弹窗已显示");
+ } catch (WindowManager.BadTokenException e) {
+ Log.e(TAG, "无法添加浮动标签页弹窗,可能是权限问题或上下文无效", e);
+ } 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);
+ }
+ }
+
+ private void showPage(int pageIndex) {
+ // 确保所有视图都已正确加载(例如,通过 inflated 后的 findViewById)
+ if (pageOneView == null || pageTwoView == null) {
+ Log.e(TAG, "页面视图未初始化!");
+ return;
+ }
+
+ // 隐藏所有页面
+ pageOneView.setVisibility(View.GONE);
+ pageTwoView.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
+
+ // 显示选定页面并更新标签样式
+ switch (pageIndex) {
+ case 0:
+ pageOneView.setVisibility(View.VISIBLE);
+ tabOneButton.setTextColor(originalContext.getResources().getColor(android.R.color.black)); // 选中颜色
+ break;
+ case 1:
+ pageTwoView.setVisibility(View.VISIBLE);
+ tabTwoButton.setTextColor(originalContext.getResources().getColor(android.R.color.black)); // 选中颜色
+ break;
+ // 添加更多页面
+ }
+ }
+
+ public void removeFloatingTabDialog() {
+ if (floatingDialogView != null) {
+ try {
+ windowManager.removeView(floatingDialogView);
+ floatingDialogView = null;
+ Log.d(TAG, "浮动标签页弹窗已移除");
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "尝试移除不存在的视图", e);
+ }
+ }
+ }
+
+ 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, // 高度自适应内容
+ overlayType,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | // 如果希望点击弹窗外部能穿透,则保持
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, // 确保它能接收触摸事件
+ PixelFormat.TRANSLUCENT
+ );
+ params.gravity = Gravity.CENTER; // 居中显示
+ return params;
+ }
+}
\ 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 afeab2a..08b0e18 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
@@ -9,6 +9,7 @@ import android.content.IntentFilter;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Point;
+import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.text.InputType;
@@ -38,6 +39,8 @@ 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.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;
import com.auto.clicker.autoclicker.util.ViewUtils;
@@ -66,12 +69,15 @@ public class FloatingViewManager {
private boolean areEventsVisible = true;
+ private FloatingTabDialogManager floatingTabDialogManager;
+
public FloatingViewManager(Context context) {
this.context = context;
this.windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Point screenSize = ScreenUtils.getScreenSize(context);
this.screenWidth = screenSize.x;
this.screenHeight = screenSize.y;
+ this.floatingTabDialogManager = new FloatingTabDialogManager(context);
registerBroadcastReceiver();
}
@@ -199,9 +205,20 @@ public class FloatingViewManager {
addButton.setOnClickListener(v -> addPointEvent());
removeButton.setOnClickListener(v -> removeLastEvent());
slideButton.setOnClickListener(v -> addSlideEvent());
+
saveButton.setOnClickListener(v -> {
- ScriptManagerDialog dialog = new ScriptManagerDialog(context);
- dialog.show(); // 显示弹窗
+ // 直接调用 FloatingTabDialogManager 来显示悬浮弹窗
+ // 记得要检查悬浮窗权限!
+ if (!Settings.canDrawOverlays(context)) {
+ Toast.makeText(context, "需要悬浮窗权限才能显示弹窗", Toast.LENGTH_LONG).show();
+ // 引导用户去开启权限
+ Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
+ Uri.parse("package:" + context.getPackageName()));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ } else {
+ floatingTabDialogManager.showFloatingTabDialog();
+ }
});
eyeButton.setOnClickListener(v -> {
@@ -493,6 +510,10 @@ public class FloatingViewManager {
multipleControlBarView = null;
}
+ if (floatingTabDialogManager != null) {
+ floatingTabDialogManager.removeFloatingTabDialog();
+ }
+
isMultipleRunning = false;
Log.d(TAG, "悬浮窗已移除");
} catch (Exception e) {
diff --git a/app/src/main/res/layout/activity_transparent_floating.xml b/app/src/main/res/layout/activity_transparent_floating.xml
new file mode 100644
index 0000000..1ba554e
--- /dev/null
+++ b/app/src/main/res/layout/activity_transparent_floating.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ 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
new file mode 100644
index 0000000..89045e9
--- /dev/null
+++ b/app/src/main/res/layout/floating_tab_dialog.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_load_script.xml b/app/src/main/res/layout/fragment_load_script.xml
new file mode 100644
index 0000000..2ec6e73
--- /dev/null
+++ b/app/src/main/res/layout/fragment_load_script.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_save_script.xml b/app/src/main/res/layout/fragment_save_script.xml
new file mode 100644
index 0000000..5f42d8a
--- /dev/null
+++ b/app/src/main/res/layout/fragment_save_script.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 5039878..ff47f4e 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -6,5 +6,19 @@
- @drawable/activity_background
+
+
+
\ No newline at end of file