I have following code to fire an alarm at exact time. When my app goes to sleep/background, Alarm does not FIRE, however as soon as I unlock my phone, then it fires right away.
Intent i = new Intent(this, typeof(MyReceiver));
i.PutExtra("Speaker", txtSpeaker.Text);
PendingIntent pi = PendingIntent.GetBroadcast(this, 0, i, 0);
string _date = DateTime.Today.ToString("MM-dd-yyyy") ;
string _time = tpick.Hour + ":" + tpick.Minute;
DateTime scheduleAt = Convert.ToDateTime(_date).Add(TimeSpan.Parse(_time));
DateTimeOffset dateOffsetValue = DateTimeOffset.Parse(scheduleAt.ToString());
var millisec = dateOffsetValue.ToUnixTimeMilliseconds();
AlarmManager alarmManager = (AlarmManager)GetSystemService(AlarmService);
alarmManager.SetExact(AlarmType.RtcWakeup, millisec, pi);
and here is my Receiver Class...
[BroadcastReceiver]
public class MyReceiver : BroadcastReceiver
{
public async override void OnReceive(Context context, Intent intent)
{
Console.WriteLine("FIRED");
String result = intent.Extras.GetString("Speaker");
Toast.MakeText(context, "Alarm Ringing!", ToastLength.Short).Show();
}
}
If the App is running on API 23 or newer, you might want to look into using the SetExactAndAllowWhileIdle method. According to the documentation it appears to me that this method will ignore whether the Device is in some low-power state or doze is enabled.
Related
So I made an app that upon a button click sets up a repeating task using an Alarm Manager.
In on create:
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
servicePendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
On the button click:
alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
firingCal= Calendar.getInstance();
firingCal.setTimeInMillis(System.currentTimeMillis());
firingCal.set(Calendar.HOUR_OF_DAY, 1); // At the hour you want to fire the alarm
firingCal.set(Calendar.MINUTE, 47); // alarm minute
firingCal.set(Calendar.SECOND, 0); // and alarm second
long intendedTime = firingCal.getTimeInMillis();
long interval = 1000 * 60 * 1;
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, intendedTime, interval, servicePendingIntent);
In the AlarmReceiver class:
public void onReceive(Context context, Intent intent) {
Intent myIntent = new Intent(context, WallpaperService.class);
context.startService(myIntent);
Log.d(TAG,"Am apelat serviciul");
context.stopService(myIntent);
}
And in the WallpaperService class I just make a get request and set an wallpaper.
public class WallpaperService extends Service {
String requestLink="";
boolean requestFinished = false;
public final String TAG = "Service";
public static int SERVICE_ID = 1;
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"Wallpaper Service started");
Toast.makeText(WallpaperService.this,"Service started",Toast.LENGTH_LONG);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,"In onStartCommand");
taskToBeRepeated();
stopSelf();
return START_STICKY;
}
.....
}
And the behaviour is that when I start the app and I click the button everything works well the first time the Alarm Manager fires ( With the app in the background). The second time the receiver gets triggered I get the error in the tile. To be more specific :
java.lang.RuntimeException: Unable to start receiver com.example.dailywallpaper.AlarmReceiver: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.example.dailywallpaper/.WallpaperService }: app is in background uid UidRecord{3e313bf u0a357 RCVR bg:+1m21s273ms idle change:uncached procs:1 seq(0,0,0)}
What seems to be the problem ? And why is working the first time and then it gives the error? How can I fix it ?
you need to read android official documentation about the policy of using background service or alarms in android 8 and above and adapt your app with this limitations.
I suggest you to read this two articles very carefully :
https://developer.android.com/guide/components/services
https://developer.android.com/about/versions/oreo/background
I'm trying to set an alarm to fire every 5 minutes.
This is the code for setting the alarm :
#Override
public void scheduleAlarmManager() {
Timber.i("After SignIn sets AlarmManager");
// broadcast
Intent intent = new Intent(this, PatientAlarmReceiver.class);
intent.setAction(PATIENT_START_ALARM_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this, REQUEST_CODE, intent, 0);
// and set alarmManager
AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Calendar currentCal = Calendar.getInstance();
long currentTIme = currentCal.getTimeInMillis();
// if there's not an Alarm already set then set one
if (!isAlarmSet(this)) {
Timber.i("Alarm not set - so set one");
alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
currentTIme + TWO_MINS_DURATION, TWO_MINS_DURATION, pendingIntent);
}
}
and I can verify that I set the alarm correctly since I see in my logcat the messages I log with Timber.
My Receiver class is :
public class PatientAlarmReceiver extends BroadcastReceiver {
public static final String TAG = "PATIENT-ALARM-RECEIVER";
public static final String PATIENT_START_ALARM_ACTION = "bp.headsup.receivers.alarm.patient";
#Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Inside OnReceive Patient");
Timber.i("Inside OnReceive Patient");
if (intent == null || intent.getAction() == null) {
return;
}
String action = intent.getAction();
if (PATIENT_START_ALARM_ACTION.equalsIgnoreCase(action)) {
onStartCheckForConnectionRequest(context);
}
}
/**
* If is connected to network starts services
*/
private void onStartCheckForConnectionRequest(Context context) {
NetworkUtils networkUtils = new NetworkUtils(context);
if (networkUtils.isNetworkConnected()) {
Intent checkForConnRequestIntent = new Intent(context, PatientCheckForConnectionRequestService.class);
context.startService(checkForConnRequestIntent);
Timber.i("Starts Service From PatientALARMMANAGER");
}
}
}
And I have declared in Manifest :
<!-- Receivers -->
<receiver
android:name="bp.headsup.receivers.PatientAlarmReceiver" />
Also if I run : adb shell dumpsys alarm
I can see :
ELAPSED_WAKEUP #0: Alarm{42d804e8 type 2 bp.headsup.mock}
operation=PendingIntent{42d0c230: PendingIntentRecord{42d0f000 bp.headsup.mock broadcastIntent}}
Mock in the above response is the sourceSet I'm using - dont know if it has anything to do with this I just mention it.
The problem is I never read in logcat the messages I have in onReceive on my Receiver class, and obviously no service starts. Anyone can help with that ? I'm using a device which runs with kitKat 4.4 (api 19) but I have tried it with an emulator too and the result was the same.
You're setting an ELAPSED_REALTIME alarm, which is based on the time since the last boot. However, you're passing it a starting time based on the "wall clock", so your alarm is actually set quite far in the future.
You can either change the alarm to an RTC type, or get the starting time from SystemClock.elapsedRealtime(). Given your described behavior, keeping the elapsed type and correcting the starting time seems appropriate.
Before closing this issue or marking as duplicate based on the title, it is different from the regular "cancel alarm using AlarmManager and PendingIntent" questions.
I am capable of creating and cancelling pending intents, as long as they are set for a time in the future and haven't gone off yet. I'm testing this using the following terminal command to view the PendingIntents before creating an alarm as well as after:
adb shell dumpsys alarm
Here is my code for scheduling alarms in my custom Alarm class:
/**
* Schedules a PendingIntent for the alarm.
* #param context Activity context
*/
public void scheduleAlarm(Context context) {
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyBroadcastReceiver.class);
Gson g = new Gson();
String s = g.toJson(this);
intent.putExtra("alarm", s);
String id = this.getId().replaceAll("[^0-9]+", "");
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, Integer.parseInt(id), intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, this.getHour());
calendar.set(Calendar.MINUTE, this.getMinute());
long calendarTime = calendar.getTimeInMillis();
am.setExact(AlarmManager.RTC_WAKEUP, calendarTime, alarmIntent);
}
Not surprisingly, before creating an alarm, there was no pending intent regarding my app's alarms in the terminal output. After creating the alarm, there was 1 pending intent related to my app in the terminal output, as seen below:
+Batch{b28a2db num=1 start=619295497 end=619385497}: + RTC #0: Alarm{24e4178 tag
alarm:com.google.android.location.internal.action.ULR_BAROMETER_READ_ALARM type 1 when 1501206840000 com.google.android.gms} +
tag=alarm:com.google.android.location.internal.action.ULR_BAROMETER_READ_ALARM + type=1 whenElapsed=+1m59s428ms when=2017-07-27 21:54:00 + window=+1m30s0ms repeatInterval=120000 count=0 flags=0x0 +
operation=PendingIntent{cb99ddd: PendingIntentRecord{f0fbd52
com.google.android.gms startService}}
Note I don't have access to my home computer right now so I can't post exactly what it will be for my app, so I just grabbed the PendingIntent for a different app but it is the same structure.
I cancelled the alarm before it went off using the code found below, reran the adb command from before and the pending intent was no longer in the terminal output so everything worked great.
Here is my code for cancelling alarms:
/**
* Cancels the PendingIntent for the alarm.
* #param context
*/
public void cancelAlarm(Context context) {
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyBroadcastReceiver.class);
intent.putExtra("alarm", this);
String id = this.getId().replaceAll("[^0-9]+", "");
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, Integer.parseInt(id), intent, 0);
alarmIntent.cancel();
am.cancel(alarmIntent);
}
Now, if the PendingIntent was reached (BroadcastReceiver runs its onReceive() code and opens a custom activity), I get the following entry in the terminal output when I rerun that adb command:
u0a149:com.my.app +172ms running, 0 wakeups:
+172ms 0 wakes 3 alarms, last -5d8h25m0s423ms:
alarm:com.google.firebase.INSTANCE_ID_EVENT
but I can no longer see the PendingIntent, as expected. Whether I run the cancelAlarm() code or not, this entry will always stay here.
The result of this is that whenever I open the app after the PendingIntent has "gone off" and my BroadcastReceiver class runs it's code, the app acts as if the alarm is continually going off so it does this repeatedly, but like I said there's no PendingIntent entry in the adb output. I want to know how to shut this alarm off or "dismiss" it if you will.
Here is my BroadcastReceiver class:
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String alarm = intent.getStringExtra("alarm");
Intent myIntent = new Intent();
Toast.makeText(context, "Alarm is 1: " + alarm, Toast.LENGTH_SHORT).show();
myIntent.setClassName("com.my.package.name", "com.my.package.name.AlarmReceivedActivity");
myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
myIntent.putExtra("alarm", alarm);
context.startActivity(myIntent);
}
}
and here's my AlarmReceivedActivity:
public class AlarmReceivedActivity extends AppCompatActivity {
private Alarm alarmReceived;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alarm_received);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
TextView tvTime = (TextView) findViewById(R.id.tv_time);
String lTime = "11:05";
tvTime.setText(lTime);
Intent intent = getIntent();
String s = intent.getStringExtra("alarm");
Toast.makeText(getApplicationContext(), "Alarm is: " + s, Toast.LENGTH_SHORT).show();
Gson g = new Gson();
alarmReceived = g.fromJson(s, Alarm.class);
Uri ringtoneUri = Uri.parse(alarmReceived.getRingtone());
Toast.makeText(getApplicationContext(), "Ringtone is: " + alarmReceived.getRingtone(), Toast.LENGTH_SHORT).show();
try {
MediaPlayer mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource(this, ringtoneUri);
final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
mMediaPlayer.setLooping(false);
mMediaPlayer.prepare();
mMediaPlayer.start();
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Failed to play ringtone", Toast.LENGTH_SHORT).show();
}
}
public void dismissButtonClick(View view) {
Context context = getApplicationContext();
alarmReceived.cancelAlarm(context);
alarmReceived.setIsSet(false);
writeAlarmToSharedPrefs(alarmReceived, context);
alarmReceived.cancelAlarm(context);
System.exit(0);
}
private void writeAlarmToSharedPrefs(Alarm alarmReceived, Context context) {
String alarm = getAlarmObjectAsJson(alarmReceived);
SharedPreferences sPrefs = getApplicationContext().getSharedPreferences("Sleepin", MODE_PRIVATE);
SharedPreferences.Editor pe = sPrefs.edit();
pe.putString(alarmReceived.getId(), alarm);
pe.apply();
}
private String getAlarmObjectAsJson(Alarm a) {
Gson g = new Gson();
return g.toJson(a);
}
public void snoozeButtonClick(View view) {
}
}
So I have a couple questions:
1) Since the PendingIntent has been reached, what is this entry in the adb command output referred to as? A "ReachedPendingIntent" (obviously not called this but I hope you get where I'm going with this).
2) How do I stop my app from running the BroadcastReceiver code? My workaround right now is to clear the app's data and cache. After I do this, the second entry from the adb command doesn't appear and the BroadcastReceiver stops running it's code.
I'm trying to set an alarm to fire every 5 minutes.
This is the code for setting the alarm :
#Override
public void scheduleAlarmManager() {
Timber.i("After SignIn sets AlarmManager");
// broadcast
Intent intent = new Intent(this, PatientAlarmReceiver.class);
intent.setAction(PATIENT_START_ALARM_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this, REQUEST_CODE, intent, 0);
// and set alarmManager
AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Calendar currentCal = Calendar.getInstance();
long currentTIme = currentCal.getTimeInMillis();
// if there's not an Alarm already set then set one
if (!isAlarmSet(this)) {
Timber.i("Alarm not set - so set one");
alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
currentTIme + TWO_MINS_DURATION, TWO_MINS_DURATION, pendingIntent);
}
}
and I can verify that I set the alarm correctly since I see in my logcat the messages I log with Timber.
My Receiver class is :
public class PatientAlarmReceiver extends BroadcastReceiver {
public static final String TAG = "PATIENT-ALARM-RECEIVER";
public static final String PATIENT_START_ALARM_ACTION = "bp.headsup.receivers.alarm.patient";
#Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Inside OnReceive Patient");
Timber.i("Inside OnReceive Patient");
if (intent == null || intent.getAction() == null) {
return;
}
String action = intent.getAction();
if (PATIENT_START_ALARM_ACTION.equalsIgnoreCase(action)) {
onStartCheckForConnectionRequest(context);
}
}
/**
* If is connected to network starts services
*/
private void onStartCheckForConnectionRequest(Context context) {
NetworkUtils networkUtils = new NetworkUtils(context);
if (networkUtils.isNetworkConnected()) {
Intent checkForConnRequestIntent = new Intent(context, PatientCheckForConnectionRequestService.class);
context.startService(checkForConnRequestIntent);
Timber.i("Starts Service From PatientALARMMANAGER");
}
}
}
And I have declared in Manifest :
<!-- Receivers -->
<receiver
android:name="bp.headsup.receivers.PatientAlarmReceiver" />
Also if I run : adb shell dumpsys alarm
I can see :
ELAPSED_WAKEUP #0: Alarm{42d804e8 type 2 bp.headsup.mock}
operation=PendingIntent{42d0c230: PendingIntentRecord{42d0f000 bp.headsup.mock broadcastIntent}}
Mock in the above response is the sourceSet I'm using - dont know if it has anything to do with this I just mention it.
The problem is I never read in logcat the messages I have in onReceive on my Receiver class, and obviously no service starts. Anyone can help with that ? I'm using a device which runs with kitKat 4.4 (api 19) but I have tried it with an emulator too and the result was the same.
You're setting an ELAPSED_REALTIME alarm, which is based on the time since the last boot. However, you're passing it a starting time based on the "wall clock", so your alarm is actually set quite far in the future.
You can either change the alarm to an RTC type, or get the starting time from SystemClock.elapsedRealtime(). Given your described behavior, keeping the elapsed type and correcting the starting time seems appropriate.
I'm trying to set an alarm to fire every 5 minutes.
This is the code for setting the alarm :
#Override
public void scheduleAlarmManager() {
Timber.i("After SignIn sets AlarmManager");
// broadcast
Intent intent = new Intent(this, PatientAlarmReceiver.class);
intent.setAction(PATIENT_START_ALARM_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this, REQUEST_CODE, intent, 0);
// and set alarmManager
AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Calendar currentCal = Calendar.getInstance();
long currentTIme = currentCal.getTimeInMillis();
// if there's not an Alarm already set then set one
if (!isAlarmSet(this)) {
Timber.i("Alarm not set - so set one");
alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
currentTIme + TWO_MINS_DURATION, TWO_MINS_DURATION, pendingIntent);
}
}
and I can verify that I set the alarm correctly since I see in my logcat the messages I log with Timber.
My Receiver class is :
public class PatientAlarmReceiver extends BroadcastReceiver {
public static final String TAG = "PATIENT-ALARM-RECEIVER";
public static final String PATIENT_START_ALARM_ACTION = "bp.headsup.receivers.alarm.patient";
#Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Inside OnReceive Patient");
Timber.i("Inside OnReceive Patient");
if (intent == null || intent.getAction() == null) {
return;
}
String action = intent.getAction();
if (PATIENT_START_ALARM_ACTION.equalsIgnoreCase(action)) {
onStartCheckForConnectionRequest(context);
}
}
/**
* If is connected to network starts services
*/
private void onStartCheckForConnectionRequest(Context context) {
NetworkUtils networkUtils = new NetworkUtils(context);
if (networkUtils.isNetworkConnected()) {
Intent checkForConnRequestIntent = new Intent(context, PatientCheckForConnectionRequestService.class);
context.startService(checkForConnRequestIntent);
Timber.i("Starts Service From PatientALARMMANAGER");
}
}
}
And I have declared in Manifest :
<!-- Receivers -->
<receiver
android:name="bp.headsup.receivers.PatientAlarmReceiver" />
Also if I run : adb shell dumpsys alarm
I can see :
ELAPSED_WAKEUP #0: Alarm{42d804e8 type 2 bp.headsup.mock}
operation=PendingIntent{42d0c230: PendingIntentRecord{42d0f000 bp.headsup.mock broadcastIntent}}
Mock in the above response is the sourceSet I'm using - dont know if it has anything to do with this I just mention it.
The problem is I never read in logcat the messages I have in onReceive on my Receiver class, and obviously no service starts. Anyone can help with that ? I'm using a device which runs with kitKat 4.4 (api 19) but I have tried it with an emulator too and the result was the same.
You're setting an ELAPSED_REALTIME alarm, which is based on the time since the last boot. However, you're passing it a starting time based on the "wall clock", so your alarm is actually set quite far in the future.
You can either change the alarm to an RTC type, or get the starting time from SystemClock.elapsedRealtime(). Given your described behavior, keeping the elapsed type and correcting the starting time seems appropriate.