Android 应用锁开发 的 获取栈顶包名、系统桌面包名等一系列骚操作

发布于 2018-11-30  168 次阅读


最近我在折腾一个应用锁的应用,里面有不少平时用很少接触的东西,整理记录下。

源码GayHub


1.权限生擒

因为涉及到一下系统权限,所以常规的权限申请没用
- 先在manifest里登记下

<uses-permission android:name="android.permission.GET_TASKS"/>
<uses-permission
        android:name="android.permission.PACKAGE_USAGE_STATS"
        tools:ignore="ProtectedPermissions"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!--<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>-->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
  • 找系统要权限
//开始要:
private void checkPermission() {
    if (!isStatAccessPermissionSet() && isNoOption()) {
        UDialog.getInstance(this, false, false)
            .showNoticeWithOnebtn("我要权限,给我。"),
                (result, dia) -> {
                    //去系统权限页面
                    Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
                    startActivityForResult(intent, RESULT_ACTION_USAGE_ACCESS_SETTINGS);
                });
    } else {
        toNext();
    }
}

/**
 * 查看是存在查看使用情况的应用程序界面
 */
private boolean isNoOption() {
    PackageManager packageManager = getPackageManager();
    Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
    List<ResolveInfo> list = packageManager
        .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
    return list.size() > 0;
}

/**
 * @return 判断是否已经获取 有权查看使用情况的应用程序 权限
 */
public boolean isStatAccessPermissionSet() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        try {
            PackageManager packageManager = getPackageManager();
            ApplicationInfo info = packageManager
                .getApplicationInfo(getPackageName(), 0);
            AppOpsManager appOpsManager = (AppOpsManager) context
                .getSystemService(Context.APP_OPS_SERVICE);
            appOpsManager
                .checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, info.uid, info.packageName);
            return appOpsManager
                .checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, info.uid, info.packageName) ==
                AppOpsManager.MODE_ALLOWED;
        } catch (Exception e) {
            return false;
        }
    } else {
        return false;
    }
}

2.涉及到应用锁的相关方法

获取当前正在运行的桌面launcher包名

public String getLauncherPkgName() {
        final Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        final ResolveInfo res = getPkgMgr().resolveActivity(intent, 0);
        if (res.activityInfo == null) {
            return null;// 一般不会发生
        }
        if (res.activityInfo.packageName.equals("android")) {
            return null;// 有多个桌面程序存在,且未指定默认项时;
        } else {
            return res.activityInfo.packageName;
        }
    }

轻量: 获取栈顶应用包名(非系统程序在在栈顶时有效)

public String getCurTopAppPkg() {
        ActivityManager am = (ActivityManager) KApp.getInstance().getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningTaskInfo> appTask = am.getRunningTasks(Integer.MAX_VALUE);
        if (!UText.isEmpty(appTask)) {
            return appTask.get(0).topActivity.getPackageName();
        }
        return "";
    }

获取栈顶应用包名(所有程序有效)

public String getTopAppPkg() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
            if (actMgr == null) {
                actMgr = (ActivityManager) KApp.getInstance().getSystemService(Context.ACTIVITY_SERVICE);
            }
            List<ActivityManager.RunningTaskInfo> appTasks = actMgr.getRunningTasks(1);
            if (null != appTasks && !appTasks.isEmpty()) {
                return appTasks.get(0).topActivity.getPackageName();
            }
        } else {
            //5.0以后需要用这方法
            if (usgMgr == null) {
                usgMgr = (UsageStatsManager) KApp.getInstance().getSystemService(Context.USAGE_STATS_SERVICE);
            }
            long endTime = System.currentTimeMillis();
            long beginTime = endTime - 10000;
            String result = "";
            UsageEvents.Event event = new UsageEvents.Event();
            UsageEvents usageEvents = usgMgr.queryEvents(beginTime, endTime);
            while (usageEvents.hasNextEvent()) {
                usageEvents.getNextEvent(event);
                if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
                    result = event.getPackageName();
                }
            }
            if (!android.text.TextUtils.isEmpty(result)) {
                return result;
            }
        }
        return "";
    }

3.关于应用锁的实现

我觉的我这个方法效率并不太高,不知道大家有什么更高效的方法没?
我是后台启动一个服务,每隔一定时间扫描一次栈顶包名
大概是这样的:

//开启定时线程,1000 毫秒扫描一次
 Disposable subscribe =Observable
     .interval(1000, TimeUnit.MICROSECONDS)
     .subscribe(aLong -> runCompare());
 rxDisposable(subscribe);

private void runCompare() {
   Disposable subscribe = Observable
       .create((ObservableOnSubscribe<String>) emitter -> {
           //锁总开关
           if (Code.lockState.get()) {
               String topName = UApps.instance().getTopApp();//获取栈顶app的包名
               //返回桌面
               if (!UText.isEmpty(topName) && topName.equals(Code.LauncherPkgName)) {
                   Code.doLock.set(true);//锁激活
                   lastPkgName = "";//上次解锁失效-置空
               }
//                    ULog.commonD("【" + topName + "】【" + UApps.instance().getLauncherPkgName() + "】");
               //栈顶包名在加锁列表
               if (!UText.isEmpty(topName) && DbLockHelper.SearchByPkgName(topName) != null) {
                   emitter.onNext(topName);
               }
           }
       })
       .observeOn(AndroidSchedulers.mainThread())
       .subscribe(lock -> {
           ULog.commonD("【" + lastPkgName + "】--【" + Code.doLock.get() + "】--【" + lock + "】");
           //锁可用
           if (Code.doLock.get()) {
               //包名为空 或者包名和上次解锁的不同
               if (UText.isEmpty(lastPkgName) || !lastPkgName.equals(lock)) {
                   checkData(lock);//加锁
               }
           }
       });
   rxDisposable(subscribe);
}

赤夜染尽 千樱散落 零时夜雨 无茵之音