From 95b996d6d19807cfda89c6570e4aef0db9b60e28 Mon Sep 17 00:00:00 2001 From: Colman Date: Wed, 25 Jun 2025 20:32:53 +0800 Subject: [PATCH 1/4] =?UTF-8?q?1=E3=80=81=E4=BF=AE=E5=A4=8DGoogleMLKit?= =?UTF-8?q?=E8=AF=86=E5=88=AB=E5=80=BE=E6=96=9C=E6=96=87=E6=9C=AC=E6=8E=92?= =?UTF-8?q?=E5=BA=8F=E5=BC=82=E5=B8=B8=E9=97=AE=E9=A2=98=EF=BC=8C=E5=8D=87?= =?UTF-8?q?=E7=BA=A7GoogleMLKit=E4=B8=BA16.0.1=E7=A8=B3=E5=AE=9A=E7=89=88?= =?UTF-8?q?=E6=9C=AC=EF=BC=8C=E8=BF=94=E5=9B=9E=E7=BB=93=E6=9E=9C=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0Level4=202=E3=80=81=E4=BF=AE=E5=A4=8DmatchTemplate?= =?UTF-8?q?=E9=87=8A=E6=94=BEmat=E8=B5=84=E6=BA=90=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E5=90=8C=E6=AD=A5=E6=88=AA=E5=9B=BE=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BB=A3=E7=A0=81=203=E3=80=81=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E4=BA=86gradle=E5=88=B08.7=EF=BC=8C=E6=97=A7=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=9C=A8=E6=9E=84=E5=BB=BA34=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E8=AF=AD=E6=B3=95=E5=85=BC=E5=AE=B9=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- autojs/build.gradle.kts | 8 +- .../autojs/core/image/ImageWrapper.java | 9 +- .../autojs/core/image/TemplateMatching.java | 130 ++++++++++-------- .../core/image/capture/ScreenCapturer.kt | 29 ++-- .../autojs/core/mlkit/GoogleMLKitOcrResult.kt | 17 ++- .../autojs/runtime/api/GoogleMLKit.kt | 12 +- build.gradle.kts | 8 +- gradle/libs.versions.toml | 13 +- gradle/wrapper/gradle-wrapper.properties | 3 +- 9 files changed, 132 insertions(+), 97 deletions(-) diff --git a/autojs/build.gradle.kts b/autojs/build.gradle.kts index aa4620d53..758e19dac 100644 --- a/autojs/build.gradle.kts +++ b/autojs/build.gradle.kts @@ -32,7 +32,7 @@ android { dependencies { androidTestImplementation(libs.espresso.core) debugImplementation(libs.leakcanary.android) - implementation(libs.leakcanary.`object`.watcher.android) + implementation(libs.leakcanary.watcher.android) testImplementation(libs.junit) implementation(libs.documentfile) @@ -67,10 +67,6 @@ dependencies { // libs api(fileTree("../app/libs"){include("dx.jar", "rhino-1.7.14-jdk7.jar")}) api("cz.adaptech:tesseract4android:4.1.1") - api("com.google.mlkit:text-recognition:16.0.0-beta5") - api("com.google.mlkit:text-recognition-chinese:16.0.0-beta5") - api("com.google.mlkit:text-recognition-devanagari:16.0.0-beta5") - api("com.google.mlkit:text-recognition-japanese:16.0.0-beta5") - api("com.google.mlkit:text-recognition-korean:16.0.0-beta5") + implementation(libs.bundles.mlkit) } diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/ImageWrapper.java b/autojs/src/main/java/com/stardust/autojs/core/image/ImageWrapper.java index 70c4ebf42..91a5c6130 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/image/ImageWrapper.java +++ b/autojs/src/main/java/com/stardust/autojs/core/image/ImageWrapper.java @@ -3,7 +3,6 @@ import android.graphics.Bitmap; import android.graphics.Color; import android.media.Image; -import android.os.Build; import com.stardust.autojs.core.opencv.Mat; import com.stardust.autojs.core.opencv.OpenCVHelper; @@ -16,7 +15,7 @@ import java.io.FileOutputStream; import java.nio.ByteBuffer; -import androidx.annotation.RequiresApi; +import androidx.annotation.NonNull; /** * Created by Stardust on 2017/11/25. @@ -139,10 +138,7 @@ public Bitmap getBitmap() { } public void recycle() { - if (mBitmap != null) { - mBitmap.recycle(); - mBitmap = null; - } + mBitmap = null; if (mMat != null) { OpenCVHelper.release(mMat); mMat = null; @@ -155,6 +151,7 @@ public void ensureNotRecycled() { throw new IllegalStateException("image has been recycled"); } + @NonNull public ImageWrapper clone() { ensureNotRecycled(); if (mBitmap == null) { diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/TemplateMatching.java b/autojs/src/main/java/com/stardust/autojs/core/image/TemplateMatching.java index 9eb1ae6cc..634ab06b0 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/image/TemplateMatching.java +++ b/autojs/src/main/java/com/stardust/autojs/core/image/TemplateMatching.java @@ -1,5 +1,6 @@ package com.stardust.autojs.core.image; +import android.util.Log; import android.util.TimingLogger; import com.stardust.autojs.core.opencv.OpenCVHelper; @@ -72,70 +73,87 @@ public static Point fastTemplateMatching(Mat img, Mat template, int matchMethod, */ public static List fastTemplateMatching(Mat img, Mat template, int matchMethod, float weakThreshold, float strictThreshold, int maxLevel, int limit) { TimingLogger logger = new TimingLogger(LOG_TAG, "fast_tm"); - if (maxLevel == MAX_LEVEL_AUTO) { - //自动选取金字塔层数 - maxLevel = selectPyramidLevel(img, template); - logger.addSplit("selectPyramidLevel:" + maxLevel); - } - //保存每一轮匹配到模板图片在原图片的位置 - List finalMatchResult = new ArrayList<>(); - List previousMatchResult = Collections.emptyList(); - boolean isFirstMatching = true; - for (int level = maxLevel; level >= 0; level--) { - // 放缩图片 - List currentMatchResult = new ArrayList<>(); - Mat src = getPyramidDownAtLevel(img, level); - Mat currentTemplate = getPyramidDownAtLevel(template, level); - // 如果在上一轮中没有匹配到图片,则考虑是否退出匹配 - if (previousMatchResult.isEmpty()) { - // 如果不是第一次匹配,并且不满足shouldContinueMatching的条件,则直接退出匹配 - if (!isFirstMatching && !shouldContinueMatching(level, maxLevel)) { - break; + // 创建资源回收列表 + List resourcesToRelease = new ArrayList<>(); + + try { + if (maxLevel == MAX_LEVEL_AUTO) { + maxLevel = selectPyramidLevel(img, template); + logger.addSplit("selectPyramidLevel:" + maxLevel); + } + List finalMatchResult = new ArrayList<>(); + List previousMatchResult = Collections.emptyList(); + boolean isFirstMatching = true; + for (int level = maxLevel; level >= 0; level--) { + List currentMatchResult = new ArrayList<>(); + Mat src = getPyramidDownAtLevel(img, level); + Mat currentTemplate = getPyramidDownAtLevel(template, level); + + // +++ 添加到释放列表 +++ + if (src != img) { + resourcesToRelease.add(src); } - Mat matchResult = matchTemplate(src, currentTemplate, matchMethod); - getBestMatched(matchResult, currentTemplate, matchMethod, weakThreshold, currentMatchResult, limit, null); - OpenCVHelper.release(matchResult); - } else { - for (Match match : previousMatchResult) { - // 根据上一轮的匹配点,计算本次匹配的区域 - Rect r = getROI(match.point, src, currentTemplate); - Mat m = new Mat(src, r); - Mat matchResult = matchTemplate(m, currentTemplate, matchMethod); - getBestMatched(matchResult, currentTemplate, matchMethod, weakThreshold, currentMatchResult, limit, r); - OpenCVHelper.release(m); - OpenCVHelper.release(matchResult); + if (currentTemplate != template) { + resourcesToRelease.add(currentTemplate); } - } - if (src != img) - OpenCVHelper.release(src); - if (currentTemplate != template) - OpenCVHelper.release(currentTemplate); - - logger.addSplit("level:" + level + ", result:" + previousMatchResult); - - // 把满足强阈值的点找出来,加到最终结果列表 - if (!currentMatchResult.isEmpty()) { - Iterator iterator = currentMatchResult.iterator(); - while (iterator.hasNext()) { - Match match = iterator.next(); - if (match.similarity >= strictThreshold) { - pyrUp(match.point, level); - finalMatchResult.add(match); - iterator.remove(); + // 如果在上一轮中没有匹配到图片,则考虑是否退出匹配 + if (previousMatchResult.isEmpty()) { + // 如果不是第一次匹配,并且不满足shouldContinueMatching的条件,则直接退出匹配 + if (!isFirstMatching && !shouldContinueMatching(level, maxLevel)) { + break; + } + Mat matchResult = matchTemplate(src, currentTemplate, matchMethod); + resourcesToRelease.add(matchResult); + getBestMatched(matchResult, currentTemplate, matchMethod, weakThreshold, currentMatchResult, limit, null); + } else { + for (Match match : previousMatchResult) { + Rect r = getROI(match.point, src, currentTemplate); + Mat m = new Mat(src, r); + Mat matchResult = matchTemplate(m, currentTemplate, matchMethod); + + // +++ 添加到释放列表 +++ + resourcesToRelease.add(m); + resourcesToRelease.add(matchResult); + + getBestMatched(matchResult, currentTemplate, matchMethod, weakThreshold, currentMatchResult, limit, r); } } - // 如果所有结果都满足强阈值,则退出循环,返回最终结果 - if (currentMatchResult.isEmpty()) { - break; + + logger.addSplit("level:" + level + ", result:" + previousMatchResult); + + // 把满足强阈值的点找出来,加到最终结果列表 + if (!currentMatchResult.isEmpty()) { + Iterator iterator = currentMatchResult.iterator(); + while (iterator.hasNext()) { + Match match = iterator.next(); + if (match.similarity >= strictThreshold) { + pyrUp(match.point, level); + finalMatchResult.add(match); + iterator.remove(); + } + } + // 如果所有结果都满足强阈值,则退出循环,返回最终结果 + if (currentMatchResult.isEmpty()) { + break; + } + } + isFirstMatching = false; + previousMatchResult = currentMatchResult; + } + logger.addSplit("result:" + finalMatchResult); + logger.dumpToLog(); + return finalMatchResult; + } finally { + // 释放所有中间Mat对象 + for (Mat mat : resourcesToRelease) { + try { + OpenCVHelper.release(mat); + } catch (Exception e) { + Log.e(LOG_TAG, "Error releasing Mat resource", e); } } - isFirstMatching = false; - previousMatchResult = currentMatchResult; } - logger.addSplit("result:" + finalMatchResult); - logger.dumpToLog(); - return finalMatchResult; } diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCapturer.kt b/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCapturer.kt index 6c3542b85..b19717c9b 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCapturer.kt +++ b/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCapturer.kt @@ -2,6 +2,7 @@ package com.stardust.autojs.core.image.capture import android.content.Context import android.content.res.Configuration +import android.graphics.Bitmap import android.graphics.PixelFormat import android.hardware.display.DisplayManager import android.hardware.display.VirtualDisplay @@ -13,7 +14,6 @@ import android.os.Looper import android.util.Log import com.stardust.autojs.core.image.ImageWrapper import com.stardust.util.ScreenMetrics -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.withTimeout import kotlinx.coroutines.yield @@ -33,8 +33,7 @@ class ScreenCapturer( private var mVirtualDisplay: VirtualDisplay private var mImageReader: ImageReader - private val mCachedImage = AtomicReference() - private val mCachedImageWrapper = AtomicReference() + private val cachedImageBitmap = AtomicReference() @Volatile var available = true @@ -74,8 +73,6 @@ class ScreenCapturer( private fun refreshVirtualDisplay(orientation: Int) = synchronized(this) { mImageReader.close() - mCachedImage.set(null) - mCachedImageWrapper.set(null) val screenHeight = ScreenMetrics.getOrientationAwareScreenHeight(orientation) val screenWidth = ScreenMetrics.getOrientationAwareScreenWidth(orientation) mImageReader = createImageReader(screenWidth, screenHeight) @@ -86,18 +83,15 @@ class ScreenCapturer( fun capture(): Image? = synchronized(this) { if (!available) throw Exception("ScreenCapturer is not available") val newImage = mImageReader.acquireLatestImage() - if (newImage != null) { - mCachedImage.getAndSet(newImage)?.close() - } return newImage } - suspend fun captureImageWrapper(): ImageWrapper = coroutineScope { + suspend fun captureImageWrapper(): ImageWrapper { var image = capture() - val imageWrapper = mCachedImageWrapper.get() - if (image == null && imageWrapper != null) { + val preBitmap = cachedImageBitmap.get() + if (image == null && preBitmap != null) { Log.i(LOG_TAG, "Using cached image") - return@coroutineScope imageWrapper + return ImageWrapper.ofBitmap(preBitmap) } //在缓存图像均不可用的情况下等待2秒取得截图,否则抛出错误 val newImage = image ?: runCatching { @@ -107,23 +101,22 @@ class ScreenCapturer( image = capture() } yield() - return@withTimeout image!! + image!! } }.getOrElse { - it.printStackTrace() available = false throw Exception("ScreenCapturer timeout") } - val newImageWrapper = ImageWrapper.ofImage(newImage) - mCachedImageWrapper.set(newImageWrapper) - return@coroutineScope newImageWrapper ?: throw Exception("Not available yet ImageWrapper") + val bitmap = ImageWrapper.toBitmap(newImage) + newImage.close() + cachedImageBitmap.set(bitmap) + return ImageWrapper.ofBitmap(bitmap) } fun release() = synchronized(this) { available = false mVirtualDisplay.release() mImageReader.close() - mCachedImage.getAndSet(null)?.close() } @Throws(Throwable::class) diff --git a/autojs/src/main/java/com/stardust/autojs/core/mlkit/GoogleMLKitOcrResult.kt b/autojs/src/main/java/com/stardust/autojs/core/mlkit/GoogleMLKitOcrResult.kt index 991ea26b3..4179d4cf9 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/mlkit/GoogleMLKitOcrResult.kt +++ b/autojs/src/main/java/com/stardust/autojs/core/mlkit/GoogleMLKitOcrResult.kt @@ -2,6 +2,7 @@ package com.stardust.autojs.core.mlkit import android.graphics.Rect import kotlin.math.abs +import kotlin.math.min data class GoogleMLKitOcrResult( val level: Int, @@ -114,11 +115,17 @@ data class GoogleMLKitOcrResult( } override fun compareTo(other: GoogleMLKitOcrResult): Int { - val deviation = (bounds!!.height() / 2f).coerceAtLeast(other.bounds!!.height() / 2f) - return if (abs((bounds.top + bounds.bottom) / 2f - (other.bounds.top + other.bounds.bottom) / 2f) < deviation) { - bounds.left - other.bounds.left - } else { - bounds.bottom - other.bounds.bottom + // 1. 使用固定阈值替代动态偏差(避免破坏传递性) + val yThreshold = min(bounds!!.height(), other.bounds!!.height()) * 0.5f + + // 2. 优先比较垂直位置(确保传递性) + val thisCenterY = (bounds.top + bounds.bottom) / 2f + val otherCenterY = (other.bounds.top + other.bounds.bottom) / 2f + + // 3. 使用稳定的比较逻辑:同水平线:按左边界排序;不同水平线:按垂直中线排序 + return when { + abs(thisCenterY - otherCenterY) < yThreshold -> bounds.left.compareTo(other.bounds.left) + else -> thisCenterY.compareTo(otherCenterY) } } diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/GoogleMLKit.kt b/autojs/src/main/java/com/stardust/autojs/runtime/api/GoogleMLKit.kt index 319b0cfb6..5a31a35ab 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/GoogleMLKit.kt +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/GoogleMLKit.kt @@ -1,7 +1,6 @@ package com.stardust.autojs.runtime.api import android.util.Log -import com.baidu.paddle.lite.demo.ocr.OcrResult import com.google.mlkit.vision.common.InputImage import com.google.mlkit.vision.text.Text import com.google.mlkit.vision.text.TextRecognition @@ -52,7 +51,16 @@ class GoogleMLKit { text = e.text, language = e.recognizedLanguage, bounds = e.boundingBox, - children = null + children = e.symbols.map { symbol -> + GoogleMLKitOcrResult( + level = 4, + confidence = symbol.confidence, + text = symbol.text, + language = e.recognizedLanguage, + bounds = symbol.boundingBox, + children = null + ) + } ) } ) diff --git a/build.gradle.kts b/build.gradle.kts index 43fc4821a..3aadcff85 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,7 +19,7 @@ buildscript { mavenCentral { url = uri("https://maven.aliyun.com/repository/public") } } dependencies { - classpath("com.android.tools.build:gradle:8.0.2") + classpath("com.android.tools.build:gradle:8.5.0") classpath(kotlin("gradle-plugin", version = "$kotlin_version")) classpath("com.jakewharton:butterknife-gradle-plugin:10.2.3") classpath("org.codehaus.groovy:groovy-json:3.0.8") @@ -39,6 +39,12 @@ allprojects { google { url = uri("https://maven.aliyun.com/repository/google") } mavenCentral { url = uri("https://maven.aliyun.com/repository/public") } } + configurations.all { + resolutionStrategy { + // 强制指定 androidx.core 版本 + force("androidx.core:core:1.8.0") + } + } // tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class.java){ // kotlinOptions{ // freeCompilerArgs = freeCompilerArgs.toMutableList().apply { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 84b4f0160..54bdb64b4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,6 +13,7 @@ espresso-core = "3.5.1" appcompat = "1.4.2" material = "1.9.0" accompanist-version = "0.24.13-rc" +mlkit-version = "16.0.1" [libraries] @@ -65,7 +66,7 @@ rxjava3 = "io.reactivex.rxjava3:rxjava:3.1.5" rxjava3-rxandroid = "io.reactivex.rxjava3:rxandroid:3.0.2" #leakcanary leakcanary-android = "com.squareup.leakcanary:leakcanary-android:2.13" -leakcanary-object-watcher-android = "com.squareup.leakcanary:leakcanary-object-watcher-android:2.13" +leakcanary-watcher-android = "com.squareup.leakcanary:leakcanary-object-watcher-android:2.13" androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" } espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } @@ -74,8 +75,16 @@ material = { group = "com.google.android.material", name = "material", version.r andserver-processor = { module = "com.yanzhenjie.andserver:processor", version.ref = "andserver" } andserver-api = { module = "com.yanzhenjie.andserver:api", version.ref = "andserver" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.1.4" } + +# mlkit +mlkit-text-recognition = { module = "com.google.mlkit:text-recognition", version.ref = "mlkit-version" } +mlkit-text-recognition-chinese = { module = "com.google.mlkit:text-recognition-chinese", version.ref = "mlkit-version" } +mlkit-text-recognition-devanagari = { module = "com.google.mlkit:text-recognition-devanagari", version.ref = "mlkit-version" } +mlkit-text-recognition-japanese = { module = "com.google.mlkit:text-recognition-japanese", version.ref = "mlkit-version" } +mlkit-text-recognition-korean = { module = "com.google.mlkit:text-recognition-korean", version.ref = "mlkit-version" } [plugins] [bundles] ktor = ["ktor-server-core", "ktor-server-netty", "ktor-server-websockets", "ktor-client-websockets", "ktor-client-okhttp", "ktor-client-core"] -accompanist = ["accompanist-webview", "accompanist-systemuicontroller", "accompanist-insets-ui", "accompanist-insets", "accompanist-appcompat-theme", "accompanist-swiperefresh", "accompanist-pager", "accompanist-pager-indicators", "accompanist-permissions"] \ No newline at end of file +accompanist = ["accompanist-webview", "accompanist-systemuicontroller", "accompanist-insets-ui", "accompanist-insets", "accompanist-appcompat-theme", "accompanist-swiperefresh", "accompanist-pager", "accompanist-pager-indicators", "accompanist-permissions"] +mlkit = ["mlkit-text-recognition", "mlkit-text-recognition-chinese", "mlkit-text-recognition-devanagari", "mlkit-text-recognition-japanese", "mlkit-text-recognition-korean"] \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 81430cbc6..c4bccbfee 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +#distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.7-all.zip From 4fc58e75028a88d2a7ba33df1baa022be0917068 Mon Sep 17 00:00:00 2001 From: Colman Date: Thu, 3 Jul 2025 00:20:09 +0800 Subject: [PATCH 2/4] =?UTF-8?q?1=E3=80=81=E4=BF=AE=E5=A4=8D=E5=BE=AA?= =?UTF-8?q?=E7=8E=AF=E8=84=9A=E6=9C=AC=E4=B8=8D=E8=83=BD=E5=81=9C=E6=AD=A2?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98=202=E3=80=81=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=E6=A0=8F=EF=BC=8C=E4=BF=AE=E5=A4=8D=E6=88=AA?= =?UTF-8?q?=E5=9B=BE=E6=9C=8D=E5=8A=A1=E6=97=A0=E6=B3=95=E9=80=80=E5=87=BA?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=88=E6=88=AA=E5=9B=BE=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=90=8E=E5=8F=B0=E6=9C=8D=E5=8A=A1=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../foreground/ForegroundService.java | 5 +- .../autojs/ui/main/drawer/DrawerPage.kt | 57 +++++++++++--- autojs/src/main/AndroidManifest.xml | 3 +- .../image/capture/CaptureForegroundService.kt | 77 +------------------ .../image/capture/ScreenCaptureManager.kt | 3 +- .../stardust/autojs/core/looper/Loopers.kt | 6 ++ .../engine/LoopBasedJavaScriptEngine.java | 1 + .../foreground/AbstractBroadcastService.kt | 54 +++++++++++++ 8 files changed, 115 insertions(+), 91 deletions(-) create mode 100644 common/src/main/java/com/stardust/app/foreground/AbstractBroadcastService.kt diff --git a/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java b/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java index bfde4685f..56a79f80f 100644 --- a/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java +++ b/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java @@ -6,7 +6,6 @@ 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.os.Build; @@ -16,10 +15,12 @@ import androidx.annotation.RequiresApi; import androidx.core.app.NotificationCompat; +import com.stardust.app.foreground.AbstractBroadcastService; + import org.autojs.autoxjs.R; import org.autojs.autojs.ui.main.MainActivity; -public class ForegroundService extends Service { +public class ForegroundService extends AbstractBroadcastService { private static final int NOTIFICATION_ID = 1; diff --git a/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerPage.kt b/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerPage.kt index 4c6da334a..388066d94 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerPage.kt +++ b/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerPage.kt @@ -11,29 +11,58 @@ import android.widget.TextView import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.* -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.windowInsetsBottomHeight +import androidx.compose.foundation.layout.windowInsetsTopHeight +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.* +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.AlertDialog +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.RadioButton +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material.TextButton import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.ExitToApp import androidx.compose.material.icons.filled.Notifications import androidx.compose.material.icons.filled.Settings -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.window.Dialog import androidx.lifecycle.viewmodel.compose.viewModel import androidx.preference.PreferenceManager -import coil.compose.rememberAsyncImagePainter import com.stardust.app.GlobalAppContext +import com.stardust.app.foreground.AbstractBroadcastService import com.stardust.app.isOpPermissionGranted import com.stardust.app.permission.DrawOverlaysPermission import com.stardust.app.permission.DrawOverlaysPermission.launchCanDrawOverlaysSettings @@ -46,7 +75,11 @@ import com.stardust.view.accessibility.AccessibilityService import io.github.g00fy2.quickie.QRResult import io.github.g00fy2.quickie.ScanQRCode import io.noties.markwon.Markwon -import kotlinx.coroutines.* +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.autojs.autojs.Pref import org.autojs.autojs.autojs.AutoJs import org.autojs.autojs.devplugin.DevPlugin @@ -60,11 +93,10 @@ import org.autojs.autojs.ui.compose.widget.MyIcon import org.autojs.autojs.ui.compose.widget.MySwitch import org.autojs.autojs.ui.floating.FloatyWindowManger import org.autojs.autojs.ui.settings.SettingsActivity +import org.autojs.autoxjs.BuildConfig import org.autojs.autoxjs.R import org.joda.time.DateTimeZone import org.joda.time.Instant -import org.autojs.autoxjs.BuildConfig -import androidx.compose.ui.text.style.TextAlign private const val TAG = "DrawerPage" private const val URL_DEV_PLUGIN = "https://github.com/kkevsekk1/Auto.js-VSCode-Extension" @@ -304,11 +336,14 @@ private fun BottomButtons() { } fun exitCompletely(context: Context) { + AutoJs.getInstance().scriptEngineService.stopAll() if (context is Activity) context.finish() FloatyWindowManger.hideCircularMenu() - ForegroundService.stop(context) context.stopService(Intent(context, FloatyService::class.java)) - AutoJs.getInstance().scriptEngineService.stopAll() + + // 发送广播触发所有服务停止 + val stopIntent = Intent(AbstractBroadcastService.ACTION_STOP_ALL_SERVICES) + context.sendBroadcast(stopIntent) } @Composable diff --git a/autojs/src/main/AndroidManifest.xml b/autojs/src/main/AndroidManifest.xml index 5d6224315..b2ba8d70f 100644 --- a/autojs/src/main/AndroidManifest.xml +++ b/autojs/src/main/AndroidManifest.xml @@ -57,8 +57,7 @@ + android:exported="false" /> diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.kt b/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.kt index 6f600da75..444d00d15 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.kt +++ b/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.kt @@ -1,26 +1,18 @@ package com.stardust.autojs.core.image.capture -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.PendingIntent -import android.app.Service import android.content.Intent import android.media.projection.MediaProjection -import android.os.Build import android.os.Handler import android.os.IBinder import android.util.Log -import androidx.annotation.RequiresApi -import androidx.core.app.NotificationCompat -import com.stardust.autojs.R -import com.stardust.autojs.core.image.capture.ScreenCaptureRequestActivity +import com.stardust.app.foreground.AbstractBroadcastService /** * Created by TonyJiangWJ(https://github.com/TonyJiangWJ). * From [TonyJiangWJ/Auto.js](https://github.com/TonyJiangWJ/Auto.js) */ -class CaptureForegroundService : Service() { +class CaptureForegroundService : AbstractBroadcastService() { + val callback = object : MediaProjection.Callback() { override fun onStop() { stopSelf() @@ -41,78 +33,15 @@ class CaptureForegroundService : Service() { return START_NOT_STICKY } - override fun onCreate() { - super.onCreate() - startForeground(NOTIFICATION_ID, buildNotification()) - } - - - private fun buildNotification(): Notification { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createNotificationChannel() - } - val flags = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0 - val contentIntent = PendingIntent.getActivity( - this, 0, - Intent(this, ScreenCaptureRequestActivity::class.java), flags - ) - return NotificationCompat.Builder(this, CHANNEL_ID) - .setContentTitle(NOTIFICATION_TITLE) - .setSmallIcon(R.drawable.autojs_logo) - .setWhen(System.currentTimeMillis()) - .setContentIntent(contentIntent) - .addAction(createExitAction()) - .setChannelId(CHANNEL_ID) - .setVibrate(LongArray(0)) - .build() - } - - @RequiresApi(api = Build.VERSION_CODES.O) - private fun createNotificationChannel() { - val manager = (getSystemService(NOTIFICATION_SERVICE) as NotificationManager) - val channel = NotificationChannel( - CHANNEL_ID, - NOTIFICATION_TITLE, - NotificationManager.IMPORTANCE_DEFAULT - ) - channel.description = NOTIFICATION_TITLE - channel.enableLights(false) - manager.createNotificationChannel(channel) - } - - private fun createExitAction(): NotificationCompat.Action { - val pendingIntent = PendingIntent.getService( - this, 12, - Intent(this, CaptureForegroundService::class.java).apply { - action = STOP - }, PendingIntent.FLAG_IMMUTABLE - ) - return NotificationCompat.Action.Builder( - null, - "停止截图", - pendingIntent - ).build() - } - - private fun removeNotification() { - (getSystemService(NOTIFICATION_SERVICE) as NotificationManager).cancel(NOTIFICATION_ID) - } - override fun onDestroy() { super.onDestroy() mediaProjection?.unregisterCallback(callback) mediaProjection?.stop() - removeNotification() - stopForeground(true) } companion object { var mediaProjection: MediaProjection? = null private const val TAG = "CaptureService" private const val STOP = "STOP_SERVICE" - private const val NOTIFICATION_ID = 2 - private val CHANNEL_ID = CaptureForegroundService::class.java.name + ".foreground" - private const val NOTIFICATION_TITLE = "前台截图服务运行中" } } \ No newline at end of file diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureManager.kt b/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureManager.kt index 86bc690dd..364adcae5 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureManager.kt +++ b/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureManager.kt @@ -8,9 +8,8 @@ import android.media.projection.MediaProjectionManager import com.stardust.app.OnActivityResultDelegate import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.coroutineScope -import java.util.concurrent.CancellationException -import android.util.Log import kotlinx.coroutines.delay +import java.util.concurrent.CancellationException class ScreenCaptureManager : ScreenCaptureRequester { @Volatile diff --git a/autojs/src/main/java/com/stardust/autojs/core/looper/Loopers.kt b/autojs/src/main/java/com/stardust/autojs/core/looper/Loopers.kt index b31d32558..be99a113a 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/looper/Loopers.kt +++ b/autojs/src/main/java/com/stardust/autojs/core/looper/Loopers.kt @@ -127,6 +127,12 @@ class Loopers(val runtime: ScriptRuntime) { mServantLooper?.quit() } + fun forceStop() { + if (!isUiLooper) { + myLooper.quit() + } + } + @Deprecated("使用AsyncTask代替") fun setMainLooperQuitHandler(mainLooperQuitHandler: LooperQuitHandler?) { mMainLooperQuitHandler = mainLooperQuitHandler diff --git a/autojs/src/main/java/com/stardust/autojs/engine/LoopBasedJavaScriptEngine.java b/autojs/src/main/java/com/stardust/autojs/engine/LoopBasedJavaScriptEngine.java index 533baa47a..f056b164d 100644 --- a/autojs/src/main/java/com/stardust/autojs/engine/LoopBasedJavaScriptEngine.java +++ b/autojs/src/main/java/com/stardust/autojs/engine/LoopBasedJavaScriptEngine.java @@ -72,6 +72,7 @@ public void execute(final ScriptSource source, final ExecuteCallback callback) { @Override public void forceStop() { + getRuntime().loopers.forceStop(); Activity activity = (Activity) getTag("activity"); if (activity != null) { activity.finish(); diff --git a/common/src/main/java/com/stardust/app/foreground/AbstractBroadcastService.kt b/common/src/main/java/com/stardust/app/foreground/AbstractBroadcastService.kt new file mode 100644 index 000000000..63617c6c8 --- /dev/null +++ b/common/src/main/java/com/stardust/app/foreground/AbstractBroadcastService.kt @@ -0,0 +1,54 @@ +package com.stardust.app.foreground + +import android.app.Service +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Build +import androidx.annotation.RequiresApi + +/** + * 支持广播消息的Service + */ +abstract class AbstractBroadcastService : Service() { + private var mReceiver: BroadcastReceiver? = null + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + override fun onCreate() { + super.onCreate() + registerBroadcastReceiver() + } + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + private fun registerBroadcastReceiver() { + mReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + // 响应停止广播 + if (intent.action == ACTION_STOP_ALL_SERVICES) { + stopForeground(STOP_FOREGROUND_REMOVE) + stopSelf() + } else { + onHandleAction(context, intent) + } + } + } + val filter = IntentFilter(ACTION_STOP_ALL_SERVICES) + registerReceiver(mReceiver, filter, RECEIVER_NOT_EXPORTED) + } + + /** + * 个性化广播消息处理 + */ + open fun onHandleAction(context: Context, intent: Intent) {} + + override fun onDestroy() { + super.onDestroy() + // 注销广播 + unregisterReceiver(mReceiver) + } + + companion object { + const val ACTION_STOP_ALL_SERVICES: String = "AUTOX_STOP_ALL_SERVICES" + } +} \ No newline at end of file From 34a3052a3c229ebeefe813b5267c931c231490ff Mon Sep 17 00:00:00 2001 From: Colman Date: Tue, 8 Jul 2025 11:35:37 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=88=AA=E5=9B=BE?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E9=80=80=E5=87=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../foreground/ForegroundService.java | 7 +- .../autojs/ui/main/drawer/DrawerPage.kt | 9 +- autojs/src/main/AndroidManifest.xml | 7 +- .../image/capture/CaptureForegroundService.kt | 110 ++++++++++++++++-- .../image/capture/ScreenCaptureManager.kt | 2 +- .../foreground/AbstractBroadcastService.kt | 54 --------- .../app/service/AbstractAutoService.kt | 46 ++++++++ 7 files changed, 155 insertions(+), 80 deletions(-) delete mode 100644 common/src/main/java/com/stardust/app/foreground/AbstractBroadcastService.kt create mode 100644 common/src/main/java/com/stardust/app/service/AbstractAutoService.kt diff --git a/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java b/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java index 56a79f80f..e8cc40830 100644 --- a/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java +++ b/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java @@ -15,13 +15,12 @@ import androidx.annotation.RequiresApi; import androidx.core.app.NotificationCompat; -import com.stardust.app.foreground.AbstractBroadcastService; +import com.stardust.app.service.AbstractAutoService; import org.autojs.autoxjs.R; import org.autojs.autojs.ui.main.MainActivity; -public class ForegroundService extends AbstractBroadcastService { - +public class ForegroundService extends AbstractAutoService { private static final int NOTIFICATION_ID = 1; private static final String CHANEL_ID = ForegroundService.class.getName() + ".foreground"; @@ -34,7 +33,7 @@ public static void start(Context context) { } } - public static void stop(Context context){ + public static void stop(Context context) { context.stopService(new Intent(context, ForegroundService.class)); } diff --git a/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerPage.kt b/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerPage.kt index 388066d94..d2ae0a271 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerPage.kt +++ b/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerPage.kt @@ -62,11 +62,11 @@ import androidx.compose.ui.window.Dialog import androidx.lifecycle.viewmodel.compose.viewModel import androidx.preference.PreferenceManager import com.stardust.app.GlobalAppContext -import com.stardust.app.foreground.AbstractBroadcastService import com.stardust.app.isOpPermissionGranted import com.stardust.app.permission.DrawOverlaysPermission import com.stardust.app.permission.DrawOverlaysPermission.launchCanDrawOverlaysSettings import com.stardust.app.permission.PermissionsSettingsUtil +import com.stardust.app.service.AbstractAutoService.Companion.stopAllServices import com.stardust.enhancedfloaty.FloatyService import com.stardust.notification.NotificationListenerService import com.stardust.toast @@ -337,13 +337,10 @@ private fun BottomButtons() { fun exitCompletely(context: Context) { AutoJs.getInstance().scriptEngineService.stopAll() - if (context is Activity) context.finish() FloatyWindowManger.hideCircularMenu() context.stopService(Intent(context, FloatyService::class.java)) - - // 发送广播触发所有服务停止 - val stopIntent = Intent(AbstractBroadcastService.ACTION_STOP_ALL_SERVICES) - context.sendBroadcast(stopIntent) + stopAllServices() + if (context is Activity) context.finish() } @Composable diff --git a/autojs/src/main/AndroidManifest.xml b/autojs/src/main/AndroidManifest.xml index b2ba8d70f..825406bc0 100644 --- a/autojs/src/main/AndroidManifest.xml +++ b/autojs/src/main/AndroidManifest.xml @@ -12,7 +12,7 @@ - + @@ -24,6 +24,7 @@ + - + android:exported="false" + android:foregroundServiceType="mediaProjection" /> diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.kt b/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.kt index 444d00d15..267c97631 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.kt +++ b/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.kt @@ -1,35 +1,48 @@ package com.stardust.autojs.core.image.capture +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context import android.content.Intent import android.media.projection.MediaProjection +import android.os.Build import android.os.Handler import android.os.IBinder -import android.util.Log -import com.stardust.app.foreground.AbstractBroadcastService +import androidx.annotation.RequiresApi +import androidx.core.app.NotificationCompat +import com.stardust.app.service.AbstractAutoService +import com.stardust.autojs.R /** * Created by TonyJiangWJ(https://github.com/TonyJiangWJ). * From [TonyJiangWJ/Auto.js](https://github.com/TonyJiangWJ/Auto.js) */ -class CaptureForegroundService : AbstractBroadcastService() { +class CaptureForegroundService : AbstractAutoService() { val callback = object : MediaProjection.Callback() { override fun onStop() { - stopSelf() + stopServiceInternal() } } + override fun onCreate() { + super.onCreate() + startForeground(NOTIFICATION_ID, buildNotification()) + } + override fun onBind(intent: Intent): IBinder? { return null } - override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { - super.onStartCommand(intent, flags, startId) - if (intent.action == (STOP)) { - Log.i(TAG, "stopSelf") - stopSelf() + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + val action = intent?.action + when (action) { + STOP -> stopServiceInternal() + REGISTER -> mediaProjection?.registerCallback(callback, Handler(mainLooper)) } - mediaProjection?.registerCallback(callback, Handler(mainLooper)) + super.onStartCommand(intent, flags, startId) return START_NOT_STICKY } @@ -37,11 +50,84 @@ class CaptureForegroundService : AbstractBroadcastService() { super.onDestroy() mediaProjection?.unregisterCallback(callback) mediaProjection?.stop() + removeNotification() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + stopForeground(STOP_FOREGROUND_REMOVE) + } + } + + private fun buildNotification(): Notification { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel() + } + val flags = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0 + val contentIntent = PendingIntent.getActivity( + this, 0, + Intent(this, ScreenCaptureRequestActivity::class.java), flags + ) + return NotificationCompat.Builder(this, CHANNEL_ID) + .setContentTitle(NOTIFICATION_TITLE) + .setSmallIcon(R.drawable.autojs_logo) + .setWhen(System.currentTimeMillis()) + .setContentIntent(contentIntent) + .addAction(createExitAction()) + .setChannelId(CHANNEL_ID) + .setVibrate(LongArray(0)) + .build() + } + + @RequiresApi(api = Build.VERSION_CODES.O) + private fun createNotificationChannel() { + val manager = (getSystemService(NOTIFICATION_SERVICE) as NotificationManager) + val channel = NotificationChannel( + CHANNEL_ID, + NOTIFICATION_TITLE, + NotificationManager.IMPORTANCE_DEFAULT + ) + channel.description = NOTIFICATION_TITLE + channel.enableLights(false) + manager.createNotificationChannel(channel) + } + + private fun createExitAction(): NotificationCompat.Action { + val pendingIntent = PendingIntent.getService( + this, 12, + Intent(this, CaptureForegroundService::class.java).apply { + action = STOP + }, PendingIntent.FLAG_IMMUTABLE + ) + return NotificationCompat.Action.Builder( + null, + "停止截图", + pendingIntent + ).build() + } + + private fun removeNotification() { + (getSystemService(NOTIFICATION_SERVICE) as NotificationManager).cancel(NOTIFICATION_ID) } + companion object { - var mediaProjection: MediaProjection? = null - private const val TAG = "CaptureService" + private var mediaProjection: MediaProjection? = null private const val STOP = "STOP_SERVICE" + private const val REGISTER = "REGISTER_CALLBACK" + private const val NOTIFICATION_ID = 2 + private val CHANNEL_ID = CaptureForegroundService::class.java.name + ".foreground" + private const val NOTIFICATION_TITLE = "前台截图服务运行中" + + + fun setMediaProjection(context: Context, media: MediaProjection) { + mediaProjection = media + val intent = Intent(context, CaptureForegroundService::class.java).apply { + action = REGISTER + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(intent) + } else { + context.startService(intent) + } + } } } \ No newline at end of file diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureManager.kt b/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureManager.kt index 364adcae5..f009d6c5e 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureManager.kt +++ b/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureManager.kt @@ -46,7 +46,7 @@ class ScreenCaptureManager : ScreenCaptureRequester { Activity.RESULT_OK, result ) - CaptureForegroundService.mediaProjection = mediaProjection + CaptureForegroundService.setMediaProjection(context, mediaProjection!!) screenCapture = ScreenCapturer(mediaProjection!!, orientation) } diff --git a/common/src/main/java/com/stardust/app/foreground/AbstractBroadcastService.kt b/common/src/main/java/com/stardust/app/foreground/AbstractBroadcastService.kt deleted file mode 100644 index 63617c6c8..000000000 --- a/common/src/main/java/com/stardust/app/foreground/AbstractBroadcastService.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.stardust.app.foreground - -import android.app.Service -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.os.Build -import androidx.annotation.RequiresApi - -/** - * 支持广播消息的Service - */ -abstract class AbstractBroadcastService : Service() { - private var mReceiver: BroadcastReceiver? = null - - @RequiresApi(Build.VERSION_CODES.TIRAMISU) - override fun onCreate() { - super.onCreate() - registerBroadcastReceiver() - } - - @RequiresApi(Build.VERSION_CODES.TIRAMISU) - private fun registerBroadcastReceiver() { - mReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - // 响应停止广播 - if (intent.action == ACTION_STOP_ALL_SERVICES) { - stopForeground(STOP_FOREGROUND_REMOVE) - stopSelf() - } else { - onHandleAction(context, intent) - } - } - } - val filter = IntentFilter(ACTION_STOP_ALL_SERVICES) - registerReceiver(mReceiver, filter, RECEIVER_NOT_EXPORTED) - } - - /** - * 个性化广播消息处理 - */ - open fun onHandleAction(context: Context, intent: Intent) {} - - override fun onDestroy() { - super.onDestroy() - // 注销广播 - unregisterReceiver(mReceiver) - } - - companion object { - const val ACTION_STOP_ALL_SERVICES: String = "AUTOX_STOP_ALL_SERVICES" - } -} \ No newline at end of file diff --git a/common/src/main/java/com/stardust/app/service/AbstractAutoService.kt b/common/src/main/java/com/stardust/app/service/AbstractAutoService.kt new file mode 100644 index 000000000..613aff33c --- /dev/null +++ b/common/src/main/java/com/stardust/app/service/AbstractAutoService.kt @@ -0,0 +1,46 @@ +package com.stardust.app.service + +import android.app.Service +import androidx.annotation.CallSuper +import androidx.core.app.ServiceCompat + +abstract class AbstractAutoService : Service() { + @CallSuper + override fun onCreate() { + super.onCreate() + registerService(this) + } + + /** + * 统一停止服务的方法 + */ + protected fun stopServiceInternal() { + ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) + stopSelf() + } + + @CallSuper + override fun onDestroy() { + super.onDestroy() + unregisterService(this) + } + + companion object { + private val runningServices = mutableSetOf() + + internal fun registerService(serviceClass: AbstractAutoService) { + runningServices.add(serviceClass) + } + + internal fun unregisterService(serviceClass: AbstractAutoService) { + runningServices.remove(serviceClass) + } + + fun stopAllServices() { + runningServices.forEach { serviceClass -> + serviceClass.stopServiceInternal() + } + runningServices.clear() + } + } +} \ No newline at end of file From 4d4b541c43109ecb730dd124eb7348f8876c6096 Mon Sep 17 00:00:00 2001 From: Colman Date: Mon, 18 Aug 2025 09:35:37 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=AE=8C=E5=96=84AbstractAutoService?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/stardust/app/service/AbstractAutoService.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/main/java/com/stardust/app/service/AbstractAutoService.kt b/common/src/main/java/com/stardust/app/service/AbstractAutoService.kt index 613aff33c..f72c502d0 100644 --- a/common/src/main/java/com/stardust/app/service/AbstractAutoService.kt +++ b/common/src/main/java/com/stardust/app/service/AbstractAutoService.kt @@ -40,7 +40,6 @@ abstract class AbstractAutoService : Service() { runningServices.forEach { serviceClass -> serviceClass.stopServiceInternal() } - runningServices.clear() } } } \ No newline at end of file