I have created a Service in which I check for foreground activity using 'UsageEvents'.
private void getForegroundActivity() {
String packageName = "";
String className = "";
Calendar cal_begin = Calendar.getInstance();
cal_begin.set(Calendar.YEAR, -1);
long _begTime = cal_begin.getTimeInMillis();
long _endTime = System.currentTimeMillis();
UsageStatsManager usageStatsManager = (UsageStatsManager) this.getSystemService(Context.USAGE_STATS_SERVICE);
if (usageStatsManager != null) {
UsageEvents queryEvents = usageStatsManager.queryEvents(_begTime, _endTime);
if (queryEvents != null) {
UsageEvents.Event event = new UsageEvents.Event();
while (queryEvents.hasNextEvent()) {
UsageEvents.Event eventAux = new UsageEvents.Event();
queryEvents.getNextEvent(eventAux);
if (eventAux.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
event = eventAux;
}
}
packageName = event.getPackageName();
className = event.getClassName();
}
The above code works fine and gives proper foreground activity, but after some time(let's say 2-3 hours later), the 'queryEvents(_begTime, _endTime)'returns '0'(zero) counts !!
Is there some other way of using usageStatsManager.queryEvents() in Service ?
Any other pointers, ideas are appreciated.
The events returned by queryEvents() are kept by the system only for a few days. So, you might have to maintain a local database to maintain those stats locally. There is no problem in your code.
For more information about queryEvents() method and UsageStatsManager, checkout the below link :
UsageStatsManager queryEvents
Related
I'm working on an android app for tracking daily app usage. The idea is that a user can set daily time limit for any app and a notification will appear within at most 2 minutes after the limit is exceeded. (The reason for delay: I've created an alarm system using AlarmManager class that will go off every minute to run a JobIntentService which will check whether limit for any app is exceeded)
I've used queryEvents method of UsageStatsManager class to count app usage time.
Here's my code for counting app usage time (I've followed How to use queryEvents):
HashMap<String, Integer> getTimeSpent(Context context, String packageName, long beginTime, long endTime) {
UsageEvents.Event currentEvent;
List<UsageEvents.Event> allEvents = new ArrayList<>();
HashMap<String, Integer> appUsageMap = new HashMap<>();
UsageStatsManager usageStatsManager = (UsageStatsManager)context.getSystemService(Context.USAGE_STATS_SERVICE);
UsageEvents usageEvents = usageStatsManager.queryEvents(beginTime, endTime);
while (usageEvents.hasNextEvent()) {
currentEvent = new UsageEvents.Event();
usageEvents.getNextEvent(currentEvent);
if(currentEvent.getPackageName().equals(packageName) || packageName == null) {
if (currentEvent.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED
|| currentEvent.getEventType() == UsageEvents.Event.ACTIVITY_PAUSED) {
allEvents.add(currentEvent);
String key = currentEvent.getPackageName();
if (appUsageMap.get(key) == null)
appUsageMap.put(key, 0);
}
}
}
for (int i = 0; i < allEvents.size() - 1; i++) {
UsageEvents.Event E0 = allEvents.get(i);
UsageEvents.Event E1 = allEvents.get(i + 1);
if (E0.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED
&& E1.getEventType() == UsageEvents.Event.ACTIVITY_PAUSED
&& E0.getClassName().equals(E1.getClassName())) {
int diff = (int)(E1.getTimeStamp() - E0.getTimeStamp());
diff /= 1000;
Integer prev = appUsageMap.get(E0.getPackageName());
if(prev == null) prev = 0;
appUsageMap.put(E0.getPackageName(), prev + diff);
}
}
return appUsageMap;
}
In short the above code counts the time difference of the timestamp when an app goes foreground (UsageEvents.Event.ACTIVITY_RESUMED) and the timestamp when it goes background (UsageEvents.Event.ACTIVITY_PAUSED). Then it adds this difference to the total usage time of the app.
The problem is that the amount of time spent on foreground can't be counted unless the app goes background. So, if usage limit is exceeded, notification won't appear until the app goes background.
Is it actually possible to get foreground time while app is on foreground?
N.B. I've tried queryUsageStats along with UsageStats.getTotalTimeInForeground() but couldn't succeed since queryUsageStats had some other issues not related to this question.
I've solved the issue.
Adding difference of current time and timestamp of current running app going foreground does the trick.
I just added the following code before the return statement:
UsageEvents.Event lastEvent = allEvents.get(allEvents.size() - 1);
if(lastEvent.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED) {
int diff = (int)System.currentTimeMillis() - (int)lastEvent.getTimeStamp();
diff /= 1000;
Integer prev = appUsageMap.get(lastEvent.getPackageName());
if(prev == null) prev = 0;
appUsageMap.put(lastEvent.getPackageName(), prev + diff);
}
It is pretty straightforward, I should have thought about it before posting the question.
I'm working on an android app for tracking daily app usage. The idea is that a user can set daily time limit for any app and a notification will appear within at most 2 minutes after the limit is exceeded. (The reason for delay: I've created an alarm system using AlarmManager class that will go off every minute to run a JobIntentService which will check whether limit for any app is exceeded)
I've used queryEvents method of UsageStatsManager class to count app usage time.
Here's my code for counting app usage time (I've followed How to use queryEvents):
HashMap<String, Integer> getTimeSpent(Context context, String packageName, long beginTime, long endTime) {
UsageEvents.Event currentEvent;
List<UsageEvents.Event> allEvents = new ArrayList<>();
HashMap<String, Integer> appUsageMap = new HashMap<>();
UsageStatsManager usageStatsManager = (UsageStatsManager)context.getSystemService(Context.USAGE_STATS_SERVICE);
UsageEvents usageEvents = usageStatsManager.queryEvents(beginTime, endTime);
while (usageEvents.hasNextEvent()) {
currentEvent = new UsageEvents.Event();
usageEvents.getNextEvent(currentEvent);
if(currentEvent.getPackageName().equals(packageName) || packageName == null) {
if (currentEvent.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED
|| currentEvent.getEventType() == UsageEvents.Event.ACTIVITY_PAUSED) {
allEvents.add(currentEvent);
String key = currentEvent.getPackageName();
if (appUsageMap.get(key) == null)
appUsageMap.put(key, 0);
}
}
}
for (int i = 0; i < allEvents.size() - 1; i++) {
UsageEvents.Event E0 = allEvents.get(i);
UsageEvents.Event E1 = allEvents.get(i + 1);
if (E0.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED
&& E1.getEventType() == UsageEvents.Event.ACTIVITY_PAUSED
&& E0.getClassName().equals(E1.getClassName())) {
int diff = (int)(E1.getTimeStamp() - E0.getTimeStamp());
diff /= 1000;
Integer prev = appUsageMap.get(E0.getPackageName());
if(prev == null) prev = 0;
appUsageMap.put(E0.getPackageName(), prev + diff);
}
}
return appUsageMap;
}
In short the above code counts the time difference of the timestamp when an app goes foreground (UsageEvents.Event.ACTIVITY_RESUMED) and the timestamp when it goes background (UsageEvents.Event.ACTIVITY_PAUSED). Then it adds this difference to the total usage time of the app.
The problem is that the amount of time spent on foreground can't be counted unless the app goes background. So, if usage limit is exceeded, notification won't appear until the app goes background.
Is it actually possible to get foreground time while app is on foreground?
N.B. I've tried queryUsageStats along with UsageStats.getTotalTimeInForeground() but couldn't succeed since queryUsageStats had some other issues not related to this question.
I've solved the issue.
Adding difference of current time and timestamp of current running app going foreground does the trick.
I just added the following code before the return statement:
UsageEvents.Event lastEvent = allEvents.get(allEvents.size() - 1);
if(lastEvent.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED) {
int diff = (int)System.currentTimeMillis() - (int)lastEvent.getTimeStamp();
diff /= 1000;
Integer prev = appUsageMap.get(lastEvent.getPackageName());
if(prev == null) prev = 0;
appUsageMap.put(lastEvent.getPackageName(), prev + diff);
}
It is pretty straightforward, I should have thought about it before posting the question.
I am working on a Android TV app (system app) where i am trying to use UsageStatsManager to get recently used app list.
By following approach ,
UsageStatsManager usm = getUsageStatsManager(context);
Calendar calendar = Calendar.getInstance();
long endTime = calendar.getTimeInMillis();
calendar.add(Calendar.YEAR, -1)
final List<UsageStats> usageStatsList = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, startTime, endTime);
Above approach gives right usage data, but after a device reboot it wont give previously used app usage data, until that app has been opened again after reboot .
But same code works fine (after reboot) in an android Phone .
I also tried this sample App from Github in Android TV which also fails to give details after reboot in Android TV (but works in Mobile , both OS version 8) .
Any reason why it's failing to get App usage data for Android TV ?
TIA
#Nainal here is a workaround.
As we had experienced that after a device power cycle , Android Tv box won’t return the usage stats of installed application. Here is a workaround I followed .
We fetch the details of installed application then store only last used time in a Hash map.
And we used a custom comparator to compare via last time used and sort our Installed application details list (ResolveInfo) , for recently used app list.
Now to overcome the situation of device power cycle scenario, We are maintaining a Hash map locally in app . Where we will store the installed App’s Last used time as Long millis (epoch) . lastTimeUsedMap = new HashMap();
And we update the map every time we get a new data from UsageStatsManager.
We simplify the map object and store as string in shared preference.
And after reboot first we will find in usage stats if installed package is having a time stamp or not . If not we will get the time from stored MAP .
Here is the code :
private void SortByRecentlyUsed(final List<ResolveInfo> info, final Context context){
/*load LastTimeUsedMap from pref*/
lastTimeUsedMap = loadLastTimeUsedMap(context);
UsageStatsManager usm = getUsageStatsManager(context);
Calendar calendar = Calendar.getInstance();
long endTime = calendar.getTimeInMillis();
calendar.add(Calendar.MONTH, -1);
long startTime = calendar.getTimeInMillis();
final List<UsageStats> usageStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_BEST, startTime, endTime);
/*Update AppRecentTimeUsedMap with latest data from UsageStatsManager*/
updateAppRecentTimeUsedMap(usageStatsList,info);
class RecentUseComparator implements Comparator<ResolveInfo> {
#Override
public int compare(ResolveInfo lhs, ResolveInfo rhs) {
String lhsPackageName=lhs.activityInfo.applicationInfo.packageName;
String rhsPackageName=rhs.activityInfo.applicationInfo.packageName;
long lhsUsedTime = getLastUsedTime(lhsPackageName);
long rhsUsedTime = getLastUsedTime(rhsPackageName);
return (lhsUsedTime > rhsUsedTime) ? -1 : (lhsUsedTime == rhsUsedTime) ? 0 : 1;
}
private long getLastUsedTime(String packageDetails) {
long appRecentUsedtime = -1;
if (appRecentTimeUsedMap.containsKey(packageDetails)) {
appRecentUsedtime = appRecentTimeUsedMap.get(packageDetails);
}
return appRecentUsedtime;
}
}
RecentUseComparator mRecentComp = new RecentUseComparator();
Collections.sort(info, mRecentComp);
/*Save the updated LastTimeUsedMap in pref*/
saveLastTimeUsedMap(lastTimeUsedMap, context);
}
private void updateAppRecentTimeUsedMap(List<UsageStats> usageStatsList,List<ResolveInfo> info){
String packageName=null;
if (usageStatsList != null) {
for (ResolveInfo Rinfo : info) {
packageName = Rinfo.activityInfo.applicationInfo.packageName;
boolean added = false;
for (UsageStats usageStats : usageStatsList) {
if (packageName.equalsIgnoreCase(usageStats.getPackageName())) {
appRecentTimeUsedMap.put(usageStats.getPackageName(), usageStats.getLastTimeUsed());
updateLastTimeUsedMap(usageStats.getPackageName(), usageStats.getLastTimeUsed());
added=true;
}
}
if (!added && lastTimeUsedMap.containsKey(packageName)) {
appRecentTimeUsedMap.put(packageName, lastTimeUsedMap.get(packageName));
}
}
}
}
private void updateLastTimeUsedMap(String packageName,Long timeStamp){
lastTimeUsedMap.put(packageName, timeStamp);
}
/**
* Return Map containing Package name and recent used time from preference
*
* #param context
* #return Map<String,Long>
*/
private Map<String,Long> loadLastTimeUsedMap(Context context){
Map<String,Long> outputMap = new HashMap<String,Long>();
SharedPreferences pSharedPref = context.getSharedPreferences(LAST_TIME_USED_PREFS, Context.MODE_PRIVATE);
try{
if (pSharedPref != null){
String jsonString = pSharedPref.getString(LAST_TIME_USED_MAP, (new JSONObject()).toString());
JSONObject jsonObject = new JSONObject(jsonString);
Iterator<String> keysItr = jsonObject.keys();
while(keysItr.hasNext()) {
String key = keysItr.next();
Long value = jsonObject.getLong(key);
outputMap.put(key, value);
}
}
}catch(Exception e){
e.printStackTrace();
}
return outputMap;
}
/**
* Save the updated map containing Package name and recent used time in preference
*
* #param inputMap
* #param context
*/
private void saveLastTimeUsedMap(Map<String,Long> inputMap, Context context){
final SharedPreferences sharedPreferences = context.getSharedPreferences(LAST_TIME_USED_PREFS,Context.MODE_PRIVATE);
if (sharedPreferences != null){
JSONObject jsonObject = new JSONObject(inputMap);
String jsonString = jsonObject.toString();
final SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove(LAST_TIME_USED_MAP).commit();
editor.putString(LAST_TIME_USED_MAP, jsonString);
editor.commit();
}
}
I am trying to read when an app was last used using UsageStatsManager Class recently introduced in Android.
But there is a issue. it gives proper result for most of apps. but for some apps for call : appUsageStats.get(appInformation.packageName).getLastTimeUsed() it returns a 4 digit millisecond which is not correct.
Attached is a screen shot of evaluate window showing 8738 for true caller app. Is this a bug or am I doing something wrong?
Note: As an observation this API returns correct results when mLastTimeSystemUsed is same as mLastTimeUsed. Just an FYI.
Code snippet below:
Map<String, UsageStats> appUsageStats = UStats.getMapOfAggregatedUsage(MainActivity.this);
if(appUsageStats.get(appInformation.packageName)!=null) {
long used = appUsageStats.get(appInformation.packageName).getLastTimeUsed();
long installedDiff2 = new Date().getTime() - new Date(used).getTime();
long daysGap = TimeUnit.DAYS.convert(installedDiff2, TimeUnit.MILLISECONDS);
if(daysGap == 0) {
appInfoObj.setAppUsedDate("Used : Today");
} else {
appInfoObj.setAppUsedDate("Used : " + String.valueOf(daysGap) + " days ago");
}
appInfoObj.setAppUsedDateNumber(daysGap);
}
UStats Class Method:
public static Map<String, UsageStats> getMapOfAggregatedUsage(Context context) {
Calendar calendar = Calendar.getInstance();
long endTime = calendar.getTimeInMillis();
calendar.add(Calendar.YEAR, -1);
long startTime = calendar.getTimeInMillis();
return getUsageStatsManager(context).queryAndAggregateUsageStats(startTime,endTime);
// return null;
}
To learn android I'm making a tamagotchi like app. Its food lvl decreases 1 every hour so if you dont feed it for some hours it dies. I also have that between 8pm and 8am its asleep. Only there is a problem. To change its state to sleeping you need to open the app between 8pm and 8am. That gives the following problem:
If you feed it, lets say at 7pm, 1 hour before it sleeps, and you dont open the app between 8pm and 8 am but at 9am the following day he thinks 13 hours have elapsed instead of 1 (he shouldnt count the sleeping hours) Do you guys ahve any tips?
this is the sleepy and decay code
public void checkSleepyTime()
{
c = Calendar.getInstance();
int hour = c.get(Calendar.HOUR_OF_DAY);
int daypart = c.get(Calendar.AM_PM);
if (hour >= 20 && daypart == 1)
{
foodButton.setText("ZZzz");
prefs.edit().remove("foodTime").commit();
buddy.setSleeping(true);
}
else
{
foodButton.setText("Awake");
buddy.setSleeping(false);
}
}
.
public void initBuddy()
{
debugView.setText("FoodLevel: " + buddy.getFoodLevel());
if(!buddy.getSleeping() && buddy.getAlive())
{
long currentTime = prefs.getLong("currentTime", getCurentTime());
long foodTime = prefs.getLong("foodTime", getCurentTime());
while (foodTime < currentTime)
{
if (currentTime - foodTime >= ONE_HOUR)
{
buddy.decayFood();
}
foodTime = foodTime + ONE_HOUR;
}
}
If the time since last feeding is longer than sleep time, check if the expected sleep period falls into that time. Then act accordingly (quick hack would be to add the sleep time to the time of last feeding). Also check if more than one day has passed since the feed time. Something like this:
if (currentTime - foodTime >= ONE_HOUR)
{
if (currentTime - foodTime >= WHOLE_NIGHT && sleepPeriodFitsInBetween(foodTime, currentTime))
{
foodTime+=WHOLE_NIGHT;
int numberOfFullDays=countNumberOfDays(currentTime - foodTime);
if(numberOfFullDays>1)
{
currentTime+=numberOfFullDays*(24-WHOLE_HIGHT); // assuming WHOLE_NIGHT is in hours.
}
}
...
}
I now have the following two methods that get the current time and the close time (saved in onStop();) and i converted it to days/weeks/months/years. I want to check how many nights there are between the closeTIme and the currentTIme but I'm not sure what to do with the info. Can anyone point me in the right direction ?
public void checkSleepTimes()
{
closeCalendar = timeHelper.convertLongToCal(loadCloseTime());
closeArray = loadDateArrayList(closeArray, closeCalendar);
currentCalendar = timeHelper.convertLongToCal(getCurentTime());
closeArray = loadDateArrayList(currentArray, currentCalendar);
long curTime = getCurentTime();
int times = 0;
long totalSleepTime = 0;
while (closeTime < curTime)
{
closeTime = closeTime + WHOLE_DAY;
times ++;
}
//TODO Check how many times it was between 08:00 and 20:00
totalSleepTime = SLEEP_TIME * times; // not sure if correct approach
}
public ArrayList loadDateArrayList(ArrayList arrayList, Calendar calendar)
{
arrayList.add(0,calendar.DATE);
arrayList.add(1,calendar.WEEK_OF_YEAR);
arrayList.add(2,calendar.MONTH);
arrayList.add(3,calendar.YEAR);
return arrayList;
}