2 buttons on widget - refresh and show activity - java

I have widget with 2 buttons button with id refresh and second button with id detailsInfo. First button should trigger widget update, second button to show detailed info (downloaded after widget refreshed).
This is weather widget. Refresh should trigger to download full weather data. Basic weather info should be displayed directly on widget, full data on details activity, launched on detailsInfo button click.
This is my code:
public class AppWidget extends AppWidgetProvider
{
public static String ACTION_DETAILS = "m.m.meteowidget.ACTION_DETAILS";
#Override
public void onReceive(Context context, Intent intent)
{
Log.i("onReceive",intent.getAction());
super.onReceive(context, intent);
}
#Override
public void onUpdate(Context ctxt, AppWidgetManager mgr, int[] appWidgetIds)
{
ComponentName me = new ComponentName(ctxt, AppWidget.class);
final RemoteViews updateViews = new RemoteViews(ctxt.getPackageName(), R.layout.widget_layout);
Intent intent = new Intent(ctxt, AppWidget.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pi = PendingIntent.getBroadcast(ctxt, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
updateViews.setOnClickPendingIntent(R.id.refresh, pi);
Intent intent2 = new Intent(ctxt, DetailsActivity.class);
intent2.setAction(ACTION_DETAILS);
PendingIntent di = PendingIntent.getActivity(ctxt, 0, intent2, 0);
updateViews.setOnClickPendingIntent(R.id.detailsInfo, di);
mgr.updateAppWidget(me, updateViews);
for (int i = 0; i < appWidgetIds.length; i++)
new WeatherInfo(updateViews,appWidgetIds[i],mgr).execute();
}
}
WeatherInfo is class that actually performs weather details download (it extends AsyncTask). As you can see, it gets my updateViews as constructor argument and then sets basic weather info displayed on my widget.
However, I have no idea how to display detailed info activity and pass detailed weather info to it. When I try to run my activity as shown above, my widget fails to load ("Problems loading widget"), without any exception that I can debug.
Any ideas what am I doing wrong?
[edit]
This seems to be (almost) ok:
Widget provider:
#Override
public void onReceive(Context context, Intent intent)
{
Log.i("onReceive",intent.getAction());
super.onReceive(context, intent);
}
#Override
public void onUpdate(Context ctxt, AppWidgetManager mgr, int[] appWidgetIds)
{
ComponentName me = new ComponentName(ctxt, AppWidget.class);
final RemoteViews updateViews = new RemoteViews(ctxt.getPackageName(), R.layout.widget_layout);
Intent intent = new Intent(ctxt, AppWidget.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pi = PendingIntent.getBroadcast(ctxt, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
updateViews.setOnClickPendingIntent(R.id.refresh, pi);
mgr.updateAppWidget(me, updateViews);
Intent intent2 = new Intent(ctxt, DetailsActivity.class);
intent2.setAction(ACTION_DETAILS);
PendingIntent di = PendingIntent.getActivity(ctxt, 0, intent2, 0);
updateViews.setOnClickPendingIntent(R.id.detailsInfo, di);
mgr.updateAppWidget(me, updateViews);
for (int i = 0; i < appWidgetIds.length; i++)
new WeatherInfo(updateViews,appWidgetIds[i],mgr).execute();
}
My async task:
public class WeatherInfo extends AsyncTask<String, Void, Map>
{
private RemoteViews views;
private int WidgetID;
private AppWidgetManager WidgetManager;
private DetailsActivity detailsActivity;
public WeatherInfo(RemoteViews views, int appWidgetID, AppWidgetManager appWidgetManager)
{
this.views = views;
this.WidgetID = appWidgetID;
this.WidgetManager = appWidgetManager;
}
#Override
protected Map doInBackground(String... strings)
{
Document doc = null;
try
{
doc = Jsoup.connect("http://meteo.uwb.edu.pl/").get();
}
catch (IOException e)
{
Log.e("","Connection failed: " + e.getMessage());
return null;
}
Elements tables = doc.select("td");
Elements headers = tables.get(2).select("b");
Elements vals = tables.get(3).select("b");
Map all = new LinkedHashMap();
for (int i=0;i<headers.size() ; i++)
all.put(headers.get(i).text(),vals.get(i).text());
Global.weatherInfo = all;
return all;
}
#Override
protected void onPostExecute(Map map)
{
if(map==null) return;
String txt = "";
String temp = (String) map.values().toArray()[0];
String hum = (String) map.values().toArray()[1];
String pressure = (String) map.values().toArray()[2];
String temp2 = "Odczuwalna: " + map.values().toArray()[3];
views.setTextViewText(R.id.info_temp, temp);
views.setTextViewText(R.id.info_temp2, temp2);
views.setTextViewText(R.id.info_hum, hum);
views.setTextViewText(R.id.info_pressure, pressure);
WidgetManager.updateAppWidget(WidgetID, views);
}
}
So I there is Global class with weatherInfo static field to share value between my thread and details activity.
However, there are 2 things that I have no idea how to fix:
- if activity is destroyed (removed from last app list in Android), after I press details button on my widget, activity is empty (bacause Global.weather info is null). I need to trigger widget refresh again and then lanunch my activity
- if I try to set Global.weatherInfo inside PostExecute method my widgets fails to show, without any exception thrown - why?
- I also tried to trigger my async task on create my activity. So i created second WeatherInfo constructor and passed DetailSActivity object into this, to be able to refresh my activity. Even if I don't use that second constructor, my widgets again fails to load without any exception.
I'm confused, can anybody tell me what's going on here? And how to solve my problem?

Create AsyncTask separately. Return the values from postexecute() method. Handler will give you better solution to handle response of asynktask.
Call this asynctask before populating the values in UI components. Handle error case separately either by dialogue box whatever you wish.
follow the link for clearance in handler

I made it working by storing data in sqlite database. So, after widget refresh, all data are saved to database. Database is also for data cache, so if there is no internet connection, widget disaplays cached data. The same cached data will be loaded to be displayed on my activity.

Related

Where should I set the logic to properly update my Widget?

Introduction
I'm trying to make a widget for school as homework. It is done for Android SO and coded in Java. I'm pretty new to this so I had to read A LOT of documentation, specially from their main Website for Android Devs, I'm not that interested in Android developing so a quick and solid answer is more than welcome.
Problem
I'm sending a name and ID from a WidgetConfig class (an Activity) using an Intent to the widget. This data is supposed to be written on the TextView from the widget layout but for some reason it does not.
In this code below you may see my attempt, if you tested the program from the repository that I will provide you are going to see that it is not properly updated.
So my question is:
How I can manage to update my widget from data sent from an activity?
public class FerrixWidget extends AppWidgetProvider {
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
CharSequence widgetText = context.getString(R.string.appwidget_text);
// Construct the RemoteViews object
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.ferrix_widget);
//views.setTextViewText(R.id.appwidget_text, widgetText);
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
RemoteViews spm = new RemoteViews(context.getPackageName(), R.layout.ferrix_widget);
spm.setTextViewText(R.id.alumnoName, "plsupdate");
}
//This is called to update the App Widget at intervals defined by the updatePeriodMillis attribute
// in the AppWidgetProviderInfo.
#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);
}
}
//This is called when an instance the App Widget is created for the first time.
#Override
public void onEnabled(Context context) {
// Enter relevant functionality for when the first widget is created
System.out.println("[INFO] FIRST INSTANCE CREATED");
}
//This is called when the last instance of your App Widget is deleted from the App Widget host.
#Override
public void onDisabled(Context context) {
// Enter relevant functionality for when the last widget is disabled
}
//This is called for every broadcast and before each of the above callback methods.
#Override
public void onReceive(Context context, Intent intent) {
RemoteViews controles = new RemoteViews(context.getPackageName(), R.layout.ferrix_widget);
String alumnoName = intent.getStringExtra("Name"); //Gets the intent withthe key "Name"
System.out.println("[INFO] Key: Name gets result: " + alumnoName);
controles.setTextViewText(R.id.alumnoName, alumnoName);
controles.setTextViewText(R.id.claseName, "DAM2");
System.out.println("[INFO] Context: " + context);
System.out.println("[INFO] Received: " + intent.getAction().toString());
}
}
Full code at
https://gitlab.com/JonaFerre/ferrixwidget
My goal
I want that when you set tup the widget on your screen it pops up a config screen (it does that already), then you insert your name and class, this data is sent over to the widget (via Intent) so it gets written and shown alongside the time of the day and some other stuff (That I will add once I get this properly solved)
Notes:
IDE: Android Studio
Andriod: v15
You can easily fix this by updating your widget in widget's onReceive:
Just send a broadcast from configuration activity:
loginBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SharedPreferences prefs = getSharedPreferences("WidgetPrefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("msg_" + widgetId, nameInput.getText().toString()); //???
editor.commit();
//Actualizar el widget tras la config
System.out.println("[INFO] Getting instance of WidgetConfig");
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(WidgetConfig.this);
System.out.println("[INFO] Updating the widget");
FerrixWidget.updateAppWidget(WidgetConfig.this, appWidgetManager, widgetId);
//Devolver un buen OK
Intent resultado = new Intent();
resultado.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
//System.out.println("[CONFIG INFO] Name text is: " + nameInput.getText().toString());
resultado.putExtra("Name", nameInput.getText().toString());
resultado.setAction("myUpdate");
ComponentName componentName = new ComponentName(getApplicationContext(), FerrixWidget.class);
resultado.setComponent(componentName);
sendBroadcast(resultado);
setResult(RESULT_OK, getIntent());
finish();
System.out.println("[INFO] OK devuelto!");
}
});
and on widget's onReceive
public void onReceive(Context context, Intent intent) {
if(intent.getAction() != null && intent.getAction().equals("myUpdate")) {
RemoteViews controles = new RemoteViews(context.getPackageName(), R.layout.ferrix_widget);
String alumnoName = intent.getStringExtra("Name"); //Gets the intent withthe key "Name"
int id = intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
System.out.println("[INFO] Key: Name gets result: " + alumnoName);
controles.setTextViewText(R.id.alumnoName, alumnoName);
controles.setTextViewText(R.id.claseName, "DAM2");
AppWidgetManager.getInstance(context).updateAppWidget(id, controles);
System.out.println("[INFO] Context: " + context);
System.out.println("[INFO] Received: " + intent.getAction().toString());
} else {
super.onReceive(context, intent);
}
}
But the better way is to save your data and widget's id in db/preference and just update on widget's side in onUpdate.
Please, just read official doc. There a lot of stuff that can helps you.

