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);
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 want to make a widget app that is able to dial a number and user can set the number when he first drag and drop widget to home screen using widget configuration.But when the phone restarts widget uses default number again. I decide to save the entered phonenumber to Shared Preferences to save and load user's phone number but eclipse says that using getSharedPreferences is not allowed in onUpdate.Is there another way to perform it?
What should I do?
my code:
public class Main extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onUpdate(context, appWidgetManager, appWidgetIds);
for(int i=0 ; i<appWidgetIds.length ; i++)
{
SharedPreferences details = getSharedPreferences("OPERATOR", 0);
int appWidgetId = appWidgetIds[i];
String phNumber = "5554574";
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:"+(phNumber)));
PendingIntent pending = PendingIntent.getActivity(context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);
views.setOnClickPendingIntent(R.id.button1, pending);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}
Use:
SharedPreferences details = context.getSharedPreferences("OPERATOR", 0);
SharedPreferences has to be acquired from the context. onUpdate(...) provides you the context.
Your changed code should look like this:
public class Main extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onUpdate(context, appWidgetManager, appWidgetIds);
for(int i=0 ; i<appWidgetIds.length ; i++)
{
SharedPreferences details = context.getSharedPreferences("OPERATOR", 0);
int appWidgetId = appWidgetIds[i];
String phNumber = "5554574";
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:"+(phNumber)));
PendingIntent pending = PendingIntent.getActivity(context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);
views.setOnClickPendingIntent(R.id.button1, pending);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}
Please try it.
AppWidgetProvider is not a Context. But you can often create a static method to reach the context of an application, then do this
In Android Manifest file declare following
<application android:name="com.example.MyApplication">
</application>
then write the class
public class MyApplication extends Application{
private static Context context;
public void onCreate(){
super.onCreate();
MyApplication.context = getApplicationContext();
}
public static Context getAppContext() {
return MyApplication.context;
}
}
Then you can use
SharedPreferences details = MyApplication.getAppContext().getSharedPreferences("OPERATOR", 0);
I haven't worked with widgets yet so can't guarantee this solution but hope it work!
I am having some trouble sending data(strings) from my activity to my appWidgetProvide class.
I have a method called updateWidget. This gets called by the widget configure activity when the widget first gets placed in the home screen .This method is also called by the onReceive method when it receives data from one of my activities when the user enters data into that activity.
Here is my problem : the widget get placed in the home screen with all the data in the right place. But when my activity sends data the onReceive (using intents and extras) the data does not come through.I just get a empty string on the extras.getString .
I have used intents to send data between activities before , do I need to do something different when I send data to a widget provide class? Or am I just being stupid and missing something obvious ?
I have attached (what I think are the) relevant bits of code. Let me know if you need any clarification or any more of the code.
Thanks for taking the time to read this and for any help that you can give,
Cheers Rakshak
the onListItemClick in the widget configure class.
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Cursor note = mDbHelper.fetchNote(id);
startManagingCursor(note);
String title = note.getString(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE));
String text = note.getString(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY));
loadData(title);
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK,resultValue);
finish();
}
void loadData(String title) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
SingleNote.updateWidget(this, appWidgetManager, mAppWidgetId, title);
}
The intent that sends data to the onReceive (this is in one of my activity classes)
private void updateWidget() {
Intent i = new Intent(this, SingleNote.class);
i.setAction(SingleNote.UPDATE_ACTION);
Toast.makeText(getApplicationContext(),mTitleText.getText()+"from the activity",
Toast.LENGTH_SHORT).show();//This works just fine
i.putExtra("title", mTitleText.getText());
sendBroadcast(i);
}
My widget provide class
public class SingleNote extends AppWidgetProvider {
public static String UPDATE_ACTION = "ActionUpdateSinglenoteWidget";
private static NotesDbAdapter mDbHelper;
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];
// Tell the AppWidgetManager to perform an update on the current app widget
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.singlenote_widget);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle extras = intent.getExtras();
String title1 = extras.getString("title");//this value does not come through
Toast.makeText(context, title1,Toast.LENGTH_LONG).show();//this gives an empty space
if (action != null && action.equals(UPDATE_ACTION)) {
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName name = new ComponentName(context, SingleNote.class);
int[] appWidgetId = AppWidgetManager.getInstance(context).getAppWidgetIds(name);
final int N = appWidgetId.length;
if (N < 1)
{
return ;
}
else {
int id = appWidgetId[N-1];
updateWidget(context, appWidgetManager, id ,title1);
}
}
else {
super.onReceive(context, intent);
}
}
static void updateWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId, String title){
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.singlenote_widget);
views.setTextViewText(R.id.single_note_title, title);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
I suggest to try replacing i.putExtra("title", mTitleText.getText()); in updateWidget() with i.putExtra("title", mTitleText.getText().toString());
String title1 = extras.getString("title");
expects string, and mTitleText.getText() returns Editable - this is likely a mismatch
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.
*/
}
}
}
I have setup a widget like this
ToggleWidget.java
public class ToggleWidget extends AppWidgetProvider {
static RemoteViews remoteViews;
boolean status = false;
static int appWidgetId;
#Override
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++) {
appWidgetId = appWidgetIds[i];
remoteViews = new RemoteViews(context.getPackageName(), R.layout.toggle_widget);
Intent intent = new Intent(context.getApplicationContext(), WakeService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent = PendingIntent.getService(
context.getApplicationContext(), 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.bulb_widget, pendingIntent);
if (WakeService.isAwake) {
remoteViews.setImageViewResource(R.id.bulb_widget, R.drawable.bulb_on);
}
else {
remoteViews.setImageViewResource(R.id.bulb_widget, R.drawable.bulb_off);
}
// Tell the AppWidgetManager to perform an update on the current app widget
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
}
#Override
public void onDeleted(Context context, int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onDeleted(context, appWidgetIds);
}
}
How can i save the ID of every widget i create and delete those that i've deleted with sharedPreferences?
Also from a service i'd like to update the widget like this
private void updateWidget(){
Intent intent = new Intent(this,ToggleWidget.class);
intent.setAction("android.appwidget.action.APPWIDGET_UPDATE");
int[] ids = {ToggleWidget.appWidgetId};
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,ids);
sendBroadcast(intent);
}
How can i retrieve the saved IDs from the service to make the update?
In your Reciever class just override onCreate() method and under that method,
int[] ids = getIntent().getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
Then you will get int array, pls check it, it will work...:)
Take a look at android's Data Storage options. Here is how you could use SharedPreferences...
public class MyService extends Service{
//Class used to store data
private SharedPreferences preferences;
private int[] ids;
private int numIds;
public void retreiveIds(){
preferences = PreferenceManager.getDefaultSharedPreferences(this);
numIds = preferences.getInt("num",0);
ids = new int[numIds];
for(int i=0;i<numIds;i++){
ids[i] = preferences.getInt("ID"+i,0);
}
}
public void saveIds(){
SharedPreferences.Editor ed = preferences.edit();
numIds = ids.length;
//iterate through array and save each id
for(int i=0;i<numIds;i++){
ed.putInt("ID"+i,ids[i]);
}
ed.putInt("num",numIds);
ed.commit();
}
}
There is another way to do it. Code in kotlin:
val thisWidget = ComponentName(context, this.javaClass)
val allWidgetIds: IntArray = appWidgetManager.getAppWidgetIds(thisWidget)
Now you can use these allWidgetIds to update widgets.