diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a55a6d90..749903f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -80,6 +80,11 @@ android:enabled="true" android:foregroundServiceType="mediaProjection" /> + + \ No newline at end of file diff --git a/app/src/main/java/com/android/skip/KeepAliveActivity.kt b/app/src/main/java/com/android/skip/KeepAliveActivity.kt index 7dd11144..45401a94 100644 --- a/app/src/main/java/com/android/skip/KeepAliveActivity.kt +++ b/app/src/main/java/com/android/skip/KeepAliveActivity.kt @@ -16,6 +16,10 @@ import com.android.skip.compose.PictureDialog import com.android.skip.compose.ResourceIcon import com.android.skip.compose.RowContent import com.android.skip.compose.ScaffoldPage +import com.android.skip.service.MyAccessibilityService +import com.android.skip.utils.Constants +import com.android.skip.utils.DataStoreUtils +import com.blankj.utilcode.util.ServiceUtils.startService import java.net.URLEncoder class KeepAliveActivity : BaseActivity() { @@ -26,68 +30,91 @@ class KeepAliveActivity : BaseActivity() { finish() } } -} -@Composable -fun KeepAliveInterface(onBackClick: () -> Unit) { - val context = LocalContext.current - val showPicDialog = remember { mutableStateOf(false) } - val showBrowserDialog = remember { mutableStateOf(false) } - ScaffoldPage(stringResource(id = R.string.alive), onBackClick = onBackClick, content = { - PictureDialog(showPicDialog) - FlatButton(content = { - RowContent( - stringResource(id = R.string.alive_power_saving_title), - stringResource(id = R.string.alive_power_saving_subtitle), - { ResourceIcon(iconResource = R.drawable.counter_1) } - ) - }) { - val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) - intent.data = Uri.parse("package:${context.packageName}") - context.startActivity(intent) - } - FlatButton(content = { - RowContent( - stringResource(id = R.string.alive_self_start_title), - stringResource(id = R.string.alive_self_start_subtitle), - { ResourceIcon(iconResource = R.drawable.counter_2) } - ) - }) { - val intent = Intent() - intent.component = ComponentName( - "com.miui.securitycenter", - "com.miui.permcenter.autostart.AutoStartManagementActivity" - ) - context.startActivity(intent) - } - FlatButton(content = { - RowContent( - stringResource(id = R.string.alive_backstage_title), - stringResource(id = R.string.alive_backstage_subtitle), - { ResourceIcon(iconResource = R.drawable.counter_3) } - ) - }) { - showPicDialog.value = true - } - FlatButton(content = { - RowContent( - stringResource(id = R.string.alive_warn_title), - stringResource(id = R.string.alive_warn_subtitle), - { ResourceIcon(iconResource = R.drawable.warning) } + @Composable + fun KeepAliveInterface(onBackClick: () -> Unit) { + val context = LocalContext.current + val showPicDialog = remember { mutableStateOf(false) } + val showBrowserDialog = remember { mutableStateOf(false) } + val checkForegroundAccessibility = remember { + mutableStateOf( + DataStoreUtils.getSyncData(Constants.SKIP_FOREGROUND_ACCESSIBILITY, false), ) - }) { - showBrowserDialog.value = true } - }) + ScaffoldPage(stringResource(id = R.string.alive), onBackClick = onBackClick, content = { + PictureDialog(showPicDialog) + FlatButton(content = { + RowContent( + stringResource(id = R.string.alive_power_saving_title), + stringResource(id = R.string.alive_power_saving_subtitle), + { ResourceIcon(iconResource = R.drawable.counter_1) } + ) + }) { + val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) + intent.data = Uri.parse("package:${context.packageName}") + context.startActivity(intent) + } + FlatButton(content = { + RowContent( + stringResource(id = R.string.alive_self_start_title), + stringResource(id = R.string.alive_self_start_subtitle), + { ResourceIcon(iconResource = R.drawable.counter_2) } + ) + }) { + val intent = Intent() + intent.component = ComponentName( + "com.miui.securitycenter", + "com.miui.permcenter.autostart.AutoStartManagementActivity" + ) + context.startActivity(intent) + } + FlatButton(content = { + RowContent( + stringResource(id = R.string.alive_backstage_title), + stringResource(id = R.string.alive_backstage_subtitle), + { ResourceIcon(iconResource = R.drawable.counter_3) } + ) + }) { + showPicDialog.value = true + } + FlatButton(content = { + RowContent( + stringResource(id = R.string.alive_warn_title), + stringResource(id = R.string.alive_warn_subtitle), + { ResourceIcon(iconResource = R.drawable.warning) } + ) + }) { + showBrowserDialog.value = true + } + FlatButton(content = { + RowContent( + stringResource(id = R.string.alive_foreground_service_title), + stringResource(id = R.string.alive_foreground_service_subtitle), + { ResourceIcon(iconResource = R.drawable.counter_4) }, + checkForegroundAccessibility.value, + { + checkForegroundAccessibility.value = it + DataStoreUtils.putSyncData(Constants.SKIP_FOREGROUND_ACCESSIBILITY, it) + + val intent = Intent(Constants.FOREGROUND_ACCESSIBILITY_RECEIVER_ACTION) + intent.putExtra(Constants.FOREGROUND_ACCESSIBILITY_RECEIVER_ENABLED, it) + intent.setPackage(packageName) + sendBroadcast(intent) + } + ) + }) + }) - val searchContent = Build.MANUFACTURER + stringResource(id = R.string.alive_warn_search_content) - OpenBrowserDialog( - openName = searchContent, - openUrl = createBaiduSearchUrl(searchContent), - showDialog = showBrowserDialog - ) + val searchContent = Build.MANUFACTURER + stringResource(id = R.string.alive_warn_search_content) + OpenBrowserDialog( + openName = searchContent, + openUrl = createBaiduSearchUrl(searchContent), + showDialog = showBrowserDialog + ) + } + + private fun createBaiduSearchUrl(query: String): String { + return "https://www.baidu.com/s?wd=${URLEncoder.encode(query, "UTF-8")}" + } } -fun createBaiduSearchUrl(query: String): String { - return "https://www.baidu.com/s?wd=${URLEncoder.encode(query, "UTF-8")}" -} \ No newline at end of file diff --git a/app/src/main/java/com/android/skip/service/MyAccessibilityService.kt b/app/src/main/java/com/android/skip/service/MyAccessibilityService.kt index 71d1da5e..f5e4507b 100644 --- a/app/src/main/java/com/android/skip/service/MyAccessibilityService.kt +++ b/app/src/main/java/com/android/skip/service/MyAccessibilityService.kt @@ -2,12 +2,17 @@ package com.android.skip.service import android.accessibilityservice.AccessibilityService import android.accessibilityservice.GestureDescription +import android.content.BroadcastReceiver +import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.graphics.Path import android.graphics.Rect +import android.os.Build import android.view.KeyEvent import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo +import androidx.annotation.RequiresApi import com.android.skip.SKIP_LAYOUT_INSPECT import com.android.skip.SKIP_PERMIT_NOTICE import com.android.skip.handler.BoundsHandler @@ -16,8 +21,10 @@ import com.android.skip.handler.TextNodeHandler import com.android.skip.manager.AnalyticsManager import com.android.skip.manager.ToastManager import com.android.skip.manager.WhitelistManager +import com.android.skip.utils.Constants import com.android.skip.utils.DataStoreUtils import com.blankj.utilcode.util.LogUtils +import com.blankj.utilcode.util.ServiceUtils data class MyNode(val node: AccessibilityNodeInfo, val depth: Int) @@ -27,6 +34,7 @@ class MyAccessibilityService : AccessibilityService() { private val boundsHandler = BoundsHandler() private var isLayoutInspect = false private var layoutInspectClassName: String? = null + private lateinit var foregroundAccessibilityReceiver: ForegroundAccessibilityChangeReceiver init { textNodeHandler.setNextHandler(idNodeHandler).setNextHandler(boundsHandler) @@ -59,7 +67,7 @@ class MyAccessibilityService : AccessibilityService() { click(this, rect) } } catch (e: Exception) { - LogUtils.e(e) +// LogUtils.e(e) } finally { AnalyticsManager.increaseScanCount() } @@ -155,4 +163,45 @@ class MyAccessibilityService : AccessibilityService() { false } } + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + override fun onServiceConnected() { + super.onServiceConnected() + + val intentFilter = IntentFilter(Constants.FOREGROUND_ACCESSIBILITY_RECEIVER_ACTION) + foregroundAccessibilityReceiver = ForegroundAccessibilityChangeReceiver() + registerReceiver(foregroundAccessibilityReceiver, intentFilter, RECEIVER_NOT_EXPORTED) + + if (DataStoreUtils.getSyncData(Constants.SKIP_FOREGROUND_ACCESSIBILITY, false)) { + val intent = Intent(this, MyForegroundService::class.java) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(intent); + } else { + startService(intent); + } + } + } + + override fun onDestroy() { + super.onDestroy() + unregisterReceiver(foregroundAccessibilityReceiver) + } + + inner class ForegroundAccessibilityChangeReceiver: BroadcastReceiver() { + override fun onReceive(p0: Context?, p1: Intent?) { + if (p1 != null && p1.action.equals(Constants.FOREGROUND_ACCESSIBILITY_RECEIVER_ACTION)) { + val enabled = p1.getBooleanExtra(Constants.FOREGROUND_ACCESSIBILITY_RECEIVER_ENABLED, false) + val intent = Intent(this@MyAccessibilityService, MyForegroundService::class.java) + if (enabled) { // start foreground service + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(intent); + } else { + startService(intent); + } + } else { // stop foreground service + stopService(intent) + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/android/skip/service/MyForegroundService.kt b/app/src/main/java/com/android/skip/service/MyForegroundService.kt new file mode 100644 index 00000000..9be06b80 --- /dev/null +++ b/app/src/main/java/com/android/skip/service/MyForegroundService.kt @@ -0,0 +1,40 @@ +package com.android.skip.service + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.Service +import android.content.Context +import android.content.Intent +import android.graphics.BitmapFactory +import android.os.Build +import android.os.IBinder +import androidx.core.app.NotificationCompat +import com.android.skip.NewMainActivity +import com.android.skip.R + +class MyForegroundService: Service() { + override fun onBind(p0: Intent?): IBinder? { + TODO("Not yet implemented") + } + + override fun onCreate() { + super.onCreate() + + val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channel = NotificationChannel("skip_foreground_service", "SKIP 前台服务", NotificationManager.IMPORTANCE_DEFAULT) + manager.createNotificationChannel(channel) + } + val it = Intent(this, NewMainActivity::class.java) + val pi = PendingIntent.getActivity(this, 0, it, PendingIntent.FLAG_IMMUTABLE) + val notification = NotificationCompat.Builder(this, "skip_foreground_service") + .setContentTitle("SKIP 前台服务") + .setContentText("SKIP 前台服务运行中") + .setSmallIcon(R.drawable.warning) + .setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.warning)) + .setContentIntent(pi) + .build() + startForeground(1, notification) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/android/skip/utils/Constants.kt b/app/src/main/java/com/android/skip/utils/Constants.kt new file mode 100644 index 00000000..9cdcfa08 --- /dev/null +++ b/app/src/main/java/com/android/skip/utils/Constants.kt @@ -0,0 +1,9 @@ +package com.android.skip.utils + +object Constants { + const val FOREGROUND_ACCESSIBILITY_RECEIVER_ACTION = "SKIP_FOREGROUND_ACCESSIBILITY_RECEIVER_ACTION" + + const val FOREGROUND_ACCESSIBILITY_RECEIVER_ENABLED = "SKIP_FOREGROUND_ACCESSIBILITY_RECEIVER_ENABLED" + + const val SKIP_FOREGROUND_ACCESSIBILITY = "SKIP_FOREGROUND_ACCESSIBILITY" +} \ No newline at end of file diff --git a/app/src/main/res/drawable/counter_4.xml b/app/src/main/res/drawable/counter_4.xml new file mode 100644 index 00000000..2e801eba --- /dev/null +++ b/app/src/main/res/drawable/counter_4.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3a1bd048..66ecb942 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,6 +38,8 @@ 请注意 以上操作方法可能仅适用于MIUI或HyperOS,其他系统手机请自行查询相应的应用保活方法 如何实现应用后台保活 + 前台服务 + 是否允许以前台服务方式运行无障碍服务 应用白名单 diff --git a/gui/svg/counter_4_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg b/gui/svg/counter_4_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg new file mode 100644 index 00000000..3c63a01c --- /dev/null +++ b/gui/svg/counter_4_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg @@ -0,0 +1 @@ + \ No newline at end of file