How does an android activity interact with app-widget (Home screen widgets)? - android

If I have an activity, so how can I use some of its methods (functionalities) in android widgets. Do I have to recreate the logic? What are the ways of interaction? Through intent or through service? Please please reply.
Update: I have a .java class that implements Parcelable and contains the method that returns the list. I want that list in my widget.
public Stores getStoreListings() {
Log.i("List val","mStoreListings");
return mStoreListings;
}
Can I use Intent in my app widget to get this method or variable? This is not an activity..
UPDATE2: Using Async task, its not working also..Where I am going wrong?? PLease help..
public class ListViewWidget extends AppWidgetProvider{
#Override
public void onUpdate(Context context,AppWidgetManager appWidgetManager, int []appWidgetIds){
super.onUpdate(context, appWidgetManager, appWidgetIds);
RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.list_layout);
ComponentName thisWidget = new ComponentName(context,ListViewWidget.class);
String url=String.valueOf("https://api.paypal.com/v1/stores?view=local&lat=37.3756096&lng=-121.9239449&radius=50&count=20&start_id=1&country_code=US");
FetchTask fetchTask=new FetchTask();
//fetchTask.appWidgetManager=appWidgetManager;
//fetchTask.onPostExecute(updateViews,context);
fetchTask.execute();
//context.startService(new Intent(context,UpdateService.class));
}
public static class FetchTask extends AsyncTask<URL,Integer,Stores>{
String text=null;
#Override
protected Stores doInBackground(URL... arg0) {
// TODO Auto-generated method stub
HttpClient httpClient=new DefaultHttpClient();
HttpContext localContext=new BasicHttpContext();
HttpGet httpGet=new HttpGet("https://api.paypal.com/v1/stores?view=local&lat=37.3756096&lng=-121.9239449&radius=50&count=20&start_id=1&country_code=US");
Stores newList=new Stores();
try{
HttpResponse response=httpClient.execute(httpGet,localContext);
HttpEntity entity=response.getEntity();
text=String.valueOf(entity);
//assign the list to the the correct entity type
//newList=..;
Log.i("Inside on update with Http call","Text"+text);
}
catch(Exception e){
e.printStackTrace();
}
return newList;
}
protected void onPostExecute(RemoteViews updateViews,Context context){
Intent intent=new Intent("android.appwidget.action.APPWIDGET_UPDATE");
PendingIntent pendingIntent=PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
updateViews.setTextViewText(R.id.text_view, text);
updateViews.setOnClickPendingIntent(R.id.next, pendingIntent);
// Push update for this widget to the home screen
ComponentName thisWidget = new ComponentName(this, ListViewWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
}
}
}

You can use the same server side logic, but you probably want to copy and paste it to a new service.
App Widgets work differently with their lifecycle. If you want your app widget to update periodically, you set that in your app widget config xml. Android will then call your provider when it is time to update the data.
At that point is where you would start your service, where you can reuse the logic to fetch whatever data you need. The only different is once the fetching of the data is done, it needs to be sent to the widget provider and not the activity.
Thats a basic overview of widgets, but to answer your question, it is best to re implement your logic to suit the app widget lifecycle.

Ok, you have activity and service (or AsyncTask) for now, right? What athor is trying to say - your data-fetching logic should be in Service or AsyncTask. But according to AppWidget lifecycle you need to use Service to fetch new data on schedule and to update your widget views (via RemoteViews).
Thats where you can reuse your data-fetching logic - for example, if you had AsyncTask in your activity - you can use instance of same AsyncTask in this new Service.
UPD.
No. Usually, you should start your own service class derived from IntenService (for example) with intent from your widget. Then in service you can just simply get all your data in the same way you did it before.
Check this: AppWidgetProvider extends BroadcastReceiver class.
"
A BroadcastReceiver object is only valid for the duration of the call to onReceive(Context, Intent). Anything that requires asynchronous operation is not available, because you will need to return from the function to handle the asynchronous operation, but at that point the BroadcastReceiver is no longer active and thus the system is free to kill its process before the asynchronous operation completes.
In particular, you may not show a dialog or bind to a service from within a BroadcastReceiver. For the former, you should instead use the NotificationManager API. For the latter, you can use Context.startService() to send a command to the service
"
More details on http://developer.android.com/reference/android/content/BroadcastReceiver.html

