I have a very weird problem with my AppWidget in Android 4.1.1. I'm currently developing a music player application and its widget. Widget must be updated when song changes, player starts and stops. It has a ListView which must be in sync with playlist in application.
Prior to Jelly Bean everything was working fine. After my test device is upgraded from 4.0.3 to 4.1.1, whenever widget is programmatically forced to update, layout of Android's Messaging widget is set to my widget for a few seconds! I also checked this case in emulator with 4.1, which works fine.
I use that piece of code to force my widget to update:
AppWidgetManager man = AppWidgetManager.getInstance(applicationContext);
int[] ids = man.getAppWidgetIds(
new ComponentName(applicationContext, MuzikLargeWidgetProvider.class));
Intent updateIntent = new Intent();
updateIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
applicationContext.sendBroadcast(updateIntent);
And here is my onUpdate method
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
this.context = context;
this.appWidgetManager = appWidgetManager;
ComponentName thisWidget = new ComponentName(context,
MuzikLargeWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
for (int appWidgetId : allWidgetIds) {
Intent svcIntent = new Intent(context, UpdateService.class);
svcIntent.putExtra(WidgetActions.DATA_WIDGET_ID, appWidgetId);
context.startService(svcIntent);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
I'm using a service (UpdateService), which is a static inner class, to update my widget:
public static class UpdateService extends IntentService {
public UpdateService() {
super("UpdateService");
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
public RemoteViews buildUpdate(Context context, int widgetId) {
final RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.muzikwidget_large);
return views;
}
#Override
protected void onHandleIntent(Intent intent) {
// Build the widget update
RemoteViews updateViews = buildUpdate(this, intent.getIntExtra(WidgetActions.DATA_WIDGET_ID, 0));
// Push update for this widget to the home screen
ComponentName thisWidget = new ComponentName(this, MuzikLargeWidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
}
}
buildUpdate method does a few more things (setting intents for widget's buttons, setting textviews, etc.) but they are not related with my problem. I'm having this problem on a Asus TF 300 TG tablet (not rooted).
Any help is appreciated.
The problem is resolved. I modified the code piece which I use to force widget to update. This is working:
AppWidgetManager man = AppWidgetManager.getInstance(applicationContext);
int[] ids = man.getAppWidgetIds(
new ComponentName(applicationContext, MuzikLargeWidgetProvider.class));
for (int appWidgetId : ids) {
Intent svcIntent = new Intent(applicationContext, UpdateService.class);
svcIntent.putExtra(WidgetActions.DATA_WIDGET_ID, appWidgetId);
applicationContext.startService(svcIntent);
}
Widget's onUpdate method is not called and UpdateService is invoked directly. It seems like sendBroadcast should be avoided sometimes.
EDIT
If you need to use broadcast to update your widget, this seems to be a proper way to accomplish that:
AppWidgetManager man = AppWidgetManager.getInstance(applicationContext);
int[] ids = man.getAppWidgetIds(
new ComponentName(applicationContext, MuzikLargeWidgetProvider.class));
Intent updateIntent = new Intent(applicationContext, MuzikLargeWidgetProvider.class);
updateIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
applicationContext.sendBroadcast(updateIntent);
Related
Good Day! I want to add text from inside application to widget, i have a main activity and it has list view and lots of text contents and it has a button to add the text to widget via shared preference, it's works fine when i close the widget and recreate it only, otherwise it's not automatically refresh.if anyone know; how to solve this please help me. here i attached the widget code below.
public class WidgetMaster extends AppWidgetProvider {
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
Intent intentHome = new Intent(context, MainActivity.class);
PendingIntent pendingIntentHome = PendingIntent.getActivity(context, 0, intentHome, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_master);
views.setOnClickPendingIntent(R.id.wid_home, pendingIntentHome);
final SharedPreferences sharedPreferencestoWi = context.getSharedPreferences(String.valueOf(R.string.addTextToWidgetPref), MODE_PRIVATE);
String forWidget = sharedPreferencestoWi.getString("textToWidget", "");
String dum = "add from reading";
if(forWidget.equals("")){
views.setTextViewText(R.id.dum_appwidget_text, dum);
appWidgetManager.updateAppWidget(appWidgetId, views);
} else {
views.setTextViewText(R.id.appwidget_text, forWidget);
views.setViewVisibility(R.id.appwidget_text, 0);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
#Override
public void onEnabled(Context context) {
// Enter relevant functionality for when the first widget is created
}
#Override
public void onDisabled(Context context) {
// Enter relevant functionality for when the last widget is disabled
}
}
How do you update the widget from mainActivity?
In the widget configuration file you can specify a fixed update interval. The smallest update interval is 1800000 milliseconds (30 minutes).
But its better to update widget programmatically, to do this You should send a broadCast to update widget and use a
method like alarmManager or Handler for a repeating task for example you can use the following broadCast to update widget form mainActivity:
Intent intent = new Intent(this, WidgetMaster.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
int[] ids = AppWidgetManager.getInstance(getApplication())
.getAppWidgetIds(new ComponentName(getApplication(),WidgetMaster.class));
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
sendBroadcast(intent);
I created a class that downloads some text from the internet and I want to take that text and update a TextView in my widget. I know that the event (OnDownloadCompleteListener) is getting triggered because I'm Logging it but I can't figure out how to update the TextView from within that event. I know it's a newbie mistake, just not sure what I'm missing.
public class Widget extends AppWidgetProvider{
InternetText internettext; //Handles downloading the text from the internet
RemoteViews views;
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;
// Perform this loop procedure for each App Widget that belongs to this provider
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
// Create an Intent to launch ExampleActivity
Intent intent = new Intent(context, MainActivity.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
views = new RemoteViews(context.getPackageName(), R.layout.widget);
views.setOnClickPendingIntent(R.id.tvWidgetVerse, pendingIntent);
internettext = new InternetText(context);
internettext.setOnDownloadCompleteListener(new OnDownloadCompleteListener() {
#Override
public void onEvent() {
TheText thetext = internettext.downloadedText(); //The text object
Log.i("", "Widget Text Downloaded " + thetext.getText()); //This fires so I know we've downloaded the text
TextStyling textStyle = new TextStyling();
//*****THIS IS WHERE I'M HAVING THE PROBLEM********
views.setTextViewText(R.id.tvWidgetText, Html.fromHtml(textStyle.boldWords(thetext.getText()))); //this never updates
}
});
internettext.getText();
// Tell the AppWidgetManager to perform an update on the current app widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}
Ok, I figured it out. I have to call appWidgetManager.updateAppWidget() from within the event. I knew it was a no-brainer...
appWidgetManager.updateAppWidget(appWidgetId, views);
which also means I have to make appWidgetManager and appWidgetId final
public void onUpdate(Context context, final AppWidgetManager appWidgetManager, int[] appWidgetIds) {
...
}
i am developing android widget and i displayed some data on that widget from web service and its working fine.Hear is the code for that.
CODE
public class WatchWidget extends AppWidgetProvider{
private static final String TAG = "WatchWidget";
#Override
public void onUpdate( Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds ){
Log.i(TAG, "* onUpdate");
EBWeaterData ebwd=new EBWeaterData();
EBWeatreUtils.getWeatherFeeds(context, ebwd, "my url to get data");
RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.main);
RemoteViews newView = new RemoteViews(context.getPackageName(), R.layout.test);
//get data
String day=ebwd.getDay_1()+ebwd.getDay_suffix_1()+" "+ebwd.getDay_name_1();
String minmaxtemp="Max Temp "+ebwd.getDay_max_temp_1()+"°C "+"Min Temp "+ebwd.getDay_min_temp_1()+"°C";
//set dat to views
newView.setTextViewText(R.id.textView_day_name, day);
newView.setTextViewText(R.id.textView_minmaxtemp, minmaxtemp);
newView.setImageViewResource(R.id.ImageView_icon, R.drawable.sunny);
views.addView(R.id.view_container, newView);
ComponentName thisWidget = new ComponentName(context,WatchWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(thisWidget, views);
}
}
i have a button on that widget now i want to update the widget view with different values when user clicks on the button.I know I have to do with it pendingIntent like following.
Intent intent = new Intent(context, activitytogetNewdata.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.imageView1, pendingIntent);
But it will start another activity i dont want to go to anther activity.I just need to do is to update the same widget view with new values after button click.Any Idea??
I have found the problem of my code.when i going to update my view using pendingintent it will dynamically add view on each and every button click because i have used 2 layout hear so i solved the problem as follows.(i use one layout instead of using two)
CODE
public class WatchWidget extends AppWidgetProvider{
private static final String TAG = "WatchWidget";
#Override
public void onUpdate( Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds ){
Log.i(TAG, "* onUpdate");
EBWeaterData ebwd=new EBWeaterData();
EBWeatreUtils.getWeatherFeeds(context, ebwd, "my url to get data");
RemoteViews newView = new RemoteViews(context.getPackageName(), R.layout.test);
//get data
String day=ebwd.getDay_1()+ebwd.getDay_suffix_1()+" "+ebwd.getDay_name_1();
String minmaxtemp="Max Temp "+ebwd.getDay_max_temp_1()+"°C "+"Min Temp "+ebwd.getDay_min_temp_1()+"°C";
//set dat to views
newView.setTextViewText(R.id.textView_day_name, day);
newView.setTextViewText(R.id.textView_minmaxtemp, minmaxtemp);
newView.setImageViewResource(R.id.ImageView_icon, R.drawable.sunny);
ComponentName thisWidget = new ComponentName(context,WatchWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(thisWidget, newView);
}
Go through this tutorial.
Visit http://www.developer.com/ws/android/programming/Handling-User-Interaction-with-Android-App-Widgets-3837531.htm
They have solved the problem what you have.
When I'm running in debugging mode I can't seem to reach any breakpoints that are inside of the service, why is that?
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
context.startService(new Intent(context, UpdateService.class));
}
public static class UpdateService extends Service {
#Override
public void onStart(Intent intent, int startId) {
// Build the widget update for today
RemoteViews updateViews = buildUpdate(this);
// Push update for this widget to the home screen
ComponentName thisWidget = new ComponentName(this, WidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
}
public RemoteViews buildUpdate(Context context) {
return new RemoteViews(context.getPackageName(), R.id.widget_main_layout);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
The "onUpdate"-method is only executed if the widget is initalized (e.g. put on the homescreen) or the updatePeriodMillis are expired. If you want to execute the service e.g. by a click on the widget, you have to "attach" a pending intent like this:
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final Intent intent = new Intent(context, UpdateService.class);
PendingIntent pendingIntent = PendingIntent.getService(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....);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
for(int i=0,n=appWidgetIds.length;i<n;i++){
int appWidgetId = appWidgetIds[i];
appWidgetManager.updateAppWidget(appWidgetId , views);
}
(cleaned up version of a working widget).
The point is, that the onUpdate() method is really very seldom executed. The real interaction with a widget is specified through pending intents.
Your Service might not be registered in the manifest. Or your AppWidgetProvider might not be registered in the manifest.
You might want to think of not using a service for what you're doing. If it's just running the updateViews() once a day then consider just setting android:updatePeriodMillis to 86400000 in the XML file that's linked to your appwidget. Your XML file would look something like this:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="72dp"
android:maxWidth="72dp"
android:updatePeriodMillis="86400000" >
</appwidget-provider>
This will have android update your appwidget once a day without having a service run in the background that might get killed by a task killer that the user is running which then stops your widget from updating. Just a note, if you need it to update faster than every 30 minutes then android:updatePeriodMillis won't work (it's minimum value is 30 minutes) at that point I'd recommend using an AlarmManager since that'll use up less battery than a Service and also won't be killed by task killers.
I read some example in internet/book about the App Widget, a normal example to update the widget is in onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) method of AppWidgetProvider like this:
final int N = appWidgetIds.length;
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.mywidget_layout);
updateViews.setTextViewText(R.id.mytext, "updated text");
appWidgetManager.updateAppWidget(appWidgetId, updateViews);
}
It update each Widgets in a loop.
But now, I have to implement an App Widget, it is updated in BroadcastReceiver, onReceive(Context context, Intent intent) method since there are no int[] appWidgetIds passed in. So I implemented the code like this:
RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.mywidget_layout);
updateViews.setTextViewText(R.id.mytext, "updated text");
ComponentName myComponentName = new ComponentName(context, AndroidBatteryWidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(myComponentName, updateViews);
It didn't update widget one-by-one, but actually all widgets were updated at once. Even though it worked as I want, but I got confused as to why there is no need to update all widgets one-by-one as before.
What's the difference between two methods?
Can I send another broadcast from BroadcastReceiver.onReceive() to trigger AppWidgetProvider.onUpdate()? And how to?
It's the same thing. Update with ComponentName loops through all the ids like your first code block.
You can see it in the Android code here:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.4_r1.2/com/android/server/AppWidgetService.java#AppWidgetService.updateAppWidgetProvider%28android.content.ComponentName%2Candroid.widget.RemoteViews%29