4

I want to send offline notifications to users daily.Tried many things like: Alarm manager,Work manager but no luck.The notifications are not triggered or they just trigger at later.

Any way to get the job done?

Alarm function:

public void StartAlarm()
{
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY,23);
    calendar.set(Calendar.MINUTE,59);
    calendar.set(Calendar.SECOND,0);


    AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);


    Intent alarmIntent = new Intent(getApplicationContext(), AlarmReceiver.class);
    //alarmIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

    Log.d("LOG", String.valueOf(calendar.getTimeInMillis()));

    PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 156, alarmIntent, 0);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
        } else {
            alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
        }

Work manager code:

 public MyWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
    super(context, workerParams);
}


@NonNull
@Override
public Result doWork() {


    StartAlarm();


    Log.d("In worker","yes");


    return Result.success();
}

Work manager driver:

public void StartPeriodicWorker()
{

    final PeriodicWorkRequest periodicWorkRequest = new
            PeriodicWorkRequest.Builder(MyWorker.class,24,TimeUnit.HOURS)
            .addTag("Birthday")
            .build();

    WorkManager.getInstance(getApplicationContext()).enqueueUniquePeriodicWork("Birthday Notifier", ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest);

}

Alarm Receiver:

  public class AlarmReceiver extends BroadcastReceiver {

private DatabaseReference myRef;
private ArrayList<String> allPeoples;

private int numberOfPeoples;

private Context ctx;

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

    Toast.makeText(context,"In alarm receiver",Toast.LENGTH_LONG).show();

    ctx = context;
    initPeoples();

}

public String getCurrentDate() {
    Calendar calendar = Calendar.getInstance();
    SimpleDateFormat mdformat = new SimpleDateFormat("d MMMM");
    String strDate = mdformat.format(calendar.getTime());
    return strDate;
}


private  void initPeoples() {

    FirebaseDatabase database = FirebaseDatabase.getInstance();
    myRef = database.getReference("Users");
    myRef.keepSynced(true);

    myRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

            allPeoples =  new ArrayList<>();
            for(DataSnapshot snapshot : dataSnapshot.getChildren()){
                if(snapshot.getKey().equals("Teacher") || snapshot.getKey().equals("Staff")){
                    for(DataSnapshot faculty : snapshot.child("peoples").getChildren()){
                        String birthday = (String) faculty.child("DOB").getValue();

                        if(birthday.equals(getCurrentDate())) {
                            String member = birthday;
                            allPeoples.add(member);
                        }
                    }
                }else{
                    for(DataSnapshot student : snapshot.child("peoples").getChildren()){
                        String birthday = (String) student.child("DOB").getValue();

                        if(birthday.equals(getCurrentDate())) {
                            String member = birthday;
                            allPeoples.add(member);
                        }
                    }
                }
            }

            numberOfPeoples = allPeoples.size();

            ShowNotification();

        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });

}

public void ShowNotification()
{
    String CHANNEL_ID = "Channel_1453";
    String CHANNEL_NAME = "Birthday Notification";

    NotificationManagerCompat manager = NotificationManagerCompat.from(ctx);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
                NotificationManager.IMPORTANCE_HIGH);
        manager.createNotificationChannel(channel);
    }

    Notification notification = new NotificationCompat.Builder(ctx, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_stat_notify)
            .setContentTitle("Birthday Reminder")
            .setColor(Color.GREEN)
            .setContentText("Click to see who has birthday today!")
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setAutoCancel(true)
            .setCategory(NotificationCompat.CATEGORY_MESSAGE)
            .setContentIntent(PendingIntent.getActivity(ctx, 0, new Intent(ctx, Birthday.class), PendingIntent.FLAG_UPDATE_CURRENT))
            .build();

    manager.notify(454, notification);
}

}

Noaman Akram
  • 3,680
  • 4
  • 20
  • 37