Related

Is it possible to get data from RemoteViewsService from AppWidgetProvider?

I want to provide a widget for my app and when a user click a button, the app will be opened with the data from RemoteViewsService. Is it possible to get it? To setup the list, we need RemoteViews#setRemoteAdapter. Is there any like RemoteViews#getRemoteAdapter?
public class WidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
Intent itemIntent = new Intent(context, WidgetItemService.class);
views.setRemoteAdapter(R.id.item_list, itemIntent);
views.setEmptyView(R.id.item_list, R.id.empty);
Intent buttonIntent = new Intent(context, ActivityA.class);
//TODO: Something like this
buttonIntent.putExtra(EXTRA_DATA, views.getRemoteAdapter().getData());
PendingIntent buttonPendingIntent = PendingIntent.getActivity(context, 0, buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.button, buttonPendingIntent);
}
}
}
Even if there was a method like getRemoteAdapter(), I'd prefer another way of passing the data to the Activity from your AppWidgetProvider. The main reason for this is security:
Everything you pass as Intent extra with startActivity() is not private to your application.
PendingIntent.getActivity() will call startActivity() under the hood, so it's basically the same problem.
But as your RemoteViewsService extends from Service, you can simply start the Activity, and the Activity can ask the Service for the data you want to display. There are several ways of communication between an Activity and a Service (e.g. sending local broadcasts via LocalBroadcastManager) - it's your choice (although using a bound Service may be a good idea because this type seems to be preferred for future android versions)

How do I pass data from a widget config class to a widget provider class via intents?

I am not going to delete this question as commons brings up some excellent points below, but I did rework the code and ask the question differently here: How do I retrieve shared preferences data in a Widget Service class without passing in incorrect default values or getting null pointer errors?
I am working on an app that takes a user's input choices and passes them to a widget. It is currently running a service to manage it and it works well, but I cannot figure out how to pass a String from one to the other effectively. Here is my code so far:
//First Widget config is called:
public class WidgetConfig extends Activity{
//Stuff happens here to get data from EditTexts and spinners and converts
//them to strings.
//Eventually a button is pressed which enters all the information:
public void onClick(View v) {
//I have already tried shared preferences like this:
//This was intended to give the shared preferences a unique identifier.
//It does not work for what I am trying to do
String str = Integer.toString(appWidgetId);
sp.putString(editor, str + "::" + "username", user_name);
//The appWidgetID was unique and so I thought it would work as an
//identifier for shared prefs.
//This is the intent for opening the provider
Intent intentUpdate = new Intent(context, MailWidgetProvider.class);
//I also attempted to put items here:
intentUpdate.putExtra("username", user_name);
//I left out the rest of the pending update code as it is irrelevant to this.
}
}
//Next the AppWidgetProvider is called
public class MailWidgetProvider extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
ComponentName thisWidget = new ComponentName(context,
MailWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
//This is the intent to open up and run the service
Intent intent = new Intent(context.getApplicationContext(),
MailWidgetUpdateService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);
context.startService(intent);
}
}
//Service Class
public class MailWidgetUpdateService extends Service {
public void onStart(Intent intent, int startId) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this
.getApplicationContext());
int[] allWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
ComponentName thisWidget = new ComponentName(getApplicationContext(),
MailWidgetProvider.class);
int[] allWidgetIds2 = appWidgetManager.getAppWidgetIds(thisWidget);
//Loop through the IDs
for (int widgetId : allWidgetIds) {
int awid = widgetId;
String str = Integer.toString(widgetId);
String user_name = sp.getString(settings, str + "::" + "chosen_accout_string", "Loading...");
Log.d(TRACKING_USERNAME, user_name);
/*
Issue Here, see explanation below
*/
}
}
How do I retrieve the extras in the Widget Provider class from the widget config class and how do I go about passing them on to the service after receiving them?
You start by not doing much of any of that.
Your AppWidgetProvider is merely one means of updating the app widget contents, one that will specifically be used by Android when your app widget is added and on periodic updates as requested by your app widget metadata. Moreover, bear in mind that an instance of your AppWidgetProvider is used just once and is then discarded.
If you want to update your app widget in other places, go update the app widget, by creating the RemoteViews and giving them to an AppWidgetManager. Your AppWidgetProvider has nothing to do with it. To quote the documentation:
When an App Widget uses a configuration Activity, it is the responsibility of the Activity to update the App Widget when configuration is complete. You can do so by requesting an update directly from the AppWidgetManager.
If you want to have a common implementation of the update-the-app-widget logic, put that is some common class that is used by your configuration Activity, your AppWidgetProvider, and anything else that needs to update the app widget contents.
So, when the user configures the app widget through the activity, you need to:
update the app widget yourself via the AppWidgetManager, and
hold onto the configuration data (in a database, SharedPreferences, or other sort of file) so that it can be used for future updates

