пятница, 19 августа 2011 г.

Android: AlarmManager and notification example





Let's talk about a task, that many application developers have to complete: notification user at time, specified by user, without using timer and service.

I suppose, that we already have got property "notifyTime" as a string value, (looks like "14:05"). I'll describe few steps, that we need to notify user everyday at specified time.

1. We need custom BroadcastReceiver class:
  
public class TimeNotification extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
    // Talk about it later
    }
}
  
2. In main activity, let's realise following method:
  
 private void restartNotify() {
        // Alarm manager - magic thing that does what we need
        am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        // Intent for our  BroadcastReceiver 
        Intent intent = new Intent(this, TimeNotification.class);
        // PendingIntent for AlarmManager 
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
                intent, PendingIntent.FLAG_CANCEL_CURRENT );
        // Just in case we have already set up AlarmManager,
        // we do cancel.
        am.cancel(pendingIntent);

        //Some simple code to define time of notification:
        Calendar cal = Calendar.getInstance();
        Date stamp =  cal.getTime();
        int pos = notifyTime.indexOf(":");
        int hour = Integer.parseInt(notifyTime.substring(0, pos));
        int minute = Integer.parseInt(notifyTime.substring(pos+1));
        stamp.setHours(hour);
        stamp.setMinutes(minute);
        stamp.setSeconds(0);

        // In case it's too late notify user today
        if(stamp.getTime() < System.currentTimeMillis())
            stamp.setTime(stamp.getTime() + AlarmManager.INTERVAL_DAY);
                
        // Set one-time alarm
        am.set(AlarmManager.RTC_WAKEUP, stamp.getTime(), pendingIntent);
    }
  
Call this method in onStart() of main activity.
There is one not obvious thing: we could use am.setRepeat method with AlarmManager.INTERVAL_DAY interval. But user can change noification time preference anytime, so I decided to re-start alarm manager each time in BroadcastReceiver object. It is described in next step.

3. Fill onReceive method in TimeNotification
  
public void onReceive(Context context, Intent intent) {
        // Notification manager - we already know how to use it
        nm = (NotificationManager)        context.getSystemService(Context.NOTIFICATION_SERVICE);
        Notification notification = new Notification(R.drawable.icon, "Test", System.currentTimeMillis());
        // This is intent, we want to launch when user clicks on the notification.
        Intent intentTL = new Intent(context, MainActivity.class);
        notification.setLatestEventInfo(context, "Test", "Do something!",
                PendingIntent.getActivity(context, 0, intentTL,
                PendingIntent.FLAG_CANCEL_CURRENT));
        notification.flags = Notification.DEFAULT_LIGHTS | Notification.FLAG_AUTO_CANCEL;
        nm.notify(1, notification);
// Here we set next notification, in day interval
 AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
                intent, PendingIntent.FLAG_CANCEL_CURRENT);
        am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + AlarmManager.INTERVAL_DAY, pendingIntent);
    }
  

4. Don't forget the main thing - add receiver to manifest file
  
    <receiver android:name=".TimeNotification" />
  

I hope that was useful!

среда, 17 августа 2011 г.

Android: Notification in your application (example).




Here we'll see how to make your application notify user to do something with it, even when it's off. Most of tutorials show notifications after click on the button or something like that, so - are useless, because it usually makes sense to notify user when application activity is not working.
Notification constructor has "when" parameter, notification builder has setWhen method. But the truth is, when you call NotificationManager.notify, notification appears immediately. "When" is used only to sort notifications.

I need to remind user to run my application periodically... One way I found is to make this - use service and timer (or AlarmManager, but it's next story).

So, the first step is - create service.
    
public class NotifyService extends Service {
    // Notification interval. 
    private static final long UPDATE_INTERVAL = 1000*60*60*24;

    private Timer timer = new Timer();

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        // we shedule task "showNotification" to run everyday.
        timer.scheduleAtFixedRate(
                new TimerTask() {
                    public void run() {
                        showNotification();
                    }
                },
                0,
                UPDATE_INTERVAL);
    }

    @Override
    public void onDestroy() {
    }

    @Override
    public void onStart(Intent intent, int startid) {
    }

    public void showNotification() {
// Here we are going do something...later
    }
}
Don't forget add information about service in your manifest file.
Inside "application" tag put

<service android:enabled="true" android:name="NotifyService" />
Then we should start service from activity. I suggest to do it in
"onStart" method:

public void onStart() {
  super.onStart();
  notifyService = new Intent(this, NotifyService.class);
  startService(notifyService);
       
}
To stop service, use simple code:

notifyService = new Intent(this, NotifyService.class);
stopService(notifyService);
Do not put it into onPause e t.c., because your service will die when application is closed, that's not what we want. Maybe you can offer user special button "Stop notifications", or, better, check option - it's up to you.
In this example I just let it live forever.

You can see if your service is working in the list of active applications on your phone - close activity window, active application should remain.

Now, let's add notification code - fill showNotification method of our service

public void showNotification() {
  NotificationManager mManager = 
    (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
  Notification notification = 
    new Notification(R.drawable.icon, "My App", System.currentTimeMillis());
  Intent intent = new Intent(this, MainActivity.class);
    notification.setLatestEventInfo(this, 
     "My App", 
     "Use me!",
      PendingIntent.getActivity(this.getBaseContext(), 
      0, 
      intent,
      PendingIntent.FLAG_CANCEL_CURRENT));
  notification.flags = Notification.DEFAULT_LIGHTS 
   | Notification.FLAG_AUTO_CANCEL;
  mManager.notify(1, notification);
}

Check it, must work!

P.S.
To notify a user in specified time requires some more logic in code above.
Also, there is a problem: some devices stop processors or reduce frequency, and timer does not work properly.
For long-period notifications, the right way is to use AlarmManager, I hope to write about it in my next post...