48

I want to give notification to my app on a specific time. Say everyday i have to give notification on 7 AM even if the app is closed.

How can i do this? Any tutorial? Please mention the link.

admin 7798
  • 697
  • 1
  • 7
  • 15

6 Answers6

38

first you need to use a broadcastreceiver. and because a broadcast receiver up only for a short time

from the android developer blog.When handling a broadcast, the application is given a fixed set of time (currently 10 seconds) in which to do its work. If it doesn't complete in that time, the application is considered to be misbehaving, and its process immediately tossed into the background state to be killed for memory if needed.

its a better practice to use also intent service here you have a example how to do it.

this is the broadcast receiver class.

public class MyReceiver extends BroadcastReceiver {
    public MyReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {

        Intent intent1 = new Intent(context, MyNewIntentService.class);
        context.startService(intent1);
    }
}

and register it in the manifest.

<receiver
    android:name=".MyReceiver"
    android:enabled="true"
    android:exported="false" >
</receiver>

this is the intent service class.

public class MyNewIntentService extends IntentService {
    private static final int NOTIFICATION_ID = 3;

    public MyNewIntentService() {
        super("MyNewIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Notification.Builder builder = new Notification.Builder(this);
            builder.setContentTitle("My Title");
            builder.setContentText("This is the Body");
            builder.setSmallIcon(R.drawable.whatever);
        Intent notifyIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 2, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        //to be able to launch your activity from the notification 
        builder.setContentIntent(pendingIntent);
        Notification notificationCompat = builder.build();
        NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this);
        managerCompat.notify(NOTIFICATION_ID, notificationCompat);
    }
}

and register it in the manifest.

<service
    android:name=".MyNewIntentService"
    android:exported="false" >
</service>

and then in your activity set the alarm manger to start the broadcast receiver at a specific time and use AlarmManager setRepeating method to repeat it this example bellow will repeat it every day.

 Intent notifyIntent = new Intent(this,MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast
            (context, NOTIFICATION_REMINDER_NIGHT, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,  System.currentTimeMillis(),
            1000 * 60 * 60 * 24, pendingIntent);

i hope this will help you.

Da2da2
  • 57
  • 2
  • 3
  • 13
Shia G
  • 1,444
  • 11
  • 10
  • 2
    Not working for me, tried with manually set time but still wont work. – Mitesh Jain Sep 29 '16 at 11:28
  • 1
    Any reason for using Broadcasts? Don't see this as being necessary/useful. You could directly talk to your IntentService with a PendingIntent. – Zordid Dec 01 '17 at 11:28
  • Will it work in Pie version? Doesn't seem to do so :( – Narendra Singh Apr 27 '20 at 14:24
  • @NarendraSingh That's because you need a notification channel for android O and higher. See https://developer.android.com/training/notify-user/build-notification#Priority – LimitX Jun 06 '20 at 14:15
  • Use the Google Sample for Alarm System based Notifications https://github.com/googlecodelabs/android-kotlin-notifications – Sri Krishna Mar 26 '23 at 17:39
  • @SriKrishna Hey, I have a notification issue would appreciate your help. https://stackoverflow.com/questions/76949162/scheduled-notification-not-appearing-andriod-13 – Mahmoud Ragab Aug 22 '23 at 10:05
  • Is it possible to rewrite the answer for the current andriodX platform for andriod 13 – Mahmoud Ragab Aug 22 '23 at 16:56
17

You can use AlarmManager to set alarm at specified time

SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (!prefs.getBoolean("firstTime", false)) {

    Intent alarmIntent = new Intent(this, AlarmReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);

    AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, 7);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 1);

    manager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
            AlarmManager.INTERVAL_DAY, pendingIntent);

    SharedPreferences.Editor editor = prefs.edit();
    editor.putBoolean("firstTime", true);
    editor.apply();
}

