So, I seem to have fallen down a rabbit hole trying to figure out the best way to notify a user of an alarm going off.
Basically, I want some kind of notification/dialog to come up at a certain time, and it should come up no matter what the user is doing, and block further use until acted upon (dismissed or otherwise).
Right now, I have an AlarmManager, and the BroadcastReceiver that is registered with it starts a new service.
Every time I thought I was heading in the right direction, I hit a problem where someone online had a similar issue, and was told "don't do it that way." (Having a service create/show an AlertDialog, for instance.)
I was hoping someone could give me a brief list of what their recommendation would be; I don't need code (at least I shouldn't), just some high level abstraction.
Go with Notification, which plays a sound perhaps, that would pull your user's attention to your notification, just like the default alarm does.
And make the notification an ongoing one. Which can't be removed by the user, until and unless some action is performed to change the state of the notification.
Android: How to create an "Ongoing" notification?
Dialogs for this situation would be annoying for me. The docs also suggest not to use them in these scenarios.
Have a look at this Sample Open Source Project
I did it in this way and it work fine for me.
Create a class and Call it something like ScheduledService it extends IntentService, in this class you'll do what you want to do when alarm goes off.
public class ScheduledService extends IntentService {
public ScheduledService() {
super("My service");
}
#Override
protected void onHandleIntent(Intent intent) {
//Do something, fire a notification or whatever you want to do here
Log.d("debug", "Ring Rind !");
}
}
then in you activity to start the alarm use the following:
AlarmManager mgr = (AlarmManager) YourActivity.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(YourActivity, ScheduledService.class);
PendingIntent pi = PendingIntent.getService(YourActivity, 0, i, 0);
mgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + PERIOD, pi);
Which PERIOD is after how much milliseconds you want the alarm to goes off.
To cancel stop the timer and cancel the alarm use:
if (mgr != null)
mgr.cancel(pi);
Finally for all this to work you need to register your ScheduledService class as a Service.
In your manifest add this tou your application:
<application
... />
...
<service android:name=".ScheduledService" >
</service>
</application>
This way the Android OS will take care of firing the alarm when it's time. even if other application is running or even if your app process is terminated.
Hope this help.
Regards.
Just a crazy idea: Create an activity and set it's theme to be fullscreen with no title bar and a button to stop the alarm maybe, instead of doing a notification just make an intent that starts that activity "maybe you will need This" to work even when phone is locked and play some annoying sounds, "This" may help, when the activity starts. you can also override the onBackPressed() to do nothing.
Related
I have an Android Service (implementation of Servcie interface) which is running on a separate process compared to my real app. Unfortunately when I leave my real App (in which I clicked the Button to start my Service) and swipe it out from Task Manager, my Service gets killed as well.
I know there are a lot of questions like this here, but somehow none are targeting the Problem in my concrete constellation or they are vaguely answered.
So in my manifest I have something like:
<application ...>
<activity .../>
<service Android:name="MyService"
Android:label="MyLabel"
Android:export="false"
Android:process=":MyRemoteProcessName" />
</application>
I first have played around with an IntentService, but also switched to an own implementation of the Service Interface (eliminating the IntentService to be the point of failure) which looks something like:
public class MyService extends Service {
private ScheduledExecutorService scheduledWorker = Executors.newSingleThreadScheduledExecutor();
#Override
public void onStart() {
// Init components
startForeground(this, MyNotification);
}
#Override
public int onStartCommand(Intent i, int startId) {
// Execute Work on Threadpool here
scheduledWorker.execute(new ScheduledStopRequest(this, startId), 5, TimeUnit.Minutes);
return START_REDILIVER_INTENT;
}
// Overwritten onDestroy-Method
#Override
public void onLowMemory() {
Log.e(LOG_TAG, "On Low Memory called!");
}
#Override
public IBind onBind() {
// Dont't want to let anyone bind here
return null;
}
// Simply tries to stop the service after e.g. 5 Minutes after a call
private static class MyRunnable implements Runnable {
// Constructor with params used in run method..
#Override
public void run() {
mReferenceToMyService.stopSelfResult(startId);
}
}
}
I'm starting my Service in an onClick-Listener on a special button, with an explicit Intent, which kinda looks like the following:
#Override
public void onClick(View v) {
Intent i = new Intent(this, MyService.class);
startService(i);
}
My Intention is to keep the Service running when the user leaves the app, so that the Service can finish downloading and storing some important data. When the user comes back to my app again, he can view the data (That's why I'm executing it in a separate process). So is this possible?
My assumption right now is, that Android somehow notices my Service is just being used by my App (due to missing IntentFilters in Manifest or explicit call rather than by filter?!) and thus kills it immediately when my App is closed (even when running as ForegroundService as you can see above).
Does it seem possible to you and might some changes in the call of the service fix this problem or am I getting the concept of a service wrong?
(One last note: onLowMemory-Method doesn't get called -> No log entries.)
So, according to your hints (and so new keywords for me to look for) and after some additional research by myself, I think I have solved my problem. During my research I have found an very interisting blog post on this topic, maybe also for you, which is why I would like to share it with you: http://workshop.alea.net/post/2016/06/android-service-kill/ .
After verifying and going through the steps in this article everything seems to work fine (so startForeground seems to solve the problem). I want to point out here, that I have only tested it, with my service instance still running in separate process, so manifest entries as is above.
The actual thing which really confused me at the beginning was my android studio debug session being killed everytime, just after swiping out my app from recent apps (menu). This made me think my service being killed by the system as well. But according to the article (I have added some logs to the callback methods provided) when
Opening my app
starting service
swiping out app
starting app again and finally
calling service again,
I only received callbacks to the methods as if my service would still be running. Having an explicit look at DDMS (tool) also prooved my 2nd process, and thus my service, being still alive. Having verified this, I then cleared all my app data and repeated the steps above (excluding step no. 5). Having had a look in the database afterwards, prooved the data having been downloaded by the service.
For the curious of you:
The process of swiping out my app from recent apps (and thus having the onTaskRemoved callback method being called) lead to another problem. It somehow increases the startId parameter of onStartCommand by 1 so that my DelayedStopRequest malfunctiones and doesn't stop my service anymore.
This means: Repeating above steps 1 - 3 makes me receive startId = 1 in onStartCommand. By calling stopSelfResult(1) later on (which was the latest startId) it returnes false and the service keeps running. Continuing to follow step 4 + 5 then, makes onStartCommand being called with startId = 3 (but should actually be 2! which is skipped somehow). Calling stopSelfResult(3) with parameter 3 later on is then going to stop the service again (also visible in screenshots).
I hope my answer is correct so far (, understandable) and also helpful for you. Thank you for all of your answers which provided beneficial input and also pointed me to the solution. The android version I have been working with is:
4.1.2 - Jelly Bean | API Level : 16
I also added screenshots of the log entries from DDMS (imgur is rejecting my uploads so you'll temporarily have a link to my dropbox):
screenshots from logs from DDMS
Unfortunately running service in a separate process does not help you. I think there is no way you can prevent your service from being closed if user removes its task. You can however restart your service overriding onTaskRemoved. See this answer.
If you want to run this service class indefinitely after closing the app also.. you should use Alaram Manager class ..
public void scheduleAlarm() {
// Construct an intent that will execute the AlarmReceiver
Intent intent = new Intent(this, LocationListnerServiec.class);
// Create a PendingIntent to be triggered when the alarm goes off
final PendingIntent pIntent = PendingIntent.getBroadcast(this, MyAlarmReceiver.REQUEST_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Setup periodic alarm every 5 seconds
long firstMillis = System.currentTimeMillis(); // alarm is set right away
AlarmManager alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
// First parameter is the type: ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC_WAKEUP
// Interval can be INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_DAY
alarm.setRepeating(AlarmManager.RTC_WAKEUP, firstMillis,
60000, pIntent);
}
Use this method for keep checking the service class is on or off.. By using this method Your service class will keep working after destroying of you application.
i'm creating an alarm application, and this is the method to run the alarm :
public void startAlarm(int minuteToStart)
{
Toast.makeText(context, "Alarm Start in " + formatTime(minuteToStart), Toast.LENGTH_SHORT).show();
Calendar cal = Calendar.getInstance();
cal.add(Calendar.MINUTE, minuteToStart);
Intent intent = new Intent(context, AlarmActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, idPendingIntent, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);
}
And it run this activity after given specific time:
public class AlarmActivity extends Activity {
......
}
It works, but i see people are using BroadcastReceiver, am i doing it wrong? should i use BroadcastReceiver too? I've been searching about BroadcastReceiver but i don't get what difference it will make with my application.
Thanks.
In the general case, A--C's answer would be correct.
However, you are using RTC_WAKEUP as the alarm type. The only guarantee that we have with _WAKEUP alarms is if we use a BroadcastReceiver, then Android will ensure that the device will stay awake long enough for us to execute onReceive(). Any other type of PendingIntent -- activity or service -- has no guarantee, and it is very possible for the device to fall back asleep before the startActivity() or startService() actually occurs.
You can use AlarmManager with whatever PendingIntent is capable of (Activity, service, Receiver), though, it is usually used with Receivers - taks executing in the future usually are small and don't need an Activity to run in since the user doesn't need something popping up.
A Receiver isn't an Activity, so it does not have a UI and it has a processing time limit of about 10 seconds, so make sure to be quick. If you require a UI to be shown at a specific time, stick with an Activity, but usually this isn't the case unless it's something like an Alarm Clock app that the user has to see). If you have something like a small behind the scenes operation, go for a Receiver. The Receiver's onReceive() gets a Context passed to it so it can do anything a Context can.
Just keep in mind you will have to change the PendingIntent.getActivity() call to whatever else you decide to use if it's not going to be an Activity.
So it all depends on what you want to do.
You don't have to use a BroadcastReceiver. It's just generally frowned upon (in most cases) to steal focus and launch an Activity from the background without user interaction. There are certainly valid use cases though. If you intend to launch an Activity immediately anyway, doing that directly instead of via BroadcastReceiver is perfectly valid.
I'm developing a location based alarm which is an Android application.
All the coding part has been done.
But the alarm alert dialog is not displaying when the application is closed.
Please help me, I'm trying to pop up the alarm even when user is using another application or is in the home screen.
The code of my alarm at the moment is shown below:
final MediaPlayer mp = MediaPlayer.create(LocAlarmProject.this, R.raw.airtel);
mp.start();
// LocAlarmProject.this below is what's causing the problem:
final AlertDialog.Builder builder=new AlertDialog.Builder(LocAlarmProject.this);
builder.setTitle(disp_title);
builder.setMessage(disp_desc);
builder.setIcon(R.drawable.alarm);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mp.stop();
}
});
builder.show();
I am pretty sure you are looking for Service.
Copied and Paste:
A Service is an application component representing either an
application's desire to perform a longer-running operation while not
interacting with the user or to supply functionality for other
applications to use. Each service class must have a corresponding
declaration in its package's AndroidManifest.xml. Services
can be started with Context.startService() and Context.bindService().
Use Notification service to keep track of the status.....visit......http://developer.android.com/guide/topics/ui/notifiers/notifications.html
The proper way to do this requires a few steps. Sorry, it's not trivial if you haven't done it before:
First, you're going to use Android's built-in AlarmManager to schedule the time you want your app to be activated. You schedule a new alarm with the time to wake up and a PendingIntent.
Second, create your PendingIntent that's used to activate your app for the time to show the alarm.
Third, you can handle this Intent several ways, the most common is to create a class that extends BroadcastReceiver to receive the Intent. This class is notified when it's time to show the alarm. The Intent that you created and put inside your PendingIntent is passed to its onReceive() method. You pass this information to your app to display the alert dialog. --- You can alternatively just register your main app to receive the Intent instead, then override the onNewIntent(Intent) method in your Activity.
Four, register your BroadcastReceiver in your AndroidManifest.xml file. This is also the place to register this receiver to listen for the Intent you created. You do this using the <intent-filter> tag.
Five, (optional) if you want your alarm to display even if the phone is asleep, and you want to make sure it doesn't go back to sleep before the user acknowledges the alarm, you'll need to obtain a WAKE_LOCK to do so.
The reason this is preferable to using a Service is it doesn't consume resources just to wait for the alarm, and also it still works if Android decides to kill your app free up memory. Good luck and happy coding!
I have a widget that, when pressed, sends a broadcast that starts a CountDownTimer that updates my widget, and at the end plays an alarm. This works beautifully in the emulator.
On my phone, however, it's a different story. My phone is so resource constrained that my process is killed regularly, which, of course, means that the CountDownTimer no longer updates my widget.
It seems to be that the only way to reliably do stuff in the future is to use the AlarmManager, as this sets a system-level alarm. However, the documentation states, and I agree, that you're not supposed to use it for ticks. However, since anything else you're likely to use has the possibility of being shut down arbitrarily, they're not really giving us much choice.
My question is: is there a way to ensure that a CountDownTimer keeps ticking and finally calls onFinish(), or do I have to simply drop it and switch to AlarmManager, and "misuse" it? Any other options of guaranteeing that the thing ticks and finishes are also welcome.
I should add that I can't rely on the OS calling onUpdate(), both because it will do so no faster than every 30 minutes, and also because most of the time the widget just does nothing. It's only when it is clicked that it ticks down every second for a handful of minutes.
I think this is a solution.. Create a dummy service..
public class DummyService extends Service{
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
and start it from your activity using countdowntimer like this.. in oncreate of the activity
Intent intent = new Intent(this, DummyService.class);
startService(intent);
and also dont forget to declare this service in your manifest like this..
<service android:name=".DummyService" >
</service>
hope this helps..
What I ended up doing was moving from a BroadcastReceiver to a Service.
Not just using a dummy service, but having a real service embody what the BroadcastReceiver did previously.
As this won't guarantee that my timers won't get killed, if requested, android will automatically try to restart your service after it's been killed. In this event, I plan to have some code that will restore the running state from disk and continue.
To add to the accepted answer: from what I remember, the BroadcastReceiver only lives as long as it takes to process the broadcast.
So launching a timer or something from within the BroadcastReceiver will not work (as stated).
This is why it is recommended to use the BroadcastReceiver to launch a Service that will do the timing. Sure, the service can still be killed, but not as early as the BroadcastReceiver.
Im trying to make an scheduled activity go off every hour or so, all working in the background.
Right now i have a BroadcastReceiver that picks up when the device is booted.
The BroadcastReceiver creates a PendingIntent to an activity (Called AlarmController) that creates has all necessary methods that i need for making the scheduled activity to go off.
How ever, this doesnt seem to work.
This is how my BroadcastReciever class onReceive{} looks like and is indentical to my main activity onCreate{}(Only for testing)
Intent intent = new Intent(serviceactivirt.this, AlarmController.class);
PendingIntent sender = PendingIntent.getActivity(serviceactivirt.this, 0, intent, 0);
try {
sender.send();
} catch (CanceledException e) {
Toast.makeText(getApplicationContext(), "FEJLSAN", Toast.LENGTH_LONG).show();
}
This actually works, except that my app crashes at launch, but the scheduled activity is working...
Any ideas? Is this "The way to do it" or is there a more recommended way?
Cheers!
Solution:
Instead of having a BroadcastReciever calling an Activity, i made the BroadcastReciever starting a Service. And changed my Activity to a Service, programmaticly and in manifest.
Works great!
Im trying to make an scheduled activity go off every hour or so, all working in the background.
Please allow users to configure other options, such as using a Notification, rather than being interrupted by an activity taking over the foreground.
Right now i have a BroadcastReceiver that picks up when the device is booted.
You would only need that to set up an AlarmManager schedule for your hourly events. Your PendingIntent for the AlarmManager could be one you obtain via getActivity().
How ever, this doesnt seem to work.
If you want to start an activity, call startActivity(). Do not create a PendingIntent, then immediately send() the PendingIntent.
Also, get rid of getApplicationContext() and simply use this.
except that my app crashes at launch
Use adb logcat, DDMS, or the DDMS perspective in Eclipse to examine LogCat and look at the stack trace associated with your crash.