GIT COMMIT MESSAGE:

删除无障碍服务并重构广告处理

- 重构广告管理,删除过时的无障碍功能,以简化应用程序。
- 引入新库并更新依赖以改善功能和兼容性。
- 增强展示插页广告的UI组件,并简化主活动结构。
This commit is contained in:
lvlisong 2025-01-23 22:28:30 +08:00
parent 7713c2cfc3
commit c622b7e435
10 changed files with 160 additions and 369 deletions

View File

@ -1,6 +1,30 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" /> <option name="previewFile" value="true" />
@ -9,6 +33,9 @@
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" /> <option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" /> <option name="previewFile" value="true" />

View File

@ -64,6 +64,7 @@ dependencies {
implementation("com.applovin:applovin-sdk:+") implementation("com.applovin:applovin-sdk:+")
implementation("com.applovin.dsp:linkedin-adapter:+") implementation("com.applovin.dsp:linkedin-adapter:+")
implementation("com.adjust.sdk:adjust-android:5.0.2") implementation("com.adjust.sdk:adjust-android:5.0.2")
implementation(libs.mediation.test.suite)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.espresso.core)

View File

@ -2,8 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<application <application
android:name=".BreakingNewsApplication" android:name=".BreakingNewsApplication"
android:allowBackup="true" android:allowBackup="true"
@ -31,18 +29,6 @@
android:theme="@style/Theme.Breakingnews"> android:theme="@style/Theme.Breakingnews">
</activity> </activity>
<service
android:name=".MyAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="false"> <!-- 明确指定 exported 属性 -->
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<!-- 这里可以添加自定义的配置文件,根据你的需求添加 -->
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config" />
</service>
</application> </application>
</manifest> </manifest>

View File

@ -1,75 +0,0 @@
package io.sixminutes.breakingnews
import android.content.Context
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.lang.Exception
class GAIDHelper(private val context: Context) {
/**
* 检查 Google Play 服务是否可用
*
* @return true 如果 Google Play 服务可用否则返回 false
*/
fun isGooglePlayServicesAvailable(): Boolean {
val googleApiAvailability = GoogleApiAvailability.getInstance()
val resultCode = googleApiAvailability.isGooglePlayServicesAvailable(context)
return resultCode == ConnectionResult.SUCCESS
}
/**
* 异步获取 GAID
*
* @param callback 获取结果回调接口
*/
suspend fun getAdvertisingId(callback: OnAdvertisingIdListener) {
// 使用 withContext(Dispatchers.IO) 将获取 GAID 的操作切换到 IO 线程,避免阻塞主线程
withContext(Dispatchers.IO) {
try {
// 使用 AdvertisingIdClient.getAdvertisingIdInfo 获取 GAID 信息
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)
// 获取 GAID 字符串
val gaid = adInfo.id
// 使用 withContext(Dispatchers.Main) 将回调切换到主线程
withContext(Dispatchers.Main) {
// 调用回调接口的 onSuccess 方法,传递 GAID
if (gaid != null) {
callback.onSuccess(gaid)
} else {
callback.onFailure(Exception("Failed to retrieve advertising ID. GAID is null."))
}
}
} catch (e: Exception) {
// 使用 withContext(Dispatchers.Main) 将回调切换到主线程
withContext(Dispatchers.Main) {
// 调用回调接口的 onFailure 方法,传递异常信息
callback.onFailure(e)
}
}
}
}
/**
* GAID 获取结果回调接口
*/
interface OnAdvertisingIdListener {
/**
* 获取 GAID 成功回调
*
* @param gaid 获取到的 GAID
*/
fun onSuccess(gaid: String)
/**
* 获取 GAID 失败回调
*
* @param e 异常信息
*/
fun onFailure(e: Exception)
}
}

View File

