Techniques to implement a notification service - android

I have a main activity where the user can enable/disable notifications, set the notification interval, and set the base time the notification interval will use. Notifications will typically trigger about 2 hours from each other. After a certain time, an accumulator will reach a maximum value and notifications will no longer be needed.
What is the standard way of implementing such a notification scheme? I tried using a handler inside of a service using postAtTime, but it seems that there are a lot of conditions that can cause it to never run. I looked at a timer inside of the service, but putting the phone in standby will stop any timers, plus it just seems like a bad idea.
The only other option I came across I have yet to explore, but it involves using an AlarmManager and a BroadcastReceiver. Should I just ditch the service and schedule a repeating alarm instead? I need to be able to disable all remaining alarms once my accumulator has reached max value.
Thanks for any input.

What if you start a service that spawns a thread like this:
thread t = new thread(new Runnable(){
public void Run(){
boolean notified = false;
while( !notified ){
if( notify_time - time > 1000 ){
Thread.sleep(999);
else if( notify_time - time <= 0 ){
// START NOTIFICATION ACTIVITY
notified = true;
}
}
}
}
t.start();
I have not done anything like this personally, so I am not sure what service can do to notify the user or start an activity, but it does have the full panoply of options available to an activity, so yeah.
Oh but it just occured to me you'll need to use a handler for that because of the multithreaded aspect here.

Since I will always have a finite number of notifications and I can calculate the elapsed time in advance, it seems the combination of AlarmManager and a BroadcastReceiver work pretty well. Here is how I implemented this:
I first created a BroadcastReceiver
public class NotificationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Get handle to system notification manager
NotificationManager mNM = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
//Get message from intent
Bundle bundle = intent.getExtras();
CharSequence text = bundle.getString("notification_message");
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.notification_icon, text, System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(context, context.getText(R.string.app_name),text, contentIntent);
// Set Flags
notification.flags |= Notification.FLAG_AUTO_CANCEL;
// Send the notification.
mNM.notify(R.string.notification, notification);
}
}
I then created a class that used a AlarmManager to create/cancel alarms that send a message to the BroadcastReceiver
public class NotificationSender {
private AlarmManager mAlarmManager;
private Context mContext;
private Intent mIntent;
public NotificationSender(Context context){
this.mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
this.mIntent = new Intent(context, NotificationReceiver.class);
this.mContext = context;
}
public void setAlarm(Long etaMillis, int accumulator){
//Create intent to send to Receiver
this.mIntent.putExtra("notification_message","Message");
//Use accumulator as requestCode so we can cancel later
PendingIntent sender = PendingIntent.getBroadcast(this.mContext, accumulator, this.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//Set Alarm
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, etaMillis, sender);
}
public void cancelAlarms(){
//requestCode (accumulator) will always be a multiple of 10 and less than 100
for (int x = 10; x <= 100; x += 10){
PendingIntent operation = PendingIntent.getBroadcast(this.mContext, x, this.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.cancel(operation);
}
}
public void createAlarms(PreferenceHelper prefs){
//Calculate time notifications are due and set an alarm for each one
//PreferenceHelper is a class to help pull values from shared preferences
Date currentTime = new Date();
for (int i = prefs.getNotificationInterval(); i <= 100; i += prefs.getNotificationInterval()) {
if (i > prefs.getAccumulator()) {
this.setAlarm(SystemClock.elapsedRealtime() + calculateETA(i, prefs).getTime() - currentTime.getTime(), i);
}
}
}
public void refreshAlarms(PreferenceHelper prefs){
this.cancelAlarms();
if (prefs.isNotificationsEnabled()) this.createAlarms(prefs);
}
}
The important part is to use the accumulator as the requestCode so we can cancel all of our alarms later.
Finally I used the NotificationSender class in my activity by calling refreshAlarms() in onCreate() and whenever the user modifies preferences that are relevant to scheduling notifications. Rebooting the phone will clear all alarms so the app must be restarted before notifications will begin. If the system happens to kills the process, the alarms will still trigger at the appropriate time.

Related

Android - Trouble with service sending multiple local notifications

I've inherited a code base for an Android app and I'm facing a particularly though problem with local notifications.
The idea is to send a notification for each event which is scheduled in the future, considering also the reminder preference on how many minutes before the event the user wants to be notified.
Everything works just fine, except that after the notification is thrown for the first time, if the user opens the app before the event starts, the notification gets thrown another time. This happens every time the app is opened between (event start date - reminder) and event start date.
I've already gave a look at this and also this with no luck.
I've read that using a service may cause exactly this problem and some suggest to remove it but I think this is needed since the notification must be thrown also when the app is closed.
Currently the structure of the code is the following:
Edit - updated description of TabBarActivity
Inside TabBarActivity I have the method scheduleTravelNotification that schedules the AlarmManager.
This method is executed everytime there is a new event to be added on local database, or if an existing event have been updated.
The TabBarActivity runs this method inside the onCreate and onResume methods.
TabBarActivity is also the target of the notification - onclick event.
private static void scheduleTravelNotification(Context context, RouteItem routeItem) {
long currentTime = System.currentTimeMillis();
int alarmTimeBefore = routeItem.getAlarmTimeBefore();
long alarmTime = routeItem.getStartTime() - (alarmTimeBefore * 1000 * 60);
if(alarmTimeBefore < 0){
return;
}
if(alarmTime < currentTime){
return;
}
Intent actionOnClickIntent = new Intent(context, TravelNotificationReceiver.class);
PendingIntent travelServiceIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis(), actionOnClickIntent, PendingIntent.FLAG_ONE_SHOT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(alarmTime);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), travelServiceIntent);
Log.e("NEXT ALARM", "Time: " + String.valueOf(calendar.getTimeInMillis()));
}
This is TravelNotificationReceiver.java (should I use LocalBroadcastReceiver instead of BroadcastReceiver?)
public class TravelNotificationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.e("RECEIVER", "received TravelNotification request");
Intent notificationIntent = new Intent(context, TravelNotificationService.class);
context.startService(notificationIntent);
}
}
TravelNotificationService.java extends NotificationService.java setting as type = "Travel", flags = 0, title = "something" and text = "something else".
public abstract class NotificationService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
sendNotification();
return super.onStartCommand(intent, flags, startId);
}
public abstract String setNotificationType();
public abstract int setNotificationFlags();
public abstract String setNotificationTitle();
public abstract String setNotificationText();
/**
* Executes all the logic to init the service, prepare and send the notification
*/
private void sendNotification() {
int flags = setNotificationFlags();
String type = setNotificationType();
NotificationHelper.logger(type, "Received request");
// Setup notification manager, intent and pending intent
NotificationManager manager = (NotificationManager) this.getApplicationContext().getSystemService(this.getApplicationContext().NOTIFICATION_SERVICE);
Intent intentAction = new Intent(this.getApplicationContext(), TabBarActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this.getApplicationContext(), 0, intentAction, flags);
// Prepares notification
String title = setNotificationTitle();
String text = setNotificationText();
Notification notification = NotificationHelper.buildNotification(getApplicationContext(), title, text, pendingIntent);
// Effectively send the notification
manager.notify(101, notification);
NotificationHelper.logger(type, "Notified");
}
}
Edit - Here's the code for NotificationHelper.buildNotification
public static Notification buildNotification(Context context, String title, String text, PendingIntent pendingIntent) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setAutoCancel(true);
builder.setContentText(text);
builder.setContentTitle(title);
builder.setContentIntent(pendingIntent);
builder.setSmallIcon(R.mipmap.launcher);
builder.setCategory(Notification.CATEGORY_MESSAGE);
builder.setVisibility(Notification.VISIBILITY_PUBLIC);
return builder.build();
}
Thank you for the answers!
Edit I've seen also this but has no accepted answers, while this post suggest something that I think it's already managed with if(alarmTime < currentTime){ return; } in scheduleTravelNotification.
This may not be your exact problem, but at a glance, you're sending the notification in onStartCommand() which can itself be run many times during the lifetime of the service -- for example, if you issue the service start command "blindly" in an onCreate of an activity, it will happen every time the activity is (re)created.
You have a few options for handling this.
One is to create a boolean flag as a property of the service, default to false, and check it before sending the notification. If it's false, send the notification and set it to true, and if it's already true you do not send a notification.
Another is to check and see if the service is already running, and if it is, don't send the service start command in the first place. This can be tedious to do everywhere, and violates DRY, so if you take this route you may want to create a static method in your service class which checks to see if the service is running and then starts it if not, and call that instead of explicitly starting the service.
Similar to user3137702 answer you could simple have a static boolean of APPISINFORGROUND which is checked everytime the send notification method is hit, and managed from your application/activities code.
As User said it is likely that your onStartCommand method is being called at odd times due to the app / service lifecycle.
Alternatively check your receiver is not being called somewhere else from your code.
It may be your NotificationHelper class which is causing an issue. Please share the code for this class.
One thought may be that your notification is not set to be auto cancelled, check if you include the setAutoCancel() method in your Notification Builder.
Notification notification = new Notification.Builder(this).setAutoCancel(true).build();
I've found a way to make it work, I'm posting this since it seems to be a problem of many people using the approach suggested in this and this articles. After months of testing I can say I'm pretty satisfied with the solution I've found.
The key is to avoid usage of Services and rely on AlarmScheduler and Receivers.
1) Register the receiver in your manifest by adding this line:
<receiver android:name="<your path to>.AlarmReceiver" />
2) In your activity or logic at some point you want to schedule a notification related to an object
private void scheduleNotification(MyObject myObject) {
// Cal object to fix notification time
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(myObject.getTime());
// Build intent and extras: pass id in case you need extra details in notification text
// AlarmReceiver.class will receive the pending intent at specified time and handle in proper way
Intent intent = new Intent(this, AlarmReceiver.class);
intent.putExtra("OBJECT_ID", myObject.getId());
// Schedule alarm
// Get alarmManager system service
AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(getBaseContext().ALARM_SERVICE);
// Build pending intent (will trigger the alarm) passing the object id (must be int), and use PendingIntent.FLAG_UPDATE_CURRENT to replace existing intents with same id
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), myObject.getId(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Finally schedule the alarm
alarmManager.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);
}
3) Define AlarmReceiver
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Find object details by using objectId form intent extras (I use Realm but it can be your SQL db)
MyObject myObject = RealmManager.MyObjectDealer.getObjectById(intent.getStringExtra("OBJECT_ID"), context);
// Prepare notification title and text
String title = myObject.getSubject();
String text = myObject.getFullContent();
// Prepare notification intent
// HomeActivity is the class that will be opened when user clicks on notification
Intent intentAction = new Intent(context, HomeActivity.class);
// Same procedure for pendingNotification as in method of step2
PendingIntent pendingNotificationIntent = PendingIntent.getActivity(context, myObject.getId(), intentAction, PendingIntent.FLAG_UPDATE_CURRENT);
// Send notification (I have a static method in NotificationHelper)
NotificationHelper.createAndSendNotification(context, title, text, pendingNotificationIntent);
}
}
4) Define NotificationHelper
public class NotificationHelper {
public static void createAndSendNotification(Context context, String title, String text, PendingIntent pendingNotificationIntent) {
// Get notification system service
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
// Build notification defining each property like sound, icon and so on
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context);
notificationBuilder.setContentTitle(title);
notificationBuilder.setContentText(text);
notificationBuilder.setSmallIcon(R.drawable.ic_done);
notificationBuilder.setCategory(Notification.CATEGORY_MESSAGE);
notificationBuilder.setVisibility(Notification.VISIBILITY_PUBLIC);
notificationBuilder.setAutoCancel(true);
notificationBuilder.setContentIntent(pendingNotificationIntent);
notificationBuilder.setDefaults(Notification.DEFAULT_SOUND);
notificationManager.notify(1001, notificationBuilder.build());
}
}
At this point it should work and schedule / trigger notification at the right time, and when notification is opened it will appear only once starting the activity declared in notification pending intent.
There is still a problem, AlarmManager have a "volatile" storage on user device, so if user reboots or switch off the phone you will lose all intents that you previously scheduled.
But fortunately there is also a solution for that:
5) Add at top of your manifest this uses permission
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
6) Right below the line added at step 1 register the boot receiver
<receiver android:name="<your path to>.BootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
7) Define the BootReceiver
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Do something very similar to AlarmReceiver but this time (at least in my case) since you have no source of intents loop through collection of items to understand if you need to schedule an alarm or not
// The code is pretty similar to step 3 but repeated in a loop
}
}
At this point your app should be able to schedule / trigger notification and restores those reminders even if the phone is switched off or rebooted.
Hope this solution will help someone!

