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.
Related
I'm trying to create a countdown. I don't see how exactly I can make a countdown. Can anyone help?
The code does what you tell it. You say in onStart:
end.add(Calendar.MINUTE, 4);
end.add(Calendar.SECOND, 5);
which tells the system you want a countdown starting at 4 minutes and 4 seconds. Instead, do:
end.add(Calendar.SECOND, 45);
EDIT
Took a while to understand what you meant. First, go to github and copy TickTockView into your own project. Remove the dependency to the original project. Open the file and in onDraw, change:
if (mCircleDuration == DURATION_TOTAL && mStartTime != null) {
long totalTime = mEndTime.getTimeInMillis() - mStartTime.getTimeInMillis();
float percentage = (((float) mTimeRemaining) / ((float) totalTime));
angle = 360f * percentage;
}
to:
if (mCircleDuration == DURATION_TOTAL && mStartTime != null) {
long totalTime = mEndTime.getTimeInMillis() - mStartTime.getTimeInMillis();
float percentage = (((float) mTimeRemaining) / ((float) 45000));//45000(ms) = 45 seconds
angle = 360f * percentage;
}
In your xml file where the circle is defined, add:
app:tickCircleDuration="total_time"
And by doing that, you only need to change totalTime in onDraw in the TickTockView. Remember to update the package name in the XML file to point to the place you saved the file, not the original project.
For reference, this is the file you should copy
The good way is to use CountDownTimer class provided by Android for downward counting in time. I have recently used it in my game and is simple in use. First you
CountDownTimer timer = new CountDownTimer(45000, 1000) {
// 45000 milliseconds countdown and 1000 milliseconds decrement at each tick.
public void onTick(long millisUntilFinished) {
//you can change your UI here based on time
}
public void onFinish() {
// you can define something to happen when timer ends.
}
};
timer.start();
Change the below code into
Calendar end = Calendar.getInstance();
end.add(Calendar.MINUTE, 4);
end.add(Calendar.SECOND, 5);
into
Calendar end = Calendar.getInstance();
end.add(Calendar.MINUTE, 0);//O minute
end.add(Calendar.SECOND, 45);//45 second
I am trying to build an analog stopwatch. There is a button that when I click, the stopwatch starts counting in seconds.
Note that this code runs okay (technically) but there is something wrong in terms of my "java language"-based logic or/and my thinking-based logic. Also I know that my stop watch would have some marginal error within milli seconds. There are two print out statements that can check my logic. Note that I have some C coding experience but new to this. EDIT: The platform is javaFX
Now, I am facing 3 problems with this code.
The three problems technically written:
1- The second print out statement's output is same as the first one's. i.e, my IF statement is not working since temp + 1 is still equal to the current number of seconds although this is impossible if we consider the fact that the lines of codes, between them, are executed faster. UPDATE: THIS, (1), IS SOLVED.
2- Ignoring problem (1), my image does not rotate although it should have since the IF statement is executed. Note that I am using stackpane and I added that image to my layout but it just sticks at 0 degrees and is not doing any thing.
3- The While loop itself is not working either even if I wait too much of time. I am not sure if it is "okay" or not to use a while loop inside an event handling method.
The three problems structurally written:
1- N/A
2- Would it be possible for me to update my layout elements continuously in an event method from inside the even method using loops and not by using the button or something else?
3- Same as (2)
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
int temp = 0;
Calendar calendar = Calendar.getInstance();
System.out.println("Seconds in current minute = " + calendar.get(Calendar.SECOND));
temp = calendar.get(Calendar.SECOND);
int temp2 = temp + 60;
int counter = 1;
do {
System.out.println("Seconds in current minute = " + calendar.get(Calendar.SECOND));
if ((temp + 1) == calendar.get(Calendar.SECOND)) {
System.out.println("Seconds in current minute = " + calendar.get(Calendar.SECOND));
imageHolder2.rotateProperty().set(6.0 * counter);
counter++;
temp = calendar.get(Calendar.SECOND);
}
} while (temp2 != (calendar.get(Calendar.SECOND) + 60));
}
});
1) I think you are just seeing the first System.out.println(...) (from before the loop) and the first one in the loop, which will produce the same output (except in the very rare (a few occurrences per million runs?) circumstance that the seconds "tick over" during execution of the three lines of code in between).
2) This is the real issue. Your loop is running (busily) in the FX Application Thread (because it is invoked by an event handler). This is the same thread that is used to update the UI and process events. Because you are keeping this thread busy, it will not have any opportunity to update the UI until your loop finishes. You need to either execute this loop in a background thread, and schedule the UI updates on the FX Application Thread, or (much much easier) use the animation API to do this.
3) You have a bunch of bugs in your logic. (1) You assign calendar before you start the loop, and never update it; so it will always represent the instant the handler starts. (2) You assign temp2 to be seconds + 60 and then your loop condition is temp2 != seconds + 60, which obviously is false, so the loop exits. Even if you update calendar on the loop iteration, it is extremely unlikely the seconds field changes in during the first iteration of the loop, so you will almost certainly exit the loop immediately. (3) If you update calendar, the seconds field will always be a value between 0 and 59 (inclusive), i.e. it will "wrap" at 60. temp2 will always be at least 60, so once you fix bugs (1) and (2) your loop will never exit... (4) if (temp + 1 == calendar.get(Calendar.SECONDS)) will fail when the minutes tick over, because 60 != 0, so your iteration will stop as soon as that happens.
You can use a Timeline to do this:
#Override
public void handle(ActionEvent event) {
Timeline timeline = new Timeline(
// key frame that updates rotation on each second:
new KeyFrame(Duration.seconds(1), e -> {
imageHolder2.setRotate(imageHolder2.getRotate() + 6);
System.out.println("Seconds in current minute = " + Calendar.getInstance().get(Calendar.SECOND));
})
);
// repeat 60 times:
timeline.setCycleCount(60);
timeline.play();
}
If you want to use your original code, you need to wrap it in a background thread, and schedule the UI updates on the FX Application Thread. As noted above, there are multiple other bugs in your code. Here is a version with the logic fixed:
#Override
public void handle(ActionEvent event) {
Thread thread = new Thread(() -> {
int temp = 0;
Calendar calendar = Calendar.getInstance();
System.out.println("Seconds in current minute = " + calendar.get(Calendar.SECOND));
temp = calendar.get(Calendar.SECOND);
int counter = 1;
do {
calendar = Calendar.getInstance() ;
if (temp != calendar.get(Calendar.SECOND)) {
System.out.println("Seconds in current minute = " + calendar.get(Calendar.SECOND));
double rotation = 6.0 * counter ;
Platform.runLater(() -> imageHolder2.rotateProperty().set(rotation) );
counter++;
temp = calendar.get(Calendar.SECOND);
}
} while (counter <= 60);
});
thread.setDaemon(true);
thread.start();
}
Finally, note you can also just do this:
#Override
public void handle(ActionEvent event) {
RotateTransition animation = new RotateTransition(Duration.seconds(60), imageHolder2);
animation.setByAngle(360);
animation.play();
}
I'm writing a lap timing app but have run into a GPS update frequency problem. At speeds greater than 75km/h (21m/s) my code stops working. My question is how can I request updates at a faster rate? I need it work at speeds up to 300km/h (83m/s) and would like the app to get updates every couple of meters traveled which would mean it would need an update every 0.025 seconds # 300km/h. Below is my code, I tried an alternate code to get time stamp but got the same result, I believe it's a GPS update frequency problem not a code problem. I wanted updates every couple of meters # 300km/h in case the phone passes through the proximity radius on a tangent.
int prox = 30; // Proximity Switch To Finish Line = 30 meters
int speedGov = 0; // Speed In Kmh
public void OnProviderDisabled(string provider)
{
}
public void OnProviderEnabled(string provider)
{
}
public void OnStatusChanged(string provider, Availability status, Bundle extras)
{
}
protected override void OnResume()
{
this.InitializeLocationManager();
base.OnResume();
_locationManager.RequestLocationUpdates(_locationProvider, 0, 0, this);
}
void InitializeLocationManager()
{
_locationManager = (LocationManager)GetSystemService(LocationService);
Criteria criteriaForLocationService = new Criteria
{
Accuracy = Accuracy.Fine
};
IList<string> acceptableLocationProviders = _locationManager.GetProviders(criteriaForLocationService, true);
if (acceptableLocationProviders.Any())
{
_locationProvider = acceptableLocationProviders.First();
}
else
{
_locationProvider = String.Empty;
}
}
public void OnLocationChanged(Location location)
{
_currentLocation = location;
if (_currentLocation == null)
{
}
else
{
d2fl = Convert.ToInt32(_currentLocation.DistanceTo(fl));
speedGov = Convert.ToInt32(_currentLocation.Speed * 3.6);
}
}
int A = 0; // 1st Distance to Finish Line
int B = 1000000; // 2nd Distance to Finish Line
// Get Time Stamp
while (true)
{
A = d2fl;
if (A > B && d2fl < prox && speedGov > 2) // Travelling away from Finish Line & Within 30m proximity to Finish Line & Going faster than 2km/h
{
// Time stamp for when phone first starts travelling away from Finish Line
string hours = DateTime.Now.ToString("HH");
string minutes = DateTime.Now.ToString("mm");
string seconds = DateTime.Now.ToString("ss");
string milliseconds = DateTime.Now.ToString("fff");
lapFinishTimeStamp = (Convert.ToDecimal(hours) * 3600) + (Convert.ToDecimal(minutes) * 60) + Convert.ToDecimal(seconds) + (Convert.ToDecimal(milliseconds) / 1000);
A = 0;
B = 1000000;
break;
}
B = A;
}
// Alternate Get Time Stamp - worked the same as above "Get Time Stamp"
while (true)
{
int A = d2fl;
Thread.Sleep(5);
int B = d2fl;
if (A < B && d2fl < prox && speedGov > 2)
{
string hours = DateTime.Now.ToString("HH");
string minutes = DateTime.Now.ToString("mm");
string seconds = DateTime.Now.ToString("ss");
string milliseconds = DateTime.Now.ToString("fff");
lapFinishTimeStamp = (Convert.ToDecimal(hours) * 3600) + (Convert.ToDecimal(minutes) * 60) + Convert.ToDecimal(seconds) + (Convert.ToDecimal(milliseconds) / 1000);
A = 0;
B = 0;
break;
}
A = 0;
B = 0;
}
Have read some other anwsers on this forum but are a few years old. This app will need to work on Galaxy S4 onwards.
Plus I'm a little confused about the GPS frequency's, from what I've read the GPS frequency operates at quite a high rate (hardware is around 1.6 GHz) but the phones operating systems seems to cull the data to a lower frequency, is this intentional?
Don't confuse the the radio frequency value (1.1-1.6GHz) from how frequently you will get location updates (1Hz).
Have you seen the device list in: Get GPS position on a precise time interval ? Even though its a few years old, I doubt any on device GPS will report any faster (probably due to battery/noise/use case design). Even if the on board device was reporting at 10Hz or 20Hz that is only 100ms or 50ms which is still slower than your requirement of 25ms. Remember if the CPU is talking to the GPS and calculating location - it is eating battery which is the limiting factor on mobile devices.
If you want consistent sub-second GPS value updates you'll need to use an external device.
Consumer devices update gps positions with a rate of 1hz, so one location per second.
A higher rate of e.g 10 / s would not make much sense.
Positions would not get better, more likely they get worse, however this a theoretical discussion, since consumer GPS chips usually will not provide a higher rate than 1 or 2 hz.
So just change your application design.
Especially at high speeds it is save to interpolate between two locations.
Keep in mind that position have an circular error of at least 3-5m.
So your lap timing app, migght addionally output an timingAccuracy value.
5m error at 100km/h result in an timing accuracy of 0.18s.
You can get the estimated positional error with location.getHoricontalAccuracy() (or similar names)
I want to measure the time between the 1st button click and the 3rd button click. I'm not getting any sort of thext on the main screen, where the textView1 is placed. If i'm launching the app, I'm getting a nullpointer. What does thar mean?
#Override
public void onClick(View v) {
Random r = new Random();
int x = r.nextInt(800);
int y = r.nextInt(800);
long startTime = SystemClock.elapsedRealtime();
i++;
View b = findViewById(R.id.start_time);
b.setX(x);
b.setY(y);
if (i == 1 ) {
b.setX(+9);
b.setY(+5);
}
if (i == 2 ) {
b.setX(x);
b.setY(y);
}
if (i == 3 ) {
b.setX(x);
b.setY(y);
}
else if (i == 4) {
long difference = SystemClock.elapsedRealtime() - startTime;
Intent intent = new Intent(Game.this, MainScreen.class);
intent.putExtra("time",difference);
// Toast.makeText(getApplicationContext(), getIntent().getStringExtra("time"), Toast.LENGTH_LONG).show();
textview1.setText(getIntent().getStringExtra("time"));
finish();
}
}
Well, that function isn't doing what you think. startTime is a local variable and will be cleared every time the function exits. If you want to keep the time between button presses, you need to use a class variable. You would also not want to initialize startTime unless i==1. Right now you're doing it each time and that will cause it to always have a 0 (or very close to 0) difference.
Also why are you using an intent for the toast? At best that's a waste, at worst its a problem. There's no reason for it. Just convert the difference to a string.
I'd like to create MIDI clock which works basically like a normal clock. It simply ticks and counts its ticks. Now I have read quite a few times that Thread.sleep() isn't accurate at all. So correcting it every every few cycles ensures that it is stable in the long term?
My Clock Class
public class Clock implements Runnable {
long beatsPassed = 0;
double bpm = 120; // default
double beatLength; // default
boolean running = false;
Clock(int bpm) {
this.bpm = bpm;
this.beatLength = 60.0 / bpm;
this.running = true;
}
public void run() {
int beatLengthInMS = (int) (this.beatLength * 1000);
long baseTime = System.currentTimeMillis();
// long corrected = 1;
try {
while (running) {
// check delay every 9 beats
// mod == 0 lets it the first time through which causes a negative timeout
if (this.beatsPassed % 10 == 9) {
// corrected = (System.currentTimeMillis() - baseTime) - (beatLengthInMS * 9);
Thread.sleep(beatLengthInMS + ((System.currentTimeMillis() - baseTime) - (beatLengthInMS * 9)));
baseTime = System.currentTimeMillis();
} else {
Thread.sleep(beatLengthInMS);
}
this.beatsPassed++;
// System.out.println(corrected);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Now I have measured actually quite steady times. It always adds about 6-9ms.
Am I forgetting something fundamental or is my approach wrong? Also great would be if you could tell me a more performant way to this?
The simplest approach (apart from using Timer, there are AFAIK two of them in the JDK) is a method
void sleepUntil(long absoluteTime) throw InterruptedException {
while (true) {
long now = System.currentTimeMillis();
if (now >= absoluteTime) break;
Thread.sleep(absoluteTime - now);
}
}
The loop is used because of spurious wakeups (which may never occur in practice, but better safe than sorry). The absoluteTime gets computed in advance (basically, you only look at the current time at the very beginning).