Android AppLock(應(yīng)用鎖)開(kāi)發(fā)

android App Lock開(kāi)發(fā),主要有幾個(gè)點(diǎn):
第一個(gè)是所謂的應(yīng)用鎖,就是在應(yīng)用打開(kāi)的時(shí)候覆蓋一個(gè)Activity(或者是一個(gè)全局的Dialog,甚至WindowManager懸浮窗也可以)在應(yīng)用上面,要通過(guò)解鎖才能夠看到應(yīng)用。
所以,問(wèn)題來(lái)了:1、怎么知道用戶打開(kāi)和關(guān)閉了加鎖的應(yīng)用。2、用戶關(guān)閉解鎖界面的時(shí)候怎么把界面底下的應(yīng)用也關(guān)了。

問(wèn)題一:
android里面沒(méi)有直接能監(jiān)聽(tīng)?wèi)?yīng)用打開(kāi)和關(guān)閉的方法,所以做法是不斷遍歷應(yīng)用棧,獲取棧頂?shù)膽?yīng)用的包名來(lái)對(duì)比,如果是加鎖的包名,則彈出解鎖界面,如果不是則不處理。
獲取棧頂包名的方法有好幾個(gè),根據(jù)不同的android版本方法也不一樣,在android5.0以上,推薦使用UsageStatsManager來(lái)獲取,具體方法:

public String getLauncherTopApp(Context context, ActivityManager activityManager) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            List<ActivityManager.RunningTaskInfo> appTasks = activityManager.getRunningTasks(1);
            if (null != appTasks && !appTasks.isEmpty()) {
                return appTasks.get(0).topActivity.getPackageName();
            }
        } else {
            //5.0以后需要用這方法
            UsageStatsManager sUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
            long endTime = System.currentTimeMillis();
            long beginTime = endTime - 10000;
            String result = "";
            UsageEvents.Event event = new UsageEvents.Event();
            UsageEvents usageEvents = sUsageStatsManager.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 "";
    }