Should I create 5 different receivers/services for my app?

I just started making an application aside from school and work and am not sure what route I should take. Essentially, I am building an app that calculates 5 different prayer times that change every day (the 5 prayers are named Fajr, Zuhr, Asr, Maghrib, and Isha). The calculation is done locally on the device and I found open source code for that and got it to calculate them properly. Once the getTimes() method is called, the prayer times should be calculated for that day and then recalculated once every single day after that. I'm thinking the setRepeating() method of the AlarmManager class would be good for that. How would I go about that? Once the prayer times are calculated, a service(s) should be started to create a notification at that exact time to notify the user that it is time to pray. The dilemma here is that I don't think that I should be using 5 different services/receivers to notify for each of the 5 different prayers. What would be the best way to go about this?
Currently, my app only notifies the user of Maghrib (one of the prayers) prayer time. It does not recalculate the times either.
Sorry if I am not very clear as I am new to this. I can expand more if needed.
My getTimes() method: (for the sake of simplicity I have removed the code that calculates the times)
public void getLocationTime(View v) {
//Maghrib
Calendar calMaghribTime = Calendar.getInstance();
calMaghribTime.set(Calendar.HOUR_OF_DAY, getHourOfDay(strMaghribTime));
calMaghribTime.set(Calendar.MINUTE, Integer.parseInt(strMaghribTime.substring(3,5)));
calMaghribTime.set(Calendar.SECOND, 0);
Intent myIntent = new Intent(MainActivity.this, NotificationCreatorReceiver.class);
pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC, calMaghribTime.getTimeInMillis(), pendingIntent);
Toast.makeText(this, "NotificationCreator onReceive()", Toast.LENGTH_SHORT).show();
} //end of getLocationTime()
Here is my receiver:
public class NotificationCreatorReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent service1 = new Intent(context, NotificationCreatorService.class);
context.startService(service1);
}
}
Here is my service:
public class NotificationCreatorService extends Service {
#Override
public IBinder onBind(Intent arg0)
{
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate()
{
super.onCreate();
// TODO Auto-generated method stub
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "NotificationCreator onStartCommand()", Toast.LENGTH_SHORT).show();
// Use NotificationCompat.Builder to set up our notification.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
//icon appears in device notification bar and right hand corner of notification
builder.setSmallIcon(R.mipmap.ic_launcher);
// This intent is fired when notification is clicked
Intent intent1 = new Intent(this.getApplicationContext(),MainActivity.class);
PendingIntent pendingNotificationIntent = PendingIntent.getActivity(this.getApplicationContext(),
0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
// Set the intent that will fire when the user taps the notification.
builder.setContentIntent(pendingNotificationIntent);
// Content title, which appears in large type at the top of the notification
builder.setContentTitle("It's time for Maghrib");
// Content text, which appears in smaller text below the title
builder.setContentText("Maghrib prayer time has started in your area");
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Will display the notification in the notification bar
notificationManager.notify(0, builder.build());
return START_STICKY;
}
#Override
public void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
}
}
The short answer - probably 1 service, 5 receivers. A Receiver is designed to listen for events and take quick action. A Service should be used to do all of the "heavy lifting" if necessary.
A Receiver should listen for an event and, if you only need to post a notification, then you can probably just do that and be done. But if you want to do much of anything else, it should pass an Intent to the Service with data to tell the Service how to respond.
EDIT:
Receivers have 10 seconds to do their job or else an ANR (Application Not Responding) error will occur. (See the docs: http://developer.android.com/reference/android/content/BroadcastReceiver.html) Creating and sending a notification should not take this long.
However, "good design" means you should acquire a Wake Lock and then pass an intent to a Service to do much of anything. Also, you will probably find that you will want to do "other processing" at some point. However, if I were you and all that is required is to post a notification, I'd just use the Receiver and worry about it later. I've probably processed over a billion notifications this way without error. But a code reviewer may argue that it's "possible" for an ANR to occur... blah... blah... blah...

Not sure of what to choose between Service, BroadcastReceiver and AlarmManager

Hello guys i am building an app in which i would like to add subscription. That means that every user has to pay monthly.
So i want to check if the user has paid he will be able to proceed with the orders if he didn't then i want a dialog to redirect him to pay. What would you suggest me to use Service, BroadcastReceiver or AlarmaManager?
I was thinking of creating a Service and within it create an AsyncTask that will check to the database if the user has paid and then if not inform the user with a dialog. Also i was thinking of creating Notiofications to the user that the subscription ending.
What is your opinion???
I developed a similar function to check many bills. I combined the three methods to ensure stability. But I think you should use Google Play In-app Billing to achieve subscriptions instead of using a local database. If you must use a database to subscribe:
1.After users subscribe, saved info to the database and start a service. the service start a thread,the thread get Data and Analyzing user payments. then use AlarmManager to set Notification and stopSelf.
public class NotificationService extends Service {
...
private AlarmManager am;
private PendingIntent pi;
private NotificationManager mNM;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Thread thread = new Thread(null, mTask, "AlarmService_Service");
thr.start();
return START_REDELIVER_INTENT;
}
Runnable mTask = new Runnable() {
public void run() {
List<Subscription> mDataList = getData;
if (mDataList.size() > 0) {
for (Subscription mSubscription : mDataList) {
if (mSubscription.isSub == true) {
Intent intent = new Intent(NotificationService.this,
AlamrReceiver.class);
intent.putExtra("data", (Serializable)mSubscription);
intent.setData(Uri.parse("custom://" + uniqueCode));
intent.setAction(String.valueOf(uniqueCode));
am = (AlarmManager) getSystemService(ALARM_SERVICE);
pi = PendingIntent.getBroadcast(
NotificationService.this, uniqueCode, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
am.set(AlarmManager.RTC_WAKEUP, reminderTime, pi);
uniqueCode = uniqueCode + 1;
}
}
}
NotificationService.this.stopSelf();
}
};
}
2.Receive broadcast information and show Notification.
public class AlamrReceiver extends BroadcastReceiver {
private NotificationManager mNM;
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
mNM = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
Subscription mSubscription = intent.getSerializableExtra("data");
if (mSubscription != null) {
showNotification(context, mSubscription);
}
}
private void showNotification(Context context, Subscription mSubscription) {
...
NotificationCompat.Builder builder = new NotificationCompat.Builder(
context);
builder.setContentTitle(text);
builder.setContentText(subTitleString + currencyString);
builder.setSmallIcon(Common.CATEGORY_ICON[cIcon]);
builder.setDefaults(Notification.DEFAULT_VIBRATE);
builder.setAutoCancel(true);
Intent intent = new Intent(context, BillDetailsActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(BillDetailsActivity.class);
intent.putExtra("dataMap", (Serializable) tMap);
stackBuilder.addNextIntent(intent);
PendingIntent contentIntent = stackBuilder.getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(contentIntent);
int uniqueRequestCode = (int) System.currentTimeMillis();
mNM.notify(uniqueRequestCode, builder.build());
}
}
Do not forget BOOT_COMPLETED , when the phone restarted , start the service and check the database
I am not sure of concept of your app, but if you want to check whether user has subscribed for current month or not, you don't need to run a Service for that.
You should check this in Splash Screen or Main Activity of your app. But anyways,
if you still need to do this, i suggest to go with Service or AlarmManager.
BroadcastReceiver won't work alone, you need to trigger them on particular events according to your need.
Also if you use service, please keep in mind that Android MIGHT kill your service in low memory kind of situation.
So i'll suggest that you should go with AlarmManager, which will check for subscription status after specific time or something.
PS : i know this should be addded as comment, but i don't have enough reputations to comment so posted as answer

my alarm service run several times and not in actual time

i have an alarm service that works with alarm manager. it's works correctly when you set alarm for later. but when you set it for past ( you want to alert you tomorrow) it gone crazy and alert several times instead of once and finally wont work tomorrow.
some one plz help me on this.
i do like this: I have a alarm class. when i create it, so it make an pending intent that run a service with alarm manger. and the service will open my activity.
is it the wrong way?
here is my alarm class:
public class MyAlarm {
private Context myContext;
private NotificationManager mNM;
private int NOTIFICATION = 10002; //Any unique number for this notification
MyAlarm(Context myAct){
myContext = myAct;
showNotification();
}
// this constractor is for cancelling alarm
MyAlarm(Context myAct, String str) {
myContext = myAct;
cancelPendigIntent();
}
private void cancelPendigIntent() {
//Intent myIntent = new Intent(myContext, MyAlarmService.class);
//G.pendingIntent = PendingIntent.getService(myContext, 0, myIntent, PendingIntent.FLAG_CANCEL_CURRENT);
//G.pendingIntent.cancel();
//G.alarmManager.cancel(G.pendingIntent);
}
private void showNotification() {
Intent myIntent = new Intent(myContext, MyAlarmService.class);
G.pendingIntent = PendingIntent.getService(myContext, 0, myIntent, PendingIntent.FLAG_CANCEL_CURRENT);
G.alarmManager = (AlarmManager)myContext.getSystemService(Context.ALARM_SERVICE);
G.calendar = Calendar.getInstance();
int to_day= G.calendar.get(Calendar.DATE);
int to_mounth= G.calendar.get(Calendar.MONTH);
int to_year= G.calendar.get(Calendar.YEAR);
G.calendar.set(to_year, to_mounth, to_day, Integer.parseInt(G.myPref.loadString("alert_time_houre")), Integer.parseInt(G.myPref.loadString("alert_time_mins")), 0);
Log.i("LOG", "alert_time_houre="+G.myPref.loadString("alert_time_houre")+" alert_time_mins="+G.myPref.loadString("alert_time_mins"));
G.alarmManager.setRepeating(AlarmManager.RTC, G.calendar.getTimeInMillis(),AlarmManager.INTERVAL_DAY, G.pendingIntent);
}
i finally find the answer.
my problem is not about alarm manager.
i set the pendig intent to a service that opens a activity. the problem was while the service is run in background it opened my activity again and again and my app go crazy. maybe i should destroy the service after her job done once.
i change it from service to broadcastreceiver and it's works correctly.
thanks for guides :)

Use alarmManager and service to perform schedule notification only during specific time period

I'm building an app that will trigger notifications at specific time-intervals during the users waking hours.
I have an alarmManager running inside of a service. The service is explicitly started via button click on the main activity and has the alarmManager executing notifications during specific time invervals. How would I go about stopping the notifications during certain hours of the day? I do not want these notification to be fired, for instance, while the user is sleeping.
My code that is currently firing notifications at user-set intervals is below (imports removed....this is long enough already):
public class FartSmackinChunks extends Service {
public Notification scheduleNotification;
public AlarmManager alarmScheduleManager;
public PendingIntent alarmScheduleIntent;
private Boolean autoUpdateBoolean = true;
private int intervalsGoneByInt = 0;
private Notification notification;
public static final int NOTIFICATION_ID = 1;
#Override
public void onCreate() {
// TODO: Actions to perform when service is created.
int icon = R.drawable.icon;
String tickerText = "INTERVAL FIRED";
long when = System.currentTimeMillis();
scheduleNotification = new Notification(icon, tickerText, when);
alarmScheduleManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
String ALARM_ACTION;
ALARM_ACTION = ScheduleAlarmReceiver.ACTION_REFRESH_SCHEDULE_ALARM;
Intent intentToFire = new Intent(ALARM_ACTION);
alarmScheduleIntent = PendingIntent.getBroadcast(this, 0, intentToFire,
0);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
SharedPreferences mySharedPreferences =
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
boolean autoUpdateBoolean =
mySharedPreferences.getBoolean("storedAutoUpdateBoolean", false);
String updateFreq =
mySharedPreferences.getString("storedInitialAverageTimeInterval", "00:00:00");
SimpleDateFormat dfInterval = new SimpleDateFormat("hh:mm:ss");
Date intervalTimeAsDateObject = null;
long updateFreqMilliLong;
try {
intervalTimeAsDateObject = dfInterval.parse(updateFreq);
} catch (ParseException e) {
e.printStackTrace();
}
updateFreqMilliLong = intervalTimeAsDateObject.getTime() - 18000000;
if (autoUpdateBoolean) {
int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
long timetoRefresh = SystemClock.elapsedRealtime() +
updateFreqMilliLong;
alarmScheduleManager.setInexactRepeating(alarmType,
timetoRefresh, updateFreqMilliLong, alarmScheduleIntent);
notifications();
} else alarmScheduleManager.cancel(alarmScheduleIntent);
return Service.START_NOT_STICKY;
};
private void notifications() {
**notification stuff in here***
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Replace with service binding implementation.
return null;
}
#Override
public void onDestroy() {
this.alarmScheduleManager.cancel(alarmScheduleIntent);
}
}
.....and my broadcast receiver implementation here:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class ScheduleAlarmReceiver extends BroadcastReceiver {
public static final String ACTION_REFRESH_SCHEDULE_ALARM
= "com.application.ACTION_REFRESH_SCHEDULE_ALARM";
#Override
public void onReceive(Context context, Intent intent) {
Intent startIntent = new Intent(context, SmokerReducerService.class);
context.startService(startIntent);
}
}
I'm having a little difficulty wrapping my brain around how this should be implemented.
I was thinking to rework this code so that the alarmManager is fired at waketime and stopped at sleepTime, all while inside the service is a Timer that fires the notification method at specific intervals? Is there a better way to go about doing this?
Any input would be appreciated. I've been trying to work this out in my head for days now.
Thanks
EDIT:
#anyone who comes across this intending to use a Timer for daily notifications:
A timer which runs inside of a service will be paused by the runtime when the device is put to sleep (ie...the user puts the phone in standby). Therefor, using a Timer to fire notifications at specific time intervals won't work correctly within a service because when Android pauses the service, it also pauses the timer, which throws off the interval.
The correct way to do this is to use AlarmManager with an array of pending intents to set alarms at specific times during the day. This ensures that even if the phone is put in standby, the notifications (or whatever you want to happen at that time) will still be executed.
I was thinking to rework this code so that the alarmManager is fired at waketime and stopped at sleepTime, all while inside the service is a Timer that fires the notification method at specific intervals? Is there a better way to go about doing this?
To my mind, forget thinking of a 'better' way, it seems the only way. Using a timer to control (enable/disable) another timer isn't so strange and makes complete sense to me.

Categories

Resources