I am currently starting a service using a Broadcast Receiver which fires 60 seconds after the device boots up. This broadcast receiver triggers an alarm so that my service runs every 60 seconds. Code for this is as follows:
public class ScheduleReceiver extends BroadcastReceiver {
// Restart service every 60 seconds
private static final long REPEAT_TIME = 1000 * 60;
#Override
public void onReceive(Context context, Intent intent) {
AlarmManager service = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, StartServiceReceiver.class);
PendingIntent pending = PendingIntent.getBroadcast(context, 0, i,PendingIntent.FLAG_CANCEL_CURRENT);
Calendar cal = Calendar.getInstance();
// Start 60 seconds after boot completed
cal.add(Calendar.SECOND, 60);
// Fetch every 60 seconds
// InexactRepeating allows Android to optimize the energy consumption
service.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), REPEAT_TIME, pending);
}
}
The above starts the service as follows:
public class StartServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, PhotoService.class);
context.startService(service);
}
}
I would like to somehow modify this so that my service runs every 60 seconds when the phone screen is on and every 20 minutes when off.
What is the best way to do this ? Can I modify the alarm dynamically when the screen is switched off/on ?
Thanks for any help,
Regards,
Fido
Okay I have found out how to do this so I thought I would put this here in case anyone else is ever curious. (More elegant solutions are always welcome):
First thing is that in my service I added the following to the OnCreate:
#Override
public void onCreate() {
super.onCreate();
// REGISTER RECEIVER THAT HANDLES SCREEN ON AND SCREEN OFF LOGIC
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver mReceiver = new ScheduleReceiver();
registerReceiver(mReceiver, filter);
}
This is done so that my broadcast receiver can handle those events. Apparently you cannot register these via intent-filters in the xml.
This means that when the service is created it ensures that these additional system actions can be handled.
I then modified my broadcast receiver to be:
public class ScheduleReceiver extends BroadcastReceiver {
private static final int ALARM_ID = 909;
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
AlarmManager service = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, StartServiceReceiver.class);
PendingIntent pending = PendingIntent.getBroadcast(context, ALARM_ID, i,PendingIntent.FLAG_CANCEL_CURRENT);
service.cancel(pending);
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 60); //Delay startup by one minute - This is useful to prevent over utilization of resources at boot
// Start alarm. Set a long repeat time if the screen is off and a short repeat time if the screen is on
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON) || intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){
service.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 60000, pending);
}
else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)){
service.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 600000, pending);
}
}
}
From the above I cancel any current alarms with the same pending intent and then I set a new alarm whenever the screen if switched off/on.
This seems to be working for me. As always any improvements are very welcome.
Related
I have some existing code that spawns a service intent which does a bunch of stuff in the background. This code does work...
Intent serviceIntent = new Intent(context, APMService.class);
serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startService(serviceIntent);
My question is: how to change this to use the AlarmManager.setInexactRepeating(...) methods?
I have changed the above code to this:
Intent serviceIntent = new Intent(context, APMService.class);
serviceIntent.putExtra("STARTED_BY", starter);
serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//Set up recurring alarm that restarts our service if
// it crashes or if it gets killed by the Android OS
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pi = PendingIntent.getService(context, 0, serviceIntent, 0);
//am.cancel(pi);
am.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP //wake up the phone if it's asleep
, cal.getTimeInMillis()
, 10000
, pi);
And I have added these permissions to AndroidManifest.xml...
<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
<uses-permission android:name="com.android.alarm.permission.WAKE_LOCK"/>
My understanding is that this is supposed to start the service immediately and then try to restart it again every 10 seconds. But this code isn't working properly.
Using this new code, the service never starts at all and I cannot see why not. To complicate matters the debugger never seems to attach to the app in time to see what's going on.
Can anyone see what I'm doing wrong?
Put AlarmManager code under onDestroy() function of service to schedule start of service as below:
#Override
public void onDestroy() {
/**
* Flag to restart service if killed.
* This flag specify the time which is ued by
* alarm manager to fire action.
*/
final int TIME_TO_INVOKE = 5 * 1000; // try to re-start service in 5 seconds.
// get alarm manager
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AutoStartServiceReceiver.class);
PendingIntent pendingIntent = PendingIntent
.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
// set repeating alarm.
alarms.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() +
TIME_TO_INVOKE, TIME_TO_INVOKE, pendingIntent);
}
And handle starting of your service in AutoStartServiceReceiver as below:
public class AutoStartServiceReceiver extends BroadcastReceiver {
private static final String TAG = AutoStartServiceReceiver.class.getSimpleName();
#Override
public void onReceive(Context context, Intent intent) {
// check broadcast action whether action was
// boot completed or it was alarm action.
if (intent.getAction().equals(AppConstants.ACTION_ALARM_INTENT)) {
context.startActivity(new Intent(context, YourActivity.class));
// handle service restart event
LockerServiceHelper.handleServiceRestart(context);
}
}
}
Kindly note that, your service will not restart if you stop it manually from settings-apps-running apps-your app.
Your service is not starting because of AlarmManager.ELAPSED_REALTIME_WAKEUP, while it should be using AlarmManager.RTC_WAKEUP
If you want to run every 10s keep in mind that above API 21 alarm intervals below 60s are rounded up to 60s.
Also, consider using WakefulIntentService
https://github.com/commonsguy/cwac-wakeful
I'm making an android app to be used to help recover a lost phone. The app uses a Service that is constantly running and when a text message (SMS) is received by the phone, a BroadcastReceiver's (already registered in the service) onReceive() event is triggered. The functionality that I want is that the BroadcastReceiver be able to make the phone produce a loud noise even if the phone was on silent. The most logical way I can think of doing this is to use and alarm clock set one minute from the current time.
This is the code I'm currently using inside of the broadcast receiver:
Calendar c = Calendar.getInstance();
Intent alarmIntent = new Intent(AlarmClock.ACTION_SET_ALARM);
alarmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
alarmIntent.putExtra(AlarmClock.EXTRA_MESSAGE, "Polo!");
alarmIntent.putExtra(AlarmClock.EXTRA_HOUR, c.get(Calendar.HOUR_OF_DAY));
alarmIntent.putExtra(AlarmClock.EXTRA_MINUTES, c.get(Calendar.MINUTE) + 1);
context.startActivity(alarmIntent);
Fortunately, this code works, but it only ever works once. But that I mean, the first time this code is run, the phone's flow is interrupted and the clock app is opened with a new alarm set. When you attempt to do this again, the clock app is opened, but no alarm is set. I have to restart my phone to get this to work again.
From what I can tell, the issue has to do with the clock app and I need to restart the clock app to get my service to work. This is a really sketchy solution, but I don't know what else to do.
Any recommendations?
The trick is to develop your own Alarm screen and re-set the alarm while onCreate.
Like that, your app will continuously ring up.
for that :
initialise a WakefulBroadcastReceiver
manifest.xml
<receiver android:name="com.mycompany.myapp.AlarmManagerBroadcastReceiver"></receiver>
AlarmManagerBroadcastReceiver.java
public class AlarmManagerBroadcastReceiver extends WakefulBroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
Calendar c = Calendar.getInstance();
Intent alarmIntent = new Intent(context, MyAlarm.class);
alarmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
alarmIntent.putExtra(AlarmClock.EXTRA_MESSAGE, "Polo!");
alarmIntent.putExtra(AlarmClock.EXTRA_HOUR, c.get(Calendar.HOUR_OF_DAY));
alarmIntent.putExtra(AlarmClock.EXTRA_MINUTES, c.get(Calendar.MINUTE) + 1);
context.startActivity(alarmIntent);
}
}
MyAlarm.java :
public class MyAlarm extends Activity{
#Override
public void onCreate(Bundle savedInstanceState){
// doing UI stuff
AlarmUtil.setAlarm(this);
}
}
AlarmUtil.java
public class AlarmUtil {
#SuppressLint("NewApi")
public static void setAlarm(Context context){
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
int requestCode = 1234;
Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
am.setExact(AlarmManager.RTC_WAKEUP, 60 * 1000, pi);
}
else{
am.set(AlarmManager.RTC_WAKEUP, 60 * 1000, pi);
}
}
}
I've seen several examples on how to make some event to be repeated even when the app isnt running, but still I'm not sure if I got it.
With AlarmManager you can make your app to wake up to do something in some fixed interval without it consuming system resources between the periods, right?
But can it be to show up a toast over your current activity instead of having an Activity with a layout for it?
AlarmReceiver class:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// this is where to start activity or service to launch toast message
}
}
In activity or boot receiver:
private static final int PERIOD = 60000; //or whatever you need for repeating alarm
AlarmManager mgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent alIntent = new Intent(context, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, alIntent, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60000, PERIOD, pi);
In AndroidManifest, add:
<receiver android:name=".AlarmReceiver"></receiver>
Have a BroadcastReceiver which I want to create a PendingIntent inside the onReceive method
public class MyPushNotificationReceiver extends BroadcastReceiver {
..
public void onReceive(Context context, Intent intent) {
// How to start a PendingIntent here?
}
Most of the doc from Google is not starting within the onReceive method, so any sample cod e I can use?
Thanks.
a sample code which uses a pending intent in broadcast reciver pls check
public class MyScheduleReceiver extends BroadcastReceiver {
// Restart service every 30 minute
private static final long REPEAT_TIME = 1000 * 30 ;
#Override
public void onReceive(Context context, Intent intent) {
AlarmManager service = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, MyStartServiceReceiver.class);
PendingIntent pending = PendingIntent.getBroadcast(context, 0, i,
PendingIntent.FLAG_CANCEL_CURRENT);
Calendar cal = Calendar.getInstance();
// Start 30 seconds after boot completed
cal.add(Calendar.SECOND, 30);
//
// Fetch every 30 seconds
// InexactRepeating allows Android to optimize the energy consumption
service.setInexactRepeating(AlarmManager.RTC_WAKEUP,
cal.getTimeInMillis(), REPEAT_TIME, pending);
// service.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),
// REPEAT_TIME, pending);
}
}
this code is from vogella ...
I have an update service that starts at boot. The thing is I want it to make a check and then wait for a period of time and restart itself. I did this with alarm clock when my service was tied to an application but now it is independent and I only use a broadcast receiver to start it at boot. The problem is that now for some reason I can't integrate my alarm clock with it.
I only have my UpdateService class and my broadcastreceiver class.My code so far in the broadcastreceiver is this, but I want to put the alarm clock here to say schedule the service to start every 30 seconds. I really need this.
#Override
public void onReceive(Context context, Intent intent) {
Intent startServiceIntent = new Intent(context, UpdateService.class);
context.startService(startServiceIntent);
}
Any suggestions are welcome. Thanks in advance.
I found the answer to my problem:
private boolean service_started=false;
private PendingIntent mAlarmSender;
#Override
public void onReceive(Context context, Intent intent) {
if(!service_started){
// Create an IntentSender that will launch our service, to be scheduled
// with the alarm manager.
mAlarmSender = PendingIntent.getService(context,
0, new Intent(context, UpdateService.class), 0);
//We want the alarm to go off 30 secs from now.
long firstTime = SystemClock.elapsedRealtime();
// Schedule the alarm!
AlarmManager am = (AlarmManager)context.getSystemService(context.ALARM_SERVICE);
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
firstTime,30*1000, mAlarmSender);
service_started=true;
}
}
Eventually,my problem was that I didn't get the context right as follows:
(AlarmManager)getSystemService(ALARM_SERVICE);
changed to
(AlarmManager)context.getSystemService(context.ALARM_SERVICE);