Ch13 Listings


<manifest..>
   <application>
      ....
      <receiver android:name=".BDayWidgetProvider">
         <meta-data android:name="android.appwidget.provider"
            android:resource="@xml/bday_appwidget_provider" />
         <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
         </intent-filter>
      </receiver>
      ...
      <activity>
         .....
      </activity>
   <application>
</manifest>

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
   android:minWidth="150dp"
   android:minHeight="120dp"
   android:updatePeriodMillis="43200000"
   android:initialLayout="@layout/bday_widget"
   android:configure="com.ai.android.BDayWidget.ConfigureBDayWidgetActivity"
   >
</appwidget-provider>

FrameLayout
LinearLayout
RelativeLayout

AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView

public class BDayWidgetProvider extends AppWidgetProvider
{
   public void onUpdate(Context context,
                  AppWidgetManager appWidgetManager,
                  int[] appWidgetIds){}
                  
   public void onDeleted(Context context, int[] appWidgetIds){}
   public void onEnabled(Context context){}
   public void onDisabled(Context context) {}
}

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.ai.android.BDayWidget"
   android:versionCode="1"
   android:versionName="1.0.0">
   <application android:icon="@drawable/icon" android:label="Birthday Widget">
      <!--
      **********************************************************************
      * Birthday Widget Provider Receiver
      **********************************************************************
      -->
      <receiver android:name=".BDayWidgetProvider">
         <meta-data android:name="android.appwidget.provider"
            android:resource="@xml/bday_appwidget_provider" />
         <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
         </intent-filter>
      </receiver>
      <!--
      **********************************************************************
      * Birthday Provider Confiurator Activity
      **********************************************************************
      -->
      <activity android:name=".ConfigureBDayWidgetActivity"
         android:label="Configure Birthday Widget">
         <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
         </intent-filter>
      </activity>
   </application>
   <uses-sdk android:minSdkVersion="3" />
</manifest>

<!-- res/xml/bday_appwidget_provider.xml -->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
   android:minWidth="150dp"
   android:minHeight="120dp"
   android:updatePeriodMillis="4320000"
   android:initialLayout="@layout/bday_widget"
   android:configure="com.ai.android.BDayWidget.ConfigureBDayWidgetActivity"
   >
</appwidget-provider>

<!-- res/layout/bday_widget.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="150dp"
   android:layout_height="120dp"
   android:background="@drawable/box1"
   >
   <TextView
      android:id="@+id/bdw_w_name"
      android:layout_width="fill_parent"
      android:layout_height="30dp"
      android:text="Anonymous"
      android:background="@drawable/box1"
      android:gravity="center"
      />
   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:orientation="horizontal"
      android:layout_width="fill_parent"
      android:layout_height="60dp"
      >
      <TextView
         android:id="@+id/bdw_w_days"
         android:layout_width="wrap_content"
         android:layout_height="fill_parent"
         android:text="0"
         android:gravity="center"
         android:textSize="30sp"
         android:layout_weight="50"
         />
      <TextView
         android:id="@+id/bdw_w_button_buy"
         android:layout_width="wrap_content"
         android:layout_height="fill_parent"
         android:textSize="20sp"
         android:text="Buy"
         android:layout_weight="50"
         android:background="#FF6633"
         android:gravity="center"
         />
   </LinearLayout>
   <TextView
      android:id="@+id/bdw_w_date"
      android:layout_width="fill_parent"
      android:layout_height="30dp"
      android:text="1/1/2000"
      android:background="@drawable/box1"
      android:gravity="center"
      />
</LinearLayout>

<!-- res/drawable/box1.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
   <stroke android:width="4dp" android:color="#888888" />
   <padding android:left="2dp" android:top="2dp"
      android:right="2dp" android:bottom="2dp" />
   <corners android:radius="4dp" />
</shape>

///src/<your-package>/BDayWidgetProvider.java
public class BDayWidgetProvider extends AppWidgetProvider
{
   private static final String tag = "BDayWidgetProvider";

