|
|
@@ -0,0 +1,208 @@
|
|
|
+package com.atmob.mobile_use_statistics.core;
|
|
|
+
|
|
|
+import android.Manifest;
|
|
|
+import android.app.Activity;
|
|
|
+import android.app.AppOpsManager;
|
|
|
+import android.app.usage.UsageEvents;
|
|
|
+import android.app.usage.UsageStatsManager;
|
|
|
+import android.content.Context;
|
|
|
+import android.content.Intent;
|
|
|
+import android.content.pm.PackageManager;
|
|
|
+import android.net.Uri;
|
|
|
+import android.os.Build;
|
|
|
+
|
|
|
+import com.atmob.mobile_use_statistics.bean.DeviceUsageInfo;
|
|
|
+
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+public class UsageStats {
|
|
|
+
|
|
|
+ private static final String TAG = "UsageStats";
|
|
|
+
|
|
|
+ public static void requestPermission(Context context) {
|
|
|
+ Intent intent = new Intent(android.provider.Settings.ACTION_USAGE_ACCESS_SETTINGS)
|
|
|
+ .setData(Uri.fromParts("package", context.getPackageName(), null));
|
|
|
+ if (!(context instanceof Activity)) {
|
|
|
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
+ }
|
|
|
+ context.startActivity(intent);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static boolean hasPermission(Context context) {
|
|
|
+ boolean granted;
|
|
|
+ AppOpsManager appOps = (AppOpsManager) context
|
|
|
+ .getSystemService(Context.APP_OPS_SERVICE);
|
|
|
+ int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
|
|
|
+ android.os.Process.myUid(), context.getPackageName());
|
|
|
+
|
|
|
+ if (mode == AppOpsManager.MODE_DEFAULT) {
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
+ granted = (context.checkCallingOrSelfPermission(Manifest.permission.PACKAGE_USAGE_STATS) == PackageManager.PERMISSION_GRANTED);
|
|
|
+ } else {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ granted = (mode == AppOpsManager.MODE_ALLOWED);
|
|
|
+ }
|
|
|
+ return granted;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static List<DeviceUsageInfo.Event> queryAndAggregateAppUsageData(Context context, long startTime, long endTime) {
|
|
|
+ UsageEvents usageEvents = queryAppUsageData(context, startTime, endTime);
|
|
|
+ if (usageEvents == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // App usage tracking
|
|
|
+// Map<String, AppUsageState> packageStateMap = new HashMap<>();
|
|
|
+ // Device usage tracking
|
|
|
+ DeviceUsageState deviceUsageState = new DeviceUsageState();
|
|
|
+
|
|
|
+ UsageEvents.Event event = new UsageEvents.Event();
|
|
|
+
|
|
|
+ while (usageEvents.getNextEvent(event)) {
|
|
|
+ String packageName = event.getPackageName();
|
|
|
+ long timeStamp = event.getTimeStamp();
|
|
|
+ int eventType = event.getEventType();
|
|
|
+
|
|
|
+ // Handle system events
|
|
|
+ if ("android".equals(packageName)) {
|
|
|
+ if (eventType == UsageEvents.Event.SCREEN_INTERACTIVE) {
|
|
|
+ deviceUsageState.onScreenOn(timeStamp);
|
|
|
+ } else if (eventType == UsageEvents.Event.SCREEN_NON_INTERACTIVE) {
|
|
|
+ deviceUsageState.onScreenOff(timeStamp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Handle app events
|
|
|
+// else if (!AppUsageUtil.isSystemApp(context,packageName)) {
|
|
|
+// AppUsageState state = packageStateMap.get(packageName);
|
|
|
+// if (state == null) {
|
|
|
+// state = new AppUsageState();
|
|
|
+// packageStateMap.put(packageName, state);
|
|
|
+// }
|
|
|
+//
|
|
|
+// if (eventType == UsageEvents.Event.ACTIVITY_RESUMED) {
|
|
|
+// state.onActivityResumed(timeStamp);
|
|
|
+// } else if (eventType == UsageEvents.Event.ACTIVITY_PAUSED) {
|
|
|
+// state.onActivityPausedOrStop(timeStamp);
|
|
|
+// } else if (eventType == UsageEvents.Event.ACTIVITY_STOPPED) {
|
|
|
+// state.onActivityPausedOrStop(timeStamp);
|
|
|
+// } else if (eventType == 10) {
|
|
|
+// //NOTIFICATION_SEEN
|
|
|
+// LogUtil.d(TAG, "Notification seen for package: " + packageName + " at " + timeStamp);
|
|
|
+// } else if (eventType == 12) {
|
|
|
+// //NOTIFICATION_INTERRUPTION
|
|
|
+// LogUtil.d(TAG, "Notification interruption for package: " + packageName + " at " + timeStamp);
|
|
|
+// }
|
|
|
+// }
|
|
|
+ }
|
|
|
+
|
|
|
+ deviceUsageState.finishIncompleteEvent(0);
|
|
|
+
|
|
|
+ // Build app usage info list
|
|
|
+// List<AppUsageInfo> newAppUsageInfos = new ArrayList<>();
|
|
|
+// for (Map.Entry<String, AppUsageState> entry : packageStateMap.entrySet()) {
|
|
|
+// String packageName = entry.getKey();
|
|
|
+// AppUsageState state = entry.getValue();
|
|
|
+//
|
|
|
+// // Skip apps with no usage
|
|
|
+// if (state.events.isEmpty()) {
|
|
|
+// continue;
|
|
|
+// }
|
|
|
+//
|
|
|
+// // Get app name (cached)
|
|
|
+// String appName = appNameCache.get(packageName);
|
|
|
+// if (appName == null) {
|
|
|
+// appName = AppUsageUtil.getAppName(packageName);
|
|
|
+// appNameCache.put(packageName, appName);
|
|
|
+// }
|
|
|
+//
|
|
|
+// newAppUsageInfos.add(new AppUsageInfo(packageName, appName, state.events));
|
|
|
+// }
|
|
|
+
|
|
|
+ return deviceUsageState.events;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static UsageEvents queryAppUsageData(Context context, long begin, long end) {
|
|
|
+ UsageStatsManager manager =
|
|
|
+ (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
|
|
|
+
|
|
|
+ return manager.queryEvents(begin, end);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// private static class AppUsageState {
|
|
|
+// int count;
|
|
|
+// long currentStartTime = -1; // -1 means no active event
|
|
|
+// final List<AppUsageInfo.Event> events = new ArrayList<>();
|
|
|
+//
|
|
|
+// void onActivityResumed(long timeStamp) {
|
|
|
+// count++;
|
|
|
+// if (count == 1) {
|
|
|
+// // Transition from 0 to 1, start new event
|
|
|
+// currentStartTime = timeStamp;
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// void onActivityPausedOrStop(long timeStamp) {
|
|
|
+// if (count <= 0) {
|
|
|
+// count = 0;
|
|
|
+// return;
|
|
|
+// }
|
|
|
+//
|
|
|
+// count--;
|
|
|
+// if (count == 0) {
|
|
|
+// // Transition to 0, complete event
|
|
|
+// events.add(new AppUsageInfo.Event(currentStartTime, timeStamp));
|
|
|
+// currentStartTime = -1;
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// void finishIncompleteEvent(long endTime) {
|
|
|
+// if (count > 0 && currentStartTime != -1) {
|
|
|
+// events.add(new AppUsageInfo.Event(currentStartTime, endTime));
|
|
|
+// currentStartTime = -1;
|
|
|
+// count = 0;
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+ private static class DeviceUsageState {
|
|
|
+ int count;
|
|
|
+ long currentStartTime = -1; // -1 means no active event
|
|
|
+ final List<DeviceUsageInfo.Event> events = new ArrayList<>();
|
|
|
+
|
|
|
+ void onScreenOn(long timeStamp) {
|
|
|
+ count++;
|
|
|
+ if (count == 1) {
|
|
|
+ // Transition from 0 to 1, start new event
|
|
|
+ currentStartTime = timeStamp;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void onScreenOff(long timeStamp) {
|
|
|
+ if (count <= 0) {
|
|
|
+ count = 0;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ count--;
|
|
|
+ if (count == 0) {
|
|
|
+ // Transition to 0, complete event
|
|
|
+ events.add(new DeviceUsageInfo.Event(currentStartTime, timeStamp));
|
|
|
+ currentStartTime = -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void finishIncompleteEvent(long endTime) {
|
|
|
+ if (count > 0 && currentStartTime != -1) {
|
|
|
+ events.add(new DeviceUsageInfo.Event(currentStartTime, endTime));
|
|
|
+ currentStartTime = -1;
|
|
|
+ count = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|