@ -14,65 +14,66 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class InterstitialActivity : Activity(), MaxAdListener { class InterstitialActivity : Activity(), MaxAdListener {
private lateinit var interstitialAd: MaxInterstitialAd private lateinit var interstitialAd: MaxInterstitialAd
private var retryAttempt = 0 private var retryAttempt = 0
private val maxRetryAttempts = 6 // 最大重试次数 private val maxRetryAttempts = 6 // 最大重试次数
private var loadCount = 0 // 用于计数广告加载次数 private var adId: String? = null // 广告 ID
private val maxLoadCount = 10 // 最大加载次数 private val scope = CoroutineScope(Dispatchers.Main) // 创建一个 CoroutineScope
private var isAdShowing = false // 用于跟踪广告是否正在显示
private val scope = CoroutineScope(Dispatchers.Main) // 创建一个CoroutineScope
private val TAG = "BreakingNews" private val TAG = "BreakingNews"
fun createInterstitialAd() { override fun onCreate(savedInstanceState: Bundle?) {
interstitialAd = MaxInterstitialAd("be20b7a9d66e8895", applicationContext) super.onCreate(savedInstanceState)
interstitialAd.setListener(this) // 获取从 MainActivity 传递过来的广告 ID
adId = intent.getStringExtra("AD_ID")
if (adId != null) {
createInterstitialAd(adId!!)
} else {
Log.e(TAG, "Ad ID is null. Cannot load ad.")
finish()
}
}
// Load the first ad private fun createInterstitialAd(adId: String) {
interstitialAd = MaxInterstitialAd(adId, this)
interstitialAd.setListener(this)
interstitialAd.loadAd() interstitialAd.loadAd()
} }
// MAX Ad Listener // MAX Ad Listener
override fun onAdLoaded(maxAd: MaxAd) { override fun onAdLoaded(maxAd: MaxAd) {
// Interstitial ad is ready to be shown. interstitialAd.isReady() will now return 'true'
Log.d(TAG, "Ad loaded successfully: ${maxAd.adUnitId}") Log.d(TAG, "Ad loaded successfully: ${maxAd.adUnitId}")
// Reset retry attempt
retryAttempt = 0 retryAttempt = 0
// 显示广告 // 显示广告
if (interstitialAd.isReady) { if (interstitialAd.isReady) {
interstitialAd.showAd(this) interstitialAd.showAd()
}
} }
override fun onAdLoadFailed(ad: String, error: MaxError) { // 返回广告加载成功状态
// Interstitial ad failed to load setAdStatusResult("Ad Loaded: ${maxAd.adUnitId}")
Log.d(TAG, "Ad load failed: ${ad}, error code: ${error.code}, message: ${error.message}") }
override fun onAdLoadFailed(adUnitId: String, error: MaxError) {
Log.d(TAG, "Ad load failed: $adUnitId, error code: ${error.code}, message: ${error.message}")
if (retryAttempt < maxRetryAttempts) { if (retryAttempt < maxRetryAttempts) {
retryAttempt++ retryAttempt++
val delayMillis = val delayMillis = TimeUnit.SECONDS.toMillis(Math.pow(2.0, retryAttempt.toDouble()).toLong())
TimeUnit.SECONDS.toMillis(Math.pow(2.0, retryAttempt.toDouble()).toLong())
// 使用 Coroutine 延迟重试
scope.launch { scope.launch {
delay(delayMillis) delay(delayMillis)
interstitialAd.loadAd() interstitialAd.loadAd()
} }
} else { } else {
Log.d(TAG, "Maximum retry attempts reached. No more retries.") Log.d(TAG, "Maximum retry attempts reached. No more retries.")
// 返回广告加载失败状态
setAdStatusResult("Ad Load Failed: $adUnitId")
} }
} }
override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) { override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) {
// Interstitial ad failed to display. AppLovin recommends that you load the next ad. Log.d(TAG, "Ad display failed: ${ad.adUnitId}, error code: ${error.code}, message: ${error.message}")
interstitialAd.loadAd() interstitialAd.loadAd()
Log.d(
TAG,
"Ad disable failed: ${ad.adUnitId}, error code: ${error.code}, message: ${error.message}"
)
} }
override fun onAdDisplayed(maxAd: MaxAd) { override fun onAdDisplayed(maxAd: MaxAd) {
@ -84,23 +85,16 @@ class InterstitialActivity : Activity(), MaxAdListener {
} }
override fun onAdHidden(maxAd: MaxAd) { override fun onAdHidden(maxAd: MaxAd) {
// Interstitial ad is hidden. Pre-load the next ad
interstitialAd.loadAd()
Log.d(TAG, "Ad hidden: ${maxAd.adUnitId}") Log.d(TAG, "Ad hidden: ${maxAd.adUnitId}")
interstitialAd.loadAd()
finish() // 广告关闭后结束当前 Activity
} }
// 返回主页面的方法 // 设置广告状态结果
private fun returnToMainPage() { private fun setAdStatusResult(status: String) {
val intent = Intent(this, MainActivity::class.java) // 假设主页面是 MainActivity val resultIntent = Intent().apply {
startActivity(intent) putExtra("AD_STATUS", status)
finish() // 结束当前 Activity
} }
setResult(RESULT_OK, resultIntent)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
createInterstitialAd() // 初始调用,创建并加载插页广告
} }
} }

View File

@ -0,0 +1,24 @@
package io.sixminutes.breakingnews
import android.content.Intent
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
/**
* InterstitialAdManager 是一个单例对象用于管理插页广告的显示逻辑
*/
object InterstitialAdManager {
/**
* 显示插页广告
*
* @param activity 当前的 Activity
* @param adId 广告 ID
* @param launcher 用于启动 InterstitialActivity ActivityResultLauncher
*/
fun showAd(activity: ComponentActivity, adId: String, launcher: ActivityResultLauncher<Intent>) {
val intent = Intent(activity, InterstitialActivity::class.java).apply {
putExtra("AD_ID", adId) // 传递广告 ID
}
launcher.launch(intent) // 启动 InterstitialActivity
}
}

