diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 00ea882a..829c6850 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/app/build.gradle b/app/build.gradle index 3db21e1f..2c36f9fe 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -64,10 +64,19 @@ android { productFlavors { common { buildConfigField "String", "CHANNEL", '"common"' + ndk { + abiFilters "armeabi-v7a" + } } coolapk { buildConfigField "String", "CHANNEL", '"coolapk"' } + x86 { + buildConfigField "String", "CHANNEL", '"common"' + ndk { + abiFilters "x86" + } + } } } diff --git a/autojs/src/main/assets/modules/__automator__.js b/autojs/src/main/assets/modules/__automator__.js index 484f0629..21071194 100644 --- a/autojs/src/main/assets/modules/__automator__.js +++ b/autojs/src/main/assets/modules/__automator__.js @@ -111,7 +111,8 @@ module.exports = function(runtime, global){ const flagsMap = { "findOnUiThread": 1, - "useUsageStats": 2 + "useUsageStats": 2, + "useShell": 4 }; var auto = function(mode){ @@ -163,7 +164,10 @@ module.exports = function(runtime, global){ auto.getRoot = function(){ var root = runtime.accessibilityBridge.getRootInCurrentWindow(); - return com.stardust.automator.UiObject.createRoot(root); + if(root == null){ + return null; + } + return com.stardust.automator.UiObject.Companion.createRoot(root); } auto.setWindowFilter = function(filter){ diff --git a/autojs/src/main/java/com/stardust/autojs/AutoJs.java b/autojs/src/main/java/com/stardust/autojs/AutoJs.java index eccbe757..d65b7780 100644 --- a/autojs/src/main/java/com/stardust/autojs/AutoJs.java +++ b/autojs/src/main/java/com/stardust/autojs/AutoJs.java @@ -30,7 +30,7 @@ import com.stardust.autojs.script.JavaScriptSource; import com.stardust.util.ResourceMonitor; import com.stardust.util.ScreenMetrics; import com.stardust.util.UiHandler; -import com.stardust.view.accessibility.AccessibilityInfoProvider; +import com.stardust.autojs.core.activity.ActivityInfoProvider; import com.stardust.view.accessibility.AccessibilityNotificationObserver; import com.stardust.view.accessibility.AccessibilityService; import com.stardust.view.accessibility.LayoutInspector; @@ -54,7 +54,7 @@ public abstract class AutoJs { private final Application mApplication; private final UiHandler mUiHandler; private final AppUtils mAppUtils; - private final AccessibilityInfoProvider mAccessibilityInfoProvider; + private final ActivityInfoProvider mActivityInfoProvider; private final ScreenCaptureRequester mScreenCaptureRequester = new ScreenCaptureRequesterImpl(); private final ScriptEngineService mScriptEngineService; private final GlobalConsole mGlobalConsole; @@ -68,7 +68,7 @@ public abstract class AutoJs { mAppUtils = createAppUtils(mContext); mGlobalConsole = createGlobalConsole(); mNotificationObserver = new AccessibilityNotificationObserver(mContext); - mAccessibilityInfoProvider = new AccessibilityInfoProvider(mContext); + mActivityInfoProvider = new ActivityInfoProvider(mContext); mScriptEngineService = buildScriptEngineService(); ScriptEngineService.setInstance(mScriptEngineService); init(); @@ -166,7 +166,7 @@ public abstract class AutoJs { private void addAccessibilityServiceDelegates() { - AccessibilityService.Companion.addDelegate(100, mAccessibilityInfoProvider); + AccessibilityService.Companion.addDelegate(100, mActivityInfoProvider); AccessibilityService.Companion.addDelegate(200, mNotificationObserver); AccessibilityService.Companion.addDelegate(300, mAccessibilityActionRecorder); } @@ -195,8 +195,8 @@ public abstract class AutoJs { return mScriptEngineService; } - public AccessibilityInfoProvider getInfoProvider() { - return mAccessibilityInfoProvider; + public ActivityInfoProvider getInfoProvider() { + return mActivityInfoProvider; } @@ -229,8 +229,8 @@ public abstract class AutoJs { } @Override - public AccessibilityInfoProvider getInfoProvider() { - return mAccessibilityInfoProvider; + public ActivityInfoProvider getInfoProvider() { + return mActivityInfoProvider; } @Override diff --git a/autojs/src/main/java/com/stardust/autojs/core/accessibility/AccessibilityBridge.java b/autojs/src/main/java/com/stardust/autojs/core/accessibility/AccessibilityBridge.java index 8adaf42f..2e3c13e9 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/accessibility/AccessibilityBridge.java +++ b/autojs/src/main/java/com/stardust/autojs/core/accessibility/AccessibilityBridge.java @@ -1,5 +1,6 @@ package com.stardust.autojs.core.accessibility; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; import android.os.Build; @@ -14,7 +15,7 @@ import com.stardust.app.AppOpsKt; import com.stardust.autojs.runtime.accessibility.AccessibilityConfig; import com.stardust.util.IntentUtil; import com.stardust.util.UiHandler; -import com.stardust.view.accessibility.AccessibilityInfoProvider; +import com.stardust.autojs.core.activity.ActivityInfoProvider; import com.stardust.view.accessibility.AccessibilityNotificationObserver; import com.stardust.view.accessibility.AccessibilityService; @@ -34,6 +35,7 @@ public abstract class AccessibilityBridge { public static final int FLAG_FIND_ON_UI_THREAD = 1; public static final int FLAG_USE_USAGE_STATS = 2; + public static final int FLAG_USE_SHELL = 4; private int mMode = MODE_NORMAL; private int mFlags = 0; @@ -63,6 +65,7 @@ public abstract class AccessibilityBridge { @Nullable public AccessibilityNodeInfo getRootInCurrentWindow() { AccessibilityService service = getService(); + if (service == null) return null; if (mWindowFilter != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -89,7 +92,7 @@ public abstract class AccessibilityBridge { mWindowFilter = windowFilter; } - public abstract AccessibilityInfoProvider getInfoProvider(); + public abstract ActivityInfoProvider getInfoProvider(); public void setMode(int mode) { mMode = mode; @@ -108,6 +111,7 @@ public abstract class AccessibilityBridge { } } getInfoProvider().setUseUsageStats((mFlags & FLAG_USE_USAGE_STATS) != 0); + getInfoProvider().setUseShell((mFlags & FLAG_USE_SHELL) != 0); } @NonNull diff --git a/autojs/src/main/java/com/stardust/autojs/core/activity/ActivityInfoProvider.kt b/autojs/src/main/java/com/stardust/autojs/core/activity/ActivityInfoProvider.kt new file mode 100644 index 00000000..02393978 --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/core/activity/ActivityInfoProvider.kt @@ -0,0 +1,181 @@ +package com.stardust.autojs.core.activity + +import android.accessibilityservice.AccessibilityService +import android.app.AppOpsManager +import android.app.usage.UsageStatsManager +import android.content.ComponentName +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import android.util.Log +import android.view.accessibility.AccessibilityEvent +import androidx.annotation.RequiresApi +import com.stardust.app.isOpPermissionGranted +import com.stardust.autojs.core.util.Shell +import com.stardust.view.accessibility.AccessibilityDelegate +import java.util.regex.Pattern + +/** + * Created by Stardust on 2017/3/9. + */ + +class ActivityInfoProvider(private val context: Context) : AccessibilityDelegate { + + private val mPackageManager: PackageManager = context.packageManager + + @Volatile + private var mLatestPackage: String = "" + @Volatile + private var mLatestActivity: String = "" + private var mLatestComponentFromShell: ComponentName? = null + + private var mShell: Shell? = null + private var mUseShell = false + + val latestPackage: String + get() { + val compFromShell = mLatestComponentFromShell + if (useShell && compFromShell != null) { + return compFromShell.packageName + } + if (useUsageStats && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { + mLatestPackage = getLatestPackageByUsageStats() + } + return mLatestPackage + } + + val latestActivity: String + get() { + val compFromShell = mLatestComponentFromShell + if (useShell && compFromShell != null) { + return compFromShell.className + } + return mLatestActivity + } + + var useUsageStats: Boolean = false + + var useShell: Boolean + get() = mUseShell + set(value) { + if (value) { + mShell.let { + if (it == null) { + mShell = createShell(200) + } + } + } else { + mShell?.exit() + mShell = null + } + mUseShell = value + } + + override val eventTypes: Set? + get() = AccessibilityDelegate.ALL_EVENT_TYPES + + override fun onAccessibilityEvent(service: AccessibilityService, event: AccessibilityEvent): Boolean { + if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { + setLatestComponent(event.packageName, event.className) + } + return false + } + + fun getLatestPackageByUsageStatsIfGranted(): String { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1 && context.isOpPermissionGranted(AppOpsManager.OPSTR_GET_USAGE_STATS)) { + return getLatestPackageByUsageStats() + } + return mLatestPackage + } + + private fun setLatestComponentFromShellOutput(output: String) { + val matcher = WINDOW_PATTERN.matcher(output) + if (!matcher.find() || matcher.groupCount() < 1) { + Log.w(LOG_TAG, "invalid format: $output") + return + } + val latestPackage = matcher.group(1) + if (latestPackage.contains(":")) { + return + } + val latestActivity = if (matcher.groupCount() >= 2) { + matcher.group(2).orEmpty() + } else { + "" + } + Log.d(LOG_TAG, "setLatestComponent: output = $output, comp = $latestPackage/$latestActivity") + mLatestComponentFromShell = ComponentName(latestPackage, latestActivity) + } + + private fun createShell(dumpInterval: Int): Shell { + val shell = Shell(true) + shell.setCallback(object : Shell.Callback { + override fun onOutput(str: String) { + + } + + override fun onNewLine(line: String) { + setLatestComponentFromShellOutput(line) + } + + override fun onInitialized() { + } + + override fun onInterrupted(e: InterruptedException) { + + } + }) + shell.exec(DUMP_WINDOW_COMMAND.format(dumpInterval)) + return shell + } + + @RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1) + fun getLatestPackageByUsageStats(): String { + val usageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager + val current = System.currentTimeMillis() + val usageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, current - 60 * 60 * 1000, current) + return if (usageStats.isEmpty()) { + mLatestPackage + } else { + usageStats.sortBy { + it.lastTimeStamp + } + usageStats.last().packageName + } + + } + + private fun setLatestComponent(latestPackage: CharSequence?, latestClass: CharSequence?) { + if (latestPackage == null || latestClass == null) + return + val latestPackageStr = latestPackage.toString() + val latestClassStr = latestClass.toString() + if (latestClassStr.startsWith("android.view.") || latestClassStr.startsWith("android.widget.")) + return + try { + val componentName = ComponentName(latestPackageStr, latestClassStr) + mLatestActivity = mPackageManager.getActivityInfo(componentName, PackageManager.MATCH_DEFAULT_ONLY).name + } catch (ignored: PackageManager.NameNotFoundException) { + return + } + mLatestPackage = latestPackage.toString() + } + + companion object { + private val WINDOW_PATTERN = Pattern.compile("Window\\{\\S+\\s\\S+\\s([^\\/]+)\\/?([^}]+)?\\}") + private val DUMP_WINDOW_COMMAND = """ + oldActivity="" + currentActivity=`dumpsys window windows | grep -E 'mCurrentFocus'` + while true + do + if [[ ${'$'}oldActivity != ${'$'}currentActivity && ${'$'}currentActivity != *"=null"* ]]; then + echo ${'$'}currentActivity + oldActivity=${'$'}currentActivity + fi + currentActivity=`dumpsys window windows | grep -E 'mCurrentFocus'` + done + """.trimIndent() + + private const val LOG_TAG = "ActivityInfoProvider" + } +} 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 93a7b3c9..2fe5f3e0 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java @@ -47,7 +47,7 @@ import com.stardust.util.ScreenMetrics; import com.stardust.util.SdkVersionUtil; import com.stardust.util.Supplier; import com.stardust.util.UiHandler; -import com.stardust.view.accessibility.AccessibilityInfoProvider; +import com.stardust.autojs.core.activity.ActivityInfoProvider; import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.RhinoException; @@ -140,7 +140,7 @@ public class ScriptRuntime { public final SimpleActionAutomator automator; @ScriptVariable - public final AccessibilityInfoProvider info; + public final ActivityInfoProvider info; @ScriptVariable public final UI ui; diff --git a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityInfoProvider.kt b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityInfoProvider.kt deleted file mode 100644 index 8d6892a4..00000000 --- a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityInfoProvider.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.stardust.view.accessibility - -import android.accessibilityservice.AccessibilityService -import android.app.AppOpsManager -import android.app.usage.UsageStatsManager -import android.content.ComponentName -import android.content.Context -import android.content.pm.PackageManager -import android.os.Build -import android.view.accessibility.AccessibilityEvent -import androidx.annotation.RequiresApi -import com.stardust.app.isOpPermissionGranted - -/** - * Created by Stardust on 2017/3/9. - */ - -class AccessibilityInfoProvider(private val context: Context) : AccessibilityDelegate { - - private val mPackageManager: PackageManager = context.packageManager - - @Volatile - private var mLatestPackage: String = "" - - val latestPackage: String - get() { - if (useUsageStats && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { - mLatestPackage = getLatestPackageByUsageStats() - } - return mLatestPackage - } - - @Volatile - var latestActivity = "" - private set - - var useUsageStats: Boolean = false - - override val eventTypes: Set? - get() = AccessibilityDelegate.ALL_EVENT_TYPES - - override fun onAccessibilityEvent(service: AccessibilityService, event: AccessibilityEvent): Boolean { - if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { - setLatestComponent(event.packageName, event.className) - } - return false - } - - fun getLatestPackageByUsageStatsIfGranted(): String { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1 && context.isOpPermissionGranted(AppOpsManager.OPSTR_GET_USAGE_STATS)) { - return getLatestPackageByUsageStats() - } - return mLatestPackage - } - - @RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1) - fun getLatestPackageByUsageStats(): String { - val usageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager - val current = System.currentTimeMillis() - val usageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, current - 60 * 60 * 1000, current) - return if (usageStats.isEmpty()) { - mLatestPackage - } else { - usageStats.sortBy { - it.lastTimeStamp - } - usageStats.last().packageName - } - - } - - private fun setLatestComponent(latestPackage: CharSequence?, latestClass: CharSequence?) { - if (latestPackage == null || latestClass == null) - return - val latestPackageStr = latestPackage.toString() - val latestClassStr = latestClass.toString() - if (latestClassStr.startsWith("android.view.") || latestClassStr.startsWith("android.widget.")) - return - try { - val componentName = ComponentName(latestPackageStr, latestClassStr) - latestActivity = mPackageManager.getActivityInfo(componentName, PackageManager.MATCH_DEFAULT_ONLY).name - } catch (ignored: PackageManager.NameNotFoundException) { - return - } - mLatestPackage = latestPackage.toString() - } -}