I would like to know if it is possible to replicate in my visible Activity changes which are ocurring in a not visible activity.
For example, I have a loop with an integer incrementing in Activity A, then I invoke Activity B passing the integer as an extra.
Is there a way to see reflected the increments (which are ocurring in A) in B?
Thanks in advance.
Since int is a primitive type, a variable with int type will not be changed when you pass it into activity B.
I would suggest you pass in a wrapper of int type (i.e. class IntWrapper { int value; })
The change will be reflected when you increase value.
You can also pass data using broadcast recievers:
In the listening activity/fragment create the reciever and register it (do this in oncreate or onresume or something, maybe even onrestart). The activity will now listen for a broadcast and you test if it's a broadcast meant for that reciever using a built in keyword. Because a the broadcast is done using an intent, you can also get and use other data stored in that intent (like your int):
my_broadcast_reciever = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {String action = intent.getAction();
if (action.equals("finish_buy_activity"))
//do whatever
}
}
};
registerReceiver(my_broadcast_reciever, new IntentFilter("theKeywordThisBCRRespondsTo"));
Dont forget to unregister any recievers in onpause or ondestroy (whatever makes most sense depending on where you register the reciever):
{unregisterReceiver(my_broadcast_reciever);
Now, in the activity/fragment which you want to use to send the data, fire off the intent like so (you might want to create and keep the intent at a higher level so you don't keep on creating a new object every update):
Intent sintent = new Intent("theKeywordThisBCRRespondsTo");
//maybe also do: sintent.addInt(int); or something
sendBroadcast(sintent);
Keep in mind, this might be a little overkill in certain cases, but it is a great way to communicate and transmit data between activities/fragments.
Related
Is it possible to send an intent from a service to an Application class? Not Activity?
I wouldn't know what activity would be running at a particular time, so I am adding a boolean flag in the activity class that detects the activity and sends the appropriate data based on the broadcast received.
If your Service is active, then your Application class is active as well.
Otherwise you wouldn't be able to use getApplicationContext().
Although I'm skeptic about a service that runs forever there is a very clean way to make the Service communicate with a certain Activity, should the last one be currently active.
Such clean way is called LocalBroadcastManager.
The Activity meant to receive the data should register a BroadcastReceiver in onResume() and unregister it in onPause().
You instantiate your BroadcastReceiver in your Activity's onCreate()
this.localBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Do what you have to do here if you receive data from the Service.
}
}
You create a Filter so your Activity only listens to a certain type of signals.
private IntentFilter notifIntentFilter new IntentFilter("com.you.yourapp.MY_SIGNAL");
in onResume()
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, notifIntentFilter);
in onPause()
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
Now whenever you want to send data to your Activity, your Service can call:
final Intent intent = new Intent();
intent.setAction("com.you.yourapp.MY_SIGNAL");
// put your data in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
If your Activity is awake, it will respond to the signal. Otherwise, if it's in the background, or it is not instantiated it won't.
You can apply this pattern to as many Activities as you wish.
Still, I have never used this inside the Application class. But you can try to register your receiver there. It might work, since if the Application class is destroyed, the BroadcastReceiver is destroyed too and thus probably unregistered as well.
The point is, if your Application gets destroyed, your Service will be killed as well. Unless you launched it in another process. But then it will have it's own instance of Application; and this is a complex thing you probably do not want to get into details now...
Important: since the Application class is not tied to any UI component, you can do whatever you need directly inside your service. If you need to manipulate the UI, then the pattern described above will work for you.
Please read about new Android's background limitations.
Edit:
Oh yeah right, if you need your Service to call a function declared in your Application class, you can just do
((MyApplication) getApplication()).myFunctionToHandleData(Intent intent);
I didn't really understand your question though, but either of the methods described above should work for you.
I've got a Service which is playing music. It has different functions like 'play' ,'pause' , 'stop' etc. I'm creating the object for this Service in my MainActivity with bindService(). Everything works fine!
But how can I use this object through other classes (BroadcastReceiver, Widget) or another Activity while the MainActivity is not running ?
If I declare it static I only can access it when MainActivity is running.
How can I save/hold this Service object without it being deleted because the Activity finishes ?
You do not have to use a bound service for media playback. Just use
startService(myPlaybackIntent);
and before that supply the information the service needs (play/pause/skip/...) as an intent extra.
EDIT
If for example you use int constants like ACTION_PLAY = 0, ACTION_PREVIOUS = 1,..., then you can write
myPlaybackIntent.putExtra("your.package.name.MEDIA_ACTION", ACTION_PLAY);
before calling 'startService()'
and evaluate the intent extra at the beginning of the service's 'onStartCommand()' method:
if(intent.hasExtra("your.package.name.MEDIA_ACTION"))
{
switch(intent.getIntExtra("your.package.name.MEDIA_ACTION", -1))
// -1 = some value you don't use for your actions
{
case ACTION_PLAY: // start playback
case ACTION_PREVIOUS: // jump to last song
...
}
}
End of EDIT
There is a very nice guide to media playback using a service .
This way, you can keep your service from being stopped/deleted by returning with "START_STICKY" from the 'onStartCommand()' method in your service class.
The other good thing about using the 'onStartCommand()' method is that you can call it from a Widget. The only difference is that you first have to define a pending intent for each action you want to take.
Let's say your RemoteViews object (I assume you're familiar with widgets, if not - I learned a lot from the guide at developer.android.com) is called 'updateViews' and you have different integer constants for indicating which action to take, like 'ACTION_PLAY' for starting playback. Then for the 'play'-button (R.id.btn_play) you could write:
Intent iPlay = new Intent(this, SVC.class);
iPlay.putExtra("your.package.name.MEDIA_ACTION", ACTION_PLAY);
PendingIntent piPlay = PendingIntent.getService(context, ACTION_PLAY,
iPlay, 0);
updateViews.setOnClickPendingIntent(R.id.btn_play, piPlay);
Please make sure to use different values for the second parameter of the 'getService()' method for the different pending intents you are going to need (play, pause, previous,...).
Because if you set up another intent like
Intent iPrevious = new Intent(this, SVC.class);
then - no matter what extras you put to iPrevious - if you code
PendingIntent piPrevious = PendingIntent.getService(context, ACTION_PREVIOUS,
iPrevious, 0);
the system will know the difference between the pending intents only by comparing ACTION_PLAY to ACTION_PREVIOUS.
In my app i have to receive USER_PRESENT and AIRPLANE_MODE intents. But each intent is broadcasted twice (sometimes thrice) which i don't want. I was trying to resolve this issue using SharedPreferences but i need to identify each intent uniquely (with any 'id' if it has or timestamp etc).
The following is the code where i was trying to allow only one onReceive call.
#Override
public void onReceive(Context context, Intent intent) {
final SharedPreferences instanceCount = context.getSharedPreferences("Instances",Context.MODE_WORLD_WRITEABLE);
SharedPreferences.Editor instance_editor = instanceCount.edit();
if(instanceCount.getBoolean("instance",true))
{
instance_editor.putBoolean("instance",false);
instance_editor.commit();
}
else
{
instance_editor.putBoolean("instance",true);
instance_editor.commit();
return;
}
.
.
.
.
}
Can any one please help in solving this. Thank You :)
Those are system broadcasts and you cannot control how many times they are broadcasted. Make your receiver(s) idempotent, e.g., it stays in the same state even if it is invoke multiple times. Instead of counting broadcasts, save the current state of your app and check it when you receive a broadcast. If you are already in the desired state, do nothing. Otherwise, change the state as appropriate and save it.
I have a Service that can be stopped in multiple ways. Whenever I call stopService(Intent), I pass an intent with some extras. How do you retrieve those extras?
Thanks.
You need to override onStartCommand() in your Service this is how you get a reference to the incoming intent from startService.
In this case you would have a special action in your intent to tell the service to stop itself. You add extras to this intend which can be read in the onStartCommand() method.
Sample Code
public class MyService extends Service {
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
final String yourExtra = intent.getStringExtra(YOUR_EXTRA_ID);
// now you can e.g. call the stopSelf() method but have the extra information
}
}
Explanation
Every time you call context.startService(Intent) onStartCommand() will be called. If the service is already running a new service isn't created but onStartCommand is still called. This is how you can get a new intent with extras to a running service.
I found that the extras are not passed with the intent when stopService is called. As a workaround, simply call startService(Intent) and stopService(Intent) right after one another.
Example code from Activity:
Intent j = new Intent(SocketToYa.this, AccelerometerDataSocket.class);
j.putExtra("com.christophergrabowski.sockettoya.DestroyService", true);
startService(j);
stopService(j);
In Service.onStartCommand,
intent.hasExtra("com.christophergrabowski.sockettoya.DestroyService")
will evaluate to true, and you will destroy your service the way intended by the API (i.e., by calling stopService, which will in turn call OnDestroy after onStartCommand is called).
My suggetion is that use static member in class that extends Activity for passing information to service & it in service as normal static member access in outside class
Please don't do this unless you have no other option. You should try to use the mechanisms built into the framework for passing data, and not use public static fields unless there is no other choice. Read the Service documentation for examples.
Are you able to use an Intent with a "shutdown" action with Context.startService()?
That is, send an Intent with a shutdown action and extras to Service.onStartCommand(), decide how to shutdown based on the extras, then use Service.stopSelf() to stop the service.
I agree this isn't a great solution, since it potentially starts the service in order to shut it down. I would still like to hear of the "correct" way (if one exists) of doing this with Context.stopService().
You can not write
getIntent()
method in a class extending Service. So I think using getExtra() won't work.
My suggetion is that use static member in class that extends Activity for passing information to service & it in service as normal static member access in outside class i.e.
Classname.yourobject
.
see this link for other option
http://developer.android.com/resources/faq/framework.html#3
At the moment I am developing an application which catches the action NEW_OUTGOING_CALL with the help of a BroadcastReceiver. I am aborting the call by calling setResultData(null). After that I am showing the user a dialog which allows him to decide if he wants to use my application to rewrite its number. When the users decision has happened I am placing the new call depending on the decision. Now my broadcast receiver gets called up once again.
What is the correct way of getting to know that I have already processed the number? I got a working solution that uses a timestamp to guess if it could be already processed. Another solution would be to add a "+" at the end of the processed number.
These methods are working fine for my application being the only one catching the NEW_OUTGOING_CALL event. But what should I do when other applications (like Sipdroid or Google Voice) are also sitting there catching the NEW_OUTGOING_CALL broadcast aborting it and restarting it again? I don't see a possibility to get to know if we are still in the same "call flow" and if I already processed the number.
I would love to hear your ideas about this problem!
What API level are you working with? If it's >= 11, check out the new BroadcastReceiver.goAsync function that lets you extend the processing of the broadcast outside of the onReceive function of your receiver. This could bypass the need to loop altogether.
If, like me, you're stuck trying to do this before level 11, it is surprisingly tricky to do this elegantly. You may have done this as well, but I tried to include a "processed" flag as an extra in the ACTION_CALL intent that my code generated, hoping that it would somehow get included in the resulting ACTION_NEW_OUTGOING_CALL broadcast, but that sadly does not work.
The best solution I have been able to find is including a fragment in the URI for the ACTION_CALL intent that you generate. This fragment will be included for the resulting ACTION_NEW_OUTGOING_CALL broadcast, so your broadcast receiver can differentiate between the original call and the one that you generate, but it won't interfere with handlers that aren't looking for it.
Here's the basic code.
In your BroadcastReceiver for the ACTION_NEW_OUTGOING_CALL
public class YourBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// extract the fragment from the URI
String uriFragment = Uri.parse(
intent.getStringExtra("android.phone.extra.ORIGINAL_URI")).getFragment();
// if the fragment is missing or does not have your flag, it is new
if (uriFragment == null || !uriFragment.contains("your_flag")) {
// launch your activity, pass the phone number, etc.
// use getResultData to get the number in order to respect
// earlier broadcast receivers
...
// abort the broadcast
this.setResultData(null);
this.abortBroadcast();
}
// otherwise, your code is there, this call was triggered by you
else {
// unless you have a special need, you'll probably just let the broadcast
// go through here
// note that resultData ignores the fragment, so other receivers should
// be blissfully unaware of it
}
}
}
When the user first dials the number, the fragment will either be missing altogether or your flag won't be present, so you'll abort the broadcast and start your activity. In your activity, if you decide to place the call again, do something like the following:
startActivity(new Intent(Intent.ACTION_CALL,
Uri.parse("tel:" + modified_number + "#your_flag")));
The "your_flag" fragment will then be present in the subsequent NEW_OUTGOING_CALL broadcast and thus allow you to handle this case differently in your broadcast receiver.
The nice thing about this is the the fragment is completely ignored unless you look for it in the ORIGINAL_URI, so other broadcast receivers can continue to function. If you want to be really nice, you may want to look for an existing fragment and add your flag to it (perhaps with a comma separator).
I hope that helps. Good luck!
I don't see a possibility to get to
know if we are still in the same "call
flow" and if I already processed the
number.
Technically, you are not in the same "call flow" as placing a new call is asynchronous. You have to use hints (such as a timestamp) as you seem to be doing already.
If you are confident that other applications will not rewrite the number except to change the prefix or to add a suffix, you may want to add another "proximity check" hint to avoid false positives/negatives, but I'm afraid that's about all you can do.
The onReceive() method in Broadcast receiver receives an Intent as an argument.
Extract the Bundle from the Intent using Intent.getExtras().
This Bundle contains 3 key-value pairs as follows :
android.phone.extra.ALREADY_CALLED = null
android.intent.extra.PHONE_NUMBER = 98xxxxxx98
android.phone.extra.ORIGINAL_URI = tel:98xxxxxx98
98xxxxxx98 is the number dialled by the user.
When the onReceive() is called again, this number changes to 98xxxxxx98* or 0*
By checking for the asterisk(*) at the end of the dialled number, it can be inferred if the onReceive() method is called for the first time or the next subsequent times.
One of the answers would be to track the boolean extra in the intent. It is done in similar way by the Google Phone app. You can check this BroadcastReceiver here (look for alreadyCalled usage)
The other way would be just to pass that "rewritten" number from your broadcast to the next broadcast receiver down the road (can be any app, like Sipdroid, Google Voice, or custom VoIP app) without calling ACTION_CALL intent (this is why you get loop and you broadcast receiver called again) The following code is example of how I am handling call in my custom VoIP app. When I intercept NEW_OUTGOING_CALL in my broadcast receiver, I first check if there is internet connection. If phone is connected to internet I use custom defined intent action of my activity to place call through my VoIP app. If there is no internet connection, I just set original phone number to the broadcast receiver result data. This is used by the next broadcast receiver (probably default phone app, but doesn't have to be) in the flow to place a call.
public class BHTTalkerCallReceiver extends BroadcastReceiver {
private static final String TAG = "BHTTalkerCallReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Broadcast successfull ... ");
// Extract phone number reformatted by previous receivers
String phoneNumber = getResultData();
if (phoneNumber == null) {
// No reformatted number, use the original
phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
}
if (isNetworkAvailable(context)) { // Make sure this app handles call only if there is internet connection
// My app will bring up the call, so cancel the broadcast
setResultData(null);
// Start my app to bring up the call
Intent voipCallIntent = new Intent(context, TalkerActivity.class);
voipCallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
voipCallIntent.putExtra(TalkerActivity.OUT_CALL_NUMBER, phoneNumber);
voipCallIntent.setAction(TalkerActivity.BHT_TALKER_OUT_CALL);
context.startActivity(voipCallIntent);
} else { //otherwise make a regular call...
// Forward phone data to standard phone call
setResultData(phoneNumber);
}
}
private boolean isNetworkAvailable(final Context context) {
final ConnectivityManager connectivityManager = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
return connectivityManager.getActiveNetworkInfo() != null && connectivityManager.getActiveNetworkInfo().isConnected();
}
}