How to use Eventbus in a widget? - android

I'm trying to figure out how I can use the Greenbot Eventbus library in my AppWidgetProvider. I've tried the following, which doesn't work:
public class SimpleWidgetProvider extends AppWidgetProvider {
RemoteViews remoteViews;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int count = appWidgetIds.length;
for (int i = 0; i < count; i++) {
int widgetId = appWidgetIds[i];
remoteViews = new RemoteViews(context.getPackageName(), R.layout.simple_widget);
//set image
remoteViews.setImageViewResource(R.id.piggy_bank, R.drawable.piggy_bank);
Intent intent = new Intent(context, SimpleWidgetProvider.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);
//set refresh button
remoteViews.setOnClickPendingIntent(R.id.refresh_btn, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
#Override
public void onEnabled(Context context) {
super.onEnabled(context);
EventBus.getDefault().register(this);
}
//set total price
#Subscribe
public void onPriceEvent(TotalPriceEvent event) {
double price = event.totalPrice;
remoteViews.setTextViewText(R.id.total_amount, String.valueOf(price));
}
#Override
public void onDisabled(Context context) {
EventBus.getDefault().unregister(this);
super.onDisabled(context);
}
}
Please, let me know if I need to attach more code.

An AppWidgetProvider is just a BroadcastReceiver with a specialized onReceive() method that delegates broadcasts to other methods based on the action. Instances of a manifest-registered BroadcastReceiver aren't meant to live very long. They run just long enough to handle a broadcast and then die, so subscribing one to an event bus isn't going to work as expected, and is kinda pointless, given the overlapping patterns. If you want to notify your SimpleWidgetProvider of something, just send a broadcast to it.
For an example, we define our own action for the SimpleWidgetProvider class, and check for it in the onReceive() method. If it's ours, we'll handle it as needed, and otherwise call the super method to allow AppWidgetProvider to properly delegate it.
public class SimpleWidgetProvider extends AppWidgetProvider {
public static final String MY_SPECIAL_ACTION = "com.mycompany.myapp.SPECIAL_ACTION";
#Override
public void onReceive(Context context, Intent intent) {
if(MY_SPECIAL_ACTION.equals(intent.getAction())) {
// Do your thing
}
else {
// Not our action, so let AppWidgetProvider handle it
super.onReceive(context, intent);
}
}
...
}
We can send a broadcast to it with the usual mechanism.
Intent widgetNotify = new Intent(context, SimpleWidgetProvider.class);
widgetNotify.setAction(SimpleWidgetProvider.MY_SPECIAL_ACTION);
widgetNotify.putExtra(...);
...
context.sendBroadcast(widgetNotify);
I would also mention that the super calls in onEnabled() and onDisabled() are unnecessary, as those methods are empty in AppWidgetProvider.

Related

Call AsyncTask in the onRecive

I need to call AsyncTask function since the onReceive(). The problem is when I call the function, the different TextViews it must change in the onPostExecute(), don't change it!
This is the code:
#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);
int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
RemoteViews remoteViews;
ComponentName watchWidget;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
watchWidget = new ComponentName(context, widget.class);
remoteViews.setTextViewText(R.id.textView56, "ACTUALIZANDO");
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
new LongOperation(views, appWidgetId, appWidgetManager).execute("MyTestString"); //Calling the asyncTask
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);
}
And the AsyncTask. This part of the code we use and also works when I call sice the onUpdate:
public class LongOperation extends AsyncTask<String, Void, String> {
private RemoteViews views;
private int WidgetID;
private AppWidgetManager WidgetManager;
public LongOperation(RemoteViews views, int appWidgetID, AppWidgetManager appWidgetManager){
this.views = views;
this.WidgetID = appWidgetID;
this.WidgetManager = appWidgetManager;
}
#Override
public void onPreExecute() {
super.onPreExecute();
}
#Override
public String doInBackground(String... params) {
try {
....
} catch (Exception e) {
....
}
return temperatura;
}
#Override
public void onPostExecute(String result) {
views.setTextViewText(R.id.textView66, result+ "ÂșC ");
WidgetManager.updateAppWidget(WidgetID, views);
}
}
I think that the problem is in the appWidgetId but I can't solve...
Thanks,
MArc
The use of AsyncTask in BroadCast is bad practice, because Android may kill your process in onReceive() if there is no any active Service or Activity, and no gurantee its return.
In this case, official documentation recommends IntentService:
"The specific constraint on BroadcastReceiver execution time
emphasizes what broadcast receivers are meant to do: small, discrete
amounts of work in the background such as saving a setting or
registering a Notification. So as with other methods called in the UI
thread, applications should avoid potentially long-running operations
or calculations in a broadcast receiver. But instead of doing
intensive tasks via worker threads, your application should start an
IntentService if a potentially long running action needs to be taken
in response to an intent broadcast."