Android Widgets: Not able to retain old values

I will make it simple. I have my widget code. My widget layout contains a linear layout with one button in it. In my widget code, I initialize a String List with some values in it.
When I click the button in my widget, I have to update my List with some more values.
So, this is my code,
List<String> myList = null;
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
myList = new List<String>();
myList.add("1");
myList.add("2");
...
Intent intent = new Intent(context, getClass());
intent.setAction("CALL_UPDATE");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
....
}
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("CALL_UPDATE")) {
Toast.makeText(context, "Intent received"+myList , 2000).show();
}
}
When the button is clicked, the broadcast is properly received by onReceive(). The problem is, on onReceive(), I see my list as null instead of some values in it as some string objects were added earlier.
Can anyone help?
Thx!
Rahul.
Documentation says:
This has important repercussions to what you can do in an
onReceive(Context, Intent) implementation: anything that requires
asynchronous operation is not available, because you will need to
return from the function to handle the asynchronous operation, but at
that point the BroadcastReceiver is no longer active and thus the
system is free to kill its process before the asynchronous operation
completes.
In particular, you may not show a dialog or bind to a service from
within a BroadcastReceiver. For the former, you should instead use the
NotificationManager API. For the latter, you can use
Context.startService() to send a command to the service.
And:
onReceive() is normally called within the main thread of its process,
so you should never perform long-running operations in it (there is a
timeout of 10 seconds that the system allows before considering the
receiver to be blocked and a candidate to be killed). You cannot
launch a popup dialog in your implementation of onReceive().
Edit:
AppWidgetProvider is a BroadcastReceiver and it's instance (and so it's fields) will be deleted after it's lifcycle.When you create a new instance of widget in HomeScreen,onUpdate and onReceive of AppWidgetProvider invoke and list of this instance of AppWidgetProvider is not null.But after invoking onReceive (for example 10 seconds)this instance will delete.When you click on button ,second instance of AppWidgetProvider will be create and it's list is null.
You can save your list public static field of a class and retrieve it when you need.

android widget fetching data with service, cannot work with more than one at once

My widget gets data from the internet every 3 minutes, some are displayed directly on the widget and others are stored in SharedPreferences so when the user taps on the widget that information appears as a dialog. When having more than one widget running, no matter which widget I click the log says the appWidgetId comes from one of them always
My problem seems to be the way I'm declaring the widget's setOnClickPendingIntent(). I'm doing this inside the service, right before fetching the data and since the same service is run by every (widget) AlarmManager, every widget gets the PendingIntent from the last service ran.
public class WidgetService extends Service
{
#Override
public void onStart(Intent intent, int startId)
{
Intent intentUmbrales = new Intent(context, LaunchUmbralesDialog.class);
intentUmbrales.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
PendingIntent pendingIntentUmbrales = PendingIntent.getActivity(context,0,intentUmbrales,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.widget);
// views.setOnClickPendingIntent(R.id.energia_widget, pendingIntentImei);
views.setOnClickPendingIntent(R.id.imageLogo_widget, pendingIntentUmbrales);
//..then I fetch data, store the rest in SharedPreferences and update widget remoteViews
}
}
How can I avoid this? How can I make an individual "button" for each widget with getting them overlapped? Also note that I've already tried to declare those PendingIntents in the AppWidgetProvider's onUpdate() method (inside a loop for every appWidgetId from the array given by the method)
Thanks in advance!
Regards, Rodrigo.
When declaring the .setOnClickPendingIntent() first add to the Intent
Uri data = Uri.withAppendedPath(
Uri.parse(URI_SCHEME + "://widget/id/")
,String.valueOf(appWidgetId));
intent.setData(data);
so that each widget gets a unique ID and they don't get messed up!

