I created an app to send message using alarm manager but if i put an alarm for a long duration the app is killed by android automatically, so i need to prevent the app from getting killed.Please tell me how can I do it.
Calendar cal = Calendar.getInstance();
int currentApiVersion = android.os.Build.VERSION.SDK_INT;
if (currentApiVersion > android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
cal.set(Calendar.MINUTE, time_picker.getMinute());
cal.set(Calendar.HOUR_OF_DAY, time_picker.getHour());
} else {
//Setting the date and time from the time picker
cal.set(Calendar.MINUTE, time_picker.getCurrentMinute());
cal.set(Calendar.HOUR_OF_DAY, time_picker.getCurrentHour());
}
//System clock time
Calendar c = Calendar.getInstance();
Long a ;//=(long) (Calendar.getInstance().get(Calendar.SECOND) * 1000);
if(cal.get(Calendar.HOUR_OF_DAY) < c.get(Calendar.HOUR_OF_DAY))
h = (cal.get(Calendar.HOUR_OF_DAY) + 24 - c.get(Calendar.HOUR_OF_DAY)) * 60;
else
h = (cal.get(Calendar.HOUR_OF_DAY) - c.get(Calendar.HOUR_OF_DAY * 60;
m = (cal.get(Calendar.MINUTE) - c.get(Calendar.MINUTE));
a = (m + h) * 60;
myIntent = new Intent(this, MyReceiver.class);
myIntent.putExtra("pos", array.select);
//Pending Intent for sending the intent afterwards
pendingIntent[array.select] = PendingIntent.getBroadcast(this.getApplicationContext(), array.select, myIntent, 0);
alarmManager[array.select] = (AlarmManager) (this.getSystemService(Context.ALARM_SERVICE));
alarmManager[array.select].set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + a * 1000, pendingIntent[array.select]);
pendingarray.add(pendingIntent[array.select]);
sms_list.Phone[array.select] = Phone;
Intent back = new Intent(this, sms_list.class);
back.putExtra("PHONE", Phone);
back.putExtra("Flag",2);
back.putExtra("MSG", Message);
back.putExtra("HOUR", (int) cal.get(Calendar.HOUR_OF_DAY));
back.putExtra("MIN", (int) cal.get(Calendar.MINUTE));
back.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(back);
If the answer is wake lock can you please tell me where to use it.
You can use a service to do it, this will also work after the device is rebooted. You also have to make the service foreground to prevent the system from killing it. It can be done by adding an ongoing notification. See the service code below.
In your Manifest add the following
<receiver
android:name=".Autostart"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<service
android:name=".StarterService"
android:enabled="true"
android:exported="true" />
Then create a new class as follows:
public class Autostart extends BroadcastReceiver {
/**
* Listens for Android's BOOT_COMPLETED broadcast and then executes
* the onReceive() method.
*/
#Override
public void onReceive(Context context, Intent arg1) {
Log.d("Autostart", "BOOT_COMPLETED broadcast received. Executing starter service.");
Intent intent = new Intent(context, StarterService.class);
context.startService(intent);
}
}
And finally your service as follows:
public class StarterService extends Service {
private static final String TAG = "MyService";
/**
* starts the AlarmManager.
*/
#Override
public void onCreate() {
super.onCreate();
//TODO: Start ongoing notification here to make service foreground
}
#Override
public void onStart(Intent intent, int startid) {
//TODO: Put your AlarmManager code here
//TODO: you also need to add some logic to check if some previous work is pending in case of a device reboot
Log.d(TAG, "onStart");
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
//TODO: cancel the notification
Log.d(TAG, "onDestroy");
}
}
Now all you need to do is call the service whenever you need to send the message.
PS: I know an answer is accepted but hope this helps you or someone else.
An alarm should be triggered in a Broadcast Receiver.
If it performs long-lived operations, you should then use threads or Services. Both of them can be launched from a receiver.
EDIT
As a short example, I use this method in a button's onClickListener in the activity :
scheduleAlarm(name);
Method :
public void scheduleAlarm(String client)
{
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
String delay = sharedPref.getString(SettingsActivity.PREF_DELIVERY_DELAY, "48");
// time at which alarm will be scheduled here alarm is scheduled at 1 day from current time,
// we fetch the current time in milliseconds and added 1 day time
// i.e. 24*60*60*1000= 86,400,000 milliseconds in a day
Long time = new GregorianCalendar().getTimeInMillis()+ Integer.parseInt(delay) * 1000; //todo change seconds to hours
// create an Intent and set the class which will execute when Alarm triggers, here we have
// given AlarmReciever in the Intent, the onRecieve() method of this class will execute when
// alarm triggers and
//we will write the code to send SMS inside onRecieve() method pf Alarmreciever class
Intent intentAlarm = new Intent(this, AlarmReceiver.class);
intentAlarm.putExtra("CLIENT", client);
// create the object
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
//set the alarm for particular time
//todo string res
alarmManager.set(AlarmManager.RTC_WAKEUP,time, PendingIntent.getBroadcast(this,1, intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT));
Toast.makeText(this, "Alarm Scheduled in " + delay + " hours", Toast.LENGTH_LONG).show();
}
And finally, the AlarmReceiver.java
package com.patrickmiller.test2;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent)
{
Toast.makeText(context, "Alarm received", Toast.LENGTH_SHORT).show();
String client = intent.getStringExtra("CLIENT");
Notify(context, client);
}
public void Notify(Context context, String client) {
//todo expanded layout with options Fiche de contact | Rapport and cover image
//todo send name, address, phone, email and id through Intent to ContactClientActivity
//todo delete notification when generated
try {
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
//todo set notification icon, content title and content text as string resources
.setSmallIcon(R.drawable.warning)
.setContentTitle(client)
.setContentText("N'oubliez pas de générer le rapport du client");
Intent resultIntent = new Intent(context, ContactClientActivity.class);
//todo may need to expend instead of calling activity. Buttons will do.
// Because clicking the notification opens a new ("special") activity, there's
// no need to create an artificial back stack.
PendingIntent resultPendingIntent =
PendingIntent.getActivity(
context,
0,
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
// Sets an ID for the notification
int mNotificationId = 001;
// Gets an instance of the NotificationManager service
NotificationManager mNotifyMgr = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
// Builds the notification and issues it.
mNotifyMgr.notify(mNotificationId, mBuilder.build());
}
catch(Exception e) {
Toast.makeText(context, String.valueOf(e), Toast.LENGTH_LONG).show();
}
}
}
You don't have to care about the client's thing. Just the way I scheduled the alarm..
My operation is a short-lived one, which is sending a notification. If you plan a long-lived operation, you should start a service or a thread from the receiver (onReceive callback method).
ok, your app is finished because is running in the main thread, so you need to make this process in other thread that is not killed when the app is closed. check this documentation from the official page. if you decide start using asyncTask class check this reference
Related
I am trying to figure out how alarm timers work so I can trigger an event when a user selects the predefined times in the app. To start off I just want to show a toast so I can clearly see the app is working. But when I run the app and set the time for 10 seconds the class handling my Intent never seems to get called.
I am using Log.d in the Main and I can see it being logged correctly when the button is clicked. But the event does not fire off at the selected time.
This is the function that fires off when the button is clicked and the Log.d is displayed in the console.
public void scheduleAlarm()
{
Long time = System.currentTimeMillis() + 10000;
Log.d("logs", "This is running in the main act");
Intent intentAlarm = new Intent(this, affirmationSchedule.class);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, time, PendingIntent.getBroadcast(this, 1, intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT));
Toast.makeText(this, "Alarm Has Been Scheduled", Toast.LENGTH_LONG).show();
}
And this is the class which handles the code to run when the alarm time comes
public class affirmationSchedule extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("logs", "This function is running");
Toast.makeText(context, "this is a toast working.", Toast.LENGTH_LONG).show();
}
}
Log.d never displays. the toast in this class never displays.
This leads me to believe I am not creating my object correctly.
This is how I am registering receiver in the manifest.
<receiver
android:name="com.wuno.wunoaffirmations"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.wuno.BroadcastReceiver" />
</intent-filter>
</receiver>
Any ideas?
This might be relevant,
After I click the button and the original toast goes away. This pops up in console.
05-16 23:10:11.989 14242-14268/com.wuno.wunoaffirmations E/Surface: getSlotFromBufferLocked: unknown buffer: 0xb4015c60
But not in ten seconds. More like 5.The alarmManager is set for 10 seconds.
This how i used alarm manager within a project of mine. Basically i followed some code from a google's app code. so here it is. I hope this will help you.
How to use it? Well just Create instance of AlramReciver and then set it.
private AlarmReceiver alarmReceiver = new AlarmReceiver();
alramReceiver.setAlram();
This is helper class which set the alram receiver.
public class AlarmReceiver extends WakefulBroadcastReceiver {
private static AlarmManager alarmManager;
private static PendingIntent alarmIntent;
#Override
public void onReceive(Context context, Intent intent) {
/*
* If your receiver intent includes extras that need to be passed along to the
* service, use setComponent() to indicate that the service should handle the
* receiver's intent. For example:
*
* ComponentName comp = new ComponentName(context.getPackageName(),
* MyService.class.getName());
*
* // This intent passed in this call will include the wake lock extra as well as
* // the receiver intent contents.
* startWakefulService(context, (intent.setComponent(comp)));
*
* In this example, we simply create a new intent to deliver to the service.
* This intent holds an extra identifying the wake lock.
*/
Intent service= new Intent(context, AlarmService.class);
startWakefulService(context,service);
}
/**
*set the alram
* #param context
*/
public void setAlarm(Context context){
alarmManager=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent =PendingIntent.getBroadcast(context,0,intent,0);
alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), 1000 * 60, alarmIntent);
ComponentName receiver = new ComponentName(context, BootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
/**
* cancels the alram
* #param context
*/
public void cancelAlarm(Context context){
// If the alarm has been set, cancel it.
if (alarmManager!= null) {
alarmManager.cancel(alarmIntent);
}
// Disable {#code SampleBootReceiver} so that it doesn't automatically restart the
// alarm when the device is rebooted.
ComponentName receiver = new ComponentName(context, BootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
}
This is bootReceiver class used when your device goes off and switch on again
public class BootReceiver extends BroadcastReceiver {
AlarmReceiver alarmReceiver = new AlarmReceiver();
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED"))
{
alarmReceiver.setAlarm(context);
}
}
}
This is intent service class here you have to write your logic for your app.
public class AlarmService extends IntentService {
public AlarmService() {
super("AlarmService");
}
#Override
protected void onHandleIntent(Intent intent) {
//Write the logice here
AlarmReceiver.completeWakefulIntent(intent); // this tell if the related work is complete then system tracks for another alram.
}
Last you have to make changes in your manifest.
<service android:name="AlarmService" />
<receiver android:name="AlarmReceiver" />
<receiver android:name="BootReceiver" />
I hope this will help you atleast. P.s you don't have to post same question twice.
public void scheduleAlarm()
{
Calendar cal = Calendar.getInstance();
Log.d("logs", "This is running in the main act");
Intent intentAlarm = new Intent(this, affirmationSchedule.class);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, cal + 10000, PendingIntent.getBroadcast(this, 1, intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT));
Toast.makeText(this, "Alarm Has Been Scheduled", Toast.LENGTH_LONG).show();
}
Broadcast Receiver
public class affirmationSchedule extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("logs", "This function is running");
Toast.makeText(context, "this is a toast so this is working.", Toast.LENGTH_LONG).show();
}
}
In manifest
<receiver
android:name="com.wuno.affirmationSchedule"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.wuno.BroadcastReceiver" />
</intent-filter>
</receiver>
I've inherited a code base for an Android app and I'm facing a particularly though problem with local notifications.
The idea is to send a notification for each event which is scheduled in the future, considering also the reminder preference on how many minutes before the event the user wants to be notified.
Everything works just fine, except that after the notification is thrown for the first time, if the user opens the app before the event starts, the notification gets thrown another time. This happens every time the app is opened between (event start date - reminder) and event start date.
I've already gave a look at this and also this with no luck.
I've read that using a service may cause exactly this problem and some suggest to remove it but I think this is needed since the notification must be thrown also when the app is closed.
Currently the structure of the code is the following:
Edit - updated description of TabBarActivity
Inside TabBarActivity I have the method scheduleTravelNotification that schedules the AlarmManager.
This method is executed everytime there is a new event to be added on local database, or if an existing event have been updated.
The TabBarActivity runs this method inside the onCreate and onResume methods.
TabBarActivity is also the target of the notification - onclick event.
private static void scheduleTravelNotification(Context context, RouteItem routeItem) {
long currentTime = System.currentTimeMillis();
int alarmTimeBefore = routeItem.getAlarmTimeBefore();
long alarmTime = routeItem.getStartTime() - (alarmTimeBefore * 1000 * 60);
if(alarmTimeBefore < 0){
return;
}
if(alarmTime < currentTime){
return;
}
Intent actionOnClickIntent = new Intent(context, TravelNotificationReceiver.class);
PendingIntent travelServiceIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis(), actionOnClickIntent, PendingIntent.FLAG_ONE_SHOT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(alarmTime);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), travelServiceIntent);
Log.e("NEXT ALARM", "Time: " + String.valueOf(calendar.getTimeInMillis()));
}
This is TravelNotificationReceiver.java (should I use LocalBroadcastReceiver instead of BroadcastReceiver?)
public class TravelNotificationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.e("RECEIVER", "received TravelNotification request");
Intent notificationIntent = new Intent(context, TravelNotificationService.class);
context.startService(notificationIntent);
}
}
TravelNotificationService.java extends NotificationService.java setting as type = "Travel", flags = 0, title = "something" and text = "something else".
public abstract class NotificationService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
sendNotification();
return super.onStartCommand(intent, flags, startId);
}
public abstract String setNotificationType();
public abstract int setNotificationFlags();
public abstract String setNotificationTitle();
public abstract String setNotificationText();
/**
* Executes all the logic to init the service, prepare and send the notification
*/
private void sendNotification() {
int flags = setNotificationFlags();
String type = setNotificationType();
NotificationHelper.logger(type, "Received request");
// Setup notification manager, intent and pending intent
NotificationManager manager = (NotificationManager) this.getApplicationContext().getSystemService(this.getApplicationContext().NOTIFICATION_SERVICE);
Intent intentAction = new Intent(this.getApplicationContext(), TabBarActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this.getApplicationContext(), 0, intentAction, flags);
// Prepares notification
String title = setNotificationTitle();
String text = setNotificationText();
Notification notification = NotificationHelper.buildNotification(getApplicationContext(), title, text, pendingIntent);
// Effectively send the notification
manager.notify(101, notification);
NotificationHelper.logger(type, "Notified");
}
}
Edit - Here's the code for NotificationHelper.buildNotification
public static Notification buildNotification(Context context, String title, String text, PendingIntent pendingIntent) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setAutoCancel(true);
builder.setContentText(text);
builder.setContentTitle(title);
builder.setContentIntent(pendingIntent);
builder.setSmallIcon(R.mipmap.launcher);
builder.setCategory(Notification.CATEGORY_MESSAGE);
builder.setVisibility(Notification.VISIBILITY_PUBLIC);
return builder.build();
}
Thank you for the answers!
Edit I've seen also this but has no accepted answers, while this post suggest something that I think it's already managed with if(alarmTime < currentTime){ return; } in scheduleTravelNotification.
This may not be your exact problem, but at a glance, you're sending the notification in onStartCommand() which can itself be run many times during the lifetime of the service -- for example, if you issue the service start command "blindly" in an onCreate of an activity, it will happen every time the activity is (re)created.
You have a few options for handling this.
One is to create a boolean flag as a property of the service, default to false, and check it before sending the notification. If it's false, send the notification and set it to true, and if it's already true you do not send a notification.
Another is to check and see if the service is already running, and if it is, don't send the service start command in the first place. This can be tedious to do everywhere, and violates DRY, so if you take this route you may want to create a static method in your service class which checks to see if the service is running and then starts it if not, and call that instead of explicitly starting the service.
Similar to user3137702 answer you could simple have a static boolean of APPISINFORGROUND which is checked everytime the send notification method is hit, and managed from your application/activities code.
As User said it is likely that your onStartCommand method is being called at odd times due to the app / service lifecycle.
Alternatively check your receiver is not being called somewhere else from your code.
It may be your NotificationHelper class which is causing an issue. Please share the code for this class.
One thought may be that your notification is not set to be auto cancelled, check if you include the setAutoCancel() method in your Notification Builder.
Notification notification = new Notification.Builder(this).setAutoCancel(true).build();
I've found a way to make it work, I'm posting this since it seems to be a problem of many people using the approach suggested in this and this articles. After months of testing I can say I'm pretty satisfied with the solution I've found.
The key is to avoid usage of Services and rely on AlarmScheduler and Receivers.
1) Register the receiver in your manifest by adding this line:
<receiver android:name="<your path to>.AlarmReceiver" />
2) In your activity or logic at some point you want to schedule a notification related to an object
private void scheduleNotification(MyObject myObject) {
// Cal object to fix notification time
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(myObject.getTime());
// Build intent and extras: pass id in case you need extra details in notification text
// AlarmReceiver.class will receive the pending intent at specified time and handle in proper way
Intent intent = new Intent(this, AlarmReceiver.class);
intent.putExtra("OBJECT_ID", myObject.getId());
// Schedule alarm
// Get alarmManager system service
AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(getBaseContext().ALARM_SERVICE);
// Build pending intent (will trigger the alarm) passing the object id (must be int), and use PendingIntent.FLAG_UPDATE_CURRENT to replace existing intents with same id
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), myObject.getId(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Finally schedule the alarm
alarmManager.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);
}
3) Define AlarmReceiver
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Find object details by using objectId form intent extras (I use Realm but it can be your SQL db)
MyObject myObject = RealmManager.MyObjectDealer.getObjectById(intent.getStringExtra("OBJECT_ID"), context);
// Prepare notification title and text
String title = myObject.getSubject();
String text = myObject.getFullContent();
// Prepare notification intent
// HomeActivity is the class that will be opened when user clicks on notification
Intent intentAction = new Intent(context, HomeActivity.class);
// Same procedure for pendingNotification as in method of step2
PendingIntent pendingNotificationIntent = PendingIntent.getActivity(context, myObject.getId(), intentAction, PendingIntent.FLAG_UPDATE_CURRENT);
// Send notification (I have a static method in NotificationHelper)
NotificationHelper.createAndSendNotification(context, title, text, pendingNotificationIntent);
}
}
4) Define NotificationHelper
public class NotificationHelper {
public static void createAndSendNotification(Context context, String title, String text, PendingIntent pendingNotificationIntent) {
// Get notification system service
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
// Build notification defining each property like sound, icon and so on
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context);
notificationBuilder.setContentTitle(title);
notificationBuilder.setContentText(text);
notificationBuilder.setSmallIcon(R.drawable.ic_done);
notificationBuilder.setCategory(Notification.CATEGORY_MESSAGE);
notificationBuilder.setVisibility(Notification.VISIBILITY_PUBLIC);
notificationBuilder.setAutoCancel(true);
notificationBuilder.setContentIntent(pendingNotificationIntent);
notificationBuilder.setDefaults(Notification.DEFAULT_SOUND);
notificationManager.notify(1001, notificationBuilder.build());
}
}
At this point it should work and schedule / trigger notification at the right time, and when notification is opened it will appear only once starting the activity declared in notification pending intent.
There is still a problem, AlarmManager have a "volatile" storage on user device, so if user reboots or switch off the phone you will lose all intents that you previously scheduled.
But fortunately there is also a solution for that:
5) Add at top of your manifest this uses permission
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
6) Right below the line added at step 1 register the boot receiver
<receiver android:name="<your path to>.BootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
7) Define the BootReceiver
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Do something very similar to AlarmReceiver but this time (at least in my case) since you have no source of intents loop through collection of items to understand if you need to schedule an alarm or not
// The code is pretty similar to step 3 but repeated in a loop
}
}
At this point your app should be able to schedule / trigger notification and restores those reminders even if the phone is switched off or rebooted.
Hope this solution will help someone!
I'd like to make an notification which start to count time when user exited android application. If user do not executed application after 1hours, It notified me to execute and If user ignoring it, It executes saved SMS messages. I found some examples on timer, but I do not know how to find application exit time. Please give me some advice with full code. I am desperately need it...
TimerTask task = new TimerTask(){
public void run() {
try {
mainTime++;
int min = mainTime / 60;
int sec = mainTime % 60;
String strTime = String.format("%s : %s", min, sec);
} catch (Exception e) {
e.printStackTrace();
}
}
};
Timer mTimer = new Timer();
mTimer.schedule(task, 0, 60000);
Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.putExtra("Chack your app", smsBody);
sendIntent.putExtra("12345678", phonenumber);
sendIntent.setType("vnd.android-dir/mms-sms");
startActivity(sendIntent);
Okay so what you need to do is to store the system time locally (may be using SharedPreferences) when the application exits. You can register a BroadcastReceiver which will help you trigger some action when 1hr or a certain time has passed from the locally stored time when app exited.
If you want to know how to handle programmatically when and how to exit the app , please refer this answer.
You could also try to use the Android alarm system. Once the user exit your application, you could set up an Alarm. Something like:
YourActivityOrFragment.java
#Override
protected void onStop() {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(System.currentTimeMillis());
c.add(Calendar.HOUR,1);
scheduleAlarm(c.getTimeInMillis());
}
private void scheduleAlarm(long time) {
Intent yourIntent = new Intent("Some_ID");
PendingIntent pi = PendingIntent.getBroadcast(YourClass.this, ALARM_ID, yourIntent, PendingIntent.FLAG_CANCEL_CURRENT);
// Put some extras here, if you need so. Like:
// yourIntent.putExtra("field","value");
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP,time,pi);
}
Now, create a BroadcastReceiver to handle those alarms.
AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
private static final String LOG_TAG = AlarmReceiver.class.getSimpleName();
#Override
public void onReceive(Context context, Intent intent) {
Log.d(LOG_TAG, "Alarm fired!");
Intent it = new Intent(context, YourNotificationHandler.class);
// Get your Extras here. And do whatever you want, if you need.
// For what you said, there's no need to start an Activity, so let's handle that alarm as a service.
context.startService(it);
// But if for some reason you want to start an Activity, just do it like:
// context.startActivity(it);
}
}
On your AndroidManifest.xml declare your BroadcastReceiver.
<receiver android:name=".AlarmReceiver" >
<intent-filter>
<action android:name="Some_ID" />
<category android:name="android.intent.category.default" />
</intent-filter>
</receiver>
And last of all, create your service to handle your notifications, you could try something like an IntentService. On that file, you'll have a onHandleIntent(Intent intent) method. Get your Intent there, and it's Extras, and do whatever you want to do. Later, just call your Notifications. I've used a utility class on my projects to handle those, but feel free to choose how you'll do that.
Example:
public static void createService(Context context, CharSequence tickerMessage, CharSequence title,
CharSequence message, int icon, int id, Intent intent, long[] pattern, Boolean autoCancel) {
PendingIntent p = PendingIntent.getService(context, 0, intent, 0);
Notification n;
int apiLevel = Build.VERSION.SDK_INT;
if (apiLevel >= 11) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setContentTitle(title)
.setTicker(tickerMessage)
.setContentText(message)
.setSmallIcon(icon)
.setContentIntent(p)
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
if (pattern.length > 0) {
builder.setVibrate(pattern);
}
if (autoCancel != null) {
builder.setAutoCancel(autoCancel);
}
if (apiLevel >= 17) {
// Android 4.2+
n = builder.build();
}
else {
// Android 3.x
n = builder.getNotification();
}
}
else {
// Android 2.2+
n = new Notification(icon, tickerMessage, System.currentTimeMillis());
// Data
n.setLatestEventInfo(context, title, message, p);
}
NotificationManager nm = (NotificationManager)
context.getSystemService(Activity.NOTIFICATION_SERVICE);
nm.notify(id, n);
}
You can read more about alarms here.
More on Service here.
BroadcastReceiver here.
Notifications, here and here.
And this might be an interesting read about Notification as well.
What I want
I want a notification every day at the same time.
I already read some posts and tutorials/examples but it won't work correctly.
Version 1
The Error: Android process / service dies every ~3 minutes after re/starting
11-07 07:33:05.725 4611 6121 I ActivityManager: Process at.htl3r.appmosphere (pid 5238) has died.
11-07 07:33:05.725 4611 6121 W ActivityManager: Scheduling restart of crashed service at.htl3r.appmosphere/.notify.NotifyService in 14648ms
11-07 07:33:20.400 4611 4632 I ActivityManager: Start proc at.htl3r.appmosphere for service at.htl3r.appmosphere/.notify.NotifyService: pid=5463 uid=10096 gids={50096}
---
11-07 07:33:41.580 4611 4623 I ActivityManager: Process at.htl3r.appmosphere (pid 5463) has died.
11-07 07:33:41.580 4611 4623 W ActivityManager: Scheduling restart of crashed service at.htl3r.appmosphere/.notify.NotifyService in 73293ms
11-07 07:33:44.310 4611 5385 F ProcessStats: Starting service ServiceState{43760cf0 at.htl3r.appmosphere.notify.NotifyService pkg=at.htl3r.appmosphere proc=43760cf0} without owner
these are the two ways (with and without owner in last line)
This bug is only on my S3 so extrem, on my N7 (2013) is it a bit better
After every restart I get a notification. (just a thought: And if I delete it, the possibility is higher to make a crash.)
A bit annoying to receive a notification every 3 minutes ^-^
The Code
version 1 - with service
UPDATE 1
updated code like Larry Schiefer told
new full log
UPDATE 2
NotifyManager
see below for newest version
version from this update
NotifyReceiver
public class NotifyReceiver extends BroadcastReceiver {
private static final String TAG = "NotifyReceiver";
public static final int ID_NEWHINTAVAILABLE = 1;
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive");
SharedPreferences spref = PreferenceManager.getDefaultSharedPreferences(context);
NotificationManager mNM = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent i = new Intent(context.getApplicationContext(), MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, i, 0);
Notification.Builder mNotifyBuilder = new Notification.Builder(context);
mNotifyBuilder.setSmallIcon(R.drawable.ic_stat_name);
mNotifyBuilder.setContentTitle(context.getString(R.string.app_name));
mNotifyBuilder.setContentText(context.getString(R.string.notification_contenttext));
mNotifyBuilder.setContentIntent(pIntent);
mNotifyBuilder.setAutoCancel(true);
// has to have an icon - now the app icon
// auto cancel after click: in main use cancel(int id);
// mNotifyBuilder.addAction(R.drawable.ic_stat_name, getString(R.string.notification_action), pIntent);
// mNotifyBuilder.setTicker(getString(R.string.app_name));
// mNotifyBuilder.setTicker(getString(R.string.app_name)+" "+getString(R.string.notification_contenttext));
// mNotifyBuilder.setWhen(System.currentTimeMillis());
// mNotifyBuilder.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS);
// http://stackoverflow.com/questions/2724871/how-to-bring-up-list-of-available-notification-sounds-on-android
String sound = spref.getString(SettingsFragment.pref_notify_sound, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION).toString());
mNotifyBuilder.setSound(Uri.parse(sound));
if (spref.getBoolean(SettingsFragment.pref_notify_vibrate, true)) {
// mNotifyBuilder.setVibrate(new long[] { 0, 1000 });
mNotifyBuilder.setDefaults(Notification.DEFAULT_VIBRATE);
}
if (spref.getBoolean(SettingsFragment.pref_notify_light, true)) {
mNotifyBuilder.setLights(Color.GREEN, 3000, 3000);
}
Notification mNotify = mNotifyBuilder.build();
mNM.notify(ID_NEWHINTAVAILABLE, mNotify);
NotifyManager.startAlarm(context, true);
// wenn aktiviert: ausgeführt & neu gestartet
// bei Deaktiviertung: abgebrochen - demnach kein Neustart
}
}
Update 3
Autostart worked..
but now, it dies too
nothing changed in this code; only the code above
<receiver android:name="at.htl3r.appmosphere.notify.Autostart" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Autostart.java
public class Autostart extends BroadcastReceiver {
private static final String TAG = "autostart";
#Override
public void onReceive(Context context, Intent intent) {
if (NotifyManager.isNotificationEnabled(context)) {
NotifyManager.startAlarm(context);
Log.i(TAG, "started");
}
}
}
CatLog
s3 - full
n7
12-14 23:15:19.227 1452 1679 I ActivityManager: Start proc at.htl3r.appmosphere for broadcast at.htl3r.appmosphere/.notify.Autostart: pid=5837 uid=10391 gids={50391, 3003}
12-14 23:15:42.300 1452 4109 I ActivityManager: Killing 5837:at.htl3r.appmosphere/u0a391 (adj 15): empty #17
12-15 06:43:47.501 18799 18819 D JsonParser: at.htl3r.appmosphere: publishState=6
12-15 06:43:47.501 18799 18819 D JsonParser: Skipping app 0 with state != 1: package name=at.htl3r.appmosphere: state=6
Update 4
NotifyManager
public class NotifyManager {
private static final String TAG = "NotifyManager";
/**
* {#link #startAlarm(Context, boolean)}<br>
* default: restart: true
*
* #param context Context of activity
* #return alarm started: true<br>
* is running: false
*/
public static boolean startAlarm(Context context) {
return startAlarm(context, false);
}
/**
* #param context Context of activity
* #param restart start the alarm even when already running
* #return true if started | false if running and not started
*/
public static boolean startAlarm(Context context, boolean restart) {// todo restart alarm on settings change
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
SharedPreferences spref = PreferenceManager.getDefaultSharedPreferences(context);
String time = spref.getString(SettingsFragment.pref_notify_time, TimePreference.notify_default);
int hour = Integer.parseInt(time.split("\\:")[0]);
int minute = Integer.parseInt(time.split("\\:")[1]);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.HOUR_OF_DAY, hour);
// alternative: HOUR and AM_PM
if (calendar.getTimeInMillis() < Calendar.getInstance().getTimeInMillis()) {
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
// String time = new SimpleDateFormat("hh:mm", Locale.getDefault()).format(calendar.getTime());
if (!isAlarmRunning(context) || restart) {
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), getPendingIntent(context));
Log.d(TAG, "Start Alarm at " + time);
// Toast.makeText(context, "Start Alarm at " + time, Toast.LENGTH_LONG).show();
return true;
}
Log.d(TAG, "Service already running");
return false;
}
/**
* #param context Context of activity
* #return true if running and canceled
*/
public static boolean cancelAlarm(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (isAlarmRunning(context)) {
alarmManager.cancel(getPendingIntent(context));
Log.d(TAG, "Cancel Alarm");
NotifyManager.isAlarmRunning(context);
// Toast.makeText(context, "Cancel Alarm from " + time, Toast.LENGTH_LONG).show();
return true;
}
Log.d(TAG, "Service already canceled");
return false;
}
/**
* #param context Context of activity
* #return if alarm is running
*/
public static boolean isAlarmRunning(Context context) {
Intent intent_service = new Intent(context, NotifyReceiver.class);
Log.d(TAG, "isAlarmRunning:" + (PendingIntent.getBroadcast(context, 0, intent_service, PendingIntent.FLAG_NO_CREATE) != null));
return (PendingIntent.getBroadcast(context, 0, intent_service, PendingIntent.FLAG_NO_CREATE) != null);
}
/**
* #param context Context of activity
* #return PendingIntent
*/
public static PendingIntent getPendingIntent(Context context) {
Intent intent = new Intent(context, NotifyReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_NO_CREATE);
// If it exists return it
if (pi != null)
return pi;
// It doesn't exist, make it (last parameter to 0 for reusable):
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
}
/**
* #return yyMMdd
*/
public static String getCurrentTimeStamp() {
SimpleDateFormat sdfDate = new SimpleDateFormat("yyMMdd", Locale.getDefault());
Date now = new Date();
String strDate = sdfDate.format(now);
return strDate;
}
/**
* #param context Context of the activity
* #return if notification is enabled or not
*/
public static boolean isNotificationEnabled(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SettingsFragment.pref_notify, true);
}
}
Point A: The service code is missing a key component
In the code above, the service has an onCreate and onDestroy, which will be triggered when the service is created and destroyed. However, if a service is triggered and it is already running, then it will not go through onCreate. It will, however, go through onstartCommand (onStart pre android 2.0). The actual structure of your code should be:
onCreate() {
// Stuff you only do when this class is instantiated the first time
// and don't need to do if it is called (started in android terminology)
// thereafter
}
// The next two are >=2.0 and then <2.0
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startHandleIntent(intent);
return START_STICKY; // If you want the service to hang around
}
#Override
public void onStart(Intent intent, int startId) {
startHandleIntent(intent);
}
void startHandleIntent(Intent intent) {
// Do things that shiould happen every time here
// eg. in your case, the notification
}
Point B: This isn't really what a service was designed for
You cannot rely on a service hanging around for that long. Inactive services will often be removed to make space for other things. Given that the the service does very little, it would probably be better to use a BroadcastReceiver, which was designed specifically for things that need triggering occasionally but don't really need to be there otherwise. So:
Use a BroadcastRecevier to catch the triggers and issue a notification. Something like this:
class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Issue the notidfication
<...>
// Reissue a request for a future alarm call here if needed
<...>
}
}
Remember to set it up to receive broadcasts in the manifest:
<application>
... other stuff ...
<receiver android:name=".MyBroadcastReceiver" android:enabled="true">
<intent-filter>
<action android:name="com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION"/>
</intent-filter>
</receiver>
</application>
To trigger that, you need an intent that will trigger a broadcast:
Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
context.sendBroadcast(intent);
If you are setting it up to call later via a PendingIntent (change the final flag to zero if you want a reusable PendingIntent for a recurring event):
Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT)
If later on you wish to change, or cancel somehting, or if you simply need to know if the Pending Intent exists from the system's point of view:
Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_NO_CREATE);
if (pi != null) {
// It exists. If you want then to cancel the alarm that triggers it:
alarmManager.cancel(pi);
}
else {
// It doesn't exist. If you need to create a reusable PendingIntent:
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
}
Personally, I would use this approach instead of initializePendingIntent, ie:
public static PendingIntent getPendingIntent() {
Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_NO_CREATE);
// If it exists return it
if (pi != null) return pi;
// It doesn't exist, make it (last parameter to 0 for reusable):
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
}
Use SharedPreferences (as you already do) to keep track of what is going on (time of alarm)
My preference would be to only create a one shot alarm with a one shot intent for when the next alarm should sound. If it changes, remove this alarm and create a new one. When it triggers, crate a new one. This way you minimise the number of things that have to stay alive for lengths of time.
Check your logcat for a stack trace. It will be before the activity manager service entries you have provided. This line looks suspect to me, specifically the setAction as it is not providing a proper resource value for the icon:
mNotifyBuilder.setContentTitle(getString(R.string.app_name)).setContentText(getString(R.string.notification_contenttext)).setContentIntent(pIntent).addAction(0, getString(R.string.notification_action), pIntent).setAutoCancel(true)
I want my app to show a notification with a "GOOD MORNING" message at 6 A.M everyday. As I read, for this I need the app to run in background so I need to use Service.
I have tried the following code but I'm stuck.
MainActivity.java
public void onClickStartService(View v)
{
startService(new Intent(this,MyService.class));
}
public void onClickStopService(View v)
{
stopService(new Intent(this,MyService.class));
}
and MyService.java is
public class MyService extends Service{
private static final String TAG = "MyService";
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
Toast.makeText(this, "Congrats! MyService Created", Toast.LENGTH_LONG).show();
Log.d(TAG, "onCreate");
}
#Override
public void onStart(Intent intent, int startId) {
Toast.makeText(this, "My Service Started", Toast.LENGTH_LONG).show();
Log.d(TAG, "onStart");
//Note: You can start a new thread and use it for long background processing from here.
}
#Override
public void onDestroy() {
Toast.makeText(this, "MyService Stopped", Toast.LENGTH_LONG).show();
Log.d(TAG, "onDestroy");
}
}
I have buttons to start and stop the Service and it works. Now I want the service to create notification as I have mentioned at the beginning of the post. How can I do this?
To start the service at a specific time, I suggest you create a BroadcastReceiver triggered by an Alarm, which will in turn start your service.
First write a BroadcastReceiver like this :
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
context.startService(new Intent(context, MyService.class));
}
/**
* Schedule the next update
*
* #param context
* the current application context
*/
private static void scheduleServiceUpdates(final Context context) {
// create intent for our alarm receiver (or update it if it exists)
final Intent intent = new Intent(context, AlarmReceiver.class);
final PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// compute first call time 1 minute from now
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, 10);
long trigger = calendar.getTimeInMillis();
// set delay between each call : 24 Hours
long delay = 24 * 60 * 60 * 1000;
// Set alarm
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC, trigger, delay, pending);
// you can use RTC_WAKEUP instead of RTC to wake up the device
}
}
Then you just need to call the scheduleServiceUpdate method to start the reccuring event. If you only use the RTC type, then if the phone is locked when the alarm should trigger the service, it won't and will wait until the device is unlocked by the user. If you use RTC_Wakeup, the service will start exactly at the time given.
Note that there are other methods in the AlarmManager to trigger events.
You can start from PendingIntent and AlarmManager
Tutorial here
Dont forget to add possibility to cancel alarm manager with
mAlarmManager.cancel(pendingIntent);
Also you may want to intercept android.intent.action.BOOT_COMPLETED event to make you app starting immediately after reboot if you want to start your service by schedule.