public interface C {

	// Mostly constants for referring to table names and columns
	public static final String db_ID = "_id";
	//
	public static final String db_GROUP_TABLE = "Groups";
	public static final String db_GROUP_NAME = "name";
	//
	public static final String db_TRACKER_TABLE = "Trackers";
	public static final String db_TRACKER_GROUP = "group_id";
	public static final String db_TRACKER_NAME = "name";
	public static final String db_TRACKER_BODY = "body";
	public static final String db_TRACKER_LAST_LOG_ID = "last_log_id";
	public static final String db_TRACKER_USE_VALUE = "use_value";
	public static final String db_TRACKER_VALUE_TYPE = "value_type";
	public static final String db_TRACKER_VALUE_LABEL = "value_label";
	public static final String db_TRACKER_VALUE_LABEL_POS = "value_label_position";
	public static final String db_TRACKER_SKIP_NEXT_ALERT = "flag_skip_next_alert";
	//
	public static final String db_LOG_TABLE = "TrackerLogs";
	public static final String db_LOG_TRACKER = "tracker_id";
	public static final String db_LOG_TIME = "log_time";
	public static final String db_LOG_BODY = "body";
	public static final String db_LOG_VALUE_TYPE = "value_type";
	public static final String db_LOG_VALUE = "value";
	public static final String db_LOG_IS_BREAK = "is_break";
	//
	public static final String db_VALUE_TABLE = "ValueTypes";
	public static final String db_VALUE_NAME = "name";
	public static final String db_VALUE_TYPE = "type";
	//
	public static final String db_ALERT_TABLE = "Alerts";
	public static final String db_ALERT_TRACKER = "tracker_id";
	public static final String db_ALERT_INTERVAL_MONTHS = "interval_months";
	public static final String db_ALERT_INTERVAL_WEEKS = "interval_weeks";
	public static final String db_ALERT_INTERVAL_DAYS = "interval_days";
	public static final String db_ALERT_INTERVAL_HOURS = "interval_hours";
	public static final String db_ALERT_INTERVAL_MINUTES = "interval_minutes";
	public static final String db_ALERT_INTERVAL_SECONDS = "interval_seconds";
	public static final String db_ALERT_NEXT_TIME = "next_time";
	public static final String db_ALERT_RINGTONE = "ringtone";
	public static final String db_ALERT_ENABLED = "is_enabled";
	public static final String db_ALERT_SKIP_NEXT = "skip_next";

	public static final String db_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
	public static final DateFormat dbDateFormat = new SimpleDateFormat(db_DATE_FORMAT);