Start IntentService from Activity and refresh Activity when IntentService is finished

In my Android application, I have a simple list view with adapter. There's a heavy query which is to fill the list view with data. So I put it to an IntentService that runs in another thread.
The IntentService is normally running separately, on its own, just to query some data and insert it into the SQLite database.
But now I would like to have the following possibility:
The activity starts the IntentService with startService().
The IntentService does its heavy work.
When the IntentService is finished, it should inform the activity about the result so that the activity can be refreshed to show the new data.
Is this possible? I read a lot of questions here on Stack Overflow on this topic. But in every question, there was another solution. So I want to ask you all: Which solution is the best for my purpose?
Binding the IntentService to the Activity does not seem to be the best solution as there might be conflicts with configuration changes of the activity etc. Correct?
This blog post suggests using AIDL with Parcelables - which sounds very complex to me. There is an easier way, isn't it?
One could set up a broadcast receiver in the activity and fire this broadcast in the IntentService when it is finished.
Some people say you should use createPendingResult() to pass a PendingIntent to the IntentService. If the IntentService finds that PendingIntent in its extras, it uses this to trigger off onActivityResult() in the Activity. Is this the way to choose?
As an example, I use a ResultReceiver to call notifyDataSetChanged() on the adapter of my Activity (which extends ListActivity). It can be adapted to do whatever you need.
ResultReceiver code:
public class MyResultReceiver extends ResultReceiver {
private Context context = null;
protected void setParentContext (Context context) {
this.context = context;
}
public MyResultReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult (int resultCode, Bundle resultData) {
// Code to process resultData here
((BaseAdapter) ((ListActivity)context).getListAdapter()).notifyDataSetChanged();
}
}
MyActivity code:
public class MyActivity extends ListActivity {
private MyResultReceiver theReceiver = null;
...
private void callService () {
theReceiver = new MyResultReceiver(new Handler());
theReceiver.setParentContext(this);
Intent i = new Intent("com.mycompany.ACTION_DO_SOMETHING");
// Code to define and initialize myData here
i.putExtra("someData", myData);
i.putExtra("resReceiver", theReceiver);
startService(i);
}
}
IntentService code:
Bundle resultBundle = new Bundle();
ResultReceiver resRec = intent.getParcelableExtra("resReceiver");
// Do some work then put some stuff in resultBundle here
resRec.send(12345, resultBundle);
When the IntentService completes, it should use LocalBroadcastManager to send an intent to any registered activity.
The IntentService will contain code like this:
private void sendBroadcast() {
Intent intent = new Intent("myBroadcastIntent");
intent.putExtra("someName", someValue);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
The activity receiving the notification will contain code like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String someValue = intent.getStringExtra("someName");
// ... do something ...
}
};
LocalBroadcastManager.getInstance(this)
.registerReceiver(receiver, new IntentFilter("myBroadcastIntent"));
}
For more depth, see the blog post Using LocalBroadcastManager In Service To Activity Communications.
None of the other answers references the official android documentation
https://developer.android.com/training/run-background-service/report-status.html
that states clearly that for the Activity-IntentService communication "The recommended way to send and receive status is to use a LocalBroadcastManager, which limits broadcast Intent objects to components in your own app"!
I would suggest using a Broadcast Receiver in the The Activity waiting for the result.
Your Service would just use sendBroadcast with a custom Intent.
I think the event bus is the way to go. Simple and effective interprocess communication.
http://square.github.io/otto/
https://github.com/greenrobot/EventBus

Categories

Resources