From 673f27d2d3185357a57a5d353fa3f1708cd6f3ee Mon Sep 17 00:00:00 2001 From: TonyJiangWJ Date: Sun, 23 Jan 2022 17:36:39 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=88=AA=E5=9B=BE=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E5=A4=9A=E8=84=9A=E6=9C=AC=E4=B9=8B=E9=97=B4?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E5=85=B1=E4=BA=AB=E6=88=AA=E5=9B=BE=E6=9D=83?= =?UTF-8?q?=E9=99=90=E3=80=82=E8=A2=AB=E5=85=B6=E4=BB=96=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E6=8A=A2=E5=8D=A0=E6=88=AA=E5=9B=BE=E6=9D=83=E9=99=90=E5=90=8E?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E8=87=AA=E5=8A=A8=E9=87=8D=E6=96=B0=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + app/build.gradle | 11 +- .../main/assets/sample/复杂界面/应用浏览器.js | 74 +++++ .../main/assets/sample/复杂界面/音乐浏览器.js | 71 ----- .../autojs/autojs/ui/doc/DocsFragment.java | 2 + .../main/java/com/stardust/autojs/AutoJs.java | 6 +- .../capture/CaptureForegroundService.java | 1 + .../image/capture/GlobalScreenCapture.java | 292 ++++++++++++++++++ .../capture/ScreenCaptureRequestActivity.java | 3 + .../image/capture/ScreenCaptureRequester.java | 4 + .../stardust/autojs/core/ui/ViewExtras.java | 2 +- .../autojs/rhino/AndroidContextFactory.java | 8 +- .../InterruptibleAndroidContextFactory.java | 4 +- .../autojs/runtime/ScriptRuntime.java | 6 +- .../stardust/autojs/runtime/api/Images.java | 38 ++- .../mozilla/javascript/VMBridge_custom.java | 8 +- 16 files changed, 429 insertions(+), 102 deletions(-) create mode 100644 app/src/main/assets/sample/复杂界面/应用浏览器.js delete mode 100644 app/src/main/assets/sample/复杂界面/音乐浏览器.js create mode 100644 autojs/src/main/java/com/stardust/autojs/core/image/capture/GlobalScreenCapture.java 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()); }