diff --git a/.gitignore b/.gitignore
index 4206e035..f9a74bb8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@ build/
*.exe
.idea/caches/build_file_checksums.ser
.idea/
+*.log
diff --git a/app/build.gradle b/app/build.gradle
index 392cf785..882e67d4 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,6 +6,9 @@ apply plugin: 'com.jakewharton.butterknife'
def AAVersion = '4.8.0'
def SupportLibVersion = '28.0.0'
def homePath = System.properties['user.home']
+def storePasswd = System.getenv('autojspasswd')
+def keyPasswd = System.getenv('autojspasswd')
+def alias = System.getenv('autojsalias')
configurations.all {
resolutionStrategy {
@@ -18,9 +21,9 @@ android {
signingConfigs {
release {
storeFile file(homePath + '/auto-js-t-pkcs12.jks')
- storePassword System.getenv('autojspasswd')
- keyPassword System.getenv('autojspasswd')
- keyAlias = System.getenv('autojsalias')
+ storePassword storePasswd
+ keyPassword keyPasswd
+ keyAlias = alias
}
debug {
storeFile file('debug/autojs-pkcs12-debug.jks')
@@ -53,7 +56,7 @@ android {
shrinkResources false
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- signingConfig signingConfigs.release
+ signingConfig signingConfigs.debug
applicationIdSuffix ".debug"
}
release {
diff --git a/app/src/main/assets/sample/复杂界面/应用浏览器.js b/app/src/main/assets/sample/复杂界面/应用浏览器.js
new file mode 100644
index 00000000..4ed2be89
--- /dev/null
+++ b/app/src/main/assets/sample/复杂界面/应用浏览器.js
@@ -0,0 +1,74 @@
+"ui";
+
+let pm = context.getPackageManager();
+let iconCache = {};
+
+let IconView = (function () {
+ // 继承ui.Widget
+ util.extend(IconView, ui.Widget);
+
+ function IconView() {
+ // 调用父类构造函数
+ ui.Widget.call(this);
+ // 自定义属性packageName
+ this.defineAttr("packageName", (view, name, defaultGetter) => {
+ return this._packageName;
+ }, (view, name, value, defaultSetter) => {
+ this._packageName = value;
+ view.setImageDrawable(iconCache[value]);
+ });
+ }
+ IconView.prototype.render = function () {
+ return (
+
+ );
+ }
+ ui.registerWidget("icon", IconView);
+ return IconView;
+})();
+
+let apps = [];
+
+ui.layout(
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+ui.apps.setDataSource(apps);
+
+ui.apps.on("item_click", function (item, pos) {
+ toast(util.inspect(item));
+});
+
+// 启动线程来扫描app
+threads.start(function () {
+ listApps(apps);
+ // 切换回UI线程隐藏进度条
+ ui.run(() => {
+ ui.progressbar.attr("visibility", "gone");
+ });
+});
+
+function listApps(apps) {
+ let list = pm.getInstalledPackages(0);
+ list.forEach(pkgInfo => {
+ let appInfo = pm.getApplicationInfo(pkgInfo.packageName, android.content.pm.PackageManager.GET_META_DATA)
+ apps.push({
+ appName: appInfo.loadLabel(pm),
+ packageName: pkgInfo.packageName,
+ versionName: pkgInfo.versionName,
+ versionCode: pkgInfo.versionCode,
+ });
+ iconCache[pkgInfo.packageName] = pkgInfo.applicationInfo.loadIcon(pm)
+ });
+}
\ No newline at end of file
diff --git a/app/src/main/assets/sample/复杂界面/音乐浏览器.js b/app/src/main/assets/sample/复杂界面/音乐浏览器.js
deleted file mode 100644
index 0664fbdc..00000000
--- a/app/src/main/assets/sample/复杂界面/音乐浏览器.js
+++ /dev/null
@@ -1,71 +0,0 @@
-"ui";
-
-var IconView = (function() {
- //继承ui.Widget
- util.extend(IconView, ui.Widget);
-
- function IconView() {
- //调用父类构造函数
- ui.Widget.call(this);
- //自定义属性color,定义按钮颜色
- this.defineAttr("icon", (view, name, defaultGetter) => {
- return this._icon;
- }, (view, name, value, defaultSetter) => {
- this._icon = value;
- view.setImageResource(value);
- });
- }
- IconView.prototype.render = function() {
- return (
-
- );
- }
- ui.registerWidget("icon", IconView);
- return IconView;
-})();
-
-var apps = [];
-
-ui.layout(
-
-
-
-
-
-
-
-
-
-
-
-
-);
-
-ui.apps.setDataSource(musicFiles);
-
-ui.apps.on("item_click", function(item, pos){
- toast(item);
-});
-
-//启动线程来扫描音乐文件
-threads.start(function () {
- listApps(apps);
- ui.run(()=> {
- ui.progressbar.setVisility(8);
- });
-});
-
-function listApps(apps) {
- var pm = context.getPackageManager();
- let list = pm.getInstalledPackages(0);
- for(let i = 0; i < list.size(); i++){
- let p = list.get(i);
- apps.push({
- appName: p.applicationInfo.loadLabel(pm).toString(),
- packageName: p.packageName,
- versionName: p.versionName,
- versionCode: p.versionCode,
- icon: p.applicationInfo.loadIcon(pm)
- });
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/autojs/autojs/ui/doc/DocsFragment.java b/app/src/main/java/org/autojs/autojs/ui/doc/DocsFragment.java
index aa5fea4c..aff024a7 100644
--- a/app/src/main/java/org/autojs/autojs/ui/doc/DocsFragment.java
+++ b/app/src/main/java/org/autojs/autojs/ui/doc/DocsFragment.java
@@ -53,6 +53,8 @@ public class DocsFragment extends ViewPagerFragment implements BackPressedHandle
@AfterViews
void setUpViews() {
mWebView = mEWebView.getWebView();
+ mWebView.getSettings().setSupportZoom(false);
+ mWebView.getSettings().setBuiltInZoomControls(false);
mEWebView.getSwipeRefreshLayout().setOnRefreshListener(() -> {
if (TextUtils.equals(mWebView.getUrl(), mIndexUrl)) {
loadUrl();
diff --git a/autojs/src/main/java/com/stardust/autojs/AutoJs.java b/autojs/src/main/java/com/stardust/autojs/AutoJs.java
index ef789b07..320d00bb 100644
--- a/autojs/src/main/java/com/stardust/autojs/AutoJs.java
+++ b/autojs/src/main/java/com/stardust/autojs/AutoJs.java
@@ -101,9 +101,9 @@ public abstract class AutoJs {
return exception;
});
ResourceMonitor.setUnclosedResourceDetectedHandler(mGlobalConsole::error);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- mContext.startForegroundService(new Intent(mContext, CaptureForegroundService.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- }
+// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+// mContext.startForegroundService(new Intent(mContext, CaptureForegroundService.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+// }
}
public abstract void ensureAccessibilityServiceEnabled();
diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.java b/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.java
index ae75bd13..b86a6bbb 100644
--- a/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.java
+++ b/autojs/src/main/java/com/stardust/autojs/core/image/capture/CaptureForegroundService.java
@@ -49,6 +49,7 @@ public class CaptureForegroundService extends Service {
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Recording")
+ .setContentText("本通知为截图权限需要")
.setSmallIcon(R.drawable.autojs_material)
.setWhen(System.currentTimeMillis())
.setContentIntent(contentIntent)
diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/capture/GlobalScreenCapture.java b/autojs/src/main/java/com/stardust/autojs/core/image/capture/GlobalScreenCapture.java
new file mode 100644
index 00000000..59ce69f4
--- /dev/null
+++ b/autojs/src/main/java/com/stardust/autojs/core/image/capture/GlobalScreenCapture.java
@@ -0,0 +1,292 @@
+package com.stardust.autojs.core.image.capture;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.Image;
+import android.media.ImageReader;
+import android.media.projection.MediaProjection;
+import android.media.projection.MediaProjectionManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.OrientationEventListener;
+
+import androidx.annotation.Nullable;
+
+import com.stardust.autojs.runtime.exception.ScriptException;
+import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
+import com.stardust.lang.ThreadCompat;
+import com.stardust.util.ScreenMetrics;
+
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Created by TonyJiangWJ on 2022/1/22
+ */
+public class GlobalScreenCapture {
+ public static final int ORIENTATION_AUTO = Configuration.ORIENTATION_UNDEFINED;
+ public static final int ORIENTATION_LANDSCAPE = Configuration.ORIENTATION_LANDSCAPE;
+ public static final int ORIENTATION_PORTRAIT = Configuration.ORIENTATION_PORTRAIT;
+
+ private static final String TAG = "GlobalScreenCapture";
+ private final ConcurrentHashMap registeredThreads = new ConcurrentHashMap<>();
+
+ private MediaProjectionManager mProjectionManager;
+ private ImageReader mImageReader;
+ private MediaProjection mMediaProjection;
+ private VirtualDisplay mVirtualDisplay;
+ private volatile Looper mImageAcquireLooper;
+ private volatile Image mUnderUsingImage;
+ private Context mContext;
+ private Intent mData;
+ private Handler mHandler;
+ private final AtomicReference mCachedImage = new AtomicReference<>();
+ private volatile Exception mException;
+
+ private final int mScreenDensity;
+ private int mOrientation = -1;
+ private int mDetectedOrientation;
+ private OrientationEventListener mOrientationEventListener;
+
+ private boolean hasPermission;
+ private boolean noRegister;
+
+ private GlobalScreenCapture() {
+ mScreenDensity = ScreenMetrics.getDeviceScreenDensity();
+ }
+
+ private static class Holder {
+ @SuppressLint("StaticFieldLeak")
+ private final static GlobalScreenCapture INSTANCE = new GlobalScreenCapture();
+ }
+
+ public static GlobalScreenCapture getInstance() {
+ return Holder.INSTANCE;
+ }
+
+ public synchronized void initCapture(Context context, Intent data, int orientation) {
+ Log.d(TAG, "initCapture: ");
+ mProjectionManager = (MediaProjectionManager) context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
+ mMediaProjection = mProjectionManager.getMediaProjection(Activity.RESULT_OK, (Intent) data.clone());
+ mContext = context;
+ mData = (Intent) data.clone();
+ new Thread(() -> {
+ Looper.prepare();
+ mHandler = new Handler(Looper.myLooper());
+ synchronized (GlobalScreenCapture.this) {
+ GlobalScreenCapture.this.notifyAll();
+ }
+ Looper.loop();
+ }).start();
+ synchronized (this) {
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ throw new ScriptInterruptedException();
+ }
+ }
+ observeOrientation();
+ setOrientation(orientation);
+ hasPermission = true;
+ }
+
+ public synchronized boolean hasPermission() {
+ return hasPermission;
+ }
+
+ public void setOrientation(int orientation) {
+ if (mOrientation == orientation) {
+ return;
+ }
+ mOrientation = orientation;
+ mDetectedOrientation = mContext.getResources().getConfiguration().orientation;
+ refreshVirtualDisplay(mOrientation == ORIENTATION_AUTO ? mDetectedOrientation : mOrientation);
+ }
+
+
+ private void observeOrientation() {
+ mOrientationEventListener = new OrientationEventListener(mContext) {
+ @Override
+ public void onOrientationChanged(int o) {
+ int orientation = mContext.getResources().getConfiguration().orientation;
+ if (mOrientation == ORIENTATION_AUTO && mDetectedOrientation != orientation) {
+ mDetectedOrientation = orientation;
+ try {
+ refreshVirtualDisplay(orientation);
+ } catch (Exception e) {
+ e.printStackTrace();
+ mException = e;
+ }
+ }
+ }
+
+ };
+ if (mOrientationEventListener.canDetectOrientation()) {
+ mOrientationEventListener.enable();
+ }
+ }
+
+ private void refreshVirtualDisplay(int orientation) {
+ if (mImageAcquireLooper != null) {
+ mImageAcquireLooper.quit();
+ }
+ if (mImageReader != null) {
+ mImageReader.close();
+ }
+ if (mVirtualDisplay != null) {
+ mVirtualDisplay.release();
+ }
+ try {
+ mMediaProjection = mProjectionManager.getMediaProjection(Activity.RESULT_OK, (Intent) mData.clone());
+ } catch (Exception e) {
+ Log.d(TAG, "refreshVirtualDisplay: 获取新projection失败 可能只是MIUI的bug " + e);
+ }
+ int screenHeight = ScreenMetrics.getOrientationAwareScreenHeight(orientation);
+ int screenWidth = ScreenMetrics.getOrientationAwareScreenWidth(orientation);
+ initVirtualDisplay(screenWidth, screenHeight, mScreenDensity);
+ startAcquireImageLoop();
+ }
+
+ @SuppressLint("WrongConstant")
+ private void initVirtualDisplay(int width, int height, int screenDensity) {
+ mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 3);
+ try {
+ mVirtualDisplay = mMediaProjection.createVirtualDisplay(TAG,
+ width, height, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ mImageReader.getSurface(), null, null);
+ } catch (SecurityException e) {
+ Log.d(TAG, "initVirtualDisplay: 获取virtualDisplay失败" + e);
+ release();
+ }
+ }
+
+ private void startAcquireImageLoop() {
+ if (mImageReader == null) {
+ // 初始化virtualDisplay异常
+ return;
+ }
+
+ if (mHandler != null) {
+ setImageListener(mHandler);
+ return;
+ }
+ new Thread(() -> {
+ Log.d(TAG, "AcquireImageLoop: start");
+ Looper.prepare();
+ mImageAcquireLooper = Looper.myLooper();
+ setImageListener(new Handler());
+ Looper.loop();
+ Log.d(TAG, "AcquireImageLoop: stop");
+ }).start();
+ }
+
+ private void setImageListener(Handler handler) {
+ mImageReader.setOnImageAvailableListener(reader -> {
+ try {
+ if (noRegister) {
+ return;
+ }
+ Image oldCacheImage = mCachedImage.getAndSet(null);
+ if (oldCacheImage != null) {
+ oldCacheImage.close();
+ }
+ mCachedImage.set(reader.acquireLatestImage());
+ } catch (Exception e) {
+ mException = e;
+ }
+
+ }, handler);
+ }
+
+ @Nullable
+ public synchronized Image capture() {
+ Exception e = mException;
+ if (e != null) {
+ mException = null;
+ throw new ScriptException(e);
+ }
+ Thread thread = ThreadCompat.currentThread();
+ long startTime = System.currentTimeMillis();
+ while (!thread.isInterrupted()) {
+ Image cachedImage = mCachedImage.getAndSet(null);
+ if (cachedImage != null) {
+ if (mUnderUsingImage != null) {
+ mUnderUsingImage.close();
+ }
+ mUnderUsingImage = cachedImage;
+ return cachedImage;
+ }
+ if (System.currentTimeMillis() - startTime > 1000) {
+ startTime = System.currentTimeMillis();
+ this.refreshVirtualDisplay(mOrientation);
+ }
+ }
+ throw new ScriptInterruptedException();
+ }
+
+ public synchronized void unregister(Looper looper) {
+ Log.d(TAG, "unregister: " + looper.getThread().getName());
+ registeredThreads.remove(looper.getThread());
+ Iterator keyThreads = registeredThreads.keySet().iterator();
+ while (keyThreads.hasNext()) {
+ Thread thread = keyThreads.next();
+ if (!thread.isAlive()) {
+ keyThreads.remove();
+ }
+ }
+ noRegister = registeredThreads.size() == 0;
+ if (noRegister) {
+ Log.d(TAG, "全部引擎已注销,释放截图权限,清除通知");
+ release();
+ }
+ }
+
+ public synchronized void register(Looper looper) {
+ Log.d(TAG, "新引擎注册:" + looper.getThread().getName() + " hasPermission? " + hasPermission);
+ noRegister = false;
+ registeredThreads.put(looper.getThread(), true);
+ }
+
+ private void release() {
+ noRegister = true;
+ hasPermission = false;
+ if (mImageAcquireLooper != null) {
+ mImageAcquireLooper.quit();
+ mImageAcquireLooper = null;
+ }
+ if (mMediaProjection != null) {
+ mMediaProjection.stop();
+ mMediaProjection = null;
+ }
+ if (mVirtualDisplay != null) {
+ mVirtualDisplay.release();
+ }
+ if (mImageReader != null) {
+ mImageReader.setOnImageAvailableListener(null, null);
+ mImageReader.close();
+ mImageReader = null;
+ }
+ if (mUnderUsingImage != null) {
+ mUnderUsingImage.close();
+ mUnderUsingImage = null;
+ }
+ Image cachedImage = mCachedImage.getAndSet(null);
+ if (cachedImage != null) {
+ cachedImage.close();
+ }
+ if (mOrientationEventListener != null) {
+ mOrientationEventListener.disable();
+ mOrientationEventListener = null;
+ }
+ mContext.stopService(new Intent(mContext, CaptureForegroundService.class));
+ }
+
+}
diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureRequestActivity.java b/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureRequestActivity.java
index 4905b6f1..b71f2c05 100644
--- a/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureRequestActivity.java
+++ b/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureRequestActivity.java
@@ -23,6 +23,9 @@ public class ScreenCaptureRequestActivity extends Activity {
private ScreenCaptureRequester.Callback mCallback;
public static void request(Context context, ScreenCaptureRequester.Callback callback) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ context.startForegroundService(new Intent(context, CaptureForegroundService.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
Intent intent = new Intent(context, ScreenCaptureRequestActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
IntentExtras.newExtras()
diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureRequester.java b/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureRequester.java
index 50508789..70672f55 100644
--- a/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureRequester.java
+++ b/autojs/src/main/java/com/stardust/autojs/core/image/capture/ScreenCaptureRequester.java
@@ -1,6 +1,7 @@
package com.stardust.autojs.core.image.capture;
import android.app.Activity;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.media.projection.MediaProjectionManager;
@@ -78,6 +79,9 @@ public interface ScreenCaptureRequester {
@Override
public void request() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ mActivity.startForegroundService(new Intent(mActivity, CaptureForegroundService.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
mActivity.startActivityForResult(((MediaProjectionManager) mActivity.getSystemService(Context.MEDIA_PROJECTION_SERVICE)).createScreenCaptureIntent(), REQUEST_CODE_MEDIA_PROJECTION);
}
diff --git a/autojs/src/main/java/com/stardust/autojs/core/ui/ViewExtras.java b/autojs/src/main/java/com/stardust/autojs/core/ui/ViewExtras.java
index 351fd3a4..3e9d7700 100644
--- a/autojs/src/main/java/com/stardust/autojs/core/ui/ViewExtras.java
+++ b/autojs/src/main/java/com/stardust/autojs/core/ui/ViewExtras.java
@@ -24,7 +24,7 @@ public class ViewExtras {
public static ViewExtras get(View view) {
ViewExtras extras;
Object tag = view.getTag(R.id.view_tag_view_extras);
- Log.d(LOG_TAG, "view = " + view + ", tag = " + tag);
+// Log.d(LOG_TAG, "view = " + view + ", tag = " + tag);
if (tag instanceof ViewExtras) {
extras = (ViewExtras) tag;
} else {
diff --git a/autojs/src/main/java/com/stardust/autojs/rhino/AndroidContextFactory.java b/autojs/src/main/java/com/stardust/autojs/rhino/AndroidContextFactory.java
index 25252b27..a66568d6 100644
--- a/autojs/src/main/java/com/stardust/autojs/rhino/AndroidContextFactory.java
+++ b/autojs/src/main/java/com/stardust/autojs/rhino/AndroidContextFactory.java
@@ -50,5 +50,11 @@ public class AndroidContextFactory extends ShellContextFactory {
return cx;
}
-
+ @Override
+ protected boolean hasFeature(Context cx, int featureIndex) {
+ if (featureIndex == Context.FEATURE_ENABLE_XML_SECURE_PARSING) {
+ return false;
+ }
+ return super.hasFeature(cx, featureIndex);
+ }
}
diff --git a/autojs/src/main/java/com/stardust/autojs/rhino/InterruptibleAndroidContextFactory.java b/autojs/src/main/java/com/stardust/autojs/rhino/InterruptibleAndroidContextFactory.java
index 07fcc61b..53a0860b 100644
--- a/autojs/src/main/java/com/stardust/autojs/rhino/InterruptibleAndroidContextFactory.java
+++ b/autojs/src/main/java/com/stardust/autojs/rhino/InterruptibleAndroidContextFactory.java
@@ -42,14 +42,14 @@ public class InterruptibleAndroidContextFactory extends AndroidContextFactory {
protected void onContextCreated(Context cx) {
super.onContextCreated(cx);
int i = mContextCount.incrementAndGet();
- Log.d(LOG_TAG, "onContextCreated: count = " + i);
+// Log.d(LOG_TAG, "onContextCreated: count = " + i);
}
@Override
protected void onContextReleased(Context cx) {
super.onContextReleased(cx);
int i = mContextCount.decrementAndGet();
- Log.d(LOG_TAG, "onContextReleased: count = " + i);
+// Log.d(LOG_TAG, "onContextReleased: count = " + i);
}
}
diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java b/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java
index 29f84ae1..5ce138f6 100644
--- a/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java
+++ b/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java
@@ -432,16 +432,14 @@ public class ScriptRuntime {
ignoresException(threads::shutDownAll);
ignoresException(events::recycle);
ignoresException(media::recycle);
+ ignoresException(images::releaseScreenCapturer);
+ ignoresException(images::recycle);
ignoresException(loopers::recycle);
ignoresException(() -> {
if (mRootShell != null) mRootShell.exit();
mRootShell = null;
mShellSupplier = null;
});
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- ignoresException(images::releaseScreenCapturer);
- ignoresException(images::recycle);
- }
ignoresException(sensors::unregisterAll);
sensors = null;
ignoresException(timers::recycle);
diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Images.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Images.java
index 5b0bb196..489d2289 100644
--- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Images.java
+++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Images.java
@@ -12,14 +12,15 @@ import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Base64;
+import android.util.Log;
import android.view.Gravity;
import com.stardust.autojs.annotation.ScriptVariable;
import com.stardust.autojs.core.image.ColorFinder;
import com.stardust.autojs.core.image.ImageWrapper;
import com.stardust.autojs.core.image.TemplateMatching;
+import com.stardust.autojs.core.image.capture.GlobalScreenCapture;
import com.stardust.autojs.core.image.capture.ScreenCaptureRequester;
-import com.stardust.autojs.core.image.capture.ScreenCapturer;
import com.stardust.autojs.core.opencv.Mat;
import com.stardust.autojs.core.opencv.OpenCVHelper;
import com.stardust.autojs.core.ui.inflater.util.Drawables;
@@ -54,13 +55,14 @@ public class Images {
private WeakReference mScriptRuntime;
private ScreenCaptureRequester mScreenCaptureRequester;
- private ScreenCapturer mScreenCapturer;
private Context mContext;
private Image mPreCapture;
private ImageWrapper mPreCaptureImage;
private ScreenMetrics mScreenMetrics;
private volatile boolean mOpenCvInitialized = false;
+ private final static String TAG = "Images";
+
@ScriptVariable
public final ColorFinder colorFinder;
@@ -76,16 +78,23 @@ public class Images {
public ScriptPromiseAdapter requestScreenCapture(int orientation) {
ScriptRuntime.requiresApi(21);
ScriptPromiseAdapter promiseAdapter = new ScriptPromiseAdapter();
- if (mScreenCapturer != null) {
- mScreenCapturer.setOrientation(orientation);
- promiseAdapter.resolve(true);
- return promiseAdapter;
+ if (GlobalScreenCapture.getInstance().hasPermission()) {
+ synchronized (GlobalScreenCapture.getInstance()) {
+ if (GlobalScreenCapture.getInstance().hasPermission()) {
+ Log.d(TAG, "requestScreenCapture hasPermission 直接注册");
+ GlobalScreenCapture.getInstance().setOrientation(orientation);
+ GlobalScreenCapture.getInstance().register(mScriptRuntime.get().loopers.getMainLooper());
+ new Handler(mScriptRuntime.get().loopers.getMainLooper())
+ .post(() -> promiseAdapter.resolve(true));
+ return promiseAdapter;
+ }
+ }
}
- Looper servantLooper = mScriptRuntime.get().loopers.getServantLooper();
+
mScreenCaptureRequester.setOnActivityResultCallback((result, data) -> {
if (result == Activity.RESULT_OK) {
- mScreenCapturer = new ScreenCapturer(mContext, data, orientation, ScreenMetrics.getDeviceScreenDensity(),
- new Handler(servantLooper));
+ GlobalScreenCapture.getInstance().initCapture(mContext, data, orientation);
+ GlobalScreenCapture.getInstance().register(mScriptRuntime.get().loopers.getMainLooper());
promiseAdapter.resolve(true);
} else {
promiseAdapter.resolve(false);
@@ -97,11 +106,12 @@ public class Images {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public synchronized ImageWrapper captureScreen() {
+ Log.d(TAG, "captureScreen: 请求截图 " + mScriptRuntime.get().loopers.getMainLooper().getThread().getName());
ScriptRuntime.requiresApi(21);
- if (mScreenCapturer == null) {
+ if (!GlobalScreenCapture.getInstance().hasPermission()) {
throw new SecurityException("No screen capture permission");
}
- Image capture = mScreenCapturer.capture();
+ Image capture = GlobalScreenCapture.getInstance().capture();
if (capture == mPreCapture && mPreCaptureImage != null) {
return mPreCaptureImage;
}
@@ -262,8 +272,10 @@ public class Images {
}
public void releaseScreenCapturer() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mScreenCapturer != null) {
- mScreenCapturer.release();
+ if (GlobalScreenCapture.getInstance().hasPermission()) {
+ synchronized (GlobalScreenCapture.getInstance()) {
+ GlobalScreenCapture.getInstance().unregister(mScriptRuntime.get().loopers.getMainLooper());
+ }
}
}
diff --git a/autojs/src/main/java/org/mozilla/javascript/VMBridge_custom.java b/autojs/src/main/java/org/mozilla/javascript/VMBridge_custom.java
index ffdeebfc..3f3c465d 100644
--- a/autojs/src/main/java/org/mozilla/javascript/VMBridge_custom.java
+++ b/autojs/src/main/java/org/mozilla/javascript/VMBridge_custom.java
@@ -55,9 +55,11 @@ public class VMBridge_custom extends VMBridge_jdk18 {
} catch (Throwable e) {
e.printStackTrace();
// notify the script thread to exit
- com.stardust.autojs.runtime.ScriptRuntime runtime = engine.getRuntime();
- Log.d(LOG_TAG, "runtime = " + runtime);
- runtime.exit(e);
+ if (engine != null) {
+ com.stardust.autojs.runtime.ScriptRuntime runtime = engine.getRuntime();
+ Log.d(LOG_TAG, "runtime = " + runtime);
+ runtime.exit(e);
+ }
// even if we caught the exception, we must return a value to for the method call.
return defaultValue(method.getReturnType());
}