null_override
  • 467
  • 3
  • 10
  • 30
  • are you having this problem on all devices? based on my experience it has to work well on emulator. correct? – Keivan.k Aug 07 '20 at 14:14
  • Yeah all devices.My emulator is Google Pixel 3A xl . – null_override Aug 07 '20 at 14:17
  • Yes, please try another another emulator like nexus s. Then please report back if it works fine or not. I will explain to you what might be your solution. If it also doesn't work on nexus s then please add your code to the question. – Keivan.k Aug 07 '20 at 14:19
  • Okay..One thing I forgot to say, I have used Nexus S API 19 on which works great.I am adding the codes here. – null_override Aug 07 '20 at 14:21
  • Just edited the question.I have also tried setRepeating on alarms (Didn't used work manager then) but they don't fire up – null_override Aug 07 '20 at 14:27
  • This answer can solve your problems https://stackoverflow.com/questions/63471520/send-daily-notification-at-a-scheduled-time-android/68936552#68936552 – Yaqoob Bhatti Aug 26 '21 at 10:11

3 Answers3

2

It seems like you are doing everything right, and as you said in the comments that it's working on nexus S, I assume that you also declared the receiver in the manifest.

So after week of having the same problem here is the result:

In some devices with custom OS like Xiaomi, when you swipe the app away from the list of recents, the OS considers it as a force stop and therefor all services and other stuff will be killed. Based on Issue Tracker there is no way around it at the moment to solve this issue. They responded that they are working with OEMs to resolve this issue.

But based on my own experience, if you setup your notification using work manager, it will be delayed, but you will eventually receive it (at least most of the time).

But if you want the timing to be exact, there is no way to proceed at the moment.

The only solution at the moment is to give some permission manully at the moment. Please visit dontkillmyapp for more information regarding this manual settings.

Keivan.k
  • 548
  • 6
  • 21
  • I have been also tried everything(Almost) for a week and no luck.I wonder how does games do this(Offline notification about "You haven't entered the game blah blah..." .Thanks for the info and if you have time can you look at my other question? This post and that one is related to each other.This is the link: https://stackoverflow.com/questions/63262762/whats-wrong-with-my-notifications-and-service – null_override Aug 07 '20 at 14:43
  • At first I went to the services approach and it kind of worked sometimes...Then came back to work manager and alarms – null_override Aug 07 '20 at 14:46
  • I understand, games can accomplish it using work manager, if you try to send a notifcation by workmanager (and then set an initial delay) then it eventually sends the notification, but with some delay (like 8 or 10 hours delay) – Keivan.k Aug 07 '20 at 17:48
  • I checked your other questions, that was exactly the same for me, I also tried all these approaches with no luck. if you open the app it sends the notification. But if you wait longer, it will send the notification even if the app is not opened. the battery optimization will do this, the cluster some workrequests together and do them altogether, that is the reason behind the delay – Keivan.k Aug 07 '20 at 17:50
  • and if this was your answer, please accept it so everyone else can see it, I had this headache for this problem searching everywhere – Keivan.k Aug 07 '20 at 17:51
  • I am currently testing the approach Amin has told.I will accept it as an answer if I get it working. In past ,I have also tried to use initial delay.No luck.Can't test for like 4-10 hours .More info: https://medium.com/androiddevelopers/workmanager-periodicity-ff35185ff006 – null_override Aug 07 '20 at 17:54
  • I see, please let me know if you found a solution. I will check that link – Keivan.k Aug 07 '20 at 17:58
  • Worked like a charm :) I will add a complete answer later.And yes, I have accepted the answer. – null_override Aug 07 '20 at 18:12
  • Wow great, I will try it too. Excellent – Keivan.k Aug 07 '20 at 18:13
  • 1
    Could you please respond to my comment? Have you managed to make it work in a reliable way? I used the foreground service. It worked a few times, but the the Alarm stopped working when the app is closed. – Keivan.k Aug 25 '20 at 13:49
  • Another thing, when you Edited the question, you added that it didn’t work for you. But anyone may take this as Amin's comment. – null_override Aug 25 '20 at 16:38
  • A small suggestion: Decompile Xiaomi's applications (Whichever sends notifications). You will find out that, they also use Broadcast receivers – null_override Aug 25 '20 at 16:42
  • It shows that I edited the answer, don't worry about that. I did the compile some apps, they are also using alarm manager and broadcast receiver and their app also doesnt work some of the times – Keivan.k Aug 25 '20 at 16:53
  • I also don't know how to start the chat, usually there is a small link down the comments sections. Anyway, did you manage to send notification on Xiaomi devices? – Keivan.k Aug 25 '20 at 16:53
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/220441/discussion-between-null-override-and-keivan-k). – null_override Aug 25 '20 at 16:56
2

I can see one possible issue, and it's possible to do this without the workmanager (assuming the device has a healthy connection at the time the notification runs).

Instead of doing your network in the receiver itself, I suggest starting a service (foreground service if above Android 8.0), and doing your work there. This is because android's time limit for a receiver is much lower than a service/foreground service. So to me, it sounds plausible that your receiver is killed before the network request completes, and so no notification is shown.

You can show the notification in the service, and also schedule the next alarm since setExactAndAllowWhileIdle isn't a repetitive alarm on it's own. So in your receiver, something like:

@Override
public void onReceive(Context context, Intent intent) {
        Intent service = new Intent(context, BirthdayNotifyService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(service);
        } else {
            context.startService(service);
        }
}

Then a possible service class:

public class BirthdayNotifyService extends IntentService {

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

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //Build a simple notification to show if a foreground service is neccesary
            Notification noti = notiBuilder.build();
            startForeground(1, noti);
        }

        //Do your network request work here. After completed, show your birthday notification and stop the foreground service.
    }
}

