diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9bc6f41..e03c3e2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,11 @@ + + + + + + + + + + @@ -43,6 +63,25 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/data/Event.java b/app/src/main/java/com/auto/clicker/autoclicker/data/Event.java new file mode 100644 index 0000000..2ceb22e --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/data/Event.java @@ -0,0 +1,13 @@ +package com.auto.clicker.autoclicker.data; + +public abstract class Event { + protected int id; + + public Event(int id) { + this.id = id; + } + + public int getId() { + return id; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/data/EventWrapper.java b/app/src/main/java/com/auto/clicker/autoclicker/data/EventWrapper.java new file mode 100644 index 0000000..93ee581 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/data/EventWrapper.java @@ -0,0 +1,40 @@ +package com.auto.clicker.autoclicker.data; + +public class EventWrapper { + public enum EventType { POINT, SLIDE } + + private EventType type; + private Event event; + private int order; // 添加顺序编号(用于UI编号) + + public EventWrapper(EventType type, Event event, int order) { + this.type = type; + this.event = event; + this.order = order; + } + + + public EventType getType() { + return type; + } + + public void setType(EventType type) { + this.type = type; + } + + public Event getEvent() { + return event; + } + + public void setEvent(Event event) { + this.event = event; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } +} 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 new file mode 100644 index 0000000..ade4d60 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/data/PointEvent.java @@ -0,0 +1,52 @@ +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 x; + private int y; + private TextView view; // 悬浮窗上的控件 + private WindowManager.LayoutParams layoutParams; + + public PointEvent(int id, int x, int y, TextView view, WindowManager.LayoutParams layoutParams) { + super(id); + this.x = x; + this.y = y; + this.view = view; + this.layoutParams = layoutParams; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public View getView() { + return view; + } + + public void setView(TextView view) { + this.view = view; + } + + public WindowManager.LayoutParams getLayoutParams() { + return layoutParams; + } + + public void setLayoutParams(WindowManager.LayoutParams layoutParams) { + this.layoutParams = layoutParams; + } +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/data/PositionUpdater.java b/app/src/main/java/com/auto/clicker/autoclicker/data/PositionUpdater.java new file mode 100644 index 0000000..b72be4b --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/data/PositionUpdater.java @@ -0,0 +1,5 @@ +package com.auto.clicker.autoclicker.data; + +public interface PositionUpdater { + void update(float paramX, float paramY, float dx, float dy); +} 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 new file mode 100644 index 0000000..1e9c527 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/data/SlideEvent.java @@ -0,0 +1,63 @@ +package com.auto.clicker.autoclicker.data; + +import android.view.WindowManager; + +import com.auto.clicker.autoclicker.view.ConnectingLineView; + + +public class SlideEvent extends Event { + private PointEvent startPoint; + private PointEvent endPoint; + private ConnectingLineView lineView; + private WindowManager.LayoutParams lineParams; + private long durationMillis; // 滑动时间 + + public SlideEvent(int id, PointEvent startPoint, PointEvent endPoint, + ConnectingLineView lineView, WindowManager.LayoutParams lineParams) { + super(id); + this.startPoint = startPoint; + this.endPoint = endPoint; + this.lineView = lineView; + this.lineParams = lineParams; + } + + public PointEvent getStartPoint() { + return startPoint; + } + + public void setStartPoint(PointEvent startPoint) { + this.startPoint = startPoint; + } + + public PointEvent getEndPoint() { + return endPoint; + } + + public void setEndPoint(PointEvent endPoint) { + this.endPoint = endPoint; + } + + public ConnectingLineView getLineView() { + return lineView; + } + + public void setLineView(ConnectingLineView lineView) { + this.lineView = lineView; + } + + public WindowManager.LayoutParams getLineParams() { + return lineParams; + } + + public void setLineParams(WindowManager.LayoutParams lineParams) { + this.lineParams = lineParams; + } + + public long getDurationMillis() { + return durationMillis; + } + + public void setDurationMillis(long durationMillis) { + this.durationMillis = durationMillis; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/MainActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/MainActivity.java deleted file mode 100644 index 9155218..0000000 --- a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/MainActivity.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.auto.clicker.autoclicker.presentation.ui.activity; - -import android.content.Intent; -import android.os.Bundle; - -import androidx.activity.EdgeToEdge; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.graphics.Insets; -import androidx.core.view.ViewCompat; -import androidx.core.view.WindowInsetsCompat; - -import com.auto.clicker.autoclicker.R; -import com.auto.clicker.autoclicker.databinding.ActivityMainBinding; -import com.auto.clicker.autoclicker.presentation.ui.activity.menu.MenuActivity; -import com.auto.clicker.autoclicker.presentation.ui.activity.welcome.AgreementActivity; -import com.auto.clicker.autoclicker.presentation.ui.activity.welcome.SplashActivity; - -public class MainActivity extends AppCompatActivity { - private ActivityMainBinding binding; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - binding = ActivityMainBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - EdgeToEdge.enable(this); - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { - Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); - v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); - return insets; - }); - - initData(); - initEvent(); - } - - private void initData() { - - } - - private void initEvent() { - binding.menu.setOnClickListener(v -> { - Intent intent = new Intent(this, MenuActivity.class); - startActivity(intent); - }); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - binding = null; - } -} \ No newline at end of file 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 new file mode 100644 index 0000000..f98997f --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/AppDatabase.java @@ -0,0 +1,30 @@ +package com.auto.clicker.autoclicker.room; + +import android.content.Context; + +import androidx.room.Database; +import androidx.room.Room; +import androidx.room.RoomDatabase; + +import com.auto.clicker.autoclicker.MyApplication; + +@Database(entities = {TouchEventEntity.class}, version = MyApplication.DB_VERSION, exportSchema = false) +public abstract class AppDatabase extends RoomDatabase { + + public abstract TouchEventDao touchEventDao(); + + private static volatile AppDatabase INSTANCE; + + public static AppDatabase getInstance(Context context) { + if (INSTANCE == null) { + synchronized (AppDatabase.class) { + if (INSTANCE == null) { + INSTANCE = Room.databaseBuilder(context.getApplicationContext(), + AppDatabase.class, MyApplication.DB_NAME) + .build(); + } + } + } + return INSTANCE; + } +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventDao.java b/app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventDao.java new file mode 100644 index 0000000..c92221c --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventDao.java @@ -0,0 +1,19 @@ +package com.auto.clicker.autoclicker.room; + +import androidx.room.Dao; +import androidx.room.Delete; +import androidx.room.Insert; +import androidx.room.Update; + +@Dao +public interface TouchEventDao { + + @Insert + long insertEvent(TouchEventEntity event); + + @Delete + void deleteEvent(TouchEventEntity event); + + @Update + void updateEvent(TouchEventEntity event); +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventEntity.java b/app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventEntity.java new file mode 100644 index 0000000..e13911f --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/TouchEventEntity.java @@ -0,0 +1,116 @@ +package com.auto.clicker.autoclicker.room; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "touch_event") +public class TouchEventEntity { + + @PrimaryKey(autoGenerate = true) + private int id; + private int eventType; + private int orderIndex; + + private int x; + private int y; + + private Integer endX; + private Integer endY; + + private int clickInterval; + private Integer slideDuration; + + private int displayOrderNumber; + + public TouchEventEntity(int eventType, int orderIndex, int x, int y, Integer endX, Integer endY, int clickInterval, Integer slideDuration, int displayOrderNumber) { + this.eventType = eventType; + this.orderIndex = orderIndex; + this.x = x; + this.y = y; + this.endX = endX; + this.endY = endY; + this.clickInterval = clickInterval; + this.slideDuration = slideDuration; + this.displayOrderNumber = displayOrderNumber; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getEventType() { + return eventType; + } + + public void setEventType(int eventType) { + this.eventType = eventType; + } + + public int getOrderIndex() { + return orderIndex; + } + + public void setOrderIndex(int orderIndex) { + this.orderIndex = orderIndex; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public Integer getEndX() { + return endX; + } + + public void setEndX(Integer endX) { + this.endX = endX; + } + + public Integer getEndY() { + return endY; + } + + public void setEndY(Integer endY) { + this.endY = endY; + } + + public int getClickInterval() { + return clickInterval; + } + + public void setClickInterval(int clickInterval) { + this.clickInterval = clickInterval; + } + + public Integer getSlideDuration() { + return slideDuration; + } + + public void setSlideDuration(Integer slideDuration) { + this.slideDuration = slideDuration; + } + + public int getDisplayOrderNumber() { + return displayOrderNumber; + } + + public void setDisplayOrderNumber(int displayOrderNumber) { + this.displayOrderNumber = displayOrderNumber; + } +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/repository/TouchEventRepository.java b/app/src/main/java/com/auto/clicker/autoclicker/room/repository/TouchEventRepository.java new file mode 100644 index 0000000..03996d1 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/repository/TouchEventRepository.java @@ -0,0 +1,34 @@ +package com.auto.clicker.autoclicker.room.repository; + +import android.app.Application; + +import com.auto.clicker.autoclicker.room.AppDatabase; +import com.auto.clicker.autoclicker.room.TouchEventDao; +import com.auto.clicker.autoclicker.room.TouchEventEntity; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class TouchEventRepository { + private final TouchEventDao dao; + private final ExecutorService executorService; + + public TouchEventRepository(Application application) { + AppDatabase db = AppDatabase.getInstance(application); + this.dao = db.touchEventDao(); + this.executorService = Executors.newSingleThreadExecutor(); + } + + public void insertEvent(TouchEventEntity touchEventEntity){ + executorService.execute(() -> dao.insertEvent(touchEventEntity)); + } + + public void deleteEvent(TouchEventEntity touchEventEntity){ + executorService.execute(() -> dao.deleteEvent(touchEventEntity)); + } + + public void updateEvent(TouchEventEntity touchEventEntity){ + executorService.execute(() -> dao.updateEvent(touchEventEntity)); + } + +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/room/viewmodel/TouchEventViewModel.java b/app/src/main/java/com/auto/clicker/autoclicker/room/viewmodel/TouchEventViewModel.java new file mode 100644 index 0000000..b715061 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/room/viewmodel/TouchEventViewModel.java @@ -0,0 +1,18 @@ +package com.auto.clicker.autoclicker.room.viewmodel; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; + +import com.auto.clicker.autoclicker.room.repository.TouchEventRepository; + +public class TouchEventViewModel extends AndroidViewModel { + private final TouchEventRepository repository; + + public TouchEventViewModel(@NonNull Application application) { + super(application); + repository = new TouchEventRepository(application); + } + +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/service/AutoClickService.java b/app/src/main/java/com/auto/clicker/autoclicker/service/AutoClickService.java new file mode 100644 index 0000000..409df0e --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/service/AutoClickService.java @@ -0,0 +1,264 @@ +package com.auto.clicker.autoclicker.service; + +import android.accessibilityservice.AccessibilityService; +import android.accessibilityservice.GestureDescription; +import android.content.Intent; +import android.graphics.Path; +import android.graphics.Point; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.view.accessibility.AccessibilityEvent; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + + +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.util.ScreenUtils; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class AutoClickService extends AccessibilityService { + private static final String TAG = "AutoClickService"; + private static AutoClickService instance; + + private static final long MIN_CLICK_INTERVAL = 40; + private static final long MAX_CLICK_INTERVAL = 10000; + private static final int MIN_CLICK_DURATION = 50; + private static final int MAX_CLICK_DURATION = 1000; + private static final int MIN_SLIDE_DURATION = 100; + private static final int MAX_SLIDE_DURATION = 2000; + + private List runtimeEvents = new ArrayList<>(); + + private final Handler handler = new Handler(Looper.getMainLooper()); + private boolean isRunning = false; + + private long clickInterval = 1000; + private int clickDuration = 200; + private int slideDuration = 500; + private int currentEventIndex = 0; + + @Override + public void onCreate() { + super.onCreate(); + instance = this; + Point screenSize = ScreenUtils.getScreenSize(this); + int screenWidth = screenSize.x; + int screenHeight = screenSize.y; + logDebug("屏幕尺寸: " + screenWidth + "x" + screenHeight); + } + + public static AutoClickService getInstance() { + return instance; + } + + @Override + public void onServiceConnected() { + super.onServiceConnected(); + logDebug("无障碍服务已连接"); + } + + @Override + public void onInterrupt() { + stopClicking(); + logDebug("无障碍服务中断"); + } + + public void clearEvents() { + runtimeEvents.clear(); + logDebug("清除所有事件"); + } + + public void addMultipleClickEvents(List eventWrapperList) { + clearEvents(); + + runtimeEvents = new ArrayList<>(eventWrapperList); + logDebug("设置多点点击事件: " + eventWrapperList.size() + " 个点"); + } + + public void setClickInterval(long interval) { + if (interval < MIN_CLICK_INTERVAL || interval > MAX_CLICK_INTERVAL) { + Log.w(TAG, "点击间隔超出范围: " + interval + "ms"); + return; + } + this.clickInterval = interval; + logDebug("设置点击间隔: " + interval + "ms"); + } + + public void setClickDuration(int duration) { + if (duration < MIN_CLICK_DURATION || duration > MAX_CLICK_DURATION) { + Log.w(TAG, "点击时长超出范围: " + duration + "ms"); + return; + } + this.clickDuration = duration; + logDebug("设置点击时长: " + duration + "ms"); + } + + public void setSlideDuration(int duration) { + if (duration < MIN_SLIDE_DURATION || duration > MAX_SLIDE_DURATION) { + Log.w(TAG, "滑动时长超出范围: " + duration + "ms"); + return; + } + this.slideDuration = duration; + logDebug("设置滑动时长: " + duration + "ms"); + } + + public void startClicking() { + if (!isRunning) { + if (runtimeEvents.isEmpty()) { + Log.w(TAG, "无事件队列,忽略开始"); + return; + } + + isRunning = true; + currentEventIndex = 0; + logDebug("开始执行事件队列 - 共 " + runtimeEvents.size() + " 个事件"); + executeEventsByType(); + } + } + + public void stopClicking() { + if (isRunning) { + isRunning = false; + handler.removeCallbacksAndMessages(null); + logDebug("停止执行事件"); + } + } + + public boolean isClicking() { + return isRunning; + } + + private void executeEventsByType() { + if (!isRunning || runtimeEvents.isEmpty()) { + logDebug("跳过执行:服务未运行或事件队列为空"); + return; + } + + runtimeEvents.sort(Comparator.comparingInt(EventWrapper::getOrder)); + + if (currentEventIndex >= runtimeEvents.size()) { + currentEventIndex = 0; + } + + EventWrapper wrapper = runtimeEvents.get(currentEventIndex); + Event event = wrapper.getEvent(); + + if (wrapper.getType() == EventWrapper.EventType.POINT && event instanceof PointEvent) { + performSingleClick((PointEvent) event); + } else if (wrapper.getType() == EventWrapper.EventType.SLIDE && event instanceof SlideEvent) { + performSlide((SlideEvent) event); + } else { + logDebug("未知或不匹配的事件类型,跳过执行: " + wrapper.getType()); + currentEventIndex++; + handler.postDelayed(this::executeEventsByType, clickInterval); + } + } + + private void performSingleClick(PointEvent pointEvent) { + int x = pointEvent.getX(); + int y = pointEvent.getY(); + + logDebug("执行点击: (" + x + ", " + y + ")"); + + Path path = new Path(); + path.moveTo(x, y); + GestureDescription.StrokeDescription stroke = + new GestureDescription.StrokeDescription(path, 0, clickDuration); + GestureDescription gesture = + new GestureDescription.Builder().addStroke(stroke).build(); + + dispatchGesture(gesture, new GestureResultCallback() { + @Override + public void onCompleted(GestureDescription gestureDescription) { + logDebug("点击完成"); + if (isRunning) { + int feedbackIndex = currentEventIndex; + currentEventIndex++; + handler.postDelayed(() -> executeEventsByType(), clickInterval); + flashTouchFeedback(feedbackIndex); + } + } + + @Override + public void onCancelled(GestureDescription gestureDescription) { + Log.e(TAG, "点击被取消"); + if (isRunning) { + currentEventIndex++; + handler.postDelayed(() -> executeEventsByType(), clickInterval + 300); + } + } + }, null); + } + + private void performSlide(SlideEvent slideEvent) { + PointEvent start = slideEvent.getStartPoint(); + PointEvent end = slideEvent.getEndPoint(); + int startX = start.getX(); + int startY = start.getY(); + int endX = end.getX(); + int endY = end.getY(); + + logDebug("执行滑动: 从 (" + startX + ", " + startY + ") 到 (" + endX + ", " + endY + ")"); + + Path path = new Path(); + path.moveTo(startX, startY); + path.lineTo(endX, endY); + GestureDescription.StrokeDescription stroke = + new GestureDescription.StrokeDescription(path, 0, slideDuration); + GestureDescription gesture = + new GestureDescription.Builder().addStroke(stroke).build(); + + dispatchGesture(gesture, new GestureResultCallback() { + @Override + public void onCompleted(GestureDescription gestureDescription) { + logDebug("滑动完成"); + if (isRunning) { + int feedbackIndex = currentEventIndex; + currentEventIndex++; + handler.postDelayed(() -> executeEventsByType(), clickInterval); + flashTouchFeedback(feedbackIndex); + } + } + + @Override + public void onCancelled(GestureDescription gestureDescription) { + Log.e(TAG, "滑动被取消"); + if (isRunning) { + currentEventIndex++; + handler.postDelayed(() -> executeEventsByType(), clickInterval + 300); + } + } + }, null); + } + + private void flashTouchFeedback(int index) { + Intent intent = new Intent("com.auto.autoclicker.FLASH_TOUCH_POINT"); + intent.putExtra("index", index); + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + } + + @Override + public void onAccessibilityEvent(AccessibilityEvent event) { + } + + private void logDebug(String message) { + Log.d(TAG, message); + } + + @Override + public void onDestroy() { + super.onDestroy(); + instance = null; + stopClicking(); + Intent intent = new Intent("com.auto.autoclicker.SERVICE_DESTROYED"); + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + logDebug("无障碍服务已销毁"); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/service/ForegroundService.java b/app/src/main/java/com/auto/clicker/autoclicker/service/ForegroundService.java new file mode 100644 index 0000000..b1d88dc --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/service/ForegroundService.java @@ -0,0 +1,101 @@ +package com.auto.clicker.autoclicker.service; + +import static com.auto.clicker.autoclicker.ui.activity.main.MainActivity.FLOATING_SINGLE; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.os.Build; +import android.os.IBinder; +import android.util.Log; + +import androidx.core.app.NotificationCompat; + +import com.auto.clicker.autoclicker.R; +import com.auto.clicker.autoclicker.ui.activity.main.MainActivity; +import com.auto.clicker.autoclicker.view.FloatingViewManager; + +public class ForegroundService extends Service { + private static final String TAG = "ForegroundService"; + private static final String CHANNEL_ID = "AutoClickerChannel"; + private static final int NOTIFICATION_ID = 1; + private FloatingViewManager floatingViewManager; + private boolean isFloatingViewShown = false; + + @Override + public void onCreate() { + super.onCreate(); + createNotificationChannel(); + try { + floatingViewManager = new FloatingViewManager(this); + } catch (Exception e) { + Log.e(TAG, "初始化 FloatingViewManager 失败", e); + stopSelf(); + } + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + int type = intent != null ? intent.getIntExtra("FLOATING_TYPE", FLOATING_SINGLE) : FLOATING_SINGLE; + + Intent notificationIntent = new Intent(this, MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE); + Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) + .setContentTitle("自动点击运行中") + .setContentText("自动点击正在后台运行") + .setSmallIcon(R.drawable.notification) + .setContentIntent(pendingIntent) + .setOngoing(true) + .build(); + + startForeground(NOTIFICATION_ID, notification); + + if (floatingViewManager != null && !isFloatingViewShown) { + try { + floatingViewManager.showFloatingViews(type); + isFloatingViewShown = true; + } catch (SecurityException e) { + Log.e(TAG, "未授予悬浮窗权限", e); + stopSelf(); + } + } else if (floatingViewManager == null) { + stopSelf(); + } + + return START_STICKY; + } + + private void createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + "自动点击服务", + NotificationManager.IMPORTANCE_LOW + ); + channel.setDescription("自动点击前台服务通知渠道"); + NotificationManager manager = getSystemService(NotificationManager.class); + if (manager != null) { + manager.createNotificationChannel(channel); + } + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (floatingViewManager != null) { + floatingViewManager.removeFloatingViews(); + isFloatingViewShown = false; + Log.d(TAG, "悬浮窗已移除"); + } + stopForeground(true); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/AccessibilityActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/main/AccessibilityActivity.java similarity index 93% rename from app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/AccessibilityActivity.java rename to app/src/main/java/com/auto/clicker/autoclicker/ui/activity/main/AccessibilityActivity.java index c690453..4499aa1 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/AccessibilityActivity.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/main/AccessibilityActivity.java @@ -1,4 +1,4 @@ -package com.auto.clicker.autoclicker.presentation.ui.activity; +package com.auto.clicker.autoclicker.ui.activity.main; import android.os.Bundle; diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/main/MainActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/main/MainActivity.java new file mode 100644 index 0000000..7831edf --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/main/MainActivity.java @@ -0,0 +1,390 @@ +package com.auto.clicker.autoclicker.ui.activity.main; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.app.ActivityManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.PowerManager; +import android.provider.Settings; +import android.util.Log; +import android.view.accessibility.AccessibilityManager; +import android.widget.Toast; +import android.widget.VideoView; + +import androidx.activity.EdgeToEdge; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.auto.clicker.autoclicker.R; +import com.auto.clicker.autoclicker.databinding.ActivityMainBinding; +import com.auto.clicker.autoclicker.service.AutoClickService; +import com.auto.clicker.autoclicker.service.ForegroundService; +import com.auto.clicker.autoclicker.ui.activity.menu.MenuActivity; +import com.auto.clicker.autoclicker.ui.activity.setting.SettingActivity; +import com.auto.clicker.autoclicker.util.PrefUtils; + +import java.util.List; + +public class MainActivity extends AppCompatActivity { + private static final String TAG = "MainActivity"; + private static final String ACTION_FLOATING_STATE_CHANGED = "com.auto.clicker.autoclicker.FLOATING_WINDOW_STATE_CHANGED"; + + private ActivityMainBinding binding; + + public static final int FLOATING_NONE = 0; + public static final int FLOATING_SINGLE = 1; + public static final int FLOATING_MULTI = 2; + + private Toast debounceToast; + + private ActivityResultLauncher permissionLauncher; + + private long lastClickTime = 0; + private static final long DEBOUNCE_INTERVAL = 500; + + private int isFloatingShown; + private int selectedFloatingMode = FLOATING_SINGLE; + + private final BroadcastReceiver floatingReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + isFloatingShown = intent.getIntExtra("isShown", FLOATING_NONE); + logDebug("接收到浮窗状态广播: isShown = " + isFloatingShown); + + if (isFloatingShown == FLOATING_NONE) { + } else { + selectedFloatingMode = isFloatingShown; + } + updateSelectionButtons(); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + binding = ActivityMainBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + EdgeToEdge.enable(this); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + + initData(); + initEvent(); + } + + @Override + protected void onResume() { + super.onResume(); + logDebug("onResume: 检查权限和同步服务状态"); + checkPermissions(); + syncServiceState(); + } + + private void initData() { + isFloatingShown = PrefUtils.getFloatingShown(this); + if (isFloatingShown != FLOATING_NONE) { + selectedFloatingMode = isFloatingShown; + } else { + selectedFloatingMode = FLOATING_SINGLE; + } + logDebug("initData: 初始化 isFloatingShown=" + isFloatingShown + ", selectedFloatingMode=" + selectedFloatingMode); + + updateSelectionButtons(); + + LocalBroadcastManager.getInstance(this).registerReceiver( + floatingReceiver, new IntentFilter(ACTION_FLOATING_STATE_CHANGED)); + + permissionLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { + logDebug("权限回调结果: " + result.getResultCode()); + binding.getRoot().postDelayed(() -> { + checkPermissions(); + syncServiceState(); + }, 300); + }); + + setVideo(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.bolangblue)); + } + + private void initEvent() { + binding.menu.setOnClickListener(v -> { + Intent intent = new Intent(this, MenuActivity.class); + startActivity(intent); + }); + + binding.question.setOnClickListener(v -> { + Intent intent = new Intent(this, QuestionActivity.class); + startActivity(intent); + }); + + binding.setting.setOnClickListener(v -> { + Intent intent = new Intent(this, SettingActivity.class); + startActivity(intent); + }); + + binding.animatedView.setOnClickListener(v -> onToggleFloatingWindowClicked()); + + binding.single.setOnClickListener(v -> { + selectedFloatingMode = FLOATING_SINGLE; + updateSelectionButtons(); + + logDebug("选择单点模式. selectedFloatingMode=" + selectedFloatingMode); + }); + + binding.multi.setOnClickListener(v -> { + selectedFloatingMode = FLOATING_MULTI; + updateSelectionButtons(); + + logDebug("选择多点模式. selectedFloatingMode=" + selectedFloatingMode); + }); + } + + private void checkPermissions() { + boolean accessibilityGranted = isAccessibilityServiceEnabled(); + boolean overlayGranted = Settings.canDrawOverlays(this); + boolean batteryOptimizationIgnored = isIgnoringBatteryOptimizations(); + + logDebug("检查权限: 无障碍服务=" + accessibilityGranted + ", 悬浮窗=" + overlayGranted + ", 电池优化=" + batteryOptimizationIgnored); + + if (!accessibilityGranted) { + logDebug("无障碍服务未启用,请求权限"); + showPermissionRequest(Settings.ACTION_ACCESSIBILITY_SETTINGS, "请启用无障碍服务以便应用正常工作", "无法打开无障碍设置"); + setStartButtonEnabled(false); + + return; + } + + if (!overlayGranted) { + logDebug("悬浮窗权限未授予,请求权限"); + Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); + showPermissionRequest(intent, "请授予悬浮窗权限,否则无法显示浮窗", "无法打开悬浮窗设置"); + setStartButtonEnabled(false); + + return; + } + + if (!batteryOptimizationIgnored) { + logDebug("电池优化未忽略,请求权限"); + Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + intent.setData(Uri.parse("package:" + getPackageName())); + showPermissionRequest(intent, "请禁用电池优化,以确保应用在后台稳定运行", "无法打开电池优化设置"); + setStartButtonEnabled(false); + + return; + } + + logDebug("所有权限均已授予"); + setStartButtonEnabled(true); + + } + + private void showPermissionRequest(String action, String toastMessage, String errorMessage) { + try { + Intent intent = new Intent(action); + permissionLauncher.launch(intent); + Toast.makeText(this, toastMessage, Toast.LENGTH_LONG).show(); + logDebug("尝试启动权限设置页面: " + action); + } catch (Exception e) { + Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show(); + logDebug("打开设置页面失败: " + errorMessage + ", 异常: " + e.getMessage()); + } + } + + + private void showPermissionRequest(Intent intent, String toastMessage, String errorMessage) { + try { + permissionLauncher.launch(intent); + Toast.makeText(this, toastMessage, Toast.LENGTH_LONG).show(); + logDebug("尝试启动权限设置页面 (带URI): " + intent.getAction()); + } catch (Exception e) { + Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show(); + logDebug("打开设置页面失败 (带URI): " + errorMessage + ", 异常: " + e.getMessage()); + } + } + + private boolean isAccessibilityServiceEnabled() { + AccessibilityManager am = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE); + if (am == null) return false; + + List enabledServices = + am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK); + + String expectedId = new ComponentName(this, AutoClickService.class).flattenToShortString(); + + for (AccessibilityServiceInfo enabledService : enabledServices) { + if (enabledService.getId().equals(expectedId)) { + return true; + } + } + return false; + } + + private boolean isIgnoringBatteryOptimizations() { + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + boolean ignored = pm != null && pm.isIgnoringBatteryOptimizations(getPackageName()); + logDebug("是否忽略电池优化: " + ignored); + return ignored; + } + + private void syncServiceState() { + int currentFloatingMode = getFloatingMode(); + logDebug("同步服务状态: 当前浮窗模式 = " + currentFloatingMode); + isFloatingShown = currentFloatingMode; + PrefUtils.setFloatingShown(this, isFloatingShown); + + updateSelectionButtons(); + } + + private int getFloatingMode() { + ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + if (manager == null) { + logDebug("ActivityManager 为空"); + return FLOATING_NONE; + } + + List runningServices = manager.getRunningServices(Integer.MAX_VALUE); + for (ActivityManager.RunningServiceInfo service : runningServices) { + if (ForegroundService.class.getName().equals(service.service.getClassName())) { + + int mode = PrefUtils.getFloatingShown(this); + logDebug("前台服务正在运行,PrefUtils 获取浮窗模式: " + mode); + return mode; + } + } + logDebug("前台服务未运行"); + return FLOATING_NONE; + } + + + public void onToggleFloatingWindowClicked() { + long currentTime = System.currentTimeMillis(); + if (currentTime - lastClickTime < DEBOUNCE_INTERVAL) { + showDebounceToast(); + logDebug("点击过于频繁"); + return; + } + lastClickTime = currentTime; + logDebug("点击启动/隐藏浮窗按钮. 当前 isFloatingShown=" + isFloatingShown + ", selectedFloatingMode=" + selectedFloatingMode); + + if (!isAccessibilityServiceEnabled() || !Settings.canDrawOverlays(this) || !isIgnoringBatteryOptimizations()) { + Toast.makeText(this, "请授予所有必要权限", Toast.LENGTH_LONG).show(); + checkPermissions(); + logDebug("权限不足,无法启动浮窗,重新检查权限"); + return; + } + + if (isFloatingShown != FLOATING_NONE && isFloatingShown != selectedFloatingMode) { + Toast.makeText(this, "已显示其他悬浮窗,请先关闭", Toast.LENGTH_SHORT).show(); + logDebug("已显示其他模式的浮窗,请先关闭"); + return; + } + + if (isFloatingShown == selectedFloatingMode) { + + logDebug("隐藏当前模式浮窗: " + selectedFloatingMode); + stopService(new Intent(this, ForegroundService.class)); + + if (AutoClickService.getInstance() != null) { + AutoClickService.getInstance().stopClicking(); + logDebug("已停止 AutoClickService 点击"); + } + isFloatingShown = FLOATING_NONE; + logDebug("悬浮窗已经隐藏"); + } else { + + logDebug("启动浮窗: " + selectedFloatingMode); + Intent serviceIntent = new Intent(this, ForegroundService.class); + serviceIntent.putExtra("FLOATING_TYPE", selectedFloatingMode); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(serviceIntent); + } else { + startService(serviceIntent); + } + + isFloatingShown = selectedFloatingMode; + logDebug("悬浮窗已显示: " + selectedFloatingMode); + } + + PrefUtils.setFloatingShown(this, isFloatingShown); + broadcastFloatingState(isFloatingShown); + updateSelectionButtons(); + + if (isFloatingShown == FLOATING_NONE) { + setVideo(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.bolangblue)); + } else { + setVideo(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.bolangred)); + } + } + + private void broadcastFloatingState(int isShown) { + Intent intent = new Intent(ACTION_FLOATING_STATE_CHANGED); + intent.putExtra("isShown", isShown); + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + logDebug("发送浮窗状态广播: isShown = " + isShown); + } + + private void updateSelectionButtons() { + logDebug("更新选择按钮UI: selectedFloatingMode=" + selectedFloatingMode); + if (selectedFloatingMode == FLOATING_SINGLE) { + binding.single.setSelected(true); + binding.multi.setSelected(false); + } else if (selectedFloatingMode == FLOATING_MULTI) { + binding.single.setSelected(false); + binding.multi.setSelected(true); + } + } + + private void setStartButtonEnabled(boolean enabled) { + logDebug("设置启动按钮启用状态: " + enabled); + } + + private void setVideo(Uri uri) { + binding.animatedVideo.setVideoURI(uri); + binding.animatedVideo.setOnPreparedListener(mp -> { + mp.setLooping(true); + mp.start(); + }); + binding.animatedVideo.start(); + } + + + @Override + protected void onDestroy() { + super.onDestroy(); + binding = null; + LocalBroadcastManager.getInstance(this).unregisterReceiver(floatingReceiver); + if (debounceToast != null) { + debounceToast.cancel(); + } + logDebug("MainActivity onDestroy"); + } + + private void showDebounceToast() { + if (debounceToast == null) { + debounceToast = Toast.makeText(this, "点击太频繁", Toast.LENGTH_SHORT); + } else { + debounceToast.setText("点击太频繁"); + } + debounceToast.show(); + } + + private void logDebug(String message) { + Log.d(TAG, message); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/setting/SettingActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/main/QuestionActivity.java similarity index 80% rename from app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/setting/SettingActivity.java rename to app/src/main/java/com/auto/clicker/autoclicker/ui/activity/main/QuestionActivity.java index a487474..92a63fc 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/setting/SettingActivity.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/main/QuestionActivity.java @@ -1,4 +1,4 @@ -package com.auto.clicker.autoclicker.presentation.ui.activity.setting; +package com.auto.clicker.autoclicker.ui.activity.main; import android.os.Bundle; @@ -10,13 +10,13 @@ import androidx.core.view.WindowInsetsCompat; import com.auto.clicker.autoclicker.R; -public class SettingActivity extends AppCompatActivity { +public class QuestionActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); - setContentView(R.layout.activity_setting); + setContentView(R.layout.activity_question); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/BatteryLimitFreeActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/BatteryLimitFreeActivity.java new file mode 100644 index 0000000..8364da9 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/BatteryLimitFreeActivity.java @@ -0,0 +1,26 @@ +package com.auto.clicker.autoclicker.ui.activity.menu; + +import android.os.Bundle; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.auto.clicker.autoclicker.R; + +public class BatteryLimitFreeActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_battery_limit_free); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/FeedbackActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/FeedbackActivity.java new file mode 100644 index 0000000..cf5f863 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/FeedbackActivity.java @@ -0,0 +1,26 @@ +package com.auto.clicker.autoclicker.ui.activity.menu; + +import android.os.Bundle; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.auto.clicker.autoclicker.R; + +public class FeedbackActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_feedback); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/HowToUseActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/HowToUseActivity.java new file mode 100644 index 0000000..b38480b --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/HowToUseActivity.java @@ -0,0 +1,67 @@ +package com.auto.clicker.autoclicker.ui.activity.menu; + +import android.os.Bundle; +import android.view.LayoutInflater; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.auto.clicker.autoclicker.R; +import com.auto.clicker.autoclicker.databinding.ActivityHowToUseBinding; +import com.auto.clicker.autoclicker.databinding.HowTabBinding; +import com.auto.clicker.autoclicker.ui.adapter.menu.HowAdapter; +import com.google.android.material.tabs.TabLayoutMediator; + +public class HowToUseActivity extends AppCompatActivity { + private ActivityHowToUseBinding binding; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + binding = ActivityHowToUseBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + + initPagerAdapter(); + initTabs(); + + binding.howViewpager.setCurrentItem(0, false); + } + + private void initPagerAdapter() { + HowAdapter adapter = new HowAdapter(this); + binding.howViewpager.setAdapter(adapter); + } + + private void initTabs() { + new TabLayoutMediator(binding.howTablayout, binding.howViewpager, (tab, idx) -> { + com.auto.clicker.autoclicker.databinding.HowTabBinding tabViewBinding = HowTabBinding.inflate(LayoutInflater.from(this)); + tab.setCustomView(tabViewBinding.getRoot()); + decorateTab(tabViewBinding, idx); + }).attach(); + } + + private void decorateTab(HowTabBinding tabBinding, int position) { + String text = ""; + if (position == 0) { + text = "Single-Point"; + } else if (position == 1) { + text = "Multi-Point"; + } + tabBinding.textCustom.setText(text); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + binding = null; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/menu/MenuActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/MenuActivity.java similarity index 76% rename from app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/menu/MenuActivity.java rename to app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/MenuActivity.java index 10028f0..b92294f 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/menu/MenuActivity.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/MenuActivity.java @@ -1,5 +1,6 @@ -package com.auto.clicker.autoclicker.presentation.ui.activity.menu; +package com.auto.clicker.autoclicker.ui.activity.menu; +import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.ImageView; @@ -13,7 +14,6 @@ import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import com.auto.clicker.autoclicker.R; -import com.auto.clicker.autoclicker.databinding.ActivityMainBinding; import com.auto.clicker.autoclicker.databinding.ActivityMenuBinding; public class MenuActivity extends AppCompatActivity { @@ -49,6 +49,26 @@ public class MenuActivity extends AppCompatActivity { private void initEvent() { binding.btnBack.setOnClickListener(v -> finish()); + + binding.scripts.arrow.setOnClickListener(v -> { + Intent intent = new Intent(this, ScriptsActivity.class); + startActivity(intent); + }); + + binding.trouble.arrow.setOnClickListener(v -> { + Intent intent = new Intent(this, TroubleshootingActivity.class); + startActivity(intent); + }); + + binding.battery.arrow.setOnClickListener(v -> { + Intent intent = new Intent(this, BatteryLimitFreeActivity.class); + startActivity(intent); + }); + + binding.question.arrow.setOnClickListener(v -> { + Intent intent = new Intent(this, HowToUseActivity.class); + startActivity(intent); + }); } private void setupItem(int viewId, int iconRes, String title, String subtitle) { diff --git a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/menu/ScriptsActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/ScriptsActivity.java similarity index 92% rename from app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/menu/ScriptsActivity.java rename to app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/ScriptsActivity.java index 9ad3262..17fc777 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/menu/ScriptsActivity.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/ScriptsActivity.java @@ -1,4 +1,4 @@ -package com.auto.clicker.autoclicker.presentation.ui.activity.menu; +package com.auto.clicker.autoclicker.ui.activity.menu; import android.os.Bundle; diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/TroubleshootingActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/TroubleshootingActivity.java new file mode 100644 index 0000000..709e6d4 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/menu/TroubleshootingActivity.java @@ -0,0 +1,25 @@ +package com.auto.clicker.autoclicker.ui.activity.menu; + +import android.os.Bundle; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; +import com.auto.clicker.autoclicker.R; + +public class TroubleshootingActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_troubleshooting); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/setting/SettingActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/setting/SettingActivity.java new file mode 100644 index 0000000..c3e5588 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/setting/SettingActivity.java @@ -0,0 +1,67 @@ +package com.auto.clicker.autoclicker.ui.activity.setting; + +import android.os.Bundle; +import android.view.LayoutInflater; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.auto.clicker.autoclicker.R; +import com.auto.clicker.autoclicker.databinding.ActivitySettingBinding; +import com.auto.clicker.autoclicker.databinding.HowTabBinding; +import com.auto.clicker.autoclicker.ui.adapter.setting.SettingAdapter; +import com.google.android.material.tabs.TabLayoutMediator; + +public class SettingActivity extends AppCompatActivity { + private ActivitySettingBinding binding; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + binding = ActivitySettingBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + + initPagerAdapter(); + initTabs(); + + binding.settingViewpager.setCurrentItem(0, false); + } + + private void initPagerAdapter() { + SettingAdapter adapter = new SettingAdapter(this); + binding.settingViewpager.setAdapter(adapter); + } + + private void initTabs() { + new TabLayoutMediator(binding.settingTablayout, binding.settingViewpager, (tab, idx) -> { + com.auto.clicker.autoclicker.databinding.HowTabBinding tabViewBinding = HowTabBinding.inflate(LayoutInflater.from(this)); + tab.setCustomView(tabViewBinding.getRoot()); + decorateTab(tabViewBinding, idx); + }).attach(); + } + + private void decorateTab(HowTabBinding tabBinding, int position) { + String text = ""; + if (position == 0) { + text = "Actions"; + } else if (position == 1) { + text = "UI Size"; + } + tabBinding.textCustom.setText(text); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + binding = null; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/welcome/AgreementActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/welcome/AgreementActivity.java similarity index 93% rename from app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/welcome/AgreementActivity.java rename to app/src/main/java/com/auto/clicker/autoclicker/ui/activity/welcome/AgreementActivity.java index d31757c..bbac569 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/welcome/AgreementActivity.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/welcome/AgreementActivity.java @@ -1,4 +1,4 @@ -package com.auto.clicker.autoclicker.presentation.ui.activity.welcome; +package com.auto.clicker.autoclicker.ui.activity.welcome; import android.content.Intent; import android.os.Bundle; @@ -13,7 +13,7 @@ import androidx.core.view.WindowInsetsCompat; import com.auto.clicker.autoclicker.R; import com.auto.clicker.autoclicker.databinding.ActivityAgreementBinding; -import com.auto.clicker.autoclicker.presentation.ui.activity.MainActivity; +import com.auto.clicker.autoclicker.ui.activity.main.MainActivity; public class AgreementActivity extends AppCompatActivity { private ActivityAgreementBinding binding; diff --git a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/welcome/LauncherActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/welcome/LauncherActivity.java similarity index 68% rename from app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/welcome/LauncherActivity.java rename to app/src/main/java/com/auto/clicker/autoclicker/ui/activity/welcome/LauncherActivity.java index becf6d9..9580dea 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/welcome/LauncherActivity.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/welcome/LauncherActivity.java @@ -1,15 +1,9 @@ -package com.auto.clicker.autoclicker.presentation.ui.activity.welcome; +package com.auto.clicker.autoclicker.ui.activity.welcome; import android.content.Intent; import android.os.Bundle; -import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.graphics.Insets; -import androidx.core.view.ViewCompat; -import androidx.core.view.WindowInsetsCompat; - -import com.auto.clicker.autoclicker.R; public class LauncherActivity extends AppCompatActivity { diff --git a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/welcome/SplashActivity.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/welcome/SplashActivity.java similarity index 94% rename from app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/welcome/SplashActivity.java rename to app/src/main/java/com/auto/clicker/autoclicker/ui/activity/welcome/SplashActivity.java index 6283d51..c674287 100644 --- a/app/src/main/java/com/auto/clicker/autoclicker/presentation/ui/activity/welcome/SplashActivity.java +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/activity/welcome/SplashActivity.java @@ -1,4 +1,4 @@ -package com.auto.clicker.autoclicker.presentation.ui.activity.welcome; +package com.auto.clicker.autoclicker.ui.activity.welcome; import android.content.Intent; import android.os.Bundle; @@ -12,7 +12,7 @@ import androidx.core.view.WindowInsetsCompat; import com.auto.clicker.autoclicker.R; import com.auto.clicker.autoclicker.databinding.ActivitySplashBinding; -import com.auto.clicker.autoclicker.presentation.ui.activity.MainActivity; +import com.auto.clicker.autoclicker.ui.activity.main.MainActivity; import com.bumptech.glide.Glide; import com.bumptech.glide.load.resource.bitmap.RoundedCorners; diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/menu/HowAdapter.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/menu/HowAdapter.java new file mode 100644 index 0000000..35e6822 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/menu/HowAdapter.java @@ -0,0 +1,34 @@ +package com.auto.clicker.autoclicker.ui.adapter.menu; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.viewpager2.adapter.FragmentStateAdapter; + +import com.auto.clicker.autoclicker.ui.fragment.menu.HowMultiFragment; +import com.auto.clicker.autoclicker.ui.fragment.menu.HowSingleFragment; + +import java.util.ArrayList; +import java.util.List; + +public class HowAdapter extends FragmentStateAdapter { + private final List fragmentList; + + public HowAdapter(@NonNull FragmentActivity fragmentActivity) { + super(fragmentActivity); + fragmentList = new ArrayList<>(); + fragmentList.add(new HowSingleFragment()); + fragmentList.add(new HowMultiFragment()); + } + + @NonNull + @Override + public Fragment createFragment(int position) { + return fragmentList.get(position); + } + + @Override + public int getItemCount() { + return fragmentList.size(); + } +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/setting/SettingAdapter.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/setting/SettingAdapter.java new file mode 100644 index 0000000..78a589a --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/adapter/setting/SettingAdapter.java @@ -0,0 +1,34 @@ +package com.auto.clicker.autoclicker.ui.adapter.setting; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.viewpager2.adapter.FragmentStateAdapter; + +import com.auto.clicker.autoclicker.ui.fragment.setting.ActionFragment; +import com.auto.clicker.autoclicker.ui.fragment.setting.UISizeFragment; + +import java.util.ArrayList; +import java.util.List; + +public class SettingAdapter extends FragmentStateAdapter { + private final List fragmentList; + + public SettingAdapter(@NonNull FragmentActivity fragmentActivity) { + super(fragmentActivity); + fragmentList = new ArrayList<>(); + fragmentList.add(new ActionFragment()); + fragmentList.add(new UISizeFragment()); + } + + @NonNull + @Override + public Fragment createFragment(int position) { + return fragmentList.get(position); + } + + @Override + public int getItemCount() { + return fragmentList.size(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/menu/HowMultiFragment.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/menu/HowMultiFragment.java new file mode 100644 index 0000000..2373479 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/menu/HowMultiFragment.java @@ -0,0 +1,20 @@ +package com.auto.clicker.autoclicker.ui.fragment.menu; + +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; + +public class HowMultiFragment extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_how_multi, container, false); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/menu/HowSingleFragment.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/menu/HowSingleFragment.java new file mode 100644 index 0000000..731abee --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/menu/HowSingleFragment.java @@ -0,0 +1,20 @@ +package com.auto.clicker.autoclicker.ui.fragment.menu; + +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; + +public class HowSingleFragment extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_how_single, container, false); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/setting/ActionFragment.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/setting/ActionFragment.java new file mode 100644 index 0000000..e9e7348 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/setting/ActionFragment.java @@ -0,0 +1,20 @@ +package com.auto.clicker.autoclicker.ui.fragment.setting; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.fragment.app.Fragment; + +import com.auto.clicker.autoclicker.R; + +public class ActionFragment extends Fragment { + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_action, container, false); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/setting/UISizeFragment.java b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/setting/UISizeFragment.java new file mode 100644 index 0000000..bafd485 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/ui/fragment/setting/UISizeFragment.java @@ -0,0 +1,20 @@ +package com.auto.clicker.autoclicker.ui.fragment.setting; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.fragment.app.Fragment; + +import com.auto.clicker.autoclicker.R; + +public class UISizeFragment extends Fragment { + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_u_i_size, container, false); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/util/PrefUtils.java b/app/src/main/java/com/auto/clicker/autoclicker/util/PrefUtils.java new file mode 100644 index 0000000..c9a87c7 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/util/PrefUtils.java @@ -0,0 +1,18 @@ +package com.auto.clicker.autoclicker.util; + +import android.content.Context; + +public class PrefUtils { + private static final String PREF_NAME = "app_prefs"; + private static final String KEY_FLOATING_SHOWN = "floating_window_shown"; + + public static void setFloatingShown(Context context, int shown) { + context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .edit().putInt(KEY_FLOATING_SHOWN, shown).apply(); + } + + public static int getFloatingShown(Context context) { + return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .getInt(KEY_FLOATING_SHOWN, 0); + } +} diff --git a/app/src/main/java/com/auto/clicker/autoclicker/util/ScreenUtils.java b/app/src/main/java/com/auto/clicker/autoclicker/util/ScreenUtils.java new file mode 100644 index 0000000..13ec4e8 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/util/ScreenUtils.java @@ -0,0 +1,14 @@ +package com.auto.clicker.autoclicker.util; + +import android.content.Context; +import android.graphics.Point; +import android.util.DisplayMetrics; + + +public class ScreenUtils { + + public static Point getScreenSize(Context context) { + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + return new Point(metrics.widthPixels, metrics.heightPixels); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/util/ViewUtils.java b/app/src/main/java/com/auto/clicker/autoclicker/util/ViewUtils.java new file mode 100644 index 0000000..0713a52 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/util/ViewUtils.java @@ -0,0 +1,13 @@ +package com.auto.clicker.autoclicker.util; + +import android.graphics.Point; + + +public class ViewUtils { + + public static Point constrainToScreen(float x, float y, int maxWidth, int maxHeight) { + int constrainedX = (int) Math.max(0, Math.min(x, maxWidth)); + int constrainedY = (int) Math.max(0, Math.min(y, maxHeight)); + return new Point(constrainedX, constrainedY); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auto/clicker/autoclicker/view/ConnectingLineView.java b/app/src/main/java/com/auto/clicker/autoclicker/view/ConnectingLineView.java new file mode 100644 index 0000000..02e85ee --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/view/ConnectingLineView.java @@ -0,0 +1,38 @@ +package com.auto.clicker.autoclicker.view; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.view.View; + +import androidx.annotation.NonNull; + +public class ConnectingLineView extends View { + private float startX, startY, endX, endY; + private final Paint paint; + + public ConnectingLineView(Context context) { + super(context); + paint = new Paint(); + paint.setColor(Color.RED); + paint.setAlpha(180); + paint.setStrokeWidth(5); + paint.setAntiAlias(true); + paint.setStyle(Paint.Style.STROKE); + } + + @Override + protected void onDraw(@NonNull Canvas canvas) { + super.onDraw(canvas); + canvas.drawLine(startX, startY, endX, endY, paint); + } + + public void setPoints(float startX, float startY, float endX, float endY) { + this.startX = startX; + this.startY = startY; + this.endX = endX; + this.endY = endY; + invalidate(); + } +} \ 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 new file mode 100644 index 0000000..de6b4e1 --- /dev/null +++ b/app/src/main/java/com/auto/clicker/autoclicker/view/FloatingViewManager.java @@ -0,0 +1,688 @@ +package com.auto.clicker.autoclicker.view; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.os.Build; +import android.provider.Settings; +import android.text.InputType; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.AlertDialog; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.auto.clicker.autoclicker.R; +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.PositionUpdater; +import com.auto.clicker.autoclicker.data.SlideEvent; +import com.auto.clicker.autoclicker.service.AutoClickService; +import com.auto.clicker.autoclicker.service.ForegroundService; +import com.auto.clicker.autoclicker.util.PrefUtils; +import com.auto.clicker.autoclicker.util.ScreenUtils; +import com.auto.clicker.autoclicker.util.ViewUtils; + +import java.util.ArrayList; +import java.util.List; + +public class FloatingViewManager { + private static final String TAG = "FloatingViewManager"; + private static final long DEBOUNCE_INTERVAL = 500; + private static final int TOUCH_POINT_SIZE = 100; + + private final Context context; + private final WindowManager windowManager; + private final int screenWidth; + private final int screenHeight; + + private final List runtimeEvents = new ArrayList<>(); + private int eventOrderCounter = 1; + + private LinearLayout multipleControlBarView; + private WindowManager.LayoutParams multipleControlBarParams; + + private boolean isMultipleRunning = false; + private long lastToggleTime = 0; + + 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; + registerBroadcastReceiver(); + Log.d(TAG, "Screen size: " + screenWidth + "x" + screenHeight); + } + + public void showFloatingViews(int mode) throws SecurityException { + if (!Settings.canDrawOverlays(context)) { + throw new SecurityException("需要悬浮窗许可"); + } + + removeFloatingViews(); + showMultipleModeViews(mode); + + Log.d(TAG, "添加悬浮窗, 模式 = " + mode); + } + + public void removeFloatingViews() { + try { + for (EventWrapper wrapper : new ArrayList<>(runtimeEvents)) { + if (wrapper.getType() == EventWrapper.EventType.POINT) { + PointEvent pe = (PointEvent) wrapper.getEvent(); + safeRemoveView(pe.getView()); + } + } + + for (EventWrapper wrapper : new ArrayList<>(runtimeEvents)) { + if (wrapper.getType() == EventWrapper.EventType.SLIDE) { + SlideEvent se = (SlideEvent) wrapper.getEvent(); + safeRemoveView(se.getStartPoint().getView()); + safeRemoveView(se.getEndPoint().getView()); + safeRemoveView(se.getLineView()); + } + } + + runtimeEvents.clear(); + + Log.d(TAG, "remove runtimeEvents: " + runtimeEvents.size() + " eventOrderCounter :" + eventOrderCounter); + + if (multipleControlBarView != null) { + safeRemoveView(multipleControlBarView); + multipleControlBarView = null; + } + + isMultipleRunning = false; + Log.d(TAG, "悬浮窗已移除"); + } catch (Exception e) { + Log.e(TAG, "移除悬浮窗失败", e); + isMultipleRunning = false; + } + } + + private void safeRemoveView(View view) { + try { + windowManager.removeView(view); + } catch (Exception e) { + Log.w(TAG, "移除视图失败: " + view, e); + } + } + + private void closeFloatingViews() { + AutoClickService service = AutoClickService.getInstance(); + if (service != null) { + service.stopClicking(); + } else { + Log.d(TAG, "自动点击服务没有初始化"); + Toast.makeText(context, "请同意无障碍服务权限", Toast.LENGTH_SHORT).show(); + } + + updateTouchPointsBackground(R.drawable.un_touch_point); + removeFloatingViews(); + + context.stopService(new Intent(context, ForegroundService.class)); + PrefUtils.setFloatingShown(context, 0); + + Intent intent = new Intent("com.auto.autoclicker.FLOATING_WINDOW_STATE_CHANGED"); + intent.putExtra("isShown", false); + LocalBroadcastManager.getInstance(context).sendBroadcast(intent); + } + + private void showMultipleModeViews(int mode) { + initMultipleTouchPointView(); + initMultipleControlBar(); + + if (multipleControlBarView != null) { + windowManager.addView(multipleControlBarView, multipleControlBarParams); + } + } + + private void initMultipleTouchPointView() { + runtimeEvents.clear(); + eventOrderCounter = 1; + + Log.d(TAG, "init runtimeEvents: " + runtimeEvents.size() + " eventOrderCounter :" + eventOrderCounter); + } + + @SuppressLint("ClickableViewAccessibility") + private void initMultipleControlBar() { + multipleControlBarView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.multiple_control_bar, null); + multipleControlBarParams = createControlBarParams(); + + setupDraggableView(multipleControlBarView, multipleControlBarParams, this::updateMoreControlBarPosition); + + setupControlButtons(multipleControlBarView); + } + + private void setupControlButtons(LinearLayout controlBar) { + ImageView playButton = controlBar.findViewById(R.id.play_button); + ImageView addButton = controlBar.findViewById(R.id.add_button); + ImageView removeButton = controlBar.findViewById(R.id.remove_button); + ImageView slideButton = controlBar.findViewById(R.id.slide_button); + ImageView closeButton = controlBar.findViewById(R.id.close_button); + ImageView settingButton = controlBar.findViewById(R.id.settings_button); + + playButton.setOnClickListener(v -> toggleMultipleClicking()); + addButton.setOnClickListener(v -> addPointEvent()); + removeButton.setOnClickListener(v -> removeLastEvent()); + slideButton.setOnClickListener(v -> addSlideEvent()); + closeButton.setOnClickListener(v -> closeFloatingViews()); + settingButton.setOnClickListener(v -> showInputDialog()); + } + + private TextView createTouchPointView(String label) { + TextView touchPointView = new TextView(context); + touchPointView.setBackgroundResource(R.drawable.un_touch_point); + touchPointView.setGravity(Gravity.CENTER); + touchPointView.setText(label); + touchPointView.setTextColor(Color.BLACK); + touchPointView.setTextSize(16); + 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 createControlBarParams() { + 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.TOP | Gravity.START; + params.x = 0; + params.y = 200; + 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; + } + + @SuppressLint("ClickableViewAccessibility") + private void addPointEvent() { + if (isMultipleRunning) { + Toast.makeText(context, "请停止点击", Toast.LENGTH_SHORT).show(); + return; + } + + TextView point = createTouchPointView(String.valueOf(eventOrderCounter)); + + WindowManager.LayoutParams params = createTouchPointParams(); + params.x = screenWidth / 2 - TOUCH_POINT_SIZE / 2; + params.y = screenHeight / 2 - TOUCH_POINT_SIZE / 2; + + PointEvent pointEvent = new PointEvent(eventOrderCounter, params.x, params.y, point, 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); + updateServicePositions(); + } + + @SuppressLint("ClickableViewAccessibility") + private void addSlideEvent() { + if (isMultipleRunning) { + Toast.makeText(context, "请先停止点击", Toast.LENGTH_SHORT).show(); + return; + } + + TextView startPoint = 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); + + TextView endPoint = 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); + + ConnectingLineView lineView = new ConnectingLineView(context); + WindowManager.LayoutParams lineParams = createLineViewParams(); + + SlideEvent slideEvent = new SlideEvent(eventOrderCounter, startPointEvent, endPointEvent, lineView, 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); + + windowManager.addView(lineView, lineParams); + windowManager.addView(startPoint, startParams); + windowManager.addView(endPoint, endParams); + + updateLinePosition(slideEvent); + updateServicePositions(); + } + + private void removeLastEvent() { + if (isMultipleRunning) { + Toast.makeText(context, "请先停止点击", Toast.LENGTH_SHORT).show(); + return; + } + + if (runtimeEvents.isEmpty()) { + Toast.makeText(context, "没有更多的事件需要删除", Toast.LENGTH_SHORT).show(); + return; + } + + Event lastEvent = runtimeEvents.remove(runtimeEvents.size() - 1).getEvent(); + removeEvent(lastEvent); + eventOrderCounter--; + updateServicePositions(); + } + + private void removeEvent(Event event) { + try { + if (event instanceof PointEvent) { + PointEvent pointEvent = (PointEvent) event; + windowManager.removeView(pointEvent.getView()); + } else if (event instanceof SlideEvent) { + SlideEvent slideEvent = (SlideEvent) event; + windowManager.removeView(slideEvent.getStartPoint().getView()); + windowManager.removeView(slideEvent.getEndPoint().getView()); + windowManager.removeView(slideEvent.getLineView()); + } + } catch (Exception e) { + Log.w(TAG, "删除事件失败", e); + } + } + + private void updateServicePositions() { + AutoClickService service = AutoClickService.getInstance(); + if (service != null) { + service.addMultipleClickEvents(runtimeEvents); + } + } + + private void toggleMultipleClicking() { + if (isDebounced()) return; + + AutoClickService service = AutoClickService.getInstance(); + if (service == null) { + handleMissingService(); + return; + } + + if (runtimeEvents.isEmpty()) { + Toast.makeText(context, "请添加一个触摸点或者滑动事件", Toast.LENGTH_SHORT).show(); + return; + } + + if (isMultipleRunning) { + stopMultiClicking(service); + } else { + startMultiClicking(service); + } + + isMultipleRunning = !isMultipleRunning; + Log.d(TAG, "多点点击服务状态: " + service.isClicking()); + } + + private void startMultiClicking(AutoClickService service) { + service.addMultipleClickEvents(runtimeEvents); + service.startClicking(); + + Log.d(TAG, "开始多点点击事件 " + runtimeEvents.size()); + Toast.makeText(context, "多点点击开始", Toast.LENGTH_SHORT).show(); + + updateTouchPointsBackground(R.drawable.touch_point); + } + + private void stopMultiClicking(AutoClickService service) { + service.stopClicking(); + Toast.makeText(context, "多点点击停止", Toast.LENGTH_SHORT).show(); + + updateTouchPointsBackground(R.drawable.un_touch_point); + } + + private void updateTouchPointsBackground(int resourceId) { + for (EventWrapper wrapper : new ArrayList<>(runtimeEvents)) { + if (wrapper.getType() == EventWrapper.EventType.POINT) { + PointEvent pe = (PointEvent) wrapper.getEvent(); + pe.getView().setBackgroundResource(resourceId); + } + } + + for (EventWrapper wrapper : new ArrayList<>(runtimeEvents)) { + if (wrapper.getType() == EventWrapper.EventType.SLIDE) { + SlideEvent se = (SlideEvent) wrapper.getEvent(); + se.getStartPoint().getView().setBackgroundResource(resourceId); + se.getEndPoint().getView().setBackgroundResource(resourceId); + } + } + } + + private void handleMissingService() { + Log.e(TAG, "自动点击服务没有初始化"); + Toast.makeText(context, "请在设置里同意无障碍服务", Toast.LENGTH_LONG).show(); + Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + + private boolean isDebounced() { + long currentTime = System.currentTimeMillis(); + if (currentTime - lastToggleTime < DEBOUNCE_INTERVAL) { + return true; + } + lastToggleTime = currentTime; + return false; + } + + @SuppressLint("ClickableViewAccessibility") + private void setupDraggableView(View view, WindowManager.LayoutParams params, PositionUpdater positionUpdater) { + view.setOnTouchListener(new View.OnTouchListener() { + private float lastX, lastY; + private float paramX, paramY; + private boolean isDragging = false; + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (isMultipleRunning) { + if (event.getAction() == MotionEvent.ACTION_UP) { + v.performClick(); + return true; + } + return false; + } + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + lastX = event.getRawX(); + lastY = event.getRawY(); + paramX = params.x; + paramY = params.y; + isDragging = true; + return true; + + case MotionEvent.ACTION_MOVE: + if (isDragging) { + float dx = event.getRawX() - lastX; + float dy = event.getRawY() - lastY; + positionUpdater.update(paramX, paramY, dx, dy); + return true; + } + break; + + case MotionEvent.ACTION_UP: + isDragging = false; + v.performClick(); + return true; + } + return false; + } + }); + } + + @SuppressLint("ClickableViewAccessibility") + private void setupDraggablePoint(TextView point, WindowManager.LayoutParams params, PointEvent pointEvent) { + point.setOnTouchListener(new View.OnTouchListener() { + private float lastX, lastY; + private float paramX, paramY; + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (isMultipleRunning) { + if (event.getAction() == MotionEvent.ACTION_UP) { + v.performClick(); + return true; + } + return false; + } + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + lastX = event.getRawX(); + lastY = event.getRawY(); + paramX = params.x; + paramY = params.y; + return true; + case MotionEvent.ACTION_MOVE: + float dx = event.getRawX() - lastX; + float dy = event.getRawY() - lastY; + Point constrainedPoint = ViewUtils.constrainToScreen( + paramX + dx, paramY + dy, + screenWidth - point.getWidth(), screenHeight - point.getHeight()); + params.x = constrainedPoint.x; + params.y = constrainedPoint.y; + pointEvent.setX(params.x); + pointEvent.setY(params.y); + windowManager.updateViewLayout(point, params); + updateServicePositions(); + return true; + case MotionEvent.ACTION_UP: + v.performClick(); + return true; + } + return false; + } + }); + } + + @SuppressLint("ClickableViewAccessibility") + private void setupDraggableSlidePoint(TextView point, WindowManager.LayoutParams params, + PointEvent pointEvent, SlideEvent slideEvent) { + point.setOnTouchListener(new View.OnTouchListener() { + private float lastX, lastY; + private float paramX, paramY; + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (isMultipleRunning) { + if (event.getAction() == MotionEvent.ACTION_UP) { + v.performClick(); + return true; + } + return false; + } + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + lastX = event.getRawX(); + lastY = event.getRawY(); + paramX = params.x; + paramY = params.y; + return true; + case MotionEvent.ACTION_MOVE: + float dx = event.getRawX() - lastX; + float dy = event.getRawY() - lastY; + Point constrainedPoint = ViewUtils.constrainToScreen( + paramX + dx, paramY + dy, + screenWidth - point.getWidth(), screenHeight - point.getHeight()); + params.x = constrainedPoint.x; + params.y = constrainedPoint.y; + pointEvent.setX(params.x); + pointEvent.setY(params.y); + windowManager.updateViewLayout(point, params); + + updateLinePosition(slideEvent); + updateServicePositions(); + return true; + case MotionEvent.ACTION_UP: + v.performClick(); + return true; + } + return false; + } + }); + } + + private void updateMoreControlBarPosition(float paramX, float paramY, float dx, float dy) { + Point constrainedPoint = ViewUtils.constrainToScreen( + paramX + dx, paramY + dy, + screenWidth - multipleControlBarView.getWidth(), + screenHeight - multipleControlBarView.getHeight()); + multipleControlBarParams.x = constrainedPoint.x; + multipleControlBarParams.y = constrainedPoint.y; + windowManager.updateViewLayout(multipleControlBarView, multipleControlBarParams); + } + + private void updateLinePosition(SlideEvent slideEvent) { + PointEvent start = slideEvent.getStartPoint(); + PointEvent end = slideEvent.getEndPoint(); + ConnectingLineView lineView = slideEvent.getLineView(); + + float startCenterX = start.getX() + (float) TOUCH_POINT_SIZE / 2; + float startCenterY = start.getY() + (float) TOUCH_POINT_SIZE / 2; + float endCenterX = end.getX() + (float) TOUCH_POINT_SIZE / 2; + float endCenterY = end.getY() + (float) TOUCH_POINT_SIZE / 2; + + lineView.setPoints(startCenterX, startCenterY, endCenterX, endCenterY); + } + + public void showInputDialog() { + EditText input = new EditText(context); + input.setInputType(InputType.TYPE_CLASS_NUMBER); + input.setHint("输入点击间隔(ms)"); + input.setFocusableInTouchMode(true); + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle("设置点击间隔") + .setView(input) + .setPositiveButton("确定", (dialog, which) -> { + String value = input.getText().toString(); + try { + long interval = Long.parseLong(value); + AutoClickService service = AutoClickService.getInstance(); + if (service != null) { + service.setClickInterval(interval); + } else { + Log.w("InputDialog", "AutoClickService 未初始化"); + } + } catch (NumberFormatException e) { + Toast.makeText(context, "无效输入", Toast.LENGTH_SHORT).show(); + } + dialog.dismiss(); + }) + .setNegativeButton("取消", (dialog, which) -> dialog.dismiss()); + + AlertDialog dialog = builder.create(); + + if (!(context instanceof Activity)) { + if (dialog.getWindow() != null) { + dialog.getWindow().setType( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : + WindowManager.LayoutParams.TYPE_PHONE + ); + } + } + + dialog.show(); + + input.requestFocus(); + input.post(() -> { + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } + }); + } + + public void flashTouchPoint(int index) { + if (index >= 0 && index < runtimeEvents.size()) { + Event event = runtimeEvents.get(index).getEvent(); + Log.d(TAG, "index : " + index); + + if (event instanceof PointEvent) { + flashView(((PointEvent) event).getView()); + } else if (event instanceof SlideEvent) { + SlideEvent slide = (SlideEvent) event; + flashView(slide.getStartPoint().getView()); + flashView(slide.getEndPoint().getView()); + } + } + } + + private void flashView(View view) { + if (view != null) { + view.animate() + .alpha(0.3f) + .setDuration(100) + .withEndAction(() -> view.animate() + .alpha(1.0f) + .setDuration(100) + .start()) + .start(); + } + } + + private void registerBroadcastReceiver() { + LocalBroadcastManager.getInstance(context).registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if ("com.auto.autoclicker.FLASH_TOUCH_POINT".equals(intent.getAction())) { + int index = intent.getIntExtra("index", -1); + flashTouchPoint(index); + } + } + }, + new IntentFilter("com.auto.autoclicker.FLASH_TOUCH_POINT") + ); + } +} \ No newline at end of file diff --git a/app/src/main/res/color/switch_thumb_selector.xml b/app/src/main/res/color/switch_thumb_selector.xml new file mode 100644 index 0000000..34a52b2 --- /dev/null +++ b/app/src/main/res/color/switch_thumb_selector.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/color/switch_track_selector.xml b/app/src/main/res/color/switch_track_selector.xml new file mode 100644 index 0000000..a168593 --- /dev/null +++ b/app/src/main/res/color/switch_track_selector.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/color/tab_text_color_selector.xml b/app/src/main/res/color/tab_text_color_selector.xml new file mode 100644 index 0000000..6c0bb9c --- /dev/null +++ b/app/src/main/res/color/tab_text_color_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/add.xml b/app/src/main/res/drawable/add.xml new file mode 100644 index 0000000..476631d --- /dev/null +++ b/app/src/main/res/drawable/add.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/bg_rounded_rect.xml b/app/src/main/res/drawable/bg_rounded_rect.xml new file mode 100644 index 0000000..1105890 --- /dev/null +++ b/app/src/main/res/drawable/bg_rounded_rect.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/blue_arrow_down.xml b/app/src/main/res/drawable/blue_arrow_down.xml new file mode 100644 index 0000000..737a25e --- /dev/null +++ b/app/src/main/res/drawable/blue_arrow_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/blue_ring.xml b/app/src/main/res/drawable/blue_ring.xml new file mode 100644 index 0000000..41f4468 --- /dev/null +++ b/app/src/main/res/drawable/blue_ring.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/blue_ring_bg.xml b/app/src/main/res/drawable/blue_ring_bg.xml new file mode 100644 index 0000000..44d1e75 --- /dev/null +++ b/app/src/main/res/drawable/blue_ring_bg.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/blue_roundend_rectangle.xml b/app/src/main/res/drawable/blue_roundend_rectangle.xml new file mode 100644 index 0000000..89cb6cb --- /dev/null +++ b/app/src/main/res/drawable/blue_roundend_rectangle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/cancel.xml b/app/src/main/res/drawable/cancel.xml new file mode 100644 index 0000000..11dad8e --- /dev/null +++ b/app/src/main/res/drawable/cancel.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/control_bar_bg.xml b/app/src/main/res/drawable/control_bar_bg.xml new file mode 100644 index 0000000..c5dd885 --- /dev/null +++ b/app/src/main/res/drawable/control_bar_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/control_setting.xml b/app/src/main/res/drawable/control_setting.xml new file mode 100644 index 0000000..9ce3d8b --- /dev/null +++ b/app/src/main/res/drawable/control_setting.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/cut_off_line.xml b/app/src/main/res/drawable/cut_off_line.xml new file mode 100644 index 0000000..fafc1c5 --- /dev/null +++ b/app/src/main/res/drawable/cut_off_line.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/cycle.xml b/app/src/main/res/drawable/cycle.xml new file mode 100644 index 0000000..4b60c51 --- /dev/null +++ b/app/src/main/res/drawable/cycle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/dash_line_1.xml b/app/src/main/res/drawable/dash_line_1.xml new file mode 100644 index 0000000..f52e666 --- /dev/null +++ b/app/src/main/res/drawable/dash_line_1.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/detection.xml b/app/src/main/res/drawable/detection.xml new file mode 100644 index 0000000..dd8950b --- /dev/null +++ b/app/src/main/res/drawable/detection.xml @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/eye.xml b/app/src/main/res/drawable/eye.xml new file mode 100644 index 0000000..c2dd283 --- /dev/null +++ b/app/src/main/res/drawable/eye.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/hide_eye.xml b/app/src/main/res/drawable/hide_eye.xml new file mode 100644 index 0000000..efc3eff --- /dev/null +++ b/app/src/main/res/drawable/hide_eye.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/hourglass.xml b/app/src/main/res/drawable/hourglass.xml new file mode 100644 index 0000000..988d8c9 --- /dev/null +++ b/app/src/main/res/drawable/hourglass.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/left_swipe.xml b/app/src/main/res/drawable/left_swipe.xml new file mode 100644 index 0000000..2d5c815 --- /dev/null +++ b/app/src/main/res/drawable/left_swipe.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/line_4.xml b/app/src/main/res/drawable/line_4.xml new file mode 100644 index 0000000..f52e666 --- /dev/null +++ b/app/src/main/res/drawable/line_4.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/notification.xml b/app/src/main/res/drawable/notification.xml new file mode 100644 index 0000000..2ca1304 --- /dev/null +++ b/app/src/main/res/drawable/notification.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/play.xml b/app/src/main/res/drawable/play.xml new file mode 100644 index 0000000..7f4adca --- /dev/null +++ b/app/src/main/res/drawable/play.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/remove.xml b/app/src/main/res/drawable/remove.xml new file mode 100644 index 0000000..3420c18 --- /dev/null +++ b/app/src/main/res/drawable/remove.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ring_has_bg.xml b/app/src/main/res/drawable/ring_has_bg.xml new file mode 100644 index 0000000..7029461 --- /dev/null +++ b/app/src/main/res/drawable/ring_has_bg.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_edittext_background.xml b/app/src/main/res/drawable/rounded_edittext_background.xml new file mode 100644 index 0000000..10356e7 --- /dev/null +++ b/app/src/main/res/drawable/rounded_edittext_background.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_edittext_dotted_background.xml b/app/src/main/res/drawable/rounded_edittext_dotted_background.xml new file mode 100644 index 0000000..24aaf84 --- /dev/null +++ b/app/src/main/res/drawable/rounded_edittext_dotted_background.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/save.xml b/app/src/main/res/drawable/save.xml new file mode 100644 index 0000000..336313b --- /dev/null +++ b/app/src/main/res/drawable/save.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/seek_bar_color_white.xml b/app/src/main/res/drawable/seek_bar_color_white.xml new file mode 100644 index 0000000..a4674c9 --- /dev/null +++ b/app/src/main/res/drawable/seek_bar_color_white.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/seekbar_thumb_white.xml b/app/src/main/res/drawable/seekbar_thumb_white.xml new file mode 100644 index 0000000..749191d --- /dev/null +++ b/app/src/main/res/drawable/seekbar_thumb_white.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/slide.xml b/app/src/main/res/drawable/slide.xml new file mode 100644 index 0000000..2cc21e7 --- /dev/null +++ b/app/src/main/res/drawable/slide.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/stop.xml b/app/src/main/res/drawable/stop.xml new file mode 100644 index 0000000..3e8e1c6 --- /dev/null +++ b/app/src/main/res/drawable/stop.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/touch_point.xml b/app/src/main/res/drawable/touch_point.xml new file mode 100644 index 0000000..53630de --- /dev/null +++ b/app/src/main/res/drawable/touch_point.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/un_touch_point.xml b/app/src/main/res/drawable/un_touch_point.xml new file mode 100644 index 0000000..41f4468 --- /dev/null +++ b/app/src/main/res/drawable/un_touch_point.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/white_arrow_right.xml b/app/src/main/res/drawable/white_arrow_right.xml new file mode 100644 index 0000000..de5d78b --- /dev/null +++ b/app/src/main/res/drawable/white_arrow_right.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_accessibility.xml b/app/src/main/res/layout/activity_accessibility.xml index 245a56c..85a6353 100644 --- a/app/src/main/res/layout/activity_accessibility.xml +++ b/app/src/main/res/layout/activity_accessibility.xml @@ -7,7 +7,7 @@ android:layout_height="match_parent" android:background="@color/black" android:padding="24dp" - tools:context=".presentation.ui.activity.AccessibilityActivity"> + tools:context=".ui.activity.main.AccessibilityActivity"> + tools:context=".ui.activity.welcome.AgreementActivity"> + + + + + + + + + + + + + + + + +