I used SharedPreferences to check that's not the first time to run the app and if it is, you set that alarm otherwise do nothing instead of resetting the alarm each time you start your app.
Use a BroadcastReceiver to listen when the alarm happens

public class AlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // show toast
        Toast.makeText(context, "Alarm running", Toast.LENGTH_SHORT).show();
    }
}

Use another receiver to listen to device boots so that you can reset the alarm

public class DeviceBootReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
            // on device boot compelete, reset the alarm
            Intent alarmIntent = new Intent(context, AlarmReceiver.class);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);

            AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(System.currentTimeMillis());
            calendar.set(Calendar.HOUR_OF_DAY, 7);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 1);

            manager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
                    AlarmManager.INTERVAL_DAY, pendingIntent);
        }
    }
}

add the permission to the manifest

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

then register your receivers

<receiver android:name=".DeviceBootReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
<receiver android:name=".AlarmReceiver" />
SaNtoRiaN
  • 2,212
  • 2
  • 15
  • 25
  • What if it's to send notification when opening the app also? – Si8 Nov 27 '16 at 23:25
  • You can add the notification code in `onCreate()` method of your launcher activity. – SaNtoRiaN Nov 28 '16 at 11:43
  • Thanks for your response. How would the `SharedPReference` work for the next day? Because the boolean will be false – Si8 Nov 28 '16 at 21:12
  • You're welcome. `SharedPreferences` is supposed to work only once in case of first start of the app to set the alarm, then it sets the boolean to true i.e. app started before, to avoid resetting alarm over and over. – SaNtoRiaN Nov 29 '16 at 11:05
  • So the next day, the alarm will go off again at the time? – Si8 Nov 29 '16 at 11:08
  • I actually thought abt it and you are right, What I was originally doing was in the Activity, checking the value and keep setting the alarm everytime. Since I am doing `setRepeating()` then I set it once and never have to set it again, unless I want to clear the alarm from pushing a notification. Makes sense? – Si8 Nov 29 '16 at 14:15
  • Yeah it's ok, alarm is set always unless device booted or you stopped it. – SaNtoRiaN Nov 30 '16 at 12:36
  • In a case for device booted, the code you provided should take care of it? Can you please post a sample for the `AlarmReceiver` function to send notification at the time defined? – Si8 Nov 30 '16 at 16:41
  • I have `DeviceBootReceiver` that resets the alarm when device boots because device boot removes the alarm. Both codes in receiver and activity set the alarm at a defined time. – SaNtoRiaN Nov 30 '16 at 20:49
  • I will test it out. Thanks – Si8 Dec 01 '16 at 01:56
  • 1
    You may need to put a flag if your intents magically disappear: [see this answer](http://stackoverflow.com/a/28203623/334493). Also, your `Serializable` objects may get null if they're not wrapped in a `Bundle`: [see this answer](http://stackoverflow.com/a/40515978/334493). – Benoit Duffez May 20 '17 at 12:49
  • @SaNtoRiaN Hey, I have a notification issue would appreciate your help. https://stackoverflow.com/questions/76949162/scheduled-notification-not-appearing-andriod-13 – Mahmoud Ragab Aug 22 '23 at 10:04
16

A solution from the accepted answer will not work properly on Android 8 Oreo (api level 26) and higher due to background service limits (https://developer.android.com/about/versions/oreo/background.html#services) and will cause an exception like this when the app is in background:

java.lang.IllegalStateException: Not allowed to start service Intent xxx: app is in background

One of the possible workarounds is using JobIntentService:

  1. extend your Service from JobIntentService instead of IntentService and use onHandleWork method instead of onHandleIntent.

  2. add android:permission="android.permission.BIND_JOB_SERVICE" to your Service in AndroidManifest.xml.

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
  • this method isn't working while using the code given in Accepted Answer and modify it according to your instructions. It's not calling `BroadCastReceiver`. How may I solve that? – JAMSHAID Apr 17 '19 at 08:52
  • It's quite difficult to say without seeing your code. If you have a problem with receiver, it happens before starting a service, I mean no matter you use my suggestion or from the accepter answer. Have you registered your receiver in manifest? – Vasily Kabunov Apr 17 '19 at 11:40
  • 1
    Why not leave out the Service alltogether? Setting up a notification should be fast enough to be done in the BroadcastReceiver. – Eir Apr 22 '20 at 07:16
6

Here is my solution, tested on android 10. Also is compatible with all the previous versions of android.

MainActivity.class

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    ....
    reminderNotification();

}

public void reminderNotification()
{
    NotificationUtils _notificationUtils = new NotificationUtils(this);
    long _currentTime = System.currentTimeMillis();
    long tenSeconds = 1000 * 10;
    long _triggerReminder = _currentTime + tenSeconds; //triggers a reminder after 10 seconds.
    _notificationUtils.setReminder(_triggerReminder);
}

NotificationUtils.class

public class NotificationUtils extends ContextWrapper
{

    private NotificationManager _notificationManager;
    private Context _context;

    public NotificationUtils(Context base)
    {
        super(base);
        _context = base;
        createChannel();
    }

    public NotificationCompat.Builder setNotification(String title, String body)
    {
        return new NotificationCompat.Builder(this, CHANNEL_ID)
                .setSmallIcon(R.drawable.noti_icon)
                .setContentTitle(title)
                .setContentText(body)
                .setAutoCancel(true)
                .setPriority(NotificationCompat.PRIORITY_DEFAULT);
    }

    private void createChannel()
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, TIMELINE_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
            channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
            getManager().createNotificationChannel(channel);
        }
    }

    public NotificationManager getManager()
    {
        if(_notificationManager == null)
        {
            _notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        }

        return _notificationManager;
    }

    public void setReminder(long timeInMillis)
    {
        Intent _intent = new Intent(_context, ReminderBroadcast.class);
        PendingIntent _pendingIntent = PendingIntent.getBroadcast(_context, 0, _intent, 0);

        AlarmManager _alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

        _alarmManager.set(AlarmManager.RTC_WAKEUP, timeInMillis, _pendingIntent);
    }

}

