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..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
@@ -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,11 +15,12 @@
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
+import com.stardust.app.service.AbstractAutoService;
+
import org.autojs.autoxjs.R;
import org.autojs.autojs.ui.main.MainActivity;
-public class ForegroundService extends Service {
-
+public class ForegroundService extends AbstractAutoService {
private static final int NOTIFICATION_ID = 1;
private static final String CHANEL_ID = ForegroundService.class.getName() + ".foreground";
@@ -33,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 4c6da334a..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
@@ -11,33 +11,62 @@ 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.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
@@ -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,11 @@ private fun BottomButtons() {
}
fun exitCompletely(context: Context) {
- if (context is Activity) context.finish()
+ AutoJs.getInstance().scriptEngineService.stopAll()
FloatyWindowManger.hideCircularMenu()
- ForegroundService.stop(context)
context.stopService(Intent(context, FloatyService::class.java))
- AutoJs.getInstance().scriptEngineService.stopAll()
+ stopAllServices()
+ if (context is Activity) context.finish()
}
@Composable
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/AndroidManifest.xml b/autojs/src/main/AndroidManifest.xml
index 5d6224315..825406bc0 100644
--- a/autojs/src/main/AndroidManifest.xml
+++ b/autojs/src/main/AndroidManifest.xml
@@ -12,7 +12,7 @@
-
+
@@ -24,6 +24,7 @@
+
-
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/CaptureForegroundService.kt b/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.kt
index 6f600da75..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
@@ -4,49 +4,58 @@ import android.app.Notification
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.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.app.service.AbstractAutoService
import com.stardust.autojs.R
-import com.stardust.autojs.core.image.capture.ScreenCaptureRequestActivity
/**
* Created by TonyJiangWJ(https://github.com/TonyJiangWJ).
* From [TonyJiangWJ/Auto.js](https://github.com/TonyJiangWJ/Auto.js)
*/
-class CaptureForegroundService : Service() {
+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
}
- override fun onCreate() {
- super.onCreate()
- startForeground(NOTIFICATION_ID, buildNotification())
+ override fun onDestroy() {
+ 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()
@@ -99,20 +108,26 @@ class CaptureForegroundService : Service() {
(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 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 86bc690dd..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
@@ -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
@@ -47,7 +46,7 @@ class ScreenCaptureManager : ScreenCaptureRequester {
Activity.RESULT_OK,
result
)
- CaptureForegroundService.mediaProjection = mediaProjection
+ CaptureForegroundService.setMediaProjection(context, mediaProjection!!)
screenCapture = ScreenCapturer(mediaProjection!!, orientation)
}
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/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/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/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/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/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..f72c502d0
--- /dev/null
+++ b/common/src/main/java/com/stardust/app/service/AbstractAutoService.kt
@@ -0,0 +1,45 @@
+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()
+ }
+ }
+ }
+}
\ No newline at end of file
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