Android - Widget with configuration activity, how to keep initialised data on onUpdate

https://developer.android.com/guide/topics/appwidgets/index.html#Configuring
I read this and other tutorials to make Widget with user configuration.
I understood that ConfigurationActivity would initialise the widget, but the widget will be updated with "onUpdate" method in provider class.
Then my question is, how do I make the widget keep the initialised data when onUpdate is called?
For example, this is what I have in configurationActivity, that takes String input from user and set it in textView
public void onClick(View v) {
Context context = getApplicationContext();
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_haruhi);
//Get String input.
String test = String.valueOf(name.getText());
remoteViews.setTextViewText(R.id.sinceWhen, test);
//But after 30 min it calls default update??
appWidgetManager.updateAppWidget(mAppWidgetId, remoteViews);
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
}
And this is what I have in Widget provider
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int count = appWidgetIds.length;
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_haruhi);
long days=getDays(2011,5,15);
//This field needs to be updated periodically
remoteViews.setTextViewText(R.id.daysText, days+context.getString(R.string.days));
// But this field needs to stay the same as initialised configuration
remoteViews.setTextViewText(R.id.daysText, ????);
Intent intent = new Intent(context, HaruhiWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
for (int i = 0; i < count; i++) {
int widgetId = appWidgetIds[i];
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
One field in widget needs to be updated periodically
but the other one needs to show the String as input by user.
but by the time onUpdate is called, this widget gets reset and the initialise value would be lost.
How do I approach this problem? Should I save all initialised data in SharedPreference and load all the data everytime update signal is received?

Handling multiple buttons inside a widget - Android

I have a widget that display a simple text and 3 buttons:
Refresh (Picks another random string and displays it)
Copy (Copy the contents of the textview to the clipboard)
Share (Share the content of the textview to social media and such)
I already Got the refresh button setup and working just fine but I can't seem to figure out a way to handle the other two buttons
PS. I don't need the actual code I already know how to do the copying and sharing I just need to know how to handle click events
Here is my code so far:
Button copy_content;
Button share_content;
void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
/** Code below will be executed once the timer is over*/
String widgetText = RandQuotes[rand.nextInt(RandQuotes.length)];
// Construct the RemoteViews object
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.quotes_widget);
views.setTextViewText(R.id.sayings, widgetText);
// Instruct the widget manager to update the widget
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);
}
final int count = appWidgetIds.length;
for (int i = 0; i < count; i++) {
int widgetId = appWidgetIds[i];
String on_sayings = RandQuotes[rand.nextInt(RandQuotes.length)];
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.quotes_widget);
remoteViews.setTextViewText(R.id.sayings, on_sayings);
Intent intent = new Intent(context, HavamalWidget.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.switch_trigger, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
Just make pending intents on buttons with action. So in onReceive() just check action of intent and make some logic on that. If you make some long tasks, better to make all logic in IntentService.
Set pending intents on your buttons that are in RemoteViews like this:
public static final String ACTION_BUTTON_SHARE = "ACTION_BUTTON_SHARE";
public static final String ACTION_BUTTON_REFRESH = "ACTION_BUTTON_REFRESH";
Intent refreshIntent = new Intent(context, ExampleAppWidget.class)
.setAction(ACTION_BUTTON_REFRESH);
PendingIntent refreshPI = PendingIntent.getBroadcast(context, 0, refreshIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.refresh_button, refreshPI);
Intent shareIntent = new Intent(context, ExampleAppWidget.class)
.setAction(ACTION_BUTTON_SHARE);
PendingIntent sharePI = PendingIntent.getBroadcast(context, 0, shareIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.share_button, sharePI);
In ExampleAppWidget.class (that extends from AppWidgetProvider) override OnReceive() method
#Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
if (intent.getAction().equals(ACTION_BUTTON_REFRESH)) {
//make some logic here on button refresh
} else if (intent.getAction().equals(ACTION_BUTTON_SHARE)) {
//make some logic here on button share
} else {
super.onReceive(context, intent);
}
}
}

