web-dev-qa-db-fra.com

Comment envoyer des notifications sur Android à une heure précise dans Android Oreo?

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?

7
Tricky Bay

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.

12
Tricky Bay

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.

  • Android 6: somnoler
  • Android 7: somnolence agressive [Désactive le processeur peu de temps après la désactivation de l'écran]
  • Android 8: restrictions supplémentaires sur les services d'arrière-plan

L'option uniquement AlarmManager qui permet à un développeur de réactiver un appareil à une heure spécifique est AlarmManager.setAlarmClock.

en utilisant 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.

2
Elletlar