Wednesday, November 30, 2011

how to write app Widgets - Home screen widgets ? How to implement different sized home screen widgets?

Recently i gave a presentation in google office singapore for gtug-ph-sg android community about Layouts and Home screen widgets.

http://gtug-ph-sg.blogspot.com/2011/11/android-talk-update.html

So i wished to share more details about how to write home screen widgets.
Also when i attended a google dev talk here happened on nov27th, the key steps to keep a user engaged in keeping your application is

1 - Write home screen widget and keep the user bsy in refreshing the data on widget
2 - Push notifications then and there so that user has not forgotten your application.

So we home screen widget is one way you can keep user bsy with your application. Lets go into the details.

* What is an AppWidget ?

* AppWidget framework

* Steps for creating an AppWidget

* Example demonstrating widget showing google static map of location as home screen widget.


AppWidget:

- AppWidget is a android techinical terminology for "Home screen widgets". What is said in developers guide is "App Widgets are miniature application views that can be embedded in other applications (such as the Home screen) and receive periodic updates."
-AppWidget is a representation of an Android application on the home screen. The application can decide, what representative information it publishes on the home screen



- Home screen widgets are quick glimpse of the fully functional apps like calendar,music,weather applicaiton. Users can quickly get live data from an application.Live data i mean, widgets can keep refreshing the data based on location,time basis, or per schedule input from user.

- Widgets let users interact with the widgets like slide the images like in gallery, scroll the weather data, play,pause songs..etc.






RemoteViews and Launcher:

Before diving into how to implement app widgets, we should be aware of how the appWidgets mechanism works and what are remoteviews ?

How does Launcher (i.e Home screen provider) able to show a view created by another application in its UI ? Functionality and design of the widget is defined by one application but UI of the widget is hosted in another application (Launcher). What would help us achieving this ? Basically we need an IPC mechanism here. Inter process communication. One process sending the UI data to another process Launcher to display in it.

Remoteviews helps in solving this and is the key behind appwidget framework. Remoteviews are parcelable data strucutre that holds information about a view hierarchy and can be transferred from one process to another. Any process can recieve this RemoteView instance via IPC and get a "View" instance from it and add to its Layout and be part of receving process. The creator of remoteview can define actions for the elements in remoteview like what should happen when a button is pressed. Receiving process cannot change these properties, they can only get a view instance and host it.

So who sends remoteviews and how does Launcher communicates with creator.

- AppWidgets framework includes implementing AppWidgetProvider class which is a broadcast receiver which would receive events of when an AppWidget is added to homescreen or deleted from homescreen.

- Launcher is the guy who sends these broadcasts when the appWidgets are added to the home screen with an AppWidgetID

- AppWidgetProvider then sends an RemoteView with the AppWidgetId, which would be then be received by Launcher and Launcher updates the corresponding appWidget with that remoteView.




AppWidget FrameWork

Now lets see the four necessary things involved in creating appWidgets.

* App Widget Provider Metadata XML file

* AppWidgetProvider class

* View layout

* App Widget configuration Activity (Optional)


App Widget Provider Metadata XML file

* Describes the metadata for an App Widget, such as minimum width, minimum height, update frequency, the AppWidgetProvider class.
* It also references App widget's layout file
* This should be defined in res/xml folder.

AppWidgetProvider class

* Defines the basic methods that allow you to programmatically interface with the App Widget, based on broadcast events. (AppWidgetProvider class is a child class of BroadcastReceiver class.)
* Through it, you will receive broadcasts when the ApWidget is updated, enabled, disabled and deleted

View layout

* Intial layout to be displayed when the appWidget is added to the homescreen. It is defined in metadata XML file.

App Widget configuration Activity

* This is an optional activity which users can define to show to the users before adding the appWidget to homescreen.Usually useful in collecting some values required for your appWidget settings.

Building an AppWidget

* Declare an AppWidgetProvider in the Manifestfile

* Create the App Widget Provider Info Metadata XML file

* Create the App Widget Layout XML file

* Write the AppWidgetProvider Class

1 - Declare AppWidgetProvider in Manifest




android:resource="@xml/example_appwidget_info"/>


2 - Create App Widget Provider Info Metadata XML file

xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="294dp"
android:minHeight="72dp"
android:updatePeriodMillis="86400000"  --> Every one day
android:initialLayout="@layout/example_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigure" >


Some of the attributes are added newly in android 3.0
previewImage --> Added in android 3.0
android:previewImage="@drawable/preview"

More details take look at http://developer.android.com/reference/android/appwidget/AppWidgetProviderInfo.html

3 - Create App Widget Layout

* App Widget layouts are based on RemoteViews,which do not support every kind of layout or view widget.

* A RemoteViews object (and, consequently, an App Widget) can support the following layouts and Widget classes
FrameLayout, LinearLayout, RelativeLayoyt
AnalogClock, Button, Chronometer, ImageButton,
ImageView, ProgressBar, TextView

* Android 3.0 supports additional widgets
ListView
GridView
StackView
AdapterViewFlipper

4 - Write AppWidgetProvider Class

* The AppWidgetProvider class extends BroadcastReceiver to handle the App Widget broadcasts. The AppWidgetProvider receives only the event broadcasts that are relevant to this App Widget, such as when the App Widget is updated, deleted, enabled, and disabled.

Methods to override

onUpdate(Context, AppWidgetManager, int[]) - called
when each App Widget is added to a host (unless you use a configuration Activity), Typically the onlymethod that needs to be present

