Android: get UsageStats per hour - java

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?

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.

Calendar cal = (Calendar) Calendar.getInstance().clone();
//I used this and it worked, only for 7 days and a half ago
if (daysAgo == 0) {
//Today - I only count from 00h00m00s today to present
end = cal.getTimeInMillis();
start = LocalDate.now().toDateTimeAtStartOfDay().toInstant().getMillis();
} else {
long todayStartOfDayTimeStamp = LocalDate.now().toDateTimeAtStartOfDay().toInstant().getMillis();
if (mDaysAgo == -6) {
//6 days ago, only get events in time -7 days to -7.5 days
cal.setTimeInMillis(System.currentTimeMillis());
cal.add(Calendar.DATE, daysAgo + 1);
end = cal .getTimeInMillis();
start = end - 43200000;
} else {
//get events from 00h00m00s to 23h59m59s
//Current calendar point to 0h0m today
cal.setTimeInMillis(todayStartOfDayTimeStamp);
cal.add(Calendar.DATE, daysAgo + 1);
end = calendar.getTimeInMillis();
cal.add(Calendar.DATE, -1);
start = calendar.getTimeInMillis();
}
}

I don't think it's possible, even if you ask for data in the middle of an interval, it looks like the data is stored in buckets and the minimum bucket is a day.
In UsageStatsManager documentation, it says:
A request for data in the middle of a time interval will include that interval.
Also, INTERVAL_BEST is not a real interval, it just selects one of the available intervals for the given time range. In
UsageStatsManager.java source code, it says:
/**
* The number of available intervals. Does not include {#link #INTERVAL_BEST}, since it
* is a pseudo interval (it actually selects a real interval).
* {#hide}
*/
public static final int INTERVAL_COUNT = 4;

Yes, Android is providing minimum INTERVAL_DAILY. But for the best result, you can use INTERVAL_BEST. Android is giving the best interval timer for the given time range in queryUsageStats(int, long, long).
Happy coding...

Related

How to return sum of LocalTime in Java

It wont get the minutes. i need to return minutes.
How to return sum of minutes while iterating over Localtime in Java?
public String userLunchHoursSum(String username) {
List<WorkHour> workHours = workHourRepository.findWorkHoursByUsername(username);
System.out.println(Arrays.toString(workHours.toArray()));
long diff = 0;
LocalTime lunchTime;
long minutes = 0;
LocalTime plusMinutes = null;
for (WorkHour workHour : workHours) {
lunchTime = workHour.getLunch_time().toLocalTime(); //00:30:00
plusMinutes = lunchTime.plusMinutes(lunchTime.getMinute());
}
if(workHours.size()!= 0) {
return Long.toString(plusMinutes.getMinute());
}
return "00:00";
}
getLunch_time returns java.sql.Time.
As mentioned, you should be storing duration instead of localtime. If this is something you have no control over, consider migrating the database or creating a intermediate parsing function. Example code that I have not run, because I don't know what is in WorkHour.
// leave the string formatting to other functions
public long userLunchHoursSum(String username) {
List<WorkHour> workHours = workHourRepository.findWorkHoursByUsername(username);
Duration totalDuration = Duration.ZERO;
for (WorkHour workHour : workHours) {
// save your time in the appropriate format beforehand
// do not use local time to store duration.
Duration lunchTime = Duration.between(LocalTime.MIDNIGHT, workHour.getLunch_time().toLocalTime()); //00:30:00
totalDuration = totalDuration.plus(lunchTime);
}
return totalDuration.toMinutes();
}

Apache Flink: Wierd FlatMap behaviour

