Honeycomb home screen widgets

samples

stack view widget sample

a weather widget sample

previous notes

A widget provider is essentially a receiver. So anyone can send a broadcase message to it.

Typically this is done by explicitly creating an intent with that receriver classname and sending it with a custom action.

when this is done the custom action needs to be dealt with in the onReceive method.

when you overload the onReceive() you also need to call the super so that it can move the default actions to the respective derived methods.

what does this do?

Tell a view that it needs to refresh its data.

what call backs will it call?

tellt he widget managr to tellt heir views tor refresh.

you will need to register a content observer against the provider and then tell the widget manager that the view has changed.

You will need to set this up in the "enable" of the widget.

setup the remoteviews that contains lists

set the adapters for the remote views

set the call backs for buttons so that they issue more broadcast intents to self

override onreceive to respond to callbacks

In onreceive tell the views that they changed their data

set the remoteviews that it is going to get its adapter through a remote service

An example listview widget layout from google


<FrameLayout...>
  <ListView id="@+id/somelistviewid" ..../>
  <TextView id="@+id/emptylistviewid"..../>
</FrameLayout>

final RemoteViews rv = 
new RemoteViews(context.getPackageName(), R.layout.widget_layout);

rv.setRemoteAdapter(appWidgetIds[i], R.id.weather_list, intent);

Ofcourse the layout id R.layout.widget_layout belongs to the entire layout of the widget where as the R.id.weather_list belongs to just the id of the list view similar to the one above.


RemoteViews views = 
new RemoteViews
(context.getPackageName(), R.layout.bday_widget);

A configur activity, if present, must do very similar things that an "onUpdate" method does.

However the "onUpdate" method should watch out to see if configure activity is not finished (if the update is too frequent). (I think...)

Code/strategy similar to the following can be used both by the configure activity and the onupdate method.


public static void updateAppWidget(Context context, 
        AppWidgetManager appWidgetManager,
        BDayWidgetModel widgetModel) 
{
    RemoteViews views = 
    new RemoteViews(context.getPackageName(), 
                  R.layout.bday_widget);
    
    views.setTextViewText(R.id.bdw_w_name
        , widgetModel.getName() + ":" + widgetModel.iid);
    
    views.setTextViewText(R.id.bdw_w_date
            , widgetModel.getBday());
    
    //update the name
    views.setTextViewText(R.id.bdw_w_days,
            Long.toString(widgetModel.howManyDays()));
    
    Intent defineIntent = new Intent(Intent.ACTION_VIEW, 
            Uri.parse("http://www.google.com"));
            
    PendingIntent pendingIntent = 
        PendingIntent.getActivity(context,
                    0 /* no requestCode */, 
                    defineIntent, 
                    0 /* no flags */);
    views.setOnClickPendingIntent(R.id.bdw_w_button_buy, pendingIntent);
    
    // Tell the widget manager
    appWidgetManager.updateAppWidget(widgetModel.iid, views);
}

Quoting from Google

Home screen widgets are popular with users because they offer fast access to application-specific data directly from the home screen. Android 3.0 lets developers take home screen widgets to the next level, offering more types of content and new modes of interaction with users. Developers can now use more standard UI widget types home screen widgets, including widgets that let users flip through collections of content as 3D stacks, grids, or lists. Users can interact with the home screen widgets in new ways, such as by using touch gestures to scroll and flip the content displayed in a widget


3d stack
grids
lists

RemoteViews.RemoteView

Search for: RemoteViews.RemoteView

As of 2.3 the allowed remote views are

Go to android.widget.RemoteViews.Remoteview
click "use"

absolutelayout
framelayout
linearlayout
relativelayout

analogclock
button
chronometer
datetimeview
imagebutton
iamgeview
progressbar
textview
viewflipper

highlight RemoteView
References
in Project

absolutelayout
framelayout
linearlayout
relativelayout

analogclock
button
chronometer
imagebutton
progressbar

Gridview
stackview
textview
datetimeview
iamgeview

AdapterViewFlipper
viewflipper

//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

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

//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();
}

//setup a list view call back
//we need a pending intent that is unique for this widget id
//send a message to ourselves which we will catch in OnReceive
Intent onListClickIntent = new Intent(context,BDayWidgetProvider.class);
//set an action so that this receiver can distinguish it
//from other widget related actions
onListClickIntent.setAction(BDayWidgetProvider.ACTION_LIST_CLICK);

//because this receiver serves all instances of this app widget
//we need to know which specific instance this message is targeted for
onListClickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);

//Make this intent unique as we are getting ready
//to create a pending intent with it
//The touri method loads the extras as part of the uri string
//the data of this intent is not used at all except
//to establish this intent as a unique pending intent.
//See intent.filteEquals() method to see how intents are compared
//to see if they are unique.
onListClickIntent.setData(
    Uri.parse(
        onListClickIntent.toUri(Intent.URI_INTENT_SCHEME)));

//we need to deliver this intent later when
//the remote view is clicked as a broadcast intent
//to this same receiver.
final PendingIntent onListClickPendingIntent = 
    PendingIntent.getBroadcast(context, 0,                    
        onListClickIntent, 
        PendingIntent.FLAG_UPDATE_CURRENT); 

//Set this pending intent as a template for
//the list item view.
//Each view in the list will then need to specify
//a set of additional extras to be appended to this template
//and then broadcast the final template.
//See how the remoteviewsfactory() sets up the each item
//in the list remoteview.
//See also docs for RemoteViews.setFillIntent()
rv.setPendingIntentTemplate(R.id.bdw_list_view_id, 
        onListClickPendingIntent);

public RemoteViews getViewAt(int position) 
{        
    RemoteViews rv = 
    new RemoteViews(this.mContext.getPackageName(),
                          R.layout.item_layout);
                          
    this.loadItemOnClickExtras(rv, position);
    return rv;
}    

private void loadItemOnClickExtras(RemoteViews rv, int position)
{
    Intent ei = new Intent();
    ei.putExtra(BDayWidgetProvider.ACTION_LIST_ITEM_TEXT,
                                           "Hello:" + position);
    rv.setOnClickFillInIntent(R.id.widget_list_item_id, ei);
    return;
}

<?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"
/>

There a number of new remoteviews
  Main ones are: 
    ListView
    GridView

There are two classes introduced to support collection views
  RemoteViewService
  RemoteViewsFactory

There are two new methods to support list clciks
  setOnClickPendingIntentTemplate()
  setOnClickFillIntent

Since API 3 you can choose to cancel the appwidget creation

you need to return result_cancelled. However if you press back it is not clear if this is deemed as result_cancel. I haven't checked. It should. but not sure.


notifyAppWidgetViewDataChanged
: The view is a collection view
partiallyUpdateAppWidget
: for thsi call the view state is not restored

AppWidgetManager will save teh remoteviews and restores them on configuration changes.

a way for a package to explicily list and remove their widgets (essentially a cleanup operation)

Right now I can list the widgets but I have no way of removing them. May be that is a prerogative of the host?? But still as a package I would probably want to know.

Read up on RemoteViews service and factory at this link to get a full picture of how list widgets work.