   public void onUpdate(Context context,
                     AppWidgetManager appWidgetManager,
                     int[] appWidgetIds) {
      final int N = appWidgetIds.length;
      for (int i=0; i<N; i++)
      {
         int appWidgetId = appWidgetIds[i];
         updateAppWidget(context, appWidgetManager, appWidgetId);
      }
   }

   public void onDeleted(Context context, int[] appWidgetIds) {
      final int N = appWidgetIds.length;
      for (int i=0; i<N; i++) {
         BDayWidgetModel.removePrefs(context, appWidgetIds[i]);
      }
   }

   @Override
   public void onReceive(Context context, Intent intent) {
      final String action = intent.getAction();
      if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
         Bundle extras = intent.getExtras();
         final int appWidgetId = extras.getInt
                  (AppWidgetManager.EXTRA_APPWIDGET_ID,
                  AppWidgetManager.INVALID_APPWIDGET_ID);
         if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
            this.onDeleted(context, new int[] { appWidgetId });
         }
      }
      else {
         super.onReceive(context, intent);
      }
   }

   public void onEnabled(Context context) {
      BDayWidgetModel.clearAllPreferences(context);
      PackageManager pm = context.getPackageManager();
      pm.setComponentEnabledSetting(
            new ComponentName("com.ai.android.BDayWidget",
                           ".BDayWidgetProvider"),
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            PackageManager.DONT_KILL_APP);
   }

   public void onDisabled(Context context) {
      BDayWidgetModel.clearAllPreferences(context);
      PackageManager pm = context.getPackageManager();
      pm.setComponentEnabledSetting(
         new ComponentName("com.ai.android.BDayWidget",
                        ".BDayWidgetProvider"),
         PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
         PackageManager.DONT_KILL_APP);
   }

   private void updateAppWidget(Context context,
                           AppWidgetManager appWidgetManager,
                           int appWidgetId) {
      BDayWidgetModel bwm = BDayWidgetModel.retrieveModel(context, appWidgetId);
      if (bwm == null) {
         return;
      }
      ConfigureBDayWidgetActivity
         .updateAppWidget(context, appWidgetManager, bwm);
   }
}

//filename: src/?/IWidgetModelSaveContract.java
public interface IWidgetModelSaveContract
{
   public void setValueForPref(String key, String value);
   public String getPrefname();

   //return key value pairs you want to be saved
   public Map<String,String> getPrefsToSave();

   //gets called after restore
   public void init();
}