	// Only want to create a toast once - referred to  by the string that's shown.
	public static final Long TOAST_LOG_DELETED = new Long(R.string.log_entry_deleted);
	public static final Long TOAST_LOG_CREATED = new Long(R.string.new_log_entry);
	public static final Long TOAST_LOG_UPDATED = new Long(R.string.toast_log_entry_updated);
	public static final Long TOAST_TRACKER_DELETED = new Long(R.string.toast_tracker_deleted);
	public static final Long TOAST_TRACKER_CREATED = new Long(R.string.toast_tracker_created);
	public static final Long TOAST_TRACKER_UPDATED = new Long(R.string.toast_tracker_updated);
	public static final Long TOAST_GROUP_CREATED = new Long(R.string.toast_group_created);
	public static final Long TOAST_GROUP_UPDATED = new Long(R.string.toast_group_updated);
	public static final Long TOAST_GROUP_DELETED = new Long(R.string.toast_group_deleted);

} public class LogMyLife extends Application {
	
	private SharedPreferences mPrefs = null;

	@Override
	public void onCreate() {
		super.onCreate();
		refreshPrefs();
		
		// as backup to the broadcast receivers, make sure the next
		// reminder alert is set when starting the app.
		DbAdapter db = new DbAdapter(this);
		AlertReceiver.setNextAlert(this, db);
		db.close();
	}
	
	// repository for created toasts
	private Map<Long,Toast> mToasts = new HashMap<Long,Toast>();
	public void showToast(Long id) {
		Toast t = mToasts.get(id);
		if(t==null)
			mToasts.put(id, t = Toast.makeText(this, id.intValue(), Toast.LENGTH_SHORT));
		t.show();
	}

	/*
	 * represents the group currently selected, which persists between invocations. 
	 * this is not displayed with the Prefs activity.
	 */
	private static final String GROUP_SELECTED = "groupSelected";
	public long getSelectedGroup() {
		return mPrefs.getLong(GROUP_SELECTED, 0);
	}
	
	public void setSelectedGroup(long groupId) {
		mPrefs.edit().putLong(GROUP_SELECTED, groupId).commit();
	}

	private static final String FIRST_TIME = "firstTime";
	/**
	 * Returns whether this is the first time the user has ever used
	 * this application.
	 *
	 * @return True if this is the first time opening this app. False forever after.
	 */
	public boolean getFirstTime() {
		boolean first = mPrefs.getBoolean(FIRST_TIME, true);
		if(first)mPrefs.edit().putBoolean(FIRST_TIME, false).commit();	
		return first;
	}
	
	private static final String STORED_CHANGE_VERSION = "storedChangeVersion";
	private static final int CHANGE_VERSION = 0;
	/**
	 * Has something been updated that we should tell users about?
	 * 
	 * @return True if the version number here changes; false otherwise
	 */
	public boolean getShowChangedDialog() {
		int stored = mPrefs.getInt(STORED_CHANGE_VERSION, CHANGE_VERSION);
		if(stored < CHANGE_VERSION) {
			// previously stored version has changed
			mPrefs.edit().putInt(STORED_CHANGE_VERSION, CHANGE_VERSION).commit();	
			return true;
		} else if (mPrefs.getInt(STORED_CHANGE_VERSION, -1) == -1) 
			// first time we ever checked - initialize
			mPrefs.edit().putInt(STORED_CHANGE_VERSION, CHANGE_VERSION).commit();	
		return false;
	}

	public void refreshPrefs() {
		this.mPrefs = PreferenceManager.getDefaultSharedPreferences(this);		
	}
	
	public String getVersion() {
		try {
			return getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
		} catch (NameNotFoundException e) {
			e.printStackTrace();
			return "0.0";
		}
	}
} public final class R {
    public static final class array {
        public static final int alertIntervals=0x7f050008;
        public static final int dateFormatStored=0x7f050003;
        public static final int dateFormatVisible=0x7f050002;
        public static final int logValueTypes=0x7f050009;
        public static final int quietTimeStored=0x7f050007;
        public static final int quietTimeVisible=0x7f050006;
        public static final int timeFormatStored=0x7f050005;
        public static final int timeFormatVisible=0x7f050004;
        public static final int trackerClickBehaviorStored=0x7f050001;
        public static final int trackerClickBehaviorVisible=0x7f050000;
    }
    public static final class attr {
    }
    public static final class color {
        /**  colors to be re-used 
         */
        public static final int background=0x7f080000;
    }
    public static final class drawable {
        public static final int ic_launcher=0x7f020000;
        public static final int ic_stat_lml_notif=0x7f020001;
        public static final int icon=0x7f020002;
        public static final int timepicker_down_btn=0x7f020003;
        public static final int timepicker_down_disabled=0x7f020004;
        public static final int timepicker_down_disabled_focused=0x7f020005;
        public static final int timepicker_down_normal=0x7f020006;
        public static final int timepicker_down_pressed=0x7f020007;
        public static final int timepicker_down_selected=0x7f020008;
        public static final int timepicker_input=0x7f020009;
        public static final int timepicker_input_disabled=0x7f02000a;
        public static final int timepicker_input_normal=0x7f02000b;
        public static final int timepicker_input_pressed=0x7f02000c;
        public static final int timepicker_input_selected=0x7f02000d;
        public static final int timepicker_up_btn=0x7f02000e;
        public static final int timepicker_up_disabled=0x7f02000f;
        public static final int timepicker_up_disabled_focused=0x7f020010;
        public static final int timepicker_up_normal=0x7f020011;
        public static final int timepicker_up_pressed=0x7f020012;
        public static final int timepicker_up_selected=0x7f020013;
    }
    public static final class id {
        public static final int _to_button=0x7f0a003c;
        public static final int _to_edit_text=0x7f0a003b;
        public static final int about=0x7f0a0050;
        public static final int add_new_alert=0x7f0a0016;
        public static final int add_new_group=0x7f0a0003;
        public static final int add_new_tracker=0x7f0a0010;
        public static final int alertBody=0x7f0a0032;
        public static final int alertContainer=0x7f0a0018;
        public static final int alertFieldAddButton=0x7f0a0040;
        public static final int alertFieldDropButton=0x7f0a0041;
        public static final int alertInterval=0x7f0a003e;
        public static final int alertIntervalType=0x7f0a003f;
        public static final int alertWidget=0x7f0a0031;
        public static final int body=0x7f0a0009;
        public static final int cancel=0x7f0a000d;
        public static final int cancel_existing=0x7f0a004c;
        public static final int cancel_new=0x7f0a004b;
        public static final int data=0x7f0a004f;
        public static final int decrement=0x7f0a003a;
        public static final int delete=0x7f0a002d;
        public static final int delete_alert=0x7f0a0044;
        public static final int detailed_log=0x7f0a001a;
        public static final int done=0x7f0a0046;
        public static final int edit=0x7f0a004a;
        public static final int editDate=0x7f0a0007;
        public static final int editTime=0x7f0a0008;
        public static final int edit_alert=0x7f0a0042;
        public static final int editor=0x7f0a0029;
        public static final int enabled=0x7f0a0028;
        public static final int export_button=0x7f0a0000;
        public static final int groupName=0x7f0a0037;
        public static final int group_info=0x7f0a000f;
        public static final int group_list_spinner=0x7f0a000e;
        public static final int group_spinner=0x7f0a0020;
        public static final int help=0x7f0a0048;
        public static final int hours=0x7f0a002e;
        public static final int import_button=0x7f0a0001;
        public static final int increment=0x7f0a0038;
        public static final int info_alert=0x7f0a0017;
        public static final int info_group=0x7f0a0021;
        public static final int info_logs=0x7f0a001b;
        public static final int info_value=0x7f0a0022;
        public static final int intervalList=0x7f0a003d;
        public static final int interval_label=0x7f0a0033;
        public static final int lastLog=0x7f0a0012;
        public static final int listName=0x7f0a0004;
        public static final int logBody=0x7f0a001f;
        public static final int logDetails=0x7f0a0014;
        public static final int logRow=0x7f0a001c;
        public static final int logTime=0x7f0a0013;
        public static final int logValLabelPos=0x7f0a0026;
        public static final int logValLabelPost=0x7f0a001e;
        public static final int logValLabelPre=0x7f0a001d;
        public static final int logValue=0x7f0a000b;
        public static final int logValueLabel=0x7f0a0025;
        public static final int manage_groups=0x7f0a004e;
        public static final int minutes=0x7f0a002f;
        public static final int name=0x7f0a0011;
        public static final int new_alert=0x7f0a0053;
        public static final int new_group=0x7f0a0045;
        public static final int new_log=0x7f0a0052;
        public static final int new_tracker=0x7f0a004d;
        public static final int next_time=0x7f0a002b;
        public static final int ok=0x7f0a0027;
        public static final int quick_log=0x7f0a0019;
        public static final int quicklog=0x7f0a0051;
        public static final int result=0x7f0a0002;
        public static final int ringtone=0x7f0a002c;
        public static final int save=0x7f0a000c;
        public static final int seconds=0x7f0a0030;
        public static final int settings=0x7f0a0047;
        public static final int skip_next=0x7f0a0036;
        public static final int time_label=0x7f0a0035;
        public static final int timepicker_input=0x7f0a0039;
        public static final int toggle_alert=0x7f0a0043;
        public static final int tracker_details=0x7f0a0006;
        public static final int tracker_name=0x7f0a0005;
        public static final int units=0x7f0a002a;
        public static final int useValue=0x7f0a0023;
        public static final int value=0x7f0a0034;
        public static final int valueContainer=0x7f0a000a;
        public static final int valueType=0x7f0a0024;
        public static final int view=0x7f0a0049;
        public static final int z1=0x7f0a0015;
    }
    public static final class layout {
        public static final int a_data=0x7f030000;
        public static final int a_groups_edit=0x7f030001;
        public static final int a_groups_edit_new=0x7f030002;
        public static final int a_groups_edit_row=0x7f030003;
        public static final int a_log_edit=0x7f030004;
        public static final int a_main=0x7f030005;
        public static final int a_main_new=0x7f030006;
        public static final int a_main_row=0x7f030007;
        public static final int a_tracker_detail=0x7f030008;
        public static final int a_tracker_detail_head=0x7f030009;
        public static final int a_tracker_detail_row=0x7f03000a;
        public static final int a_tracker_edit=0x7f03000b;
        public static final int d_edit_alert=0x7f03000c;
        public static final int d_generic_info=0x7f03000d;
        public static final int d_group_edit=0x7f03000e;
        public static final int d_timepicker=0x7f03000f;
        public static final int w_alert=0x7f030010;
        public static final int w_group_spinner_display=0x7f030011;
        public static final int w_group_spinner_row=0x7f030012;
        public static final int w_number_picker=0x7f030013;
        public static final int w_text_ok=0x7f030014;
        public static final int w_tracker_edit_alert=0x7f030015;
        public static final int w_tracker_edit_alert_interval=0x7f030016;
    }
    public static final class menu {
        public static final int alert_widget_context=0x7f090000;
        public static final int groups_edit=0x7f090001;
        public static final int groups_edit_context=0x7f090002;
        public static final int log_edit=0x7f090003;
        public static final int main=0x7f090004;
        public static final int main_context=0x7f090005;
        public static final int tracker_detail=0x7f090006;
        public static final int tracker_detail_context=0x7f090007;
        public static final int tracker_edit=0x7f090008;
    }
    public static final class string {
        public static final int ae_dialog_title=0x7f060055;
        public static final int alert_message=0x7f06001c;
        public static final int alert_prefix=0x7f06001d;
        public static final int app_name=0x7f06002b;
        public static final int db_1_create_alert_tracker_id_idx=0x7f060006;
        public static final int db_1_create_alerts=0x7f060003;
        /**  Database level 1 (initial) 
         */
        public static final int db_1_create_groups=0x7f060000;
        public static final int db_1_create_log_tracker_time_idx=0x7f060005;
        public static final int db_1_create_logs=0x7f060002;
        public static final int db_1_create_tracker_group_id_idx=0x7f060004;
        public static final int db_1_create_trackers=0x7f060001;
        /**  Database level 2 
         */
        public static final int db_2_alter_tracker_add_value_label=0x7f060007;
        public static final int db_2_alter_tracker_add_value_label_pos=0x7f060008;
        /**  logs record them too because the tracker type might change later 
         */
        public static final int db_3_alter_logs_add_value_type=0x7f06000b;
        /**  Database level 3 
 introduce types so user can specify what format the value will have 
         */
        public static final int db_3_alter_tracker_add_use_value=0x7f060009;
        /**  even if the value isn't in use, remember what it was set to before 
         */
        public static final int db_3_alter_tracker_add_value_type=0x7f06000a;
        public static final int db_3_update_trackers_set_use_value=0x7f06000c;
        /**  configuration of values user sees 
         */
        public static final int db_first_list=0x7f060011;
        /**  Canned queries to be used 
         */
        public static final int db_select_trackers=0x7f06000e;
        public static final int db_select_trackers_filter=0x7f06000f;
        public static final int db_update_tracker_last_log=0x7f060010;
        /**  Database level ? - future 
         */
        public static final int db_x_create_valuetypes=0x7f06000d;
        public static final int empty_ringtone=0x7f06001f;
        public static final int empty_time=0x7f060020;
        public static final int export_fail=0x7f060016;
        public static final int export_success=0x7f060017;
        public static final int exporting_data=0x7f060018;
        public static final int ge_dialog_cancel_button=0x7f06003b;
        public static final int ge_dialog_delete_button=0x7f06003a;
        public static final int ge_dialog_delete_msg=0x7f060039;
        public static final int ge_dialog_delete_title=0x7f060038;
        public static final int ged_edit_title=0x7f06002d;
        public static final int ged_new_title=0x7f06002c;
        public static final int ged_ok_create=0x7f06002e;
        public static final int ged_ok_update=0x7f06002f;
        public static final int group_dialog_help_html=0x7f060043;
        public static final int group_dialog_help_title=0x7f060044;
        public static final int hello=0x7f06002a;
        public static final int import_dialog_cancel=0x7f060013;
        public static final int import_dialog_ok=0x7f060012;
        public static final int import_fail=0x7f060019;
        public static final int import_success=0x7f06001a;
        public static final int import_unknown_version=0x7f060015;
        public static final int import_warning_text=0x7f060014;
        public static final int importing_data=0x7f06001b;
        /**  info dialogs 
         */
        public static final int info_dialog_dismiss_button=0x7f06003c;
        public static final int ldd_cancel_button=0x7f060037;
        public static final int ldd_delete_button=0x7f060036;
        public static final int ldd_msg=0x7f060035;
        public static final int ldd_title=0x7f060034;
        public static final int log_dialog_help_html=0x7f060053;
        public static final int log_dialog_help_title=0x7f060054;
        public static final int log_entry_deleted=0x7f060029;
        public static final int main_dialog_change_asset=0x7f06003f;
        public static final int main_dialog_change_title=0x7f060040;
        public static final int main_dialog_group_asset=0x7f060041;
        public static final int main_dialog_group_title=0x7f060042;
        public static final int main_dialog_info_asset=0x7f06003d;
        public static final int main_dialog_info_title=0x7f06003e;
        public static final int new_log_entry=0x7f060028;
        public static final int ringtone_dialog_title=0x7f06001e;
        public static final int td_dialog_help_html=0x7f060051;
        public static final int td_dialog_help_title=0x7f060052;
        public static final int td_dialog_logs_html=0x7f060050;
        public static final int td_dialog_logs_title=0x7f06004f;
        public static final int tdd_cancel_button=0x7f060033;
        public static final int tdd_delete_button=0x7f060032;
        public static final int tdd_msg=0x7f060031;
        public static final int tdd_title=0x7f060030;
        public static final int te_dialog_alert_html=0x7f06004c;
        public static final int te_dialog_alert_title=0x7f06004b;
        public static final int te_dialog_create_html=0x7f060046;
        public static final int te_dialog_create_title=0x7f060045;
        public static final int te_dialog_edit_html=0x7f060048;
        public static final int te_dialog_edit_title=0x7f060047;
        public static final int te_dialog_group_html=0x7f06004a;
        public static final int te_dialog_group_title=0x7f060049;
        public static final int te_dialog_value_html=0x7f06004e;
        public static final int te_dialog_value_title=0x7f06004d;
        public static final int toast_group_created=0x7f060023;
        public static final int toast_group_deleted=0x7f060021;
        public static final int toast_group_updated=0x7f060022;
        public static final int toast_log_entry_updated=0x7f060027;
        public static final int toast_tracker_created=0x7f060025;
        public static final int toast_tracker_deleted=0x7f060026;
        public static final int toast_tracker_updated=0x7f060024;
    }
    public static final class style {
        public static final int AddNewItem=0x7f070009;
        /**  text appearances 
         */
        public static final int AddNewItem_TA=0x7f070004;
        /**  general styles and themes 
         */
        public static final int AppTheme=0x7f070000;
        public static final int BigButton=0x7f07000a;
        public static final int BodyEditor=0x7f07000c;
        public static final int DefaultTextAppearance=0x7f070001;
        public static final int EllipseText=0x7f07000d;
        public static final int InfoButton=0x7f07000e;
        /**  specific widgets 
         */
        public static final int Label=0x7f070007;
        public static final int LineLabel=0x7f070008;
        public static final int ListTextAppearance=0x7f070002;
        public static final int NameEditor=0x7f07000b;
        public static final int SpinnerDisplay_TA=0x7f070006;
        public static final int SpinnerRow=0x7f070005;
        public static final int SubTextAppearance=0x7f070003;
        /**  layout components 
         */
        public static final int horizontalLL=0x7f07000f;
        public static final int verticalLL=0x7f070010;
    }
    public static final class xml {
        public static final int settings=0x7f040000;
    }
} public class Util {
	/*
	 * public static <K,V> Map<K,V> newMap() { return new HashMap<K,V>(); }
	 * 
	 * public static <K> Set<K> newSet() { return new HashSet<K>(); }
	 */

	/**
	 * An English string representing about how long ago the "from" date is from
	 * the "now" date.
	 */
	public static String getTimeSince(Date from, Date now) {
		long diff = (now.getTime() - from.getTime()) / 1000; // seconds

		if (diff < 0)
			return "In the future";
		if (diff <= 2)
			return "Just now";
		if (diff < 120)
			return "" + diff + " seconds ago";

		diff = diff / 60; // minutes
		if (diff < 120)
			return "" + diff + " minutes ago";

		diff = diff / 60; // hours
		if (diff < 48)
			return "" + diff + " hours ago";

		diff = diff / 24; // days (ignoring DST)
		if (diff < 14)
			return "" + diff + " days ago";
		if (diff > 365)
			return "" + (diff / 365) + " years ago"; // roughly
		else
			return "" + (diff / 7) + " weeks ago";
	}


	
	public static String toString(Object o) {
		return (o == null) ? null : o.toString();
	}

	public static String toString(Object o, String defaultString) {
		return (o == null) ? defaultString : o.toString();
	}
	
	/**
	 * 
	 * @param a Activity to attach this dialog to
	 * @param filename file in assets/dialogs/ dir which contains the HTML
	 * @return Builder for creating an AlertDialog with the HTML content
	 */
	public static Builder getHtmlDialogBuilder(Activity a, String filename) {
		WebView wv = (WebView) a.getLayoutInflater().inflate(R.layout.d_generic_info, null);
		wv.loadUrl("file:///android_asset/dialogs/" + filename);
		return new AlertDialog.Builder(a)
					.setView(wv)
					.setPositiveButton(R.string.info_dialog_dismiss_button, null);
	}

	/**
	 * 
	 * @param a Activity to attach this dialog to
	 * @param resId string for file in assets/dialogs/ dir which contains the HTML
	 * @return Builder for creating an AlertDialog with the HTML content
	 */
	public static Builder getHtmlDialogBuilder(Activity a, int rId) {
		return getHtmlDialogBuilder(a, a.getString(rId));
	}
} public abstract class EventCursorAdapter extends SimpleCursorAdapter {

	//TODO: look into implementing the Filterable interface
	protected static final String TAG = "LML.EventCursorAdapter";
	protected DateFormat mDateTimeFormat;
	
	public EventCursorAdapter(Context context, int layout, Cursor c,
			String[] from, int[] to) {
		super(context, layout, c, from, to);
		refreshDateTimeFmt(context);
	}

	public void refreshDateTimeFmt(Context context) {
		mDateTimeFormat = Settings.getDateTimeFormat(context);
	}

	public void requery() {
		Log.d(TAG, "requery");
		getCursor().requery();
	}

	protected String getTextForDate(TextView v, String text) {
		if (text != null && text.length() > 0) {

			// try to reformat date text for the field
			try {
				Date d = C.dbDateFormat.parse(text);
				text = formatDate(v, d);
			} catch (ParseException e) {
				Log.w(TAG, "Date parsing failed for " + text, e);
			}
		}
		return text;
	}

	protected abstract String formatDate(TextView v, Date d);

	// hide the view if there won't be any text to display
	public static void conditionallyHideView(View view, String text) {
		conditionallyHideView(view, text == null || text.length() == 0);
	}
	
	public static void conditionallyHideView(View view, boolean hide) {
		view.setVisibility(hide ? View.GONE : View.VISIBLE);
	}

} public class GroupSpinner implements OnItemSelectedListener {
	private static final String TAG = "LML.GroupSpinner";
	private final Spinner mSpinner;
	private final DbAdapter mDba;
	private final LogMyLife mApp;
	private OnGroupSelectedListener onGroupSelectedListener = null;

	public GroupSpinner(Activity caller, Spinner spinner, DbAdapter dba) {
		Log.d(TAG, "new GroupSpinner");

		this.mSpinner = spinner;
		this.mDba = dba;

		Cursor cur = mDba.fetchGroups();
		caller.startManagingCursor(cur);
		SimpleCursorAdapter adapter = new SimpleCursorAdapter(caller,
				R.layout.w_group_spinner_display, 	// specify layout id with a TextView
				cur, // Give the cursor to the list adapter
				new String[] { C.db_GROUP_NAME },	// Map column in the database to...
				new int[] { R.id.groupName });// The TextView defined in the layout
		adapter.setDropDownViewResource(R.layout.w_group_spinner_row);
		mSpinner.setAdapter(adapter);
		mSpinner.setOnItemSelectedListener(this);

		//TODO: add a "Create group" option at beginning of list
		mApp = (LogMyLife) caller.getApplication();
		setGroupId(mApp.getSelectedGroup());
	}
	

	@Override
	public void onItemSelected(AdapterView<?> arg0av, View v, int i, long groupId) {
		mApp.setSelectedGroup(groupId);
		if(onGroupSelectedListener != null)
			onGroupSelectedListener.onGroupSelected(groupId);
	}

	@Override
	public void onNothingSelected(AdapterView<?> av) {
		// TODO Auto-generated method stub
		Log.d(TAG, "onNothingSelected");
	}

	private boolean setGroupId(long groupId) {
		SpinnerAdapter adapter = mSpinner.getAdapter();
		for (int pos = 0; pos < adapter.getCount(); pos++) {
			if (adapter.getItemId(pos) == groupId) {
				mSpinner.setSelection(pos);
				return true;
			}
		}
		// nothing in the list matched. set groupId to first item in list
		mSpinner.setSelection(0);
		if(onGroupSelectedListener != null)
			onGroupSelectedListener.onGroupSelected(mSpinner.getItemIdAtPosition(0));		
		return false;
	}
	
	public void notifyDataSetChanged() {
		Log.d(TAG, "notifyDataSetChanged");
		((SimpleCursorAdapter) mSpinner.getAdapter()).notifyDataSetChanged();
		setGroupId(mApp.getSelectedGroup());
	}
	
	public long getSelectedItemId() {
		return mSpinner.getSelectedItemId();
	}
	
	public void setOnGroupSelectedListener(OnGroupSelectedListener groupSelectedListener) {
		this.onGroupSelectedListener = groupSelectedListener;
	}

	public OnGroupSelectedListener getOnGroupSelectedListener() {
		return onGroupSelectedListener;
	}

	public interface OnGroupSelectedListener {
		void onGroupSelected(long groupId);
	}

} public class TrackerCursorAdapter extends EventCursorAdapter {

	public TrackerCursorAdapter(Context context, int layout, Cursor c,
			String[] from, int[] to) {
		super(context, layout, c, from, to);
	}

	@Override
	public void setViewText(TextView v, String text) {
		switch(v.getId()) {		
		case R.id.lastLog:
			v.setTag(null); // make sure tag is reset as it's used by lastLog "ago" updater
			text = getTextForDate(v, text);
			break;
		case R.id.logDetails:
			conditionallyHideView(v, text);
			break;
		}
		super.setViewText(v, text);
	}

	@Override
	protected String formatDate(TextView v, Date d) {
		v.setTag(d); // set tag so lastLog "ago" updater can read it
		return Util.getTimeSince(d, new Date());
	}

	
} public interface OnGroupSelectedListener {
	void onGroupSelected(long groupId);
} public class TrackerDeleteDialog {
	
	private static final String TAG = "LML.TrackerDelD";

	public static Dialog create(Activity activity, OnClickListener listener) {
		Log.d(TAG, "showDeleteDialog");
		Dialog d = new AlertDialog.Builder(activity)
		.setTitle(R.string.tdd_title)
		.setMessage(R.string.tdd_msg)
		.setNegativeButton(R.string.tdd_cancel_button, null)
		.setPositiveButton(R.string.tdd_delete_button, listener)
		.create();
		d.setOwnerActivity(activity); // why can't the builder do this?
		return d;

	}
} public class DbAdapter implements C {

	/**	 name of the file used for the sqlite DB */
	private static final String DATABASE_NAME = "LogMyLife.db";
	/** version of the DB (to determine if migration is needed) */
	private static final int DATABASE_VERSION = 3;
	/** Logging tag for this class */
    private static final String TAG = "LML.DBAdapter";

    /** Helper that handles creating/migrating/opening the DB */
    private DbAdmin mDbHelper;
    /** Actual database handle */
    private SQLiteDatabase mDb;
    /** Context object from the application **/
    private final Context mContext;
    /** String resources from the application context */ 
	private final Resources resources;

	/**
	 * Creates the database adapter that mediates all DB access
	 * 
	 * @param ctx The application context in which this adapter is created
	 */
    public DbAdapter(Context ctx) {
        mContext = ctx;
        resources = ctx.getResources();
        open(ctx);
	}
    
    /**
     * Opens a database handle, creating if needed. If it cannot be created, throw exception.
     * @param ctx 
     * 
     * @return this
     * @throws SQLException if the database could be neither opened nor created
     */
    
    public DbAdapter open(Context ctx) throws SQLException {
    	if(mDbHelper != null) return this;
        mDbHelper = new DbAdmin(ctx);
        mDb = mDbHelper.getWritableDatabase();
        return this;
    }
    
    /**
     * Close any open database handles.
     */
    public void close() {
    	if(mDbHelper == null) return;
        mDbHelper.close();
        mDb = null;
        mDbHelper = null;
    }
    
    public String getDbPath() {
    	return mDb.getPath();
    }
    
    /**
     *  Return a string defined in resource file
     * @param id The R.id for the string
     * @return The corresponding string requested
     */
	private String str(int id) {
		return (String) resources.getText(id);
	}
    
    /*
     * *********************************************************************
     * 
     * Handle group table
     * 
     */
    
    public long createGroup(String title) {
        // @return listId or -1 if failed
    	Log.d(TAG, "creating group " + title);
        ContentValues initialValues = new ContentValues();
        initialValues.put(db_GROUP_NAME, title);

        return mDb.insert(db_GROUP_TABLE, null, initialValues);
    }

    /**
     * @param groupId rowId of group to return
     * @return Cursor positioned at single group matching id if any
     */
    public Cursor fetchGroup(long groupId) {
    	Log.d(TAG, "fetching group " + groupId);
		Cursor mCursor = mDb.query(true, 
				db_GROUP_TABLE, 
				new String[] { db_ID, db_GROUP_NAME }, 
				db_ID + "=" + groupId, 
				null, null, null, null, null);
        if (mCursor != null) {
            mCursor.moveToFirst();
        }
        return mCursor;
    }

    public Cursor fetchGroups() {
        // @return Cursor over all groups
    	Log.d(TAG, "fetching all groups");
    	return mDb.query(
    			db_GROUP_TABLE, 
    			new String[] {db_ID, db_GROUP_NAME}, 
    			null, null, null, null, null);
    }
    
    public boolean updateGroup(long groupId, String title) {
        // @return true if the note was successfully updated, false otherwise
    	Log.d(TAG, "updating group " + groupId);
        ContentValues args = new ContentValues();
        args.put(db_GROUP_NAME, title);
        return mDb.update(db_GROUP_TABLE, args, db_ID + "=" + groupId, null) > 0;
    }
    
    public boolean deleteGroup(long groupId) {
        // @return true if deleted, false otherwise
    	Log.d(TAG, "deleting group " + groupId);
       	String[] whereArgs = new String[] { Long.toString(groupId)};
		String whereClause = "tracker_id in (select _id from Trackers where group_id = ?)";
		mDb.delete(db_LOG_TABLE, whereClause, whereArgs);
        mDb.delete(db_ALERT_TABLE, whereClause, whereArgs);
        mDb.delete(db_TRACKER_TABLE, db_TRACKER_GROUP + "=" + groupId, null);
        updateAlertSchedule();
        return mDb.delete(db_GROUP_TABLE, db_ID + "=" + groupId, null) > 0;
    }

    
    /*
     * ******************************************************************************8
     * Handle Trackers table
     */

	private static final String[] TRACKER_COLUMNS = new String[] { db_ID,
			db_TRACKER_NAME, db_TRACKER_BODY, db_TRACKER_SKIP_NEXT_ALERT,
			db_TRACKER_GROUP, db_TRACKER_LAST_LOG_ID, db_TRACKER_USE_VALUE,
			db_TRACKER_VALUE_TYPE, db_TRACKER_VALUE_LABEL,
			db_TRACKER_VALUE_LABEL_POS };

	public long createTracker(Tracker tr) {
        // @return rowId or -1 if failed
		Log.d(TAG, "creating item " + tr.groupId + ":" + tr.name);
		return mDb.insert(db_TRACKER_TABLE, null, tr.getChanged());
    }

    public Cursor fetchTrackerCursor(long trackerId) {
        // @return Cursor positioned to matching note, if found
    	Log.d(TAG, "fetching tracker " + trackerId);
				
		Cursor mCursor = mDb.query(
				db_TRACKER_TABLE, 
				TRACKER_COLUMNS,
				db_ID + "=" + trackerId,
				null, null,	null, null);
        if (mCursor != null) {
            mCursor.moveToFirst();
        }
        return mCursor;

    }
    

	public Tracker fetchTracker(long trackerId) {
		Cursor c = null;
		Tracker t = null;
		try {
			c = fetchTrackerCursor(trackerId);
			if (c.getCount() != 1)
				return null;

			c.moveToFirst();
			t = new Tracker(trackerId);
			t.name = c.getString(c.getColumnIndex(C.db_TRACKER_NAME));
			t.body = c.getString(c.getColumnIndex(C.db_TRACKER_BODY));
			t.groupId = c.getLong(c.getColumnIndex(C.db_TRACKER_GROUP));
			t.lastLogId = c.getLong(c.getColumnIndex(C.db_TRACKER_LAST_LOG_ID));
			t.skipNextAlert = c.getInt(c.getColumnIndex(C.db_TRACKER_SKIP_NEXT_ALERT)) > 0;
			t.logUseValue = c.getInt(c.getColumnIndex(C.db_TRACKER_USE_VALUE)) > 0;
			t.logValueType = c.getInt(c.getColumnIndex(C.db_TRACKER_VALUE_TYPE));
			t.logValueLabel = c.getString(c.getColumnIndex(C.db_TRACKER_VALUE_LABEL));
			t.logValueLabelPos = c.getInt(c.getColumnIndex(C.db_TRACKER_VALUE_LABEL_POS));
			if(t.lastLogId > 0)
				t.lastLog = fetchLog(t.lastLogId);
		} finally {
			if (c != null) {
				c.close();
			}
		}
		return t;
	}

	public Cursor fetchTrackers(long groupId, String filter) {
        // @return Cursor over all trackers in a group
		Log.d(TAG, "fetching trackers for group " + groupId);
		if(filter == null)
			return mDb.rawQuery(str(R.string.db_select_trackers),
					new String[] {"" + groupId});
		else
			return mDb.rawQuery(str(R.string.db_select_trackers_filter), 
					new String[] {"" + groupId, "%" + filter + "%"});
	}

	public void updateTracker(Tracker tracker) {
		ContentValues args = tracker.getChanged();
		if(args.size() > 0) {
			mDb.update(db_TRACKER_TABLE, args, db_ID + "=" + tracker.id, null);
			tracker.clearChanged();
		}
	}
	
    public void requeryTracker(Tracker t) {
    	Tracker newTracker = fetchTracker(t.id);
    	if(newTracker != null) {
    		t.copyFrom(newTracker);
        	t.clearChanged();
    	} else {
    		throw new EntryMissingException();
    	}
    }

    private void updateTrackerLastLog(long trackerId) {
    	mDb.execSQL(str(R.string.db_update_tracker_last_log), new Long[] {trackerId});
    	Tracker t = fetchTracker(trackerId);
		List<Alert> alerts = fetchAlertList(trackerId);
		for(Alert a : alerts) {
			a.setNextTimeFromLast(t.getLastLog());
			updateAlert(a);
		}
    }
    
    public boolean deleteTracker(long trackerId) {
        // @return true if deleted, false otherwise
    	Log.d(TAG, "deleting tracker " + trackerId);
    	mDb.delete(db_LOG_TABLE, db_LOG_TRACKER + " = " + trackerId, null);
    	deleteAlerts(trackerId);
        return mDb.delete(db_TRACKER_TABLE, db_ID + "=" + trackerId, null) > 0;
    }

    
    /*
     * ******************************************************************************
     * Handle TrackerLogs table
     */
    
	private static final String[] LOG_COLUMNS = { db_ID, db_LOG_TRACKER,
			db_LOG_TIME, db_LOG_BODY, db_LOG_VALUE_TYPE, db_LOG_VALUE,
			db_LOG_IS_BREAK };

	public long createLog(Tracker tracker) {
		return createLog(LogEntry.newLogFor(tracker));
	}
    
    public long createLog(LogEntry log) {
    	Log.d(TAG, "creating log for tracker " + log.trackerId);
    	if(log.logDate == null) 
    		log.setLogDate(new Date());
    	long logId = mDb.insert(db_LOG_TABLE, null, log.getChanged());
    	updateTrackerLastLog(log.trackerId);
    	return logId;
    }

	public LogEntry fetchLog(long logId) {
		Cursor c = null;
		LogEntry le = null;
		try {
			c = mDb.query(true, db_LOG_TABLE,
            		LOG_COLUMNS,
					db_ID + "=" + logId, null,
					null, null, null, null);
			if (c.getCount() != 1)
				return null;
            c.moveToFirst();
			le = new LogEntry(logId);
			le.body = c.getString(c.getColumnIndex(C.db_LOG_BODY));
			le.trackerId = c.getLong(c.getColumnIndex(C.db_LOG_TRACKER));
			String date = c.getString(c.getColumnIndex(C.db_LOG_TIME));
			if (date != null)
				le.logDate = dbDateFormat.parse(date);
			le.isBreak = c.getInt(c.getColumnIndex(C.db_LOG_IS_BREAK)) > 0;
			le.valueType = c.getLong(c.getColumnIndex(C.db_LOG_VALUE_TYPE));
			le.value = ValueType.getById(le.valueType)
						.getValueFromCursor(c, c.getColumnIndex(C.db_LOG_VALUE));
			
		} catch (ParseException e) {
			throw new RuntimeException("fetchLog couldn't parse date for " + logId, e);
		} finally {
			if (c != null) {
				c.close();
			}
		}
		return le;
	}

	private static final String[] LOG_CURSOR_COLUMNS = { db_ID, db_LOG_TIME,
			db_LOG_BODY, db_LOG_VALUE, db_LOG_VALUE_TYPE };

	public Cursor fetchLogs(long trackerId) {
        // @return Cursor positioned to matching log, if found
    	Log.d(TAG, "fetching logs for item " + trackerId);
        Cursor c = mDb.query(true, db_LOG_TABLE,
				LOG_CURSOR_COLUMNS,
				db_LOG_TRACKER + "=" + trackerId, null,
		        null, null, db_LOG_TIME + " DESC", null);
        if (c != null)
            c.moveToFirst();
        return c;

    }
    
	public void updateLog(LogEntry log) {
		ContentValues args = log.getChanged();
		if(args.size() > 0) {
	        mDb.update(db_LOG_TABLE, args, db_ID + "=" + log.id, null);
	    	updateTrackerLastLog(log.trackerId);
	        log.clearChanged();
		}
	}
    
    public boolean deleteLog(long logId, long trackerId) {
        // @return true if deleted, false otherwise
    	Log.d(TAG, "deleting log " + logId);
        boolean status = mDb.delete(db_LOG_TABLE, db_ID + "=" + logId, null) > 0;
    	updateTrackerLastLog(trackerId);
    	return status;
    }

    /*
     * ******************************************************************************
     * Handle Alerts table
     */
    
	    /**
	     * Create a DB alert entry from an Alert DTO
	     * 
	     * @param newAlert the DTO to create in the DB
	     * @return rowId of the alert created
	     */
    public long createAlert(Alert newAlert) {
    	Log.d(TAG, "creating Alert for tracker " + newAlert.trackerId);
       	long alertId = mDb.insert(db_ALERT_TABLE, null, newAlert.getChanged());
       	updateAlertSchedule();
       	return alertId;
    }

	    /** All columns from the Alerts table (for queries) */
	private static final String[] ALERT_COLUMNS = new String[] { 
			db_ID, db_ALERT_TRACKER,
			db_ALERT_INTERVAL_MONTHS, db_ALERT_INTERVAL_WEEKS, 
			db_ALERT_INTERVAL_DAYS, db_ALERT_INTERVAL_HOURS, 
			db_ALERT_INTERVAL_MINUTES, db_ALERT_INTERVAL_SECONDS, 
			db_ALERT_NEXT_TIME, db_ALERT_ENABLED, db_ALERT_SKIP_NEXT,
			db_ALERT_RINGTONE };
		
		/**
		 * Fetches an Alert DTO from the DB with the given rowId
		 * 
		 * @param alertId The rowId of the alert
		 * @return The corresponding Alert object, or null if not found
		 */
	public Alert fetchAlert(long alertId) {
		return fetchAlert(db_ID + "=" + alertId, null, null, null, null, null);
	}
	
	public Alert fetchAlert(String where, String[] where_params, String group_by, String having, String order_by, String limit) {
		Cursor c = null;
		Alert alert = null;
		try {
			c = mDb.query(true, db_ALERT_TABLE,
            		ALERT_COLUMNS,
					where, where_params,
					group_by, having, order_by, limit);
			if (c.getCount() != 1)
				return null;
            c.moveToFirst();
            alert = getAlertFromCursor(c);
		} finally {
			if (c != null) {
				c.close();
			}
		}
		return alert;
	}
	
    public List<Alert> fetchAlertList(long trackerId) {
        // @return ArrayList of matching alerts, if found
    	// WARN: this has never been tested
    	Log.d(TAG, "fetching alerts for tracker " + trackerId);
        List<Alert> alerts = new ArrayList<Alert>();
        Cursor c = fetchAlertsCursor(trackerId);
        if (c != null) {
	        c.moveToFirst();
	        while(!c.isAfterLast()) {
	        	alerts.add(getAlertFromCursor(c));
	        	c.moveToNext();
	        }
	        c.close();
        }
        return alerts;
    }
	
		/**
		 * Turns a single cursor row from the DB into an Alert object
		 * @param c The cursor
		 * @return The filled out Alert oject
		 */
	private Alert getAlertFromCursor(Cursor c) {
		Alert a = new Alert(c.getLong(0));
		a.trackerId = c.getLong(c.getColumnIndex(C.db_ALERT_TRACKER));
		a.isEnabled = c.getInt(c.getColumnIndex(C.db_ALERT_ENABLED)) > 0;
		a.skipNext = c.getInt(c.getColumnIndex(C.db_ALERT_SKIP_NEXT)) > 0;
		a.ivalMonths = c.getInt(c.getColumnIndex(C.db_ALERT_INTERVAL_MONTHS));
		a.ivalWeeks = c.getInt(c.getColumnIndex(C.db_ALERT_INTERVAL_WEEKS));
		a.ivalDays = c.getInt(c.getColumnIndex(C.db_ALERT_INTERVAL_DAYS));
		a.ivalHours = c.getInt(c.getColumnIndex(C.db_ALERT_INTERVAL_HOURS));
		a.ivalMinutes = c.getInt(c.getColumnIndex(C.db_ALERT_INTERVAL_MINUTES));
		a.ivalSeconds = c.getInt(c.getColumnIndex(C.db_ALERT_INTERVAL_SECONDS));
		a.ringtone = c.getString(c.getColumnIndex(C.db_ALERT_RINGTONE));
		String date = c.getString(c.getColumnIndex(C.db_ALERT_NEXT_TIME));
		try {
			if (date != null)
				a.nextTime = dbDateFormat.parse(date);
		} catch (ParseException e) {
			throw new RuntimeException("fetchAlert couldn't parse nextTime for " + a.id, e);
		}
		return a;
	}
	
		/**
		 * Fetches an Alert cursor of all alerts for a tracker 
		 * @param trackerId The rowId of the tracker
		 * @return Cursor of all the tracker's alerts
		 */
    public Cursor fetchAlertsCursor(long trackerId) {
    	Log.d(TAG, "fetching alerts for tracker " + trackerId);
        Cursor c = mDb.query(true, db_ALERT_TABLE,
				ALERT_COLUMNS,
				db_ALERT_TRACKER + "=" + trackerId, null,
		        null, null, db_ID, null);
        if (c != null)
            c.moveToFirst();
        return c;

    }
    
    	/**
    	 * Updates an alert in the DB according to changes in the DTO (if any)
    	 * 
    	 * @param alert The Alert DTO of the alert to be updated
    	 */
	public void updateAlert(Alert alert) {
		ContentValues args = alert.getChanged();
		if(args.size() > 0) {
	        mDb.update(db_ALERT_TABLE, args, db_ID + "=" + alert.id, null);
	        alert.clearChanged();
	        updateAlertSchedule();
		}
	}
    
		/**
		 * Deletes an alert from the DB
		 * 
		 * @param alertId The rowId of the alert to delete
		 */
	public void deleteAlert(long alertId) {
    	Log.d(TAG, "deleting alert " + alertId);
        mDb.delete(db_ALERT_TABLE, db_ID + "=" + alertId, null);
        updateAlertSchedule();
    }

	/**
	 * Deletes an alert from the DB
	 * 
	 * @param trackerId The rowId of the tracker to delete alerts for
	 */
	public void deleteAlerts(long trackerId) {
		Log.d(TAG, "deleting alerts for tracker " + trackerId);
	    mDb.delete(db_ALERT_TABLE, db_ALERT_TRACKER + "=" + trackerId, null);
        updateAlertSchedule();
	}

		/**
		 * Fetches the Alert from the DB that is enabled and has the soonest
		 * scheduled time to go off (even if it may be skipped once reached).
		 *   
		 * @return The Alert DTO, or none if none are scheduled
		 */
    public Alert fetchNextAlert() {
		Log.d(TAG, "fetching next alert");
		return fetchAlert(
			db_ALERT_ENABLED + " = 1 AND " + db_ALERT_SKIP_NEXT + " != 1 AND " +
			db_ALERT_NEXT_TIME + " > datetime('now', 'localtime')",
			null, null, null, db_ALERT_NEXT_TIME + " ASC", "1"
			);
	}
    
    public void updateAlertSchedule() {
		Alert next = fetchNextAlert();
		if(next == null) {
			Log.d(TAG, "Clearing alerts");			
			AlertReceiver.clearAlert(mContext);
		} else {
			Log.d(TAG, "Next alert will be at " + next.getNextTime().toLocaleString());
			AlertReceiver.setAlert(mContext, next.getTrackerId(), next.getNextTime().getTime());
		}
	}
	
	/*
	 * **************************************************************
     * 
	 * Helper classes
     */
    
    public class EntryMissingException extends RuntimeException {
    	// This "should not happen"
		private static final long serialVersionUID = 1L;
    }
    
    // inner class for handling basic DB maintenance
	private class DbAdmin extends SQLiteOpenHelper {
		public DbAdmin(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override /* create DB tables */
		public void onCreate(SQLiteDatabase db) {
			onUpgrade(db, 0, DATABASE_VERSION);
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			if (oldVersion == 0)
				Log.i(TAG, "Creating new database");
			else
				Log.i(TAG, "Upgrading database from version " + oldVersion
						+ " to " + newVersion);

			try {
				switch(oldVersion) {
				case 0:
					db.execSQL(str(R.string.db_1_create_groups));
					Log.i(TAG, "Created table " + db_GROUP_TABLE);

					ContentValues firstRow = new ContentValues();
					firstRow.put(db_GROUP_NAME, str(R.string.db_first_list));
					db.insertOrThrow(db_GROUP_TABLE, db_GROUP_NAME, firstRow);

					db.execSQL(str(R.string.db_1_create_trackers));
					Log.i(TAG, "Created table " + db_TRACKER_TABLE);

					db.execSQL(str(R.string.db_1_create_logs));
					Log.i(TAG, "Created table " + db_LOG_TABLE);
			
					db.execSQL(str(R.string.db_1_create_alerts));
					Log.i(TAG, "Created table " + db_ALERT_TABLE);
					
					db.execSQL(str(R.string.db_1_create_tracker_group_id_idx));
					db.execSQL(str(R.string.db_1_create_log_tracker_time_idx));
					db.execSQL(str(R.string.db_1_create_alert_tracker_id_idx));
					Log.i(TAG, "Created indexes");
				case 1:
					db.execSQL(str(R.string.db_2_alter_tracker_add_value_label));
					db.execSQL(str(R.string.db_2_alter_tracker_add_value_label_pos));
					Log.i(TAG, "Added tracker value labels");
				case 2:
					db.execSQL(str(R.string.db_3_alter_tracker_add_use_value));
					db.execSQL(str(R.string.db_3_alter_tracker_add_value_type));
					db.execSQL(str(R.string.db_3_alter_logs_add_value_type));
					db.execSQL(str(R.string.db_3_update_trackers_set_use_value));
					Log.i(TAG, "Added tracker value types");
					
/*					db.execSQL(str(R.string.db_create_valuetypes));
					ContentValues firstRow = new ContentValues();
					firstRow.put(db_VALUE_NAME, str(R.string.db_first_value_name));
					firstRow.put(db_VALUE_TYPE, str(R.string.db_first_value_type));
					db.insertOrThrow(db_VALUE_TABLE, null, firstRow);
					Log.i(TAG, "Created table " + db_VALUE_TABLE);
*/
				}

			} catch (SQLException e) {
				Log.e(TAG, e.getMessage(), e);
				throw (e);
			}
		}
	}
}
import java.util.Date;
import net.sosiouxme.logmylife.C;
import net.sosiouxme.logmylife.LogMyLife;
import net.sosiouxme.logmylife.R;
import net.sosiouxme.logmylife.Util;
import net.sosiouxme.logmylife.custom.EventCursorAdapter;
import net.sosiouxme.logmylife.custom.GroupSpinner;
import net.sosiouxme.logmylife.custom.TrackerCursorAdapter;
import net.sosiouxme.logmylife.custom.GroupSpinner.OnGroupSelectedListener;
import net.sosiouxme.logmylife.dialog.TrackerDeleteDialog;
import net.sosiouxme.logmylife.domain.DbAdapter;
import android.app.Dialog;
import android.app.ListActivity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView;
import android.widget.FilterQueryProvider;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
/*
    This file is part of LogMyLife, an application for logging events.
    Copyright (C) 2011 Luke Meyer

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program (see LICENSE file).
    If not, see http://www.gnu.org/licenses/
*/

package net.sosiouxme.logmylife.activity;



/**
Activity that presents the UI for the application's main screen, which 
displays all trackers for the currently selected group.

@author Luke Meyer, Copyright 2010
See LICENSE file for this file's GPLv3 distribution license.
*/

public class Main extends ListActivity implements OnItemClickListener, OnGroupSelectedListener, FilterQueryProvider, android.view.View.OnClickListener {
	private static final int RC_IMPEXP_DATA = 2;
	private static final String TAG = "LML.Main";
	private static final int DIALOG_ABOUT = 0;
	private static final int DIALOG_CHANGELOG = 1;
	private static final int DIALOG_GROUP_INFO = 2;
	private DbAdapter mDba;
	private GroupSpinner mSpinner = null;