To stop the service, right AFTER you display your notification for birthdays, use the following:

stopForeground(true);
stopSelf();

UPDATE: I used this method on my phone (Xiaomi Redmi Note 8 plus), it worked a few times but then stopped working. It works perfectly fine on stock android, but not on all devices. Therefore I would still say this is not a reliable solution to send notification on specific times.

Keivan.k
  • 548
  • 6
  • 21
Amin
  • 908
  • 1
  • 11
  • 23
  • Thanks for your answer Amin. But on Android R intentservice is depricated.Link: https://developer.android.com/reference/android/app/IntentService – null_override Aug 07 '20 at 15:29
  • There they say to use work manager or job schedular. I already tried to use a service, you can see my old post: https://stackoverflow.com/questions/63262762/whats-wrong-with-my-notifications-and-service But it had problems with battery – null_override Aug 07 '20 at 15:31
  • Were you trying to run a service in the background constantly or only when the alarm fires? – Amin Aug 07 '20 at 15:45
  • It ran constantly. – null_override Aug 07 '20 at 15:45
  • 1
    My example is designed to only start the service when the alarm fires. Any chance you'd be able to start the alarm in an activity instead and call the service from the receiver? – Amin Aug 07 '20 at 15:46
  • I ran that code with success on api levels 21 through 30 (never tested on Xiomai devices to be fair) – Amin Aug 07 '20 at 15:47
  • yeah,I tried to start alarm on Main Activity but it didn't work. So, your code works on API 30 ? okays, I am gonna try it :) – null_override Aug 07 '20 at 15:52
  • Yep, it's deprecated but should (mostly) still work until it is removed from the SDK entirely. The deprecation is news to me though - probably not a future proof solution. Hopefully this answer could help make some progress? https://stackoverflow.com/questions/46675242/issue-moving-from-intentservice-to-jobintentservice-for-android-o – Amin Aug 07 '20 at 15:55
  • My initial thought was to use Intent service but was confused.So, what should I use here? Intent service or Job INTENT service? – null_override Aug 07 '20 at 16:00
  • 1
    I haven't tried JobIntentService, so I can't speak whether that one will work in your case. But here's the documentation for Android R and starting a foreground service - allowing you to make the network request in the background. https://developer.android.com/preview/privacy/foreground-services#types, and the list of foreground service types https://developer.android.com/reference/android/R.attr#foregroundServiceType – Amin Aug 07 '20 at 16:05
  • I think another alternative is ditch the service all together, and just show the "Tap to see birthdays" notification, since the notification itself doesn't depend on any network data. When the app is opened from the notification, make a network request and load the necessary data – Amin Aug 07 '20 at 16:06
  • I have thought about this but my target is to send notifications only if a birthday exists.As the data will be fetched on first run and synced later on, the notifications should work even without networking . – null_override Aug 07 '20 at 16:09
  • 1
    I see your reasoning for the constantly running service then - but I would recommend a single foreground service started from an alarm receiver, since background services are a pain to keep running on many android OEMs. Also if that's your goal, I think you should move ```showNotification``` to inside of a conditional to check if ```numberOfPeoples``` is > 0 first – Amin Aug 07 '20 at 16:13
  • Thanks for shading light on this. I will check number Of People >0 .I haven't done that because my alarms weren't firing at the first place let alone showing notifications. – null_override Aug 07 '20 at 16:17
  • Thanks a lot man!! Worked like a charm :) One thing, how can I stop the foreground service here? – null_override Aug 07 '20 at 18:21
  • Glad it worked! I updated the answer to show how to stop the service – Amin Aug 07 '20 at 20:57
  • Thanks again for adding how to stop the service.But I am facing a problem in Android versions less than 8.0 . A notification (with just logo) popping up every time. How can I stop this from happening? – null_override Aug 08 '20 at 15:58
  • Here is the code I am using inside onHandleIntent : `final Notification noti = new NotificationCompat.Builder(getApplicationContext(),CHANNEL_ID) .setSmallIcon(R.drawable.ic_stat_notify) .setAutoCancel(true) .build(); manager.notify(2021, noti); startForeground(1, noti);` – null_override Aug 08 '20 at 15:58
  • And while creating the dummy notification: `if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE); manager.createNotificationChannel(channel); }` – null_override Aug 08 '20 at 16:02
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/219460/discussion-between-amin-and-null-override). – Amin Aug 08 '20 at 16:07
  • Does it still work for you correctly?? it worked for me a few times and then stopped working. – Keivan.k Aug 19 '20 at 16:21
-1

*At first integrate *

'$' dependencies { implementation "androidx.core:core:$core_version" }

in your gradle file. then follow the Code : https://github.com/NafimAhmed/Offline_Notification

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Alan Bosco Feb 23 '23 at 10:45
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/33878276) – Robin Davies Feb 23 '23 at 19:12