Using the code of BroadcastReceiver
of response that refers and implementing what is referred to in the notes, will be like this:
public class StartUpBootReceiver extends BroadcastReceiver {
private static String HOUR = "hour";
private static String MINUTE = "minute";
public static void setAlarm(Context context, int hour, int minute){
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.edit()
.putInt(HOUR, hour)
.putInt(MINUTE, minute)
.apply();
setAlarm(context);
}
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
setAlarm(context);
Toast.makeText(context, "Alarm set", Toast.LENGTH_LONG).show();
}
}
private static void setAlarm(Context context) {
int hour = getHour(context);
int minute = getMinute(context);
if(hour == -1 || minute == -1){
//nenhum horário definido
return;
}
// Cria um Calendar para o horário estipulado
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
//Se já passou
if(isDateBeforeNow(calendar)){
//adiciona um dia
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
//PendingIntent para lançar o serviço
Intent serviceIntent = new Intent(context, BootService.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent, 0);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
//Cancela um possível alarme existente
alarmManager.cancel(pendingIntent);
//Alarme que se repete todos os dias a uma determinada hora
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(),
AlarmManager.INTERVAL_DAY,
pendingIntent);
}
private static int getHour(Context context){
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getInt(HOUR, -1);
}
private static int getMinute(Context context){
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getInt(MINUTE, -1);
}
private static boolean isDateBeforeNow(Calendar calendar){
return calendar.getTimeInMillis() <= System.currentTimeMillis();
}
}
Use the method
StartUpBootReceiver.setAlarm(context, hour, minute);
to set/change the alarm time.
If the device is switched off, the alarm will be re-recorded.
Declare the BroadcastReceiver
in the Androidmanifest.xml
<receiver android:name="aSuaPackage.StartUpBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Add the permission
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Implement the service to launch the notification:
public class BootService extends IntentService {
private PowerManager.WakeLock wakeLock;
public BootService() {
super("name");
}
@Override
public void onCreate() {
super.onCreate();
PowerManager pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
PowerManager.ACQUIRE_CAUSES_WAKEUP |
PowerManager.ON_AFTER_RELEASE, "BootService");
wakeLock.acquire();
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//Lance a notificação aqui.
}
@Override
public void onDestroy() {
super.onDestroy();
if(wakeLock.isHeld()){
//Verificou-se que o iluminar do ecrã
//não acontecia devido ao WakeLock ser
//rapidamente libertado(apesar de PowerManager.ON_AFTER_RELEASE !?).
try {
//Atrasa a libertação do WakeLock
//de forma a permitir a iluminação do ecrâ.
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
wakeLock.release();
}
}
}
Declare it in the AndroidManifest.xml
<service android:name=".BootService"/>
and add the permission to get the Wake Lock
<uses-permission android:name="android.permission.WAKE_LOCK"/>
Note: The first pitch of a inexact Repeating Alarm, as pointed out in documentation, will never be before the indicated time, but may not occur during almost the entire interval after that time. If the repeat interval is long and the alarm is set to a time after a short time, the first release may only occur after the interval has elapsed.
An alternative is to use the method setRepeating()
instead of setInexactRepeating()
. However, from API 19 all "Repeating Alarm" are considered "inaccurate".
The solution is to set an alarm using the method set()
and then add to the calendar the repeat interval and set another alarm using setInexactRepeating()
.
A possible implementation for INTERVAL_DAY
it will be so:
public class StartUpBootReceiver extends BroadcastReceiver {
private static int REPEATING_ID = 1001;
private static int ON_TIME_ID = 1002;
private static String HOUR = "hour";
private static String MINUTE = "minute";
public static void setAlarm(Context context, int hour, int minute){
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.edit()
.putInt(HOUR, hour)
.putInt(MINUTE, minute)
.apply();
setAlarm(context);
}
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
setAlarm(context);
Toast.makeText(context, "Alarm set", Toast.LENGTH_LONG).show();
}
}
private static void setAlarm(Context context) {
int hour = getHour(context);
int minute = getMinute(context);
if(hour == -1 || minute == -1){
//nenhum horário definido
return;
}
//Cancela possiveis alarmes existentes
cancelAlarm(context);
Calendar calendar = getCalendar(hour, minute);
//Se já passou
if(isDateBeforeNow(calendar)){
//adiciona um dia
calendar.add(Calendar.DAY_OF_MONTH, 1);
}else{
//Alarme para o horário especificado
setOneTimeAlarm(context, calendar);
//adiciona um dia para repetir o alarme no dia seguinte
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
//Repete o alarme no dia seguinte
setRepeatingAlarm(context, calendar);
}
private static void setRepeatingAlarm(Context context, Calendar calendar){
PendingIntent pendingIntent = getPendingIntent(context, REPEATING_ID);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
//Alarme que se repete todos os dias a uma determinada hora
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(),
AlarmManager.INTERVAL_DAY,
pendingIntent);
}
private static void setOneTimeAlarm(Context context, Calendar calendar){
PendingIntent pendingIntent= getPendingIntent(context, ON_TIME_ID);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
//Alarme para o horário especificado
alarmManager.set(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(),
pendingIntent);
}
private static void cancelAlarm(Context context){
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(getPendingIntent(context, ON_TIME_ID));
alarmManager.cancel(getPendingIntent(context, REPEATING_ID));
}
private static PendingIntent getPendingIntent(Context context, int id){
//PendingIntent para lançar o serviço
Intent serviceIntent = new Intent(context, BootService.class);
return PendingIntent.getService(context, id, serviceIntent, 0);
}
private static Calendar getCalendar(int hour, int minute){
// Cria um Calendar para o horário especificado
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, 0);
return calendar;
}
private static int getHour(Context context){
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getInt(HOUR, -1);
}
private static int getMinute(Context context){
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getInt(MINUTE, -1);
}
private static boolean isDateBeforeNow(Calendar calendar){
return calendar.getTimeInMillis() <= System.currentTimeMillis();
}
}
Explain what you mean by "it’s not working properly"
– ramaral
@ramaral is not launching the notification to the user.
– viana
The way it is, with
calendar.add(Calendar.DAY_OF_MONTH, 1);
, is only released the next day. Read the comments there on the other question.– ramaral
@ramaral in any chance is it possible to launch type shortly? How would it be?
– viana
Take that line.
– ramaral
@Ramaral I took but it didn’t work.
– viana
The code was tested before answering that question. Don’t forget the permissions. If targetSdkVersion is 24 there are also changes with respect to ACTION_BOOT_COMPLETED. Testing with targetSdkVersion 21
– ramaral
@ramaral Does this permission enter the concept of permission in real time? I am in the market, soon arriving at home I take the test. Did you ever see my code? You saw something wrong?
– viana
No, you just have to take that line out. Pay attention to the notes and comments in the other reply.
– ramaral
@ramaral "No" to which question? I arrived home researched, really the RECEIVE_BOOT_COMPLETED does not enter the concept of Permission Runtime. I’ll do a little more digging to see what that might be. I read the comments there, saw that it worked for the boy, so it should work here for me too, considering that I didn’t change anything in the code beyond the time and the withdrawal of the line, as you can see on githubgist. But anyway, thank you for your attention and forgive the ignorance.
– viana