remote views
satya - Thursday, February 24, 2011 3:50:26 PM
New APIs
setRemoteAdapter(widgetid, viewid, serviceintent)
AbsListView:setRemoteViewsAdapter(...)
satya - Thursday, February 24, 2011 3:54:25 PM
My previous notes on widgets
satya - Friday, February 25, 2011 10:45:10 AM
Other related classes
RemoveViewsService
RemoteViewsFactory (thin wrapper on adapter)
satya - Friday, February 25, 2011 10:48:44 AM
RemoteViewFactory
Acts like an adapter that supplies views for a given data set. An implemented adapter usually holds a data set and then supply the necessary views to a list.
in case of remoteviews factory this seem to return RemoteViews instead of plain views.
satya - Friday, February 25, 2011 10:54:52 AM
RemoveViewsService
This takes an intent and returns a remoteviewsfactory for that intent.
In short it seem to return, then, the data for a list to display and also hwo to display (because of the adapter emulated by the remoteviews factory)
you are expected to inherit from this service and return an implementation ofthe remoteviewsfactory.
This seem a bit like long running services.
satya - Friday, February 25, 2011 1:40:44 PM
what is the meaning of this code
//Setting up a component to call
Intent intent = new Intent(context, WeatherWidgetService.class);
//load up the widget id
//service can retrieve it
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
//why this?
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
//Load up remote views
//what other constructors are there?
RemoteViews rv =
new RemoteViews(context.getPackageName(), R.layout.widget_layout);
//setup the service to provide the remote view
rv.setRemoteAdapter(appWidgetIds[i], R.id.weather_list, intent);
satya - Friday, February 25, 2011 1:43:52 PM
what is toUri() first
Convert this Intent into a String holding a URI representation of it. The returned URI string has been properly URI encoded, so it can be used with Uri.parse(String). The URI contains the Intent's data as the base URI, with an additional fragment describing the action, categories, type, flags, package, component, and extras.
returns a string
satya - Friday, February 25, 2011 1:46:18 PM
when do I use intent toUri() method
when do I use intent toUri() method
satya - Friday, February 25, 2011 2:36:11 PM
intent touri
intent touri
Search Google for: intent touri
Search Android Developers Group for: intent touri
Search Android Beginers Group for: intent touri
satya - Saturday, February 26, 2011 8:56:54 AM
RemoteViews setOnclickPendingIntent
RemoteViews setOnclickPendingIntent
satya - Saturday, February 26, 2011 8:57:58 AM
setPendingIntentTemplate
setPendingIntentTemplate
satya - Saturday, February 26, 2011 9:07:45 AM
Read this discussion why setData(intent.toUri) is used
satya - Saturday, February 26, 2011 9:09:49 AM
Summary
Ok I get it!!!!
this is to do with (I think) how two *innocent* intents could be deemed the same even if they are different owing to their *extras".
The uniqueness of intents when dealing with pending intents does not take into account the "EXTRAS" but it does take into account the "data" portion.
And hence the following code simulating this uniqueness by converting an intent with extras first to a string (which will include the extras as appended string) and then set that unique string as data URI. :) I get it.
satya - Monday, February 28, 2011 11:24:18 AM
The following link documents explicitly what are supported by RemoteView
The following link documents explicitly what are supported by RemoteView
This is also the main documentation link for app widgets from google.
satya - Tuesday, March 01, 2011 9:05:48 AM
netmite RemoteViews.java
netmite RemoteViews.java
satya - Tuesday, March 01, 2011 9:17:23 AM
what remoteviews are allowed?
Looks like all views with annotation of RemoteViews.RemoteView.
satya - Tuesday, March 01, 2011 9:29:36 AM
An interesting way of finding this out
satya - Tuesday, March 01, 2011 12:21:08 PM
Any view that has an annotation of RemoteViews.RemoteView is a remoteview
Any view that has an annotation of RemoteViews.RemoteView is a remoteview
annonymous - Tuesday, March 01, 2011 12:23:04 PM
You can use eclipse to help you with this
put an import on RemoteView interface
highlight RemoteView
Right click
Go to References
Choose "in Project"
You will see a list of views that are capable of remote views
annonymous - Tuesday, March 01, 2011 12:23:39 PM
As of 3.0 these are
absolutelayout
framelayout
linearlayout
relativelayout
analogclock
button
chronometer
imagebutton
progressbar
Gridview
stackview
textview
datetimeview
iamgeview
AdapterViewFlipper
viewflipper
satya - Tuesday, March 01, 2011 12:25:16 PM
For 2.3 these are
absolutelayout
framelayout
linearlayout
relativelayout
analogclock
button
chronometer
imagebutton
progressbar
viewflipper
datetimeview
iamgeview
textview
satya - Wednesday, March 02, 2011 1:55:32 PM
RemoteViewsFactory
RemoteViewsFactory
Search Google for: RemoteViewsFactory
Search Android Developers Group for: RemoteViewsFactory
Search Android Beginers Group for: RemoteViewsFactory
satya - Wednesday, March 02, 2011 4:02:54 PM
onCreate and RemoteViewsFactory
onCreate and RemoteViewsFactory
Search for: onCreate and RemoteViewsFactory
There are a number of callbacks on this factory. The main ones correspond to a collection view adapter. However there are "onCreate" and "onDestroy".
One is bound to wonder when do they get called?
These factories are explicitly constructed.
The variable of construction is the intent.
If this were to serve multiple clients it can only distinguish this from the incoming intent that was used for its construction. No other method of this class takes the intent or view or widget context.
Also because the "intent" is passed in as a constructor it is too late if this is to decide to serve certain clients through oncreate.
Does this mean this factory is somewhere cached based on the "intent" uniqueness???
satya - Friday, March 04, 2011 9:22:30 AM
you can construct a remoteview only from a layout by loading stuff
you can construct a remoteview only from a layout by loading stuff
satya - Friday, March 04, 2011 10:52:13 AM
here is an example
public RemoteViews getViewAt(int position)
{
RemoteViews rv =
new RemoteViews(this.mContext.getPackageName(),
R.layout.item_layout);
return rv;
}
satya - Friday, March 04, 2011 10:52:53 AM
Here is a simple layout without even a container
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_list_item_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Temporary"
/>
satya - Friday, March 04, 2011 1:26:21 PM
A List RemoteView: wiget provider responsiblities
//basic setup
load the layout with the list view in it
identify the list view with an id
set a remote adapter factory for the list view id
//setup an onclick callback with an action
Configure an intent to self invoke
create a pending intent out of this intent
pending intent needs to be be uniquie for this widget id
call setOnClickPendingIntentTemplate
set this pending intent for any list item click
//provide a custom onreceive
override onreceive to get this special pendign intent
define a special action for this pending intent
for all other actions call the base onreceive
satya - Friday, March 04, 2011 1:28:35 PM
A List RemoteView: list view factory responsiblities
provide a list item view
this view goes inside the list
create an intent with extras pointing to this row
pass the intent through setOnClickFillIntent
this row information will then be attached
to the previous pending intent template
satya - Friday, March 04, 2011 1:46:07 PM
working with custom actions in the widget provider
//Define an action string in your provider
public static final String ACTION_LIST_CLICK =
"com.androidbook.homewidgets.listclick";
//Override your onReceive to specialize it
@Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(BDayWidgetProvider.ACTION_LIST_CLICK))
{
//this action is not one widget actions
//this is a specific action that is directed here
dealwithListAction(context,intent);
return;
}
//make sure you call this
super.onReceive(context, intent);
}
public void dealwithListAction(Context context, Intent intent)
{
Toast t = Toast.makeText(context,"Clicked",Toast.LENGTH_SHORT);
t.show();
}
satya - Saturday, March 05, 2011 12:01:39 PM
A sample remoteview factory
class TestRemoteViewsFactory
implements RemoteViewsService.RemoteViewsFactory
{
private Context mContext;
private int mAppWidgetId;
private static String tag="TRVF";
public TestRemoteViewsFactory(Context context, Intent intent)
{
mContext = context;
mAppWidgetId =
intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
Log.d(tag,"factory created");
}
//Called when your factory is first constructed.
//The same factory may be shared across multiple
//RemoteViewAdapters depending on the intent passed.
public void onCreate()
{
Log.d(tag,"onCreate called for widget id:" + mAppWidgetId);
}
//Called when the last RemoteViewsAdapter that is
//associated with this factory is unbound.
public void onDestroy()
{
Log.d(tag,"destroy called for widget id:" + mAppWidgetId);
}
//The total number of items
//in this list
public int getCount()
{
return 20;
}
public RemoteViews getViewAt(int position)
{
Log.d(tag,"getview called:" + position);
RemoteViews rv =
new RemoteViews(
this.mContext.getPackageName(),
R.layout.list_item_layout);
this.loadItemOnClickExtras(rv, position);
return rv;
}
private void loadItemOnClickExtras(RemoteViews rv, int position)
{
Intent ei = new Intent();
ei.putExtra(TestListWidgetProvider.ACTION_LIST_ITEM_TEXT,
"Position of the item Clicked:" + position);
rv.setOnClickFillInIntent(R.id.widget_list_item_id, ei);
return;
}
//This allows for the use of a custom loading view
//which appears between the time that getViewAt(int)
//is called and returns. If null is returned,
//a default loading view will be used.
public RemoteViews getLoadingView()
{
return null;
}
//Not sure how this matters.
//How many different types of views
//are there in this list.
public int getViewTypeCount()
{
return 1;
}
//The internal id of the item
//at this position
public long getItemId(int position)
{
return position;
}
//True if the same id
//always refers to the same object.
public boolean hasStableIds()
{
return true;
}
//Called when notifyDataSetChanged() is triggered
//on the remote adapter. This allows a RemoteViewsFactory
//to respond to data changes by updating
//any internal references.
//Note: expensive tasks can be safely performed
//synchronously within this method.
//In the interim, the old data will be displayed
//within the widget.
public void onDataSetChanged()
{
Log.d(tag,"onDataSetChanged");
}
}
satya - Monday, December 12, 2011 10:56:03 AM
Here is an example remoteview service that returns the factory
public class TestRemoteViewsService
extends android.widget.RemoteViewsService
{
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent)
{
return new TestRemoteViewsFactory(
this.getApplicationContext(), intent);
}
}
satya - Monday, December 12, 2011 11:03:04 AM
Who calls this??
well, earlier in this page, you have learned that a broadcast receiver is responsible for returning a remoteviews object to the app widget manager. This remoteviews object then gets painted in the space.
In 3.0 this remoteviews object can contain a child list view id that is tied to a service name. This is done by setting a pending intent on that list view id. This information is probably kept in a registry somewhere.
Now when it is time to display that remvoteviews object, someone will invoke the service using the pending intent and gets back a remote views factory, which is just like a list view adapter except that it returns remove views.
It is possible that this factory may be reused multiple times, and hence giving raise to the onCrate() method in that factory which is suggested to be used for onetime initialization.
satya - Tuesday, December 13, 2011 10:06:57 PM
Now that souce code for ICS is available I can see some light
Now that souce code for ICS is available I can see some light
satya - Tuesday, December 13, 2011 10:10:10 PM
So, tell me, is the factory object cached?
Yes it is. It is cached based on the uniqueness of intents. So everytime an update of the widget gets called, the factory is not recreated. The old one is used as long as the update is for the same widget id and no other parameters of the intent has changed.
You can see this in the source code of RemoteViewsService class in the android source code.
satya - Tuesday, December 13, 2011 10:11:49 PM
What about onDestroy()? who calls that?
Well this is called when the last widget id goes away for your widget as you delete the widgets. The ondestroy() is triggered.
this can be seen in the source code of AppWidgetService.java
satya - Tuesday, December 13, 2011 11:04:38 PM
No, that is not entirely true....
It is not the last widget id. everytime a widget is removed from the home screen, the unique widet id for that instance is gone. At that point any remote service intent that is tied to that widget are decremented or dereferenced. If the ref count goes to zero, meaning no widget id is using that intent then the corresponding factory on destroy is called.
Notice that you can provide the same RemoteViewService for creating multiple types of RemoteViewFactory objects depending on the inent that is received.
satya - Tuesday, December 13, 2011 11:05:49 PM
So theoretically....
If you just dragged on a widget and dragged off a widget I believe you should see the onDestroy() called.
satya - Tuesday, December 13, 2011 11:53:09 PM
More wrinkles in the fabric...
It may be counterproductive to pass widget id to the intent that kicks off the remote view service
Because when you do that, especially when you make the intent unique by forcing the extra, you end up with too many factories: one for each widget id
Perhaps you dont need that.
satya - Tuesday, December 13, 2011 11:55:32 PM
onDestroy() logic
1. Consider the widget id that is being removed because we dragged the widget to the trash can
2. Walk through all the intents (an intent may be serving multiple widget ids)
3. For each intent, remove the widget id from its list
4.If the intent has no widget ids in the process then call destroy on that factory
satya - Wednesday, December 14, 2011 4:17:42 PM
Final word on RemoteViewsFactory and Service
1. Do not overload your service intent with widget id. if you do, you will defeat the purpose of factory caching.
2. You can do the initialization for your factory either in the constructor or in the onCreate() method. These are one to one.
3. The constructor and the onCreate() gets called when you drag your first widget on to the home page. If you drag it again for a second instance of that widget, if you do it right, your oncreate() or the constructor should not be called again.
4. As you start removing the widgets, the last widget id removal should trigger onDestroy().
5. I have tested this in ICS and it works. The ondestroy() never got called when I have tested under 3.0 earlier this year.
6. You can work with your service intents in such a way that a single service can instantiate all your factories for your widgets in that package. You will load up the widgets either with extras or actions to diffrentiate them enough for each remote view. when you do this make sure you dont totally differentiate them all the way to the widget instances ids. So there is a balance.