I've got a somewhat graphically complex Android homescreen (via AppWidgetProvider) widget that I simply can't get to respond to touch events. The general idea is that tapping the widget should make it change modes for the next 4-5 seconds to display different information, but for the life of me it's showing no sign of ever receiving the Touch event.
The steps I've taken are as follows:
I've implemented an intent-filter within the Manifest.xml like so...
<action android:name="foo.kung.fancywidget.TOUCHED" />;
... and it's inside the <receiver /> container for the widget, right next to the expected APPWIDGET_UPDATE entry that Android Studio helpfully adds.
I've ensured that the Layout being used has the clickable attribute set to true on every single element (just to be thorough) including the top-level RelativeLayout itself.
I've defined the static string for the thing at the top of the ExtraFancyWidget.class, like so...
public static final String TOUCHED = "foo.kung.fancywidget.TOUCHED";
...and according to what I've been reading it should come through as the broadcast via the onRecieve handler when done like this...
if (TOUCHED.equals(intent.getAction())) {
Log.i("onReceive", "Touch event received");
}
...but with a Log.d entry at the top of the onReceive handler I can tell I'm not getting any sort of signals through there at all aside from the heartbeat coming from the system service every ten seconds.
Lastly, I'm assigning the intent just like I've been reading about
private PendingIntent createOnClickIntent(Context context) {
Intent intent = new Intent(context, ExtraFancyWidget.class);
intent.setAction(TOUCHED); // WHY DOESN'T THIS WORK?!?!?
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
...and it's literally the last thing happening in onUpdate before the view is updated
views.setOnClickPendingIntent(R.id.TheWholeWidget, createOnClickIntent(context));
appWidgetManager.updateAppWidget(appWidgetId, views);
What could I possibly be overlooking or not seeing at this point? I don't know of anything else I'm supposed to be doing or changing to make this work... it just mysteriously ignores the user. ADB never shows me any of the log entries (although it does show a number of other, silly log messages so I know that's working) that would indicate the widget ever sees me tapping it.
The problem is in step 5, where you're creating the intent. Do NOT set a specific class to the intent, rather create it with only an action, like this:
Intent intent = new Intent();
intent.setAction(TOUCHED); // this should work now
or the shorthand
Intent intent = new Intent(TOUCHED);
If it still doesn't work, consider putting a requestCode other than 0 when building the PendingIntent. Depending on the Android version you're building from, there were some bugs when using just 0.
return PendingIntent.getBroadcast(context, 1000, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Lastly, if this broadcast will be used only within your app (which is highly probable), consider using a LocalBroadcastManager.
Related
I register a broadcastreceiver in AndroidMainfest.xml
And in my app, a function is that User can set a time and at this time the app will send a notification. I get the arguments User set ,and use alarmManager to set a task which will happened at the time user set.
But I find that GOOGLE API said that:
If there is already an alarm for this Intent scheduled (with the equality of two intents being defined by filterEquals(Intent)), then it will be removed and replaced by this one.
So if I want set two or more task,the Intent will be replaced , and at end I can only get one notification,it's not the result I want.
And then I found the intent was identified by action, data, type, class, and categories,
but I can't change action(the intent's action is the intent-filter's action was registred in the AndroidMainfest.xml ),but at the time that I change the other arguments I can't even receive a broadcast.
I thought there are four ways to solve this problem,but I only made one..
create lots of broadcastreceiver and register these in
AndroidMainfest.xml,and in this way I could change the intent's
action
register the broadcastreceiver in the program ,but I didn't
make it
use service + Timer class ..
To make two intent different without change action.
Any help will be appreciated!!
Intent intent = new Intent("aaa"); //there was a broadcastreceiver's intent-filter "aaa"
intent.putExtra("title", title);
intent.putExtra("table", "计划");
PendingIntent pi = PendingIntent.getBroadcast(Alarm.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager)getSystemService(Alarm.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+time, pi);
The code is in onClickListener{onClick(){}}
Instead of Broadcasting which is not letting you to set repeat alarm you can use the calendar Object and set the alarm at whatever desired time. Once the alarm gets ON, your code will run automatically and as many times you have set the alarm.
You can also take a help of the following tutorial. I am sure it will help you out somehow.
http://blog.mikesir87.io/2013/04/android-creating-an-alarm-with-alarmmanager/
My widget is comprised of 2 buttons and a listview displaying data. Most times, when the widget provider's onUpdate method is called, everything loads normally and everyone is happy.
However I've noticed sometimes after the update method is called, the widget just completely fails to load its data. The listview is empty, and all of the buttons are non-responsive. It's as if I initialized the layout into the widget, but none of the pending intents nor the adapter for list were set.
I logged everything and found that this isn't the case. The pending intents ARE created, as is the list adapter, every time, including the random time when it fails. For a long time I thought it had to do with how the list data is populated into the adapter, but seeing it work every single time, coupled with the fact that the button intents don't work as well leads me to believe the method updateAppWidget(ComponentName, RemoteViews) is what is failing. However, there are no error stacks to help me confirm this.
Here is the code which runs in a separate service called by the AppWidgetProvider's onUpdate method:
#SuppressWarnings("deprecation")
private void updateWidget(int[] ids) {
AppWidgetManager widgetManager = AppWidgetManager.getInstance(this);
int[] widgetIds = widgetManager.getAppWidgetIds(new ComponentName(this, WidgetReceiver.class));
for (int currentWidgetId : widgetIds) {
RemoteViews widget = new RemoteViews(this.getPackageName(), R.layout.widget_layout);
Intent intent = new Intent(this, WidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, currentWidgetId);
intent.putExtra("random", randomNumber);
randomNumber++;
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
widget.setRemoteAdapter(currentWidgetId, android.R.id.list, intent);
Intent clickIntent = new Intent(this, DetailsActivity.class);
PendingIntent pending = PendingIntent.getActivity(this, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
widget.setPendingIntentTemplate(android.R.id.list, pending);
Intent settingsIntent = new Intent(this, WidgetSettingsActivity.class);
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent settingsPending = PendingIntent.getActivity(this, 0, settingsIntent, PendingIntent.FLAG_UPDATE_CURRENT);
widget.setOnClickPendingIntent(R.id.widget_settings, settingsPending);
Intent mainIntent = new Intent(this, MainActivity.class);
PendingIntent mainPending = PendingIntent.getActivity(this, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
widget.setOnClickPendingIntent(R.id.widget_logo, mainPending);
ComponentName widgetCompName = new ComponentName(this, WidgetReceiver.class);
widgetManager.updateAppWidget(widgetCompName, widget);
}
}
What is most frustrating about this bug is that I can't reliably recreate it. Sometimes I get (un)lucky and it shows it ugly head, other times (the majority of the time) it works perfectly.
Another thing that I thought was interesting is that I've seen the same exact problem in Facebook's widget. Due to NDA I can't show a screen of my app when it fails, but I can show a screen of Facebook's identical problem:
When Facebook's widget looks like this, it has the exact same issues as mine. No data loaded, all buttons are unresponsive.
For both Facebook and my widget, a subsequent update interval will usually make the problem go away.
As much as I appreciate that I'm not the only one who is running into this, I still don't want to release until I fix this. Has anyone here run into this, and better yet found a solution, or even a cause of the problem? Thanks for helping.
EDIT: Something interesting. I ran an experiment where I set the update interval to a full day rather than every 30 minutes. My hypothesis was that maybe the update method wasn't the cause, it was something else which was causing the widget to become blank and unresponsive.
Sure enough, after about 2 hours, I checked my phone and the widget was dead, despite no update method being called. This would lead me to believe that something else is causing this problem, NOT widgetManager.updateAppWidget(widgetCompName, widget); like I previously thought.
I know that a configuration change can cause the widget to be rebuilt, and thus it is possible that it can fail. However, I already use the Service class's onConfigurationChanged method to reload the widget if necessary. Is there another case like configuration change which can cause the widget to destroy and recreate itself?
After a lot of blood, sweat, and tears I found the solution. I am going to hold off on confirming this answer for a few days to make sure that the error doesn't pop back up, but after a lot of observation I think it is resolved.
So I was right in my edited comments in the original question. It wasn't the update method of the AppWidgetManager that caused the problem, but rather some other Android process which caused the app widget to recreate itself. Unfortunately I couldn't isolate that trigger, but I did find a solution.
In my RemoteViewsFactory class (basically the wrapper which is used to load the data set), I had a block of code that looked like this:
RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.widget_layout);
if(mItems.size() == 0)
views.setViewVisibility(R.id.widget_empty, View.VISIBLE);
else
views.setViewVisibility(R.id.widget_empty, View.GONE);
AppWidgetManager manager = AppWidgetManager.getInstance(mContext);
ComponentName widgetCompName = new ComponentName(mContext, WidgetReceiver.class);
manager.updateAppWidget(widgetCompName, views);
Basically, if the list was empty, I showed a Loading message that took up the entire area where the listview is located. If the list wasn't empty, I'd just hide that message.
So this is what was happening: When the factory was destroyed (presumably for memory purposes) the widget itself was not. So all of the code which sets the intents for the data and stuff was not run. However, the factory was recreated, which ran that block of code which updated the app widget with the new remote views object. This remote views object didn't do any of the methods that you see in my onUpdate() method I originally posted, so all of its features didn't work.
Moral of the story: DON'T use updateAppWidget in your RemoteViewsFactory class! Now it may work if you run all the necessary lines, but I can't confirm that.
I have a widget that sets up a number of pendingIntents. All is working just fine on the widget as long as I only have a single widget on the home screen.
For the time being I decided that I was going to limit the user to only one of my home screen widgets. If one is already in use the ConfigureActivity gives the user an alertDialog box telling them they can't add a second one and then sets setResult(RESULT_CANCELED, null) and then finish().
This works just fine and keeps the user from being able to setup any further home screen widgets but for the one that is already in place most of my pendingIntents won't fire. Logcat tells me "Cannot send pending intent:"
Each of the pendingIntents is set to start up a service that is used to update the widget. If I remove the widget and reset it up it works just fine again, until I try to add a second widget. It is a bit baffling to me at this point. Anyone have any ideas as to why starting to add the 2nd widget but not completing it would cancel out my previous pendingIntents?
Incase it matters, the pendingIntents where setup with FLAG_CANCEL_CURRENT.
Here is the code used to set my intent and pendingIntents.
Intent intentDialog = new Intent(getBaseContext(), ScheduleActionsActivity.class);
intentDialog.putExtra("Action", ACTION_ENTER_SCHEDULE);
intentDialog.setAction("abc.hwRowOne");
intentDialog.putExtra("scheduleId", sch.getId());
intentDialog.putExtra("scheduleDescription", sch.getDescription());
PendingIntent pendingIntentDialog1 = PendingIntent.getActivity(getBaseContext(), 0, intentDialog, PendingIntent.FLAG_CANCEL_CURRENT);
views.setOnClickPendingIntent(R.id.hwRowOne, pendingIntentDialog1);
The line where intentDialog.setAction() changes to be unique for each row of the widget, so I thought that was making my pendingIntent unique as well.
I saw this answer last night after posting my question but I am not sure why/how it works and I am unsure where to put it in my area. It appears to set a unique data for each intent, but that is what I thought my .setAction() was doing. Multiple Instances Of Widget Only Updating Last widget
So to answer your question, no I am not using the appWidgetId at all in my intents, do I just need to pass it along as a putExtra() then on the intentDialog?
The answer to this question solved my issues:
Multiple Instances Of Widget Only Updating Last widget
I have no idea why adding the setData(uri) works but it did allow me to have more than one widget on the home screen now and the pendingIntents are unique and firing as I would expect them.
On the safe side I am also adding the appWidgetId to the intent and might need it later if I decide to allow more than one widget per device.
I have an app widget with a Button in it's layout.
When clicking the button, an intent is fired which calls my broadcast receiver.
It works just fine, but occasionally, after using the "Clear memory" button in the Task Manager, the widget gets stuck - clicking on it does nothing. But it can still receive updates from my app, if its running.
I'm not sure if the fact that the pending intent isn't fired is the memory clearing fault, or my fault.
Anyway, here's the code:
Registering the pending intent (onUpdate method of the app widget)
Intent intent = new Intent(context, ServiceControl.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget);
views.setOnClickPendingIntent(R.id.appwidgetbutton, pendingIntent);
and then updating the widgets with the views.
Here is the decleration of the app widget provider:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="72dp"
android:minHeight="72dp"
android:initialLayout="#layout/appwidget"
android:updatePeriodMillis="0">
</appwidget-provider>
I don't want the system to call widget updates, I only update it from my app itself.
So why does the pending intent stop firing?
Thanks in advance.
#Jong
#CommonsWare
Hi guys, I figured it out. Ofc this is an Android issue, the receiver should ALWAYS receive.
Now how to get around it? Obviously all the widgets are working, so there must have been a simple out there.
I read on SO somewhere (trying to find the guy) reminding us all that the widget class is actually extending a BroadcastReceiver.
So, you could register the widget (in the manifest) to receive the threats itself. Thus the entire system is self-contained in the class instance of AppWidgetProvider.
Now, for communicating back with the app, you can in the onReceive call any static class of your app, and LocalBroadcastManager won't fail you if the app is active. If it's not active, your buttons should be starting activities anyway!
Should you want the code, I can detail it.
I have a widget and 4 buttons on it. The buttons are actually shortcuts to other applications. I've set an onClick pending intent for each of the buttons with this code:
Intent i = context.getPackageManager().getLaunchIntentForPackage(s);
PendingIntent pi = PendingIntent.getActivity(context, 0, i, Intent.FLAG_ACTIVITY_NEW_TASK);
remoteViews.setOnClickPendingIntent(curIconId, pi);
Usually it works fine, but sometimes it doesn't do anything and in the logcat I see a SendIntentException with the message "Cannot send pending intent".
If I update the widget (i.e. setting the pending intent again), it works fine again.
Any ideas on why it breaks down sometimes?
Thanks
It seems that whenever you send RemoteViews to a widget, you need to completely specify the widget. If you only partially specify the widget, if something causes your widget to be updated (such as a screen rotation) your widget will only be recreated with the most recent RemoteViews.
Here's the link that pointed this out to me http://www.androiddiscuss.com/1-android-discuss/95040.html
The problem was that I sent too much data to the remote views (in my case, too many icons). So the update actually failed. There is a size limit on what you can send, don't remember where I read it.
The solution in my case was to update the images with resource URLs instead of the actual images.
I'm afraid that I can't answer you - I get the same behavior on one coworker's (Motorola) phone, but not other phones.
I can point out, though, that your PendingIntent.getActivity() flags parameter is wrong: that Intent.FLAG_ACTIVITY_NEW_TASK needs to go on Intent i. The getActivity() call wants one of FLAG_ONE_SHOT, FLAG_NO_CREATE, FLAG_CANCEL_CURRENT, FLAG_UPDATE_CURRENT, or one of the Intent.FILL_IN_ flags.
I can also mention that my clicks sometimes failed until I used PendingIntent.FLAG_CANCEL_CURRENT. This might be why adding "unique data" helps.
I had the same problem:
The widget didn't work after rotation. The problem in my widget was that I've overridden
the onReceive method and there touched the view to increment a text value. I solved the problem duplicating the setOnClickPendingIntent was in onUpdate, in the onReceive either.