Thursday, March 1, 2012

How to create AppWidget in android?


AppWidgets are small views or widgets that can be embedded in another application. The containing application is called App Widget Host. One example of host application is the Home Screen application. When you long-press the home screen and selecting Widgets option will present you a list of widgets available. This is a two part series on how to create App Widgets. In this part I will stick on to the basics of creating the widget. In the second part I will explain how to provide more advanced topics.
We can create our own App Widgets by extending AppWidgetProvider class, which is actually a BroadcastReceiver with action android.appwidget.action.APPWIDGET_UPDATE. To create an App Widget we extends this class and override the onUpdate method. Then we have to specify the App Widget in AndroidManifest.xml file using the<receiver> tag. Finally we have to describe the App Widget in using AppWidgetProviderInfo object in an XML file.

Creating AppWidget

To create and App Widget, we create a class extending AppWidgetProvider. The AppWidgetProvider class has following methods:

void onEnabled(Context context)
void onDisabled(Context context)
void onDeleted(Context context, int[] appWidgetIds)
void onUpdate(Context context,AppWidgetManager appWidgetManager, int[] appWidgetIds)
void onReceive(Context context, Intent intent)
The method onEnabled is called when an instance is create for the first time, i.e. when the user add the widget for the first time. It will not be called for subsequent additions. In this method we can perform any startup tasks.
The method onDisabled is called when the last instance of the widget is deleted. This method is to do some cleanup tasks.
The onDeleted is called when each instance of the widget is deleted.
The onUpdate method is first called when the widget is added to the host. After that this method will be called after the specified time intervals. This is the most important method of the App Widget. In this method we update the view with the data we want to display. If the data is readily available, we can display it in this method itself, if the data is not available and need to be fetched then it is better to create a Service application which actually fetch the data and update the widget. This is to avoid the ANR (Application Not Responding) error. We can update the widgets usingAppWidgetManager’s updateAppWidget() method.

Declaring the App Widget

We have to declare the App Widget in the AndroidManifest.xml file using the <receiver> tag. As I told you before the AppWidgetProvider is actually is a BroadcastReceiver. Anything that can be done using this class can also be done using the BroadcastReceiver. Following snippet specifies the GPS App Widget:

<receiver android:name=".GPSWidgetProvider">
        <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/gpswidgetinfo" />
</receiver>
Everything is same as specifying BroadcastReceiver except the meta-data section, here we describe our App Widget in a separate XML file.

Describing the App Widget

App Widget description is specified in a separate XML file. In this XML file we specify the minimum width and height of the widget, update period, widget layout, configuration screen layout, etc. Following snippet describes the GPS App Widget:
<appwidget-provider xmlns:android="http://schemas>android>com/apk/res/android"
    android:minWidth="294dp"
    android:minHeight="72dp"
    android:updatePeriodMillis="900000"
    android:initialLayout="@layout/gpswidget">
</appwidget-provider>
The minWidth specifies the minimum width the widget, minHeight specifies the minimum height of the widget. TheupdatePeriodMillis specifies the update period in milliseconds. The initialLayout specifies the widget’s layout.

EXAMPLE
The example application provided is a widget that displays the GPS coordinates the device. The GPS coordinates are reverse geo-coded to get the name of location and will be displayed if it is available. To reverse geo-code the location I used the class Geocoder. The widget will be updated in every 15 minutes.

In Manifest Write down following code...

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.samples.gpswidget"
    android:versionCode="1"
    android:versionName="1.0" >

  
    <application
        android:enabled="true"
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name=".InfoActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name=".GPSWidgetProvider" >
            <intent-filter >
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/gpswidgetinfo" />
        </receiver>

        <service android:name=".GPSWidgetProvider$GPSWidgetService" >
        </service>
    </application>

    <uses-sdk
        android:minSdkVersion="3"
        android:targetSdkVersion="8" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

</manifest>

In Source Code Package create 3 Class AppLog,....

1)AppLog
package com.samples.gpswidget;

import android.util.Log;

public class AppLog {
        private static final String APP_TAG = "GPSWidget";
        
        public static int logString(String message){
                return Log.i(APP_TAG, message);
        }
}
2)GPSWidgetProvider

package com.samples.gpswidget;
import java.util.List;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.location.Address;
import android.location.Criteria;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.widget.RemoteViews;

public class GPSWidgetProvider extends AppWidgetProvider {
  @Override  public void onDeleted(Context context, int[] appWidgetIds) {    super.onDeleted(context, appWidgetIds);  }  @Override  public void onDisabled(Context context) {    super.onDisabled(context);  }  @Override  public void onEnabled(Context context) {    super.onEnabled(context);  }  @Override  public void onReceive(Context context, Intent intent) {    super.onReceive(context, intent);  }  @Override  public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {    super.onUpdate(context, appWidgetManager, appWidgetIds);    
    final int N = appWidgetIds.length;

        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];

            Intent intent = new Intent(context, InfoActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.gpswidget);
            views.setOnClickPendingIntent(R.id.txtInfo, pendingIntent);

            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
     