使用UsageStatsManager需要獲取權(quán)限相關(guān)代碼:

    /**
     * 判斷是否已經(jīng)獲取 有權(quán)查看使用情況的應(yīng)用程序 權(quán)限
     *
     * @param context
     * @return
     */
    public static boolean isStatAccessPermissionSet(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            try {
                PackageManager packageManager = context.getPackageManager();
                ApplicationInfo info = packageManager.getApplicationInfo(context.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) {
                e.printStackTrace();
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * 查看是存在查看使用情況的應(yīng)用程序界面
     *
     * @return
     */
    public static boolean isNoOption(Context context) {
        PackageManager packageManager = context.getPackageManager();
        Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
        List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        return list.size() > 0;
    }

    /**
     * 轉(zhuǎn)跳到 有權(quán)查看使用情況的應(yīng)用程序 界面
     *
     * @param context
     */
    public static void startActionUsageAccessSettings(Context context) {
        Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
        context.startActivity(intent);
    }

這里有坑,就是有些手機(jī)是不存在這個(gè)權(quán)限和申請(qǐng)這個(gè)權(quán)限的設(shè)置界面的,所以要先判斷是否存在這個(gè)節(jié)面。

應(yīng)用鎖的主要核心代碼就這些。

問(wèn)題二:
只需要修改返回鍵,使返回鍵的功能跟Home賤一樣即可:

    /**
     * Home鍵操作
     */
    public static void goHome(BaseActivity activity) {
        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
        homeIntent.addCategory(Intent.CATEGORY_HOME);
        activity.startActivity(homeIntent);
        activity.finish();
    }

關(guān)于加載手機(jī)的應(yīng)用列表:
加載手機(jī)的應(yīng)用列表是耗時(shí)的,需要放在子線程中加載,但如果是在進(jìn)入列表頁(yè)面的時(shí)候才加載,那樣時(shí)間也會(huì)比較長(zhǎng),最好的方法是一進(jìn)應(yīng)用就開(kāi)啟一個(gè)后臺(tái)服務(wù)去加載好,在進(jìn)入列表頁(yè)面的時(shí)候直接取,這樣體驗(yàn)會(huì)好很多。

怎么判斷應(yīng)用的打開(kāi)和關(guān)閉:
上面說(shuō)到,因?yàn)榕袛嗍欠翊蜷_(kāi)了加鎖應(yīng)用是在一個(gè)死循環(huán)里面進(jìn)行的,如果用戶解鎖了,就不應(yīng)該再次加鎖,應(yīng)該等用戶退出這個(gè)應(yīng)用的時(shí)候再加鎖,不然就會(huì)導(dǎo)致解鎖了就立馬又彈出解鎖界面這樣的錯(cuò)誤。所以要有一個(gè)標(biāo)志位去判斷。

怎么做:
在用戶解鎖成功后將解鎖的應(yīng)用的包名保存在SP文件中,在死循環(huán)遍歷里面去判斷。如果保存的包名跟當(dāng)前獲取的棧頂包名一樣,則應(yīng)用正在打開(kāi),不需要加鎖,如果不一樣,則用戶已經(jīng)關(guān)閉了當(dāng)前應(yīng)用,需要重新加鎖,例子:

//當(dāng)前棧頂?shù)陌?String packageName = getLauncherTopApp(LockService.this, activityManager);
//解鎖后保存的包名
String savePkgName = SpUtil.getInstance().getString(AppConstants.LOCK_LAST_LOAD_PKG_NAME, "");
if (!TextUtils.isEmpty(savePkgName)) {
   if (!TextUtils.isEmpty(packageName)) {
       if (!savePkgName.equals(packageName)) {
            //再加多層判斷,更加準(zhǔn)確,如果返回了桌面
           if (getHomes().contains(packageName) || packageName.contains("launcher")) { 
               //這是設(shè)置相關(guān)的代碼,表示是否設(shè)置了不鎖這個(gè)應(yīng)用,可忽略
               boolean isSetUnLock = mLockInfoManager.isSetUnLock(savePkgName);
               if (!isSetUnLock) {
                   //加鎖
                   mLockInfoManager.lockCommApplication(savePkgName);
               }
           }
       }
   }
}

    /**
     * 獲得屬于桌面的應(yīng)用的應(yīng)用包名稱
     */
    private List<String> getHomes() {
        List<String> names = new ArrayList<>();
        PackageManager packageManager = this.getPackageManager();
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo ri : resolveInfo) {
            names.add(ri.activityInfo.packageName);
        }
        return names;
    }

因?yàn)閼?yīng)用列表是存在數(shù)據(jù)庫(kù)里面的,那么加鎖解鎖的操作其實(shí)就是修改數(shù)據(jù)庫(kù)中的一個(gè)標(biāo)志位而已。

Demo已經(jīng)初步改版好,地址:https://github.com/lizixian18/AppLock

感謝各位的關(guān)注。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,828評(píng)論 25 709
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,525評(píng)論 19 139
  • 今天中午我剛進(jìn)門,兒子就叫我過(guò)去,我有點(diǎn)不耐煩說(shuō):“干什么?有事就說(shuō)?!薄皨寢?,你到我房間來(lái)看看,你進(jìn)來(lái)時(shí)要...
    董勝杰麻麻閱讀 162評(píng)論 0 2
  • 我們已經(jīng)走遠(yuǎn)了, 距那扇曾經(jīng)生動(dòng)的窗口, 總會(huì)在寂寞的夜里, 靜靜地, 到最初的相逢里去牽手。 恍惚間, 又走在那...
    廈門廚留香創(chuàng)始人曉凡閱讀 222評(píng)論 1 0
  • 孩子頂嘴說(shuō)他沒(méi)規(guī)矩,聽(tīng)話說(shuō)他沒(méi)主見(jiàn); 活潑好動(dòng)擔(dān)心是多動(dòng)癥,內(nèi)向文靜又怕是自閉癥; 打人說(shuō)他太霸道,被打說(shuō)他太懦弱...
    藍(lán)心老師閱讀 268評(píng)論 0 0

友情鏈接更多精彩內(nèi)容