Since API19 (KitKat), AlarmManager broadcasts are not acted on immediately, but batched together. The documentation says that it should act immediately in KitKat if the app is targeted to API 18 or less, but this doesnt seem to work.
Example, my clock widget is targeted to API 18 but it doesnt update when required on KitKat emulator (i dont have a device to test on). I read online that many people report their old clock widgets no longer updating on their KitKat devices.
The AlarmManager doc suggests this is to avoid wakeups, but it affects my RTC alarm also.
The docs suggest setExact, but this is only for API19+. Is there a way to trigger updates that will be processed immediately on API8+ including API19? Or is it better to add a separate AlarmManager for API19+?What could be a good solution for getting widgets to update properly on API19+ and still work properly on older devices?
One strategy that has a single solution for both pre-API19 and API19+ that works for me: replace AlarmManager's setRepeating() broadcast with a new set()/setExact() created each time the widgets are updated.This strategy allows continued use of AlarmManager's RTC alarm type, which doesnt trigger while the phone is asleep. It causes the widget to update at the intended time on any API and keeps my widget as power efficient as it was using setRepeating().Instead of creating a setRepeating() alarm in the onEnabled method in the "widget manager" class... Create a set() for API18 or less, or setExact() for API19+ in the "Update Widget" class, after the widget update code is run. Everytime the widget is updated, a new alarm is set for whatever time is next required.Here is a section of code (incomplete) from within onStartCommand() in the "updateservice" class, that is called by both onEnabled() and the broadcast receiver class.
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w(LOG, "UpdateService.onStartCommand activated");
// reset the time dependent millis addition
long alarmAddMillis = 0;
// get time instance and values
Calendar rightNow = Calendar.getInstance();
int thisMin = rightNow.get(Calendar.MINUTE);
int thisHour = rightNow.get(Calendar.HOUR);
// set correct hour values to update widget
nextHour = thisHour + 1;
if (thisHour == 0) {thisHour = 12;}
if (nextHour == 13) {nextHour = 1;}
// set text values based on minutes
// set the values for the next alarm time
if (thisMin >= 0 && thisMin <= 6) {
clockH = thisHour; nextAlarmMin = 8; alarmAddMillis = 0;}
if (thisMin >= 7 && thisMin <= 21) {
clockH = thisHour; nextAlarmMin = 22; alarmAddMillis = 0;}
if (thisMin >= 21 && thisMin <= 35) {
clockH = thisHour; nextAlarmMin = 37; alarmAddMillis = 0;}
if (thisMin >= 36 && thisMin <= 50) {
clockH = nextHour; nextAlarmMin = 52; alarmAddMillis = 0;}
if (thisMin >= 51 && thisMin <= 59) {
clockH = nextHour; nextAlarmMin = 8; alarmAddMillis = 3600000;}
----CODE FOR UPDATING THE WIDGETS GOES IN HERE----
// cancel any unsent alarm
if (alarmManager != null){
alarmManager.cancel(pendingIntent);
Log.w(LOG, "UpdateService.onStartCommand unsent alarm canceled");}
// SET UP THE NEXT ALARM
// set the time using values set above
Calendar nextAlarm = Calendar.getInstance();
// add one hour of millis if its for the next hour
nextAlarm.setTimeInMillis(System.currentTimeMillis() + alarmAddMillis);
// set the correct minute
nextAlarm.set(Calendar.MINUTE, nextAlarmMin);
nextAlarm.set(Calendar.SECOND, 0);
nextAlarm.set(Calendar.MILLISECOND, 0);
// request the alarm
alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent usIntent = new Intent(this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(this, 0, usIntent, 0);
// ACCOUNT FOR DIFFERENT APIs HERE...
// use onExact for API19+
int currentApiVersion = android.os.Build.VERSION.SDK_INT;
if (currentApiVersion <= 18) {
alarmManager.set(AlarmManager.RTC, nextAlarm.getTimeInMillis(), pendingIntent);
Log.w(LOG, "FuzzyTimeWidget.onEnabled pre-kitkat AlarmManager requested");
}else{
alarmManager.setExact(AlarmManager.RTC, nextAlarm.getTimeInMillis(), pendingIntent);
Log.w(LOG, "FuzzyTimeWidget.onEnabled API19+ AlarmManager requested");
}
The only code required to manage the different API's is at the point of setting the alarm. Just choosing set() or setExact(). All the other code is for any API. In my case, I use nextAlarmMin to set the exact time for the next Alarm, which goes off 4 times per hour at 8, 22, 37, 52.For widgets that update every minute, I'm not sure how this strategy would be for the battery life.
Related
UsageEvents usageEvent = mUsageStatsManager.queryEvents(time - 100 * 1000, time);
UsageEvents.Event event = new UsageEvents.Event();
// get last event
while (usageEvent.hasNextEvent()) {
usageEvent.getNextEvent(event);
}
if (topPackageName.equals(event.getPackageName()) && event.getEventType() ==
UsageEvents.Event.MOVE_TO_FOREGROUND) {
pActivity = topPackageName;
}
Per the queryStats documentation:
NOTE: The last few minutes of the event log will be truncated to prevent abuse by applications.
So this is working as intended.
I'm trying to use UsageStatsManager to get the foreground app on a Nexus 5 with Marshmallow. I remember it used to work, but for some reason I'm getting null strings for package/class names now.
Here's my implementation
public String[] getForegroundPackageNameClassNameByUsageStats() {
String packageNameByUsageStats = null;
String classByUsageStats = null;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager mUsageStatsManager = (UsageStatsManager)getSystemService("usagestats");
final long INTERVAL = 1000;
final long end = System.currentTimeMillis();
final long begin = end - INTERVAL;
final UsageEvents usageEvents = mUsageStatsManager.queryEvents(begin, end);
while (usageEvents.hasNextEvent()) {
UsageEvents.Event event = new UsageEvents.Event();
usageEvents.getNextEvent(event);
if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
packageNameByUsageStats = event.getPackageName();
classByUsageStats = event.getClassName();
Log.d(TAG, "packageNameByUsageStats is" + packageNameByUsageStats + ", classByUsageStats is " + classByUsageStats);
}
}
}
return new String[]{packageNameByUsageStats,classByUsageStats};
}
For some reason, it doesn't go into the while loop, i.e. usageEvents.hasNextEvent() is false. Because of this, it returns null package/class names.
Any idea what I'm doing wrong?
Thanks.
OK, so I found that once I set the interval to 10000 instead of 1000, it works. Apparently a 1s interval is too small.
I am using this myself. I think the usage stats will only be updated when an app comes to foreground. So if the foreground app got to the foreground (and stayed) before your 'begin' timestamp then you will not get it. :(
On the other hand when you use a long time ago you will get a giant list where you only need the highest time to determine the foreground app.
So what I do is I create 3 different times: 1min ago, 1 hour ago and 12 hours ago.
When I get an empty list on 1min I repeat request with 1h and so on. That way I get foreground most of the time. But I never get it to work ALL of the time.
I really miss the old way of just asking the package manager which app is foreground (prior to android 5), the new way is a bit messy.
I am trying to make an app for students, and in the home screen of the app I would like to display a message like "Hey! Good Morning" and it should change to "Hey!Good evening" when it is after 15:00hrs of a day and so on.
How can I achieve this ?
Please check this one.It may help you.
Calendar c = Calendar.getInstance();
int timeOfDay = c.get(Calendar.HOUR_OF_DAY);
if(timeOfDay >= 0 && timeOfDay < 12){
Toast.makeText(this, "Good Morning", Toast.LENGTH_SHORT).show();
}else if(timeOfDay >= 12 && timeOfDay < 16){
Toast.makeText(this, "Good Afternoon", Toast.LENGTH_SHORT).show();
}else if(timeOfDay >= 16 && timeOfDay < 21){
Toast.makeText(this, "Good Evening", Toast.LENGTH_SHORT).show();
}else if(timeOfDay >= 21 && timeOfDay < 24){
Toast.makeText(this, "Good Night", Toast.LENGTH_SHORT).show();
}
you can get the time or hour by
import java.util.Calendar
Calendar c = Calendar.getInstance();
int hours = c.get(Calendar.HOUR);
from this you can choose a String for your textview:
TextView mytextview=(TextView) findViewByID(...);
String message="";
if(hours<11){
message="good morning"
}else if(hours>=11 & hours<17){
message=...}
mytextview.setText(message);
You can place a check for time of day in your activity in onResume(). This will check whenever the screen which has to display the text comes to foreground. So, even when you return to it from a different screen, the check will be made. For realtime check you will need a mechanism for absolute callback of the event [of 1500 hrs].
This might be a bit cumbersome and we need more info to decide on a mechanism. You should also decide if this is exactly what you want to do or not.
Not showing checking code because of its trivial nature.
I have some things in my Android application that need to update once per day.
It's pretty simple I think, but I have no idea in what format I need to format the date and time (if time is needed) and how to check if an update has been done today (where today is between 00:01am and 23:59pm in the user's local time). The update should not be done if it was already done for today.
Here's what I DO know how to do:
Save the last update date in SharedPreferences (but how do I get a
string of it, I do not know)
Get things from SharedPreferences (but I
don't know how to compare dates in string format)
It is irrelevant what format you choose. It is just matter of recalculations.
I'd suggest using milliseconds since epoch, as all system calls use it, so it would be easier for you to use the same.
As 1000 millis is 1 second it's easy to figure out that 1000*60*60*24 equals to one day (24hrs). So, if storedMillis is bigger than NOW - 1000*60*60*24, (and NOW is i.e. System.currentTimeMillis()), then it is too early to do the check. If storedMillis is smaller, then save your NOW timestamp and do the check:
long now = System.currentTimeMillis();
long diffMillis = now - lastCheckedMillis;
if( diffMillis >= (3600000 * 24) ) {
// store now (i.e. in shared prefs)
// do the check
} else {
// too early
}
EDIT
I am interested in doing it when the app is first opened for the
current day, even if the last update was done 10 minutes ago.
It's just the matter how to get the proper millis to compare against. Replace long now = System.currentTimeMillis(); from above code with following code block:
Calendar cal = Calendar.getInstance();
cal.clear(Calendar.HOUR);
cal.clear(Calendar.HOUR_OF_DAY);
cal.clear(Calendar.MINUTE);
cal.clear(Calendar.SECOND);
cal.clear(Calendar.MILLISECOND);
long now = cal.getTimeInMillis();
which shall do the trick.
If you store your date in format 20121122 (YYYYmmdd) then you can compare is like 20121122 > 20121123. But it must be stored as int or cast to int when comparing.
Store the timestamp (System.currentTimeMillis() ) of the Last execution and compair it with the currient one. If the difference is more than 24 hours... You know it or?
Set up an Alarm with AlarmManager that executes every 24 hours, then do stuff
Check this question: Alarm Manager Example
It's a more complicated approach than the rest, but makes sure things are done, while with the other options the app must be executed in order to check if it has to update whatever.
Here is the method
public boolean runOnceADay() {
SharedPreferences shp= c.getSharedPreferences(Constants.GENERAL_SHP, MODE_PRIVATE);
SharedPreferences.Editor editor= shp.edit();
long lastCheckedMillis = shp.getLong(Constants.ONCE_A_DAY, 0); // "KEY" you may change yhe value
long now = System.currentTimeMillis();
long diffMillis = now - lastCheckedMillis;
if (diffMillis >= (3600000 * 24)) { // set up your time circulation
editor.putLong(Constants.ONCE_A_DAY, now);
editor.commit();
Util.showMessage(c, "Once a Day Test");
return false;
} else {
Util.showMessage(c, "Too Early");
return true;
}
}
I would like to start a task at a specific time. For that I use runnable and postDelayed method as follows:
private Runnable mLaunchTask = new Runnable() {
public void run() {
try {
MY TASK
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
};
In my code I use mLunchTask as follows:
mHandler = new Handler();
mHandler.postDelayed(mLaunchTask, myDelay*1000);
and myDelay is computed as follows:
s = DateFormat.format("hh:mm:ss aaa", d.getTime());
cTime = s.toString(); // current Time
ch = Integer.parseInt(cTime.substring(0,2)); // current hour
cm = Integer.parseInt(cTime.substring(3,5)); // current minute
cs = Integer.parseInt(cTime.substring(6,8)); // current second
if (cTime.substring(9,11).equalsIgnoreCase("pm") && (ch<12) ) ch = ch+12;
myDelay=(desiredHour-ch)*3600+(desiredMinute-cm)*60 - cs;
if (myDelay<0) myDelay = 0;
and desiredHour and desiredMinute are set by user. The expectation is that MY TASK starts at desiredHour and desiredMinute and 0 seconds.
However "MY TASK starts with a few seconds delay, which looks like is random.
Based on the above code, is there any reason that it does not start at the exact desired time?
Thanks
First of all, your delay calculation is correct, but the "pm" detection doesn't work with other languages. It is much better to use the calendar to get the delay:
Calendar calendar = Calendar.getInstance();
long currentTimestamp = calendar.getTimeInMillis();
calendar.set(Calendar.HOUR_OF_DAY, desiredHour);
calendar.set(Calendar.MINUTE, desiredMinute);
calendar.set(Calendar.SECOND, 0);
long diffTimestamp = calendar.getTimeInMillis() - currentTimestamp;
long myDelay = (diffTimestamp < 0 ? 0 : diffTimestamp);
Now you have the delay in milli secs and can start the handler:
new Handler().postDelayed(mLaunchTask, myDelay);
In my test the following runnable logs at the desired time with no delay.
private Runnable mLaunchTask = new Runnable() {
public void run() {
Calendar calendar = Calendar.getInstance();
Log.d("test", "started at "
+ calendar.get(Calendar.HOUR_OF_DAY) + " "
+ calendar.get(Calendar.MINUTE) + " "
+ calendar.get(Calendar.SECOND)
);
}
};
Maybe your started task needs some seconds to be loaded?
Could the AlarmManager be an alternative?
Based on my experience and reading of the documentation, postDelayed is based on uptime, not real time. If the phone goes into deep sleep, it can cause real time to pass without uptime incrementing. That means your task will start later than desired.
The Handler really seems to be designed for updating the GUI in an active activity, not scheduling tasks far in the future. As scessor said, AlarmManager is probably your best bet.
The Naliba's answer is not completely correct.
Based on documentation that can be seen here
it can be seen that this class is specifically used for running tasks in the future.
"There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own. "
There is no reference about the behavior of the application when the system enters in sleep mode and this issue should be tested before posting additional information.