refactor: ###
重构和清理浮动窗口功能 - 重构MainActivity.kt以删除与叠加和浮动窗口功能相关的未使用代码 - 更新deploymentTargetSelector.xml中的时间戳 - 从MainViewModel.kt中删除internalState属性和相关函数 - 更新floating_window.xml中的布局属性
This commit is contained in:
parent
58f13dca0c
commit
5dcda8829d
|
@ -4,7 +4,7 @@
|
||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
<DropdownSelection timestamp="2025-03-24T05:07:47.457354700Z">
|
<DropdownSelection timestamp="2025-03-25T08:46:34.427486100Z">
|
||||||
<Target type="DEFAULT_BOOT">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\xihel\.android\avd\Pixel_6_API_31.avd" />
|
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\xihel\.android\avd\Pixel_6_API_31.avd" />
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
package io.sixminutes.ridicule
|
package io.sixminutes.ridicule
|
||||||
|
|
||||||
import android.app.ActivityManager
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.WindowManager
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
@ -16,7 +12,6 @@ import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import io.sixminutes.ridicule.databinding.ActivityMainBinding
|
import io.sixminutes.ridicule.databinding.ActivityMainBinding
|
||||||
import io.sixminutes.ridicule.databinding.FloatingWindowBinding
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@ -24,22 +19,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
private val viewModel: MainViewModel by viewModels()
|
private val viewModel: MainViewModel by viewModels()
|
||||||
|
|
||||||
// 使用懒加载处理权限请求
|
|
||||||
// private val overlayPermissionLauncher by lazy {
|
|
||||||
// registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
||||||
// if (isActive()) viewModel.checkOverlayPermission(applicationContext)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private val accessibilityLauncher by lazy {
|
|
||||||
// registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
||||||
// if (isActive()) viewModel.checkAccessibilityService()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 跟踪悬浮窗状态
|
|
||||||
private var isFloatingWindowVisible = false
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
@ -66,17 +45,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
handleDeepLink(intent)
|
handleDeepLink(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
// 确保移除悬浮窗视图
|
|
||||||
if (isFloatingWindowVisible) {
|
|
||||||
viewModel.getFloatingView()?.let {
|
|
||||||
windowManager.removeView(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
viewModel.cleanUpResources()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置观察者
|
* 设置观察者
|
||||||
*/
|
*/
|
||||||
|
@ -89,14 +57,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
binding.progressBar.isVisible = isLoading
|
binding.progressBar.isVisible = isLoading
|
||||||
binding.gaidText.text = gaid
|
binding.gaidText.text = gaid
|
||||||
binding.launchButton.isEnabled = isServiceReady
|
binding.launchButton.isEnabled = isServiceReady
|
||||||
|
|
||||||
// 处理悬浮窗显示/隐藏
|
|
||||||
floatingWindowVisible?.let { visible ->
|
|
||||||
when {
|
|
||||||
visible && !isFloatingWindowVisible -> showFloatingWindow()
|
|
||||||
!visible && isFloatingWindowVisible -> hideFloatingWindow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,75 +75,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示悬浮窗
|
|
||||||
*/
|
|
||||||
private fun showFloatingWindow() {
|
|
||||||
try {
|
|
||||||
val windowBinding = FloatingWindowBinding.inflate(LayoutInflater.from(this))
|
|
||||||
val params = WindowManager.LayoutParams().apply {
|
|
||||||
configureWindowParams()
|
|
||||||
windowBinding.root.setOnClickListener {
|
|
||||||
if (isActive()) {
|
|
||||||
hideFloatingWindow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
windowManager.addView(windowBinding.root, params)
|
|
||||||
viewModel.setFloatingView(windowBinding.root)
|
|
||||||
isFloatingWindowVisible = true
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("MainActivity", "Show floating window failed", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 隐藏悬浮窗并回到主窗口
|
|
||||||
*/
|
|
||||||
private fun hideFloatingWindow() {
|
|
||||||
try {
|
|
||||||
viewModel.getFloatingView()?.let {
|
|
||||||
windowManager.removeView(it)
|
|
||||||
isFloatingWindowVisible = false
|
|
||||||
}
|
|
||||||
// 停止后台自动点击服务
|
|
||||||
MyAccessibilityService.getInstance()?.stop()
|
|
||||||
// 强制将 MainActivity 带到前台
|
|
||||||
bringMainActivityToFront()
|
|
||||||
|
|
||||||
// 启动 MainActivity
|
|
||||||
val intent = Intent(this, MainActivity::class.java).apply {
|
|
||||||
flags =
|
|
||||||
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
|
|
||||||
}
|
|
||||||
startActivity(intent)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("MainActivity", "Hide floating window failed", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试将应用的主Activity任务栈带到前台
|
|
||||||
private fun bringMainActivityToFront() {
|
|
||||||
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
|
||||||
val appTasks = activityManager.appTasks
|
|
||||||
var found = false
|
|
||||||
|
|
||||||
for (appTask in appTasks) {
|
|
||||||
val taskInfo = appTask.taskInfo
|
|
||||||
if (taskInfo.topActivity?.packageName == packageName) {
|
|
||||||
appTask.moveToFront()
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
Log.e("MainActivity", "MainActivity found")
|
|
||||||
} else {
|
|
||||||
Log.e("MainActivity", "MainActivity not found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理深度链接
|
* 处理深度链接
|
||||||
* @param intent 意图
|
* @param intent 意图
|
||||||
|
@ -196,22 +87,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 配置窗口参数
|
|
||||||
*/
|
|
||||||
private fun WindowManager.LayoutParams.configureWindowParams() {
|
|
||||||
width = WindowManager.LayoutParams.WRAP_CONTENT
|
|
||||||
height = WindowManager.LayoutParams.WRAP_CONTENT
|
|
||||||
type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
|
|
||||||
|
|
||||||
flags =
|
|
||||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
|
||||||
format = android.graphics.PixelFormat.TRANSLUCENT
|
|
||||||
gravity = android.view.Gravity.BOTTOM or android.view.Gravity.END // 修改为右下角
|
|
||||||
x = 0 // 初始 x 坐标
|
|
||||||
y = 0 // 初始 y 坐标
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查Activity是否活跃
|
* 检查Activity是否活跃
|
||||||
* @return 是否活跃
|
* @return 是否活跃
|
||||||
|
|
|
@ -22,12 +22,6 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
// region 状态管理
|
// region 状态管理
|
||||||
private val _state = MutableStateFlow(MainViewState())
|
private val _state = MutableStateFlow(MainViewState())
|
||||||
val state = _state.asStateFlow()
|
val state = _state.asStateFlow()
|
||||||
|
|
||||||
private data class InternalState(
|
|
||||||
var floatingViewRef: WeakReference<View>? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
private val internalState = InternalState()
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region 依赖项
|
// region 依赖项
|
||||||
|
@ -84,19 +78,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
}.also { intent ->
|
}.also { intent ->
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
updateState { it.copy(floatingWindowVisible = true) }
|
|
||||||
// fun checkOverlayPermission(context: Context) {
|
|
||||||
// val hasPermission = !shouldRequestOverlayPermission(context)
|
|
||||||
// updateState { it.copy(floatingWindowVisible = hasPermission) }
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cleanUpResources() {
|
|
||||||
removeFloatingView()
|
|
||||||
}
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region 内部实现
|
// region 内部实现
|
||||||
|
@ -132,23 +116,6 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
return !Settings.canDrawOverlays(context)
|
return !Settings.canDrawOverlays(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeFloatingView() {
|
|
||||||
internalState.floatingViewRef?.get()?.let { view ->
|
|
||||||
try {
|
|
||||||
windowManager.removeView(view)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("ViewModel", "Remove floating view failed", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internalState.floatingViewRef = null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setFloatingView(view: View) {
|
|
||||||
internalState.floatingViewRef = WeakReference(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFloatingView(): View? = internalState.floatingViewRef?.get()
|
|
||||||
|
|
||||||
private inline fun updateState(block: (MainViewState) -> MainViewState) {
|
private inline fun updateState(block: (MainViewState) -> MainViewState) {
|
||||||
_state.update(block)
|
_state.update(block)
|
||||||
}
|
}
|
||||||
|
@ -159,6 +126,5 @@ data class MainViewState(
|
||||||
val isLoading: Boolean = true,
|
val isLoading: Boolean = true,
|
||||||
val gaid: String = "",
|
val gaid: String = "",
|
||||||
val isServiceReady: Boolean = false,
|
val isServiceReady: Boolean = false,
|
||||||
val floatingWindowVisible: Boolean? = null,
|
|
||||||
val errorMessage: String? = null
|
val errorMessage: String? = null
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,14 +3,21 @@ package io.sixminutes.ridicule
|
||||||
import android.accessibilityservice.AccessibilityService
|
import android.accessibilityservice.AccessibilityService
|
||||||
import android.accessibilityservice.AccessibilityServiceInfo
|
import android.accessibilityservice.AccessibilityServiceInfo
|
||||||
import android.accessibilityservice.GestureDescription
|
import android.accessibilityservice.GestureDescription
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Path
|
import android.graphics.Path
|
||||||
|
import android.graphics.PixelFormat
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
import android.view.accessibility.AccessibilityEvent
|
import android.view.accessibility.AccessibilityEvent
|
||||||
import android.view.accessibility.AccessibilityNodeInfo
|
import android.view.accessibility.AccessibilityNodeInfo
|
||||||
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
@ -22,6 +29,7 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
private const val DEFAULT_MAX_RETRIES = 5
|
private const val DEFAULT_MAX_RETRIES = 5
|
||||||
private const val DEFAULT_RETRY_INTERVAL = 4000L
|
private const val DEFAULT_RETRY_INTERVAL = 4000L
|
||||||
|
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
@Volatile
|
@Volatile
|
||||||
private var instance: MyAccessibilityService? = null
|
private var instance: MyAccessibilityService? = null
|
||||||
|
|
||||||
|
@ -36,6 +44,9 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region 类成员与初始化
|
// region 类成员与初始化
|
||||||
|
private var windowManager: WindowManager? = null
|
||||||
|
private var floatingView: View? = null
|
||||||
|
|
||||||
private var targetAppName: String? = null
|
private var targetAppName: String? = null
|
||||||
private var targetAppPackageName: String? = null
|
private var targetAppPackageName: String? = null
|
||||||
private var currentAppPackageName: String? = null
|
private var currentAppPackageName: String? = null
|
||||||
|
@ -64,6 +75,15 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
instance = this
|
instance = this
|
||||||
Log.d(TAG, "Service connected")
|
Log.d(TAG, "Service connected")
|
||||||
startMainActivitySafely()
|
startMainActivitySafely()
|
||||||
|
|
||||||
|
// 配置服务信息
|
||||||
|
val info = AccessibilityServiceInfo().apply {
|
||||||
|
eventTypes =
|
||||||
|
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED or AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
|
||||||
|
feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC
|
||||||
|
notificationTimeout = 100
|
||||||
|
}
|
||||||
|
serviceInfo = info
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
@ -72,6 +92,8 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
cleanUpResources()
|
cleanUpResources()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
|
||||||
|
// 移除悬浮窗口,防止内存泄漏
|
||||||
|
removeFloatingWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUnbind(intent: Intent?): Boolean {
|
override fun onUnbind(intent: Intent?): Boolean {
|
||||||
|
@ -90,7 +112,7 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
* @param x X轴坐标(像素)
|
* @param x X轴坐标(像素)
|
||||||
* @param y Y轴坐标(像素)
|
* @param y Y轴坐标(像素)
|
||||||
*/
|
*/
|
||||||
fun simulateTap(x: Int, y: Int) {
|
private fun simulateTap(x: Int, y: Int) {
|
||||||
Log.d(TAG, "Simulating tap at ($x, $y)")
|
Log.d(TAG, "Simulating tap at ($x, $y)")
|
||||||
val command = "input tap $x $y"
|
val command = "input tap $x $y"
|
||||||
|
|
||||||
|
@ -128,19 +150,21 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
launcherButtonText = buttonText
|
launcherButtonText = buttonText
|
||||||
targetAppName = appName
|
targetAppName = appName
|
||||||
success = true
|
success = true
|
||||||
running = true
|
|
||||||
|
// 显示悬浮窗
|
||||||
|
createFloatingWindow()
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Failed to click app: $appName")
|
Log.w(TAG, "Failed to click app: $appName")
|
||||||
|
showToast("Failed to click app: $appName")
|
||||||
}
|
}
|
||||||
} ?: Log.w(TAG, "App not found: $appName")
|
} ?: {
|
||||||
|
Log.w(TAG, "App not found: $appName")
|
||||||
|
showToast("App not found: $appName")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
|
||||||
running = false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isTargetAppRunning(): Boolean {
|
private fun isTargetAppRunning(): Boolean {
|
||||||
val currentRoot = rootInActiveWindow
|
val currentRoot = rootInActiveWindow
|
||||||
return if (currentRoot != null) {
|
return if (currentRoot != null) {
|
||||||
|
@ -243,11 +267,11 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
private fun createClickGesture(x: Int, y: Int): GestureDescription {
|
private fun createClickGesture(x: Int, y: Int): GestureDescription {
|
||||||
val path = Path().apply { moveTo(x.toFloat(), y.toFloat()) }
|
val path = Path().apply { moveTo(x.toFloat(), y.toFloat()) }
|
||||||
return GestureDescription.Builder().addStroke(
|
return GestureDescription.Builder().addStroke(
|
||||||
GestureDescription.StrokeDescription(
|
GestureDescription.StrokeDescription(
|
||||||
path, 0L, // 开始时间
|
path, 0L, // 开始时间
|
||||||
50L // 持续时间(毫秒)
|
50L // 持续时间(毫秒)
|
||||||
)
|
)
|
||||||
).build()
|
).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -274,8 +298,7 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
var found = false
|
var found = false
|
||||||
findNodesByCondition(root) { node ->
|
findNodesByCondition(root) { node ->
|
||||||
node.textMatches(
|
node.textMatches(
|
||||||
buttonText,
|
buttonText, exact = true
|
||||||
exact = true
|
|
||||||
) || node.contentDescriptionMatches(buttonText, exact = true)
|
) || node.contentDescriptionMatches(buttonText, exact = true)
|
||||||
}.firstOrNull()?.apply {
|
}.firstOrNull()?.apply {
|
||||||
Log.d(
|
Log.d(
|
||||||
|
@ -315,6 +338,8 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
var retryCount = 0
|
var retryCount = 0
|
||||||
|
|
||||||
fun attempt() {
|
fun attempt() {
|
||||||
|
if (!running)
|
||||||
|
return
|
||||||
when {
|
when {
|
||||||
checkCondition() -> onSuccess()
|
checkCondition() -> onSuccess()
|
||||||
retryCount < maxRetries -> {
|
retryCount < maxRetries -> {
|
||||||
|
@ -473,6 +498,7 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查前端应用
|
||||||
private fun checkAppPackageName() {
|
private fun checkAppPackageName() {
|
||||||
Log.w(TAG, "Detected switch to non-target app: $currentAppPackageName")
|
Log.w(TAG, "Detected switch to non-target app: $currentAppPackageName")
|
||||||
try {
|
try {
|
||||||
|
@ -492,6 +518,7 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查当前 Activity
|
||||||
private fun checkActivity() {
|
private fun checkActivity() {
|
||||||
Log.w(TAG, "Current class name is : $currentClassName")
|
Log.w(TAG, "Current class name is : $currentClassName")
|
||||||
try {
|
try {
|
||||||
|
@ -506,8 +533,7 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
|
|
||||||
"io.sixminutes.breakingnews.ClickTrackerActivity" -> simulateTap(
|
"io.sixminutes.breakingnews.ClickTrackerActivity" -> simulateTap(
|
||||||
Random.nextInt(
|
Random.nextInt(
|
||||||
0,
|
0, 720
|
||||||
720
|
|
||||||
), Random.nextInt(0, 1080)
|
), Random.nextInt(0, 1080)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -539,10 +565,91 @@ class MyAccessibilityService : AccessibilityService() {
|
||||||
private fun returnToHomeAndRestart(): Boolean {
|
private fun returnToHomeAndRestart(): Boolean {
|
||||||
Log.d(TAG, "Used home+restart strategy")
|
Log.d(TAG, "Used home+restart strategy")
|
||||||
return performGlobalAction(GLOBAL_ACTION_HOME) && targetAppName != null && targetAppPackageName != null && findAndLaunchApp(
|
return performGlobalAction(GLOBAL_ACTION_HOME) && targetAppName != null && targetAppPackageName != null && findAndLaunchApp(
|
||||||
targetAppName!!,
|
targetAppName!!, targetAppPackageName!!
|
||||||
targetAppPackageName!!
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
// region 县浮窗
|
||||||
|
/**
|
||||||
|
* 创建悬浮窗口,并添加到 WindowManager
|
||||||
|
*/
|
||||||
|
@SuppressLint("InflateParams")
|
||||||
|
private fun createFloatingWindow() {
|
||||||
|
if (running) return
|
||||||
|
running = true
|
||||||
|
|
||||||
|
// 获取 WindowManager 服务
|
||||||
|
windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
|
if (floatingView == null) {
|
||||||
|
// 使用 LayoutInflater 加载悬浮窗口布局
|
||||||
|
floatingView = LayoutInflater.from(this).inflate(R.layout.floating_window, null, false)
|
||||||
|
// 设置点击监听器,点击时移除悬浮窗口
|
||||||
|
floatingView?.setOnClickListener {
|
||||||
|
hideFloatingWindow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 设置窗口布局参数
|
||||||
|
val layoutParams = WindowManager.LayoutParams(
|
||||||
|
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||||
|
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||||
|
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
||||||
|
// FLAG_NOT_FOCUSABLE 表示窗口不获取焦点,FLAG_LAYOUT_IN_SCREEN 表示全屏显示
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
|
||||||
|
PixelFormat.TRANSLUCENT
|
||||||
|
)
|
||||||
|
// 设置悬浮窗口显示在屏幕的左上角,x,y 为偏移量
|
||||||
|
layoutParams.gravity = android.view.Gravity.BOTTOM or android.view.Gravity.END
|
||||||
|
layoutParams.x = 0
|
||||||
|
layoutParams.y = 100
|
||||||
|
|
||||||
|
// 将悬浮窗口添加到 WindowManager 中显示
|
||||||
|
windowManager?.addView(floatingView, layoutParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 隐藏悬浮窗并回到主窗口
|
||||||
|
*/
|
||||||
|
private fun hideFloatingWindow() {
|
||||||
|
if (!running) return
|
||||||
|
running = false
|
||||||
|
|
||||||
|
try {
|
||||||
|
floatingView?.let {
|
||||||
|
// 获取 WindowManager 服务
|
||||||
|
windowManager?.removeView(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动 MainActivity
|
||||||
|
val intent = Intent(this, MainActivity::class.java).apply {
|
||||||
|
flags =
|
||||||
|
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
|
||||||
|
}
|
||||||
|
startActivity(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("MainActivity", "Hide floating window failed", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新悬浮窗口中显示的文本
|
||||||
|
* @param text 要显示的文本内容
|
||||||
|
*/
|
||||||
|
private fun updateFloatingText(text: String) {
|
||||||
|
// 在悬浮窗口中找到 TextView 并设置新文本
|
||||||
|
val textView = floatingView?.findViewById<TextView>(R.id.first_message)
|
||||||
|
textView?.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除悬浮窗口,释放资源
|
||||||
|
*/
|
||||||
|
private fun removeFloatingWindow() {
|
||||||
|
if (floatingView != null) {
|
||||||
|
windowManager?.removeView(floatingView)
|
||||||
|
floatingView = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,49 @@
|
||||||
<!-- res/layout/floating_window.xml -->
|
<!-- res/layout/floating_window.xml -->
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/frameLayout2"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="#22000000"
|
android:layout_gravity="bottom|end"
|
||||||
android:layout_gravity="bottom|end">
|
android:background="#22000000">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/first_message"
|
||||||
|
android:layout_width="240ddp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="16dp"
|
android:padding="2dp"
|
||||||
android:text="悬浮窗"
|
android:text="2"
|
||||||
android:textSize="18sp" />
|
android:textSize="18sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
</FrameLayout>
|
<TextView
|
||||||
|
android:id="@+id/first_second"
|
||||||
|
android:layout_width="240pdp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:text="1"
|
||||||
|
android:textSize="18sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="1.0" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/main_message"
|
||||||
|
android:layout_width="240pdp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:text="xxxx"
|
||||||
|
android:textSize="18sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/first_message"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue
Block a user