ReminderBroadcast.class

public class ReminderBroadcast extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        NotificationUtils _notificationUtils = new NotificationUtils(context);
        NotificationCompat.Builder _builder = _notificationUtils.setNotification("Testing", "Testing notification system");
        _notificationUtils.getManager().notify(101, _builder.build());
    }
}

AndroidManifest.xml

<application>
    ...
    <receiver android:name=".custom.ReminderBroadcast"/>
</application>

NB: CHANNEL_ID and TIMELINE_CHANNEL_NAME, have been created on another class.

For example,

CHANNEL_ID = "notification channel";

TIMELINE_CHANNEL_NAME = "Timeline notification";

Any misconception on my code and bugs, don't hesitate to comment. I will reply as soon as possible.

4xMafole
  • 479
  • 1
  • 8
  • 20
2
  • Get the Alarm service from the system.
  • Make a pending intent, passing in the broadcast receiver class's name.
  • Make a calendar object and set the time of it too 8 am.
  • Check if the current time is past 8. If yes then add another day to it.
  • Call the set repeating method of AlarmManager class.

Sample code for the same:

alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);   
alarmIntent = new Intent(context of current file, AlarmReceiver1.class); 
AlarmReceiver1 = broadcast receiver

    pendingIntent = PendingIntent.getBroadcast(  Menu.this, 0, alarmIntent, 
PendingIntent.FLAG_UPDATE_CURRENT);
    alarmIntent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
    alarmManager.cancel(pendingIntent);

    Calendar alarmStartTime = Calendar.getInstance();
    Calendar now = Calendar.getInstance();
    alarmStartTime.set(Calendar.HOUR_OF_DAY, 8);
    alarmStartTime.set(Calendar.MINUTE, 00);
    alarmStartTime.set(Calendar.SECOND, 0);
    if (now.after(alarmStartTime)) {
        Log.d("Hey","Added a day");
        alarmStartTime.add(Calendar.DATE, 1);
    }

     alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, 