//filename: /src/?/APrefWidgetModel.java
public abstract class APrefWidgetModel
implements IWidgetModelSaveContract
{
   private static String tag = "AWidgetModel";
   public int iid;

   public APrefWidgetModel(int instanceId) {
      iid = instanceId;
   }

   //abstract methods
   public abstract String getPrefname();
   public abstract void init();
   public Map<String,String> getPrefsToSave(){ return null;}

   public void savePreferences(Context context){
      Map<String,String> keyValuePairs = getPrefsToSave();
      if (keyValuePairs == null){
         return;
      }
      //going to save some values
      SharedPreferences.Editor prefs =
         context.getSharedPreferences(getPrefname(), 0).edit();

      for(String key: keyValuePairs.keySet()){
         String value = keyValuePairs.get(key);
         savePref(prefs,key,value);
      }
      //finally commit the values
      prefs.commit();
   }

   private void savePref(SharedPreferences.Editor prefs,
                     String key, String value) {
      String newkey = getStoredKeyForFieldName(key);
      prefs.putString(newkey, value);
   }

   private void removePref(SharedPreferences.Editor prefs, String key) {
      String newkey = getStoredKeyForFieldName(key);
      prefs.remove(newkey);
   }

   protected String getStoredKeyForFieldName(String fieldName){
      return fieldName + "_" + iid;
   }
   public static void clearAllPreferences(Context context, String prefname) {
      SharedPreferences prefs=context.getSharedPreferences(prefname, 0);
      SharedPreferences.Editor prefsEdit = prefs.edit();
      prefsEdit.clear();
      prefsEdit.commit();
   }

   public boolean retrievePrefs(Context ctx) {
      SharedPreferences prefs = ctx.getSharedPreferences(getPrefname(), 0);
      Map<String,?> keyValuePairs = prefs.getAll();
      boolean prefFound = false;

      for (String key: keyValuePairs.keySet()){
         if (isItMyPref(key) == true){
            String value = (String)keyValuePairs.get(key);
            setValueForPref(key,value);
            prefFound = true;
         }
      }
      return prefFound;
   }

   public void removePrefs(Context context) {
      Map<String,String> keyValuePairs = getPrefsToSave();
      if (keyValuePairs == null){
         return;
      }
      //going to save some values
      SharedPreferences.Editor prefs =
            context.getSharedPreferences(getPrefname(), 0).edit();
      for(String key: keyValuePairs.keySet()){
         removePref(prefs,key);
      }
      //finally commit the values
      prefs.commit();
   }
   private boolean isItMyPref(String keyname) {
      if (keyname.indexOf("_" + iid) > 0){
         return true;
      }
      return false;
   }
   public void setValueForPref(String key, String value) {
      return;
   }
}

//filename: /src/?/BDayWidgetModel.java
public class BDayWidgetModel extends APrefWidgetModel
{
   private static String tag="BDayWidgetModel";

   // Provide a unique name to store date
   private static String BDAY_WIDGET_PROVIDER_NAME=
                  "com.ai.android.BDayWidget.BDayWidgetProvider";

   // Variables to paitn the widget view
   private String name = "anon";
   private static String F_NAME = "name";
   private String bday = "1/1/2001";
   private static String F_BDAY = "bday";
   private String url="http://www.google.com";

   // Constructor/gets/sets
   public BDayWidgetModel(int instanceId){
      super(instanceId);
   }
   public BDayWidgetModel(int instanceId, String inName, String inBday){
      super(instanceId);
      name=inName;
      bday=inBday;
   }
   public void init(){}
   public void setName(String inname){name=inname;}
   public void setBday(String inbday){bday=inbday;}
   public String getName(){return name;}
   public String getBday(){return bday;}

   public long howManyDays(){
      try {
         return Utils.howfarInDays(Utils.getDate(this.bday));
      }
      catch(ParseException x){
         return 20000;
      }
   }

   //Implement save contract
   public void setValueForPref(String key, String value){
      if (key.equals(getStoredKeyForFieldName(BDayWidgetModel.F_NAME))){
         this.name = value;
         return;
      }
      if (key.equals(getStoredKeyForFieldName(BDayWidgetModel.F_BDAY))){
         this.bday = value;
         return;
      }
   }

   public String getPrefname() {
      return BDayWidgetModel.BDAY_WIDGET_PROVIDER_NAME;
   }

   //return key value pairs you want to be saved
   public Map getPrefsToSave() {
      Map map   = new HashMap();
      map.put(BDayWidgetModel.F_NAME, this.name);
      map.put(BDayWidgetModel.F_BDAY, this.bday);
      return map;
   }

   public String toString() {
      StringBuffer sbuf = new StringBuffer();
      sbuf.append("iid:" + iid);
      sbuf.append("name:" + name);
      sbuf.append("bday:" + bday);
      return sbuf.toString();
   }
   public static void clearAllPreferences(Context ctx){
      APrefWidgetModel.clearAllPreferences(ctx,
                  BDayWidgetModel.BDAY_WIDGET_PROVIDER_NAME);
   }

   public static BDayWidgetModel retrieveModel(Context ctx, int widgetId){
      BDayWidgetModel m = new BDayWidgetModel(widgetId);
      boolean found = m.retrievePrefs(ctx);
      return found ? m:null;
   }
}

