1. 問題描述
需要實現(xiàn)從我們app跳轉到第三方app廉沮,并統(tǒng)計第三方app在前臺所待時長的功能逮壁。
2. 問題分析
大概過程如下:
1)首先得有權限孵坚,我們這個需要實現(xiàn)允許查看應用使用情況、懸浮窗兩個權限窥淆。
2)跳轉到第三方app卖宠;
3)啟動一個服務監(jiān)聽;
4)彈出一個懸浮窗
5)監(jiān)聽第三方app是否在前臺運行忧饭。實現(xiàn)思路是啟動一個定時器扛伍,每隔1s去查看前臺應用信息。
6)預期時間完成词裤,銷毀懸浮窗刺洒,解綁Service汁咏。
3. 實現(xiàn)過程
3.1 懸浮窗
- 懸浮窗權限
當我們自己app在后臺運行時,無法彈出一個Toast或者Dialog作媚,所以得做一個類似于360安全衛(wèi)士一樣的懸浮窗攘滩。所以得要申請懸浮窗權限。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)申請漂问,需要跳轉到系統(tǒng)設置中去獲取:
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();
//設置type.系統(tǒ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;
}
//設置效果為背景透明.
params.format = PixelFormat.RGBA_8888;
//設置flags.不可聚焦及不可使用按鈕對懸浮窗進行操控.
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
//設置窗口初始土籽觯靠位置.
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以前是通過獲取手機當前活躍進程列表灶平,5.0后這種辦法用不了了,5.0以后通過UsageStatsManager(統(tǒng)計服務類)來獲取箍土。
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)計服務類)
- 申請“允許查看應用使用情況”。
5.0以后查看應用使用情況
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions"/>
在代碼中吴藻,我們也做一層判斷瞒爬,是否已經獲取該權限,如果沒有沟堡,則跳轉到設置權限界面:
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)計服務類
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());
//獲取前臺應用侧但,排除其他應用因通知欄而產生的統(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;
}