refactor: ###

优化代码并更新文件中的UI元素

- 重构了MainViewModel.kt中的代码,去除了未使用的导入项和不必要的初始化
- 更新了floating_window.xml布局中的样式
- 在AndroidManifest.xml中添加了PACKAGE_USAGE_STATS权限
This commit is contained in:
lvlisong 2025-03-26 00:26:20 +08:00
parent 5dcda8829d
commit 054e86ec13
4 changed files with 119 additions and 116 deletions

View File

@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<application
android:allowBackup="true"

View File

@ -6,8 +6,6 @@ import android.content.Intent
import android.net.Uri
import android.provider.Settings
import android.util.Log
import android.view.View
import android.view.WindowManager
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.delay
@ -15,7 +13,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.lang.ref.WeakReference
class MainViewModel(application: Application) : AndroidViewModel(application) {
@ -26,9 +23,6 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
// region 依赖项
private val gaidHelper by lazy { GAIDHelper(application) }
private val windowManager by lazy {
getApplication<Application>().getSystemService(Context.WINDOW_SERVICE) as WindowManager
}
// endregion
// region 公共API
@ -58,9 +52,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
delay(1000) // 延迟 1000 毫秒,确保桌面已经显示
try {
MyAccessibilityService.getInstance()?.findAndLaunchApp(
"breakingnews",
"io.sixminutes.breakingnews",
"Test"
"breakingnews", "io.sixminutes.breakingnews", "Test"
//"Show Inter Ad 1: 3689d2816239b64e"
)
} catch (e: Exception) {

View File

@ -50,7 +50,7 @@ class MyAccessibilityService : AccessibilityService() {
private var targetAppName: String? = null
private var targetAppPackageName: String? = null
private var currentAppPackageName: String? = null
private var currentClassName: String? = null
private var currentActivityName: String? = null
private var launcherButtonText: String? = null
private val mainHandler by lazy { Handler(Looper.getMainLooper()) }
private var running = false
@ -119,13 +119,13 @@ class MyAccessibilityService : AccessibilityService() {
// **显示指示器**
tapIndicatorView.showTapIndicator(x, y)
if (RootHelper.runRootCommand(command)) {
Toast.makeText(this, "点击 ($x, $y) 成功", Toast.LENGTH_SHORT).show()
Log.d(TAG, "Executed: $command")
} else {
Toast.makeText(this, "点击 ($x, $y) 失败", Toast.LENGTH_SHORT).show()
Log.e(TAG, "Failed to execute: $command")
}
// if (RootHelper.runRootCommand(command)) {
// Toast.makeText(this, "点击 ($x, $y) 成功", Toast.LENGTH_SHORT).show()
// Log.d(TAG, "Executed: $command")
// } else {
// Toast.makeText(this, "点击 ($x, $y) 失败", Toast.LENGTH_SHORT).show()
// Log.e(TAG, "Failed to execute: $command")
// }
}
/**
@ -140,6 +140,8 @@ class MyAccessibilityService : AccessibilityService() {
buttonText: String? = null,
): Boolean {
var success = false
// 显示悬浮窗
createFloatingWindow()
safeRootOperation { root ->
findNodesByCondition(root) { node ->
node.textMatches(appName) || node.contentDescriptionMatches(appName)
@ -150,32 +152,18 @@ class MyAccessibilityService : AccessibilityService() {
launcherButtonText = buttonText
targetAppName = appName
success = true
// 显示悬浮窗
createFloatingWindow()
} else {
Log.w(TAG, "Failed to click app: $appName")
showToast("Failed to click app: $appName")
updateFloatingText("Failed to click app: $appName", true)
}
} ?: {
Log.w(TAG, "App not found: $appName")
showToast("App not found: $appName")
updateFloatingText("App not found: $appName", true)
}
}
return success
}
private fun isTargetAppRunning(): Boolean {
val currentRoot = rootInActiveWindow
return if (currentRoot != null) {
currentAppPackageName = currentRoot.packageName?.toString()
currentAppPackageName == targetAppPackageName
} else {
Log.e(TAG, "isTargetAppRunning: rootInActiveWindow is null")
false
}
}
// region 点击逻辑优化
/**
* 智能点击方案综合节点属性/父容器/坐标点击
@ -290,6 +278,7 @@ class MyAccessibilityService : AccessibilityService() {
retryInterval: Long = DEFAULT_RETRY_INTERVAL
): Boolean {
Log.d(TAG, "Attempting to click button: '$buttonText'")
updateFloatingText("Attempting to click button: '$buttonText'")
var success = false
safeRootOperation { root ->
@ -338,8 +327,7 @@ class MyAccessibilityService : AccessibilityService() {
var retryCount = 0
fun attempt() {
if (!running)
return
if (!running) return
when {
checkCondition() -> onSuccess()
retryCount < maxRetries -> {
@ -454,75 +442,95 @@ class MyAccessibilityService : AccessibilityService() {
}
}
// 检查当前窗口是哪个
private fun isTargetAppRunning(): Boolean {
val currentRoot = rootInActiveWindow
return if (currentRoot != null) {
currentAppPackageName = currentRoot.packageName?.toString()
currentAppPackageName == targetAppPackageName
} else {
Log.e(TAG, "isTargetAppRunning: rootInActiveWindow is null")
updateFloatingText("rootInActiveWindow is null", true)
false
}
}
private fun handleWindowChange(event: AccessibilityEvent) {
if (!running) return
if (targetAppPackageName == null) return
currentAppPackageName = event.packageName?.toString() ?: return
currentClassName = event.className?.toString() ?: return
val packageName = event.packageName?.toString() ?: "unknownPackageName"
val activity = event.className?.toString() ?: "unknownActivity"
if (packageName == "io.sixminutes.ridicule" && activity == "android.widget.LinearLayout") {
Log.w(TAG, "Detected current app is floating window")
return
}
// 增强日志输出
Log.d(
TAG, """
Window Changed:
Current Package : $currentAppPackageName
Current Package : $packageName
Target Package : $targetAppPackageName
Class Name : ${currentClassName?.substringAfterLast('.')}
Full Class Name : $currentClassName
Class Name : ${activity.substringAfterLast('.')}
Full Class Name : $activity
""".trimIndent()
)
if (targetAppPackageName == null) return
currentAppPackageName = packageName
// 当检测到切换到其他应用时
if (currentAppPackageName != targetAppPackageName) {
// 先检查 isCheckAppPackageName如果为 true 则不重复启动
if (!isCheckAppPackageName) {
isCheckAppPackageName = true
mainHandler.postDelayed(::checkAppPackageName, DEFAULT_RETRY_INTERVAL)
} else {
Log.w(TAG, "CheckAppPackageName operation is already running, skipping...")
}
checkAppPackageName()
} else {
// 先检查 isCheckActivity如果为 true 则不重复启动
if (!isCheckActivity) {
isCheckActivity = true
mainHandler.postDelayed(::checkActivity, DEFAULT_RETRY_INTERVAL)
} else {
Log.w(TAG, "CheckActivity operation is already running, skipping...")
}
currentActivityName = activity
checkActivity()
}
Log.d(
TAG,
"Current App Package: $currentAppPackageName; Current App Class: $currentClassName; targetAppPackageName: $targetAppPackageName"
)
}
// 检查前端应用
private fun checkAppPackageName() {
Log.w(TAG, "Detected switch to non-target app: $currentAppPackageName")
try {
retryOperation(DEFAULT_MAX_RETRIES, DEFAULT_RETRY_INTERVAL, checkCondition = {
val ok = isTargetAppRunning()
ok || performReturnGlobalAction()
Log.d(TAG, "Check $targetAppPackageName is activated: $ok")
ok
}, onSuccess = {
Log.i(TAG, "Successfully returned to target app")
}, onFailure = {
Log.e(TAG, "Failed to return after $DEFAULT_MAX_RETRIES attempts")
returnToHomeAndRestart()
})
} finally {
isCheckAppPackageName = false // 任务结束后重置
if (isCheckAppPackageName) {
Log.w(TAG, "CheckAppPackageName operation is already running, skipping...")
updateFloatingText("CheckAppPackageName operation is already running, skipping...")
return
}
isCheckAppPackageName = true
Log.w(TAG, "Detected switch to non-target app: $currentAppPackageName")
updateFloatingText("Detected switch to non-target app: $currentAppPackageName")
retryOperation(DEFAULT_MAX_RETRIES, DEFAULT_RETRY_INTERVAL, checkCondition = {
val ok = isTargetAppRunning()
ok || performReturnGlobalAction()
Log.d(TAG, "Check $targetAppPackageName is activated: $ok")
ok
}, onSuccess = {
isCheckAppPackageName = false // 任务结束后重置
updateFloatingText("Successfully returned to target app")
Log.i(TAG, "Successfully returned to target app")
// 返回后再次检查 Activity
mainHandler.postDelayed(::checkActivity, DEFAULT_RETRY_INTERVAL)
}, onFailure = {
isCheckAppPackageName = false // 任务结束后重置
updateFloatingText("Failed to return after $DEFAULT_MAX_RETRIES attempts", true)
Log.e(TAG, "Failed to return after $DEFAULT_MAX_RETRIES attempts")
returnToHomeAndRestart()
})
}
// 检查当前 Activity
private fun checkActivity() {
Log.w(TAG, "Current class name is : $currentClassName")
if (isCheckActivity) {
Log.w(TAG, "CheckActivity operation is already running, skipping...")
updateFloatingText("CheckActivity operation is already running, skipping...")
return
}
isCheckActivity = true
Log.w(TAG, "Current class name is : $currentActivityName")
try {
when (currentClassName) {
when (currentActivityName) {
"io.sixminutes.breakingnews.MainActivity" -> launcherButtonText?.let {
findAndClickButton(it)
}
@ -538,6 +546,13 @@ class MyAccessibilityService : AccessibilityService() {
)
"com.applovin.adview.AppLovinFullscreenActivity" -> simulateTap(648, 62)
else -> {
Log.d(TAG, "current Activity is unknown")
updateFloatingText("current Activity is unknown")
mainHandler.postDelayed({
checkActivity()
}, DEFAULT_RETRY_INTERVAL)
}
}
} finally {
isCheckActivity = false
@ -564,8 +579,9 @@ class MyAccessibilityService : AccessibilityService() {
*/
private fun returnToHomeAndRestart(): Boolean {
Log.d(TAG, "Used home+restart strategy")
updateFloatingText("used home+restart strategy")
return performGlobalAction(GLOBAL_ACTION_HOME) && targetAppName != null && targetAppPackageName != null && findAndLaunchApp(
targetAppName!!, targetAppPackageName!!
targetAppName!!, targetAppPackageName!!, launcherButtonText
)
}
@ -602,7 +618,7 @@ class MyAccessibilityService : AccessibilityService() {
// 设置悬浮窗口显示在屏幕的左上角x,y 为偏移量
layoutParams.gravity = android.view.Gravity.BOTTOM or android.view.Gravity.END
layoutParams.x = 0
layoutParams.y = 100
layoutParams.y = 150
// 将悬浮窗口添加到 WindowManager 中显示
windowManager?.addView(floatingView, layoutParams)
@ -636,10 +652,17 @@ class MyAccessibilityService : AccessibilityService() {
* 更新悬浮窗口中显示的文本
* @param text 要显示的文本内容
*/
private fun updateFloatingText(text: String) {
private fun updateFloatingText(text: String, isError: Boolean = false) {
// 在悬浮窗口中找到 TextView 并设置新文本
val textView = floatingView?.findViewById<TextView>(R.id.first_message)
textView?.text = text
if (isError) {
val errorView = floatingView?.findViewById<TextView>(R.id.error_message)
errorView?.text = text
} else {
val info1 = floatingView?.findViewById<TextView>(R.id.info_message_1)
val info2 = floatingView?.findViewById<TextView>(R.id.info_message_2)
info2?.text = info1?.text
info1?.text = text
}
}
/**

View File

@ -1,49 +1,36 @@
<!-- res/layout/floating_window.xml -->
<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"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/frameLayout2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:background="#22000000">
android:background="#22000080"
android:padding="8dp"
android:orientation="vertical">
<TextView
android:id="@+id/first_message"
android:layout_width="240ddp"
android:id="@+id/info_message_2"
android:layout_width="240dp"
android:layout_height="wrap_content"
android:padding="2dp"
android:text="2"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:text=""
android:textSize="12sp"/>
<TextView
android:id="@+id/first_second"
android:layout_width="240pdp"
android:id="@+id/info_message_1"
android:layout_width="240dp"
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" />
android:text=""
android:textSize="12sp"/>
<TextView
android:id="@+id/main_message"
android:layout_width="240pdp"
android:id="@+id/error_message"
android:layout_width="240dp"
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" />
android:text=""
android:textColor="#ff0000"
android:textSize="14sp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>