Android: Home screen Widgets: Use of Broadcast Intents?

I am trying to understand an app that communicates with a widget on the home screen. But i do not understand what the following code does within the application:
Intent i = new Intent(this, AppWidget.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.phoneState, pi);
return updateViews;
Full Class:
public class AppWidget extends AppWidgetProvider {
// This is called for every broadcast. We normally don't need to implement this
// method because the default AppWidgetProvider implementation filters all App Widget
// broadcasts and calls the above methods as appropriate
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == null) {
context.startService(new Intent(context, ToggleService.class));
} else {
super.onReceive(context, intent);
}
}
#Override
// This is called to update the App Widget at intervals defined by
// the updatePeriodMillis attribute in the AppWidgetProviderInfo. This method is also called when
// the user adds the App Widget, so it should perform the essential setup, such as define event
// handlers for Views and start a temporary Service, if necessary.
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
context.startService(new Intent(context, ToggleService.class));
}
// This class is used to set up the intent service in order to provide views for
// the widget. It also supports to set up a pending intent. Furthermore, the app widget can be
// updated with a remote adapter.
public static class ToggleService extends IntentService {
public ToggleService() {
super("AppWidget$ToggleService");
}
#Override
protected void onHandleIntent(Intent intent) {
ComponentName me = new ComponentName(this, AppWidget.class);
AppWidgetManager mgr = AppWidgetManager.getInstance(this);
mgr.updateAppWidget(me, buildUpdate(this));
}
private RemoteViews buildUpdate(Context context) {
RemoteViews updateViews = new RemoteViews(context.getPackageName(),
R.layout.widget);
AudioManager audioManager = (AudioManager) context
.getSystemService(Activity.AUDIO_SERVICE);
if (audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
updateViews.setImageViewResource(R.id.phoneState,
R.drawable.phone_state_normal);
audioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
} else {
updateViews.setImageViewResource(R.id.phoneState,
R.drawable.phone_state_silent);
audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
}
//KNOW THIS CODE
Intent i = new Intent(this, AppWidget.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.phoneState, pi);
return updateViews;
}
}
}
All it does is open the ToggleService when the user taps on the viewId R.id.phoneState.
It's kinda of a silly way of doing it, the more straight forward would be:
Intent i = new Intent(this, ToggleService.class);
PendingIntent pi = PendingIntent.getService(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.phoneState, pi);
you see, this code you posted calls the BroadcastReceiver that then calls the service. Makes no sense.

Workaround for AppWidgetProvider's onEnabled & onDisabled is not being called

