I'm trying to use Messenger as an IPC mechanism between my appwidget and an activity.
I do the following:
I create an intent that starts a specific activity when a button is clicked on my appwidget.
intent = new Intent(mContext, TorchLightActivity.class);
intent.putExtra(RPC, new Messenger(mHandler));
intent.putExtra(TYPE, mType);
...
final PendingIntent actionPendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
remoteView.setOnClickPendingIntent(layout, actionPendingIntent);
When I click on my widget button an activity is started
onCreate(...)
final Bundle bun = getIntent().getExtras();
// Get null here
mMessanger = (Messenger) bun.get(FlashlightModule.RPC);
// Working great
mType = bun.getInt(FlashlightModule.Type);
When looking at the bundle I can see that the only extra data I have is FlashlightModule.Type e.g. the int value.
The intent is created with extras from the start (see Get the intent from onCreate in Android ) and setData is used from the start (see Application widget with bundle?)
Is it possible at all to pass Messenger between AppWidget to my own activity?
Thank you.
I'm trying to use Messenger as an IPC mechanism between my appwidget and an activity.
This makes no sense.
Your AppWidgetProvider instance will live for a couple of milliseconds -- any longer, and you're doing it wrong. It will never have an opportunity to use "IPC" with your activity, via Messenger or any other means.
Moreover, you should never create something from a BroadcastReceiver (such as an AppWidgetProvider) that you are expecting to live past the end of onReceive() (on onUpdate() for the AppWidgetProvider), such as your Handler, let alone the Messenger.
When your activity wishes to update the app widget's UI, it can create the RemoteViews and update the app widget.
Related
I have a question regarding proximity alerts.
In all tutorials I ve read they are created and destroyed while the activity that create them is still running.
But what happens if say an activity creates n proximity alerts and then the activity itself is destroyed (the PA are not)
Then if I want to build another activity that finds these Proximity Alerts, how can I do that? Is that even possible?
You have to maintain your own list of proximity alerts. There is no way to get them back. However, #Mercato is correct when he says that you can remove a PA using only pending intents, but you don't have to store them. According to the docs:
A PendingIntent itself is simply a reference to a token maintained by the system describing the original data used to retrieve it. This means that, even if its owning application's process is killed, the PendingIntent itself will remain usable from other processes that have been given it. If the creating application later re-retrieves the same kind of PendingIntent (same operation, same Intent action, data, categories, and components, and same flags), it will receive a PendingIntent representing the same token if that is still valid, and can thus call cancel() to remove it.
This means that the system will store your PendingIntent for you between app restarts, and you can retrieve it by passing the same Intent you used to create it. So for example, if you created the following PendingIntent:
Intent intent = new Intent(context, Foo.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Then all you have to store is the requestId (1) and the Class or class name (Foo.class or Foo.class.getName()). Then if you want to retrieve that same PendingIntent without creating a new one, you can do the following:
Class<Foo> className = retrieveClass(); //You implement this
//String clazz = retrieveClassName(); //This is another option
int requestId = retrieveId(); //You implement this
Intent intent = new Intent(context, className);
//The flag given attempts to retrieve the PendingIntent if it exists, returns null if it doesn't.
PendingIntent pi = PendingIntent.getBroadcast(context, requestId, intent, PendingIntent.FLAG_NO_CREATE);
if (pi != null) {
//This pending intent was registered once before.
//Go ahead and call the function to remove the PA. Also, go ahead and call pi.cancel() on this.
}
else {
//This pending intent was not registered, and therefore can't have a PA registered to it.
}
Technically, all proximity alerts need a PendingIntent defined and used as a parameter. Android's Documentation shows that if you know the list of PendingIntents then you can remove them as well.
removeProximityAlert(PendingIntent intent) Removes the proximity alert
with the given PendingIntent.
Since PendingIntent is Parecelable see here then you could add it as an Extra to any Intent. This means, that on starting another Activity, you can create an Parcelable[] array to hold all these PendingIntent, then
putExtra(String name, Parcelable[] value)
Add extended data to the intent.
then retrieve them in the next Activity via getIntent() and it's relevant methods.
I am making a home screen App Widget for my Android app. In that App Widget there is a ListView. That ListView has a custom layout which has a TextView & an ImageButton. I am setting a PendingIntent on that ImageButton to start the service as follows:
Intent intent = new Intent(mContext, MyService.class);
intent.putExtra(MyConstants.MY_EXTRA, c.getString(c.getColumnIndex(MyColumns.MY_COLUMN_TITLE)));
intent.setAction(MyConstants.MY_ACTION);
PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
rv.setOnClickPendingIntent(R.id.MyImageButton, pendingIntent);
in getViewAt method of RemoteViewsFactory. But on clicking the ImageButton the service does not start.
I have put an intentfilter with MY_ACTION for MyService. And also tried without it which did not make things work.
Anyone knows how to start service on click of a button inside a list view item in home screen App Widget in Android?
Everything works fine i.e. the ListView is populated perfectly from SQLite database.
From the android javadoc
public void setOnClickPendingIntent
Equivalent to calling
setOnClickListener(android.view.View.OnClickListener) to launch the
provided PendingIntent. When setting the on-click action of items
within collections (eg. ListView, StackView etc.), this method will
not work. Instead, use {#link
RemoteViews#setPendingIntentTemplate(int, PendingIntent) in
conjunction with RemoteViews#setOnClickFillInIntent(int, Intent).
In your update method of your AppWidgetProvider you have to add
final PendingIntent clickPendingIntentTemplate = PendingIntent.getService...
views.setPendingIntentTemplate(R.id.widget_list, clickPendingIntentTemplate);
and in the getViewAt method of your RemoteViewsFactory :
final Intent fillInIntent = new Intent();
fillInIntent.putExtra(...);
views.setOnClickFillInIntent(R.id.widget_list_item, fillInIntent);
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.
I know we can launch activities on notification open like this:
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
Intent targetIntent = new Intent(this, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, targetIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(contentIntent);
//...
But what if, instead of lunch an activity, I want to run a callback, like some onPushOpen, so to choose there how to handle the notification click?
You can declare an activity in your manifest, call it NotificationClickHandlerActivity, which you will set as the target for the pending intent of your notification instead of MainActivity.
In the onCreate method of this new activity, just call getIntent, based on the information in the intent (action, categories, extras) determine what needs to be opened next, start the new target activity which you determined or do some other action, then call finish from inside onCreate and return immediately.
If an activity calls finish in onCreate like this handler, it will never be shown to the user, so he will not be aware that it even exists, all he'll see is the other activity which you potentially launch from it.
Also, keep in mind that you should not do long running operations in the onCreate method of this handler activity as they will be executed on the UI thread and will block your app from responding to user input. If you have to do something longer running, put it inside an async task subclass (either STATIC nested class of the handler activity, or completely separate class), then start an instance of this task from the onCreate method, and call finish right after the task is started. The task will continue running in the background. If the task needs a context to do whatever it has to do, make sure you pass it an application context by calling getApplicationContext, and don't pass the activity itself otherwise you will have memory leaks.
You could use EventBus to achive that. Just post some event like onPushOpen() and catch it anywhere you want. Make smth like:
Intent targetIntent = new Intent(this, NaughtyActivity.class);
And make NaughtyActivity use no layout. Use it just to post the event and finish.
I've got some weird behavior and I can only assume is because of the Pending intents I am using.
Scenario
I have a widget (4x1) which has 4 buttons. Within onUpdate of the widget, I add an pending intent for each button. My intents fires a Service with a bundeled parameter and depending on this parameter starts something. I attach intents as this:
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
Bundle b = new Bundle();
b.putString("myVariable", someVariable);
Intent intent = new Intent(context, AppStarterService.class);
intent.putExtras(b);
PendingIntent pendingIntent = PendingIntent.getService(context, buttopnPosition, intent, 0);
views.setOnClickPendingIntent(R.id.btnOne, pendingIntent);
The problem
The code works just fine, until the user decides to update the content of the button. Then, a new Pending Intent is done. So, when I press again the button, sometimes it still executes the old intent and not the new one. I don't know how to explain this better. Let's say for my first intent the parameter is "TestOne", after my update, the new intent has parameter "TestX". When the user clicks on the button, on my service I get in intent extras still "TestOne" instead "TestX". So, my guess is that somehow, I need to cancel the previous intent, when the widget button content changes.
Is this the issue ? Am I doing something wrong ? How do I cancel the old intent, I need to recreate it and then cancel it ?
Thank you for your time.
I you keep having this problem even with FLAG_UPDATE_CURRENT, try defining a different requestCode each time, with something like this:
private static int request = 0;
...
PendingIntent pendingIntent = PendingIntent.getService(context, request++, intent, 0);
So each time a new PendingIntent is created, a new requestCode is used, at least during class life.
I hope it helps.
I think you want to set the flag http://developer.android.com/reference/android/app/PendingIntent.html#FLAG_UPDATE_CURRENT as the last parameter to PendingIntent.getService