Search code examples
javaandroidandroid-servicealarmmanagerandroid-alarms

How to setup once-per-day notification from app


I have a general question about setting up a daily data check from my app (giving a notification when some event occurs).

My current approach is:

  • From the app (onCreate) I start a service
  • From the service I set an alarm using AlarmManager after checking if there already is a matching PendingIntent (in which case I don't need to set a new alarm)
  • The service checks the data from SharedPreferences and eventually shows a notification

For testing purposes I set the alarm to +1 minute ... but it didn't work that way. Maybe I'm doing something wrong conceptionally?

Some code

AndroidManifest.xml

<application
    <activity
        android:name=".OverviewActivity"
        android:label="@string/app_name"
        android:theme="@style/AppTheme.NoActionBar"
        android:configChanges="orientation"
        android:screenOrientation="portrait">

        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <service
        android:name="xxxxx.CheckDaysMonths" >
    </service>
</application>

Service CheckDaysMonths

public class CheckDaysMonths extends Service {

    private static int ALARM_ID = 131313;
    private static String DECLARED_ACTION = "xxxxx.CheckDaysMonths";

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

        PendingIntent alarmIntent = AlarmHelper.getPendingIntentFromAlarm(this, ALARM_ID, DECLARED_ACTION);
        if(alarmIntent == null) {
           if(someConditionIsMet)
              sendNotification(" my notification text ");
           // Set an alarm for the next time this service should run:
           setAlarm();
        }

        stopSelf();
    }

    public void setAlarm() {

        AlarmHelper.setAlarm(this, ALARM_ID, DECLARED_ACTION, 8);
    }

    public void sendNotification(String notifyText) {

        Intent mainIntent = new Intent(this, OverviewActivity.class);
        @SuppressWarnings("deprecation")
        Notification noti = new Notification.Builder(this)
                .setAutoCancel(true)
                .setContentIntent(PendingIntent.getActivity(this, 131314, mainIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT))
                .setContentTitle("Gratuliere!")
                .setContentText("Du bist seit " + notifyText + " rauchfrei!")
                .setDefaults(Notification.DEFAULT_ALL)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setTicker("Du bist seit " + notifyText + " rauchfrei!")
                .setWhen(System.currentTimeMillis())
                .getNotification();

        NotificationManager notificationManager
                = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(131315, noti);

    }

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

Inside the main activity (OverviewActivity.java)

EDIT: Changed service call from implicit to explicit

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_overview);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    startService(new Intent(this, CheckDaysMonths.class));


    // additional stuff happening on the activity of my app goes here:
    ...
}

AlarmHelper.java

public class AlarmHelper {

    public static PendingIntent getPendingIntentFromAlarm(Context context, int alarmId, String declaredAction) {
        return (PendingIntent.getService(context, alarmId,
                new Intent(declaredAction),
                PendingIntent.FLAG_NO_CREATE));
    }

    public static void setAlarm(Context context, int alarmId, String declaredAction, int hourOfDay) {
        Intent serviceIntent = new Intent(declaredAction);
        PendingIntent pi = PendingIntent.getService(context, alarmId, serviceIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        // How long until tomorrow to its hourOfDay?
        //DateTime tomorrow = (new DateTime()).plusDays(1);
        //DateTime tomorrowAtHourOfDay = new DateTime(tomorrow.getYear(), tomorrow.getMonthOfYear(), tomorrow.getDayOfMonth(), hourOfDay, 0);

        DateTime tomorrowAtHourOfDay = (new DateTime()).plusHours(3);

        AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        am.set(AlarmManager.RTC_WAKEUP, tomorrowAtHourOfDay.getMillis(), pi);
    }
}

Solution

  • I fixed it by introducing another "middle tier" service

    So now I have

    • The app GUI starting service 1
    • Service 1 will check if there is already an alarm with the correct signature running. If not, it will start an alarm
    • The alarm will fire at 2am and start service 2
    • Service 2 will check the data (SharedPreferences) and eventually send a notification. Then it will start the alarm again
    • A BroadcastReceiver for BOOT starts Service 2

    This works quite well