Since I'm using AlarmManager to perform periodical widget update, I need to ensure onEnabled & onDisabled will work reliably.
However, I realize they will not be triggered sometimes. I'm not the only one who is facing this problem.
Android appWidgetProvider onEnabled never called on tablet
Is there any official bug ticket submitted to Google Android team?
Is there any workaround, especially onDisabled? As I do not want AlarmManager still being triggered repeatably, after the last widget had been removed.
AndroidManifest.xml
<receiver android:name="org.yccheok.MyAppWidgetProvider"
android:exported="true" >
<intent-filter >
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<action android:name="android.appwidget.action.APPWIDGET_DISABLED" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/widget_info" />
</receiver>
public class MyAppWidgetProvider extends AppWidgetProvider {
private static PendingIntent createAlarmUpdatePendingIntent(Context context) {
Intent intent = new Intent(context, JStockAppWidgetProvider.class);
intent.setAction(JStockAppWidgetProvider.ALARM_UPDATE_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
#Override
public void onEnabled(Context context)
{
super.onEnabled(context);
PendingIntent pendingIntent = createAlarmUpdatePendingIntent(context);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
int scanSpeed = JStockApplication.instance().getJStockOptions().getScanSpeed();
alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis() + scanSpeed, scanSpeed, pendingIntent);
}
#Override
public void onDisabled(Context context)
{
super.onDisabled(context);
PendingIntent pendingIntent = createAlarmUpdatePendingIntent(context);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
}
you can check in method a number of instances that are currently running.
private boolean hasInstances(Context context) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(
new ComponentName(context, this.getClass()));
return (appWidgetIds.length > 0);
}
I afraid you have misunderstood the methods of AppWidgetProvider.
onDeleted(Context context, int[] appWidgetIds)
Called in response to the ACTION_APPWIDGET_DELETED broadcast **when one or more** AppWidget instances have been deleted. Override this method to implement your own AppWidget functionality.
onDisabled(Context context)
Called in response to the ACTION_APPWIDGET_DISABLED broadcast, which is sent when the last AppWidget instance for this provider is deleted. Override this method to implement your own AppWidget functionality.
In short, If you have two or more AppWidget instances then if you remove any of them at that time only onDeleted() method for particular widget will be called.
If you have only single AppWidget instance then if you remove that time onDesabled() and onDeleted() both will be called.
So you will have to move your code from onDesabled() method to onDeleted() method and it will get called every time.!
Also take care that onEnabled() will be called only for the first instance and not for every next instance you create.
For what I understood you just need one alarm, and I suppose all widgets will be all the same. So, the idea for your alarm is run on service, not on widget.
You shouldn't run long time actions on BroadcastReceiver.
We will use the onUpdate and not onEnable (because the design of this demo). Then aways we get a new widget we can perform the service.
AppWidgetProvider:
public class MyAppWidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds)
{
ComponentName thisWidget = new ComponentName(context, MyAppWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
Intent intent = new Intent(context.getApplicationContext(), JStockAppWidgetService.class);
intent.setAction(JStockAppWidgetService.ALARM_UPDATE_ACTION);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);
context.startService(intent);
}
#Override
public void onDisabled(Context context)
{
super.onDisabled(context);
Intent intent = new Intent(context.getApplicationContext(), JStockAppWidgetService.class);
intent.setAction(JStockAppWidgetService.ALARM_STOP_ACTION);
// I kept this just in case you wanna keep running your alarm without widget. You can just stopService here too.
context.startService(intent);
}
}
And here the Service:
public class JStockAppWidgetService extends Service {
public static final String ALARM_UPDATE_ACTION = "ALARM_UPDATE_ACTION";
public static final String ALARM_STOP_ACTION = "ALARM_STOP_ACTION";
//delay to refresh your widget
private int delay = 10000; //10 secs
private Thread myThread;
#Override
public void onStart(Intent intent, int startId) {
final int[] allWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
try {
//running your timeout while is not interrupted
while(!Thread.currentThread().isInterrupted()) {
//we need to back to GUI thread
handler.post(new Runnable() {
#Override
public void run() {
runInMyGuiThread(allWidgetIds);
}
});
//everybody needs to sleep sometime =p
Thread.sleep(delay);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
};
String action = intent.getAction();
if(action == ALARM_UPDATE_ACTION) {
if(myThread != null)
myThread.interrupt();
myThread = new Thread(runnable);
myThread.start();
} else if(action == ALARM_STOP_ACTION && myThread != null) {
myThread.interrupt();
}
}
//here you are in GUI thread with all your widgets id
public void runInMyGuiThread(int[] allWidgetIds) {
for(int widgetId : allWidgetIds){
//do what you want to update each widget
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
This is just a little demo, you can stop the service or keep it running without widget, I will let with you.
I was confused by the following situation.
If you have added the widget to the lock screen and then you are testing by adding and removing the widget to the home screen, then you never see onEnabled/onDisabled called. The reason is that there is still a widget added - the lock screen widget.

How to stop service when removing widget from homescreen

I have a problem with service. I'm trying to do a simple battery level widget but the problem is that I don't know how to stop service when the widget is removed from homescreen.
Here is my service class
public class batteryService extends Service{
AppWidgetManager widgetManager;
private RemoteViews remoteViews;
private ComponentName thisWidget;
private BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
widgetManager = AppWidgetManager.getInstance(context);
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
thisWidget = new ComponentName(context, Widget.class);
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)*100;
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float value = (float)level/scale;
remoteViews.setTextViewText(R.id.widget_textview, String.valueOf(value));
widgetManager.updateAppWidget(thisWidget, remoteViews);
}
};
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
this.registerReceiver(mBatInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
super.onCreate();
}
#Override
public void onDestroy() {
this.unregisterReceiver(mBatInfoReceiver);
super.onDestroy();
}
}
and here widget class
public class Widget extends AppWidgetProvider{
private AppWidgetManager widget_manager;
private RemoteViews remoteViews;
private ComponentName thisWidget;
private Intent intent;
#Override
public void onEnabled(Context context) {
if(intent == null)
{
intent = new Intent(context, batteryService.class);
context.startService(intent);
}
super.onEnabled(context);
}
#Override
public void onDisabled(Context context) {
if(intent != null)
{
context.stopService(intent);
intent = null;
}
super.onDisabled(context);
}
}
When I remove widget from screen the service is still a live. I was trying to change onDisable to onDeleted method but it didn't change anything.
Have you any tips for me ?
Make sure you have the appropriate action in your manifest for the disabled event:
<action android:name="android.appwidget.action.APPWIDGET_DISABLED" />
This would be alongside your existing APPWIDGET_ENABLED and perhaps the classic APPWIDGET_UPDATE actions, for the <receiver> element for your AppWidgetProvider.
Also, please consider not having your service run constantly, but rather updating the battery information once every few minutes using AlarmManager. The battery level simply does not change that often to make it necessary to attempt to keep a service in memory all of the time.
Try by using onDeleted(Context, int[])

Force Android widget to update

I respond to a button press on my appwidget in the onreceive method. When the button I pressed, I want to force the widget to call the onupdate method. How do I accomplish this?
Thanks in advance!
Widget can't actually respond to clicks because it's not a separate process running. But it can start service to process your command:
public class TestWidget extends AppWidgetProvider {
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 UpdateService
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.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
// Tell the AppWidgetManager to perform an update on the current App Widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
public static class UpdateService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//process your click here
return START_NOT_STICKY;
}
}
}
You should also register the new service in your manifest file:
<service android:name="com.xxx.yyy.TestWidget$UpdateService">
You can find another example of UpdateService implementation in Wiktionary sample in SDK
And here's another good approach Clickable widgets in android
This is kinda crude, but it works rather well for me, as I've found no directly-implemented way to force an update.
public class Foo extends AppWidgetManager {
public static Foo Widget = null;
public static Context context;
public static AppWidgetManager AWM;
public static int IDs[];
public void onUpdate(Context context, AppWidgetManager AWM, int IDs[]) {
if (null == context) context = Foo.context;
if (null == AWM) AWM = Foo.AWM;
if (null == IDs) IDs = Foo.IDs;
Foo.Widget = this;
Foo.context = context;
Foo.AWM = AWM;
Foo.IDs = IDs;
.......
}
}
Now, anywhere I want to force the widget to update, it's as simple as:
if (null != Foo.Widget) Foo.Widget.onUpdate(null, null, null);

Categories

Resources