Je cherche un moyen de créer un preference
dans Settings
pour envoyer une notification à une heure spécifique de la journée (définie par l'utilisateur dans les paramètres) dans une application Android . J'ai regardé différents threads comme ceci , mais cela ne fonctionne pas dans Android Oreo.
Quelqu'un peut-il m'aider ou me diriger vers un tutoriel?
Après avoir regardé différents articles et quelques recherches sur l'implémentation de AlarmManager
, c'est ce qui a fonctionné pour moi.
La base de ceci est this publier et planifier des alarmes répétitives Android Documentation .
Voici ma mise en œuvre actuelle:
J'ai une implémentation SwitchPreference
et une implémentation TimePicker
est Settings
SwitchPreference pour demander si l'utilisateur souhaite activer les notifications quotidiennes répétitives.
TimePicker pour définir l'heure de notification.
Dans la méthode MainActivity
de OnCreate
ou partout où vous lisez la SharedPreferences
, procédez comme suit:
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 notifications
if (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 day
if (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 Notifications
if (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);
}
Ajoutez ensuite la classe AlarmReceiver
qui implémente BroadcastReceiver
comme ceci:
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(Objects.requireNonNull(context));
SharedPreferences.Editor sharedPrefEditor = prefs.edit();
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingI = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
if (Android.os.Build.VERSION.SDK_INT >= Android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("default",
"Daily Notification",
NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("Daily Notification");
if (nm != null) {
nm.createNotificationChannel(channel);
}
}
NotificationCompat.Builder b = new NotificationCompat.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());
Calendar nextNotifyTime = Calendar.getInstance();
nextNotifyTime.add(Calendar.DATE, 1);
sharedPrefEditor.putLong("nextNotifyTime", nextNotifyTime.getTimeInMillis());
sharedPrefEditor.apply();
}
}
}
Le système désactivera le AlarmManager
si l'appareil est éteint ou redémarre, alors redémarrez-le à nouveau sur BOOT COMPLETE
ajoutez cette classe:
public class DeviceBootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Objects.equals(intent.getAction(), "Android.intent.action.BOOT_COMPLETED")) {
// on device boot complete, 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);
final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(Objects.requireNonNull(context));
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);
Calendar newC = new GregorianCalendar();
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);
}
}
}
}
Et enfin n'oubliez pas d'ajouter ces autorisations à AndroidManidest
:
<uses-permission Android:name="Android.permission.WAKE_LOCK" />
<uses-permission Android:name="Android.permission.RECEIVE_BOOT_COMPLETED" />
et enregistrez vos récepteurs dans AndroidManifest
<application
<!--YOUR APPLICATION STUFF-->
<receiver Android:name=".DeviceBootReceiver"
Android:enabled="false">
<intent-filter>
<action Android:name="Android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver Android:name=".AlarmReceiver" />
Le Notification
doit être défini par ceci à une heure spécifique de la journée spécifiée par TimePicker
et si l'utilisateur a activé le SwitchPreference
.
Il existe des tonnes d'exemples sur la façon de le faire sur StackOverflow, mais malheureusement, aucun d'entre eux ne fonctionne plus car Google a changé le fonctionnement d'AlarmManager.
L'option uniquement AlarmManager qui permet à un développeur de réactiver un appareil à une heure spécifique est AlarmManager.setAlarmClock.
En plus de l'alarme, vous pouvez définir une intention qui vous permettra de lancer votre application lorsque l'alarme est cliquée.
L'appareil peut également être réveillé à une heure précise à l'aide d'un FCM de haute priorité, Firebase Cloud Message.
Mais en résumé, il n'y a pas d'autre option: 'setExact' et les méthodes associées ne fonctionnent plus comme le nom l'indique.