新增 可以使用"查看使用统计权限"增强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.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<!-- 非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.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)

View File

@ -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() {

View File

@ -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>

View File

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

View File

@ -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

View File

@ -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

View File

@ -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()
}
}

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.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();
}
}
}