Android跳轉(zhuǎn)到第三方app,運行時長監(jiān)聽

1. 問題描述

需要實現(xiàn)從我們app跳轉(zhuǎn)到第三方app,并統(tǒng)計第三方app在前臺所待時長的功能。

2. 問題分析

大概過程如下:
1)首先得有權(quán)限,我們這個需要實現(xiàn)允許查看應(yīng)用使用情況懸浮窗兩個權(quán)限。
2)跳轉(zhuǎn)到第三方app;
3)啟動一個服務(wù)監(jiān)聽;
4)彈出一個懸浮窗
5)監(jiān)聽第三方app是否在前臺運行。實現(xiàn)思路是啟動一個定時器,每隔1s去查看前臺應(yīng)用信息。
6)預(yù)期時間完成,銷毀懸浮窗,解綁Service。

3. 實現(xiàn)過程

3.1 懸浮窗
  • 懸浮窗權(quán)限
    當(dāng)我們自己app在后臺運行時,無法彈出一個Toast或者Dialog,所以得做一個類似于360安全衛(wèi)士一樣的懸浮窗。所以得要申請懸浮窗權(quán)限。6.0之前只需要在AndroidManifest文件中申請:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW"/>

6.0之后,代碼中動態(tài)申請,需要跳轉(zhuǎn)到系統(tǒng)設(shè)置中去獲?。?/p>

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
    if (!Settings.canDrawOverlays(MainActivity.this)){
         Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
         startActivityForResult(intent, REQUESTCODE_OVER);
    }
}
  • 顯示一個懸浮窗
    顯示一個懸浮窗,首先寫一個布局文件,然后添加到WindowManager中。
    // LayoutInflater.from中Context用getApplicationContext()
    View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.layout_window,null);
    WindowManager mWindowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);
    WindowManager.LayoutParams params = new WindowManager.LayoutParams();
    //設(shè)置type.系統(tǒng)提示型窗口,一般都在應(yīng)用程序窗口之上.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
        params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    }else{
        params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
    }
    //設(shè)置效果為背景透明.
    params.format = PixelFormat.RGBA_8888;
    //設(shè)置flags.不可聚焦及不可使用按鈕對懸浮窗進行操控.
    params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

    //設(shè)置窗口初始停靠位置.
    params.gravity = Gravity.LEFT | Gravity.TOP;
    params.x = 0;
    params.y = 0;

    params.width = WindowManager.LayoutParams.MATCH_PARENT;
    params.height = WindowManager.LayoutParams.WRAP_CONTENT;

    mWindowManager.addView(view,params);

注意:在Android 8.0后 params.type變化。具體參考: Android 8.0 懸浮窗變動與用法

3.2 監(jiān)聽app在前臺運行

Android檢測app運行在前臺,5.0以前是通過獲取手機當(dāng)前活躍進程列表,5.0后這種辦法用不了了,5.0以后通過UsageStatsManager(統(tǒng)計服務(wù)類)來獲取。

1)5.0之前

通過ActivityManager獲取運行app進程來判斷app是否處于前臺。

public boolean isRunningForeground(Context context,String packageName){
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningAppProcessInfo> processes = activityManager.getRunningAppProcesses();
    for (ActivityManager.RunningAppProcessInfo processInfo: processes) {

        if (processInfo.processName.equals(context.getPackageName())) {
            if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                return true;
            }
        }
    }
    return false;
}
2)5.0之后

Android5.0之后通過UsageStatsManager(統(tǒng)計服務(wù)類)

  • 申請“允許查看應(yīng)用使用情況”。
    5.0以后查看應(yīng)用使用情況
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
        tools:ignore="ProtectedPermissions"/>

在代碼中,我們也做一層判斷,是否已經(jīng)獲取該權(quán)限,如果沒有,則跳轉(zhuǎn)到設(shè)置權(quán)限界面:

Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivityForResult(intent,REQUESTCODE_USAGE);
  • 獲取最近運行app
public String getRunningPackageNameOver21(Context context){
    String topPackageName = "";
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
        return topPackageName;
    }
    // 1.獲取統(tǒng)計服務(wù)類
    UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
    long currTime = System.currentTimeMillis();
    //2.獲取從今天0點到現(xiàn)在的使用情況
    List<UsageStats> usageStatsList = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, getStartTime(),currTime);
    //3.根據(jù)最后使用時間降序排列
    Collections.sort(usageStatsList,new UsageComparator());

    //獲取前臺應(yīng)用,排除其他應(yīng)用因通知欄而產(chǎn)生的統(tǒng)計信息
    Field mLastEventField = null;
    try {
        mLastEventField = UsageStats.class.getField("mLastEvent");
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    }
    for (UsageStats usageStats:usageStatsList){
        if (mLastEventField != null){
            int lastEvent = 0;
            try {
                lastEvent =  mLastEventField.getInt(usageStats);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

            if (lastEvent == 1){
                topPackageName = usageStats.getPackageName();
//                        Log.i(TAG,"包名:" + usageStats.getPackageName() + ",:" + dateFormat.format(new Date(date)));
                return topPackageName;
            }
        }
    }
    return topPackageName;
}

4. 最后效果

device-2018-03-30-155006.gif

源碼:https://github.com/AnXy1218/AppRunTime

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

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