mirror of
https://github.com/TonyJiangWJ/Auto.js.git
synced 2026-06-06 21:13:08 +08:00
新增 可以使用"查看使用统计权限"增强currentPackage()的准确性,在代码中通过auto.setFlags("useUsageStats")启用
This commit is contained in:
parent
ada179690b
commit
ee2860b6de
@ -15,6 +15,7 @@
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
|
||||
|
||||
|
||||
<!-- 非Auto.js运行必需,不会主动申请,某些脚本可以自行申请-->
|
||||
|
||||
@ -31,6 +31,7 @@ import org.autojs.autojs.ui.floating.layoutinspector.LayoutHierarchyFloatyWindow
|
||||
import org.autojs.autojs.ui.main.MainActivity_;
|
||||
import org.autojs.autojs.ui.explorer.ExplorerView;
|
||||
import org.autojs.autojs.theme.dialog.ThemeColorMaterialDialogBuilder;
|
||||
|
||||
import com.stardust.util.ClipboardUtil;
|
||||
import com.stardust.util.Func1;
|
||||
import com.stardust.view.accessibility.LayoutInspector;
|
||||
@ -214,7 +215,7 @@ public class CircularMenu implements Recorder.OnStateChangedListener, LayoutInsp
|
||||
}
|
||||
|
||||
private void inspectLayout(Func1<NodeInfo, FloatyWindow> windowCreator) {
|
||||
if(mLayoutInspectDialog != null){
|
||||
if (mLayoutInspectDialog != null) {
|
||||
mLayoutInspectDialog.dismiss();
|
||||
mLayoutInspectDialog = null;
|
||||
}
|
||||
@ -260,7 +261,7 @@ public class CircularMenu implements Recorder.OnStateChangedListener, LayoutInsp
|
||||
@OnClick(R.id.settings)
|
||||
void settings() {
|
||||
mWindow.collapse();
|
||||
mRunningPackage = AutoJs.getInstance().getInfoProvider().getLatestPackage();
|
||||
mRunningPackage = AutoJs.getInstance().getInfoProvider().getLatestPackageByUsageStatsIfGranted();
|
||||
mRunningActivity = AutoJs.getInstance().getInfoProvider().getLatestActivity();
|
||||
mSettingsDialog = new OperationDialogBuilder(mContext)
|
||||
.item(R.id.accessibility_service, R.drawable.ic_service_green, R.string.text_accessibility_settings)
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
package org.autojs.autojs.ui.main.drawer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
@ -21,6 +24,7 @@ import com.bumptech.glide.request.RequestOptions;
|
||||
import com.bumptech.glide.request.target.CustomViewTarget;
|
||||
import com.bumptech.glide.request.target.SimpleTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
import com.stardust.app.AppOpsKt;
|
||||
import com.stardust.app.GlobalAppContext;
|
||||
import com.stardust.notification.NotificationListenerService;
|
||||
|
||||
@ -113,6 +117,7 @@ public class DrawerFragment extends androidx.fragment.app.Fragment {
|
||||
};
|
||||
|
||||
private DrawerMenuItem mNotificationPermissionItem = new DrawerMenuItem(R.drawable.ic_ali_notification, R.string.text_notification_permission, 0, this::goToNotificationServiceSettings);
|
||||
private DrawerMenuItem mUsageStatsPermissionItem = new DrawerMenuItem(R.drawable.ic_ali_notification, R.string.text_usage_stats_permission, 0, this::goToUsageStatsSettings);
|
||||
private DrawerMenuItem mForegroundServiceItem = new DrawerMenuItem(R.drawable.ic_service_green, R.string.text_foreground_service, R.string.key_foreground_servie, this::toggleForegroundService);
|
||||
|
||||
private DrawerMenuItem mFloatingWindowItem = new DrawerMenuItem(R.drawable.ic_robot_64, R.string.text_floating_window, 0, this::showOrDismissFloatingWindow);
|
||||
@ -163,6 +168,7 @@ public class DrawerFragment extends androidx.fragment.app.Fragment {
|
||||
mStableModeItem,
|
||||
mNotificationPermissionItem,
|
||||
mForegroundServiceItem,
|
||||
mUsageStatsPermissionItem,
|
||||
|
||||
new DrawerMenuGroup(R.string.text_script_record),
|
||||
mFloatingWindowItem,
|
||||
@ -226,6 +232,17 @@ public class DrawerFragment extends androidx.fragment.app.Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
void goToUsageStatsSettings(DrawerMenuItemViewHolder holder) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return;
|
||||
}
|
||||
boolean enabled = AppOpsKt.isOpPermissionGranted(getContext(), AppOpsManager.OPSTR_GET_USAGE_STATS);
|
||||
boolean checked = holder.getSwitchCompat().isChecked();
|
||||
if ((checked && !enabled) || (!checked && enabled)) {
|
||||
IntentUtil.requestAppUsagePermission(getContext());
|
||||
}
|
||||
}
|
||||
|
||||
void showOrDismissFloatingWindow(DrawerMenuItemViewHolder holder) {
|
||||
boolean isFloatingWindowShowing = FloatyWindowManger.isCircularMenuShowing();
|
||||
boolean checked = holder.getSwitchCompat().isChecked();
|
||||
@ -390,7 +407,9 @@ public class DrawerFragment extends androidx.fragment.app.Fragment {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
setChecked(mNotificationPermissionItem, NotificationListenerService.Companion.getInstance() != null);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
setChecked(mUsageStatsPermissionItem, AppOpsKt.isOpPermissionGranted(getContext(), AppOpsManager.OPSTR_GET_USAGE_STATS));
|
||||
}
|
||||
}
|
||||
|
||||
private void enableAccessibilityService() {
|
||||
|
||||
@ -273,7 +273,7 @@
|
||||
<string name="text_reset_succeed">重置成功</string>
|
||||
<string name="text_cannot_read_file">无法读取文件</string>
|
||||
<string name="text_service">服务</string>
|
||||
<string name="text_notification_permission">通知权限</string>
|
||||
<string name="text_notification_permission">通知读取权限</string>
|
||||
|
||||
|
||||
<string name="text_open_main_activity">打开主界面</string>
|
||||
@ -429,4 +429,5 @@
|
||||
<string name="text_night_mode">夜间模式</string>
|
||||
<string name="key_night_mode">key_night_mode</string>
|
||||
<string name="text_run_on_startup">Auto.js启动时</string>
|
||||
<string name="text_usage_stats_permission">查看使用统计权限</string>
|
||||
</resources>
|
||||
|
||||
@ -110,7 +110,8 @@ module.exports = function(runtime, global){
|
||||
}
|
||||
|
||||
const flagsMap = {
|
||||
"findOnUiThread": 1
|
||||
"findOnUiThread": 1,
|
||||
"useUsageStats": 2
|
||||
};
|
||||
|
||||
var auto = function(mode){
|
||||
|
||||
@ -5,6 +5,7 @@ import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
@ -67,7 +68,7 @@ public abstract class AutoJs {
|
||||
mAppUtils = createAppUtils(mContext);
|
||||
mGlobalConsole = createGlobalConsole();
|
||||
mNotificationObserver = new AccessibilityNotificationObserver(mContext);
|
||||
mAccessibilityInfoProvider = new AccessibilityInfoProvider(mContext.getPackageManager());
|
||||
mAccessibilityInfoProvider = new AccessibilityInfoProvider(mContext);
|
||||
mScriptEngineService = buildScriptEngineService();
|
||||
ScriptEngineService.setInstance(mScriptEngineService);
|
||||
init();
|
||||
@ -205,7 +206,7 @@ public abstract class AutoJs {
|
||||
private class AccessibilityBridgeImpl extends AccessibilityBridge {
|
||||
|
||||
public AccessibilityBridgeImpl(UiHandler uiHandler) {
|
||||
super(createAccessibilityConfig(), uiHandler);
|
||||
super(mContext, createAccessibilityConfig(), uiHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,12 +1,18 @@
|
||||
package com.stardust.autojs.core.accessibility;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.AccessibilityWindowInfo;
|
||||
|
||||
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.view.accessibility.AccessibilityNotificationObserver;
|
||||
@ -27,17 +33,20 @@ public abstract class AccessibilityBridge {
|
||||
public static final int MODE_FAST = 1;
|
||||
|
||||
public static final int FLAG_FIND_ON_UI_THREAD = 1;
|
||||
public static final int FLAG_USE_USAGE_STATS = 2;
|
||||
|
||||
private int mMode = MODE_NORMAL;
|
||||
private int mFlags = 0;
|
||||
private final AccessibilityConfig mConfig;
|
||||
private WindowFilter mWindowFilter;
|
||||
private final UiHandler mUiHandler;
|
||||
private final Context mContext;
|
||||
|
||||
public AccessibilityBridge(AccessibilityConfig config, UiHandler uiHandler) {
|
||||
public AccessibilityBridge(Context context, AccessibilityConfig config, UiHandler uiHandler) {
|
||||
mConfig = config;
|
||||
mUiHandler = uiHandler;
|
||||
mConfig.seal();
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public abstract void ensureServiceEnabled();
|
||||
@ -80,7 +89,6 @@ public abstract class AccessibilityBridge {
|
||||
mWindowFilter = windowFilter;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public abstract AccessibilityInfoProvider getInfoProvider();
|
||||
|
||||
public void setMode(int mode) {
|
||||
@ -93,6 +101,13 @@ public abstract class AccessibilityBridge {
|
||||
|
||||
public void setFlags(int flags) {
|
||||
mFlags = flags;
|
||||
if ((mFlags & FLAG_USE_USAGE_STATS) != 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (!AppOpsKt.isOpPermissionGranted(mContext, AppOpsManager.OPSTR_GET_USAGE_STATS)) {
|
||||
IntentUtil.requestAppUsagePermission(mContext);
|
||||
throw new SecurityException("没有\"查看使用情况\"权限");
|
||||
}
|
||||
}
|
||||
getInfoProvider().setUseUsageStats((mFlags & FLAG_USE_USAGE_STATS) != 0);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@ -1,33 +1,41 @@
|
||||
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.ActivityInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.view.accessibility.AccessibilityNodeInfo
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityDelegate
|
||||
|
||||
import java.util.Arrays
|
||||
import java.util.Collections
|
||||
import java.util.HashSet
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.stardust.app.isOpPermissionGranted
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
class AccessibilityInfoProvider(private val mPackageManager: PackageManager) : AccessibilityDelegate {
|
||||
class AccessibilityInfoProvider(private val context: Context) : AccessibilityDelegate {
|
||||
|
||||
private val mPackageManager: PackageManager = context.packageManager
|
||||
|
||||
@Volatile
|
||||
var latestPackage = ""
|
||||
private set
|
||||
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 = true
|
||||
|
||||
override val eventTypes: Set<Int>?
|
||||
get() = AccessibilityDelegate.ALL_EVENT_TYPES
|
||||
|
||||
@ -38,6 +46,29 @@ class AccessibilityInfoProvider(private val mPackageManager: PackageManager) : A
|
||||
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()) {
|
||||
""
|
||||
} else {
|
||||
usageStats.sortBy {
|
||||
it.lastTimeStamp
|
||||
}
|
||||
usageStats.last().packageName
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun setLatestComponent(latestPackage: CharSequence?, latestClass: CharSequence?) {
|
||||
if (latestPackage == null || latestClass == null)
|
||||
return
|
||||
@ -51,7 +82,6 @@ class AccessibilityInfoProvider(private val mPackageManager: PackageManager) : A
|
||||
} catch (ignored: PackageManager.NameNotFoundException) {
|
||||
return
|
||||
}
|
||||
|
||||
this.latestPackage = latestPackage.toString()
|
||||
mLatestPackage = latestPackage.toString()
|
||||
}
|
||||
}
|
||||
|
||||
20
common/src/main/java/com/stardust/app/AppOps.kt
Normal file
20
common/src/main/java/com/stardust/app/AppOps.kt
Normal file
@ -0,0 +1,20 @@
|
||||
package com.stardust.app
|
||||
|
||||
import android.app.AppOpsManager
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
||||
fun Context.isOpPermissionGranted(permission: String): Boolean {
|
||||
val appOps = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
|
||||
val mode = appOps.checkOpNoThrow(permission, android.os.Process.myUid(), packageName)
|
||||
|
||||
return if (mode == AppOpsManager.MODE_DEFAULT && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
checkCallingOrSelfPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) == PackageManager.PERMISSION_GRANTED
|
||||
} else {
|
||||
mode == AppOpsManager.MODE_ALLOWED
|
||||
}
|
||||
}
|
||||
@ -4,8 +4,12 @@ import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import android.os.Build;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.stardust.R;
|
||||
@ -182,4 +186,15 @@ public class IntentUtil {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public static void requestAppUsagePermission(Context context) {
|
||||
Intent intent = new Intent(android.provider.Settings.ACTION_USAGE_ACCESS_SETTINGS);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
try {
|
||||
context.startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user