Ch13 Listings
satya - Sunday, February 21, 2010 8:39:58 AM
Listing 13-1. Widget Definition in Android Manifest File
<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>
satya - Sunday, February 21, 2010 8:42:41 AM
Listing 13-2. Widget View Definition in Widget Provider Information XML File
<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>
satya - Sunday, February 21, 2010 8:44:20 AM
Listing 13-3. Allowed View Controls in RemoteViews
FrameLayout
LinearLayout
RelativeLayout
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
satya - Sunday, February 21, 2010 8:48:00 AM
Listing 13-4. A Widget Provider Shell
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) {}
}
satya - Sunday, February 21, 2010 8:54:15 AM
Listing 13-5. Android Manifest File for BDayWidget Sample Application
<?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>
satya - Sunday, February 21, 2010 8:57:02 AM
Listing 13-6. Widget View Definition for BDayWidget
<!-- 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>
satya - Sunday, February 21, 2010 9:01:20 AM
Listing 13-7. Widget View Layout Definition for BDayWidget
<!-- 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>
satya - Sunday, February 21, 2010 9:07:37 AM
Listing 13-8. A Boundary Box Shape Definition
<!-- 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>
satya - Sunday, February 21, 2010 2:09:18 PM
Listing 13-9. Sample Widget Provider: BDayWidgetProvider
///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);
}
}
satya - Sunday, February 21, 2010 2:11:44 PM
Listing 13-10. Saving Widget State: The Contract
//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();
}
satya - Sunday, February 21, 2010 2:18:23 PM
Listing 13-11. Implementing Widget Saves Through Shared Preferences
//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;
}
}
satya - Sunday, February 21, 2010 2:30:32 PM
Listing 13-12. BDayWidgetModel: Implementing a State Model
//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;
}
}
satya - Sunday, February 21, 2010 2:33:44 PM
Listing 13-13. Date Utilities
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);
}
}
satya - Sunday, February 21, 2010 2:46:17 PM
Listing 13-14. Implementing a Configurator Activity
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);
}
}
satya - Sunday, February 21, 2010 2:52:09 PM
Listing 13-15. Layout Definition for Configurator Activity
<!-- 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>