I'm ingesting a stream of data into Flink. For each 'instance' of this data, I have a timestamp. I can detect if the machine I'm getting the data from is 'producing' or 'not producing', this is done via a custom flat map function that's located in it's own static class.
I want to calculate how long the machine has been producing / not producing.
My current approach is collecting the production and non production timestamps in two plain lists. For each 'instance' of the data, I calculate the current production/non-production duration by subtracting the latest timestamp from the earliest timestamp. This is giving me incorrect results, though. When the production state changes from producing to non producing, I clear the timestamp list for producing and vice versa, so that if the production starts again, the duration starts from zero.
I've looked into the two lists I collect the respective timestamps in and I see things I don't understand. My assumption is that, as long as the machine 'produces', the first timestamp in the production timestamp list stays the same, while new timestamps are added to the list per new instance of data.
Apparantly, this assumption is wrong since I get seemingly random timestamps in the lists. They are still correctly ordered, though.
Here's my code for the flatmap function:
public static class ImaginePaperDataConverterRich extends RichFlatMapFunction<ImaginePaperData, String> {
private static final long serialVersionUID = 4736981447434827392L;
private transient ValueState<ProductionState> stateOfProduction;
SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SS");
DateFormat timeDiffFormat = new SimpleDateFormat("dd HH:mm:ss.SS");
String timeDiffString = "00 00:00:00.000";
List<String> productionTimestamps = new ArrayList<>();
List<String> nonProductionTimestamps = new ArrayList<>();
public String calcProductionTime(List<String> timestamps) {
if (!timestamps.isEmpty()) {
try {
Date firstDate = dateFormat.parse(timestamps.get(0));
Date lastDate = dateFormat.parse(timestamps.get(timestamps.size()-1));
long timeDiff = lastDate.getTime() - firstDate.getTime();
if (timeDiff < 0) {
System.out.println("Something weird happened. Maybe EOF.");
return timeDiffString;
}
timeDiffString = String.format("%02d %02d:%02d:%02d.%02d",
TimeUnit.MILLISECONDS.toDays(timeDiff),
TimeUnit.MILLISECONDS.toHours(timeDiff) % TimeUnit.HOURS.toHours(1),
TimeUnit.MILLISECONDS.toMinutes(timeDiff) % TimeUnit.HOURS.toMinutes(1),
TimeUnit.MILLISECONDS.toSeconds(timeDiff) % TimeUnit.MINUTES.toSeconds(1),
TimeUnit.MILLISECONDS.toMillis(timeDiff) % TimeUnit.SECONDS.toMillis(1));
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println("State duration: " + timeDiffString);
}
return timeDiffString;
}
#Override
public void open(Configuration config) {
ValueStateDescriptor<ProductionState> descriptor = new ValueStateDescriptor<>(
"stateOfProduction",
TypeInformation.of(new TypeHint<ProductionState>() {}),
ProductionState.NOT_PRODUCING);
stateOfProduction = getRuntimeContext().getState(descriptor);
}
#Override
public void flatMap(ImaginePaperData ImaginePaperData, Collector<String> output) throws Exception {
List<String> warnings = new ArrayList<>();
JSONObject jObject = new JSONObject();
String productionTime = "0";
String nonProductionTime = "0";
// Data analysis
if (stateOfProduction == null || stateOfProduction.value() == ProductionState.NOT_PRODUCING && ImaginePaperData.actSpeedCl > 60.0) {
stateOfProduction.update(ProductionState.PRODUCING);
} else if (stateOfProduction.value() == ProductionState.PRODUCING && ImaginePaperData.actSpeedCl < 60.0) {
stateOfProduction.update(ProductionState.NOT_PRODUCING);
}
if(stateOfProduction.value() == ProductionState.PRODUCING) {
if (!nonProductionTimestamps.isEmpty()) {
System.out.println("Production has started again, non production timestamps cleared");
nonProductionTimestamps.clear();
}
productionTimestamps.add(ImaginePaperData.timestamp);
System.out.println(productionTimestamps);
productionTime = calcProductionTime(productionTimestamps);
} else {
if(!productionTimestamps.isEmpty()) {
System.out.println("Production has stopped, production timestamps cleared");
productionTimestamps.clear();
}
nonProductionTimestamps.add(ImaginePaperData.timestamp);
warnings.add("Production has stopped.");
System.out.println(nonProductionTimestamps);
//System.out.println("Production stopped");
nonProductionTime = calcProductionTime(nonProductionTimestamps);
}
// The rest is just JSON stuff
Do I maybe have to hold these two timestamp lists in a ListState?
EDIT: Because another user asked, here is the data I'm getting.
{'szenario': 'machine01', 'timestamp': '31.10.2018 09:18:39.432069', 'data': {1: 100.0, 2: 100.0, 101: 94.0, 102: 120.0, 103: 65.0}}
The behaviour I expect is that my flink program collects the timestamps in the two lists productionTimestamps and nonProductionTimestamps. Then I want my calcProductionTime method to subtract the last timestamp in the list from the first timestamp, to get the duration between when I first detected the machine is "producing" / "not-producing" and the time it stopped "producing" / "not-producing".
I found out that the reason for the 'seemingly random' timestamps is Apache Flink's parallel execution. When the parallelism is set to > 1, the order of events isn't guaranteed anymore.
My quick fix was to set the parallelism of my program to 1, this guarantees the order of events, as far as I know.

How to calculate number of documents per hour depending on their "last modified"

I'm working on a tool to count archived files from another program. Therefor I'm using the DirectoryStream and filter subdirectories and some files with a simple if-clause (shown below).
For the statistics I would like to know, how many documents were created per hour on average.
I'm not very experienced in working with files and directories, but I guess there is some sort of "getLastModified", getting the Timerange from oldest to youngest and the calculate the average number of docs per hour?
Well, files have a lastModified() method, returning the timestamp of last modification. It returns 0 if the file does not exist or an I/O error occurred. To convert a Path to a File you can use the toFile() method. With that, it will be rather easy to calculate the files/hour average:
long minTimestamp = Long.MAX_VALUE; // definitely greater than any timestamp you will ever find
long maxTimestamp = 0;
int count = 0;
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get("DIRECTORY PATH"))) {
for(Path path: directoryStream) {
if (!(Files.isDirectory(path) || path.toString().endsWith("\\databaseinfo.xml") || path.toString().endsWith(".log"))) {
long lastModified = path.toFile().lastModified();
if (lastModified > 0L) { // check that no error occurred
if (lastModified < minTimestamp) minTimestamp = lastModified; // new minimum
if (maxTimestamp < lastModified) maxTimestamp = lastModified; // new maximum
}
count = count + 1;
}
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(count);
double filesPerHour = 0;
if (maxTimestamp != minTimestamp) { // avoid division by 0
filesPerHour = (double) count * 60 * 60 * 1000 / (maxTimestamp - minTimestamp); // 60 * 60 * 1000 = milliseconds in one hour
}
System.out.println(filesPerHour);
Edit: Inverted the if condition, to avoid the empty if statement which had code in the else block

java code to use timestamp items inside a loop

ResultSet rs2 = stmt2.executeQuery("SELECT starttime FROM user_req ORDER BY req_id ASC LIMIT 1");
Timestamp a;
while (rs2.next()) {
a = rs2.getTimestamp("starttime");
System.out.println(a);
long t=a.getTime();
long m=5*60*1000;
Timestamp b= new Timestamp(t+m);
System.out.println(b);
}
This code produces two timestamps: 2013-12-12 09:00:00 and 2013-12-12 09:05:00.
In this code I get two timestamp items one with adding 5 min to the previous one, can anyone pls help me out in how to put it in a for loop such that it should start wd the "timestamp a" till "timestamp b" and it should increment with 1 minute, so that I can perform roundrobin code in it. please help me how to iterate over the timestamps.
Simply:
for(long current = a ; current <= a+5*60*1000 ; current += 60*1000) {
// do something with current
}
You can use :
long starTime=a.getTime();
long endTime=starTime+5*60*1000;
for (long currTime = starTime; currTime < endTime; currTime += 60*1000) {
// Do your stuff, currTime will increment in 1 minute steps
}

How to Convert TimeStamps to Ticks (PPQ) - Real Time Midi

I'm currently reading in MIDI messages in real-time from my midi keyboard using a class that implements Receiver, and outputting the notes played.
The only information i receive when I press a note is the MidiMessage and a timeStamp.
I am trying to paint the notes as actual piano sheet music and currently the user has to set the bpm beforehand.
Therefore if I know the tempo is 120bpm (for example), how can I use the timeStamps to determine the length of the note pressed?
I'm assuming if I can convert the timeStamps into ticks (ppq), then I can use that to work out the timings.
Any help is greatly appreciated. Below is my "send" method in my Receiver class.
The 'firstStamp' and 'secondStamp' variables are just to output the length of a note. I took the timeStamp when a note was released and subtracted the timeStamp when it was pressed.
#Override
public void send(MidiMessage message, long timeStamp) {
String strMessage = null;
if (firstStamp == 0) {
firstStamp = timeStamp;
secondStamp = timeStamp;
}
firstStamp = secondStamp;
secondStamp = timeStamp;
stampDif = (secondStamp - firstStamp);
if (message instanceof ShortMessage) {
strMessage = decodeMessage((ShortMessage) message, timeStamp);
} else if (message instanceof MetaMessage) {
strMessage = decodeMessage((MetaMessage) message);
} else if (message instanceof SysexMessage) {
strMessage = decodeMessage((SysexMessage) message);
} else {
strMessage = "other message" + message.getStatus();
}
r_out.println("Timestamp: " + timeStamp + " " + strMessage);
r_printStream.println("Timestamp: " + timeStamp + " " + strMessage);
}
If the timestamp is in milliseconds then you can convert it to ticks like this:
long ticks = timestamp * bpm / (1000 * 60);
Bit you will get a high start tick since the timestamp is probably since Jan 1 1970. So if you want to have your first "tick" as 0 you need to keep track of if this is your first seen event.
if (tickOffset == -1) { // Using -1 as not initialized
tickOffset = ticks;
}
ticks = ticks - tickOffset;

Categories

Resources