View File

@ -1,53 +1,37 @@
package io.sixminutes.breakingnews package io.sixminutes.breakingnews
import android.content.ComponentName import android.os.Build
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle import android.os.Bundle
import android.os.IBinder
import android.provider.Settings
import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.applovin.mediation.MaxAd
import com.applovin.mediation.MaxAdViewAdListener
import com.applovin.mediation.MaxError
import com.applovin.mediation.ads.MaxAdView
import io.sixminutes.breakingnews.ui.theme.BreakingnewsTheme import io.sixminutes.breakingnews.ui.theme.BreakingnewsTheme
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
private val gaidHelper by lazy { GAIDHelper(this) }
private val TAG = "BreakingNews" private val TAG = "BreakingNews"
private var accessibilityService: MyAccessibilityService? = null
private val ACCESSIBILITY_SERVICE_NAME = "io.sixminutes.breakingnews.MyAccessibilityService"
// ActivityResultLauncher 用于启动无障碍设置页面 // 定义 ActivityResultLauncher
private lateinit var enableAccessibilityLauncher: ActivityResultLauncher<Intent> private val interstitialLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK) {
val status = result.data?.getStringExtra("AD_STATUS") ?: "Unknown"
// 更新广告状态
updateAdStatus(status)
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// 初始化 ActivityResultLauncher
enableAccessibilityLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
// 用户从设置页面返回后,再次检查无障碍服务是否可用
checkAccessibilityService()
}
// 检查无障碍服务是否可用
checkAccessibilityService()
setContent { setContent {
BreakingnewsTheme { BreakingnewsTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
@ -57,117 +41,75 @@ class MainActivity : ComponentActivity() {
.padding(innerPadding), .padding(innerPadding),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
val gaid = remember { mutableStateOf<String?>(null) } MainContent()
LaunchedEffect(Unit) {
gaidHelper.getAdvertisingId(object : GAIDHelper.OnAdvertisingIdListener {
override fun onSuccess(gaidValue: String) {
gaid.value = gaidValue
Log.d(TAG, "GAID: $gaidValue")
} }
override fun onFailure(e: Exception) {
Log.e(TAG, "Failed to retrieve GAID: ${e.message}")
}
})
}
GAIDInfo(gaid = gaid.value)
} }
} }
} }
} }
// 显示插页式广告 @Composable
//InterstitialAdManager.showAd(this) fun MainContent() {
val deviceModel = Build.MODEL
var adStatus by remember { mutableStateOf("Ad Status: Unknown") }
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Device Model: $deviceModel")
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
// 调用 InterstitialAdManager 显示广告
InterstitialAdManager.showAd(this@MainActivity, "be20b7a9d66e8895", interstitialLauncher)
adStatus = "Ad 1 Loading..."
}) {
Text(text = "Show Inter Ad 1: be20b7a9d66e8895")
} }
/** Spacer(modifier = Modifier.height(16.dp))
* 检查无障碍服务是否可用如果不可用则打开设置页面
*/ Button(onClick = {
private fun checkAccessibilityService() { // 调用 InterstitialAdManager 显示广告
accessibilityService = MyAccessibilityService.getInstance() InterstitialAdManager.showAd(this@MainActivity, "be20b7a9d66e8895", interstitialLauncher)
if (accessibilityService == null) { adStatus = "Ad 2 Loading..."
Log.e(TAG, "Accessibility service is not available") }) {
openAccessibilitySettings() Text(text = "Show Inter Ad 2: be20b7a9d66e8896")
} else { }
Log.d(TAG, "Accessibility service is available: $accessibilityService")
continueMainActivityExecution() Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
finish()
}) {
Text(text = "Kill")
}
Spacer(modifier = Modifier.height(16.dp))
Text(text = adStatus)
} }
} }
/** private fun updateAdStatus(status: String) {
* 打开无障碍设置页面 // 更新 UI 中的广告状态
*/ runOnUiThread {
private fun openAccessibilitySettings() { setContent {
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) BreakingnewsTheme {
enableAccessibilityLauncher.launch(intent) Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Log.d(TAG, "Opening accessibility settings") Box(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding),
contentAlignment = Alignment.Center
) {
MainContent()
}
}
} }
/**
* 无障碍服务可用后继续执行 MainActivity 的逻辑
*/
private fun continueMainActivityExecution() {
// 在这里添加 MainActivity 的后续操作,例如更新 UI 或执行其他任务
Log.d(TAG, "Continuing MainActivity execution after accessibility service is enabled.")
} }
override fun onDestroy() {
super.onDestroy()
} }
// 调用点击功能的方法
fun simulateTap(x: Int, y: Int) {
accessibilityService?.simulateTap(x, y)
}
}
@Composable
fun GAIDInfo(gaid: String?) {
Text(text = gaid ?: "Loading GAID...")
}
@Composable
fun BannerAdView(
adUnitId: String,
modifier: Modifier = Modifier,
onAdLoaded: () -> Unit = {},
onAdLoadFailed: (MaxError) -> Unit = {}
) {
val context = LocalContext.current
AndroidView(
factory = { ctx ->
val adView = MaxAdView(adUnitId, ctx)
adView.setListener(object : MaxAdViewAdListener {
override fun onAdLoaded(ad: MaxAd) {
onAdLoaded()
}
override fun onAdDisplayed(ad: MaxAd) {}
override fun onAdHidden(ad: MaxAd) {}
override fun onAdClicked(ad: MaxAd) {}
override fun onAdLoadFailed(adUnitId: String, error: MaxError) {
onAdLoadFailed(error)
}
override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) {}
override fun onAdExpanded(ad: MaxAd) {}
override fun onAdCollapsed(ad: MaxAd) {}
})
adView.setExtraParameter("adaptive_banner", "true")
adView.setBackgroundColor(android.graphics.Color.TRANSPARENT)
adView.loadAd()
adView
},
modifier = modifier
.fillMaxWidth()
.height(80.dp)
)
}
object InterstitialAdManager {
fun showAd(activity: ComponentActivity) {
val intent = Intent(activity, InterstitialActivity::class.java)
activity.startActivity(intent)
} }
} }

