This is more of a doubt. Currently, my code works fine, but I want to make sure I'm doing it the right way.
I have a service, which checks if the activity is running on the foreground. If it is, it sends a broadcast to the activity, so the activity updates some stuff on the screen.
I created an IntentFilter on the service:
com.harkdev.ServerStatus.SERVER_UPDATED
Here the service needs to know if the activity is on the foreground, so it uses the IsActivityRunning() method, which reads the running tasks from the ApplicationManager. This means that I need to set the GET_TASKS permission.
Being that both, the SERVICE and the ACTIVITY are on the same package, is there a better way to get this info?? And maybe try and avoid setting the GET_TASKS permission??
This is the code in my service:
if (IsActivityRunning()) {
Intent localIntent = new Intent(SERVER_UPDATED);
SendBroadcast(localIntent, null);
}
The IsActivityRunning() Method:
public bool IsActivityRunning() {
ActivityManager manager = (ActivityManager) GetSystemService(ActivityService);
IList<ActivityManager.RunningTaskInfo> runningTaskInfo = manager.GetRunningTasks(1);
ComponentName componentInfo = runningTaskInfo[0].TopActivity;
if (componentInfo.PackageName == "com.harkdev.ServerStatus")
return true;
return false;
}
And this is the code in my activity:
protected override void OnCreate (Bundle bundle) {
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
IntentFilter filter = new IntentFilter(ServerStatusCheckService.SERVER_UPDATED);
_receiver = new ServiceBroadcastReceiver ();
_receiver.Received += Receiver_Received;
RegisterReceiver(_receiver, filter);
}
First you're doing the wrong way to compare strings:
if (componentInfo.PackageName == "com.harkdev.ServerStatus")
It should be:
if ("com.harkdev.ServerStatus".equals(componentInfo.PackageName))
Second, if the service and the activity are in your app then I think the requirement to check for "same package" is not necessary.
To send information from the service to the activity, you can use ResultReceiver (available in API 3+):
When starting your service from the activity, you create a ResultReceiver, put it into the intent which starts the service.
In the service, extract the ResultReceiver and keep it. When you want to send information, use send().
In the activity, for example in onDestroy() you can trigger a command to notify the service that the ResultReceiver is invalid and it should be removed.
Edied
For example:
In your activity:
// Global variable.
private ResultReceiver mResultReceiver = new ResultReceiver() {
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
// Update the UI here...
}
}
When you start the service:
Intent i = new Intent(this, TheService.class);
// You can use different action names for different commands.
i.setAction("REGISTER_RECEIVER");
i.putExtra("ResultReceiver", mResultReceiver);
i.putExtra("ResultReceiver_ID", hashCode());
startService(i);
And in onDestroy():
Intent i = new Intent(this, TheService.class);
i.setAction("UNREGISTER_RECEIVER");
i.putExtra("ResultReceiver_ID", hashCode());
startService(i);
In your service:
import android.util.SparseArray;
// ...
private SparseArray<ResultReceiver> mReceiverMap = new SparseArray<ResultReceiver>();
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if ("REGISTER_RECEIVER".equals(intent.getAction())) {
// Extract the ResultReceiver and store it into the map
ResultReceiver receiver = intent.getParcelableExtra("ResultReceiver");
int id = intent.getIntExtra("ResultReceiver_ID", 0);
mReceiverMap.put(id, receiver);
} else if ("UNREGISTER_RECEIVER".equals(intent.getAction())) {
// Extract the ResultReceiver ID and remove it from the map
int id = intent.getIntExtra("ResultReceiver_ID", 0);
mReceiverMap.remove(id);
}
// ...
}
Related
I created a Broadcast Receiver (BR) in a service that will react to incoming SMS with specific number and body. I only need it to receive for a few seconds/minutes after user action, that's why I didn't registered it in manifest or activity (user may close it). BR has two parts, automatic (which works fine) and manual which should launch MainActivity and start a Dialog. I know that Dialog can't be started from BR and thats why I created a Listener, but my problem is that it is always null after service starts. It has value in onCreate of my MainActivity, but when service starts it changes to null, and I understand why (serivce re-initalize the Listener listener). I even tryed to put initialised listener value to SharedPrefs and restore it after, but when I try to store it with json it only stores null again. So how do I make my listener != null??? These are the relevant parts of my code:
MainActivity
onCreate {
SMSService smsReceiver = new SMSService();
smsReceiver.setListener(new SMSService.Listener() { //here listener from service is != null
#Override
public void onTextReceived(String s) {
dialogS(s); // totaly different dialog
}
});
...
mDialogBuilder = new AlertDialog.Builder(this);
...
.setPositiveButton(new OnClick...
Intent servisIntent = new Intent(MainActivity.this, SMSService.class);
startService(servisIntent);
...
}
SMSService
private Listener listener; // and here it get null which is the problem
public int onStartCommand(Intent intent, int flags, int startId) {
...
SMSReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, MainActivity.class);
context.startActivity(i);
if (listener != null) {
listener.onTextReceived("4333");
}
}
void setListener(Listener listener) {
this.listener = listener; }
interface Listener {
void onTextReceived(String text);
}
Btw I also tried to put smsReceiver.setListener block of code in my Dialog .setPossitive onClickListener after calling startService hoping it would initiate after service but nothing
Installing a listener mechanism with setter method in service is bad practice. You can use ResultReceiver to receive callback results from service. It is Parcelable, so it can be passed in an intent before service started
I'm trying to connect my service and activity, tried different approaches, but nothing worked for me.
I start Servise in SplashActivity, when the app launches. Then Service do some work in background, and when this work is done, stopself() method is calling in onStartCommand() in Service.
Also there is another activity where should be results from this background work. The problem, that i need to load data into Recyclerview only when work is done and Service is stopped. So I Tried to use LocalBroadcastManager and send message to the activity before stopself() method.
The problem is, activity doesn't get any messages from Service. Here is my code, it's from this answer on stackoverflow - https://stackoverflow.com/a/8875292/7478869
Service:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
helper = ItemDatabaseHelper.getInstance(this);
preferences = getSharedPreferences(PREFERENCES, MODE_MULTI_PROCESS);
editor = preferences.edit();
helper.clearTable(Contacts_Contract.CONTACT_TABLE_NAME);
Set<ContactItem> setC = getContactList();
for(ContactItem item: setC){
helper.insertContacts(item);
}
editor.putBoolean(IS_CONTACTS_IMPORTED, true);
editor.apply();
Intent intent2 = new Intent("myevent");
// You can also include some extra data.
intent2.putExtra("message", "This is my message!");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent2);
Log.d(TAG, "onStartCommand: stopping service");
stopSelf();
return START_STICKY;
}
Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate: starts");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contacts);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Register to receive messages.
// We are registering an observer (mMessageReceiver) to receive Intents
// with actions named "custom-event-name".
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("myevent"));
}
// Our handler for received Intents. This will be called whenever an Intent
// with an action named "custom-event-name" is broadcasted.
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Get extra data included in the Intent
String message = intent.getStringExtra("message");
Log.e("receiver", "Got message: " + message);
Toast.makeText(context, "Import completed", Toast.LENGTH_SHORT).show();
}
};
Tactically, the reason why this does not work is that LocalBroadcastManager is local to a process, and you have two processes.
Strategically, having this code in a separate process is unnecessary and dangerous. For example, you are relying on MODE_MULTI_PROCESS, which has been deprecated for over two years and never worked especially well in the first place.
I am having an issue getting my ActivityRecognition Service to remain running. I currently have a service (GService) that runs continuously in the background. I want to start the ActivityRecognition service within GService, and have the ActivityRecognition service broadcast the activity result back to GService. I am able to start the service and receive feedback that it is running, and I also get one result from the intent handler (no actual data), but never again.
Here is the section of code from my continuous service setting up the intent, pending intent:
#Override
public void onConnected(Bundle bundle) {
Log.d(TAG, "onConnected - isConnected ...............: " + mGoogleApiClient.isConnected());
startLocationUpdates();
//start process to receive activity updates
Intent intent = new Intent(this, DetectedActivitiesIntentService.class);
PendingIntent mActivityRecognitionPendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, ActivityConstants.DETECTION_INTERVAL_MILLISECONDS_MOVING,
mActivityRecognitionPendingIntent).setResultCallback(this);
startService(intent); // this should start the DetectedActivitiesIntentService
This is the Broadcast receiver within GService:
public class ActivityDetectionBroadcastReceiver extends BroadcastReceiver {
protected static final String TAG_AR = "ADRR";
#Override
public void onReceive(Context context, Intent intent){
//ArrayList<DetectedActivity> updatedActivities =
// intent.getParcelableArrayListExtra(ActivityConstants.ACTIVITY_EXTRA);
//updateDetectedActivitiesList(updatedActivities);
String action = intent.getAction();
if(action.equals("com.gt.useractivity"))
{
Log.d(TAG_AR, "received broadcast from Activity service");
// below line should grab the resulting string activity from the intent and log it.
Log.d(TAG_AR, "activity is : " + intent.getExtras().getString(ActivityConstants.ACTIVITY_EXTRA));
}
}
}
Here is the ActivityRecognition Service code:
public class DetectedActivitiesIntentService extends IntentService {
protected static final String TAG = "ADIS";
/**
* This constructor is required, and calls the super IntentService(String)
* constructor with the name for a worker thread.
*/
public DetectedActivitiesIntentService() {
// Use the TAG to name the worker thread.
super(TAG);
Log.d(TAG, "Activity service started....");
}
#Override
public void onCreate() {
super.onCreate();
}
/**
* Handles incoming intents.
* #param intent The Intent is provided (inside a PendingIntent) when requestActivityUpdates()
* is called.
*/
#Override
protected void onHandleIntent(Intent intent) {
if(ActivityRecognitionResult.hasResult(intent))
{
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
Intent localIntent = new Intent(ActivityConstants.BROADCAST_ACTION);
// Get the list of the probable activities associated with the current state of the
// device. Each activity is associated with a confidence level, which is an int between
// 0 and 100.
ArrayList<DetectedActivity> detectedActivities = (ArrayList) result.getProbableActivities();
// Log each activity.
Log.i(TAG, "activities detected");
for (DetectedActivity da: detectedActivities) {
Log.i(TAG, ActivityConstants.getActivityString(da.getType()) + " " + da.getConfidence() + "%");
}
String activity = result.getMostProbableActivity().toString(); // get the activity and convert to string
// Broadcast the list of detected activities.
//localIntent.putExtra(ActivityConstants.ACTIVITY_EXTRA, detectedActivities);
//localIntent.setAction("com.gt.useractivity");
localIntent.putExtra(ActivityConstants.ACTIVITY_EXTRA, activity); // set the activity string to be transmitted
LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
}
else{
Log.d(TAG, "Intent had no activity data....");
}
}
}
This Activity recognition sample is based from the Google Github sample.
All the examples I have found when using the PendingIntent is being called from a main activity, not from a service. I'm obviously doing something incorrect, but I can't figure it out. Any advice would be appreciated. I should also note that I have 2 broadcast receivers within my GService. I don't know if this would cause an issue or not.
It looks like I have solved the problem. I have a second intent within my GService used for broadcasting. From what I can tell from this thread (Pending intent works correctly for first notification but not for the rest) if there are multiple intents being used, they have to be unique. Thus, I added one line of code when declaring my intent intent.setAction(Long.toString(System.currentTimeMillis())); which is enough to differentiate it from the other intent to the system. Once I did that, I began to receive the Activity broadcasts from the intent service, as well as still receiving the location requests from within the GService routine.
Good day, I have an activity which i navigate to from an icon on an appwidget using pending Intents. Everything is being done in a service class. Now, the activity has a refresh button which when pressed, it sends an intent that calls the onStart() method on the service to update itself and perform some web operations. How do i go about reflecting the changes that could have occurred from the service in the activity without temporarily existing the activity.
Service to Activity:
if(intent.getExtras()!= null){
appWidgetId = intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
//if i get this action from my detailedinfo class add a boolean to it
if(intent.getAction() == refresh_action){
// boolean variable to hold condition
my_action = true;
}
Intent forecast = new Intent(this,detailedInfo.class );
forecast.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
forecast.putExtra("cityname", city);
PendingIntent forecastIntent = PendingIntent.getActivity(this, 0, forecast, 0);
/*onclick to go to detailedInfo class*/
remoteView.setOnClickPendingIntent(R.id.city_image_id, forecastIntent);
if(my_action == true){
//Log.d(TAG, "my_action is true, performing pending intent");
try {
forecastIntent.send(this, 0, forecast);
} catch (CanceledException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
And in the Activity class:
Intent service = new Intent(this, cityService.class);
service.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
service.setAction(refresh_action);
Uri data = Uri.withAppendedPath(Uri.parse(CityWidgetProvider.URI_SCHEME + "://widget/id/"), String.valueOf(appWidgetId));
service.setData(data);
startService(service);
I tried adding a setAction() method to the intent that calls the service and then use the same pendingIntent(even though i think is a long shot) but they seems to be ignored. Please how do i go about this and what could i have been doing wrong.? As usual any help is highly appreciated. Thank you.
I'm not 100% clear on what you're trying to do, but the easiest thing to do would be to register a BroadcastReceiver in your Activity onResume (remove it in onPause). When the service is done with whatever it needs to do, broadcast that info.
In the Activity
public static final String ACTION_STRING = "THE_BIG_ACTION";
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Do whatever you want here
Toast.makeText(getApplicationContext(), "received", Toast.LENGTH_SHORT);
}
};
#Override
protected void onResume() {
super.onResume();
registerReceiver(receiver, new IntentFilter(ACTION_STRING));
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
In the service, when you're done, just call...
sendBroadcast(new Intent(YourActivityClass.ACTION_STRING));
If you want to include some data, just put it in the intent like you would when starting an Activity.
If your Activity is off screen when the service completes, and the user goes back to it, you'll have missed the notification. That's a different issue to resolve.
I have an activity with a checkbox: if the chekbox is unchecked then stop the service. this is a snippet of my activity code:
Intent serviceIntent = new Intent();
serviceIntent.setAction("com.android.savebattery.SaveBatteryService");
if (*unchecked*){
serviceIntent.putExtra("user_stop", true);
stopService(serviceIntent);
when I stop the service I pass a parameter "user_stop" to say at the service that has been a user to stop it and not the system (for low memory).
now I have to read the variable "user_stop" in void onDestroy of my service:
public void onDestroy() {
super.onDestroy();
Intent recievedIntent = getIntent();
boolean userStop= recievedIntent.getBooleanExtra("user_stop");
if (userStop) {
*** notification code ****
but it doesn't work! I can't use getIntent() in onDestroy!
any suggestion?
thanks
Simone
I see two ways of doing this:
Using Shared Preferences.
Using local broadcasts.
The first approach is an easy and straightforward way. But it is not very flexible. Basically you do:
a. Set "user stop" shared preference to true.
b. Stop service
c. In you service in onDestroy check what is the value of "user stop" preference.
The other approach is a better way but requires more code.
a. Define a string constant in you service class:
final public static string USER_STOP_SERVICE_REQUEST = "USER_STOP_SERVICE".
b. Create an inner class BroadcastReceiver class:
public class UserStopServiceReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
//code that handles user specific way of stopping service
}
}
c. Register this receiver in onCreate or onStart method:
registerReceiver(new UserStopServiceReceiver(), newIntentFilter(USER_STOP_SERVICE_REQUEST));
d. From any place you want to stop your service:
context.sendBroadcast(new Intent(USER_STOP_SERVICE_REQUEST));
Note that you can pass any custom arguments through Intent using this approach.
I don't think relying on onDestroy() is a good thing. There are couple of approaches you can take.
suggest to bind the service so that you could write your userstop notification in onUnbind http://developer.android.com/guide/topics/fundamentals.html#servlife
Other option (not sure if this works) is to post your variable to SharedPreferences and obtain it from onDestroy(). [You need to check if this works in debug mode or LogCat messages.
I have the same setup in my app using CheckBoxPreference in my activity and getSharedPreference in service class's onCreate. Checking the checkbox starts the service. Unchecking stops the service.
Listen and handle preference click in my Activity:
getPreferenceManager().findPreference("prefEnable").setOnPreferenceClickListener(new OnPreferenceClickListener()
{
// not used: Intent intent = new Intent(this, MyService.class);
#Override
public boolean onPreferenceClick(Preference preference) {
CheckBoxPreference cb = (CheckBoxPreference) preference;
if (cb.isChecked()) {
Log.d(TAG, "User enabled service");
// code to start service
} else {
Log.d(TAG, "User disabled service");
// code to stop service
}
return true;
}
});
Get my "prefEnable" preference within my service onCreate:
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
Boolean isEnabled = preferences.getBoolean("prefEnable", false);
Perhaps in your onDestroy, you can re-purpose this and say:
if (isEnabled==false) {
*** notification code ***
// You can pass the parameter in this simple way:-
Intent serviceIntent = new Intent(this,ListenLocationService.class);
serviceIntent.putExtra("From", "Main");
startService(serviceIntent);
//and get the parameter in onStart method of your service class
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Bundle extras = intent.getExtras();
if(extras == null)
Log.d("Service","null");
else
{
Log.d("Service","not null");
String from = (String) extras.get("From");
if(from.equalsIgnoreCase("Main"))
StartListenLocation();
}
}
Enjoy :)