I have set up AlarmManager for 1 minute for test mode. It sends broadcast to OnAlarmReceiver.
OnAlarmReceiver start sendWakefulWork(context, TaskService); and in TaskService in doWakefulWork() method i sendOrderedBroadcast which fire Notification.
This works well^ notification with sound and vibration appear. But only when phone in active mode. When phone in sleep mode (screen is switched off) only vibration and flash work during notification. No sounds from notification in sleep mode, only vibration. Sometimes it make sounds, but only sometimes , for example make 1 sound in hour, but it should make sound every minute. Also it make sounds every minute in sleep mode when phone connected to the computer but when I unplug phone from computer it starts only vibrate. When I turn on screen everything becomes normal: notification plays sound + vibration.
So I have no idea if it problem with my code or with my phone (HTC Desire Android 2.2)
I have tried a lot of things to fix it:
default and custom sounds
notification.audioStreamType = AudioManager.STREAM_SYSTEM and others
with flash and without flash
with vibration and without vibration
Nothing. Only sounds disappear in sleep mode, vibration worked perfect.
public class OnBootReceiver extends BroadcastReceiver {
public static void setAlarm(Context ctxt) {
AlarmManager mgr = (AlarmManager) ctxt.getSystemService(Context.ALARM_SERVICE);
mgr.setRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis()+1000,
1*60*1000,
getPendingIntent(ctxt));
}
public static void cancelAlarm(Context ctxt) {
AlarmManager mgr = (AlarmManager) ctxt.getSystemService(Context.ALARM_SERVICE);
mgr.cancel(getPendingIntent(ctxt));
}
private static PendingIntent getPendingIntent(Context ctxt) {
Intent i=new Intent(ctxt, OnAlarmReceiver.class);
return PendingIntent.getBroadcast(ctxt, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
}
#Override
public void onReceive(Context ctxt, Intent intent) {
setAlarm(ctxt);
}
}
My doWakefulWork method in TaskService class:
protected void doWakefulWork(Intent intent) {
Intent mIntent = new Intent(EXERCISE_EVENT);
mIntent.putExtra(StringUtils.PARAM_NEXT_TASK_TIME, nextStringTime);
mIntent.putExtra(StringUtils.PARAM_TASK_NUMBER, taskNumber);
sendOrderedBroadcast(mIntent, null);
}
And this is my NotificationBrodcastReceiver :
public class NotificationBrodcastReceiver extends android.content.BroadcastReceiver {
public void onReceive(Context ctx, Intent intent) {
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) ctx.getSystemService(ns);
int icon = R.drawable.notif_icon;
CharSequence tickerText = ctx.getResources().getString(R.string.notification_ticker);
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, tickerText, when);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
Intent notificationIntent = new Intent(ctx, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(ctx, 0, notificationIntent, 0);
notification.setLatestEventInfo(ctx, "contentTitle", "contentText", contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, notification);
}
}
You are using an ordered broadcast to raise your Notification. Presumably, you are doing this because you want an activity to handle the request if the activity is in the foreground and has a higher-priority BroadcastReceiver for this broadcast.
That pattern is fine for non-wakeful situations. However, once doWakefulWork() ends, nothing is going to keep the device awake, and so the device will fall back asleep. Your ordered Broadcast happens asynchronously, so the its work will happen after doWakefulWork() ends, and so your Notification may not happen at all. If the Notification is partially happening, as you describe, I would personally consider this behavior to be a bug in Android (it should hold its own WakeLock for the purposes of playing back the ringtone), but it is entirely possible that your BroadcastReceiver simply does not get control at all, because the device falls asleep before the broadcast is delivered.
Your hold-a-WakeLock-for-two-seconds workaround will increase the odds of success, but that too is not guaranteed. The only guaranteed ways to ensure that you will get a chance to raise the Notification is to either:
do that work inside of doWakefulWork(), or
hold a WakeLock until the broadcast is completed and the Notification is raised.
I will look to add additional features to the WakefulIntentService framework in the future to handle your scenario better, as what you appear to want to do is reasonable, and it would be nice to have a common implementation of the pattern. However, what you are trying to do is definitely outside the current scope of what WakefulIntentService does.
Related
I'm trying to set events with AlarmManager, but I'm getting notifications only when the app is opened. I tried with set, setExact and setExactAndAllowWhileIdle but with no luck : when i close the app, even when I don't turn off the screen, I won't get notified.
Adding notification
public void addNotification(Task task) {
Intent intent = new Intent(context, NotificationPublisher.class);
intent.putExtra("NOTIFICATION",getNotification(task));
intent.putExtra("TASK_ID",(int)task.getTaskId());
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
if(DateUtility.isTimePast(new Time(task.getNotificationTime()))) // if missed, notify now
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, today.getTime()+3000, pendingIntent);
else
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, task.getNotificationTime(), pendingIntent);
}
Notification publisher , called by the alarm manager
public class NotificationPublisher extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = intent.getParcelableExtra("NOTIFICATION");
int id = intent.getIntExtra("TASK_ID", 0);
if(notification!=null&&id!=0)
notificationManager.notify(id, notification);
}
}
My custom classes or methods aren't involved, since I'm getting notified correctly when my app isn't closed.
I set my alarms again on boot with RECEIVE_BOOT_COMPLETED too, which notifies me of late notifications only (if my phone was turned off during an event, I will be notified at next boot). So alarms are set correctly, but a few seconds later it dies. Thanks for your help!
I developed an app which should remind me at a certain time. Therefore I implemented an IntentService which starts a notification. The problem is that the notification will be created while the app is in foreground or at least in background open. If I close the app using the task manager the notification is no longer running.
Am I using the wrong service? Or do I have to create something else?
in the intentservice class:
private static final String serviceName = "sebspr.de.deadlines.DeadLineService";
public DeadLineService() {
super(serviceName);
}
#Override
protected void onHandleIntent(Intent intent) {
Context ctx = getApplicationContext();
Intent intent = new Intent(ctx, DeadLineService.class);
PendingIntent pIntent = PendingIntent.getActivity(ctx, 0, intent, 0);
String title = getTitle(list.size());
String text = getText(list);
Notification noti = new Notification.Builder(ctx)
.setContentTitle(title)
.setContentText(text).setSmallIcon(R.mipmap.ic_notfi)
.setContentIntent(pIntent)
.build();
NotificationManager notificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
// hide the notification after its selected
noti.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(0, noti);
}
int the MainActivity:
Intent msgIntent = new Intent(this, DeadLineService.class);
startService(msgIntent);
UPDATE
Considering my actual problem the solution is, to take a look at the AlarmManager. The IntentService is here the wrong way to go. I found a good tutorial here: AlarmManger Example
You shouldn't worry about a user killing the app via the task manager. If that happens, you really shouldn't start back up automatically. It isn't a user-friendly way to do things.
What you should worry about is a system restart in which case you can register your app to be notified when the device turns on.
Check out this answer for more details on starting your service when the phone turns on.
Also start your service sticky
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
Actually u can try to keep your application alive after killed but it may annoy your users a bit... So, Look out...
How to save Alarm after app killing?
I have a little question. I set my notifications in specific times using AlarmManager. The times I set notifications for are stored in SQLLite database. They all work perfect except the moment I reboot the phone. alarmManager looses their repeatings of course.
I would like to ask what is the best solution in this situation? I have my alarmManager set in MainActivity and I set my notification inside BroadcastReceiver as you can see in the code below:
Here is how I call it from MainActivity:
Intent intent = new Intent(context, MyReceiver.class);
intent.putExtra(EXTRA_TITLE, title);
intent.putExtra(EXTRA_COUNT, count);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, count, intent, PendingIntent.FLAG_CANCEL_CURRENT);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), WEEK_LENGTH_MS, pendingIntent);
And here is Broadcast Receiver's method onReceive
public void onReceive(Context context, Intent intent)
{
nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
CharSequence from = context.getString(R.string.app_name);
CharSequence message = intent.getStringExtra(DayActivity.EXTRA_TITLE);
Intent intentNotification = new Intent(context,DayActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(context, intent.getIntExtra(DayActivity.EXTRA_COUNT,0), intentNotification, 0);
Notification notif = new Notification(R.drawable.notification_logo,context.getString(R.string.app_name), System.currentTimeMillis());
notif.setLatestEventInfo(context, from, message, contentIntent);
notif.defaults |= Notification.DEFAULT_LIGHTS;
notif.flags |= Notification.FLAG_AUTO_CANCEL | Notification.FLAG_SHOW_LIGHTS;
nm.notify(intent.getIntExtra(DayActivity.EXTRA_COUNT,0), notif);
}
I am declaring BroadcastReceiver for BOOT_COMPLETED event, but it always calls just empty notification at the time i start the phone and never more.
I would like to ask what is the best solution in this situation?
Register a BOOT_COMPLETED BroadcastReceiver to call setRepeating() on AlarmManager to re-establish your schedules.
I am declaring BroadcastReceiver for BOOT_COMPLETED event, but it always calls just empty notification at the time i start the phone and never more.
The objective of the BOOT_COMPLETED BroadcastReceiver should be to reschedule your alarms. You might wish to consider using a separate BroadcastReceiver than the one you are using for the alarm events themselves.
I'm trying to implement a "daily reminder" function in my Android app, that should fire once per day at a set time. The first implementation I tried worked for most people, but some subset of users (including at least one person running on Android 4.3 on a Samsung) were reporting that the alarm was firing WAY more often than it should, e.g. every 10 minutes, and every time they opened the app, and just generally being very annoying.
Here's how the alarm is enabled:
Intent myIntent = new Intent(ctx, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(ctx, 0, myIntent,0);
AlarmManager alarmManager = (AlarmManager)ctx.getSystemService(Service.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, sched,
AlarmManager.INTERVAL_DAY,
pendingIntent);
Then there's this AlarmReceiver class:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Intent service1 = new Intent(context, AlarmService.class);
context.startService(service1);
}
}
This is registered as a receiver in the AndroidManifest: <receiver android:name=".AlarmReceiver"/>
Finally there's the AlarmService, which used to look like this:
public class AlarmService extends Service {
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate()
{
// TODO Auto-generated method stub
super.onCreate();
}
#SuppressWarnings("static-access")
#Override
public void onStart(Intent intent, int startId)
{
super.onStart(intent, startId);
Log.v("pm", "about to notify");
Intent intent1 = new Intent(this.getApplicationContext(), MainActivity.class);
intent1.setAction(Intent.ACTION_MAIN);
intent1.addCategory(Intent.CATEGORY_LAUNCHER);
//intent1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP| Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingNotificationIntent = PendingIntent.getActivity( this.getApplicationContext(),0, intent1,PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this.getApplicationContext())
.setContentTitle("My App")
.setContentText("Don't forget that thing!")
.setSmallIcon(R.drawable.ic_launcher)
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingNotificationIntent)
.getNotification();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
NotificationManager nManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nManager.notify(0, notification);
}
#Override
public void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
}
}
However, as I say, people were reporting that this fired every ten minutes or so! So I tried changing the AlarmService to a less deprecated implementation, but in the process now people are saying it only fires once, and then never again!
I replaced onStart with this:
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.v("pm", "about to notify");
if (intent != null) {
Intent intent1 = new Intent(this.getApplicationContext(), MainActivity.class);
intent1.setAction(Intent.ACTION_MAIN);
intent1.addCategory(Intent.CATEGORY_LAUNCHER);
//intent1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP| Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingNotificationIntent = PendingIntent.getActivity( this.getApplicationContext(),0, intent1,PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this.getApplicationContext())
.setContentTitle("My App")
.setContentText("Don't forget that thing!")
.setSmallIcon(R.drawable.ic_launcher)
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingNotificationIntent)
.getNotification();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
NotificationManager nManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nManager.notify(0, notification);
} else {
Log.v("pm", "Null Intent");
}
return START_STICKY;
}
Since I can't reproduce the original issue on my devices, it's a bit hard to test! My two theories are:
The problem lies in AlarmReceiver, like it ought to not be starting up a brand new service but doing something with the existing service
I shouldn't bother excluding null intent values in my onStartCommand function
I'm just a little nervous to try number 2 in case it causes people's devices to annoy them again!
Here's how the alarm is enabled
Note that this will be inexact on Android 4.4+, even though you are using setRepeating(), once you raise your android:targetSdkVersion to 19 or higher.
Then there's this AlarmReceiver class
That will not be reliable with a _WAKEUP-style alarm. It is eminently possible for the device to fall asleep between the startService() call and when your service actually gets a chance to do something. Please use WakefulBroadcastReceiver or my WakefulIntentService for _WAKEUP-style alarms if you are going to use the delegate-to-a-service pattern.
but in the process now people are saying it only fires once per day!
Since that is what you want, I would think that this is a good thing.
I replaced onStart with this:
I do not know why you are using a Service instead IntentService. Regardless, please call stopSelf() at the bottom of your onStartCommand() method, so the service goes away. There is no reason for this service to stay running once this work is completed. Also, replace START_STICKY with START_NOT_STICKY.
And, if this is all the work you intend to do in the service, you could dump the service entirely and move your onStartCommand() guts into onReceive() of the BroadcastReceiver.
The pattern of delegating work to a service from a receiver is used when the work will take too long to risk tying up the main application thread (e.g., >1ms)... but then your service needs a background thread, which yours lacks. Since I would expect your code to be less than 1ms in execution time, you could just do that in onReceive() and simplify your app, you would no longer need the separate Service, nor any of the Wakeful* stuff I mentioned earlier.
The problem lies in AlarmReceiver, like it ought to not be starting up a brand new service but doing something with the existing service
If this only runs once per day, there better not be an "existing service". There is no need for you to have a running process, tying up system RAM, just waiting for the clock to tick.
I shouldn't bother excluding null intent values in my onStartCommand function
You will get a null Intent if:
Your process was terminated before the service completed onStartCommand() for a startService() call, and
Your service had successfully run onStartCommand() before and returned START_STICKY
I'm more and more wondering why my AlarmReceiver creates a service, rather than just showing the notification directly.
Agreed. If you plan on lots more work, involving disk or network I/O, then use an IntentService (background thread, service stops itself automatically). Otherwise, I'd just put this in onReceive() and call it good.
I just wanted to add for all people who got problems with the AlarmManager and Android 4.4+ that it is truly important that you add the stopSelf();, like #CommonsWave already said, to the bottom of your onStartCommand() within Service which is called from the BroadcastReceiver
I'm rather wondering if this is specific to my device (Nexus 4, Android 4.3, stock ROM), but I have an alarm, registered once via the AlarmManager. When triggered, the device is set to vibrate for two seconds. If the alarm is triggered while the device is on, then it correctly vibrates for the two seconds. However, if the alarm is triggered when the device is off (and unplugged), the vibration starts, but doesn't stop until the power button is pressed (to wake up the device). Here is the code to register the alarm:
public static void registerAlarm(Context context, int uniqueId, long triggerAlarmAt)
{
Intent intent = new Intent(context, AlarmReceiver.class);
intent.setAction("com.myapp.ALARM_EVENT");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), uniqueId, intent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, triggerAlarmAt, pendingIntent);
}
And the receiver code:
public class AlarmReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent arg1)
{
Toast.makeText(context, "Test of alarm", Toast.LENGTH_LONG).show();
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(2000);
}
}
Any ideas on why this is happening or how to prevent it?
Finally figured this one out. My innocuous 'Toast' which gets triggered before the vibration was causing the vibrator to hang until the toast was complete. The Toast wasn't showing until the screen was turned on and this somehow blocked the completion of the vibration. Removing the Toast solved my issue and the vibration ceased after two seconds as expected even with the screen off.