onDeleted(Context, int[])

onEnabled(Context)

onDisabled(Context)

onReceive(Context, Intent)

Screenshot showing demo of adding Digital and analog clock in Home screen:





AppWidget provider - onUpdate

There are certain few points about AppWidget Provider behaviour we need to know.

* First is , android:updatePeriodMillis="86400000" which defines the frequency when the appWidget will be updates. Meaning on this schedule, onUpdate on AppWidget Provider class will be called. The restriction with this timing is the minimum period is 30mins. You cannot give 10000 and except the onUpdate() to be called every 10 secs. Minimum time period is 30 mins.

* Because AppWidgetProvider is an extension of BroadcastReceiver, your process is not guaranteed to keep running after the callback methods returns i.e onUpdate,onEnabled.
So in case, you need to perform some network communication to fetch some data

* Consider starting a Service in the onUpdate() method. And delegate the network communication work to a asynchronous task and update the widget with the result.

* To update an appWidget all you need to know is AppWidgetId and have AppWidgetManager instance. So ideally pass all the appWidgetIds to "service" i.e whenever onUpdate() is triggered call startService() with appWidgetIds as intent data and ask the service to update the widgets with data fetched from network.
appWidgetManager.updateAppWidget(appWidgetId, views);

Now how to do we change the updatePeriodMillis to trigger onUpdate before 30mins.

* Use AlarmManager to update.In onUpdate() when the first time appWidget is added, set a setRepeating alarm for the schedule you wish and pass an pendingIndent to launch the service. In service you can update the appWidgets with appWidgetids and remoteview.

final AlarmManager m = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 
final Intent intent = new Intent(context, MyService.class);  
if (service == null)  
{  
 service = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 
}  
m.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), 1000 * 60, service); 


More details take a look at this post
http://www.parallelrealities.co.uk/2011/09/using-alarmmanager-for-updating-android.html

Multiple sized widgets :

- How do we support multiple sized widgets ? 4x1,4x2,2x1 there are maximum of 4 rows and 4 columns. 4x4 in handsets and tablets support upto 8x7.
Here is one of the way you can achieve this.

- Declare as many AppWidgetProviderInfo metafiles required
2x2 :
android:minWidth="147dp"
android:minHeight="142dp"
4x2 :
android:minWidth="294dp"
android:minHeight="142dp"

- Define corresponding Appwidget classes.

- Declare corresponding AppWidget Broadcast receivers in AndroidManifest.xml


For more details on the cells calculation, minWidth,minHeight please chk in this link.
http://developer.android.com/guide/practices/ui_guidelines/widget_design.html

Snapshot showing four types of appWidgets for the demo application



Things to keep in mind

* Frequency of update should not be high. ex. every 5 mins.It will reduce the battery life of the phone.

* Handled badly your widget could end up making the phone completely unresponsive. Pushing updates to onscreen widgets is a somewhat costly process.

* Avoid nesting of layouts within a layout.

* Give proper padding. As it might collide with adjacent widget. No boundary seperation would be seen.

* Use a nine patch image as background to support multiple sized widgets

* Offload any webrequest through a service to avoid ANRs.


Demo example of widget showing google static map of location as home screen widget.

Quick points of implementation.

* onEnabled() -> Start a service

* onUpdate() -> Get the appWidgetId and appWidgetType and pass to the service.

* onDeleted() -> Get the appWidgetId and pass to the service.

* Service -> Listens for movement change using PhoneStateListener and fetches current location using GPS or Wifi and downloads the static map as Bitmap  and updates the widgets 2x2 and 4x2.

I am not giving in detail explanation of the code. Please go through this block diagram which will give you high level overview. You can go through the code for more details Any queries u can drop me a comment..!!

Implementation overview:







http://code.google.com/apis/maps/documentation/staticmaps/

Demo code:
The entire source code for the demo can be downloaded from the below link.
http://www.4shared.com/get/_FeDVgpD/HomescreenWidget.html

10 comments:

  1. re-upload Demo code plz.

    ReplyDelete
  2. hi ur post is very help full... can u plz help me how to create remainder app that use alarm manager and broadcast receiver. i am showing one notification when remainder time meets. but here i am getting one problem. broadcast receiver is firing more than one time for particular remainder time. can u please tell me how to overcome this problem

    ReplyDelete
  3. You can share the code snippet to analyse what is the problem you are facing.

    ReplyDelete
  4. i had created the weather appplciation project in android by using json parsing concept. i need to change that app to widget. i d't know how to make that updation of weather in widget for every 10 mins will you plz upload the codes for creating a weather widget sir.

    ReplyDelete
  5. I am working on creating a widget that flips on the nearby places(restaurants mainly) as per your location. I have the application for that but now I want to create a widget for the sAme. I am not using Async Task. Can you please guide me how can I do this? Is there a way where I can copy my list var in activity to some file and read that from the widget? Or should I make all the network and api calls in the widget? And can we call 2 services in app widget? Please please help.

    ReplyDelete
    Replies
    1. - You need to make network calls in the widget. Or write a IntentService which does network calls background and you can use the same code for both activity & widget.
      - You can call 2 services in from the widget provider. It your design implementation.

      For more details, refer my sample project code.

      https://github.com/smanikandan14/localguide/blob/master/src/com/mani/localguide/widgetprovider/WidgetProvider.java

      Delete