I have implemented a widget with an ImageButton and a TextView. That ImageButton launch an activity when its clicked. This activity updates the widget text with what the user writes on the activity EditText. Now the problem is that I only know how to get the ids like this:
for (int widgetId : allWidgetIds) {
// Create some random data
int number = (new Random().nextInt(100));
RemoteViews remoteViews = new RemoteViews(getApplicationContext()
.getPackageName(), R.layout.widget_layout);
Log.w("WidgetExample", String.valueOf(number));
//Here I should set text from edit text, but I'm using a random for testing.
remoteViews.setTextViewText(R.id.textView1,
"Random: " + String.valueOf(number));
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
This code will obviously change the data of all the ids, since its inside a for. Is there anyway that I can past the clicked widgetId with my intent, so I can eliminate that for? This is my widgetProvider:
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// Get all ids
ComponentName thisWidget = new ComponentName(context, Widget.class);
allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
for (int widgetId : allWidgetIds) {
Intent i = new Intent(context, WidgetSetup.class);
i.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);
i.setFlags(i.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
// Create some random data
int number = (new Random().nextInt(100));
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_layout);
// Set the text
remoteViews.setTextViewText(R.id.textView1, String.valueOf(number));
Intent active = new Intent(context, Widget.class);
active.setAction(ACTION_WIDGET_REFRESH);
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
remoteViews.setOnClickPendingIntent(R.id.imageButton1, actionPendingIntent);
active = new Intent(context, Widget.class);
active.setAction(ACTION_WIDGET_SETTINGS);
actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
remoteViews.setOnClickPendingIntent(R.id.imageButton2, actionPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
}
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_WIDGET_REFRESH)) {
//Log.i("onReceive", ACTION_WIDGET_REFRESH);
Intent i = new Intent(context, MainActivity.class);
i.setFlags(i.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
} else if (intent.getAction().equals(ACTION_WIDGET_SETTINGS)) {
//Log.i("onReceive", AppWidgetManager.EXTRA_APPWIDGET_ID);
Intent i = new Intent(context, WidgetSetup.class);
i.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);
//Here I tried to pass the widgetID with no luck.
//i.putExtra(pass widget id?);
i.setFlags(i.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
} else {
super.onReceive(context, intent);
}
}
Thanks
For a running example have a look at my code for MiniCallWidget lines 97 and 169.
Add an Extra to the intent you launch onClick and create in the for loop that specifies the ID of the current widget being setup.
Then retrieve the ID from the extras in the receiving Activity, and pass it back when you're done. Then use the returned ID to make changes only to one widget.
You can do this by having an if-else that also checks for a flag that tells whether or not you're updating only one widget.
Related
I'm creating an android widget that by clicking, every instance of the widget will go to a different url.
I'm having a problem sending the url from the 'onUpdate' to the 'onReceive' method.
The onUpdate code:
List<String> urls = Arrays.asList("google.com", "yahoo.com", "bing.com", "msn.com");
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
int cnt = 0;
// Get all ids
ComponentName thisWidget = new ComponentName(context,MyWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
for (int widgetId : allWidgetIds) {
// create some random data
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_layout);
Log.w("WidgetExample", urls.get(cnt));
// Set the text
remoteViews.setTextViewText(R.id.urlData, urls.get(cnt));
// Register an onClickListener
Intent intent = new Intent(context, MyWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
intent.putExtra("url",urls.get(cnt));
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.open, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
cnt++;
}
}
The onReceive code:
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
String TAG = "onReceive";
Bundle extras = intent.getExtras();
if(extras!=null) {
String url = extras.getString("url");
Log.d(TAG, "url is : "+url);
}else {
Log.d(TAG, "no url");
}
}
The problem is that i allwas get the same url (the last one in the list - 'msn.com').
Thank's allot
Avi
I think that this append because you override everytime the intent storage in PendingIntent because the requestCode doesn't change.
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,**0**, intent, PendingIntent.FLAG_UPDATE_CURRENT);
If you want set more PendingIntent you must change the requestCode (0 in your case)
Try
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, cnt, intent, PendingIntent.FLAG_UPDATE_CURRENT);
In this way all PendingIntents are different...
You're using PendingIntent.FLAG_UPDATE_CURRENT which, as the documentation states, "[...]if the described PendingIntent already exists, then keep it but replace its extra data with what is in this new Intent[...]"
So since you're trying to create multiple instances of the same class (PendingIntent) that flag is causing the unwanted behaviour. It is updating the previously instantiated object (the first time you called PendingIntent.getBroadcast(...) method) and changes the field(s) of that object and returns it. So that all your calls end up with the last extra (last URL you supplied) .
i would like to show data as list in my app widget. But i am new with app widget so can you help me with any example or any references? Here is my code:
public class AppWidget extends AppWidgetProvider {
protected static final String file_name ="user";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// Get all ids
ComponentName thisWidget = new ComponentName(context,
AppWidget.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
for (int widgetId : allWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_layout);
//Get user name
SharedPreferences settings = context.getSharedPreferences(file_name, 0);
String name = settings.getString("name", null);
//Get data from database
Database entry = new Database(context);
entry.open();
String[] myval=entry.planlist2(name);
entry.close();
// Set the text
//remoteViews.setTextViewText(R.id.app_name, String.valueOf(number));
remoteViews.setTextViewText(R.id.movie_name, Arrays.toString(myval).replaceAll("\\[|\\]", ""));
// Register an onClickListener
Intent intent = new Intent(context, AppWidget.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.app_name, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
}
Now i want to show myval data as list.
You need to:
Add a ListView to your RemoteViews layout
Create a RemoteViewsService and RemoteViewsFactory, basically serving in the role of what an ArrayAdapter might serve in a ListView in an activity
Call setRemoteAdapter() on your RemoteViews to teach it where the service is to be able to populate the ListView rows
This is covered in the documentation, and here is a sample app of mine demonstrating it.
I have homescreen widget with ListView
How can I obtain number of current Item which was clicked:
I've tried to do it by following way:
Insite custom class which extends
AppWidgetProvider
public void onUpdate(Context ctxt, AppWidgetManager mgr,
int[] appWidgetIds) {
for (int i=0; i<appWidgetIds.length; i++) {
Intent svcIntent=new Intent(ctxt, WidgetService.class);
svcIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
svcIntent.setData(Uri.parse(svcIntent.toUri(Intent.URI_INTENT_SCHEME)));
RemoteViews widget=new RemoteViews(ctxt.getPackageName(),
R.layout.widget);
widget.setRemoteAdapter(appWidgetIds[i], R.id.contacts,
svcIntent);
Intent clickIntent=new Intent(ctxt, AppWidget.class);
clickIntent.setAction(ACTION_WIDGET_REFRESH);
clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds[i]);
PendingIntent pi=PendingIntent.getBroadcast(ctxt, 0 , clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
widget.setPendingIntentTemplate(R.id.contacts, pi);
mgr.updateAppWidget(appWidgetIds[i], widget);
}
super.onUpdate(ctxt, mgr, appWidgetIds);
}
#Override
public void onReceive(Context context, Intent intent) {
Log.i("gotcha","receive");
if (intent.getAction().equals(ACTION_WIDGET_REFRESH)) {
Intent callIntent = new Intent(Intent.ACTION_CALL);
Bundle extras = intent.getExtras();
int extrass= -1;
if(extras!=null) {
extrass = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_IDS);
}
Log.i("receive", Integer.toString(extrass) ) ; // Here is I've expected to see Item ID
}
But It's not helped. I've just see different numbers (not Items ID)
How can I obtain them?
the problem is
PendingIntent pi=PendingIntent.getBroadcast(ctxt, 0 , clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Because if you always give 0 as requestCode, and the flag is FLAG_UPDATE_CURRENT, all the list items refer to the same PendingIntent which is update by the lasted.
So just give different requestCode to the PendingIntent will solve this problem, please use
PendingIntent pi=PendingIntent.getBroadcast(ctxt, appWidgetIds[i] , clickIntent, PendingIntent.FLAG_UPDATE_CURRENT)`
instead.
And also set the OnClickFillIntent to the RemoteView in the adapter.getViewAt()
RemoteViews rv = new RemoteViews(pkg, R.layout.item);
Intent i = new Intent().putExtra("position", position);
rv.setOnClickFillInIntent(R.id.item, i);
Then you can retrieve the appwidget id , as well as the list item position.
Read it once I think you get what should we do
When using collections (eg. ListView, StackView etc.) in widgets, it is very costly to set PendingIntents on the individual items, and is hence not permitted. Instead this method should be used to set a single PendingIntent template on the collection, and individual items can differentiate their on-click behavior using setOnClickFillInIntent(int, Intent).
I have a widget, its setup so that when I click on it, it opens some settings in an activity.
views.setOnClickPendingIntent(R.id.btnActivate, pendingIntent);
This configures some settings for the application. What I want to achieve is to have the widget update its view to reflect the changed settings when the Activity I launch closes. Using the update interval or any other type of polling isn't appropriate for this.
I've seen a couple places here and in the android docs this code used:
appWidgetManager.updateAppWidget(mAppWidgetId, views);
But I don't know how to get the mAppWidgetId value. I tried following the example for a widget configuration activity here http://developer.android.com/guide/topics/appwidgets/index.html, but in the following code,
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
mAppWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
}
extras is always null, so I never get the AppWidgetID.
Ok, now I'm just rambling. What do you think I can do?
I finally found the answer I was looking for, it was in an overload of the updateAppWidget function.
appWidgetManager.updateAppWidget(new ComponentName(this.getPackageName(), Widget.class.getName()), views);
This let me access the widget without having to know the appWidgetID. My final code in my activity is then:
// Create an Intent to launch ExampleActivity
Intent intent = new Intent(this, Settings.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
views.setOnClickPendingIntent(R.id.btnActivate, pendingIntent);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
appWidgetManager.updateAppWidget(new ComponentName(this.getPackageName(), Widget.class.getName()), views);
finish();
I have to do all the same setup stuff I had to do in the onUpdate method of the Widget, but now every time I exit my activity the Widget is displaying the correct state.
There's another way to do it - pass the widget id in the pending intent that you use to start the activity:
Intent clickIntent=new Intent(context, MyConfigActivity.class);
clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
// you have the widgetId here, since it's your onUpdate
PendingIntent pendingIntent=PendingIntent
.getActivity(context, 0,
clickIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.btnActivate, pendingIntent);
Moreover, to avoid duplication of code from onUpdate(), you can broadcast an intent back to the AppWidgetProvider:
Intent intent = new Intent(this,MyAppWidgetProvider.class);
intent.setAction("android.appwidget.action.APPWIDGET_UPDATE");
// Use an array and EXTRA_APPWIDGET_IDS instead of AppWidgetManager.EXTRA_APPWIDGET_ID,
// since it seems the onUpdate() is only fired on that:
int[] ids = {widgetId};
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,ids);
sendBroadcast(intent);
I know this has been answered and accepted way ago. However while I was searching the same thing I came across an awesomely simple way to update the widget.
For future readers:
The best part, this works for all the instances of the widget and from any context (Activity, Service etc)
Heres the code,
Context context = this;
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_2x1);
ComponentName thisWidget = new ComponentName(context, MyWidget.class);
remoteViews.setTextViewText(R.id.my_text_view, "myText" + System.currentTimeMillis());
appWidgetManager.updateAppWidget(thisWidget, remoteViews);
Courtesy - Stuck :)
Instead of doing the call from your activity, I prefere to send a broad cast request to the widget for updating. The onUpdate method will be triggered and all widget layouts are passed.
Here is my code below:
1- sending broad cast from the activity:
Intent intent = new Intent(ctxt, MyAppWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
int[] ids = {R.layout.appwidget};
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,ids);
ctxt.sendBroadcast(intent);
and now, 2- implement the onUpdate method:
Intent i = new Intent(ctxt, Settings.class);
PendingIntent pi = PendingIntent.getActivity(ctxt, 0, i, 0);
for (int widgetId : allWidgetIds) {
RemoteViews remoteViews = new RemoteViews(ctxt.getPackageName(), widgetId);
remoteViews.setTextViewText(R.id.statusMsg, msg);
remoteViews.setOnClickPendingIntent(R.id.rootView, pi);
AppWidgetManager mngr = AppWidgetManager.getInstance(ctxt);
ComponentName wdgt = new ComponentName(ctxt, MyAppWidgetProvider.class);
mngr.updateAppWidget(wdgt, remoteViews);
}
That is it! I wish it helps you :)
RemoteViews view = new RemoteViews("pakagename", R.layout.widget_layout_name);
view.setTextViewText(R.id.textView_id, String.valueOf(hr + ":" + mi)); // for setting a textview
view.setCharSequence(R.id.PunchIn, "setText", "Punch In"); //for setting a button name
view.setInt(R.id.PunchIn, "setBackgroundResource", R.color.black); //for setting button color
ComponentName theWidget = new ComponentName(getActivity(), AppWidget_name.class);
AppWidgetManager manager = AppWidgetManager.getInstance(getActivity());
manager.updateAppWidget(theWidget, view);
I have a widget that you press and it then it will update the text on the widget. I have set an on click listener to launch another activity to perform the text update, But for some reason it only works temporarily and then it will become unresponsive and not do anything when pressed. Does anyone know why it might be doing that? i have posted my widget code below in case it is helpful.
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) {
thisWidget = new ComponentName(context, MemWidget.class);
Intent intent = new Intent(context, updatewidget.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
// Get the layout for the App Widget and attach an on-click listener to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
views.setOnClickPendingIntent(R.id.ImageButton01, pendingIntent);
// Tell the AppWidgetManager to perform an update on the current App Widget
appWidgetManager.updateAppWidget(thisWidget, views);
}
#Override
public void onReceive(Context context, Intent intent)
{
appWidgetManager = AppWidgetManager.getInstance(context);
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
thisWidget = new ComponentName(context, MemWidget.class);
// v1.5 fix that doesn't call onDelete Action
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
{
super.onReceive(context, intent);
}
}
Here is code that is called from my activity
thisWidget = new ComponentName(this, MemWidget.class);
appWidgetManager = AppWidgetManager.getInstance(this);
remoteViews = new RemoteViews(this.getPackageName(), R.layout.widget);
//do work
remoteViews.setTextViewText(R.id.ImageButton01,"setting text here");
appWidgetManager.updateAppWidget(thisWidget, remoteViews);
The onUpdate method there doesn't update any of the data in the RemoteViews other than the PendingIntent, so if that's ever called, the widget will revert to the state defined in R.layout.widget.
Do you have the code that calls updateAppWidget after the user interaction? That might help too.
Also, if the update is inline and doesn't require any UI, you don't need to launch an activity to do that update. It's more efficient and won't disrupt the back stack if your PendingIntent is for a broadcast receiver instead, using PendingIntent.getBroadcast. You can use the same BroadcastReceiver that is your app widget provider. You don't need another one.
Update: (I can't reply below because the text is too long)
I'd make a function like this, and call it from your activity from onUpdate(). You'll need to save text somewhere so you can also pass it in from onUpdate(). Otherwise it will revert the text to the default in R.layout.widget.
void updateWidget(Context context, CharSequence text) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
remoteViews.setOnClickPendingIntent(R.id.ImageButton01, pendingIntent);
remoteViews.setTextViewText(R.id.ImageButton01, text);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName thisWidget = new ComponentName(context, MemWidget.class);
appWidgetManager.updateAppWidget(thisWidget, remoteViews);
}