I have an app with a widget that has a switch on it and a class that extends service to manage the content on the widget. I'm using RemoteViews to access the views to edit them but there are somethings that I want to do dependant on the sate of the switch. Is there a way to get the state of the switch? I've had a look through the api and couldn't find a way to do it.
Preemptively: I can't use findViewById because its not an activity
I can post code if needed.
For clarity the type of switch I want to use is one like this
Are you following the App Widgets Guide? I don't think the Switch widget is supported in RemoteViews.
Anyways, since you can't get any information on demand, you need to use setOnClickPendingIntent() and have the click send an intent to update your app widget. Stick an extra in the Intent specifying what state the button is in (or what state it would change to when clicked, either one). In the update logic, read the extra, change the UI, and set a new PendingIntent with the extra changed.
public class MyAppWidgetProvider extends AppWidgetProvider {
public static final String ACTION_UPDATE_SWITCH = "MyAppWidgetProvider.UPDATE_SWITCH";
public static final String EXTRA_SWITCH_ON = "MyAppWidgetProvider.EXTRA_SWITCH_ON";
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (ACTION_UPDATE_SWITCH.equals(action)) {
int id = intent.getIntExtra(AppWidgetManager.EXTRA_APPWDIGET_ID, 0);
if (id != 0) {
updateAppWidgetSwitch(context, intent, id);
}
}
super.onReceive(context, intent);
}
private void updateAppWidgetSwitch(Context context, Intent intent, int appWidgetId) {
boolean switchOn = intent.getBooleanExtra(EXTRA_SWITCH_ON, false);
// take some action based on the switch being clicked
RemoteViews views = new RemoteViews (context.getPackageName(), R.layout.app_widget_layout);
// normal RemoteViews stuff
// use switchOn var to set your switch state
// make new on click pending intent
Intent intent = new Intent(ACTION_UPDATE_SWITCH);
intent.putExtra(AppWidgetManager.EXTRA_APPWDIGET_ID, id);
intent.putExtra(EXTRA_SWITCH_ON, !switchOn); // new state
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.switch_id, pendingIntent);
// update widget
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
// other AppWidgetProvider methods ...
}
In your manifest, add this intent filter to your AppWidgetProvider's <receiver> element. Note that if you change the value of the action string above, make sure you change it here as well.
<intent-filter>
<action android:name="MyAppWidgetProvider.EXTRA_SWITCH_ON" />
</intent-filter>
Related
I want to know how to link widget and activity in android so that by clicking the widget only call some functios to do work but Not launch the activity something like when we click bluetooth in notification bar it just open bluetooth or close bluetooth without entering to that bluetooth app......
Thanks in advance :)
UPDATED ::
Below is Main Activity Code.
imageFlashlight.setOnClickListener(new View.OnClickListener() {
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Override
public void onClick(View view) {
if (finalHasCameraFlash) {
if (flashLightStatus)
flashLightOff();
else
flashLightOn();
} else {
Toast.makeText(MainActivity.this, "No flash available on your device",
Toast.LENGTH_SHORT).show();
}
}
});
The Code below is my widget code...
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
// Construct the RemoteViews object
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.app_widget);
// Construct an Intent object includes web adresss.
Intent intent = new Intent(context, MainActivity.class);
// In widget we are not allowing to use intents as usually. We have to use PendingIntent instead of 'startActivity'
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
// Here the basic operations the remote view can do.
views.setOnClickPendingIntent(R.id.tvWidget, pendingIntent);
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
UPDATED : As you can see i want to handle two functions by clicking the widget Without Entering to the activity just by tapping..
You can try sending Broadcast as
Intent broadcastIntent=new Intent(this,BroadcastReceiver.class);
//use .getBroadcast instead of getActivity and start a service when u receive this
//broadcast
PendingIntent pendingBroadcastIntent = PendingIntent.getBroadcast(context, 7, broadcastIntent, 0)
//set this pendingIntent on widget
the PendingIntent.getBroadcast() method will do the trick.
Remember to start the service when you receive this broadcast you can also set action on the intent using broadcastIntent.setAction(YOUR_ACTION).
Regards,
Karya
i learned today how to develop AppWidget and my question is, how can i run methods in the app widget?
example:
I have a button in the xml and i want the button calculate something.
-the part of create the widget works fine and now i want to add some methods to the button.
how does its works in "App Widgets"?
here is my code:
public class WorkClockWidget extends AppWidgetProvider {
MySharedPreferences shared;
RemoteViews views;
private Context context;
private String text;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
for (int i = 0; i < appWidgetIds.length; i++) {
views = new RemoteViews(context.getPackageName(), R.layout.clock_widget);
/// i have button in the "R.layout.clock_widget" .
//what i need to do if i want the button run the "someText()" method?
appWidgetManager.updateAppWidget(appWidgetIds[i], views);
}
}
//The method i want to run when i press on the button.
public String someText(){
System.out.println("Works!!!");
return "Test if this method works";
}
}
One more question:
If i want my widget add data to my database, i have to use contentProvider?
You can't call a method directly, but you can fire an Intent. One way would do this would be to have an Intent send a broadcast, using PendingIntent.getBroadcast. (I'm not sure you need the category in there, but that's how I do that in my own code, so I'm including it in this example.)
Intent intent = new Intent("com.myapp.button_press").addCategory("com.myapp");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.widget_button, pendingIntent);
Next you need a BroadcastReceiver to receive that broadcast. One sneaky way to do that is to use your AppWidgetProvider class as the receiver, since that what an app widget really is. You would have to modify the manifest entry for your app widget to include the Intent that you've created in onUpdate:
<receiver android:name=".widget.MintAppWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="com.myapp.button_press"/>
<category android:name="com.myapp"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/my_provider_info"/>
</receiver>
Also, when you override onReceive, be sure to call super.onReceive if the Intent is not your special Intent, so that the base AppWidgetProvider class can process the normal intent and call onUpdate.
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if ("com.myapp.button_press".equals(action)) {
// First handle your special intent action
someText();
} else {
// otherwise let Android call onUpdate
super.onReceive(context, intent);
}
}
I'm using widget with listview that has 3 types of items. For each type of item I should use different pending intents. Currently I'm using following code:
public class MyWidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager widgetManager, int[] widgetIds) {
for (int widgetId : widgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_view);
bindEmptyView(remoteViews);
bindRemoteAdapter(context, widgetId, remoteViews);
bindIntentTemplate(context, widgetId, remoteViews);
widgetManager.updateAppWidget(widgetId, remoteViews);
}
}
private void bindEmptyView(RemoteViews remoteViews) {
remoteViews.setEmptyView(android.R.id.list, android.R.id.empty);
}
private void bindRemoteAdapter(Context context, int widgetId, RemoteViews remoteViews) {
Intent intent = new Intent(context, MyViewService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
remoteViews.setRemoteAdapter(android.R.id.list, intent);
}
private void bindIntentTemplate(Context context, int widgetId, RemoteViews remoteViews) {
Intent intent = new Intent(context, MyActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent template = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setPendingIntentTemplate(android.R.id.list, template);
}
}
From this point I don't understand how can I invoke specific intent for different list items.
Hopefully I understood your issue well, so I'll try to explain in detail what's happening and how to handle clicks on list items on the widget.
I assume you already know that you have to implement a class which:
extends BroadcastReceiver implements RemoteViewsService.RemoteViewsFactory
This will serve as the "adapter" for your widget's ListView(let's call it MyListRemoteViewFactory).
If you want to handle item clicks on a widget's listView, you do the following things:
1) set a setPendingIntentTemplate in your AppWidgetProvider class
2) set a setOnClickFillInIntent in the MyListRemoteViewFactory overridden getViewAt(int position) method
NOW:
Doing step 1), you might wanna do something like:
final Intent serviceIntent = new Intent(context, MyListRemoteViewFactory.class);
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
views.setRemoteAdapter(R.id.widget_list_view, serviceIntent);
} else {
views.setRemoteAdapter(widgetId, R.id.widget_list_view, serviceIntent);
}
// Individuals items of a collection cannot set up their own pending intents. Instead, the collection as a whole sets up a pending intent template and the individual
// items set a fillInIntent to create unique behavior on an item-by-item basis.
Intent listItemClickIntent = new Intent(context, MyWidgetProvider.class); // This is the name of your AppWidgetProvider class
// Set the action for the intent. When the user touches a particular view, it will have the effect of broadcasting an action
listItemClickIntent.setAction(context.getString("com.example.list.item.click"));
listItemClickIntent.setData(Uri.parse(listItemClickIntent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent clickPendingIntent = PendingIntent.getBroadcast(context, 0, listItemClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setPendingIntentTemplate(R.id.widget_list_view, clickPendingIntent);
You can place the above code snippet after wherever you initialize your RemoteViews object:
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.layout_widget);)
So right now you have your pendingIntentTemplate ready. One other thing to do is to implement the class' onReceive method, so you can decide what to do when an action for the above case occurred. So you'll do something like:
#Override
public void onReceive(Context context, Intent intent) {
// Called on every broadcast and before each of the above callback methods.
super.onReceive(context, intent);
ComponentName name = new ComponentName(context, WidgetProvider.class);
int[] appWidgetIds = AppWidgetManager.getInstance(context).getAppWidgetIds(name);
if (appWidgetIds == null || appWidgetIds.length == 0) {
return;
}
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
if (intent.getAction().equals("com.example.list.item.click") {
// This is where you have to decide what you'll do depending on which list item was tapped. BUT this depends on the fill Intent set in the MyListRemoteViewFactory class's getViewAt() method
// I'll jump straight to the logic here, but once you're done reading this post, get back here to understand the logic because this is the key here. But simple as hell.
int simpleDecidingFactor = intent.getIntExtra("SELECTED_ITEM", 0)
if (simpleDecidingFactor != 0) {
if (simpleDecidingFactor == 1) {
// Do something for the first case. Probably open activity2
} else if (simpleDecidingFactor == 2) {
// Do something for the second case. Probably open activity2
} else if (simpleDecidingFactor == 3) {
// Do something for the second case. Probably open activity3
}
}
}
}
[Damn this'll be long]
DOING STEP 2) (from now on we talk about implementations in the MyListRemoteViewFactory class)
If you need 3 different items in the list, first you have to add this method to the MyListRemoteViewFactory(you're forced to override it anyway, the key is to return the number of views you have):
#Override
public int getViewTypeCount() {
return 3;
}
In the getViewAt() method you add your logic based on what, you decide what to display depending on your position. Something like:
#Override
public RemoteViews getViewAt(int position) {
if (position >= mItems.size()) {
return null;
}
RemoteViews views;
if (mItems.get(position).getViewType() == 0) {
views = new RemoteViews(mContext.getPackageName(), R.layout.list_item_first);
setUpItem(views, mItems.get(position), 1); // !!!! the 3rd parameter is very important here, as you'll expect this number in the MyWidgetProvider class' onReceive method. See the simpleDecidingFactor variable there.
} else if (mItems.get(position).getViewType() == 1) {
views = new RemoteViews(mContext.getPackageName(), R.layout.list_item_second);
setUpItem(views, mItems.get(position), 2);
} else {
views = new RemoteViews(mContext.getPackageName(), R.layout.list_item_third);
setUpItem(views, mItems.get(position), 3);
} // Or add whatever logic you have. Here, I supposed I have a field inside my object telling me what type my item is
return views;
}
And the setUpItem method might look something like:
private void setUpItem(RemoteViews views, MyObject object, int viewTypeKey) {
// This is where you set your clickFillInIntent. Without setting it, nothing'll be functional
Bundle extras = new Bundle();
extras.putInt("SELECTED_ITEM", viewTypeKey);
//extras.putParcelable("LIST_ITEM_OBJECT", object); // You may send your object as well if you need it
Intent fillInIntent = new Intent();
fillInIntent.putExtras(extras);
// You have your fillInIntent prepared, you only have to decide on what view to place it.
// I assume you have a Button on all 3 of your list item layouts with the id button_click. Let's place the Intent:
views.setOnClickFillInIntent(R.id.button_click, fillInIntent);
}
You might want to make sure you declared everything in the Manifest file as well. You have to declare your widgetprovider, your receiver for the list, and a service handling your Factoryclass. You should have something like:
<receiver
android:name=".MyWidgetProvider"
android:enabled="true"
android:label="My awesome widget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<action android:name="android.appwidget.action.APPWIDGET_DISABLED" />
<!-- You have to declare your used actions here, so the AppWidgetProvider knows what to listen for-->
<action android:name="com.example.list.item.click"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/widget_provider_info"/>
</receiver>
<!-- The service serving the RemoteViews to the collection widget -->
<service
android:name=".WidgetRemoteViewsService"
android:exported="false"
android:permission="android.permission.BIND_REMOTEVIEWS"/>
<receiver
android:name=".ui.widget.MyListRemoteViewFactory"
android:enabled="true"
android:exported="false">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<!-- You might want to use an action to notify the appwidget provider from the MyListRemoteViewFactory's onReceive method. This class extends a BroadcastReceiver, so you must implement it's onReceive(Context context, Intent intent) method. If you need help on this, let me know and I'll edit my answer with some example for that too -->
<action android:name="com.example.refresh.remote.views"/>
</intent-filter>
</receiver>
Ah, BTW the WidgetRemoteViewsService class should look like this:
public class WidgetNewsRemoteViewsService extends RemoteViewsService {
#Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new MyListRemoteViewFactory();
}
}
I guess this is pretty much it. I hope I didn't skip anything.
I have an Android application with a widget, that has buttons. This code works.
The buttons on the widget stop working when something happens, such as changing the language of the phone. I use shared preferences, so if the user reinstalls the app (without uninstalling), the buttons are working again and the settings remain the set ones.
I have noticed the Intents in my AppWidgetProvider class (code beneath this analysis) are not fired appropriately.
I added a Toast message to the Call1 class instantiated from AppWidgetProvider, but it doesn't display.
My UpdateService.java is just getting the set preferences and customizing the widget's appearance, so I don't think it could possibly be related to my issue.
My Main.java file merely consists of spinners and saves shared preferences, which means I select "Computer" in a spinner, so that the "Computer" text appears on the widget. It also does not disappear when I change the language of the phone, and neither do images. Therefore, I believe UpdateService.java must be ok.
Here is the AppWidgetProvider class:
public class HelloWidget extends AppWidgetProvider {
public static String ACTION_WIDGET_CONFIGURE = "ConfigureWidget";
public static String ACTION_WIDGET_CONFIGURE2 = "ConfigureWidget";
public static String ACTION_WIDGET_RECEIVER = "ActionReceiverWidget";
public static String ACTION_WIDGET_RECEIVER2 = "ActionReceiverWidget";
private static final int REQUEST_CODE_FOUR = 40;
private static final int REQUEST_CODE_FIVE = 50;
private static final int REQUEST_CODE_SIX = 60;
private static final int REQUEST_CODE_SEVEN = 70;
private static final int REQUEST_CODE_EIGHT = 80;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
context.startService(new Intent(context, UpdateService.class));
//Intent widgetUpdateIntent = new Intent(context, UpdateService.class);
//context.startService(widgetUpdateIntent );
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetmain2);
//P1 starts Call1.class
Intent configIntent4 = new Intent(context, Call1.class);
configIntent4.setAction(ACTION_WIDGET_CONFIGURE);
PendingIntent configPendingIntent4 = PendingIntent.getActivity(context, REQUEST_CODE_FOUR, configIntent4, 0);
remoteViews.setOnClickPendingIntent(R.id.ImageView01, configPendingIntent4);
//P2 starts Call2.class
Intent configIntent5 = new Intent(context, Call2.class);
configIntent5.setAction(ACTION_WIDGET_CONFIGURE);
PendingIntent configPendingIntent5 = PendingIntent.getActivity(context, REQUEST_CODE_FIVE, configIntent5, 0);
remoteViews.setOnClickPendingIntent(R.id.ImageView02, configPendingIntent5);
//P3 starts Call3.class
Intent configIntent6 = new Intent(context, Call3.class);
configIntent6.setAction(ACTION_WIDGET_CONFIGURE);
PendingIntent configPendingIntent6 = PendingIntent.getActivity(context, REQUEST_CODE_SIX, configIntent6, 0);
remoteViews.setOnClickPendingIntent(R.id.ImageView03, configPendingIntent6);
//P4 starts Call4.class
Intent configIntent7 = new Intent(context, Call4.class);
configIntent7.setAction(ACTION_WIDGET_CONFIGURE);
PendingIntent configPendingIntent7 = PendingIntent.getActivity(context, REQUEST_CODE_SEVEN, configIntent7, 0);
remoteViews.setOnClickPendingIntent(R.id.ImageView04, configPendingIntent7);
//P5 starts Call5.class
Intent configIntent8 = new Intent(context, Call5.class);
configIntent8.setAction(ACTION_WIDGET_CONFIGURE);
PendingIntent configPendingIntent8 = PendingIntent.getActivity(context, REQUEST_CODE_EIGHT, configIntent8, 0);
remoteViews.setOnClickPendingIntent(R.id.ImageView05, configPendingIntent8);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action))
{
final int appWidgetId = intent.getExtras().getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,AppWidgetManager.INVALID_APPWIDGET_ID);
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID)
{
this.onDeleted(context, new int[] { appWidgetId });
}
}
else
{
if (intent.getAction().equals(ACTION_WIDGET_RECEIVER))
{
String msg = "null";
try {
msg = intent.getStringExtra("msg");
} catch (NullPointerException e) {
//Log.e("Error", "msg = null");
}
}
super.onReceive(context, intent);
}
}
}
I also have an EditPreferences.java, GlobalVars.java and some other now meaningless classes. The names of the classes speak for themselves.
One other thing. I also have a Widgetmain.java:
public class WidgetMain extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.widgetmain2);
}
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId)
{
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetmain2);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
}
Edit: How about this:
When I install this app on my colleague's ZTE Blade the textviews on the widget are not loaded with the appropriate text, just with the one determined in the strings.xml.
When I reinstall the app (without uninstalling), the textviews are loaded and everything is fine. This problem doesn't emerge on my HTC Desire HD.
The textviews are load in the aforementioned UpdateService.java like this (part of the code):
RemoteViews updateViews = new RemoteViews(this.getPackageName(), R.layout.main);
updateViews.setTextViewText(R.id.widget_textview, name);
ComponentName thisWidget = new ComponentName(this, HelloWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
Even if "name" is static (e.g. String name="Something"), that textview is still not loaded at the first install.
Try to update the RemoteViews with the click listeners whenever you create new instance by "new RemoteViews". Maybe the RemoteViews are freshly loaded from the XML in some circumstances, therefor the click listeners needs to be re-assigned.
My UpdateService.java is just getting the set preferences and customizing the widget's appearance, so I don't think it could possibly be related to my issue.
It is possible it is related, in as much that you could use it to "refresh" the pending intent. I have a similar issue in my appwidget that an image button stops responding to clicks after some random run time (hours).
I found this thread:
AppWidget Button onClick stops working
And this quote:
The pending intent is "burned" after each use. You need to set it again. Or wait for the widget to get refreshed, then it happens, too, but that's probably not the desired way.
Given that the widget update time normally is set at many hours or days (mine is 86400000 milli seconds) in order to prevent the phone going out of suspend every so many minutes your widget will not often run onUpdate. It is possible that setting the pending intent ALSO in the update service will prevent the problem you describe.Each time the update service runs the pending intent is re-created.
I have today added this possible fix to my appwidget and I have to wait and see if the fix really works, but so far so good.
I added the following code in the update service' loop where it refreshes each widget:
for (int i=0; i<appWidgetIds.length; i++)
{
appWidgetId=appWidgetIds[i];
/* other stuff to do */
RemoteViews views=new RemoteViews(context.getPackageName(), R.layout.example_appwidget);
/* here you "refresh" the pending intent for the button */
Intent clickintent=new Intent("net.example.appwidget.ACTION_WIDGET_CLICK");
PendingIntent pendingIntentClick=PendingIntent.getBroadcast(context, 0, clickintent, 0);
views.setOnClickPendingIntent(R.id.example_appwidget_button, pendingIntentClick);
appWidgetManager.updateAppWidget(appWidgetId, views);
/* then tell the widget manager to update */
appWidgetManager.updateAppWidget(appWidgetId, views);
}
The problem is that you can't do a partiall update for a widget, you must set all the widget features, such as the set of PendingIntent's every time you push a new remoteView. (Partiall updates are only available for API14 and up...).
The reason your widgets are loosing their pendingIntents is that the android system saves the remoteView, and rebuilds your widget with it, in case it resets the widget (shortage of memmory, TaskManager/taskKiller in use, etc...), so you must set all the update code for the widget in the remoteView in your updateService. Otherwise, it's just won't set the pendingIntents again.
So just add the code setting the pendingIntents to the service and your problem will be solved =]
I think the PendingIntents may need a flag passed to them, maybe try changing:
PendingIntent.getActivity(context, REQUEST_CODE, configIntent, 0);
to:
PendingIntent.getActivity(context, REQUEST_CODE, configIntent, PendingIntent.FLAG_UPDATE_CURRENT);
From the PendingIntent documentation, I think code '0' is undefined. In this case FLAG_UPDATE_CURRENT would work best, as you probably want to update the Intent every time the button is clicked.
Given all the information you gave, I'd say your update method is not triggered properly when the preferences are changed.
I expect after so much tests, you have verified your Manifest file contains:
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
Have you confirmed onUpdate ever runs? It seems to me that if reinstalling the application without deinstalling solves your issues, it might be because it forces an update call.
After careful check, it turns out that ScanPlayGames has a point: the official documentation's example uses super.onUpdate(). Note that it uses it at the end of the method, but several examples on Internet state you're better served using it at the start of your method.
I've had that problem for long time. My widget has button #(onUpdate). The widget has a service for updates. The button on the widget stop working when something happens, like: changing the font, etc..
When i re-install the app, the button works again. Finally, I realized that i never called onUpdate in my Service class.
Calling onUpdate from the service class fixed the problem.
If someone still has this problem try setting the attribute android:updatePeriodMillis in your AppWidgetProviderInfo;
The operating system can kill the pending intent for various reasons and your buttons can stop to work. When you set this attribute, you are telling Android when it should call the onUpdate method in the AppWidgetProvider, so all pending intents will be re-created.
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
...
android:updatePeriodMillis="3600000">
</appwidget-provider>
After lots of efforts in doing this, i finally asking. I have widget with textview, button and imageview. both views are loaded from preference. I am able to start myActivityA when user clicks textview and myActivityB when user clicks imageview from widget. But not able to receive onclick event (in service) for button. I am doing this in following manner.
MyWidgetProvider class
final ComponentName serviceName = new ComponentName(context, "com.mypkg.MyService");
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_main);
Intent intent = new Intent(Service.TOGGLE_ACTION);
intent.setComponent(serviceName);
PendingIntent pendingIntent = PendingIntent.getService(context,0 /* no requestCode */, intent, 0 /* no flags */);
remoteViews.setOnClickPendingIntent(R.id.widget_button1, pendingIntent);
In MyService class,
// broadcast message receiver for commands to service.
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
String cmd = intent.getStringExtra("Command");
if (TOGGLE_ACTION.equals(action))
{
m_iKey = intent.getIntExtra("MyService.MyKey", 0);
// call functions defined in MyService.
}
}
};
My problem is i am not able to receive TOGGLE_ACTION in service at all. OnStartCommand is called in MyService,
Can any one suggest what could be issue?
What exactly i want from button click event is....
If service is not started (service is started in first activity, life span is now limited to activity) then start it... and TOGGLE_ACTION should be executed.
IF service is already started(means my activity is minimized) then TOGGLE_ACTION should be executed.
Is there any other way to achieve this?
Or other way to pass some data and call some routine of service from widget?
try this,
Intent intent= new Intent(context, myActivityB.class);
PendingIntent pending=PendingIntent.getActivity(context, 0 , intent,PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.button, pending);
R.id.button this is your button Id.