alarmStartTime.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
     Log.d("Alarm","Alarms set for everyday 8 am.");

Coming to the broadcast receiver class. You need to register your broadcast receiver in the manifest. This will cause you to receive clock events. Override the onReceive method of this broadcast receiver and make a notification there itself or make a seperate notification building service and build and display your notification there.

The manifest code snippet:

The broadcast receiver code snippet:

public class AlarmReceiver1 extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
  Intent service1 = new Intent(context, NotificationService1.class);
service1.setData((Uri.parse("custom://"+System.currentTimeMillis())));
          context.startService(service1);
}

Notification building service code snippet:

public class NotificationService1 extends IntentService{

private NotificationManager notificationManager;
private PendingIntent pendingIntent;
private static int NOTIFICATION_ID = 1;
Notification notification;
@Override
protected void onHandleIntent(Intent intent) {
Context context = this.getApplicationContext();
       notificationManager = 
(NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent mIntent = new Intent(this, Activity to be opened after clicking on the 
notif);
        Bundle bundle = new Bundle(); 
        bundle.putString("test", "test");
        mIntent.putExtras(bundle);
        pendingIntent = PendingIntent.getActivity(context, 0, mIntent, 
PendingIntent.FLAG_UPDATE_CURRENT);     

        Resources res = this.getResources();
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
        notification = new NotificationCompat.Builder(this)
                    .setContentIntent(pendingIntent)
                    .setSmallIcon(R.drawable.ic_launcher)
                    .setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.ic_launcher))
                    .setTicker("ticker value")
                    .setAutoCancel(true)
                    .setPriority(8)
                    .setSound(soundUri)
                    .setContentTitle("Notif title")
                    .setContentText("Text").build();
        notification.flags |= Notification.FLAG_AUTO_CANCEL | Notification.FLAG_SHOW_LIGHTS;
        notification.defaults |= Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE;
        notification.ledARGB = 0xFFFFA500;
        notification.ledOnMS = 800;
        notification.ledOffMS = 1000;
        notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        notificationManager.notify(NOTIFICATION_ID, notification);
        Log.i("notif","Notifications sent.");

}

}
Vaibhav Rai
  • 183
  • 1
  • 8
  • Hey, I have a notification issue would appreciate your help. https://stackoverflow.com/questions/76949162/scheduled-notification-not-appearing-andriod-13 – Mahmoud Ragab Aug 22 '23 at 10:12
1

Use NotifyMe Android Library for simple notifications. Very easily set a delay or time when you want the notification to popup. Notification will popup through system reboots.

Download library with Jitpack.io Add this to your build.gradle file for your app.

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Add this to your dependencies in build.gradle for your project.

dependencies {
        implementation 'com.github.jakebonk:NotifyMe:1.0.1'
}

Example Create a NotifyMe Builder Object

NotifyMe.Builder notifyMe = new NotifyMe.Builder(getApplicationContext());

Then set the fields you want.

notifyMe.title(String title);
notifyMe.content(String content);
notifyMe.color(Int red,Int green,Int blue,Int alpha);//Color of notification header
notifyMe.led_color(Int red,Int green,Int blue,Int alpha);//Color of LED when 
notification pops up
notifyMe.time(Calendar time);//The time to popup notification
notifyMe.delay(Int delay);//Delay in ms
notifyMe.large_icon(Int resource);//Icon resource by ID
notifyMe.rrule("FREQ=MINUTELY;INTERVAL=5;COUNT=2")//RRULE for frequency of 
//notification
notifyMe.addAction(Intent intent,String text); //The action will call the intent when 
//pressed

After all the fields that you want are set just call build()!

notifyMe.build();