先看效果图

源码地址
https://github.com/ZengCS/ZengDemoAlbum
Step1 授权
- 必须确保设备的Android版本>=Android5.1(v22),少部分Android5.0(v21)可以使用UsageStatsManager
- 在AndroidManifest.xml文件中添加权限说明,这是一个system级别的权限,加上tools:ignore="ProtectedPermissions"可以屏蔽调AS的红线(强逼症作怪)
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
- 判断是否可以使用AppUsage功能
public static boolean hasAppUsagePermission(Context context) {
UsageStatsManager usageStatsManager = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
}
if (usageStatsManager == null) {
return false;
}
long currentTime = System.currentTimeMillis();
// try to get app usage state in last 2 min
List<UsageStats> stats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, currentTime - 2 * 60 * 1000, currentTime);
return JListKit.isNotEmpty(stats);
}
- 打开授权页面
public static void requestAppUsagePermission(Context context) {
Intent intent = new Intent(android.provider.Settings.ACTION_USAGE_ACCESS_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.i(TAG, "Start usage access settings activity fail!");
}
}
Step2 根据获取APP使用情况列表
- 根据时间范围获取应用使用情况
private void getAppUsage(long beginTime, long endTime) {
String fmt = "yyyy-MM-dd HH:mm:ss";
mHolder.tvTimeRange.setText(String.format("(%s - %s)",
JDateKit.timeToDate(fmt, beginTime),
JDateKit.timeToDate(fmt, endTime)));
// setTitle("数据分析中...");
showLoading("数据分析中...");
// a task can be executed only once,init is required every time
mLoadAppUsageTask = new LoadAppUsageTask(beginTime, endTime, list -> {
mItems = list;
initAdapter();
});
mLoadAppUsageTask.execute();
}
- LoadAppUsageTask
/**
* Created by ZengCS on 2019/5/30.
* E-mail:zengcs@vip.qq.com
* Add:成都市天府软件园E3
* 加载应用使用情况 Task
*/
public class LoadAppUsageTask extends AsyncTask<Void, Void, ArrayList<AppUsageBean>> {
private static final String TAG = "LoadAppUsageTask";
private Callback mCallback;
private long beginTime, endTime;
public LoadAppUsageTask(long beginTime, long endTime, Callback mCallback) {
this.beginTime = beginTime;
this.endTime = endTime;
this.mCallback = mCallback;
mPackageManager = BaseApplication.getApplication().getPackageManager();
}
// 方法1:onPreExecute()
// 作用:执行 线程任务前的操作
@Override
protected void onPreExecute() {
// 执行前显示提示
Log.d(TAG, "********** 开始获取应用使用情况 **********");
}
// 方法2:doInBackground()
// 作用:接收输入参数、执行任务中的耗时操作、返回 线程任务执行的结果
// 此处通过计算从而模拟“加载进度”的情况
@Override
protected ArrayList<AppUsageBean> doInBackground(Void... voids) {
return readAppUsageList();
}
private ArrayList<AppUsageBean> readAppUsageList() {
ArrayList<AppUsageBean> mItems = JListKit.newArrayList();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
UsageStatsManager usage = (UsageStatsManager) BaseApplication.getApplication().getSystemService(Context.USAGE_STATS_SERVICE);
if (usage == null) return mItems;
// 查询并按包名进行聚合操作
Map<String, UsageStats> statsMap = usage.queryAndAggregateUsageStats(beginTime, endTime);
Set<String> keySet = statsMap.keySet();
for (String packageName : keySet) {
UsageStats usageStats = statsMap.get(packageName);
if (usageStats == null) continue;
long totalTimeInForeground = usageStats.getTotalTimeInForeground();
if (totalTimeInForeground <= 0) continue;// 小于1秒的都按照没有打开过处理
AppUsageBean appUsageBean = new AppUsageBean(packageName, usageStats);
ApplicationInfo info = getAppInfo(packageName);
appUsageBean.setAppInfo(info);
if (info != null) {
// 获取应用名称
String label = (String) info.loadLabel(mPackageManager);
Drawable icon = info.loadIcon(mPackageManager);
appUsageBean.setAppName(label);
appUsageBean.setAppIcon(icon);
} else {
appUsageBean.setAppName("应用已卸载");
// Log.e(TAG, "已经找不到包名为[" + packageName + "]的应用");
}
mItems.add(appUsageBean);
// 打印日志
if (BuildConfig.DEBUG) {
Log.d("UsageStats", "**********************************************");
Log.d("UsageStats", packageName);
// Log.d("UsageStats", "运行时长:" + JDateKit.timeToStringChineChinese(totalTimeInForeground));
Log.d("UsageStats", String.format("运行时长:%s (%sms)", JDateKit.timeToStringChineChinese(totalTimeInForeground), totalTimeInForeground));
String fmt = "yyyy-MM-dd HH:mm:ss.SSS";
Log.d("UsageStats", "开始启动:" + JDateKit.timeToDate(fmt, usageStats.getFirstTimeStamp()));
Log.d("UsageStats", "最后启动:" + JDateKit.timeToDate(fmt, usageStats.getLastTimeStamp()));
Log.d("UsageStats", "最近使用:" + JDateKit.timeToDate(fmt, usageStats.getLastTimeUsed()));
}
}
}
return mItems;
}
private PackageManager mPackageManager;
private ApplicationInfo getAppInfo(String pkgName) {
try {
// ApplicationInfo info = mPackageManager.getApplicationInfo(pkgName, PackageManager.GET_ACTIVITIES);
return mPackageManager.getApplicationInfo(pkgName, 0);
} catch (PackageManager.NameNotFoundException e) {
// e.printStackTrace();
Log.e(TAG, "已经找不到包名为[" + pkgName + "]的应用");
}
return null;
}
// 方法3:onPostExecute()
// 作用:接收线程任务执行结果、将执行结果显示到UI组件
@Override
protected void onPostExecute(ArrayList<AppUsageBean> list) {
Log.d(TAG, "共获取到[" + list.size() + "]个系统应用。");
if (mCallback != null)
mCallback.onPostExecute(list);
}
public interface Callback {
void onPostExecute(ArrayList<AppUsageBean> list);
}
}
Step3 根据packageName获取APP的图标和名称
ApplicationInfo info = getAppInfo(packageName);
if (info != null) {
// 获取应用名称
String label = (String) info.loadLabel(mPackageManager);
Drawable icon = info.loadIcon(mPackageManager);
appUsageBean.setAppName(label);
appUsageBean.setAppIcon(icon);
} else {
appUsageBean.setAppName("应用已卸载");
// Log.e(TAG, "已经找不到包名为[" + packageName + "]的应用");
}
private ApplicationInfo getAppInfo(String pkgName) {
try {
// ApplicationInfo info = mPackageManager.getApplicationInfo(pkgName, PackageManager.GET_ACTIVITIES);
return mPackageManager.getApplicationInfo(pkgName, 0);
} catch (PackageManager.NameNotFoundException e) {
// e.printStackTrace();
Log.e(TAG, "已经找不到包名为[" + pkgName + "]的应用");
}
return null;
}
Step4 设置统计规则(多种)
- 获取今日零点的时间戳
/**
* @return 今日零点的时间
*/
private long getTodayTime0() {
// 获取今天凌晨0点0分0秒的time
Calendar calendar = Calendar.getInstance();
calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH),
0, 0, 0);
return calendar.getTimeInMillis();
}
- Tab点击时,动态刷新数据
public void onTabClick(int position) {
Log.d(TAG, "onTabClick() called with: position = [" + position + "]");
setTitle(TAB_NAMES[position]);
long currTime = System.currentTimeMillis();
switch (position) {
case 0:// 今天的数据 00:00 到 现在
getAppUsage(getTodayTime0(), currTime);
break;
case 1:// 昨天的数据 昨天00:00 - 今天00:00
long todayTime0 = getTodayTime0();
getAppUsage(todayTime0 - DateUtils.DAY_IN_MILLIS, todayTime0);
break;
case 2:// 最近7天数据
getAppUsage(currTime - DateUtils.WEEK_IN_MILLIS, currTime);
break;
case 3:// 最近30天数据
getAppUsage(currTime - DateUtils.DAY_IN_MILLIS * 30, currTime);
break;
case 4:// 最近一年的数据
getAppUsage(currTime - DateUtils.DAY_IN_MILLIS * 365, currTime);
break;
}
}
Step5 将数据展示在RecyclerView
private long maxTime;// 当前列表中 使用最久的APP时间 用于计算进度条百分比
private void initAdapter() {
if (JListKit.isNotEmpty(mItems)) {
Collections.sort(mItems);// 按使用时长排序
maxTime = mItems.get(0).getTotalTimeInForeground();
} else {
maxTime = 1;
}
setTitle(String.format("%s (共%s条)", getTitle(), mItems.size()));
if (mAdapter == null) {
String fmt = "yyyy-MM-dd HH:mm:ss";
mAdapter = new CommonRecyclerAdapter<AppUsageBean>(R.layout.item_app_usage, mItems) {
@Override
protected void convert(@NonNull BaseViewHolder helper, AppUsageBean item) {
helper.setText(R.id.id_tv_app_name, String.format("%s(%s)", item.getAppName(), item.getPackageName()));
Drawable appIcon = item.getAppIcon();
if (appIcon != null) {
helper.setImageDrawable(R.id.id_iv_app_icon, appIcon);
} else {
helper.setImageResource(R.id.id_iv_app_icon, R.mipmap.ic_launcher);
}
long totalTimeInForeground = item.getTotalTimeInForeground();
helper.setText(R.id.id_tv_time_in_foreground, String.format("使用时长:%s (%sms)", JDateKit.timeToStringChineChinese(totalTimeInForeground), totalTimeInForeground));
helper.setText(R.id.id_tv_last_usage, String.format("上次使用:%s", JDateKit.timeToDate(fmt, item.getLastTimeUsed())));
// 计算进度条百分比
float percent = (float) item.getTotalTimeInForeground() / maxTime;
Guideline guideline = helper.getView(R.id.guideline);
guideline.setGuidelinePercent(percent);
}
};
mHolder.rvAppUsage.setAdapter(mAdapter);
mHolder.rvAppUsage.setLayoutManager(new LinearLayoutManager(this));
} else {
mAdapter.setNewInstance(mItems);
}
hideLoading();
}
网友评论