androidnotificationsalarmmanagerandroid-10.0

How to show daily offline notifications in Android 10?


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);
}

}


Solution

  • 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.