Android Widget - update when a button is clicked - java

Good day.
I'm trying to create a widget that will show the balance on the screen.
Created 2 buttons and set up the listener to determine which button was pressed.
Difficulty:
How to update data by clicking on the button or reload the widget so that the data is updated?
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.DateFormat;
import java.util.Date;
public class EthWidgetProvider extends AppWidgetProvider {
private static final String SYNC_CLICKED = "automaticWidgetSyncButtonClick";
private static final String SYNC_CLICKED2 = "automaticWidgetSyncButtonClick2";
static void updateAppWidget(final Context context, final AppWidgetManager appWidgetManager, final int appWidgetId) {
RequestSingleton.getInstance(context).fetchData(new VolleyCallback() {
#Override
public void onSuccessRequest(JSONArray result) {
Log.i("Response", result.toString());
String price = "";
try {
JSONObject etherObject = result.getJSONObject(0);
price = etherObject.getString("price_usd");
} catch (JSONException e) {
Log.e("JSONException", e.toString());
}
Log.i("Price", price);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_widget);
views.setTextViewText(R.id.gprs, "$" + price);
String currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
views.setTextViewText(R.id.timeofday, currentDateTimeString);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
});
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews remoteViews;
ComponentName watchWidget;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.example_widget);
watchWidget = new ComponentName(context, EthWidgetProvider.class);
remoteViews.setOnClickPendingIntent(R.id.refresh, getPendingSelfIntent(context, SYNC_CLICKED));
remoteViews.setOnClickPendingIntent(R.id.example_widget_button, getPendingSelfIntent(context, SYNC_CLICKED2));
appWidgetManager.updateAppWidget(watchWidget, remoteViews);
for(int appId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appId);
}
}
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
super.onReceive(context, intent);
if (SYNC_CLICKED.equals(intent.getAction())) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews;
ComponentName watchWidget;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.example_widget);
watchWidget = new ComponentName(context, EthWidgetProvider.class);
remoteViews.setTextViewText(R.id.timeofday, "TESTING");
appWidgetManager.updateAppWidget(watchWidget, remoteViews);
}
if (SYNC_CLICKED2.equals(intent.getAction())) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews;
ComponentName watchWidget;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.example_widget);
watchWidget = new ComponentName(context, EthWidgetProvider.class);
remoteViews.setTextViewText(R.id.timeofday, "TESTING2");
appWidgetManager.updateAppWidget(watchWidget, remoteViews);
}
}
protected PendingIntent getPendingSelfIntent(Context context, String action) {
Intent intent = new Intent(context, getClass());
intent.setAction(action);
return PendingIntent.getBroadcast(context, 0, intent, 0);
}
}
By pressing button 1, I need to show the activity. By pressing the second button, you need to update the widget so that the data is updated, that is, so that the request to the server goes again and the data is updated.
Any help would be appreciated.

I don't get you idea with the Broadcast.
The broadcast should not be inner class
Broadcast should extend BroadcastReceiver class
Broadcast should be defined in manifest
So when you do this, your receiver should be called after buttons are clicked.
But, you want something to wake up your Widget. And then Widget#onUpdate will fetch the updated data (that you download on widget click) and show them.
To request update of your widget, you can use something like this:
public static void updateAppWidget(#NonNull Context applicationContext,
#NonNull Class<? extends AppWidgetProvider> widgetProviderClass) {
final ComponentName component = new ComponentName(applicationContext, widgetProviderClass);
final int[] widgetIds;
try {
widgetIds = AppWidgetManager.getInstance(applicationContext).getAppWidgetIds(component);
} catch (RuntimeException re) {
Log.d(re, "Unable to obtain widget IDs.");
return;
}
if (widgetIds.length == 0) {
Log.d("There is no widget to be updated.");
return;
}
final Intent intent = new Intent(applicationContext, widgetProviderClass);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds);
applicationContext.sendBroadcast(intent);
}
So to sum it up:
In widget setup views, listeners, and populate with last known data.
In listener broadcast either open Activity, or run some download
Don't forget BroadcastReceivers are running on UI thread; use
goAsync() approach.
The downloaded data should be stored to some place, where widget can
obtain them
call mentioned function to trigger widget update.
I believe, this is a quite specific and complex topic unlike the rest of Android, so feel free to ask anything I might missed :)

