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