Android AppWidget's button click event not received after home launcher force stop

I have an app widget and the click event can be received in onUpdate() in my provider.
However, when I try to force close the home launcher, the click event is lost.
I even put breakpoints in all onEnabled(), onReceived()...etc: the connection seems to be lost.
As a result, how can I "re-connect" the button event?
WidgetProvider extends AppWidgetProvider:
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.d(TAG, "onUpdate()");
Log.d(TAG, "isLoading: " + CONSTANT.isLoading);
// update each of the widgets with the remote adapter from updateService
// Get all ids
ComponentName thisWidget = new ComponentName(context, ScoreWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
// Build the intent to call the service
Intent intent = new Intent(context.getApplicationContext(), UpdateWidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);
// Update the widgets via the service
context.startService(intent);
// super.onUpdate(context, appWidgetManager, appWidgetIds);
}
UpdateWidgetService extends Service:
#Override
public void onStart(Intent intent, int startId) {
Log.i(TAG, "Called");
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
for (int i = 0; i < appWidgetIds.length; ++i) {
// Here we setup the intent which points to the StackViewService which will
// provide the views for this collection.
Intent remoteViewsIntent = new Intent(this.getApplicationContext(), ScoreWidgetRemoteViewsService.class);
remoteViewsIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
// When intents are compared, the extras are ignored, so we need to embed the extras
// into the data so that the extras will not be ignored.
remoteViewsIntent.setData(Uri.parse(remoteViewsIntent.toUri(Intent.URI_INTENT_SCHEME)));
RemoteViews rv = new RemoteViews(this.getApplicationContext().getPackageName(), R.layout.widget_layout);
rv.setRemoteAdapter(appWidgetIds[i], R.id.score_list, remoteViewsIntent);
// Set the empty view to be displayed if the collection is empty. It must be a sibling
// view of the collection view.
rv.setEmptyView(R.id.score_list, R.id.empty_view);
// Bind the click intent for the refresh button on the widget
final Intent refreshIntent = new Intent(this.getApplicationContext(), ScoreWidgetProvider.class);
refreshIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
refreshIntent.setAction(ScoreWidgetProvider.REFRESH_ACTION);
final PendingIntent refreshPendingIntent = PendingIntent
.getBroadcast(this.getApplicationContext(), 0, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
rv.setOnClickPendingIntent(R.id.btn_refresh, refreshPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
// stopSelf();
super.onStart(intent, startId);
}
Make sure that you are using the correct context in your onStart function. Check out getApplicationContext in the onStart part of your code, passing in the wrong type of context can cause errors. Here is a link for more information: Context.

Can't update android widget from BroadcastReceiver

I want to code an app that displays some information in an widget, which should be updated from time to time. From time to time means, that I use an alarm timer to trigger a peroidic update. So heres the problem: intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); is null for the broadcast receivers intent.
Here's my widget provider:
public class MyWidgetProvider extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;
for(int i=0; i<N; i++) {
int widgetId = appWidgetIds[i];
RemoteViews rViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
Intent intent = new Intent(context.getApplicationContext(), TrafficService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
rViews.setTextViewText(R.id.TextView01, "" + System.currentTimeMillis());
appWidgetManager.updateAppWidget(widgetId, rViews);
}
}
}
and this is the BroadcastReceiver causing the problem:
public class TimeIntervalReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// set new alarm timer (code cut out)
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context.getApplicationContext());
// PROBLEM BELOW!
int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if(appWidgetIds == null) Log.d("TRAFFIC", "oh SHIT");
if(appWidgetIds != null && appWidgetIds.length > 0) {
for(int widgetId : appWidgetIds) {
RemoteViews rViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
rViews.setTextViewText(R.id.TextView01, "some data");
appWidgetManager.updateAppWidget(widgetId, rViews);
}
}
}
}
Is this even solveable?
Lol, I have my answer...
Simply replace:
int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
by
ComponentName thisWidget = new ComponentName(getApplicationContext(),MyWidgetProvider.class);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
When the system calls your implementation of AppWidgetProvider, it fills the intent with these extras.
But when it calls your broadcast receiver which has nothing to do with the widget, it does not fill this extra in the intent.
You will have to use another method to transfer the ID's. Maybe you could fill them in the Intent which is fired as the alarm fires?
so i changed the broadcast receiver to the following:
public class TimeIntervalReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Calendar c = Calendar.getInstance();
c.add(Calendar.SECOND, Config.UPDATE_RATE);
Date d = new Date(c.getTimeInMillis());
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context.getApplicationContext());
int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if(appWidgetIds == null) Log.d("TRAFFIC", "oh SHIT"); // triggers :(
if(appWidgetIds != null && appWidgetIds.length > 0) {
for(int widgetId : appWidgetIds) {
RemoteViews rViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
rViews.setTextViewText(R.id.TextView01, "someupdateddata");
appWidgetManager.updateAppWidget(widgetId, rViews);
}
}
Intent i = new Intent(context, TimeIntervalReceiver.class);
i.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); // here i'd add the existing widget ids...
PendingIntent sender = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager aManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
aManager.set(AlarmManager.RTC, d.getTime(), sender);
}
}
this still triggers - because the first alarm timer is (needs to be) set without knowing the widget-id-array. (e.g. by an onclickhandler)... is there any way to do this right?
Wondering: Why don't you just override the onReceive() method of the WidgetProvider? Since AppWidgetProvider is extended from BroadcastReceiver, this is perfectly legal as long as you call super.onReceive().
The Intent you get through onReceive() contains the Widget Ids as an Extra, if it's been called by the AppWidgetHost (Launcher). If you call it by yourself, you have to add the required extras by yourself.
This looks to be an elegant way to trigger the WidgetProvider from any other activity while keeping the original functionality of it.
Remember: AppWidgetProvider is a convenient class to easy the development of Widgets but to the core it's just a BroadcastReceiver.
I solved it like this:
public class WidgetProvider extends AppWidgetProvider {
public static final String ACTION_RESTART_SERVICE = "ACTION_RESTART_SERVICE";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onEnabled() called.");
if (intent.getAction() != null && intent.getAction().equals(WidgetProvider.ACTION_RESTART_SERVICE))
{
// Start service
Log.d(TAG, "ACTION_RESTART_SERVICE... Restarting service with designated update interval...");
Intent i = new Intent(context, UpdateWidgetService.class);
service = startService(i, context, service);
} else
{
// Other intent, call super class
Log.d(TAG, "Not ACTION_RESTART_SERVICE... calling superclass onReceive()...");
super.onReceive(context, intent);
}
}
}
And in your Activity/Fragment:
/**
* Restart the update service via WidgetProvider to reflect new profile and settings
* #param context Context is required
*/
private void restartService(Context context)
{
Intent intent = new Intent(context,
WidgetProvider.class);
intent.setAction(WidgetProvider.ACTION_RESTART_SERVICE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
{
// Send intents to all widget provider classes
intent.setClass(context, WidgetProviderSize1.class);
getActivity().sendBroadcast(intent);
intent.setClass(context, WidgetProviderSize2.class);
getActivity().sendBroadcast(intent);
intent.setClass(context, WidgetProviderSize3.class);
getActivity().sendBroadcast(intent);
intent.setClass(context, WidgetProviderSize4.class);
getActivity().sendBroadcast(intent);
intent.setClass(context, WidgetProviderSize5.class);
getActivity().sendBroadcast(intent);
} else
getActivity().sendBroadcast(intent);
}
Looks a bit complicated because I have a dynamic resizable widget with JellyBean and fixed Widget sizes below that OS version, but the solution should be clear.
An even easier solution might be to just send an android.appwidget.action.APPWIDGET_UPDATE broadcast intent exactly like a Launcher would to trigger the onUpdate() of your WidgetProvider directly.
Then there is a totally different option available: Let the Updateservice get the WidgetIDs all by himself, so there is no need to get them from the update intent. This is ok if all widgets basicly share the same configuration and should all be updated if anything changes in configuration:
/**
* Get all Widget IDs of WidgetProviders used by this app
* #param appWidgetManager AppWidgetManager to use
* #return Array of widget IDs
*/
private int[] getAppWidgetIDs(AppWidgetManager appWidgetManager)
{
int[] widgetIdsOfOneProviderSize1 = getAppIdsOfSingleProvider(appWidgetManager, WidgetProviderSize1.class);
int[] widgetIdsOfOneProviderSize2 = getAppIdsOfSingleProvider(appWidgetManager, WidgetProviderSize2.class);
int[] widgetIdsOfOneProviderSize3 = getAppIdsOfSingleProvider(appWidgetManager, WidgetProviderSize3.class);
int[] widgetIdsOfOneProviderSize4 = getAppIdsOfSingleProvider(appWidgetManager, WidgetProviderSize4.class);
int[] widgetIdsOfOneProviderSize5 = getAppIdsOfSingleProvider(appWidgetManager, WidgetProviderSize5.class);
int[] widgetIdsOfOneProvider = getAppIdsOfSingleProvider(appWidgetManager, WidgetProvider.class);
int allWidgetIds[] = new int[widgetIdsOfOneProviderSize1.length + widgetIdsOfOneProviderSize2.length
+ widgetIdsOfOneProviderSize3.length + widgetIdsOfOneProviderSize4.length
+ widgetIdsOfOneProviderSize5.length + widgetIdsOfOneProvider.length];
int index = 0;
for (int id : widgetIdsOfOneProviderSize1)
{
allWidgetIds[index] = id;
index ++;
}
for (int id : widgetIdsOfOneProviderSize2)
{
allWidgetIds[index] = id;
index ++;
}
for (int id : widgetIdsOfOneProviderSize3)
{
allWidgetIds[index] = id;
index ++;
}
for (int id : widgetIdsOfOneProviderSize4)
{
allWidgetIds[index] = id;
index ++;
}
for (int id : widgetIdsOfOneProviderSize5)
{
allWidgetIds[index] = id;
index ++;
}
for (int id : widgetIdsOfOneProvider)
{
allWidgetIds[index] = id;
index ++;
}
return allWidgetIds;
}
private int[] getAppIdsOfSingleProvider(AppWidgetManager appWidgetManager, Class cls)
{
ComponentName thisWidget = new ComponentName(getApplicationContext(),
cls);
int[] widgetIdsOfOneProvider = appWidgetManager.getAppWidgetIds(thisWidget);
return widgetIdsOfOneProvider;
}
Yes, I should've used ArrayUtils to put the arrays together... leaves room for improvement ;-)

Categories