I have started implementing a widget into an application of mine and I am having some trouble getting the data from the application to the widget.
Here is the code for my widget:
public class ElectricityMonitorWidgetProvider extends AppWidgetProvider {
public static final String WIDGETTAG = "WidgetMood";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
System.out.println("Widget onUpdate called..");
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];
System.out.println("In loop. App widget id: "+appWidgetId);
}
}
}
In my widget I have only a textview that I want updated with a number from my application. The application gets this number via a web request.
In the onUpdate() method which runs periodically I want to be able to update that one textview.
I am not sure how to achieve this.
Do I need to implement a helper class that the widget calls and inside that helper class I have the same method as the application uses to receive the data it uses or?
This is my first time implementing a widget. I have read through the guide on widgets on developer.android.com and that helped me get the widget to show on my phone. The only thing I am missing is the functionality that I describe.
Thanks for any help.
put this in your widget updating loop
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
int value = i; //replace with actual value eg from SharedPreferences
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.your_widget);
remoteViews.setTextViewText(R.id.text_view, String.valueOf(value)); //assumed that R.layout.your_widget contains a TextView with R.id_text_view id
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
System.out.println("In loop. App widget id: "+appWidgetId);
}
Please note, that this code does not specify any action upon user touching the widget (generally you need to specify a PendingIntent and hook it with RemoteViews.setOnClickPendingIntent method).
Also, I would suggest that you update your widget when needed and not periodically e.g. your app could send custom Intent to your AppWidgetProvider, which would handle it in overridden onReceive() and update RemoteViews on widgets.
Related
I have simple widget for showing forecast and I am trying to update it after my request to the API I use is done. But I get this freaky error : "E/Finsky: [1] com.google.android.finsky.widget.a.onReceive(146): Refusing to update all widgets; need to narrow scope"
private void updateWidgets(Context context){
Intent updateIntent = new Intent();
updateIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
context.sendBroadcast(updateIntent);
}
I have tried to call this method in the activity, in the end of the service, or in a broadcast receiver(listening for broadcast from the service after its finished). Could anyone give me a suggestion what could I do wrong?
Widget class:
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for(int i = 0; i<appWidgetIds.length; i++){
int currentWidgetId = appWidgetIds[i];
RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.weathy_widget_info);
//Some other unrelated stuff
appWidgetManager.updateAppWidget(currentWidgetId,views);
}
}
The method triggers the update, but it wont update the widget, and I dont know what is the problem. Also will it be possible to be something with working with SQLite in the onUpdate()?
I am not going to delete this question as commons brings up some excellent points below, but I did rework the code and ask the question differently here: How do I retrieve shared preferences data in a Widget Service class without passing in incorrect default values or getting null pointer errors?
I am working on an app that takes a user's input choices and passes them to a widget. It is currently running a service to manage it and it works well, but I cannot figure out how to pass a String from one to the other effectively. Here is my code so far:
//First Widget config is called:
public class WidgetConfig extends Activity{
//Stuff happens here to get data from EditTexts and spinners and converts
//them to strings.
//Eventually a button is pressed which enters all the information:
public void onClick(View v) {
//I have already tried shared preferences like this:
//This was intended to give the shared preferences a unique identifier.
//It does not work for what I am trying to do
String str = Integer.toString(appWidgetId);
sp.putString(editor, str + "::" + "username", user_name);
//The appWidgetID was unique and so I thought it would work as an
//identifier for shared prefs.
//This is the intent for opening the provider
Intent intentUpdate = new Intent(context, MailWidgetProvider.class);
//I also attempted to put items here:
intentUpdate.putExtra("username", user_name);
//I left out the rest of the pending update code as it is irrelevant to this.
}
}
//Next the AppWidgetProvider is called
public class MailWidgetProvider extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
ComponentName thisWidget = new ComponentName(context,
MailWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
//This is the intent to open up and run the service
Intent intent = new Intent(context.getApplicationContext(),
MailWidgetUpdateService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);
context.startService(intent);
}
}
//Service Class
public class MailWidgetUpdateService extends Service {
public void onStart(Intent intent, int startId) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this
.getApplicationContext());
int[] allWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
ComponentName thisWidget = new ComponentName(getApplicationContext(),
MailWidgetProvider.class);
int[] allWidgetIds2 = appWidgetManager.getAppWidgetIds(thisWidget);
//Loop through the IDs
for (int widgetId : allWidgetIds) {
int awid = widgetId;
String str = Integer.toString(widgetId);
String user_name = sp.getString(settings, str + "::" + "chosen_accout_string", "Loading...");
Log.d(TRACKING_USERNAME, user_name);
/*
Issue Here, see explanation below
*/
}
}
How do I retrieve the extras in the Widget Provider class from the widget config class and how do I go about passing them on to the service after receiving them?
You start by not doing much of any of that.
Your AppWidgetProvider is merely one means of updating the app widget contents, one that will specifically be used by Android when your app widget is added and on periodic updates as requested by your app widget metadata. Moreover, bear in mind that an instance of your AppWidgetProvider is used just once and is then discarded.
If you want to update your app widget in other places, go update the app widget, by creating the RemoteViews and giving them to an AppWidgetManager. Your AppWidgetProvider has nothing to do with it. To quote the documentation:
When an App Widget uses a configuration Activity, it is the responsibility of the Activity to update the App Widget when configuration is complete. You can do so by requesting an update directly from the AppWidgetManager.
If you want to have a common implementation of the update-the-app-widget logic, put that is some common class that is used by your configuration Activity, your AppWidgetProvider, and anything else that needs to update the app widget contents.
So, when the user configures the app widget through the activity, you need to:
update the app widget yourself via the AppWidgetManager, and
hold onto the configuration data (in a database, SharedPreferences, or other sort of file) so that it can be used for future updates
I am trying to make a widget for my app, and I want it to be with a buttonm progress bar and text view, which would later be changed, so I need to somehow work with the views themselves, like I can from an activity, show a view, hide a view, change a button background etc...
I tried tweaking with the sample code from the Android documentations, but I can only start an activity with this
public class WidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
final int N = appWidgetIds.length;
for (int i = 0; i < N; i++) {
int appWidgetId = appWidgetIds[i];
Intent intent = new Intent(context, ActivityMain.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
views.setOnClickPendingIntent(R.id.sync_button, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
}
}
Does the widget need to be connected to an activity in order to make actions (connect to the internet, write in the database, write in the objects of the apps instance (if there is one))? And can I work with views the way I am asking?
Outside of setting on-click PendingIntents on views, there isn't much you can do inside of a widget. The standard recipe is:
Make a PendingIntent that sends a broadcast (PendingIntent.getBroadcast()) and set it on the appropriate view in your widget.
In the BroadcastReceiver that receives the intent, you update the widget to show a spinner and start a Service to do whatever long-running work you want to do (e.g connect to the internet).
Once that work is done, you can update your widget again and remove the spinner.
WHAT I NEED
I'm developing a custom Android launcher for a company, which will be installed on this company's tablets and therefore won't be published on any store.
It's basically a grid with widgets managed remotely (by a background service we're creating), which means that the app should decide which widgets to add or remove whenever the user opens it.
WHAT I HAVE
I'm using the following code to add the widgets:
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getContext());
AppWidgetHost appWidgetHost = new AppWidgetHost(getContext(), APPWIDGET_HOST_ID);
appWidgetHost.startListening();
List<AppWidgetProviderInfo> appWidgetInfos appWidgetInfos = appWidgetManager.getInstalledProviders();
for(int j = 0; j < appWidgetInfos.size(); j++)
{
if (appWidgetInfos.get(j).provider.getPackageName().equals(widgetPackage))
{
// Allocates an id
int appWidgetId = appWidgetHost.allocateAppWidgetId();
// Gets the app widget info
AppWidgetProviderInfo appWidgetProviderInfo = appWidgetInfos.get(j);
// Creates Widget
AppWidgetHostView hostView = appWidgetHost.createView(getContext(), appWidgetId, appWidgetProviderInfo);
hostView.setAppWidget(appWidgetId, appWidgetProviderInfo);
// Insers the view to the layout grid
insertView(row, column, rowspan, columnspan, hostView);
//
break;
}
}
And it works just fine. The widget shows and it's buttons respond to user touch.
Here's one of the the widgets onUpdate:
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
// Perform this loop procedure for each App Widget that belongs to this provider
for (int appWidgetId : appWidgetIds)
{
// Create an Intent to launch ExampleActivity
Intent launchAppIntent = new Intent(context, MapActivity.class);
PendingIntent launchApp = PendingIntent.getActivity(context, 0, launchAppIntent, 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_layout);
views.setOnClickPendingIntent(R.id.widget_layout, launchApp);
// Tell the AppWidgetManager to perform an update on the current app widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
THE PROBLEM
One of the widgets (shown above) calls an Intent to start it's fullscreen Activity when clicked, and this doesn't work on my custom launcher, though it works perfectly on the default home launcher.
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