Exact time for custom notifications in Android

2019-07-14 06:59发布

问题:

I am developing an Android application for counseling services. Client can view their scheduled appointment in app. For example,

Next appointment: Dec 31 2016 10:00AM

Now I need to do that user will receive 2 notifications – reminders about appointment. One on 7 days before and another on 3 days before. I save this date (Dec 31 2016 10:00AM) as a String so I can extract year, month, etc. I found that I need to write some kind of service which will send these notifications. This is what I tried (is not completed):

public class NotificationService extends Service {
    @Override
    public void onCreate() {
        Intent resultIntent=new Intent(this, MainActivity.class);
        PendingIntent pIntent = PendingIntent.getActivity(this, 0, resultIntent, 0);
        Notification nBuilder = new Notification.Builder(this)
                .setContentTitle("Don't miss! ")
                .setTicker("Notification!")
                .setContentIntent(pIntent)
                .setDefaults(Notification.DEFAULT_SOUND)
                .setAutoCancel(true)
                .setSmallIcon(R.drawable.my_logo)
                .setContentText("7 days left till your appointment...")
                //.setWhen(System.currentTimeMillis())
                .build();
        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        nBuilder.flags |=Notification.FLAG_AUTO_CANCEL;
        notificationManager.notify(1,nBuilder);
    }

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

And method that I don't know from where to call:

public void reminder() {
    Intent intent  = new Intent(getActivity(), MainActivity.class);

    AlarmManager manager =(AlarmManager) getActivity().getSystemService(Activity.ALARM_SERVICE);
    PendingIntent pendingIntent = PendingIntent.getService(getActivity().getApplicationContext(),
            0,intent, 0);
    Calendar cal=Calendar.getInstance();
    cal.set(Calendar.HOUR_OF_DAY, 8); 
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    manager.setRepeating(AlarmManager.RTC_WAKEUP,cal.getTimeInMillis(),24*60*60*1000,pendingIntent);
}

For testing purposes I have set hour/minute/second manually but obviously I will need to extract it from date String.

回答1:

You need to write an IntentService first. Here's an example, you can write code for showing the notification in processNotification function.

public class NotificationIntentService extends IntentService {

    private static final String ACTION_START = "ACTION_START";

    public NotificationIntentService() {
        super(NotificationIntentService.class.getSimpleName());
    }

    public static Intent createIntentStartNotificationService(Context context) {
        Intent intent = new Intent(context, NotificationIntentService.class);
        intent.setAction(ACTION_START);
        return intent;
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        try {
            String action = intent.getAction();
            if (ACTION_START.equals(action))
                processNotification();

        } finally {
            WakefulBroadcastReceiver.completeWakefulIntent(intent);
        }
    }

    private void processNotification() {
        Intent resultIntent=new Intent(this, MainActivity.class);
        PendingIntent pIntent = PendingIntent.getActivity(this, 0, resultIntent, 0);
        Notification nBuilder = new Notification.Builder(this)
                .setContentTitle("Don't miss! ")
                .setTicker("Notification!")
                .setContentIntent(pIntent)
                .setDefaults(Notification.DEFAULT_SOUND)
                .setAutoCancel(true)
                .setSmallIcon(R.drawable.my_logo)
                .setContentText("7 days left till your appointment...")
                .build();
        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        nBuilder.flags |=Notification.FLAG_AUTO_CANCEL;
        notificationManager.notify(1, nBuilder);
    }
}

And then create a NotificationEventReceiver

public class NotificationEventReceiver extends WakefulBroadcastReceiver {

    private static final String ACTION_START_NOTIFICATION_SERVICE = "ACTION_START_NOTIFICATION_SERVICE";

    public static void setupAlarm(Context context, long interval) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        PendingIntent alarmIntent = getStartPendingIntent(context);

        alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), interval, alarmIntent);
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Intent serviceIntent = null;
        if (ACTION_START_NOTIFICATION_SERVICE.equals(action)) {
            serviceIntent = NotificationIntentService.createIntentStartNotificationService(context);
        }

        if (serviceIntent != null) {
            startWakefulService(context, serviceIntent);
        }
    }

    private static PendingIntent getStartPendingIntent(Context context) {
        Intent intent = new Intent(context, NotificationEventReceiver.class);
        intent.setAction(ACTION_START_NOTIFICATION_SERVICE);
        return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    }
}

And the NotificationServiceStarterReceiver

public final class NotificationServiceStarterReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        long interval = getIntent().getLongExtra("alarm_interval", 0);
        NotificationEventReceiver.setupAlarm(context, interval);
    }
}

Add these in your AndroidManifest.xml inside <application> tag

<service
    android:name="YourPackage.NotificationIntentService"
    android:enabled="true"
    android:exported="false" />

<receiver android:name="YourPackage.BroadcastReceiver.NotificationEventReceiver" />
<receiver android:name="YourPackage.BroadcastReceiver.NotificationServiceStarterReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <action android:name="android.intent.action.TIMEZONE_CHANGED" />
        <action android:name="android.intent.action.TIME_SET" />
    </intent-filter>
</receiver>

Now from your Activity you may call the setupAlarm() inside onCreate function.

NotificationEventReceiver.setupAlarm(getApplicationContext(), interval);

You need to add WAKE_LOCK permission in your manifest.

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

Here you see you can pass the interval of the next notification to be shown. Use the interval wisely. You might consider saving the current statuses of the appointment in the database and then trigger the alarm when necessary by passing proper interval of next alarm. That's the idea.

Update

So in your case, you don't want to show the notification when user is logged out. So in this case, you might consider keeping a SharedPreference to store the login status. You might call the processNotification function based on the value stored.

So the pseudo code may look like this.

if(pref.getBoolean("login_status", false)) {
    // If the login status is true, process the notification
    processNotification();
} else {
    // Do nothing
}