新增 可以使用"查看使用统计权限"增强currentPackage()的准确性,在代码中通过auto.setFlags("useUsageStats")启用

This commit is contained in:
hyb1996
2018-11-30 14:36:16 +08:00
parent ada179690b
commit ee2860b6de
10 changed files with 126 additions and 22 deletions

View File

@@ -15,6 +15,7 @@
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> <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.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<!-- 非Auto.js运行必需不会主动申请某些脚本可以自行申请--> <!-- 非Auto.js运行必需不会主动申请某些脚本可以自行申请-->

View File

@@ -31,6 +31,7 @@ import org.autojs.autojs.ui.floating.layoutinspector.LayoutHierarchyFloatyWindow
import org.autojs.autojs.ui.main.MainActivity_; import org.autojs.autojs.ui.main.MainActivity_;
import org.autojs.autojs.ui.explorer.ExplorerView; import org.autojs.autojs.ui.explorer.ExplorerView;
import org.autojs.autojs.theme.dialog.ThemeColorMaterialDialogBuilder; import org.autojs.autojs.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.util.ClipboardUtil; import com.stardust.util.ClipboardUtil;
import com.stardust.util.Func1; import com.stardust.util.Func1;
import com.stardust.view.accessibility.LayoutInspector; import com.stardust.view.accessibility.LayoutInspector;
@@ -214,7 +215,7 @@ public class CircularMenu implements Recorder.OnStateChangedListener, LayoutInsp
} }
private void inspectLayout(Func1<NodeInfo, FloatyWindow> windowCreator) { private void inspectLayout(Func1<NodeInfo, FloatyWindow> windowCreator) {
if(mLayoutInspectDialog != null){ if (mLayoutInspectDialog != null) {
mLayoutInspectDialog.dismiss(); mLayoutInspectDialog.dismiss();
mLayoutInspectDialog = null; mLayoutInspectDialog = null;
} }
@@ -260,7 +261,7 @@ public class CircularMenu implements Recorder.OnStateChangedListener, LayoutInsp
@OnClick(R.id.settings) @OnClick(R.id.settings)
void settings() { void settings() {
mWindow.collapse(); mWindow.collapse();
mRunningPackage = AutoJs.getInstance().getInfoProvider().getLatestPackage(); mRunningPackage = AutoJs.getInstance().getInfoProvider().getLatestPackageByUsageStatsIfGranted();
mRunningActivity = AutoJs.getInstance().getInfoProvider().getLatestActivity(); mRunningActivity = AutoJs.getInstance().getInfoProvider().getLatestActivity();
mSettingsDialog = new OperationDialogBuilder(mContext) mSettingsDialog = new OperationDialogBuilder(mContext)
.item(R.id.accessibility_service, R.drawable.ic_service_green, R.string.text_accessibility_settings) .item(R.id.accessibility_service, R.drawable.ic_service_green, R.string.text_accessibility_settings)

View File

@@ -1,14 +1,17 @@
package org.autojs.autojs.ui.main.drawer; package org.autojs.autojs.ui.main.drawer;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.AppOpsManager;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View; import android.view.View;
import android.widget.TextView; 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.CustomViewTarget;
import com.bumptech.glide.request.target.SimpleTarget; import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
import com.stardust.app.AppOpsKt;
import com.stardust.app.GlobalAppContext; import com.stardust.app.GlobalAppContext;
import com.stardust.notification.NotificationListenerService; 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 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 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); 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, mStableModeItem,
mNotificationPermissionItem, mNotificationPermissionItem,
mForegroundServiceItem, mForegroundServiceItem,
mUsageStatsPermissionItem,
new DrawerMenuGroup(R.string.text_script_record), new DrawerMenuGroup(R.string.text_script_record),
mFloatingWindowItem, 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) { void showOrDismissFloatingWindow(DrawerMenuItemViewHolder holder) {
boolean isFloatingWindowShowing = FloatyWindowManger.isCircularMenuShowing(); boolean isFloatingWindowShowing = FloatyWindowManger.isCircularMenuShowing();
boolean checked = holder.getSwitchCompat().isChecked(); 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) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
setChecked(mNotificationPermissionItem, NotificationListenerService.Companion.getInstance() != null); 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() { private void enableAccessibilityService() {

View File

@@ -273,7 +273,7 @@
<string name="text_reset_succeed">重置成功</string> <string name="text_reset_succeed">重置成功</string>
<string name="text_cannot_read_file">无法读取文件</string> <string name="text_cannot_read_file">无法读取文件</string>
<string name="text_service">服务</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> <string name="text_open_main_activity">打开主界面</string>
@@ -429,4 +429,5 @@
<string name="text_night_mode">夜间模式</string> <string name="text_night_mode">夜间模式</string>
<string name="key_night_mode">key_night_mode</string> <string name="key_night_mode">key_night_mode</string>
<string name="text_run_on_startup">Auto.js启动时</string> <string name="text_run_on_startup">Auto.js启动时</string>
<string name="text_usage_stats_permission">查看使用统计权限</string>
</resources> </resources>

View File

@@ -110,7 +110,8 @@ module.exports = function(runtime, global){
} }
const flagsMap = { const flagsMap = {
"findOnUiThread": 1 "findOnUiThread": 1,
"useUsageStats": 2
}; };
var auto = function(mode){ var auto = function(mode){

View File

@@ -5,6 +5,7 @@ import android.app.Application;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
@@ -67,7 +68,7 @@ public abstract class AutoJs {
mAppUtils = createAppUtils(mContext); mAppUtils = createAppUtils(mContext);
mGlobalConsole = createGlobalConsole(); mGlobalConsole = createGlobalConsole();
mNotificationObserver = new AccessibilityNotificationObserver(mContext); mNotificationObserver = new AccessibilityNotificationObserver(mContext);
mAccessibilityInfoProvider = new AccessibilityInfoProvider(mContext.getPackageManager()); mAccessibilityInfoProvider = new AccessibilityInfoProvider(mContext);
mScriptEngineService = buildScriptEngineService(); mScriptEngineService = buildScriptEngineService();
ScriptEngineService.setInstance(mScriptEngineService); ScriptEngineService.setInstance(mScriptEngineService);
init(); init();
@@ -205,7 +206,7 @@ public abstract class AutoJs {
private class AccessibilityBridgeImpl extends AccessibilityBridge { private class AccessibilityBridgeImpl extends AccessibilityBridge {
public AccessibilityBridgeImpl(UiHandler uiHandler) { public AccessibilityBridgeImpl(UiHandler uiHandler) {
super(createAccessibilityConfig(), uiHandler); super(mContext, createAccessibilityConfig(), uiHandler);
} }
@Override @Override

View File

@@ -1,12 +1,18 @@
package com.stardust.autojs.core.accessibility; package com.stardust.autojs.core.accessibility;
import android.app.AppOpsManager;
import android.content.Context;
import android.os.Build; import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.AccessibilityWindowInfo;
import com.stardust.app.AppOpsKt;
import com.stardust.autojs.runtime.accessibility.AccessibilityConfig; import com.stardust.autojs.runtime.accessibility.AccessibilityConfig;
import com.stardust.util.IntentUtil;
import com.stardust.util.UiHandler; import com.stardust.util.UiHandler;
import com.stardust.view.accessibility.AccessibilityInfoProvider; import com.stardust.view.accessibility.AccessibilityInfoProvider;
import com.stardust.view.accessibility.AccessibilityNotificationObserver; 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 MODE_FAST = 1;
public static final int FLAG_FIND_ON_UI_THREAD = 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 mMode = MODE_NORMAL;
private int mFlags = 0; private int mFlags = 0;
private final AccessibilityConfig mConfig; private final AccessibilityConfig mConfig;
private WindowFilter mWindowFilter; private WindowFilter mWindowFilter;
private final UiHandler mUiHandler; private final UiHandler mUiHandler;
private final Context mContext;
public AccessibilityBridge(AccessibilityConfig config, UiHandler uiHandler) { public AccessibilityBridge(Context context, AccessibilityConfig config, UiHandler uiHandler) {
mConfig = config; mConfig = config;
mUiHandler = uiHandler; mUiHandler = uiHandler;
mConfig.seal(); mConfig.seal();
mContext = context;
} }
public abstract void ensureServiceEnabled(); public abstract void ensureServiceEnabled();
@@ -80,7 +89,6 @@ public abstract class AccessibilityBridge {
mWindowFilter = windowFilter; mWindowFilter = windowFilter;
} }
@NonNull
public abstract AccessibilityInfoProvider getInfoProvider(); public abstract AccessibilityInfoProvider getInfoProvider();
public void setMode(int mode) { public void setMode(int mode) {
@@ -93,6 +101,13 @@ public abstract class AccessibilityBridge {
public void setFlags(int flags) { public void setFlags(int flags) {
mFlags = 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 @NonNull

View File

@@ -1,33 +1,41 @@
package com.stardust.view.accessibility package com.stardust.view.accessibility
import android.accessibilityservice.AccessibilityService import android.accessibilityservice.AccessibilityService
import android.app.AppOpsManager
import android.app.usage.UsageStatsManager
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build
import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo import androidx.annotation.RequiresApi
import com.stardust.app.isOpPermissionGranted
import com.stardust.view.accessibility.AccessibilityDelegate
import java.util.Arrays
import java.util.Collections
import java.util.HashSet
/** /**
* Created by Stardust on 2017/3/9. * 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 @Volatile
var latestPackage = "" private var mLatestPackage: String = ""
private set
val latestPackage: String
get() {
if (useUsageStats && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
mLatestPackage = getLatestPackageByUsageStats()
}
return mLatestPackage
}
@Volatile @Volatile
var latestActivity = "" var latestActivity = ""
private set private set
var useUsageStats: Boolean = true
override val eventTypes: Set<Int>? override val eventTypes: Set<Int>?
get() = AccessibilityDelegate.ALL_EVENT_TYPES get() = AccessibilityDelegate.ALL_EVENT_TYPES
@@ -38,6 +46,29 @@ class AccessibilityInfoProvider(private val mPackageManager: PackageManager) : A
return false 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?) { private fun setLatestComponent(latestPackage: CharSequence?, latestClass: CharSequence?) {
if (latestPackage == null || latestClass == null) if (latestPackage == null || latestClass == null)
return return
@@ -51,7 +82,6 @@ class AccessibilityInfoProvider(private val mPackageManager: PackageManager) : A
} catch (ignored: PackageManager.NameNotFoundException) { } catch (ignored: PackageManager.NameNotFoundException) {
return return
} }
mLatestPackage = latestPackage.toString()
this.latestPackage = latestPackage.toString()
} }
} }

View 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
}
}

View File

@@ -4,8 +4,12 @@ import android.content.ActivityNotFoundException;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import android.os.Build;
import android.widget.Toast; import android.widget.Toast;
import com.stardust.R; import com.stardust.R;
@@ -182,4 +186,15 @@ public class IntentUtil {
return false; 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();
}
}
} }