There is a very helpful must-read official guide titled "Build an App Widget":
(https://developer.android.com/guide/topics/appwidgets)
Please read this page carefully so you understand the concepts and focus on the AppWidgetProvider section for your problem. Although in this section, they use: PendingIntent.getActivity(), you are supposed to use a Service as PendingIntent.getService() to have your Service called on button click, and then have the Service update your Widget.
Basically you'll see that you need to:
Create a Service
Create a PendingIntent:
Intent intent = new Intent(context, YourService.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
Set the onClickPendingIntent listener on your RemoteViews object like this:
views.setOnClickPendingIntent(R.id.button, pendingIntent);

To call activity:
remoteViews.setOnClickPendingIntent( R.id.button1, PendingIntent.getActivity(context, 0, new Intent( context, Main.class), 0) );
To update widgets from another class :
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance( context );
int ids[] = appWidgetManager.getAppWidgetIds( componentName );
for( int id : ids ){
//update textView here
appWidgetManager.updateAppWidget( id, views);
}

Related

onAppWidgetOptionsChanged() called on screen awake. How to prevent this?

I have a calendar widget and I'm updating it myself everyday at 12:01AM as well when the size is changed so I don't have a need for automatic update. I also set the updatePeriodMillis to 0. The problem I'm having is that the App Widget calls onAppWidgetOptionsChanged():
Every time the screen unlocks
Every time you're brought home
It wouldn't be an issue for me, but the problem is you can see for a split second the App Widget updating. Anyone know of any way I can prevent that from happening?
For reference here is part of my WidgetProvider class
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null) {
if (action.equals(ACTION_REFRESH) || action.equals(ACTION_NEXT) || action.equals(ACTION_PREVIOUS)) {
context.sendBroadcast(new Intent(action));
}
}
Bundle extra = intent.getExtras();
if (extra != null && extra.containsKey(KEY_APP_WIDGET_ID)) {
updateAppWidget(context, AppWidgetManager.getInstance(context), extra.getInt(KEY_APP_WIDGET_ID));
}
super.onReceive(context, intent);
}
#Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
int width = Util.dp2px(newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH));
Intent broadcastIntent = new Intent(ACTION_REFRESH);
broadcastIntent.putExtra(KEY_SIZE_CHANGE, width);
context.sendBroadcast(broadcastIntent);
updateAppWidget(context, appWidgetManager, appWidgetId);
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
}
private static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
Intent intent = new Intent(context, WidgetService.class);
views.setRemoteAdapter(R.id.widget_calendar_container, intent);
// Buttons on widget click handlers
views.setOnClickPendingIntent(R.id.widget_refresh, getPendingSelfIntent(context, ACTION_REFRESH, appWidgetId));
views.setOnClickPendingIntent(R.id.widget_next, getPendingSelfIntent(context, ACTION_NEXT, appWidgetId));
views.setOnClickPendingIntent(R.id.widget_previous, getPendingSelfIntent(context, ACTION_PREVIOUS, appWidgetId));
// Widget Container click handler
Intent homeIntent = new Intent(Intent.ACTION_MAIN);
homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
homeIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
homeIntent.setComponent(new ComponentName(context.getPackageName(), Home.class.getName()));
PendingIntent homePendingIntent = PendingIntent.getActivity(context, 0, homeIntent, 0);
views.setPendingIntentTemplate(R.id.widget_calendar_container, homePendingIntent);
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_calendar_container);
}
I prefer another answer, but this is what I got so far.
#Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
int width = Util.dp2px(newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH));
if (width != WidgetRemoteViewsFactory.width) {
Intent broadcastIntent = new Intent(ACTION_REFRESH);
broadcastIntent.putExtra(KEY_SIZE_CHANGE, width);
LocalBroadcastManager.getInstance(context).sendBroadcast(broadcastIntent);
updateAppWidget(context, appWidgetManager, appWidgetId);
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
}
}
I made the field I am interested in width static on WidgetRemoteViewsFactory and I am only broadcasting when that field changes. I can't seem to prevent onAppWidgetOptionsChanged() from being called because AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED is being broadcasted and received every time the screen is awaken.
Android Documentation says
Called in response to the AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED broadcast when this widget has been layed out at a new size. -
AppWidgetProvider#onAppWidgetOptionsChanged
But that action is being broadcasted every time the screen awakens.
If anyone has a better solution, please feel free to post it. There has to be a better solution or this is a bug on Android's side.

