From 94b09a235ff84429a816916b8cab612f57829664 Mon Sep 17 00:00:00 2001 From: lvlisong Date: Tue, 21 Jan 2025 00:27:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=B8=A6=E6=9C=89=E5=B9=BF?= =?UTF-8?q?=E5=91=8A=E6=94=AF=E6=8C=81=E7=9A=84=E5=8F=AF=E8=AE=BF=E9=97=AE?= =?UTF-8?q?=E6=80=A7=E6=9C=8D=E5=8A=A1=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为模拟器显示设置和可访问性服务添加新的配置文件 - 实现处理Android服务和可访问性交互的类和方法 - 更新AndroidManifest以包括必要的权限和配置项 --- .idea/emulatorDisplays.xml | 47 +++ .idea/other.xml | 362 ++++++++++++++++++ app/src/main/AndroidManifest.xml | 17 +- .../sixminutes/breakingnews/MainActivity.kt | 182 +++++---- .../breakingnews/MyAccessibiltyService.kt | 85 ++++ .../res/xml/accessibility_service_config.xml | 8 + 6 files changed, 630 insertions(+), 71 deletions(-) create mode 100644 .idea/emulatorDisplays.xml create mode 100644 .idea/other.xml create mode 100644 app/src/main/java/io/sixminutes/breakingnews/MyAccessibiltyService.kt create mode 100644 app/src/main/res/xml/accessibility_service_config.xml diff --git a/.idea/emulatorDisplays.xml b/.idea/emulatorDisplays.xml new file mode 100644 index 0000000..e6b4ce5 --- /dev/null +++ b/.idea/emulatorDisplays.xml @@ -0,0 +1,47 @@ + + + + + + \ No newline at end of file diff --git a/.idea/other.xml b/.idea/other.xml new file mode 100644 index 0000000..e3e6e5d --- /dev/null +++ b/.idea/other.xml @@ -0,0 +1,362 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 157e591..12a52e6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + + + + + + + + + - \ No newline at end of file + diff --git a/app/src/main/java/io/sixminutes/breakingnews/MainActivity.kt b/app/src/main/java/io/sixminutes/breakingnews/MainActivity.kt index 5091edd..4d21b4f 100644 --- a/app/src/main/java/io/sixminutes/breakingnews/MainActivity.kt +++ b/app/src/main/java/io/sixminutes/breakingnews/MainActivity.kt @@ -1,38 +1,80 @@ package io.sixminutes.breakingnews +import android.content.ComponentName +import android.content.Context import android.content.Intent +import android.content.ServiceConnection import android.os.Bundle +import android.os.IBinder +import android.provider.Settings import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.* import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView -import androidx.core.view.WindowCompat 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 androidx.compose.ui.unit.dp -class MainActivity : ComponentActivity(), MaxAdViewAdListener { + +class MainActivity : ComponentActivity() { private val gaidHelper by lazy { GAIDHelper(this) } private val TAG = "BreakingNews" + private var accessibilityService: MyAccessibilityService? = null + private lateinit var enableAccessibilityLauncher: ActivityResultLauncher + private val ACCESSIBILITY_SERVICE_NAME = "io.sixminutes.breakingnews.MyAccessibilityService" + + + private val connection = object : ServiceConnection { + override fun onServiceConnected(name: ComponentName?, service: IBinder?) { + if (service is MyAccessibilityService.MyBinder) { + accessibilityService = (service as MyAccessibilityService.MyBinder).getService() + Log.d(TAG, "Accessibility service connected") + } else { + Log.e(TAG, "Invalid binder type") + } + } + + override fun onServiceDisconnected(name: ComponentName?) { + accessibilityService = null + Log.d(TAG, "Accessibility service disconnected") + } + } + + private fun checkAndRequestAccessibilityPermission() { + val enabledServices = Settings.Secure.getString(contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES) + if (enabledServices == null ||!enabledServices.contains(ACCESSIBILITY_SERVICE_NAME)) { + val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) + enableAccessibilityLauncher.launch(intent) + Log.d(TAG, "Accessibility service is not enabled. Redirecting user to settings.") + } else { + Log.d(TAG, "Accessibility service is already enabled.") + continueMainActivityExecution() + } + } + + private fun continueMainActivityExecution() { + // 在这里添加 MainActivity 的后续操作,例如更新 UI 或执行其他任务 + Log.d(TAG, "Continuing MainActivity execution after accessibility service is enabled.") + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - WindowCompat.setDecorFitsSystemWindows(window, false) + + // 绑定服务 + val intent = Intent(this, MyAccessibilityService::class.java) + bindService(intent, connection, Context.BIND_AUTO_CREATE) setContent { BreakingnewsTheme { @@ -41,14 +83,14 @@ class MainActivity : ComponentActivity(), MaxAdViewAdListener { modifier = Modifier .fillMaxSize() .padding(innerPadding), + contentAlignment = Alignment.Center ) { val gaid = remember { mutableStateOf(null) } LaunchedEffect(Unit) { - gaidHelper.getAdvertisingId(object : - GAIDHelper.OnAdvertisingIdListener { + gaidHelper.getAdvertisingId(object : GAIDHelper.OnAdvertisingIdListener { override fun onSuccess(gaidValue: String) { gaid.value = gaidValue - Log.e(TAG, "GAID: ${gaidValue}") + Log.d(TAG, "GAID: $gaidValue") } override fun onFailure(e: Exception) { @@ -56,87 +98,87 @@ class MainActivity : ComponentActivity(), MaxAdViewAdListener { } }) } - // 使用 Column 组件进行垂直排列 - Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center // 垂直居中 - ) { - //BannerAdView(adUnitId = "119670a17d9d8ed5", listener = this@MainActivity) - GAIDInfo(gaid = gaid.value) - } + + GAIDInfo(gaid = gaid.value) } } } } + enableAccessibilityLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + val enabledServices = Settings.Secure.getString(contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES) + if (enabledServices!= null && enabledServices.contains(ACCESSIBILITY_SERVICE_NAME)) { + Log.d(TAG, "Accessibility service has been enabled.") + continueMainActivityExecution() + } else { + Log.d(TAG, "Accessibility service is still not enabled.") + } + } + } + checkAndRequestAccessibilityPermission() - // 启动 InterstitialActivity 来显示广告 - showInterstitialAd() - } - private fun showInterstitialAd() { - val intent = Intent(this, InterstitialActivity::class.java) - startActivity(intent) - } - // MaxAdViewAdListener implementations - override fun onAdLoaded(ad: MaxAd) { - Log.d(TAG, "Ad loaded: ${ad.adUnitId}") + // 显示插页式广告 + //InterstitialAdManager.showAd(this) } - override fun onAdDisplayed(ad: MaxAd) { - Log.d(TAG, "Ad displayed: ${ad.adUnitId}") + override fun onDestroy() { + super.onDestroy() + // 解除服务绑定 + unbindService(connection) } - override fun onAdHidden(ad: MaxAd) { - Log.d(TAG, "Ad hidden: ${ad.adUnitId}") - } - - override fun onAdClicked(ad: MaxAd) { - Log.d(TAG, "Ad clicked: ${ad.adUnitId}") - } - - override fun onAdLoadFailed(ad: String, error: MaxError) { - Log.e(TAG, "Ad load failed for $ad: ${error.message}") - } - - override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) { - Log.e(TAG, "Ad display failed for ${ad.adUnitId}: ${error.message}") - } - - override fun onAdExpanded(ad: MaxAd) { - Log.d(TAG, "Ad expanded: ${ad.adUnitId}") - } - - override fun onAdCollapsed(ad: MaxAd) { - Log.d(TAG, "Ad collapsed: ${ad.adUnitId}") + // 调用点击功能的方法 + fun simulateTap(x: Int, y: Int) { + accessibilityService?.simulateTap(x, y) } } @Composable fun GAIDInfo(gaid: String?) { - if (gaid != null) { - Text(text = "GAID: $gaid") - } else { - Text(text = "Loading GAID...") - } + Text(text = gaid?: "Loading GAID...") } @Composable -fun BannerAdView(adUnitId: String, listener: MaxAdViewAdListener) { +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(listener) + 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() // You might not need fillMaxWidth here anymore + modifier = modifier + .fillMaxWidth() .height(80.dp) - //.wrapContentHeight() ) -} \ No newline at end of file +} + +object InterstitialAdManager { + fun showAd(activity: ComponentActivity) { + val intent = Intent(activity, InterstitialActivity::class.java) + activity.startActivity(intent) + } +} diff --git a/app/src/main/java/io/sixminutes/breakingnews/MyAccessibiltyService.kt b/app/src/main/java/io/sixminutes/breakingnews/MyAccessibiltyService.kt new file mode 100644 index 0000000..145afc1 --- /dev/null +++ b/app/src/main/java/io/sixminutes/breakingnews/MyAccessibiltyService.kt @@ -0,0 +1,85 @@ +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() + + inner class MyBinder : Binder() { + fun getService(): MyAccessibilityService? { + Log.d(TAG, "Binder getService called") + return serviceInstance + } + } + + override fun onServiceConnected() { + super.onServiceConnected() + serviceInstance = this + Log.d(TAG, "Service connected") + } + + override fun onInterrupt() { + Log.d(TAG, "Service interrupted") + } + + override fun onAccessibilityEvent(event: AccessibilityEvent?) { + // 处理无障碍事件 + } + + override fun onUnbind(intent: Intent?): Boolean { + serviceInstance = null + return super.onUnbind(intent) + } + + companion object { + private var instance: MyAccessibilityService? = null + + fun getInstance(): MyAccessibilityService? { + return instance + } + } + + fun simulateTap(x: Int, y: Int) { + val rootNode = AccessibilityNodeInfoCompat.wrap(rootInActiveWindow) + if (rootNode!= null) { + val clickableNodes = ArrayList() + 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) { + 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() + } + } + } +} diff --git a/app/src/main/res/xml/accessibility_service_config.xml b/app/src/main/res/xml/accessibility_service_config.xml new file mode 100644 index 0000000..1658387 --- /dev/null +++ b/app/src/main/res/xml/accessibility_service_config.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file