Skip to content Skip to sidebar Skip to footer

How To Give Notifications On Android On Specific Time In Android Oreo?

I am looking for a way to create a preference in Settings to send a notification at a specific time of the day (set by user in settings) in an Android app. I have looked at differe

Solution 1:

After looking at different posts and some research on AlarmManager implementation, this is what worked for me.

The base for this is this post and Schedule repeating Alarms Android Documentation.

This is my current implementation:

I have a SwitchPreference and a TimePicker implementation is Settings

SwitchPreference to ask if user wants to enable Repeating Daily Notifications.

TimePicker to set the Notification time.

In MainActivity's OnCreate method or wherever you are reading the SharedPreferences do this:

PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
Boolean dailyNotify = sharedPref.getBoolean(SettingsActivity.KEY_PREF_DAILY_NOTIFICATION, true);
PackageManager pm = this.getPackageManager();
ComponentName receiver = new ComponentName(this, DeviceBootReceiver.class);
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

// if user enabled daily notificationsif (dailyNotify) {
    //region Enable Daily Notifications
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, sharedPref.getInt("dailyNotificationHour", 7));
    calendar.set(Calendar.MINUTE, sharedPref.getInt("dailyNotificationMin", 15));
    calendar.set(Calendar.SECOND, 1);
    // if notification time is before selected time, send notification the next dayif (calendar.before(Calendar.getInstance())) {
        calendar.add(Calendar.DATE, 1);
    }
    if (manager != null) {
        manager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
                AlarmManager.INTERVAL_DAY, pendingIntent);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
        }
    }
    //To enable Boot Receiver class
    pm.setComponentEnabledSetting(receiver,
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            PackageManager.DONT_KILL_APP);
    //endregion
} else { //Disable Daily Notificationsif (PendingIntent.getBroadcast(this, 0, alarmIntent, 0) != null && manager != null) {
        manager.cancel(pendingIntent);
        //Toast.makeText(this,"Notifications were disabled",Toast.LENGTH_SHORT).show();
    }
    pm.setComponentEnabledSetting(receiver,
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP);
}

Next add AlarmReceiver class that implements BroadcastReceiver like this:

publicclassAlarmReceiverextendsBroadcastReceiver {
@OverridepublicvoidonReceive(Context context, Intent intent) {

    SharedPreferencesprefs= PreferenceManager.getDefaultSharedPreferences(Objects.requireNonNull(context));
    SharedPreferences.EditorsharedPrefEditor= prefs.edit();

    NotificationManagernm= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    IntentnotificationIntent=newIntent(context, MainActivity.class);

    notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
            | Intent.FLAG_ACTIVITY_SINGLE_TOP);

    PendingIntentpendingI= PendingIntent.getActivity(context, 0,
            notificationIntent, 0);
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationChannelchannel=newNotificationChannel("default",
                    "Daily Notification",
                    NotificationManager.IMPORTANCE_DEFAULT);
            channel.setDescription("Daily Notification");
            if (nm != null) {
                nm.createNotificationChannel(channel);
            }
        }
        NotificationCompat.Builderb=newNotificationCompat.Builder(context, "default");
        b.setAutoCancel(true)
                .setDefaults(NotificationCompat.DEFAULT_ALL)
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher_foreground)
                .setTicker("{Time to watch some cool stuff!}")
                .setContentTitle("My Cool App")
                .setContentText("Time to watch some cool stuff!")
                .setContentInfo("INFO")
                .setContentIntent(pendingI);

        if (nm != null) {
            nm.notify(1, b.build());
            CalendarnextNotifyTime= Calendar.getInstance();
            nextNotifyTime.add(Calendar.DATE, 1);
            sharedPrefEditor.putLong("nextNotifyTime", nextNotifyTime.getTimeInMillis());
            sharedPrefEditor.apply();
        }
    }
}

The system will turn off the AlarmManager if Device is powered off or reboots, so restart it again on BOOT COMPLETE add this class:

publicclassDeviceBootReceiverextendsBroadcastReceiver {
@OverridepublicvoidonReceive(Context context, Intent intent) {
    if (Objects.equals(intent.getAction(), "android.intent.action.BOOT_COMPLETED")) {
        // on device boot complete, reset the alarmIntentalarmIntent=newIntent(context, AlarmReceiver.class);
        PendingIntentpendingIntent= PendingIntent.getBroadcast(context, 0, alarmIntent, 0);

        AlarmManagermanager= (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        finalSharedPreferencessharedPref= PreferenceManager.getDefaultSharedPreferences(Objects.requireNonNull(context));

        Calendarcalendar= Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.set(Calendar.HOUR_OF_DAY, sharedPref.getInt("dailyNotificationHour", 7));
        calendar.set(Calendar.MINUTE, sharedPref.getInt("dailyNotificationMin", 15));
        calendar.set(Calendar.SECOND, 1);

        CalendarnewC=newGregorianCalendar();
        newC.setTimeInMillis(sharedPref.getLong("nextNotifyTime", Calendar.getInstance().getTimeInMillis()));

        if (calendar.after(newC)) {
            calendar.add(Calendar.HOUR, 1);
        }

        if (manager != null) {
            manager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
                    AlarmManager.INTERVAL_DAY, pendingIntent);
        }
    }
}
}

And Finally do not forget to add these permissions to AndroidManidest:

<uses-permissionandroid:name="android.permission.WAKE_LOCK" /><uses-permissionandroid:name="android.permission.RECEIVE_BOOT_COMPLETED" />

and register your receivers in AndroidManifest

<application
    <!--YOURAPPLICATIONSTUFF--><receiverandroid:name=".DeviceBootReceiver"android:enabled="false"><intent-filter><actionandroid:name="android.intent.action.BOOT_COMPLETED" /></intent-filter></receiver><receiverandroid:name=".AlarmReceiver" />

The Notification should be set by this at a specific time of the day specified by TimePicker and if user enabled the SwitchPreference.

Solution 2:

There are tons of examples of how to do this on StackOverflow, but unfortunately none of them work anymore because Google changed how the AlarmManager works.

  • Android 6: Doze
  • Android 7: Aggressive Doze [Disables CPU shortly after screen is turned OFF]
  • Android 8: Additional restrictions on background services

The only AlarmManager option that allows a developer to wakeup a device at a specific time is AlarmManager.setAlarmClock.

Using setAlarmClock

In addition to the alarm, you can set an intent that will enable you to launch your app when the alarm is clicked.

The device can also be awoken at a specific time using a high priority FCM, Firebase Cloud Message.

But in summary, there is no other option: 'setExact' and associated methods no longer work as the name advertises.

Post a Comment for "How To Give Notifications On Android On Specific Time In Android Oreo?"