I have followed some tutorials from SO to generate a local notification using AlertManager some time after my app closes by broadcasting to a custom BroadcastReceiver impl of mine. It works well, I can see the notification come up in the notification area of the device.
However, it only works if the app's main activity is paused. If the app is stopped (via Settings->apps), there is no notification, the broadcast receiver is never called. I had the impression that AlertManager registers my notification request in some OS service - not related to my app, that's the whole point, to have some sort of notification through which the user can restart my app. I am testing on Android 4.2.1 BTW. Any chance I am simply doing something wrong and there is actually a way to get AlertManager to successfully broadcast something out?
Here is my AlertManager code, called from my main activity's onPause (set to 10 seconds, just for testing). 'ctx' is the main activity
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 10);
Intent intent = new Intent(ctx, MyAlarmReceiver.class);
intent.putExtra("alarm_message", "hey, wake up this app!");
// note: 192837 is just some test ID:
PendingIntent sender = PendingIntent.getBroadcast(ctx, 192837, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the AlarmManager service
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
And here is MyAlarmReceiver.onReceive(context, intent):
try {
Bundle bundle = intent.getExtras();
String message = bundle.getString("alarm_message");
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("Title!!!")
.setContentText(message);
Intent resultIntent = new Intent(context, MainActivity.class);
PendingIntent resultPendingIntent =
PendingIntent.getActivity(context, 192838, resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(123423, mBuilder.build());
} catch (Exception e) {
Toast.makeText(context, "There was an error somewhere, but we still received an alarm", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
To summarize, Force closing your app means that the user explicitly said that he
didn't want to run your app anymore
Starting with 3.1 when applications are installed they are in a “stopped” state so they will not be able to run until the user explicitly launches them. Pressing Force Stop will return them to this state, so if the user force-stops your application, all the components of your app (BroadcastReceivers, Services, AlarmManager...) will no longer work again until the user manually run your application again. And this is documented at the 3.1 release notes here.
Although the documentation for AlarmManager states that
Note: The Alarm Manager is intended for cases where you want to have
your application code run at a specific time, even if your application
is not currently running. For normal timing operations (ticks,
timeouts, etc) it is easier and much more efficient to use Handler.
It will not work also after your application is force closed and this is not a bug it's a feature.
This behavior is confirmed by the Android framework developers https://groups.google.com/forum/?fromgroups=#!topic/android-developers/anUoem0qrxU
Related
I want to develop a voice application which has to respond when the user speaks some commands. I think I need a service to accomplish my task.But I have learnt from several resources that a service cannot run for a long time.The android system automatically destroys a service if it sits idle.
So my question is whether it is possible to run a service continuously without being destroyed and respond to user actions?
I am new to Android development.So if there is anything wrong in the question, please correct me.
Thanks for the help!
You can use startForeground.
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
They are killed only as a last resort—if memory is so low that they cannot all continue to run.
https://developer.android.com/guide/components/processes-and-threads.html
1st priority.
I've followed this tutorial to create a new GCM Listener service:
http://www.appifiedtech.net/2015/08/01/android-gcm-push-notification-example/
The code for the listener service:
#Override
public void onMessageReceived(String from, Bundle data) {
super.onMessageReceived(from, data);
String msg = data.getString("message");
Log.d(TAG,"GCM Message received is "+msg);
// Notifying to user code goes here
notifyUser(getApplicationContext(),msg);
}
public void notifyUser(Context context,String data){
Intent intent = new Intent(context, NotificationActivity.class);
intent.putExtra("data", data);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setAutoCancel(true);
builder.setContentTitle("New Notification");
builder.setContentIntent(pendingIntent);
builder.setContentText(data);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Uri uri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
builder.setSound(uri);
notificationManager.notify(countNotification++, builder.build());
Log.v(TAG,"count "+countNotification);
}
When the app is running (foreground), this works fine and launches the Notification Activity as it should.
However, when it's running in background, I get the notification but the title and body are as defined in my server sender application, and tapping on it takes me to the Main Activity.
This essentially means that when it's running in the background something else handles the notification? Is there another handler I should implement to be able to manage that notification and send the user to the correct activity?
The screen does not wake when I receive this notification, nor does an LED light up on the phone as notifications from other applications do. How do you manage that?
(permissions, services and receiver are defined in the manifest as described in the tutorial)
In problem regarding when your apps running in the background something else handles the notification?
This SO question can help you in answering your question regarding the GCM Listener Service
In the problem regarding to the screen that doesn't wake when you receive notification.
Use ACQUIRE_CAUSES_WAKEUP
Normal wake locks don't actually turn on the illumination. Instead, they cause the illumination to remain on once it turns on (e.g. from user activity). This flag will force the screen and/or keyboard to turn on immediately, when the WakeLock is acquired. A typical use would be for notifications which are important for the user to see immediately.
You can visit this SO question in how to use it.
You remove
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
Using intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
In the NotificationActivity class , you need implement the onNewIntent() callback when you open NotificationActivity existed the stack.
When I swipe my app from recent tasks, my service stops, then restarts in a few seconds. I'd like it to behave more like pandora.
When you're playing music with pandora and swipe pandora out of recent tasks, the music continues to play without stopping at all. I'm trying to implement this behavior in my app.
I saw this post: Android: keeping a background service alive (preventing process death) which seems to indicate that the way to do this was to use the startForeground method.
I copied this code from http://developer.android.com/guide/components/services.html#Foreground
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
And made sure that my notification id was not zero (it's 1). I also return START_STICKY. Any clue what I'm doing wrong?
EDIT: The above behavior happens on 4.2. On 4.4, the notification doesn't go away. However, the thread I'm running inside the service stops and does not restart. At least on 4.2 it restarts :/
I have a scheduler kind of app that sends a notification to the user at a specified time in the morning and in the evening. I have done this using Alarm Manager. On the emulator the application works just fine as far as this is concerned. However in my device I use "Advanced Task Killer" application to kill running apps and free the device's memory. I noticed that on killing this application the notifications are not shown at the scheduled time. This definitely seems logical but is not at all what I want. I want the notifications to be shown at the scheduled time even though the process is killed. Is there a way to do so?
I figured a little bit of code might be helpful to realize my problem :
I have this in my Main Activity
public void setAlarm(){
Intent intent = new Intent(MainActivity.this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.cancel(pendingIntent);
Cursor tempcursor = db.getAlarmTime();
tempcursor.moveToFirst();
int hour = tempcursor.getInt(tempcursor.getColumnIndex("Hour"));
int minute = tempcursor.getInt(tempcursor.getColumnIndex("Minute"));
tempcursor.close();
db.close();
GregorianCalendar alarmtime = new GregorianCalendar();
alarmtime.set(GregorianCalendar.HOUR_OF_DAY, hour);
alarmtime.set(GregorianCalendar.MINUTE, minute);
alarmtime.set(GregorianCalendar.SECOND, 0);
alarmtime.set(GregorianCalendar.MILLISECOND, 0);
if(alarmtime.before(new GregorianCalendar()))alarmtime.add(GregorianCalendar.DAY_OF_MONTH, 1);
am.set(AlarmManager.RTC_WAKEUP, alarmtime.getTimeInMillis(), pendingIntent);
}
And this in my AlarmReceiver class that extends BroadcastReceiver :
public void onReceive(Context context, Intent intent) {
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent i = new Intent(context, Schedule_Today.class);
PendingIntent pi = PendingIntent.getActivity(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pi2 = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + AlarmManager.INTERVAL_DAY, pi2); //Setting another notification after 1 day as soon this notification broadcast is received.
CharSequence from = "Scheduler_3";
CharSequence message = "Test Notification";
notification = new Notification(R.drawable.ic_launcher, "Attention",System.currentTimeMillis());
notification.setLatestEventInfo(context, from, message, pi);
notificationManager.notify(1, notification);
}
I want to prevent the application from being killed or at least a module of it running that can broadcast the alarmmanager at the required time so that the user receives notifications
This is not strictly possible, except by making your own version of Android in your own ROM mod.
On Android 2.1 and earlier, third-party task managers, like "Advanced Task Killer", had the ability to "force stop" an application. On Android 2.2 and higher, that ability was reserved for the OS itself, and is available to users via the "Force Stop" button on the app's screen in the list of applications in Settings.
When an app is "force stopped", among other things, all scheduled alarms are removed. In addition, on Android 3.1+, nothing of that app will ever run again, until the user manually launches one of your activities (or something else manually runs one of your components).
You are welcome to write two applications, one that is the main app and the other than ensures that, if the first one appears to have been force-stopped, the alarms are rescheduled. However, there is nothing stopping the user from force-stopping both of those applications.
Also, bear in mind that some devices, like the SONY Xperia Z, block _WAKEUP alarms in general, if the user has activated "STAMINA Mode". See this blog post for more about this.
Hence, I recommend that you redesign your application to take into account that your alarms are not guaranteed to run at all, let alone at the time you expect.
I am making an app where the user can set an alarm based on GPS location. I only want 1 alarm to be active at any one time. So, when the user sets a 2nd alarm, I want to cancel the notification for the 1st alarm (then set a new notification for the 2nd alarm).
Right now, my notifications continue to stack up (as in I can't delete them, so they are all active). Here is my code where I am trying to delete the alarm and notification(s):
// Stop the location alarm activity
Intent intentAlarmService_delete = new Intent(v.getContext(), AlarmService.class);
stopService(intentAlarmService_delete); // I think this calls onDestroy() in AlarmService class ...
mNtf = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNtf.cancelAll();
Intent alarmIntent2 = new Intent(getApplicationContext(), OneTimeAlarmReceiver.class);
PendingIntent pendingIntentAlarm = PendingIntent.getBroadcast(getApplicationContext(), PENDING_INTENT_REQUEST_CODE1,
alarmIntent2, PendingIntent.FLAG_CANCEL_CURRENT);
pendingIntentAlarm.cancel();
This is the onDestroy() function in my AlarmService.class (I'm not really sure when this is called...)
public void onDestroy(){
super.onDestroy();
mNtf.cancel(NOTIFICATION_ID1);
Intent alarmIntent = new Intent(getApplicationContext(), OneTimeAlarmReceiver.class);
PendingIntent pendingIntentAlarm = PendingIntent.getBroadcast(getApplicationContext(), PENDING_INTENT_REQUEST_CODE1,
alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
pendingIntentAlarm.cancel();
Intent intentAlarmService = new Intent(getApplicationContext(), AlarmService.class);
stopService(intentAlarmService);
mNtf.cancel(NOTIFICATION_ID1);
mNtf.cancelAll();
}
Then, this is how I am setting a new alarm and notification:
Intent intentAlarmService2 = new Intent(v.getContext(), AlarmService.class);
startService(intentAlarmService2);
By the way, my AlarmService.class is working for sure.
Thanks in advance.
First, get rid of getApplicationContext(). You almost never need it and it is frequently the wrong choice. Replace it with this, since whatever you are calling getApplicationContext() on is a Context.
In the code you have listed, you never raise a Notification. Hence, it is difficult to help you figure out why you are getting more than one. Calling cancelAll() on the NotificationManager should get rid of all outstanding notifications from your application.
My best guess is that onDestroy() is not being called on your service. That would occur if something else is keeping the service in memory (e.g., you have an active bound connection to it via bindService()). Or, possibly, you have something strange in your <service> element in the manifest (e.g., an unnecessary android:process attribute) that is fouling up the NotificationManager cancel() operation.
You need to make sure you are always referencing the same instance of your NotificationManager. Different instances will produce different notifications. I'd recommend using a service to manage notifications.
http://developer.android.com/guide/topics/fundamentals.html