Android widget talk to service and service talk to widget

I have an Android app whose service is started when app starts and when app is killed, service is also destroyed.
I have a home screen widget starting alongside with app. It has 3 imageviews - "Play","Next","Previous".
On press of any of these imageview in widget, they need to talk to service and service talks to library etc.
When service has an answer, it responds to widget with status OK or NO. Based on the response, I need to update the imageviews.
When the app is killed, I need to gray out my widget fully. In this situation, when I click on the greyed out widget, I need to start the app. Could anyone help me please
how I could achieve this.
Here is my widget code -
public class MyWidgetProvider extends AppWidgetProvider {
private Intent serviceIntent;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
RemoteViews remoteViews;
ComponentName componentName;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
componentName = new ComponentName(context, ControlScreenWidgetProvider.class);
remoteViews.setOnClickPendingIntent(R.id.PLAY,getPendingSelfIntent(context, "PLAY",componentName));
remoteViews.setOnClickPendingIntent(R.id.NEXT,getPendingSelfIntent(context, "NEXT",componentName));
remoteViews.setOnClickPendingIntent(R.id.PREVIOUS,getPendingSelfIntent(context, "PREVIOUS",componentName));
appWidgetManager.updateAppWidget(componentName, remoteViews);
}
#Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews;
ComponentName componentName;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
componentName = new ComponentName(context, MyWidgetProvider.class);
if ("PLAY".equals(intent.getAction())) {
remoteViews.setOnClickPendingIntent(R.id.PLAY,getPendingSelfIntent(context, "PLAY",componentName));
// Dont know what to do here
}
else if ("NEXT".equals(intent.getAction())) {
remoteViews.setOnClickPendingIntent(R.id.lock_door,getPendingSelfIntent(context, "NEXT",componentName));
// Dont know what to do here
}
else if ("PREVIOUS".equals(intent.getAction())) {
remoteViews.setOnClickPendingIntent(R.id.lock_door,getPendingSelfIntent(context, "PREVIOUS",componentName));
// Dont know what to do here
}
appWidgetManager.updateAppWidget(componentName, remoteViews);
}
protected PendingIntent getPendingSelfIntent(Context context, String action, ComponentName componentName) {
Intent intent = new Intent(context, MyWidgetProvider.class);
intent.setAction(action);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, componentName);
return PendingIntent.getService(context, 0, intent, 0);
}
}

Onclick Event for Android Widget

I've read about 10 posts now about onclick events and Widgets and I understand they are implemented differently. However, despite the success other people are having I seem to be finding it impossible to get it to work. I'm using Android Studio 2.2 and have created a simple test app. I have a button called button. I want a toast to display when I press the button.
I've copied the code as suggested in the other posts. Can someone take a look and see if I am doing anything wrong?
public class NewAppWidget extends AppWidgetProvider {
private static final String MyOnClick = "myOnClickTag";
protected PendingIntent getPendingSelfIntent(Context context, String action) {
Intent intent = new Intent(context, getClass());
intent.setAction(action);
return PendingIntent.getBroadcast(context, 0, intent, 0);
}
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.new_app_widget);
views.setOnClickPendingIntent(R.id.button, getPendingSelfIntent(context, MyOnClick));
views.setTextViewText(R.id.appwidget_text, widgetText);
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
//RemoteViews.setOnClickPendingIntent(R.id.button, getPendingSelfIntent(context, MyOnClick));
#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
}
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);//add this line
if (MyOnClick.equals(intent.getAction())){
//your onClick action is here
//display in short period of time
Toast.makeText(context, "msg msgasdasd", Toast.LENGTH_SHORT).show();
}
};
}
OK - really weird. Restarted the android emulator and it started working.
Mmmmm.
Cheers

Get Data from another activity to App Widget