View File

@ -1,102 +0,0 @@
package io.sixminutes.breakingnews
import android.accessibilityservice.AccessibilityService
import android.content.Context
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
import android.view.accessibility.AccessibilityEvent
import androidx.compose.ui.geometry.Rect
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
class MyAccessibilityService : AccessibilityService() {
private val TAG = "BreakingNews"
private var serviceInstance: MyAccessibilityService? = null
private val binder = MyBinder()
companion object {
private var instance: MyAccessibilityService? = null
fun getInstance(): MyAccessibilityService? {
return instance
}
}
inner class MyBinder : Binder() {
fun getService(): MyAccessibilityService? {
Log.d(TAG, "Binder getService called")
return serviceInstance
}
}
override fun onServiceConnected() {
super.onServiceConnected()
serviceInstance = this
instance = this // 设置静态实例
Log.d(TAG, "Service connected, instance set to $instance")
// 无障碍服务已启用,启动 MainActivity
startMainActivity()
}
override fun onDestroy() {
super.onDestroy()
instance = null // 清除静态实例
}
override fun onInterrupt() {
Log.d(TAG, "Service interrupted")
}
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
// 处理无障碍事件
}
override fun onUnbind(intent: Intent?): Boolean {
serviceInstance = null
Log.d(TAG, "Service unbound, serviceInstance set to null")
return super.onUnbind(intent)
}
fun simulateTap(x: Int, y: Int) {
val rootNode = AccessibilityNodeInfoCompat.wrap(rootInActiveWindow)
if (rootNode != null) {
val clickableNodes = ArrayList<AccessibilityNodeInfoCompat>()
findClickableNodes(rootNode, clickableNodes)
for (node in clickableNodes) {
val bounds = android.graphics.Rect()
node.getBoundsInScreen(bounds)
if (bounds.contains(x, y)) {
node.performAction(AccessibilityNodeInfoCompat.ACTION_CLICK)
Log.d(TAG, "Clicked node at ($x,$y)")
break
}
}
} else {
Log.e(TAG, "No root node found")
}
}
private fun findClickableNodes(node: AccessibilityNodeInfoCompat, clickableNodes: ArrayList<AccessibilityNodeInfoCompat>) {
if (node.isClickable) {
clickableNodes.add(node)
}
val childCount = node.childCount
for (i in 0 until childCount) {
val child = node.getChild(i)
if (child != null) {
findClickableNodes(child, clickableNodes)
child.recycle()
}
}
}
private fun startMainActivity() {
// 创建 Intent 启动 MainActivity
val intent = Intent(this, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK // 必须设置 FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
Log.d(TAG, "MainActivity started")
}
}

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:canPerformGestures="true"
/>

View File

@ -8,6 +8,7 @@ espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.7" lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.9.3" activityCompose = "1.9.3"
composeBom = "2024.04.01" composeBom = "2024.04.01"
mediationTestSuite = "3.0.0"
[libraries] [libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@ -24,6 +25,7 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
mediation-test-suite = { group = "com.google.android.ads", name = "mediation-test-suite", version.ref = "mediationTestSuite" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }