javaandroidusage-statistics

Android: get UsageStats per hour


I use UsageStats feature of Android, but the smallest interval is DAILY INTERVAL.

long time = System.currentTimeMillis();
List<UsageStats> appList = manager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - DAY_IN_MILLI_SECONDS, time);

How can I get UsageStats in an hourly interval?


Solution

  • All credit goes to this answer. I have learned from that one.

    How can we collect app usage data for customized time range (e.g. for per 1 hour)?

    We have to call queryEvents(long begin_time, long end_time) method as it will provide us all data starting from begin_time to end_time. It give us each app data through foreground and background events instead of total spent time like queryUsageStats() method. So, using foreground and background events time stamp, we can count the number of times an app has been launched and also can find out the usage duration for each app.

    Implementation to Collect Last 1 Hour App Usage Data

    At first, add the following line in the AndroidManifest.xml file and also request user to get permission of usage access.

    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
    

    Add the following lines inside any method

        long hour_in_mil = 1000*60*60; // In Milliseconds
        long end_time = System.currentTimeMillis();
        long start_time = end_time - hour_in_mil;
    

    Then, call the method getUsageStatistics()

        getUsageStatistics(start_time, end_time);
    

    getUsageStatistics methiod

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    void getUsageStatistics(long start_time, long end_time) {
    
        UsageEvents.Event currentEvent;
      //  List<UsageEvents.Event> allEvents = new ArrayList<>();
        HashMap<String, AppUsageInfo> map = new HashMap<>();
        HashMap<String, List<UsageEvents.Event>> sameEvents = new HashMap<>();
    
        UsageStatsManager mUsageStatsManager = (UsageStatsManager)
                context.getSystemService(Context.USAGE_STATS_SERVICE);
    
        if (mUsageStatsManager != null) {
            // Get all apps data from starting time to end time
            UsageEvents usageEvents = mUsageStatsManager.queryEvents(start_time, end_time);
    
            // Put these data into the map
            while (usageEvents.hasNextEvent()) {
                currentEvent = new UsageEvents.Event();
                usageEvents.getNextEvent(currentEvent);
                if (currentEvent.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED ||
                        currentEvent.getEventType() == UsageEvents.Event.ACTIVITY_PAUSED) {
                  //  allEvents.add(currentEvent);
                    String key = currentEvent.getPackageName();
                    if (map.get(key) == null) {
                        map.put(key, new AppUsageInfo(key));
                        sameEvents.put(key,new ArrayList<UsageEvents.Event>());
                    }
                    sameEvents.get(key).add(currentEvent);
                }
            }
    
            // Traverse through each app data which is grouped together and count launch, calculate duration
            for (Map.Entry<String,List<UsageEvents.Event>> entry : sameEvents.entrySet()) {
                int totalEvents = entry.getValue().size();
                if (totalEvents > 1) {
                    for (int i = 0; i < totalEvents - 1; i++) {
                        UsageEvents.Event E0 = entry.getValue().get(i);
                        UsageEvents.Event E1 = entry.getValue().get(i + 1);
    
                        if (E1.getEventType() == 1 || E0.getEventType() == 1) {
                            map.get(E1.getPackageName()).launchCount++;
                        }
    
                        if (E0.getEventType() == 1 && E1.getEventType() == 2) {
                            long diff = E1.getTimeStamp() - E0.getTimeStamp();
                            map.get(E0.getPackageName()).timeInForeground += diff;
                        }
                    }
                }
    
        // If First eventtype is ACTIVITY_PAUSED then added the difference of start_time and Event occuring time because the application is already running.
                if (entry.getValue().get(0).getEventType() == 2) {
                    long diff = entry.getValue().get(0).getTimeStamp() - start_time;
                    map.get(entry.getValue().get(0).getPackageName()).timeInForeground += diff;
                }
                
        // If Last eventtype is ACTIVITY_RESUMED then added the difference of end_time and Event occuring time because the application is still running .
                if (entry.getValue().get(totalEvents - 1).getEventType() == 1) {
                    long diff = end_time - entry.getValue().get(totalEvents - 1).getTimeStamp();
                    map.get(entry.getValue().get(totalEvents - 1).getPackageName()).timeInForeground += diff;
                }
            }
        
        smallInfoList = new ArrayList<>(map.values());
    
        // Concatenating data to show in a text view. You may do according to your requirement
        for (AppUsageInfo appUsageInfo : smallInfoList)
        {
            // Do according to your requirement
            strMsg = strMsg.concat(appUsageInfo.packageName + " : " + appUsageInfo.launchCount + "\n\n");
        }
    
        TextView tvMsg = findViewById(R.id.MA_TvMsg);
        tvMsg.setText(strMsg);
           
        } else {
            Toast.makeText(context, "Sorry...", Toast.LENGTH_SHORT).show();
        }
    
    }
    

    AppUsageInfo.class

    import android.graphics.drawable.Drawable;
    
    class AppUsageInfo {
        Drawable appIcon; // You may add get this usage data also, if you wish.
        String appName, packageName;
        long timeInForeground;
        int launchCount;
    
        AppUsageInfo(String pName) {
            this.packageName=pName;
        }
    }
    

    How can I customize these codes to collect per 1 hour data?

    As you want to get per hour data, please change the end_time and start_time value for every hour data. For instance: If I would try to collect past per hour data (for past 2 hour data). I would do the following thing.

        long end_time = System.currentTimeMillis();
        long start_time = end_time - (1000*60*60);
    
        getUsageStatistics(start_time, end_time);
    
        end_time =  start_time;
        start_time = start_time - hour_in_mil;
    
        getUsageStatistics(start_time, end_time);
    

    However, you may use a Handler to skip repeatedly writing start_time and end_time to change value of these variables. Each time data will be collected for one hour, a task will be completed and after automatically changing the values of the variables, you will again call the getUsageStatistics method.

    Note: Maybe, you will not be able to retrieve data for more than past 7.5 days as events are only kept by the system for a few days.