I am using geofence in my app and based on geofence events (Enter or Exit) I want to perform some action. Geofence documentation says that once you set geofence it will trigger events automatically and you can catch this events with IntentService. For that I have made intentservice as below:
GeofenceTransitionsIntentService.java
public class GeofenceTransitionsIntentService extends IntentService {
Handler mHandler;
public GeofenceTransitionsIntentService() {
super("GeofenceTransitionsIntentService");
mHandler = new Handler();
}
#Override
public void onCreate() {
super.onCreate();
Log.e("JK-->>","service started!");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.e("JK-->>","onHandel--->>");
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
Log.e("JK-->>","geofenceEvent has error!");
return;
}
int geofenceTransitionType = geofencingEvent.getGeofenceTransition();
if (geofenceTransitionType == Geofence.GEOFENCE_TRANSITION_ENTER) {
Log.e("JK-->>","enter!");
mHandler.post(new DisplayToast(this,"Enter"));
} else if (geofenceTransitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
mHandler.post(new DisplayToast(this,"Exit"));
Log.e("JK-->>","exit");
}
}
public class DisplayToast implements Runnable {
private final Context mContext;
String mText;
public DisplayToast(Context mContext, String text){
this.mContext = mContext;
mText = text;
}
public void run(){
Toast.makeText(mContext, mText, Toast.LENGTH_SHORT).show();
}
}
}
Now, problem is that when app is open(No matter foreground or background) and I enter or exit in geofence it works fine and show me a toast message and logcat shows log but when I remove app from recent apps there is no toast message showing to me or no log is showing in logcat.
I have tried to find solution on google but mostly all answers suggests to use the service but if i am not wrong then IntentService stops itself automatically after work is done and start itself when any intent received. So, I think it's more efficient to use IntentService to do this task.
UPDATE
I am registering geofence using following line of code.
geofencingClient.addGeofences(getGeofencingRequest(),getGeofencePendingIntent());
and in getGeofencePendingIntent() i am starting intent service using following line of code.
private PendingIntent getGeofencePendingIntent() {
if(geofencePendingIntent != null)
return geofencePendingIntent;
Intent in = new Intent(SetProfileOnlineActivity.this,GeofenceTransitionsIntentService.class);
geofencePendingIntent = PendingIntent.getService(SetProfileOnlineActivity.this,111451,in,PendingIntent.FLAG_UPDATE_CURRENT);
return geofencePendingIntent;
}
This Service will run always :
Goto project java -> right click->New->service->service
name it watchman
watchman.java
public class watchman extends Service
{
NotificationManager mNotifyManager;
NotificationCompat.Builder mBuilder;
NotificationChannel notificationChannel;
String NOTIFICATION_CHANNEL_ID = "1";
public watchman() { }
#Override
public void onCreate()
{
try
{
mNotifyManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this, null);
mBuilder.setContentTitle("Insta Promo")
.setContentText("We are ready to help you.")
.setSmallIcon(R.drawable.ic_launcher_background);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "My Notifications", NotificationManager.IMPORTANCE_HIGH);
// Configure the notification channel.
notificationChannel.setDescription("Channel description");
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000});
notificationChannel.enableVibration(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
mNotifyManager.createNotificationChannel(notificationChannel);
}
else
{
mBuilder.setContentTitle("Insta Promo")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
.setVibrate(new long[]{100, 250})
.setLights(Color.YELLOW, 500, 5000)
.setAutoCancel(true);
}
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
mNotifyManager.notify(1, mBuilder.build());
startForeground(1, mBuilder.build());
}
catch(Exception e)
{
Log.d(TAG, "EXCEPTION IN SHOWING NOTIFICATION xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...\n");
Log.e("MY_APP", "exception", e);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
new Thread(new Runnable()
{
public void run()
{
while (true)
{
try
{
Log.d(TAG, "Thread : Running again...\n");
Thread.sleep(10000);
}
catch (InterruptedException e)
{
Log.d(TAG, "Thread : InterruptedException Error in service...\n");
}
}
}
}).start();
return START_STICKY;
}
#Override
public void onDestroy()
{
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent)
{
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
It will get automatically registered in manifest file as you created it as service, no need to update manifest file.
From main activity or from wherever you want to start it call it like
Log.d(TAG, " Good to Go \n");
Log.d(TAG, "Starting Service from main...\n");
Intent intent = new Intent(MainActivity.this, watchman.class);
startService(intent);
Log.d(TAG, "Main has started the service...\n");
Now you even if removed it from recents..., It will be there in memory running always for you, To check it keep eye on logcat. Hope it helps. Its working in project from 4.1 onwards upto latest 8.0 oreo
for showing notifications i am using vibration permission so also making manifest file available for you.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.rushi.oreo">
<uses-permission android:name="android.permission.VIBRATE"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".watchman"
android:enabled="true"
android:exported="true" />
</application>
</manifest>
Hope it really helps you or someone else.
IntentService will stop automatically when the work assigned to it is finished.
If you want a service to run in background with very less chances of getting stopped, it has to be a Foreground Service. Please make sure to start your Service in a background worker thread because by default a Service runs on the main thread.
More details are here - https://developer.android.com/reference/android/app/Service.html#startForeground(int, android.app.Notification)
But please note that making a Service as foreground impacts your phone's battery life too much. And a making a Service as Foreground is also annoying to the user since it shows a notification always and cannot be closed.
You can better use a JobScheduler or Firebase JobDispatcher to schedule background works.
I had found an answer... there was no problem in my code and IntentService was also working perfectly but the mistake was in the testing. I was testing my application on android Oreo running device.
In android oreo google has updated their policy that in foreground they will send location updates any number of times but in background they will send location updates only few times in hour.
The main reason behind it to save the bettery life of device.
For more information about android oreo location updates you can check out this documentation.
Related
My actual code blocks calls perfectly but now I want to identify an incoming SMS number ID and do stuff, like mark as read or whatever ( like Medium and this one ).
I've read a couple articles and threads but it's not even getting the intent, note again that this code works perfectly blocking calls so I'll paste the SMS related information
Manifest.xml
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<service android:name=".CallReceiverService" />
Service with Broadcast receiver
#Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new Notification.Builder(this, SERVICE_CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentText(this.getResources().getString(R.string.stg_ServiceRunning))
.setContentIntent(pendingIntent)
.setCategory(Notification.CATEGORY_CALL)
.build();
startForeground(44332255, notification);
}
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.intent.action.PHONE_STATE"); // related to call feature, ignore
intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
intentFilter.addAction("Telephony.Sms.Intents.SMS_RECEIVED_ACTION");
intentFilter.setPriority(1000);
registerReceiver(callCheckReceiver, intentFilter);
}
private BroadcastReceiver callCheckReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
try {
if (intent.getAction().equals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) {
Log.d("Call", "SMS received");
String smsSender = "";
if (intent.getAction().equals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) {
Log.d("Call", "SMS received");
String smsSender = "";
for (SmsMessage smsMessage : Telephony.Sms.Intents.getMessagesFromIntent(intent)) {
smsSender = smsMessage.getDisplayOriginatingAddress();
}
if (!isValidPhoneNumber(smsSender)) {
Log.d("Call", "Invalid SMS detected: From " + smsSender);
}
}
if (!isValidPhoneNumber(smsSender)) {
Log.d("Call", "Invalid SMS detected: From " + smsSender);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
public static boolean isValidPhoneNumber(String phoneNumber) {
return android.util.Patterns.PHONE.matcher(phoneNumber).matches();
}
Basically I'm asking the permission in MainActivity, setting them in Manifest and passing the FilterIntent in the Service that IS properly called in Oreo or lower versions of Android. Target API >=19
I don't want to build an app to manage SMS, I just want to intercept the number ID and do things. Can someone advise?
What you need is SMS Retriever API
If you want to detect the SMS, you can simply use
SmsRetrieverClient client = SmsRetriever.getClient(this /* context */);
Task<Void> task = client.startSmsRetriever();
task.addOnSuccessListener(new OnSuccessListener<Void>()
{
#Override
public void onSuccess(Void aVoid)
{
// Successfully started retriever, expect broadcast intent
// ...
}
});
task.addOnFailureListener(new OnFailureListener()
{
#Override
public void onFailure(#NonNull Exception e)
{
// Failed to start retriever, inspect Exception for more details
// ...
}
});
In AndroidManifest.xml simply add receiver
<receiver
android:name=".custom.SMSBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" />
</intent-filter>
</receiver>
Within receiver you can do whatever you want with detected message
public class SMSBroadcastReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction()))
{
Bundle extras = intent.getExtras();
Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
switch (status.getStatusCode())
{
case CommonStatusCodes.SUCCESS:
// Get SMS message contents
String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
// Extract one-time code from the message and complete verification
// by sending the code back to your server for SMS authenticity.
break;
case CommonStatusCodes.TIMEOUT:
// Waiting for SMS timed out (5 minutes)
// Handle the error ...
break;
}
}
}
}
It should be noted that SMSRetrieverClient default timeout is 5 minutes.
For creating detectable SMS please follow SMS Creator for Google
Currently i am working with an application and my app has a feature that the user will be able to click on a Navigate button and my app will start the Google Map. Till now it's fine and i have done it. But the fact where i am stuck is that i want my app to perform some tasks. To achieve that i have used JobService and scheduled it to run after every 5 seconds even when the app is in background.
When the user presses the back button then inside onDestroy method i have cancelled the scheduler. But when the app is removed from the background by sliding or pressing the cross icon the JobService keeps running as the onDestroy method can be called or not by the os when it is removed from the background. How can i stop the scheduled job when the app is removed from the background?
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="javarank.com.serviceinbackground">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MyJobService" android:exported="true" android:permission="android.permission.BIND_JOB_SERVICE" />
</application>
</manifest>
MyJobService class
public class MyJobService extends JobService {
#Override
public boolean onStartJob(final JobParameters jobParameters) {
Toast.makeText(getApplicationContext(), "Doing job", Toast.LENGTH_SHORT).show();
jobFinished(jobParameters, true);
return false;
}
#Override
public boolean onStopJob(JobParameters jobParameters) {
return false;
}
}
Here is my MainActivity
public class MainActivity extends AppCompatActivity {
private static final int JOB_ID = 1;
private JobInfo jobInfo;
private JobScheduler scheduler;
private Button navigateButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ComponentName componentName = new ComponentName(this, MyJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, componentName);
builder.setPeriodic(5000);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
// if true this job exists even after a system reboot...
builder.setPersisted(false);
jobInfo = builder.build();
scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
scheduler.schedule(jobInfo);
navigateButton = (Button) findViewById(R.id.navigate_button);
navigateButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
StringBuffer url = new StringBuffer("https://www.google.com/maps/dir/?api=1");
url.append("&origin=23.755736,90.374627");
url.append("&destination=23.754047,90.371682");
url.append("&travelmode=driving");
Uri gmmIntentUri = Uri.parse(url.toString());
Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
mapIntent.setPackage("com.google.android.apps.maps");
startActivity(mapIntent);
}
});
}
#Override
protected void onDestroy() {
Toast.makeText(getApplicationContext(), "Destroy called.", Toast.LENGTH_SHORT).show();
scheduler.cancel(JOB_ID);
super.onDestroy();
}
}
I think you need to override following onStop() method and put stopService() command to stop the JobService.
#Override
protected void onStop() {
// A service can be "started" and/or "bound". In this case, it's "started" by this Activity
// and "bound" to the JobScheduler (also called "Scheduled" by the JobScheduler). This call
// to stopService() won't prevent scheduled jobs to be processed. However, failing
// to call stopService() would keep it alive indefinitely.
stopService(new Intent(this, MyJobService.class));
super.onStop();
}
You can create a new service like
MyService.java
public class MyService extends Service {
public MyService() {
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
//stop you jobservice from here
stopSelf();
}
}
and start it from MainActivity.java
startService(new Intent(MainActivity.this,MyService.class));
Android> 7 automatically saves battery power. You must turn on the application's battery saving stop feature.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Intent intent = new Intent();
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
startActivity(intent);
}
}
add this to AndroidManifest.xml
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
I faced this issue, but I found that after schedule job service, it can't be canceled (From view).
So I turned to stop it inside the job service by calling onStopJob(params)
and it worked.
I am not sure if I have this right or not, but I have some code that I need to run in the background of my android application, and once that is complete, I would like to wait 10 seconds, and then run the code again.
I currently have this working, but I am sure it ain't the right way to go about it and wondered if someone can give me a simple example, or tell me what I need to change to make this "the right way" to do it.
First I have my ScheduleService.java file. This is where my code that I want to run it, and as you can see, once it is finished, it puts the thread to sleep for 10 seconds and then calls itself again, but this means that the code never actually completes (you should see the length of the stack trace if I get an error!)
ScheduleService.java
public class ScheduleService extends Service {
private static final String TAG = "ScheduleService";
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
startJob();
}
});
t.start();
return Service.START_STICKY;
}
private void startJob(){
// all my code is here, and i do what i need
// job completed. Rest for 10 seconds before doing another one
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//do job again
startJob();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy(){
super.onDestroy();
}
}
Now to start the service code, I have the following when my application starts (in my MainActivity.java file)
// stop just encase its already started
context.stopService(new Intent(context, ScheduleService.class));
// start service
context.startService(new Intent(context, ScheduleService.class));
In order to make sure that the service is started when the device restarts, I also have my StartOnBootReciever.java code
public class StartOnBootReciever extends BroadcastReceiver {
private static final String TAG = "Autostart";
/**
* Listens for Android's BOOT_COMPLETED broadcast and then executes
* the onReceive() method.
*/
#Override
public void onReceive(Context context, Intent arg1) {
Log.d(TAG, "BOOT_COMPLETED broadcast received. Executing starter service.");
// upload in background
Intent intent = new Intent(context, ScheduleService.class);
context.startService(intent);
// This code will start the application once the device has been restarted
Intent i = new Intent(context, MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
Then in my AndroidManifest.xml file, I have the following
<receiver android:enabled="true" android:exported="true" android:name="StartOnBootReciever" android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<service android:enabled="true" android:exported="false" android:name="com.idamigo.ticketmachine.ScheduleService" />
This all works, but I feel that have code that is self-calling, and "never ending"
In your Service, you have used Thread.Sleep which is unnecessary. Because Service will run continuously in the background until you stop it or System stops it.
Example:
public class ScheduleService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Timer timer = new Timer();
timer.schedule(task,1,10000);
return Service.START_STICKY;
}
TimerTask task= new TimerTask() {
#Override
public void run() {
//do your task here.
}
};
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Go for Service only if you need to run a task continuously in the background. Otherwise, use IntentService. IntentService will be stopped when there is no work for it so you don't need to manage its state by yourself.
Please see edits before answering!
I have an app which contains a BackgroundService class:
public class BackgroundService extends Service {
#Override
public void onCreate() {
super.onCreate();
IntentFilter filter = new IntentFilter();
filter.addAction("com.spotify.music.playbackstatechanged");
filter.addAction("com.spotify.music.metadatachanged");
filter.addAction("com.spotify.music.queuechanged");
registerReceiver(receiver, filter);
Log.e("Playing:", "APP IS PLAYING");
Notification notification = new Notification();
startForeground(1, notification);
}
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long timeSentInMs = intent.getLongExtra("timeSent", 0L);
String action = intent.getAction();
if (action.equals(BroadcastTypes.METADATA_CHANGED)) {
String trackId = intent.getStringExtra("id");
String artistName = intent.getStringExtra("artist");
String albumName = intent.getStringExtra("album");
String trackName = intent.getStringExtra("track");
int trackLengthInSec = intent.getIntExtra("length", 0);
// Do something with extracted information...
} else if (action.equals(BroadcastTypes.PLAYBACK_STATE_CHANGED)) {
boolean playing = intent.getBooleanExtra("playing", false);
Log.e("Playing:","TRUE");
}
}
};
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
static final class BroadcastTypes {
static final String SPOTIFY_PACKAGE = "com.spotify.music";
static final String PLAYBACK_STATE_CHANGED = SPOTIFY_PACKAGE + ".playbackstatechanged";
static final String METADATA_CHANGED = SPOTIFY_PACKAGE + ".metadatachanged";
}
}
and this is declared in my manifest:
<service
android:name=".BackgroundService"
android:enabled="true" >
<intent-filter>
<action android:name="com.spotify.music.playbackstatechanged" />
<action android:name="com.spotify.music.metadatachanged" />
<action android:name="com.spotify.music.queuechanged" />
</intent-filter>
</service>
So essentially my objective is to have my BackgroundService initialized when my app is opened, and to have it continue to run in the Background doing whatever I need it to do. As of now, I am using logs to determine whether my "setup" is working, but when I run my app, I am unable to see an logs even after I tested all actions that should have triggered my BroadCastReceiver. Furthermore, my persistent notification should have changed had my service been running, but it does not...
Edit::
So, I added logs to my BackgroundService's onCreate() and onReceive() methods, however, neither seem to be appearing. Im wondering, do I need to do something in my launcher activity to initialize the service? Furthermore, no notification is shown so I assume the Service is not being started for some reason...
Latest Edit:
So I added the following code to my Main activity to see if it would make a difference:
startService(new Intent(this,BackgroundService.class));
And after debugging my app, I began to see the following error:
java.lang.RuntimeException: Unable to create service com.aurum.mutify.BackgroundService: java.lang.SecurityException: Isolated process not allowed to call registerReceiver
pointing to my BroadCast Receiver class.
Intent services are designed for short tasks. And your intent handling method is empty.
If you need long running task in the background use standard service and call start foreground. This will minimize chance of system destroying your service.
To learn more go here
EDIT
Try overriding onStartCommand method. this method is called when service is started and usually you do all stuff here. Remember that there are 3 options to return.
Edit 2:
try something like this
in on create
PendingIntent pi;
BroadcastReceiver br;
Intent myIntent;
#Override
public void onCreate()
{
super.onCreate();
myIntent = new Intent("something")
if(Build.Version.SDK_INT >= 16) //The flag we used here was only added at API 16
myIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
//use myIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); if you want to add more than one flag to this intent;
pi = PendingIntent.getBroadcast(context, 1, myIntent, 0);
br = new BroadcastReceiver ()
{
public void onReceive (Context context, Intent i) {
new thread(new Runnable()
{
public void run()
{
//do something
}
}).start();
}
};
And then in on start command
this.registerReceiver(br, new IntentFilter("something"));
I have certain dates which once attained lose their relevance and new dates for these fields in the DB should be calculated, I know I can leverage the AlarmManager class for this, however I have a few concerns regarding this:
1) Note: Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups and battery use. There are new APIs to support applications which need strict delivery guarantees; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent). Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested.
So do I need to code for both cases separately or if I target kitkat, will that work for older versions too? Also as my code execution is time critical, say after 12AM in the midnight of some date my Data loses relevance, how to overcome shifting of alarms.
2)Registered alarms are retained while the device is asleep (and can optionally wake the device up if they go off during that time), but will be cleared if it is turned off and rebooted.
2.1) Set the RECEIVE_BOOT_COMPLETED permission in your application's manifest. This allows your app to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting (this only works if the app has already been launched by the user at least once)
2.1.1) If I have set an alarm at 12, the service related to this alarm fires at 12, Now when I reboot the device, the time "at 12" has already passed, the alarm will be fired again immediately and the service will be called again?
At reboot what mechanism do I need to implement in-order to stick to my code execution policy at certain time? How do I set the alarm if the user does not launch my app?
The third thing is that if my app is uninstalled I want to clear all alarms set by my code, how do I listen to when the app is uninstalled?
Also I want to know, my app is very time critical, the values in my DB get obsolete by 12 am each night, while I am updating the app, what would be the result if a user chooses to use my app at 12 while I use a service to update it and its running in the background?
EDIT: What I have tried so far:
I have a Database in which records get stale past midnight, say sharp at 12:00. I invoked an Alarm Manager(In a test project as I like to isolate the problem code) to fire a service. I also acquire a PARTIAL_WAKE_LOCK on the device so that my huge database manipulation is done properly. I have also implemented a thread to do my time consuming task. Following is my MainActivity Class which I invoke at 12 to initiate the alarm(Random time for test purpose):
public class MainActivity extends Activity {
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
BroadcastReceiver br;
TextView t;
int sum;
public void setSum(int s){
sum = s;
// t = (TextView)findViewById(R.id.textView1);
// t.setText(sum);
System.out.println("In Set Sum"+s);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setup();
t = (TextView)findViewById(R.id.textView1);
ComponentName receiver = new ComponentName(getApplicationContext(), SampleBootReceiver.class);
PackageManager pm = getApplicationContext().getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 17);
calendar.set(Calendar.MINUTE, 05); // Particular minute
calendar.set(Calendar.SECOND, 0);
alarmMgr = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
1000*60*60*24, alarmIntent);
}
public void setup() {
br = new BroadcastReceiver() {
#Override
public void onReceive(Context c, Intent i) {
Toast.makeText(c, "Rise and Shine!", Toast.LENGTH_LONG).show();
//Invoke the service here Put the wake lock and initiate bind service
t.setText("Hello Alarm set");
startService(new Intent(MainActivity.this, MyService.class));
stopService(new Intent(MainActivity.this, MyService.class));
}
};
registerReceiver(br, new IntentFilter("com.testrtc") );
alarmIntent = PendingIntent.getBroadcast( this, 0, new Intent("com.testrtc"),0 );
alarmMgr = (AlarmManager)(this.getSystemService( Context.ALARM_SERVICE ));
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
This is my SampleBootReceiver Class inorder to check for reboots and set Alarms again after reboot, I am not sure if it works as intended. I could find no means to test if this is working properly but I do receive the Toast message about completion of boot.
public class SampleBootReceiver extends BroadcastReceiver {
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
BroadcastReceiver br;
TextView t;
MainActivity main;
#Override
public void onReceive(Context context, Intent intent) {
main= new MainActivity();
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
Toast.makeText(context, "Hello from Bootloader", 10000).show();
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 15);
calendar.set(Calendar.MINUTE, 50); // Particular minute
calendar.set(Calendar.SECOND, 0);
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
1000*60*60*24, alarmIntent);
context.getApplicationContext().registerReceiver(br, new IntentFilter("com.testrtc") );
alarmIntent = PendingIntent.getBroadcast( context.getApplicationContext(), 0, new Intent("com.testrtc"),
0 );
alarmMgr = (AlarmManager)(context.getApplicationContext().getSystemService( Context.ALARM_SERVICE ));
}
}
}
The following is my service Class, unsure about the return I am doing here in the onStartCommand method:
public class MyService extends Service {
int a = 2;
int b = 2;
int c = a+b;
public MainActivity main = new MainActivity();
public MyService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
Toast.makeText(this, "The new Service was Created", Toast.LENGTH_LONG).show();
}
#Override
public int onStartCommand(Intent i, int flags , int startId){
WakeLock wakeLock = null;
try{
PowerManager mgr = (PowerManager)getApplicationContext().getSystemService(Context.POWER_SERVICE);
wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
wakeLock.acquire();
Toast.makeText(this, " Service Started", Toast.LENGTH_LONG).show();
new Thread(new Runnable() {
public void run(){
//Will be substituted with a time consuming long task.
main.setSum(c);
}
}).start();
}catch(Exception e){
System.out.println(e);
}finally{
wakeLock.release();
}
return 1;
}
#Override
public void onDestroy() {
Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
}
}
Also in the above, I want to know if the thread I am starting will interfare with how I am acquiring the wake lock. Also if I can use an async task and release the wakelock in onPostExecute?
Finally here is my Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.testrtc"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.testrtc.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".SampleBootReceiver"
android:enabled="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" >
</action>
</intent-filter>
</receiver>
<service
android:name="com.testrtc.MyService"
android:enabled="true"
android:exported="true" >
</service>
</application>
</manifest>
THis is my log Cat after reboot, there are many log messages however I find these related to the app, they contain th epackage name:
01-22 15:18:35.652: V/ActivityManager(419): getTasks: max=1, flags=0, receiver=null
01-22 15:18:35.652: V/ActivityManager(419): com.xxx.xxx/.MainActivity: task=TaskRecord{425c58f0 #5 A com.xxx.xxx U 0}
01-22 15:18:35.653: V/ActivityManager(419): We have pending thumbnails: null
More Questions: Where should I set up the Alarm, if I do it in the onCreate of my Splash screen it will be called each time the app starts, maybe overwriting the older values.
Second I want to acquire a lock on the DB when my service is running, if in this time the user tries to open my app what do I do? (As data is getting updated I dont have anything to show).
Third in the above code I am still finding problems to register an alarm after reboot.
1) To handle alarm at exact times you have 2 options:
a) Set minimum SDK level at 18 and not 19. This will make your app work on kitkat at the exact time
b) Use the setExact() method to tell Kitkat to keep your alarm at the exact time specified
Source: http://developer.android.com/about/versions/android-4.4.html
2) The boot completed notification is the best way to go. You can set up a Broadcast receiver to get the notification on a reboot. You can store alarms in a Database and retrieve on reboot.
2.1) It is horrible programming practice to do something the user did not want you to do, i.e. set an alarm even though user has not opened the app.
3) As long as you don't create files on the SD card, all application data is removed upon uninstall of the app
4) You should lock/unlock data when writing or reading from it to solve your 12AM problem. If the data is locked, it would not get affected until the user has committed the transaction. If you are using SQLite you can get more information on this from: http://www.sqlite.org/lockingv3.html
I think, you can write a Service something similar to the below one (This will do your task for every 5 minutes) and start the Service from the BroadcastReceiver which listens to BOOT_COMPLETED state.
public class YourService extends IntentService {
public YourService() {
super("YourService");
}
public YourService(String name) {
super(name);
}
private static Timer timer = new Timer();
private class mainTask extends TimerTask {
public void run() {
// TASK WHATEVER YOU WANT TO DO
}
}
public void onDestroy() {
super.onDestroy();
}
#Override
protected void onHandleIntent(Intent intent) {
timer.scheduleAtFixedRate(new mainTask(), 0, 300000); // 5 minutes
}
}