I am using the sample code(Scheduler.zip) available on the Android developer training website- http://developer.android.com/training/scheduling/index.html
Here's the code:-
MainActivity.java
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.scheduler;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
/**
* This sample demonstrates how to schedule an alarm that causes a service to
* be started. This is useful when you want to schedule alarms that initiate
* long-running operations, such as retrieving a daily forecast.
* This particular sample retrieves content from the Google home page once a day and
* checks it for the search string "doodle". If it finds this string, that indicates
* that the page contains a custom doodle instead of the standard Google logo.
*/
public class MainActivity extends Activity {
SampleAlarmReceiver alarm = new SampleAlarmReceiver();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
// Menu options to set and cancel the alarm.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// When the user clicks START ALARM, set the alarm.
case R.id.start_action:
alarm.setAlarm(this);
return true;
// When the user clicks CANCEL ALARM, cancel the alarm.
case R.id.cancel_action:
alarm.cancelAlarm(this);
return true;
}
return false;
}
}
SampleAlarmReceiver.java
package com.example.android.scheduler;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.v4.content.WakefulBroadcastReceiver;
import java.util.Calendar;
/**
* When the alarm fires, this WakefulBroadcastReceiver receives the broadcast Intent
* and then starts the IntentService {#code SampleSchedulingService} to do some work.
*/
public class SampleAlarmReceiver extends WakefulBroadcastReceiver {
// The app's AlarmManager, which provides access to the system alarm services.
private AlarmManager alarmMgr;
// The pending intent that is triggered when the alarm fires.
private PendingIntent alarmIntent;
#Override
public void onReceive(Context context, Intent intent) {
// BEGIN_INCLUDE(alarm_onreceive)
/*
* 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, SampleSchedulingService.class);
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, service);
// END_INCLUDE(alarm_onreceive)
}
// BEGIN_INCLUDE(set_alarm)
/**
* Sets a repeating alarm that runs once a day at approximately 8:30 a.m. When the
* alarm fires, the app broadcasts an Intent to this WakefulBroadcastReceiver.
* #param context
*/
public void setAlarm(Context context) {
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, SampleAlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
// Set the alarm's trigger time to 8:30 a.m.
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 30);
/*
* If you don't have precise time requirements, use an inexact repeating alarm
* the minimize the drain on the device battery.
*
* The call below specifies the alarm type, the trigger time, the interval at
* which the alarm is fired, and the alarm's associated PendingIntent.
* It uses the alarm type RTC_WAKEUP ("Real Time Clock" wake up), which wakes up
* the device and triggers the alarm according to the time of the device's clock.
*
* Alternatively, you can use the alarm type ELAPSED_REALTIME_WAKEUP to trigger
* an alarm based on how much time has elapsed since the device was booted. This
* is the preferred choice if your alarm is based on elapsed time--for example, if
* you simply want your alarm to fire every 60 minutes. You only need to use
* RTC_WAKEUP if you want your alarm to fire at a particular date/time. Remember
* that clock-based time may not translate well to other locales, and that your
* app's behavior could be affected by the user changing the device's time setting.
*
* Here are some examples of ELAPSED_REALTIME_WAKEUP:
*
* // Wake up the device to fire a one-time alarm in one minute.
* alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
* SystemClock.elapsedRealtime() +
* 60*1000, alarmIntent);
*
* // Wake up the device to fire the alarm in 30 minutes, and every 30 minutes
* // after that.
* alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
* AlarmManager.INTERVAL_HALF_HOUR,
* AlarmManager.INTERVAL_HALF_HOUR, alarmIntent);
*/
// Set the alarm to fire at approximately 8:30 a.m., according to the device's
// clock, and to repeat once a day.
alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), 2*60*1000, alarmIntent);
// Enable {#code SampleBootReceiver} to automatically restart the alarm when the
// device is rebooted.
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
// END_INCLUDE(set_alarm)
/**
* Cancels the alarm.
* #param context
*/
// BEGIN_INCLUDE(cancel_alarm)
public void cancelAlarm(Context context) {
// If the alarm has been set, cancel it.
if (alarmMgr!= null) {
alarmMgr.cancel(alarmIntent);
}
// Disable {#code SampleBootReceiver} so that it doesn't automatically restart the
// alarm when the device is rebooted.
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
// END_INCLUDE(cancel_alarm)
}
SampleBootReceiver.java
package com.example.android.scheduler;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* This BroadcastReceiver automatically (re)starts the alarm when the device is
* rebooted. This receiver is set to be disabled (android:enabled="false") in the
* application's manifest file. When the user sets the alarm, the receiver is enabled.
* When the user cancels the alarm, the receiver is disabled, so that rebooting the
* device will not trigger this receiver.
*/
// BEGIN_INCLUDE(autostart)
public class SampleBootReceiver extends BroadcastReceiver {
SampleAlarmReceiver alarm = new SampleAlarmReceiver();
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED"))
{
alarm.setAlarm(context);
}
}
}
//END_INCLUDE(autostart)
SampleSchedulingService.java
package com.example.android.scheduler;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* This {#code IntentService} does the app's actual work.
* {#code SampleAlarmReceiver} (a {#code WakefulBroadcastReceiver}) holds a
* partial wake lock for this service while the service does its work. When the
* service is finished, it calls {#code completeWakefulIntent()} to release the
* wake lock.
*/
public class SampleSchedulingService extends IntentService {
public SampleSchedulingService() {
super("SchedulingService");
}
public static final String TAG = "Scheduling Demo";
// An ID used to post the notification.
public static final int NOTIFICATION_ID = 1;
// The string the app searches for in the Google home page content. If the app finds
// the string, it indicates the presence of a doodle.
public static final String SEARCH_STRING = "doodle";
// The Google home page URL from which the app fetches content.
// You can find a list of other Google domains with possible doodles here:
// http://en.wikipedia.org/wiki/List_of_Google_domains
public static final String URL = "http://www.google.com";
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
#Override
protected void onHandleIntent(Intent intent) {
// BEGIN_INCLUDE(service_onhandle)
// The URL from which to fetch content.
String urlString = URL;
String result ="";
// Try to connect to the Google homepage and download content.
try {
result = loadFromNetwork(urlString);
} catch (IOException e) {
Log.i(TAG, getString(R.string.connection_error));
}
// If the app finds the string "doodle" in the Google home page content, it
// indicates the presence of a doodle. Post a "Doodle Alert" notification.
if (result.indexOf(SEARCH_STRING) != -1) {
sendNotification(getString(R.string.doodle_found));
Log.i(TAG, "Found doodle!!");
} else {
sendNotification(getString(R.string.no_doodle));
Log.i(TAG, "No doodle found. :-(");
}
// Release the wake lock provided by the BroadcastReceiver.
SampleAlarmReceiver.completeWakefulIntent(intent);
// END_INCLUDE(service_onhandle)
}
// Post a notification indicating whether a doodle was found.
private void sendNotification(String msg) {
mNotificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(getString(R.string.doodle_alert))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(msg))
.setContentText(msg);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
//
// The methods below this line fetch content from the specified URL and return the
// content as a string.
//
/** Given a URL string, initiate a fetch operation. */
private String loadFromNetwork(String urlString) throws IOException {
InputStream stream = null;
String str ="";
try {
stream = downloadUrl(urlString);
str = readIt(stream);
} finally {
if (stream != null) {
stream.close();
}
}
return str;
}
/**
* Given a string representation of a URL, sets up a connection and gets
* an input stream.
* #param urlString A string representation of a URL.
* #return An InputStream retrieved from a successful HttpURLConnection.
* #throws IOException
*/
private InputStream downloadUrl(String urlString) throws IOException {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Start the query
conn.connect();
InputStream stream = conn.getInputStream();
return stream;
}
/**
* Reads an InputStream and converts it to a String.
* #param stream InputStream containing HTML from www.google.com.
* #return String version of InputStream.
* #throws IOException
*/
private String readIt(InputStream stream) throws IOException {
StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
for(String line = reader.readLine(); line != null; line = reader.readLine())
builder.append(line);
reader.close();
return builder.toString();
}
}
I had to add the android-support-v4.jar file to the build path first of all to make it work.
I am getting notifications after every 2 mins, but on clicking the "Cancel Alarm" button, the alarm is not getting canceled.
So , I tried removing the if condition in the cancelAlarm function, but I then got a java.nullpointerexception.
So basically, the alarmMgr is becoming null.
But, I am not able to understand why.
Also, since this is a sample code, I think there must be no errors in it, so I must be missing out on something.
I think it is because the instance of alarmManager (alarmMgr) could be uninitialized, as it is initialized in setAlarm. So you have to initialize it also in the cancelAlarm method, in case it is null.
SampleAlarmReceiver.java:
public void cancelAlarm(Context context) {
if (alarmMgr==null) {
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
}
Intent intent = new Intent(context, SampleAlarmReceiver.class);
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0 , intent, 0);
try{
alarmMgr.cancel(alarmIntent);
Log.e("Alarma cancelada", action );
}catch (Exception e) {
Log.e("Error alarma", "AlarmManager update was not canceled. " + e.toString());
}
.
.
.
Related
Android 6, 7 and 8 have a bug in the calendar provider that makes alarms not to work randomly:
https://issuetracker.google.com/issues/64502046
So I developed my own calendar-based alarm application. But it doesn't work properly in my Samsung Galaxy J5 2017 with Android 7. Almost every alarm I set on it works successfully, but there was a single alarm that didn't work and I don't know why.
I suspected from the Samsung's power management settings so I put the application to sleep on purpose in battery settings and set some alarms to see if the power settings were responsible for the problem. But all the alarms I tested worked. So I cannot reproduce the failure. I cannot figure out why there was an alarm that didn't work. It was set properly because I read the logs of the application, but when the time arrived, the alarm receiver didn't get called and I cannot reproduce it. I know that it didn't get called because in the code the first thing I do is to log a message and the message wasn't present in the log. Also, before the alarm that didn't work, the device wasn't rebooted and the application wasn't uninstalled so the alarm must have been still set.
This is the class that sets the alarms:
package bembibre.alarmfix.alarms;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import java.util.Calendar;
import bembibre.alarmfix.database.RemindersDbAdapter;
import bembibre.alarmfix.logging.Logger;
import bembibre.alarmfix.utils.GeneralUtils;
/**
* Created by Max Power on 12/08/2017.
*/
/**
* Sets alarms in the operating system for the reminders of this application.
*/
public class ReminderManager {
/**
* This is the key that identifies a metadata item that is attached to the intent of an alarm of
* a reminder for tracking it.
*/
public static final String EXTRA_ALARM_ID = "extra_alarm_id";
private Context mContext;
private AlarmManager mAlarmManager;
public ReminderManager(Context context) {
mContext = context;
mAlarmManager =
(AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
/**
* Part of the code that is responsible for setting an alarm.
*
* #param taskId data base identifier of the reminder.
* #param alarmId number that helps distinguishing each one of the alarms set for a same reminder.
* #param when when.
*/
public void setReminder(long taskId, long alarmId, Calendar when) throws AlarmException {
Intent i = new Intent(mContext, OnAlarmReceiver.class);
i.putExtra(RemindersDbAdapter.KEY_ROWID, taskId);
i.putExtra(ReminderManager.EXTRA_ALARM_ID, alarmId);
PendingIntent pi = getReminderPendingIntent(i, taskId);
try {
this.setAlarm(pi, when);
Logger.log("An alarm has been set successfully for the reminder at " + GeneralUtils.format(when) + ". Reminder id: " + taskId);
} catch (Throwable throwable) {
Logger.log("The system doesn't let us to set an alarm for the reminder at " + GeneralUtils.format(when), throwable);
throw new AlarmException();
}
}
/**
* Unsets the alarm that would trigger for the reminder with the given database identifier.
* When calling this method, the reminder could have been erased from the database and it
* wouldn't be a problem. This method is only for unsetting its associated alarm from the
* system.
*
* #param taskId database identifier of the reminder.
* #param date date for logging purposes.
*/
public void unsetReminder(long taskId, String date) {
Intent i = new Intent(mContext, OnAlarmReceiver.class);
PendingIntent pi = getReminderPendingIntent(i, taskId);
mAlarmManager.cancel(pi);
Logger.log("An alarm has been unset successfully for the reminder at " + date + ". Reminder id: " + taskId);
}
/**
* Returns the <code>PendingIntent</code> object that must be used for calling this application
* when a reminder's alarm triggers.
*
* #param i the intent to be used.
* #param taskId reminder database identifier, it distingishes each alarm from the others.
* #return the <code>PendingIntent</code> object.
*/
private PendingIntent getReminderPendingIntent(Intent i, long taskId) {
PendingIntent pi = PendingIntent.getBroadcast(mContext, (int)taskId, i, PendingIntent.FLAG_UPDATE_CURRENT);
return pi;
}
/**
* Sets the alarm in the operating system.
*
* #param operation
* #param when
*/
private void setAlarm(PendingIntent operation, Calendar when) throws Throwable {
/*
* The alarm must be set differently depending on the OS version. Anyway, we need the
* pending intent in order to know what was the reminder for which the alarm was fired, so
* then the correct notification will be shown.
*/
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Before Marshmallow, we can do this for setting a reliable alarm.
mAlarmManager.set(AlarmManager.RTC_WAKEUP, when.getTimeInMillis(), operation);
} else {
/*
* Starting from Marshmallow, it seems like this is the only way for setting a reliable
* alarm.
* If we use the "alarm clock" framework, the user will see a icon of an alarm clock.
* If we use the setExactAndAllowWhileIdle the user will see nothing, but the OS can
* delay alarms at some sort of situations.
*/
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, when.getTimeInMillis(), operation);
}
}
}
And this is the class that should be called when every alarm goes off:
package bembibre.alarmfix.alarms;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import bembibre.alarmfix.alarms.intentservices.ReminderService;
import bembibre.alarmfix.database.RemindersDbAdapter;
import bembibre.alarmfix.logging.Logger;
/**
* Created by Max Power on 12/08/2017.
*/
/**
* Receives alarms from the OS.
*/
public class OnAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Logger.log("An alarm has been received right now.");
long rowid = intent.getExtras().getLong(RemindersDbAdapter.KEY_ROWID);
long alarmId = intent.getExtras().getLong(ReminderManager.EXTRA_ALARM_ID);
WakeReminderIntentService.acquireStaticLock(context);
Intent i = new Intent(context, ReminderService.class);
i.putExtra(RemindersDbAdapter.KEY_ROWID, rowid);
i.putExtra(ReminderManager.EXTRA_ALARM_ID, alarmId);
context.startService(i);
}
}
The full code is here: https://github.com/maykelbembibre/androidcalendarfix
Well as long as no one is able to find a valid solution to the problem that has been laid out, the only clumsy solution that I have been able to apply by myself is made up of the following:
Save every pending alarm in the database and delete it as soon as it gets triggered, removed by the user, the phone is rebooted and so forth. Every time the application is open, check if there is a past saved alarm and warn the user that it wasn't triggered.
Remember the user that he should review the power settings of his device, as the power management software created by some smartphone brands is in fact meant to prevent applications from work and consume power unless the user allows them to get enabled.
All that said, I think that after this long, Google should have made the continuous activation of an application to be a permission that the application requests to the user automatically, the same as the other permissions, so that if the user understands that it is an alarm application and allows the permission, the application is able to use alarms that are always reliable regardless of power management settings.
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
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 downloaded the scheduler example from android developer site. I have two package one package have three classes
com.example.android.scheduler----sampleSchedulerService and SampleAlarmReceiver and SampleBootReceiver
and another package is com.example1, has MainActivity class.
SampleSchedulerrService.java code as follows
package com.example.android.scheduler;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* This {#code IntentService} does the app's actual work.
* {#code SampleAlarmReceiver} (a {#code WakefulBroadcastReceiver}) holds a
* partial wake lock for this service while the service does its work. When the
* service is finished, it calls {#code completeWakefulIntent()} to release the
* wake lock.
*/
public class SampleSchedulingService extends IntentService {
public SampleSchedulingService() {
super("SchedulingService");
}
public static final String TAG = "Scheduling Demo";
// An ID used to post the notification.
public static final int NOTIFICATION_ID = 1;
// The string the app searches for in the Google home page content. If the app finds
// the string, it indicates the presence of a doodle.
public static final String SEARCH_STRING = "doodle";
// The Google home page URL from which the app fetches content.
// You can find a list of other Google domains with possible doodles here:
// http://en.wikipedia.org/wiki/List_of_Google_domains
public static final String URL = "http://www.google.com";
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
#Override
protected void onHandleIntent(Intent intent) {
Log.i("EFAIR","in onhandleintent");
// sendNotification(getString(R.string.doodle_found));
sendNotification(intent);
/*// BEGIN_INCLUDE(service_onhandle)
// The URL from which to fetch content.
String urlString = URL;
String result ="";
// Try to connect to the Google homepage and download content.
try {
result = loadFromNetwork(urlString);
} catch (IOException e) {
Log.i(TAG, getString(R.string.connection_error));
}
// If the app finds the string "doodle" in the Google home page content, it
// indicates the presence of a doodle. Post a "Doodle Alert" notification.
if (result.indexOf(SEARCH_STRING) != -1) {
sendNotification(getString(R.string.doodle_found));
Log.i(TAG, "Found doodle!!");
} else {
sendNotification(getString(R.string.no_doodle));
Log.i(TAG, "No doodle found. :-(");
}*/
// Release the wake lock provided by the BroadcastReceiver.
SampleAlarmReceiver.completeWakefulIntent(intent);
// END_INCLUDE(service_onhandle)
}
// Post a notification indicating whether a doodle was found.
private void sendNotification(Intent intent) {
String msg=intent.getStringExtra("MSG");
mNotificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
/* PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), 0);*/
PendingIntent contentIntent = PendingIntent.getActivity(this,(int) Math.random(),
intent.setClass(this,MainActivity.class),PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(getString(R.string.doodle_alert))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(msg))
.setContentText(msg);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify((int) Math.random(), mBuilder.build());
}
//
// The methods below this line fetch content from the specified URL and return the
// content as a string.
//
/** Given a URL string, initiate a fetch operation. */
private String loadFromNetwork(String urlString) throws IOException {
InputStream stream = null;
String str ="";
try {
stream = downloadUrl(urlString);
str = readIt(stream);
} finally {
if (stream != null) {
stream.close();
}
}
return str;
}
/**
* Given a string representation of a URL, sets up a connection and gets
* an input stream.
* #param urlString A string representation of a URL.
* #return An InputStream retrieved from a successful HttpURLConnection.
* #throws IOException
*/
private InputStream downloadUrl(String urlString) throws IOException {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Start the query
conn.connect();
InputStream stream = conn.getInputStream();
return stream;
}
/**
* Reads an InputStream and converts it to a String.
* #param stream InputStream containing HTML from www.google.com.
* #return String version of InputStream.
* #throws IOException
*/
private String readIt(InputStream stream) throws IOException {
StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
for(String line = reader.readLine(); line != null; line = reader.readLine())
builder.append(line);
reader.close();
return builder.toString();
}
}
SampleAlarmReceiver.java as follows
package com.example.android.scheduler;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log;
import java.util.Calendar;
/**
* When the alarm fires, this WakefulBroadcastReceiver receives the broadcast Intent
* and then starts the IntentService {#code SampleSchedulingService} to do some work.
*/
public class SampleAlarmReceiver extends WakefulBroadcastReceiver {
// The app's AlarmManager, which provides access to the system alarm services.
private AlarmManager alarmMgr;
// The pending intent that is triggered when the alarm fires.
private PendingIntent alarmIntent;
public void onReceive(Context context, Intent intent) {
Log.i("EFAIR","in onReceive ");
// BEGIN_INCLUDE(alarm_onreceive)
/*
* 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.
*/
ComponentName comp = new ComponentName("com.example.android.scheduler",
SampleSchedulingService.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)));
/*Intent service = new Intent(context, SampleSchedulingService.class);
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, service);*/
// END_INCLUDE(alarm_onreceive)
}
// BEGIN_INCLUDE(set_alarm)
/**
* Sets a repeating alarm that runs once a day at approximately 8:30 a.m. When the
* alarm fires, the app broadcasts an Intent to this WakefulBroadcastReceiver.
* #param context
*/
public void setAlarm(Context context,int day,int mongth,int year,int hour,int minute) {
Log.i("EFAIR","in set alarm"+minute);
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, SampleAlarmReceiver.class);
//put the massage
intent.putExtra("MSG","your schedule is going on......");
alarmIntent = PendingIntent.getBroadcast(context,hour+minute+day, intent,0);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
// Set the alarm's trigger time to 8:30 a.m.
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
//calendar.add
/*
* If you don't have precise time requirements, use an inexact repeating alarm
* the minimize the drain on the device battery.
*
* The call below specifies the alarm type, the trigger time, the interval at
* which the alarm is fired, and the alarm's associated PendingIntent.
* It uses the alarm type RTC_WAKEUP ("Real Time Clock" wake up), which wakes up
* the device and triggers the alarm according to the time of the device's clock.
*
* Alternatively, you can use the alarm type ELAPSED_REALTIME_WAKEUP to trigger
* an alarm based on how much time has elapsed since the device was booted. This
* is the preferred choice if your alarm is based on elapsed time--for example, if
* you simply want your alarm to fire every 60 minutes. You only need to use
* RTC_WAKEUP if you want your alarm to fire at a particular date/time. Remember
* that clock-based time may not translate well to other locales, and that your
* app's behavior could be affected by the user changing the device's time setting.
*
* Here are some examples of ELAPSED_REALTIME_WAKEUP:
*
* // Wake up the device to fire a one-time alarm in one minute.
* alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
* SystemClock.elapsedRealtime() +
* 60*1000, alarmIntent);
*
* // Wake up the device to fire the alarm in 30 minutes, and every 30 minutes
* // after that.
* alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
* AlarmManager.INTERVAL_HALF_HOUR,
* AlarmManager.INTERVAL_HALF_HOUR, alarmIntent);
*/
// Set the alarm to fire at approximately 8:30 a.m., according to the device's
// clock, and to repeat once a day.
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, alarmIntent);
// Enable {#code SampleBootReceiver} to automatically restart the alarm when the
// device is rebooted.
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
// END_INCLUDE(set_alarm)
/**
* Cancels the alarm.
* #param context
*/
// BEGIN_INCLUDE(cancel_alarm)
public void cancelAlarm(Context context) {
// If the alarm has been set, cancel it.
if (alarmMgr!= null) {
alarmMgr.cancel(alarmIntent);
}
// Disable {#code SampleBootReceiver} so that it doesn't automatically restart the
// alarm when the device is rebooted.
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
// END_INCLUDE(cancel_alarm)
}
and SamplebootReceiver.java
package com.example.android.scheduler;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* This BroadcastReceiver automatically (re)starts the alarm when the device is
* rebooted. This receiver is set to be disabled (android:enabled="false") in the
* application's manifest file. When the user sets the alarm, the receiver is enabled.
* When the user cancels the alarm, the receiver is disabled, so that rebooting the
* device will not trigger this receiver.
*/
// BEGIN_INCLUDE(autostart)
public class SampleBootReceiver extends BroadcastReceiver {
SampleAlarmReceiver alarm = new SampleAlarmReceiver();
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED"))
{
alarm.setAlarm(context,0,0,0,12,45);
}
}
}
//END_INCLUDE(autostart)
and MainActivity.java
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example1;
import com.example.android.scheduler.R;
import com.example.android.scheduler.SampleAlarmReceiver;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
/**
* This sample demonstrates how to schedule an alarm that causes a service to
* be started. This is useful when you want to schedule alarms that initiate
* long-running operations, such as retrieving a daily forecast.
* This particular sample retrieves content from the Google home page once a day and
* checks it for the search string "doodle". If it finds this string, that indicates
* that the page contains a custom doodle instead of the standard Google logo.
*/
public class MainActivity extends Activity {
SampleAlarmReceiver alarm = new SampleAlarmReceiver();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv=(TextView) findViewById(R.id.sample_output);
try{
String msg=getIntent().getStringExtra("MSG");
tv.setText(msg);
}catch(Exception e){
e.printStackTrace();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
// Menu options to set and cancel the alarm.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// When the user clicks START ALARM, set the alarm.
case R.id.start_action:
alarm.setAlarm(getApplicationContext(),0,0,0,16,35);
return true;
case R.id.start_action1:
alarm.setAlarm(getApplicationContext(),0,0,0,16,37);
return true;
case R.id.start_action2:
alarm.setAlarm(getApplicationContext(),0,0,0,16,19);
return true;
case R.id.start_action3:
alarm.setAlarm(getApplicationContext(),0,0,0,16,21);
return true;
case R.id.start_action4:
alarm.setAlarm(getApplicationContext(),0,0,0,16,23);
return true;
case R.id.start_action5:
alarm.setAlarm(getApplicationContext(),0,0,0,16,25);
return true;
// When the user clicks CANCEL ALARM, cancel the alarm.
case R.id.cancel_action:
alarm.cancelAlarm(this);
return true;
}
return false;
}
}
When I put the MainAcivity.java in same package as in SampleAlarmReceiver then it works fine.otherwise not. my problem is that when I put MainActivity.java in separate package then it not working.any body can help.
replace your manifest contents below.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.scheduler"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="11" android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission>
<uses-permission android:name="android.permission.INTERNET" />
<application android:label="#string/app_name"
android:icon="#drawable/ic_launcher"
android:theme="#style/Theme.Sample">
<activity android:name="com.example1.MainActivity"
android:label="#string/app_name"
android:uiOptions="splitActionBarWhenNarrow">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="com.example.android.scheduler.SampleAlarmReceiver"></receiver>
<receiver android:name="com.example.android.scheduler.SampleBootReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
<service android:name="com.example.android.scheduler.SampleSchedulingService" />
</application>
They need to be in the same package unless if you change the option android:exported="false" to true or add it if it is not there and your minSdk is higher than 16, i.e for minSdk 17 and above in your AndroidManifest.xml
http://developer.android.com/guide/topics/manifest/provider-element.html
android:exported
Whether the content provider is available for other applications to use:
true: The provider is available to other applications. Any application can use the provider's content URI to access it, subject to the permissions specified for the provider.
false: The provider is not available to other applications. Set android:exported="false" to limit access to the provider to your applications. Only applications that have the same user ID (UID) as the provider will have access to it.
The default value is "true" for applications that set either android:minSdkVersion or android:targetSdkVersion to "16" or lower. For applications that set either of these attributes to "17" or higher, the default is "false".
You can set android:exported="false" and still limit access to your provider by setting permissions with the permission attribute.
I'm doing a serious research on this topic for many days... I saw many topics here too...
But unfortunately I couldn't find a solution....
I'm writing an app that uses the new Google API for Geofence...
Well, I can handle "ins" and "outs" of a geofence, but only if my app is open! Even if I'm with wifi on, gps on, and 3G on, but the app, it does not trigger any event...Just if the app is open...
I'm using exactly the same GeofenceRequester class of the documentation http://developer.android.com/training/location/geofencing.html .
Even the class been the same I'll post the code here:
package br.com.marrs.imhere.geofence;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import br.com.marrs.imhere.services.ReceiveTransitionsIntentService;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener;
import com.google.android.gms.location.LocationStatusCodes;
/**
* Class for connecting to Location Services and requesting geofences.
* <b>
* Note: Clients must ensure that Google Play services is available before requesting geofences.
* </b> Use GooglePlayServicesUtil.isGooglePlayServicesAvailable() to check.
*
*
* To use a GeofenceRequester, instantiate it and call AddGeofence(). Everything else is done
* automatically.
*
*/
public class GeofenceRequester
implements
OnAddGeofencesResultListener,
ConnectionCallbacks,
OnConnectionFailedListener {
// Storage for a reference to the calling client
private final Activity mActivity;
// Stores the PendingIntent used to send geofence transitions back to the app
private PendingIntent mGeofencePendingIntent;
// Stores the current list of geofences
private ArrayList<Geofence> mCurrentGeofences;
// Stores the current instantiation of the location client
private LocationClient mLocationClient;
/*
* Flag that indicates whether an add or remove request is underway. Check this
* flag before attempting to start a new request.
*/
private boolean mInProgress;
public GeofenceRequester(Activity activityContext) {
// Save the context
mActivity = activityContext;
// Initialize the globals to null
mGeofencePendingIntent = null;
mLocationClient = null;
mInProgress = false;
}
/**
* Set the "in progress" flag from a caller. This allows callers to re-set a
* request that failed but was later fixed.
*
* #param flag Turn the in progress flag on or off.
*/
public void setInProgressFlag(boolean flag) {
// Set the "In Progress" flag.
mInProgress = flag;
}
/**
* Get the current in progress status.
*
* #return The current value of the in progress flag.
*/
public boolean getInProgressFlag() {
return mInProgress;
}
/**
* Returns the current PendingIntent to the caller.
*
* #return The PendingIntent used to create the current set of geofences
*/
public PendingIntent getRequestPendingIntent() {
return createRequestPendingIntent();
}
/**
* Start adding geofences. Save the geofences, then start adding them by requesting a
* connection
*
* #param geofences A List of one or more geofences to add
*/
public void addGeofences(List<Geofence> geofences) throws UnsupportedOperationException {
/*
* Save the geofences so that they can be sent to Location Services once the
* connection is available.
*/
mCurrentGeofences = (ArrayList<Geofence>) geofences;
// If a request is not already in progress
if (!mInProgress) {
// Toggle the flag and continue
mInProgress = true;
// Request a connection to Location Services
requestConnection();
// If a request is in progress
} else {
// Throw an exception and stop the request
throw new UnsupportedOperationException();
}
}
/**
* Request a connection to Location Services. This call returns immediately,
* but the request is not complete until onConnected() or onConnectionFailure() is called.
*/
private void requestConnection()
{
getLocationClient().connect();
}
/**
* Get the current location client, or create a new one if necessary.
*
* #return A LocationClient object
*/
private GooglePlayServicesClient getLocationClient()
{
if (mLocationClient == null) {
mLocationClient = new LocationClient(mActivity, this, this);
}
return mLocationClient;
}
/**
* Once the connection is available, send a request to add the Geofences
*/
private void continueAddGeofences() {
// Get a PendingIntent that Location Services issues when a geofence transition occurs
mGeofencePendingIntent = createRequestPendingIntent();
// Send a request to add the current geofences
mLocationClient.addGeofences(mCurrentGeofences, mGeofencePendingIntent, this);
}
/*
* Handle the result of adding the geofences
*/
#Override
public void onAddGeofencesResult(int statusCode, String[] geofenceRequestIds)
{
// Create a broadcast Intent that notifies other components of success or failure
Intent broadcastIntent = new Intent();
// Temp storage for messages
String msg;
// If adding the geocodes was successful
if (LocationStatusCodes.SUCCESS == statusCode)
{
// Create a message containing all the geofence IDs added.
msg = geofenceRequestIds.toString();
// In debug mode, log the result
Log.d("DEBUG", msg);
// Create an Intent to broadcast to the app
broadcastIntent.setAction("br.com.marrs.imhere.ACTION_GEOFENCES_ADDED")
.addCategory("br.com.marrs.imhere.CATEGORY_LOCATION_SERVICES")
.putExtra("br.com.marrs.imhere.EXTRA_GEOFENCE_STATUS", msg);
// If adding the geofences failed
}
else
{
/*
* Create a message containing the error code and the list
* of geofence IDs you tried to add
*/
msg = "Erro adicionando geofence";
// Log an error
Log.e("DEBUG", msg);
// Create an Intent to broadcast to the app
broadcastIntent.setAction("br.com.marrs.imhere.ACTION_GEOFENCE_ERROR")
.addCategory("br.com.marrs.imhere.CATEGORY_LOCATION_SERVICES")
.putExtra("br.com.marrs.imhere.EXTRA_GEOFENCE_STATUS", msg);
}
// Broadcast whichever result occurred
LocalBroadcastManager.getInstance(mActivity).sendBroadcast(broadcastIntent);
// Disconnect the location client
requestDisconnection();
}
/**
* Get a location client and disconnect from Location Services
*/
private void requestDisconnection() {
// A request is no longer in progress
mInProgress = false;
getLocationClient().disconnect();
}
/*
* Called by Location Services once the location client is connected.
*
* Continue by adding the requested geofences.
*/
#Override
public void onConnected(Bundle arg0) {
// If debugging, log the connection
Log.d("DEBUG", "GeofenceRequester connected");
// Continue adding the geofences
continueAddGeofences();
}
/*
* Called by Location Services once the location client is disconnected.
*/
#Override
public void onDisconnected() {
// Turn off the request flag
mInProgress = false;
// In debug mode, log the disconnection
Log.d("DEBUG", "GeofenceRequester disconnected");
// Destroy the current location client
mLocationClient = null;
}
/**
* Get a PendingIntent to send with the request to add Geofences. Location Services issues
* the Intent inside this PendingIntent whenever a geofence transition occurs for the current
* list of geofences.
*
* #return A PendingIntent for the IntentService that handles geofence transitions.
*/
private PendingIntent createRequestPendingIntent() {
// If the PendingIntent already exists
if (null != mGeofencePendingIntent) {
// Return the existing intent
return mGeofencePendingIntent;
// If no PendingIntent exists
} else {
// Create an Intent pointing to the IntentService
Intent intent = new Intent(mActivity, ReceiveTransitionsIntentService.class);
/*
* Return a PendingIntent to start the IntentService.
* Always create a PendingIntent sent to Location Services
* with FLAG_UPDATE_CURRENT, so that sending the PendingIntent
* again updates the original. Otherwise, Location Services
* can't match the PendingIntent to requests made with it.
*/
return PendingIntent.getService(
mActivity,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
}
/*
* Implementation of OnConnectionFailedListener.onConnectionFailed
* If a connection or disconnection request fails, report the error
* connectionResult is passed in from Location Services
*/
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
// Turn off the request flag
mInProgress = false;
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(mActivity, 9000);
/*
* Thrown if Google Play services canceled the original
* PendingIntent
*/
} catch (SendIntentException e) {
// Log the error
e.printStackTrace();
}
/*
* If no resolution is available, put the error code in
* an error Intent and broadcast it back to the main Activity.
* The Activity then displays an error dialog.
* is out of date.
*/
} else {
Intent errorBroadcastIntent = new Intent("br.com.marrs.imhere.ACTION_CONNECTION_ERROR");
errorBroadcastIntent.addCategory("br.com.marrs.imhere.CATEGORY_LOCATION_SERVICES")
.putExtra("br.com.marrs.imhere.EXTRA_CONNECTION_ERROR_CODE",
connectionResult.getErrorCode());
LocalBroadcastManager.getInstance(mActivity).sendBroadcast(errorBroadcastIntent);
}
}
}
And the service:
package br.com.marrs.imhere.services;
import br.com.marrs.imhere.ImHereActivity;
import br.com.marrs.imhere.R;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.LocationClient;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import java.util.List;
/**
* This class receives geofence transition events from Location Services, in the
* form of an Intent containing the transition type and geofence id(s) that triggered
* the event.
*/
public class ReceiveTransitionsIntentService extends IntentService {
/**
* Sets an identifier for this class' background thread
*/
public ReceiveTransitionsIntentService()
{
super("ReceiveTransitionsIntentService");
}
/**
* Handles incoming intents
* #param intent The Intent sent by Location Services. This Intent is provided
* to Location Services (inside a PendingIntent) when you call addGeofences()
*/
#Override
protected void onHandleIntent(Intent intent) {
// Create a local broadcast Intent
Intent broadcastIntent = new Intent();
// Give it the category for all intents sent by the Intent Service
broadcastIntent.addCategory("br.com.marrs.imhere.CATEGORY_LOCATION_SERVICES");
// First check for errors
if (LocationClient.hasError(intent)) {
// Get the error code
int errorCode = LocationClient.getErrorCode(intent);
// Log the error
Log.e("DEBUG", "Erro no service LocationClient has error");
// Set the action and error message for the broadcast intent
broadcastIntent.setAction("br.com.marrs.imhere.ACTION_GEOFENCES_ERROR").putExtra("br.com.marrs.imhere.EXTRA_GEOFENCE_STATUS", "problemas");
// Broadcast the error *locally* to other components in this app
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);
// If there's no error, get the transition type and create a notification
} else {
// Get the type of transition (entry or exit)
int transition = LocationClient.getGeofenceTransition(intent);
// Test that a valid transition was reported
if (
(transition == Geofence.GEOFENCE_TRANSITION_ENTER)
||
(transition == Geofence.GEOFENCE_TRANSITION_EXIT)
) {
// Post a notification
List<Geofence> geofences = LocationClient.getTriggeringGeofences(intent);
String[] geofenceIds = new String[geofences.size()];
for (int index = 0; index < geofences.size() ; index++) {
geofenceIds[index] = geofences.get(index).getRequestId();
}
String ids = TextUtils.join(",",geofenceIds);
String transitionType = getTransitionString(transition);
sendNotification(transitionType, ids);
// Log the transition type and a message
Log.d("DEBUG","Ae...n sei pq isso....mas parece que tah ok");
// An invalid transition was reported
} else {
// Always log as an error
Log.e("DEBUG","Erro, erro, erro");
}
}
}
/**
* Posts a notification in the notification bar when a transition is detected.
* If the user clicks the notification, control goes to the main Activity.
* #param transitionType The type of transition that occurred.
*
*/
private void sendNotification(String transitionType, String ids)
{
// Create an explicit content Intent that starts the main Activity
Intent notificationIntent =
new Intent(getApplicationContext(),ImHereActivity.class);
// Construct a task stack
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the main Activity to the task stack as the parent
stackBuilder.addParentStack(ImHereActivity.class);
// Push the content Intent onto the stack
stackBuilder.addNextIntent(notificationIntent);
// Get a PendingIntent containing the entire back stack
PendingIntent notificationPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
// Get a notification builder that's compatible with platform versions >= 4
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
// Set the notification contents
builder.setSmallIcon(R.drawable.abs__ic_clear)
.setContentTitle(ids)
.setContentText(transitionType)
.setContentIntent(notificationPendingIntent);
// Get an instance of the Notification manager
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Issue the notification
mNotificationManager.notify(0, builder.build());
}
/**
* Maps geofence transition types to their human-readable equivalents.
* #param transitionType A transition type constant defined in Geofence
* #return A String indicating the type of transition
*/
private String getTransitionString(int transitionType) {
switch (transitionType) {
case Geofence.GEOFENCE_TRANSITION_ENTER:
return "Entrando";
case Geofence.GEOFENCE_TRANSITION_EXIT:
return "Saindo";
default:
return "Desconhecido";
}
}
}
And the Broadcast receiver in the Activity:
public class GeofenceSampleReceiver extends BroadcastReceiver
{
/*
* Define the required method for broadcast receivers
* This method is invoked when a broadcast Intent triggers the receiver
*/
#Override
public void onReceive(Context context, Intent intent)
{
// Check the action code and determine what to do
String action = intent.getAction();
// Intent contains information about errors in adding or removing geofences
if (TextUtils.equals(action, "br.com.marrs.imhere.ACTION_GEOFENCE_ERROR"))
{
handleGeofenceError(context, intent);
// Intent contains information about successful addition or removal of geofences
}
else if (
TextUtils.equals(action, "br.com.marrs.imhere.ACTION_GEOFENCES_ADDED")
||
TextUtils.equals(action, "br.com.marrs.imhere.ACTION_GEOFENCES_REMOVED"))
{
handleGeofenceStatus(context, intent);
// Intent contains information about a geofence transition
} else if (TextUtils.equals(action, "br.com.marrs.imhere.ACTION_GEOFENCE_TRANSITION"))
{
handleGeofenceTransition(context, intent);
// The Intent contained an invalid action
}
else
{
Log.e("DEBUG", "Invalid action detail");
Toast.makeText(context, "Invalid action detail", Toast.LENGTH_LONG).show();
}
}
/**
* If you want to display a UI message about adding or removing geofences, put it here.
*
* #param context A Context for this component
* #param intent The received broadcast Intent
*/
private void handleGeofenceStatus(Context context, Intent intent) {
}
/**
* Report geofence transitions to the UI
*
* #param context A Context for this component
* #param intent The Intent containing the transition
*/
private void handleGeofenceTransition(Context context, Intent intent) {
/*
* If you want to change the UI when a transition occurs, put the code
* here. The current design of the app uses a notification to inform the
* user that a transition has occurred.
*/
}
/**
* Report addition or removal errors to the UI, using a Toast
*
* #param intent A broadcast Intent sent by ReceiveTransitionsIntentService
*/
private void handleGeofenceError(Context context, Intent intent)
{
String msg = intent.getStringExtra("br.com.marrs.imhere.EXTRA_GEOFENCE_STATUS");
Log.e("DEBUG", msg);
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}
}
And here is the piece of code that I use to create a GEofence before send to GeofenceRequester.
int raio = Integer.parseInt(spinner.getAdapter().getItem(spinner.getSelectedItemPosition()).toString());
int transitionType = (in.isChecked())?Geofence.GEOFENCE_TRANSITION_ENTER:Geofence.GEOFENCE_TRANSITION_EXIT;
Geofence geofence = new Geofence.Builder().setRequestId(nomeGeofence.getText().toString()).setTransitionTypes(transitionType).setCircularRegion(lat, lon, raio).setExpirationDuration(Geofence.NEVER_EXPIRE).build();
geofences.add(geofence);
try
{
mGeofenceRequester.addGeofences(geofences);
addCircleGeofence(raio);
}
catch (UnsupportedOperationException e)
{
Toast.makeText(getActivity(), "Já existe uma requisição de add em andamento",Toast.LENGTH_LONG).show();
}
Any help will be great!
Thanks!
I had the same exact problem. Here is what I answered over there: So after playing around with this a bit, it looks like the ReceiveTransitionsIntentService (as defined in the sample code) will stop getting the notifications when the app is not around. I think this is a big problem with the example code... Seems like that will trip folks like me up.
So I used a broadcast receiver instead, and so far it seems to be working from my tests.
Add this to the manifest:
<receiver android:name="com.aol.android.geofence.GeofenceReceiver"
android:exported="false">
<intent-filter >
<action android:name="com.aol.android.geofence.ACTION_RECEIVE_GEOFENCE"/>
</intent-filter>
</receiver>
Then in the GeofenceRequester class you need to change the createRequestPendingIntent method so that it goes to your BroadcastReceiver instead of the ReceiveTransitionsIntentService. MAKE SURE AND NOTE the change to .getBroadcast instead of getService. That got me hung up for a while.
private PendingIntent createRequestPendingIntent() {
// If the PendingIntent already exists
if (null != mGeofencePendingIntent) {
// Return the existing intent
return mGeofencePendingIntent;
// If no PendingIntent exists
} else {
// Create an Intent pointing to the IntentService
Intent intent = new Intent("com.aol.android.geofence.ACTION_RECEIVE_GEOFENCE");
//MAKE SURE YOU CHANGE THIS TO getBroadcast if you are coming from the sample code.
return PendingIntent.getBroadcast(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
}
Then I added the GeofenceReceiver class that looks something like this:
public class GeofenceReceiver extends BroadcastReceiver {
Context context;
Intent broadcastIntent = new Intent();
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
broadcastIntent.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES);
if (LocationClient.hasError(intent)) {
handleError(intent);
} else {
handleEnterExit(intent);
}
}
private void handleError(Intent intent){
// Get the error code
int errorCode = LocationClient.getErrorCode(intent);
// Get the error message
String errorMessage = LocationServiceErrorMessages.getErrorString(
context, errorCode);
// Log the error
Log.e(GeofenceUtils.APPTAG,
context.getString(R.string.geofence_transition_error_detail,
errorMessage));
// Set the action and error message for the broadcast intent
broadcastIntent
.setAction(GeofenceUtils.ACTION_GEOFENCE_ERROR)
.putExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS, errorMessage);
// Broadcast the error *locally* to other components in this app
LocalBroadcastManager.getInstance(context).sendBroadcast(
broadcastIntent);
}
private void handleEnterExit(Intent intent) {
// Get the type of transition (entry or exit)
int transition = LocationClient.getGeofenceTransition(intent);
// Test that a valid transition was reported
if ((transition == Geofence.GEOFENCE_TRANSITION_ENTER)
|| (transition == Geofence.GEOFENCE_TRANSITION_EXIT)) {
// Post a notification
List<Geofence> geofences = LocationClient
.getTriggeringGeofences(intent);
String[] geofenceIds = new String[geofences.size()];
String ids = TextUtils.join(GeofenceUtils.GEOFENCE_ID_DELIMITER,
geofenceIds);
String transitionType = GeofenceUtils
.getTransitionString(transition);
for (int index = 0; index < geofences.size(); index++) {
Geofence geofence = geofences.get(index);
...do something with the geofence entry or exit. I'm saving them to a local sqlite db
}
// Create an Intent to broadcast to the app
broadcastIntent
.setAction(GeofenceUtils.ACTION_GEOFENCE_TRANSITION)
.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES)
.putExtra(GeofenceUtils.EXTRA_GEOFENCE_ID, geofenceIds)
.putExtra(GeofenceUtils.EXTRA_GEOFENCE_TRANSITION_TYPE,
transitionType);
LocalBroadcastManager.getInstance(MyApplication.getContext())
.sendBroadcast(broadcastIntent);
// Log the transition type and a message
Log.d(GeofenceUtils.APPTAG, transitionType + ": " + ids);
Log.d(GeofenceUtils.APPTAG,
context.getString(R.string.geofence_transition_notification_text));
// In debug mode, log the result
Log.d(GeofenceUtils.APPTAG, "transition");
// An invalid transition was reported
} else {
// Always log as an error
Log.e(GeofenceUtils.APPTAG,
context.getString(R.string.geofence_transition_invalid_type,
transition));
}
}
/**
* Posts a notification in the notification bar when a transition is
* detected. If the user clicks the notification, control goes to the main
* Activity.
*
* #param transitionType
* The type of transition that occurred.
*
*/
private void sendNotification(String transitionType, String locationName) {
// Create an explicit content Intent that starts the main Activity
Intent notificationIntent = new Intent(context, MainActivity.class);
// Construct a task stack
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
// Adds the main Activity to the task stack as the parent
stackBuilder.addParentStack(MainActivity.class);
// Push the content Intent onto the stack
stackBuilder.addNextIntent(notificationIntent);
// Get a PendingIntent containing the entire back stack
PendingIntent notificationPendingIntent = stackBuilder
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
// Get a notification builder that's compatible with platform versions
// >= 4
NotificationCompat.Builder builder = new NotificationCompat.Builder(
context);
// Set the notification contents
builder.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(transitionType + ": " + locationName)
.setContentText(
context.getString(R.string.geofence_transition_notification_text))
.setContentIntent(notificationPendingIntent);
// Get an instance of the Notification manager
NotificationManager mNotificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
// Issue the notification
mNotificationManager.notify(0, builder.build());
}
}
Hopefully that helps someone else.
I had a similar issue and after trying various ways I could finally fix the issue. Unfortunately, the android documentation fails to mention these issues.
The android geofences get removed every time you reboot your device or every time you toggle the location mode.
So Add a broadcast receiver to listen to device reboots and location mode changes, and add the geofences again in the receiver.
I have posted a detailed explanation here