0

I am creating an app which shows notification in future date. For showing notifications in future i have taken help of AlarmManager.

On button click in one of my activities i create notification like this

Intent myIntent = new Intent(getApplicationContext(),
                BootService.class);
        myIntent.putExtra("stuffid", stuffId);
        PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(),
                stuffId, myIntent, 0);

        AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month - 1);
        calendar.set(Calendar.DAY_OF_MONTH, day);

        calendar.set(Calendar.HOUR_OF_DAY, hour);
        calendar.set(Calendar.MINUTE, minute);
        calendar.set(Calendar.SECOND, 00);

        alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),pendingIntent);
        // Tried set repeating also :(
        // alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 30*1000, pendingIntent);

Here is my code for BootService class:

public class BootService extends Service {
    DBController dbController = new DBController(this);

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        LoadAlarmsFromDatabase();

        int stuffId = intent.getIntExtra("stuffid", 0);
        if(stuffId > 0)
            new GetStuffDataAsync().execute(stuffId);

        return super.onStartCommand(intent, flags, startId);
    }

    private void LoadAlarmsFromDatabase() {
        DBController dbController = new DBController(this);
          // Iterate over array and call addnotification no of time
             ...
        addNotification(stuff.getStuffId(), calendar);
        dbController.close();
    }

    private void addNotification(int stuffid, Calendar cal) {

        Calendar rightNow = Calendar.getInstance();
        long rightNowMillis = rightNow.getTimeInMillis();

        // Add to alarm only when its greate than current date
        if (cal.getTimeInMillis() > rightNowMillis) {
            Log.v(TAG, "addNotification " + stuffid + ", " + cal);
            Intent intent = new Intent(getApplicationContext(),
                    BootService.class);

            intent.putExtra("stuffid", Integer.toString(stuffid));

            PendingIntent mAlarmSender = PendingIntent.getBroadcast(
                    getApplicationContext(), stuffid, intent, PendingIntent.FLAG_UPDATE_CURRENT);

            AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
            //am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), mAlarmSender);
            am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 30*1000, mAlarmSender);

        }
    }

    public class GetStuffDataAsync extends AsyncTask <Integer,Void,Stuff>{

        @Override
        protected Stuff doInBackground(Integer... params) {
            int stuffId = params[0];
            Stuff stuff = dbController.getStuffInfo(stuffId);
            dbController.close();

            return stuff;
        }

        @Override
        protected void onPostExecute(Stuff result) {
            Context ctx = getApplicationContext();

            Intent notificationIntent = new Intent(ctx, AndroidAlarmService.class);
            PendingIntent contentIntent = PendingIntent.getActivity(ctx,
                    1, notificationIntent,
                    PendingIntent.FLAG_CANCEL_CURRENT);

            NotificationManager nm = (NotificationManager) ctx
                    .getSystemService(Context.NOTIFICATION_SERVICE);

            NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx);

            builder.setContentIntent(contentIntent)
                        .setSmallIcon(R.drawable.ic_launcher)
                        .setTicker("Reminder: " + result.getName() + " expires on " + result.getExpiresOn())
                        .setWhen(System.currentTimeMillis())
                        .setAutoCancel(true)
                        .setContentTitle("NotifyWhenMyStuffExpires")
                        .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
                        .setContentText(result.getName() + " expires on " + result.getExpiresOn());


            Resources res = ctx.getResources();
            if(result.getHasPhoto() == 1){
                Bitmap bm = result.getPhoto();
                builder.setContentIntent(contentIntent).setLargeIcon(bm);
            }
            else
                builder.setContentIntent(contentIntent).setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.ic_launcher));

            Notification n = builder.build();

            nm.notify(1, n);
        }

    }

}

On testing the application i see notification getting shown, problem is specific to booting the phone. In code above i am re-creating the notifications by calling LoadAlarmsFromDatabase

To handle recreating of alarms on boot i have used BroadcastReceiver

public class BootReceiver extends BroadcastReceiver {

    private final String TAG = "BootService";

    @Override
    public void onReceive(Context context, Intent intent) {
        android.os.Debug.waitForDebugger();

        if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
            android.os.Debug.waitForDebugger();

            Intent mServiceIntent = new Intent(context,BootService.class);
            mServiceIntent.setAction("com.whenmystuffexpires.BootService");
            ComponentName service = context.startService(mServiceIntent);

            if (null == service) {
                // something really wrong here
                Log.e(TAG, "Could not start BootService ");
                Log.v(TAG, "BootReceiver - Could not start BootService ");
            }



        } else {
            Log.e(TAG, "Received unexpected intent " + intent.toString());
            Log.v(TAG, "Received unexpected intent " + intent.toString());
        }
    }
}

With debugger attached i see breakpoint getting hit and loadalarmfromdatabase getting called. But notifications are not getting shown.

Here is how my Reciever is declared in manifest

<service
            android:name=".BootService"
            android:enabled="true" />

        <receiver
            android:name=".BootReceiver"
            android:enabled="true" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </receiver>

Some more facts:

  1. On rebooting using ADB shell, notification DOES work!.

    $ am broadcast -a android.intent.action.BOOT_COMPLETED

  2. I am testing it on Gingerbread.

  3. Problem is only on reboot of phone.

  4. Manifest permissions : uses-permission android:name="android.permission.WAKE_LOCK" uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"

Signcodeindie
  • 776
  • 1
  • 10
  • 28

1 Answers1

0

Remove the DEFAULT category from your <intent-filter> (as that is incorrect) and ensure that you hold the RECEIVE_BOOT_COMPLETED permission.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Removed the default category, still don't see any notification. Yes i have the RECEIVE_BOOT_COMPLETED, updated my question to reflect that. Thanks for looking into this. – Signcodeindie Apr 10 '13 at 12:49
  • @Signcodeindie: It is also possible that you are doing too much work on the main application thread, and Android is killing off your service as a result. There should be a message in LogCat for this. Regardless, if `LoadAlarmsFromDatabase()` does what the method name claims, that should *not* be run on the main application thread, as you are doing here. Consider switching to an `IntentService` and moving this code into `onHandleIntent()`. – CommonsWare Apr 10 '13 at 12:52
  • Here is what i have observed while debugging onboot, LoadAlarmsFromDatabase gets called and it creates intents for alarm manager. But alarm does not trigger. – Signcodeindie Apr 11 '13 at 05:39