How do I set TextView inside handlers?
public class DigitalClock extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
int N = appWidgetIds.length;
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.digitalclock);
for (int i = 0; i < N; i++) {
int appWidgetId = appWidgetIds[i];
Intent clockIntent = new Intent(context, DeskClock.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
clockIntent, 0);
views.setOnClickPendingIntent(R.id.rl, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
private static Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// update your textview here.
}
};
class TickThread extends Thread {
private boolean mRun;
#Override
public void run() {
mRun = true;
while (mRun) {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mHandler.sendEmptyMessage(0);
}
}
}
Im supposed to update the TextView here:
private static Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// update your textview here.
...
How do i do this? In the OnUpdate method i would use views.setTextViewText(R.id... but in the Handler RemoteViews doesnt exist. Ive tried everything I know and so far, nothing
Make a new one :) RemoteViews just attached to the remote entity and you pretty much queue up a bunch of changes that it makes when it is realized.
So when you do
appWidgetManager.updateAppWidget(appWidgetId, views);
That is when the RemoteViews actually do something.
I think the real problem is that the design being used is a little messy. So you have a thread, not sure where this gets started but it calls into a handler, this is fine, but you should probably send some structured data so the Handler knows what to do. RemoteViews instances themselves are Parcelable, which means they can be sent as part of the payload of things such as Intent and Message instances. The real problem with this design is that you can't call updateAppWidget without the AppWidgetManager instance to actually execute your changes.
You can either cache the AppWidgetManager for the lifetime of your widget or update the update frequency and move to more of a delayed queue worker. Where on next update event that you receive from the system, or a mixture of both.
private SparseArray<RemoteView> mViews;
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
....
for (int appWidgetId : appWidgetIds) {
RemoteViews v = mViews.get(appWidgetId);
if (v != null) {
appWidgetManager.updateWidget(appWidgetId, v);
} else {
enqueue(appWidgetManager, appWidgetId, new RemoteViews(new RemoteViews(context.getPackageName(),
R.layout.digitalclock)));
/* Enqueue would pretty much associate these pieces of info together
and update their contents on your terms. What you want to do is up
to you. Everytime this update is called though, it will attempt to update
the widget with the info you cached inside the remote view.
*/
}
}
}
Related
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.
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."
I use this sample:
http://laaptu.wordpress.com/2013/07/19/android-app-widget-with-listview/
Everything is OK but I have two differences:
I use TimerTask and dynamic ArrayList.
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TasksView(context, appWidgetManager),100, 5000);
remoteViews = new RemoteViews(context.getPackageName(), R.layout.appwidget);
}
private class TasksView extends TimerTask
{
public TasksView(Context context, AppWidgetManager appWidgetManager){
remoteViews = new RemoteViews(context.getPackageName(), R.layout.appwidget);
thisWidget = new ComponentName(context, Widget.class);
}
#Override
public void run()
{
// SOME operation
}
public void updateListView()
{
// HERE I need updateListView
}
}
With delivery of the list of objects will not be a problem because I'll do it by batches. But how to call update again in AppWidgetProvider -> TasksView extends TimerTask
I don't get the exact point of your question, but if you just want to refresh a listview you can use :
ListView.invalidateViews()
or use method notifyDataSetChanged of your Adapter
Adapter.notifyDataSetChanged()
Hi all I want to make a battery widget in android with animation. I think I will be able to animate but I want to know that how can I get the battery status again and again? Will it be OK to do it through thread? Or something else is required. Here is the simple code for animation. Please explain how will I get the data from battery again and again all the time.I know the functions but I don't know the mechanism to use them.
public class HelloWidget extends AppWidgetProvider{
RemoteViews remoteViews;
AppWidgetManager appWidgetManager;
ComponentName thisWidget;
ImageView img;
Bitmap icon, icon1;
int counter = 0;
#Override
public void onUpdate(Context context, final AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
this.appWidgetManager = appWidgetManager;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
thisWidget = new ComponentName(context, HelloWidget.class);
remoteViews.setImageViewResource(R.id.imgv, R.drawable.icon2);
appWidgetManager.updateAppWidget(thisWidget, remoteViews);
( new Thread()
{
public void run()
{
while(true)
{
if(counter%15>=0 && counter%15 <=7)
{
remoteViews.setImageViewResource(R.id.imgv, R.drawable.icon2);
}
else
{
remoteViews.setImageViewResource(R.id.imgv, R.drawable.icon);
}
appWidgetManager.updateAppWidget(thisWidget, remoteViews);
counter++;
}
}
}
).start();
}
Register broadcast receiver for battery events and don't query every time.
IntentFilter batteryLevelFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
mContext.registerReceiver(batteryLevelReceiver, batteryLevelFilter);
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);