Hi I have an Activity 1 which needs to send data to Appwidget (widget) which has a text view.
For sending data between activites i know that we can use intent.putExtra("mydata", dataString); and recive the same with String data = bundle.getString("mydata"); But in my case i need to send data (String) to app Widget.
When i use
Bundle dataFromPrevious = getIntent().getExtras();
String newString = dataFromPrevious.getString("mydata");
inside AppWidgetProvider it throws an error in getIntent saying getIntent is undefined for the type Class.
How can i get the string inside this AppWidget? Also this text will be updated from activity 1 with new string often so is using SharedPrefrences a good choice for this situation? Is there any other way?
UPDATE 1:
As mentioned by Joseph, i have added
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
in my manifest,
Now i have also created an OnRecieve Override in AppWidget
#Override
public void onReceive(Context context, Intent intent) {
Bundle getPrevData = intent.getExtras();
String data = getPrevData.getString("mydata");
newdata = data;
super.onReceive(context, intent);
}
In my On Update i have
views.setTextViewText(R.id.dataWidget,newdata);
Here the newdata is the public static String But it dose not display anything!!! when i setTextViewText to a textview in the widget. Am i missing something here? Please help...
UPDATE 2:
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.RemoteViews;
import android.widget.Toast;
public class MainWidget extends AppWidgetProvider {
private RemoteViews views;
public static String newdata
#Override
public void onReceive(Context context, Intent intent) {
Bundle getPrevData = intent.getExtras();
String data = getPrevData.getString("mydata");
newdata = data;
super.onReceive(context, intent);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
views = new RemoteViews(context.getPackageName(),R.layout.widget_layout);
appWidgetManager.updateAppWidget(appWidgetIds, views);
//views.setTextViewText(R.id.dataWidget,newdata);
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
}
And this is my Another activityvthat sends data:
final String text = ((TextView)findViewById(R.id.textview)).getText().toString();
Intent intent = new Intent(FirstActivity.this, MainWidget.class);
intent.putExtra("mydata", text);
You need to use BroadcastReceivers. In a nutshell: add a BroadcastReceiver to your AppWidgetProvider which acts upon a custom Intent and then refreshes your widgets.
From your Activity then all you need to do is send a broadcast (Context.sendBroadcast) with the custom intent (and you can add data to the Intent with putExtra as per usual).
You can update the data in widget without using Broadcast receiver.
Save data in sharedPreference.
Create the remote views using new data and use AppWidgetManager.updateWidget to update the widget.
Here is the sample code.
RemoteViews updateViews = buildUpdate(context); // Update the view using the new data.
ComponentName thisWidget = new ComponentName(context, WidgetClassName.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(thisWidget, updateViews);
This is what i did in my previous application.
Saneesh CS

Why doesn't my app widget update in API 3 or 4?

I'm working on an android widget and it works great in API Level 5 or greater. It's not supported at all in API Level 1 or 2. It should work absolutely fine in 3 and 4 but for some reason the widget doesn't update.
The onUpdate method gets called and executes without errors; however, in 3 and 4 it doesn't change the text of the widget. I'm pretty much at a loss. Any thoughts?
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
DataAccess helper = new DataAccess(context);
String text = helper.getCurrentText();
helper.close();
if (text != null)
views.setTextViewText(R.id.widget_text, text);
Intent intent = new Intent(context, WidgetDetailsActivity.class);
PendingIntent pending = PendingIntent.getActivity(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.widget, pending);
appWidgetManager.updateAppWidget(appWidgetIds, views);
}
Try this:
At the end of your update method you need to call the super.
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
DataAccess helper = new DataAccess(context);
String text = helper.getCurrentText();
helper.close();
if (text != null)
views.setTextViewText(R.id.widget_text, text);
Intent intent = new Intent(context, WidgetDetailsActivity.class);
PendingIntent pending = PendingIntent.getActivity(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.widget, pending);
appWidgetManager.updateAppWidget(appWidgetIds, views);
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
You need to start service on update method and put your all code in service also,here I give onReceive and service demo
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,
Widget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
#Override
public void onReceive(Context context, Intent intent) {
final RemoteViews remoteViews = buildUpdate(context);
ComponentName cn = new ComponentName(context, Widget.class);
AppWidgetManager.getInstance(context).updateAppWidget(cn, remoteViews);
super.onReceive(context, intent);
}
where buildUpdate is method for your widget updating code

Categories

Resources