public class Utils
{
   private static String tag = "Utils";

   public static Date getDate(String dateString)
   throws ParseException {
      DateFormat a = getDateFormat();
      Date date = a.parse(dateString);
      return date;
   }
   public static String test(String sdate){
      try {
         Date d = getDate(sdate);
         DateFormat a = getDateFormat();
         String s = a.format(d);
         return s;
      }
      catch(Exception x){
         return "problem with date:" + sdate;
      }
   }

   public static DateFormat getDateFormat(){
      SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");
      //DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
      df.setLenient(false);
      return df;
   }

   //valid dates: 1/1/2009, 11/11/2009,
   //invalid dates: 13/1/2009, 1/32/2009
   public static boolean validateDate(String dateString){
      try {
         SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");
         df.setLenient(false);
         Date date = df.parse(dateString);
         return true;
      }
      catch(ParseException x) {
         return false;
      }
   }

   public static long howfarInDays(Date date){
      Calendar cal = Calendar.getInstance();
      Date today = cal.getTime();
      long today_ms = today.getTime();
      long target_ms = date.getTime();
      return (target_ms - today_ms)/(1000 * 60 * 60 * 24);
   }
}

public class ConfigureBDayWidgetActivity extends Activity
{
   private static String tag = "ConfigureBDayWidgetActivity";
   private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;

   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.edit_bday_widget);
      setupButton();
      Intent intent = getIntent();
      Bundle extras = intent.getExtras();

      if (extras != null) {
         mAppWidgetId = extras.getInt(
         AppWidgetManager.EXTRA_APPWIDGET_ID,
         AppWidgetManager.INVALID_APPWIDGET_ID);
      }
   }

   private void setupButton(){
      Button b = (Button)this.findViewById(R.id.bdw_button_update_bday_widget);
      b.setOnClickListener(
         new Button.OnClickListener(){
            public void onClick(View v)
            {
               parentButtonClicked(v);
            }
         });
   }

   private void parentButtonClicked(View v){
      String name = this.getName();
      String date = this.getDate();
      if (Utils.validateDate(date) == false){
         this.setDate("wrong date:" + date);
         return;
      }
      if (this.mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID){
         return;
      }
      updateAppWidgetLocal(name,date);
      Intent resultValue = new Intent();
      resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
      setResult(RESULT_OK, resultValue);
      finish();
   }

   private String getName(){
      EditText nameEdit = (EditText)this.findViewById(R.id.bdw_bday_name_id);
      String name = nameEdit.getText().toString();
      return name;
   }

   private String getDate(){
      EditText dateEdit = (EditText)this.findViewById(R.id.bdw_bday_date_id);
      String dateString = dateEdit.getText().toString();
      return dateString;
   }

   private void setDate(String errorDate){
      EditText dateEdit = (EditText)this.findViewById(R.id.bdw_bday_date_id);
      dateEdit.setText("error");
      dateEdit.requestFocus();
   }

   private void updateAppWidgetLocal(String name, String dob){
      BDayWidgetModel m = new BDayWidgetModel(mAppWidgetId,name,dob);
      updateAppWidget(this,AppWidgetManager.getInstance(this),m);
      m.savePreferences(this);
   }

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

<!-- res/layout/edit_bday_widget.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/root_layout_id"
   android:orientation="vertical"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   >
   <TextView
      android:id="@+id/bdw_text1"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="Name:"
      />
   <EditText
      android:id="@+id/bdw_bday_name_id"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="Anonymous"
      />
   <TextView
      android:id="@+id/bdw_text2"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="Birthday (9/1/2001):"
      />
   <EditText
      android:id="@+id/bdw_bday_date_id"
      CHAPTER 13: Home Screen Widgets 487
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="ex: 10/1/2009"
      />
   <Button
      android:id="@+id/bdw_button_update_bday_widget"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="update"
      />
</LinearLayout>