        context.startService(new Intent(context,GPSWidgetService.class));
  }  
  public static class GPSWidgetService extends Service{    private LocationManager manager = null;    
    private LocationListener listener = new LocationListener() {      @Override      public void onStatusChanged(String provider, int status, Bundle extras) {}      @Override      public void onProviderEnabled(String provider) {}      @Override      public void onProviderDisabled(String provider) {}      
      @Override      public void onLocationChanged(Location location) {        AppLog.logString("Service.onLocationChanged()");        
        updateCoordinates(location.getLatitude(),location.getLongitude());        
        stopSelf();      }    };    
    @Override    public IBinder onBind(Intent intent) {      return null;    }    @Override    public void onCreate() {      super.onCreate();      
      AppLog.logString("Service.onCreate()");      
      manager = (LocationManager)getSystemService(LOCATION_SERVICE);    }    @Override    public void onStart(Intent intent, int startId) {      super.onStart(intent, startId);      
      waitForGPSCoordinates();    }
    @Override    public void onDestroy() {      stopListening();      
      AppLog.logString("Service.onDestroy()");      
      super.onDestroy();    }    public int onStartCommand(Intent intent, int flags, int startId) {      waitForGPSCoordinates();      
      AppLog.logString("Service.onStartCommand()");      
      return super.onStartCommand(intent, flags, startId);    }    
    private void waitForGPSCoordinates() {      startListening();    }    
    private void startListening(){      AppLog.logString("Service.startListening()");      
      final Criteria criteria = new Criteria();      criteria.setAccuracy(Criteria.ACCURACY_COARSE);      criteria.setAltitudeRequired(false);      criteria.setBearingRequired(false);      criteria.setCostAllowed(true);      criteria.setPowerRequirement(Criteria.POWER_LOW);      final String bestProvider = manager.getBestProvider(criteria, true);      if (bestProvider != null && bestProvider.length() > 0) {        manager.requestLocationUpdates(bestProvider, 500, 10, listener);      }      else {        final List<String> providers = manager.getProviders(true);        for (final String provider : providers) {          manager.requestLocationUpdates(provider, 500, 10, listener);        }      }    }    
    private void stopListening(){      try {        if (manager != null && listener != null) {          manager.removeUpdates(listener);        }        manager = null;      }      catch (final Exception ex) {      }    }    
    private void updateCoordinates(double latitude, double longitude){      Geocoder coder = new Geocoder(this);      List<Address> addresses = null;      String info = "";      
      AppLog.logString("Service.updateCoordinates()");      AppLog.logString(info);      
      try       {
    
      addresses = coder.getFromLocation(latitude, longitude, 2);
    
      
    
      if(null != addresses && addresses.size() > 0){
    
        int addressCount = addresses.get(0).getMaxAddressLineIndex();
    
        
    
        if(-1 != addressCount){
    
          for(int index=0; index<=addressCount; ++index){
    
            info += addresses.get(0).getAddressLine(index);
    
            
    
            if(index < addressCount)
    
              info += ", ";
    
          }
    
        }
    
        else
    
        {
    
          info += addresses.get(0).getFeatureName() + ", " + addresses.get(0).getSubAdminArea() + ", " + addresses.get(0).getAdminArea();
    
        }
    
      }
    
      
    
      AppLog.logString(addresses.get(0).toString());      }      catch (Exception e)      {        e.printStackTrace();      }      
      coder = null;      addresses = null;      
      if(info.length() <= 0){        info = "lat " + latitude + ", lon " + longitude;      }      else{        info += ("\n" + "(lat " + latitude + ", lon " + longitude + ")");      }      
      RemoteViews views = new RemoteViews(getPackageName(), R.layout.gpswidget);      
      views.setTextViewText(R.id.txtInfo, info);      
      ComponentName thisWidget = new ComponentName(this, GPSWidgetProvider.class);
            AppWidgetManager manager = AppWidgetManager.getInstance(this);
            manager.updateAppWidget(thisWidget, views);
    }  }
}
3) InfoActivity
package com.samples.gpswidget;

import android.app.Activity;
import android.os.Bundle;

public class InfoActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

In Layout folder create 2 xml
1) gpswidget.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"  android:layout_height="fill_parent">  
  <LinearLayout    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:background="@drawable/back"    android:padding="10dip">    
    <ImageView      android:layout_width="wrap_content"      android:layout_height="fill_parent"      android:src="@drawable/widgeticon"/>    
    <TextView      android:layout_width="fill_parent"      android:layout_height="fill_parent"      android:id="@+id/txtInfo"      android:text="Waiting for GPS coordinates..."      android:textColor="#FFFFFFFF"      android:gravity="center_horizontal|center_vertical"/>      
  </LinearLayout>
</LinearLayout>

2) main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="10dip">
 
    <ImageView
    
  android:layout_width="fill_parent"
    
  android:layout_height="wrap_content"
    
  android:src="@drawable/widgeticon"
    
  android:scaleType="fitCenter"/>  <TextView
    
  android:layout_width="fill_parent"
    
  android:layout_height="wrap_content"
    
  android:gravity="center_horizontal"
    
  android:text="@string/app_name"
    
  android:textSize="20dip"
    
  android:textStyle="bold"
    
  android:padding="10dip"/>
    
  
    <TextView
    
  android:layout_width="fill_parent"
    
  android:layout_height="wrap_content"
    
  android:gravity="center_horizontal"
    
  android:text="@string/app_info"/>
    
  
</LinearLayout>
In xml folder create gpswidgetinfo.xml and write down the following code...
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dp"
    android:minHeight="72dp"
    android:updatePeriodMillis="900000"
    android:initialLayout="@layout/gpswidget">
</appwidget-provider>

In string.xml file write down following code

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<resources>
    <string name="app_name">GPS Widget</string>
    <string name="app_info">GPS Widget is a sample application to demonstrate the use of Android AppWidget.\n\nThe widget shows the GPS coordinates of the device\'s position. The widget refreshes the position in 15 minutes interval</string>
</resources>

Run the above example...........









No comments:

Post a Comment