← Back to team overview

tomdroid-dev team mailing list archive

Patch: lifecycle management and content provider

 

Hi,

I intended to look at the lifecycle management in Tomdroid 0.2 (or
actually the trunk). It all seems fine now. Except that some the power
of the ContentProvider is not used currently (as is indicated with TODOs
in the code).

The appended patch (or you can merge the branch on launchpad) contains
various changes:

1) Remove 'close' menu item, as it is not necessary with Android, use
the 'back' key instead.

2) Substitute OI About as about dialog. In eclipse you need to have a
project with the OI About sourcecode open now. If this is a problem, we
can just copy the few constants that are shared this way into Tomdroid.

3) Use a Service to fill the ContentProvider in onCreate() (but
threaded) and let Android fill the list from the ContentProvider itself.
Notes are not removed at this point. Also, some other thread mechanisms
can likely be removed.

4) Slight UI changes. At least I think this puts Tomdroid more in line
with other Android software. 

It all seems to work, but is not fully tested. Also I moved many pieces
of code around, without making changes to the many remarks and TODOs in
the code, so some refactoring and sifting through is probably in order. 

Let me know if you have any questions. I hope a new version can
eventually be released with these changes.

Regards,
-- 
pjv
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: ezelspinguin@xxxxxxxxx-20090627210405-wlnktoxtf0q3xe8y
# target_branch: file:///Data/Code%20Repository/tomdroid/tomdroid/
# testament_sha1: 22f84c05d97fe028c9ceb2f72ca06470989f9557
# timestamp: 2009-06-27 23:10:55 +0200
# base_revision_id: olivier@xxxxxxxxxxxxxxxxx-20090625011757-\
#   gui128fmsj3ddb3d
# 
# Begin patch
=== modified file '.classpath'
--- .classpath	2009-06-21 15:58:01 +0000
+++ .classpath	2009-06-27 21:04:05 +0000
@@ -4,5 +4,6 @@
 	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
 	<classpathentry kind="lib" path="lib/joda-time/joda-time-1.6.jar"/>
 	<classpathentry kind="src" path="gen"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/OI About"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>

=== modified file '.project'
--- .project	2008-11-19 07:01:14 +0000
+++ .project	2009-06-27 21:04:05 +0000
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>tomdroid</name>
+	<name>tomdroid-fix-Android-lifecycle</name>
 	<comment></comment>
 	<projects>
 	</projects>

=== modified file 'AndroidManifest.xml'
--- AndroidManifest.xml	2009-06-24 19:50:22 +0000
+++ AndroidManifest.xml	2009-06-27 21:04:05 +0000
@@ -6,7 +6,27 @@
 	<uses-sdk android:minSdkVersion="1" />
 
 	<application android:icon="@drawable/icon" android:label="@string/app_name">
-
+     	<meta-data android:name="org.openintents.metadata.COMMENTS"
+                       android:value="@string/about_comments" />
+        <meta-data android:name="org.openintents.metadata.COPYRIGHT"
+                       android:value="@string/about_copyright" />
+        <meta-data android:name="org.openintents.metadata.AUTHORS"
+                       android:resource="@array/about_authors" />
+        <meta-data android:name="org.openintents.metadata.DOCUMENTERS"
+                       android:resource="@array/about_documenters" />
+        <meta-data android:name="org.openintents.metadata.TRANSLATORS"
+                       android:resource="@string/about_translators" />
+        <meta-data android:name="org.openintents.metadata.ARTISTS"
+                       android:resource="@array/about_artists" />
+        <meta-data android:name="org.openintents.metadata.WEBSITE_LABEL"
+                       android:value="@string/about_website_label" />
+        <meta-data android:name="org.openintents.metadata.WEBSITE_URL"
+                       android:value="@string/about_website_url" />
+        <meta-data android:name="org.openintents.metadata.EMAIL"
+                       android:value="@string/about_email" />
+        <meta-data android:name="org.openintents.metadata.LICENSE"
+                       android:resource="@raw/license_short" />
+                       
         <activity android:label="@string/app_name"
         		  android:name=".ui.Tomdroid"
         		  android:launchMode="singleTop"
@@ -31,6 +51,8 @@
 		<provider android:name="NoteProvider"
             android:authorities="org.tomdroid.notes"
         />
+        
+		<service android:name="NoteCollectingService"></service>
 </application>
 	<uses-permission android:name="android.permission.INTERNET" />
 </manifest> 
\ No newline at end of file

=== modified file 'res/layout/main_list_item.xml'
--- res/layout/main_list_item.xml	2009-06-23 02:51:17 +0000
+++ res/layout/main_list_item.xml	2009-06-27 21:04:05 +0000
@@ -21,9 +21,10 @@
  You should have received a copy of the GNU General Public License
  along with Tomdroid.  If not, see <http://www.gnu.org/licenses/>.
 -->
-<TextView android:id="@+id/note_title" xmlns:android="http://schemas.android.com/apk/res/android";
+<TextView android:id="@android:id/text1" xmlns:android="http://schemas.android.com/apk/res/android";
 		android:layout_width="fill_parent"
-		android:layout_height="fill_parent"
-		android:textSize="24dp"
-		android:padding="10dip"
-		/>
+		android:layout_height="wrap_content"
+		android:minHeight="?android:attr/listPreferredItemHeight"
+		android:textAppearance="?android:attr/textAppearanceLarge"
+		android:gravity="center_vertical"
+		android:paddingLeft="5dip"/>

=== modified file 'res/menu/main.xml'
--- res/menu/main.xml	2009-04-06 01:07:32 +0000
+++ res/menu/main.xml	2009-06-27 21:04:05 +0000
@@ -28,11 +28,11 @@
 			android:title="@string/menuLoadWebNote"
 			android:id="@+id/menuLoadWebNote"
 			/>
-	<item 
+	<!-- <item 
 			android:icon="@drawable/icon_close"
 			android:title="@string/menuClose"
 			android:id="@+id/menuClose"
-			/>
+			/> -->
 	<item 
 			android:icon="@drawable/icon_about"
 			android:title="@string/menuAbout"

=== added directory 'res/raw'
=== added file 'res/raw/license_short'
--- res/raw/license_short	1970-01-01 00:00:00 +0000
+++ res/raw/license_short	2009-06-27 21:04:05 +0000
@@ -0,0 +1,12 @@
+Tomdroid 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.
+
+Tomdroid 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 Tomdroid.  If not, see <http://www.gnu.org/licenses/>.
\ No newline at end of file

=== added file 'res/values/arrays.xml'
--- res/values/arrays.xml	1970-01-01 00:00:00 +0000
+++ res/values/arrays.xml	2009-06-27 21:04:05 +0000
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string-array name="about_authors">
+        <item>Olivier Bilodeau olivier@xxxxxxxxxxxxxxxxx</item>
+        <item>pjv https://launchpad.net/~pjv</item>
+        <item>This app reuses several system / typical components.</item>
+    </string-array>
+    
+    <string-array name="about_documenters">
+        <item>Olivier Bilodeau olivier@xxxxxxxxxxxxxxxxx</item>
+        <item>pjv https://launchpad.net/~pjv</item>
+    </string-array>
+    
+    <string-array name="about_translators">
+        <item>Olivier Bilodeau olivier@xxxxxxxxxxxxxxxxx</item>
+    </string-array>
+    
+    <string-array name="about_artists">
+        <item>Olivier Bilodeau olivier@xxxxxxxxxxxxxxxxx</item>
+    </string-array>
+</resources>
\ No newline at end of file

=== modified file 'res/values/strings.xml'
--- res/values/strings.xml	2009-06-21 22:50:37 +0000
+++ res/values/strings.xml	2009-06-27 21:04:05 +0000
@@ -23,9 +23,7 @@
 -->
 <resources>
     <string name="app_name">Tomdroid</string>
-    <string name="app_desc">Tomboy compatible wikiwiki note-taking application that can\'t take notes yet.</string>
-    <string name="author">Olivier Bilodeau</string>
-
+    
 	<!-- main.xml -->
     <string name="strListEmptyWaiting">Please wait while the notes load..</string>
     <string name="strListEmptyNoNotes">
@@ -46,12 +44,20 @@
     	\n\nIf you would like to contribute, help at all levels would be appreciated. 
     	Hop on board on launchpad.net/tomdroid and contact us!\n
     </string>
-    <string name="strAbout">
-    	%1$s
-    	\n\nAuthor: %2$s
-    	\nVersion: %3$s
-    	\nLicensed under the GPLv3
-    </string>
+    	
+	<!-- About -->
+	
+	<string name="about_comments">Tomboy compatible wikiwiki note-taking application that can\'t take notes yet.</string>  
+	<string name="about_copyright">Copyright © 2009 Olivier Bilodeau</string><!-- Add your name here, separated with comma's, if you helped out --> 
+	<string name="about_website_label">Project page</string> 
+	<string name="about_website_url">http://www.launchpad.net/tomdroid/</string>
+	<string name="about_email">olivier@xxxxxxxxxxxxxxxxx</string>
+	<string name="about_feedback">tomdroid-dev@xxxxxxxxxxxxxxxxxxx</string>
+	<string name="about_translators">translator-credits</string>
+	<string name="about_backup">To view information about this app you need to install OI About. See http://www.openintents.org/ or the Market.</string>
+	<string name="link_about_dialog">market://search?q=pname:org.openintents.about</string>
+	<string name="market_backup">You do not seem to have the Android Market app installed. Ignoring.</string>
+	
 
 	<!-- load_web_note_dialog.xml -->
 	<string name="strLoadFromWebTitle">Note from the Web</string>

=== modified file 'src/org/tomdroid/Note.java'
--- src/org/tomdroid/Note.java	2009-06-21 20:25:16 +0000
+++ src/org/tomdroid/Note.java	2009-06-27 21:04:05 +0000
@@ -25,6 +25,7 @@
 import org.joda.time.DateTime;
 import org.joda.time.format.DateTimeFormatter;
 import org.joda.time.format.ISODateTimeFormat;
+import org.tomdroid.ui.Tomdroid;
 
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
@@ -45,7 +46,7 @@
 	public static final String[] PROJECTION = { Note.ID, Note.TITLE, Note.FILE, Note.MODIFIED_DATE };
 	
 	// Logging info
-	private static final String TAG = "Note";
+	private static final String TAG = Tomdroid.TAG;//"Note";
 	
 	// Notes constants
 	// TODO this is a weird yellow that was usable for the android emulator, I must confirm this for real usage

=== added file 'src/org/tomdroid/NoteCollectingService.java'
--- src/org/tomdroid/NoteCollectingService.java	1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/NoteCollectingService.java	2009-06-27 21:04:05 +0000
@@ -0,0 +1,183 @@
+package org.tomdroid;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.tomdroid.ui.Tomdroid;
+import org.tomdroid.xml.NoteHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+import android.app.Service;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.IBinder;
+import android.util.Log;
+
+public class NoteCollectingService extends Service {
+	private File notesRoot;
+
+	// domain elements
+	private NoteCollection noteCollection;
+
+	// Logging info
+	private static final String TAG = Tomdroid.TAG;//"NoteCollectingService";
+
+	/* (non-Javadoc)
+	 * @see android.app.Service#onStart(android.content.Intent, int)
+	 */
+	@Override
+	public void onStart(Intent intent, int startId) {
+		// TODO Auto-generated method stub
+		super.onStart(intent, startId);
+		
+		noteCollection = NoteCollection.getInstance();
+		notesRoot = new File(Tomdroid.NOTES_PATH);
+    	try {
+    		if (!notesRoot.exists()) {
+    			throw new FileNotFoundException("Tomdroid notes folder doesn't exist. It is configured to be at: "+Tomdroid.NOTES_PATH);
+    		}//TODO: FNFE not catched anymore
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+		  new Thread(new Runnable() {
+			    public void run() {
+					readAndParseNotes();
+					stopSelf();
+			    }
+			  }).start();
+	}
+
+
+	public void readAndParseNotes() {
+		File[] fileList = notesRoot.listFiles(new NotesFilter());
+		
+		// If there are no notes, warn the UI through an empty message
+		if (fileList.length == 0) {
+			//TODO: also remove notes from ContentProvider
+		}
+		
+		for (File file : fileList) {
+
+			// give a filename to a thread and ask to parse it when nothing's left to do its over
+			parseFile(file);
+        }
+	}
+	
+	/**
+	 * Simple filename filter that grabs files ending with .note
+	 * TODO move into its own static class in a util package
+	 */
+	class NotesFilter implements FilenameFilter {
+		public boolean accept(File dir, String name) {
+			return (name.endsWith(".note"));
+		}
+	}
+
+
+		public void parseFile(File file) {
+			Note note = new Note();
+			
+			note.setFileName(file.getAbsolutePath());
+			
+			try {
+				// Parsing
+		    	// XML 
+		    	// Get a SAXParser from the SAXPArserFactory
+		        SAXParserFactory spf = SAXParserFactory.newInstance();
+		        SAXParser sp = spf.newSAXParser();
+		
+		        // Get the XMLReader of the SAXParser we created
+		        XMLReader xr = sp.getXMLReader();
+		        
+		        // Create a new ContentHandler, send it this note to fill and apply it to the XML-Reader
+		        NoteHandler xmlHandler = new NoteHandler(note);
+		        xr.setContentHandler(xmlHandler);
+
+		        
+		        // Create the proper input source based on if its a local note or a web note
+	        	FileInputStream fin = new FileInputStream(file);
+				BufferedReader in = new BufferedReader(new InputStreamReader(fin), 8192);
+				InputSource is = new InputSource(in);
+		        
+				if (Tomdroid.LOGGING_ENABLED) Log.v(TAG, "parsing note");
+				xr.parse(is);
+			
+			// TODO wrap and throw a new exception here
+			} catch (ParserConfigurationException e) {
+				e.printStackTrace();
+			} catch (SAXException e) {
+				e.printStackTrace();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+			
+			synchronized (noteCollection) {
+				noteCollection.addNote(note);
+			}
+			
+			// notify UI that we are done here and send result 
+			updateNoteListWith(note.getTitle());
+		}
+	    
+		private void updateNoteListWith(String noteTitle) {
+			
+			// get the note instance we will work with that instead  from now on
+			Note note = NoteCollection.getInstance().findNoteFromTitle(noteTitle);
+			
+			// verify if the note is already in the content provider
+
+			// TODO I could see a problem where someone delete a note and recreate one with the same title.
+			// It would been seen as not new although it is (it will have a new filename)
+			// TODO make the query prettier (use querybuilder)
+			Uri notes = Tomdroid.CONTENT_URI;
+			String[] whereArgs = new String[1];
+			whereArgs[0] = noteTitle;
+			Cursor managedCursor = getContentResolver().query( notes,
+	                NoteProvider.PROJECTION,  
+	                Note.TITLE + "= ?",
+	                whereArgs,
+	                Note.TITLE + " ASC");
+			if (managedCursor.getCount() == 0) {
+				
+				// This note is not in the database yet we need to insert it
+				if (Tomdroid.LOGGING_ENABLED) Log.v(TAG,"A new note has been detected (not yet in db)");
+
+				// This add the note to the content Provider
+				// TODO PoC code that should be removed in next iteration's refactoring (no notecollection, everything should come from the provider I guess?)
+	    		ContentValues values = new ContentValues();
+	    		values.put(Note.TITLE, note.getTitle());
+	    		values.put(Note.FILE, note.getFileName());
+	    		Uri uri = getContentResolver().insert(Tomdroid.CONTENT_URI, values);
+	    		// now that we inserted the note put its ID in the note itself
+	    		note.setDbId(Integer.parseInt(uri.getLastPathSegment()));
+
+	    		if (Tomdroid.LOGGING_ENABLED) Log.v(TAG,"Note inserted in content provider. ID: "+uri+" TITLE:"+noteTitle+" ID:"+note.getDbId());
+			} else {
+				
+				// find out the note's id and put it in the note
+			    if (managedCursor.moveToFirst()) {
+			        int idColumn = managedCursor.getColumnIndex(Note.ID);
+		            note.setDbId(managedCursor.getInt(idColumn));
+			    }
+			}
+		}
+
+
+		@Override
+		public IBinder onBind(Intent intent) {
+			throw new RuntimeException("onBind not supported for NoteCollectingService");
+		}
+}

=== modified file 'src/org/tomdroid/NoteCollection.java'
--- src/org/tomdroid/NoteCollection.java	2009-06-21 22:20:10 +0000
+++ src/org/tomdroid/NoteCollection.java	2009-06-27 21:04:05 +0000
@@ -22,17 +22,13 @@
  */
 package org.tomdroid;
 
-import java.io.File;
-import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.regex.Pattern;
 
 import org.tomdroid.ui.Tomdroid;
-import org.tomdroid.util.AsyncNoteLoaderAndParser;
 
-import android.os.Handler;
 import android.util.Log;
 
 // TODO Transform the NoteCollection into a Provider (see .../android-sdk-linux_x86-1.0_r1/docs/devel/data/contentproviders.html#creatingacontentprovider)
@@ -44,7 +40,7 @@
 	private List<Note> notes = new ArrayList<Note>();
 
 	// Logging info
-	private static final String TAG = "NoteCollection";
+	private static final String TAG = Tomdroid.TAG;//"NoteCollection";
 	
 	public List<Note> getNotes() {
 		return notes;
@@ -76,6 +72,18 @@
 		return null;
 	}
 	
+	public synchronized Note findNoteFromDbId(long id) {
+		if (Tomdroid.LOGGING_ENABLED) Log.d(TAG,"searching for note with db id: "+id);
+		Iterator<Note> i = notes.iterator();
+		while(i.hasNext()) {
+			Note curNote = i.next();
+			if (curNote.getDbId()==id) {
+				return curNote;
+			}
+		}
+		return null;
+	}
+	
 	// TODO there is most likely a better way to do this
 	public Note findNoteFromFilename(String filename) {
 		if (Tomdroid.LOGGING_ENABLED) Log.d(TAG,"searching for note with filename: "+filename);
@@ -88,18 +96,6 @@
 		}
 		return null;
 	}
-	
-	// TODO also throw a empty exception that we will catch in tomdroid and display the empty notelist msg
-	public void loadNotes(Handler hndl) throws FileNotFoundException {
-		File notesRoot = new File(Tomdroid.NOTES_PATH);
-		
-		if (!notesRoot.exists()) {
-			throw new FileNotFoundException("Tomdroid notes folder doesn't exist. It is configured to be at: "+Tomdroid.NOTES_PATH);
-		}
-		
-		AsyncNoteLoaderAndParser asyncLoader = new AsyncNoteLoaderAndParser(notesRoot, this, hndl);
-		asyncLoader.readAndParseNotes();
-	}
 		
 	/**
 	 * Builds a regular expression pattern that will match any of the note title currently in the collection.

=== modified file 'src/org/tomdroid/NoteProvider.java'
--- src/org/tomdroid/NoteProvider.java	2009-04-06 22:30:41 +0000
+++ src/org/tomdroid/NoteProvider.java	2009-06-27 21:04:05 +0000
@@ -67,20 +67,25 @@
 	// --	
 	private static final String DATABASE_NAME = "tomdroid-notes.db";
 	private static final String DB_TABLE_NOTES = "notes";
-	private static final int DB_VERSION = 1;
+	private static final int DB_VERSION = 2;
 	// TODO once properly implemented, sort by: KEY_MODIFIED_DATE + " DESC"
-	private static final String DEFAULT_SORT_ORDER = Note.ID;
+	public static final String DEFAULT_SORT_ORDER = Note.ID;
 	
     private static HashMap<String, String> notesProjectionMap;
 
     private static final int NOTES = 1;
     private static final int NOTE_ID = 2;
     private static final int NOTE_TITLE = 3;
+    
+    public static final String[] PROJECTION = new String[] {
+		    Note.ID,
+		    Note.TITLE,
+		};
 
     private static final UriMatcher uriMatcher;
     
     // Logging info
-    private static final String TAG = "NoteProvider";
+    private static final String TAG = Tomdroid.TAG;//"NoteProvider";
 
     /**
      * This class helps open, create, and upgrade the database file.

=== modified file 'src/org/tomdroid/ui/Tomdroid.java'
--- src/org/tomdroid/ui/Tomdroid.java	2009-06-24 19:50:22 +0000
+++ src/org/tomdroid/ui/Tomdroid.java	2009-06-27 21:04:05 +0000
@@ -22,24 +22,24 @@
  */
 package org.tomdroid.ui;
 
-import java.io.FileNotFoundException;
+import java.io.File;
 
+import org.openintents.intents.AboutIntents;
 import org.tomdroid.Note;
+import org.tomdroid.NoteCollectingService;
 import org.tomdroid.NoteCollection;
+import org.tomdroid.NoteProvider;
 import org.tomdroid.R;
 
 import android.app.AlertDialog;
 import android.app.ListActivity;
-import android.content.ContentValues;
+import android.content.ActivityNotFoundException;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.DialogInterface.OnClickListener;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -47,7 +47,9 @@
 import android.view.View;
 import android.widget.ArrayAdapter;
 import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
 import android.widget.TextView;
+import android.widget.Toast;
 
 public class Tomdroid extends ListActivity {
 
@@ -56,16 +58,15 @@
 	public static final Uri	CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/notes");
     public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.tomdroid.note";
     public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.tomdroid.note";
-    public static final String PROJECT_HOMEPAGE = "http://www.launchpad.net/tomdroid/";;
-	
+
 	// config parameters
 	// TODO hardcoded for now
 	public static final String NOTES_PATH = "/sdcard/tomdroid/";
 	// Logging should be disabled for release builds
-	public static final boolean LOGGING_ENABLED = false;
+	public static final boolean LOGGING_ENABLED = true;
 
 	// Logging info
-	private static final String TAG = "Tomdroid";
+	public static final String TAG = "Tomdroid";
 	
 	// data keys
 	public static final String RESULT_URL_TO_LOAD = "urlToLoad"; 
@@ -78,7 +79,6 @@
 	private NoteCollection localNotes;
 	
 	// UI to data model glue
-	private ArrayAdapter<String> notesListAdapter;
 	private TextView listEmptyView;
 	
 	// Bundle keys for saving state
@@ -111,9 +111,23 @@
 				.show();
         }
         
-	    // listAdapter that binds the UI to the notes names
-		notesListAdapter = new ArrayAdapter<String>(this, R.layout.main_list_item);
-        setListAdapter(notesListAdapter);
+        // If no data was given in the intent (because we were started
+        // as a MAIN activity), then use our default content provider.
+        Intent intent = getIntent();
+        if (intent.getData() == null) {
+            intent.setData(CONTENT_URI);
+        }
+
+        
+        // Perform a managed query. The Activity will handle closing and requerying the cursor
+        // when needed.
+        Cursor cursor = managedQuery(getIntent().getData(), NoteProvider.PROJECTION, null, null,
+                NoteProvider.DEFAULT_SORT_ORDER);
+
+        // Used to map notes entries from the database to views
+        SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.main_list_item, cursor,
+                new String[] { Note.TITLE }, new int[] { android.R.id.text1 });
+        setListAdapter(adapter);
 
         // set the view shown when the list is empty
         listEmptyView = (TextView)findViewById(R.id.list_empty);
@@ -121,24 +135,19 @@
         
         // start loading local notes
         if (LOGGING_ENABLED) Log.v(TAG, "Loading local notes");
-        
+		File notesRoot = new File(Tomdroid.NOTES_PATH);
+		if (!notesRoot.exists()) {
+			new AlertDialog.Builder(this)
+			.setMessage("Tomdroid notes folder doesn't exist. It is configured to be at: "+Tomdroid.NOTES_PATH)
+			.setTitle("Error")
+			.setNeutralButton("Ok", new OnClickListener() {
+				public void onClick(DialogInterface dialog, int which) {
+					dialog.dismiss();
+				}})
+			.show();
+		}
+		startService(new Intent(this, NoteCollectingService.class));
 		localNotes = NoteCollection.getInstance();
-		try {
-			localNotes.loadNotes(handler);
-		} catch (FileNotFoundException e) {
-			//TODO put strings in ressource
-			listEmptyView.setText(R.string.strListEmptyNoNotes);
-			new AlertDialog.Builder(this)
-				.setMessage(e.getMessage())
-				.setTitle("Error")
-				.setNeutralButton("Ok", new OnClickListener() {
-					public void onClick(DialogInterface dialog, int which) {
-						dialog.dismiss();
-					}})
-				.show();
-			e.printStackTrace();
-		}
-     
     }
 
 	@Override
@@ -156,12 +165,12 @@
 	        case R.id.menuLoadWebNote:
 	            showLoadWebNoteDialog();
 	            return true;
-        
+/*        
 	        case R.id.menuClose:
 	        	// closing everything then closing itself
 	        	finishActivity(ACTIVITY_VIEW);
 	        	finish();
-	        	return true;
+	        	return true;*/
 	        
 	        case R.id.menuAbout:
 				showAboutDialog();
@@ -180,41 +189,29 @@
 			outState.putBoolean(WARNING_SHOWN, true);
 		}
 	}
-
-	private void showAboutDialog() {
+	
+	/**
+	 * Show an about dialog for this application.
+	 */
+	protected void showAboutDialog() {
+		Intent intent = new Intent(AboutIntents.ACTION_SHOW_ABOUT_DIALOG);
 		
-		// grab version info
-		String ver;
-		try {
-			ver = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
-		} catch (NameNotFoundException e) {
-			e.printStackTrace();
-			ver = "Not found!";
+		// Start about activity. Needs to be "forResult" with requestCode>=0
+		// so that the package name is passed properly.
+		//
+		// The details are obtained from the Manifest through
+		// default tags and metadata.
+		try{
+			startActivityForResult(intent, 0);
+		}catch(ActivityNotFoundException e){
+			try{
+				//FlurryAgent.onError("LCA:showAboutDialog1", getString(R.string.about_backup), "Toast");
+				startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.link_about_dialog))));
+			}catch(ActivityNotFoundException e2){
+				//FlurryAgent.onError("LCA:showAboutDialog2", getString(R.string.about_backup), "Toast");
+				Toast.makeText(this, getString(R.string.market_backup), Toast.LENGTH_LONG).show();
+			}
 		}
-		
-		// format the string
-		String aboutDialogFormat = getString(R.string.strAbout);
-		String aboutDialogStr = String.format(aboutDialogFormat, 
-				getString(R.string.app_desc), 	// App description
-				getString(R.string.author), 	// Author name
-				ver							// Version
-				);
-		
-		// build and show the dialog
-		new AlertDialog.Builder(this)
-			.setMessage(aboutDialogStr)
-			.setTitle("About Tomdroid")
-			.setIcon(R.drawable.icon)
-			.setNegativeButton("Project page", new OnClickListener() {
-				public void onClick(DialogInterface dialog,	int which) {
-					startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Tomdroid.PROJECT_HOMEPAGE)));
-					dialog.dismiss();
-				}})
-			.setPositiveButton("Ok", new OnClickListener() {
-				public void onClick(DialogInterface dialog,	int which) {
-					dialog.dismiss();
-				}})
-			.show();
 	}
 
 	@Override
@@ -229,31 +226,12 @@
 				}
 		}
 	}
-	
-    private Handler handler = new Handler() {
-    	
-        @Override
-        public void handleMessage(Message msg) {
-        	
-        	// thread is done fetching a note and parsing went well 
-        	if (msg.what == Note.NOTE_RECEIVED_AND_VALID) {
-
-        		// update the note list with this newly parsed note
-        		updateNoteListWith(msg.getData().getString(Note.TITLE));
-        		
-        	} else if (msg.what == Note.NO_NOTES) {
-
-        		// if there are no notes, say so in the list_empty message
-    			listEmptyView.setText(R.string.strListEmptyNoNotes);
-        	}
-		}
-    };
 
 	@Override
 	protected void onListItemClick(ListView l, View v, int position, long id) {
 
 		// get the clicked note
-		Note n =  localNotes.findNoteFromTitle(notesListAdapter.getItem(position));
+		Note n =  localNotes.findNoteFromDbId(id);
 		
 		Intent i = new Intent(Tomdroid.this, ViewNote.class);
 		i.putExtra(Note.FILE, n.getFileName());
@@ -273,54 +251,4 @@
         i.putExtra(Note.URL, url);
         startActivity(i);
 	}
-	
-	private void updateNoteListWith(String noteTitle) {
-		
-		// add note to the note list
-		notesListAdapter.add(noteTitle);
-
-		// get the note instance we will work with that instead  from now on
-		Note note = localNotes.findNoteFromTitle(noteTitle);
-		
-		// verify if the note is already in the content provider
-		String[] projection = new String[] {
-			    Note.ID,
-			    Note.TITLE,
-			};
-
-		// TODO I could see a problem where someone delete a note and recreate one with the same title.
-		// It would been seen as not new although it is (it will have a new filename)
-		// TODO make the query prettier (use querybuilder)
-		Uri notes = Tomdroid.CONTENT_URI;
-		String[] whereArgs = new String[1];
-		whereArgs[0] = noteTitle;
-		Cursor managedCursor = managedQuery( notes,
-                projection,  
-                Note.TITLE + "= ?",
-                whereArgs,
-                Note.TITLE + " ASC");
-		if (managedCursor.getCount() == 0) {
-			
-			// This note is not in the database yet we need to insert it
-			if (LOGGING_ENABLED) Log.v(TAG,"A new note has been detected (not yet in db)");
-
-			// This add the note to the content Provider
-			// TODO PoC code that should be removed in next iteration's refactoring (no notecollection, everything should come from the provider I guess?)
-    		ContentValues values = new ContentValues();
-    		values.put(Note.TITLE, note.getTitle());
-    		values.put(Note.FILE, note.getFileName());
-    		Uri uri = getContentResolver().insert(CONTENT_URI, values);
-    		// now that we inserted the note put its ID in the note itself
-    		note.setDbId(Integer.parseInt(uri.getLastPathSegment()));
-
-    		if (LOGGING_ENABLED) Log.v(TAG,"Note inserted in content provider. ID: "+uri+" TITLE:"+noteTitle+" ID:"+note.getDbId());
-		} else {
-			
-			// find out the note's id and put it in the note
-		    if (managedCursor.moveToFirst()) {
-		        int idColumn = managedCursor.getColumnIndex(Note.ID);
-	            note.setDbId(managedCursor.getInt(idColumn));
-		    }
-		}
-	}
 }

=== modified file 'src/org/tomdroid/ui/ViewNote.java'
--- src/org/tomdroid/ui/ViewNote.java	2009-06-21 20:25:16 +0000
+++ src/org/tomdroid/ui/ViewNote.java	2009-06-27 21:04:05 +0000
@@ -60,7 +60,7 @@
 	private Note note;
 	
 	// Logging info
-	private static final String TAG = "ViewNote";
+	private static final String TAG = Tomdroid.TAG;//"ViewNote";
 	
 	// TODO extract methods in here
 	@Override

=== removed file 'src/org/tomdroid/util/AsyncNoteLoaderAndParser.java'
--- src/org/tomdroid/util/AsyncNoteLoaderAndParser.java	2009-06-21 21:28:14 +0000
+++ src/org/tomdroid/util/AsyncNoteLoaderAndParser.java	1970-01-01 00:00:00 +0000
@@ -1,163 +0,0 @@
-/*
- * Tomdroid
- * Tomboy on Android
- * http://www.launchpad.net/tomdroid
- * 
- * Copyright 2009 Olivier Bilodeau <olivier@xxxxxxxxxxxxxxxxx>
- * 
- * This file is part of Tomdroid.
- * 
- * Tomdroid 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.
- * 
- * Tomdroid 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 Tomdroid.  If not, see <http://www.gnu.org/licenses/>.
- */
-package org.tomdroid.util;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.tomdroid.Note;
-import org.tomdroid.NoteCollection;
-import org.tomdroid.ui.Tomdroid;
-import org.tomdroid.xml.NoteHandler;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-
-public class AsyncNoteLoaderAndParser {
-	private final ExecutorService pool;
-	private final static int poolSize = 1;
-	private File path;
-	private NoteCollection noteCollection;
-	private Handler parentHandler;
-	
-	// logging related
-	private final static String TAG = "AsyncNoteLoaderAndParser";
-	
-	public AsyncNoteLoaderAndParser(File path, NoteCollection nc, Handler hndl) {
-		this.path = path;
-		pool = Executors.newFixedThreadPool(poolSize);
-		noteCollection = nc;
-		parentHandler = hndl;
-	}
-
-	public void readAndParseNotes() {
-		File[] fileList = path.listFiles(new NotesFilter());
-		
-		// If there are no notes, warn the UI through an empty message
-		if (fileList.length == 0) {
-			parentHandler.sendEmptyMessage(Note.NO_NOTES);
-		}
-		
-		for (File file : fileList) {
-
-			// give a filename to a thread and ask to parse it when nothing's left to do its over
-			pool.execute(new Worker(file));
-        }
-	}
-	
-	/**
-	 * Simple filename filter that grabs files ending with .note
-	 * TODO move into its own static class in a util package
-	 */
-	class NotesFilter implements FilenameFilter {
-		public boolean accept(File dir, String name) {
-			return (name.endsWith(".note"));
-		}
-	}
-	
-	/**
-	 * The worker spawns a new note, parse the file its being given by the executor and add it to the NoteCollection
-	 */
-	class Worker implements Runnable {
-		
-		// the note to be loaded and parsed
-		private Note note = new Note();
-		private File file;
-		
-		public Worker(File f) {
-			file = f;
-		}
-
-		public void run() {
-			
-			note.setFileName(file.getAbsolutePath());
-			
-			try {
-				// Parsing
-		    	// XML 
-		    	// Get a SAXParser from the SAXPArserFactory
-		        SAXParserFactory spf = SAXParserFactory.newInstance();
-		        SAXParser sp = spf.newSAXParser();
-		
-		        // Get the XMLReader of the SAXParser we created
-		        XMLReader xr = sp.getXMLReader();
-		        
-		        // Create a new ContentHandler, send it this note to fill and apply it to the XML-Reader
-		        NoteHandler xmlHandler = new NoteHandler(note);
-		        xr.setContentHandler(xmlHandler);
-
-		        
-		        // Create the proper input source based on if its a local note or a web note
-	        	FileInputStream fin = new FileInputStream(file);
-				BufferedReader in = new BufferedReader(new InputStreamReader(fin), 8192);
-				InputSource is = new InputSource(in);
-		        
-				if (Tomdroid.LOGGING_ENABLED) Log.v(TAG, "parsing note");
-				xr.parse(is);
-			
-			// TODO wrap and throw a new exception here
-			} catch (ParserConfigurationException e) {
-				e.printStackTrace();
-			} catch (SAXException e) {
-				e.printStackTrace();
-			} catch (IOException e) {
-				e.printStackTrace();
-			}
-			
-			synchronized (noteCollection) {
-				noteCollection.addNote(note);
-			}
-			
-			// notify UI that we are done here and send result 
-			warnHandler();
-		}
-		
-	    private void warnHandler() {
-			
-			// notify the main UI that we are done here (sending an ok along with the note's title)
-			Message msg = Message.obtain();
-			Bundle bundle = new Bundle();
-			bundle.putString(Note.TITLE, note.getTitle());
-			msg.setData(bundle);
-			msg.what = Note.NOTE_RECEIVED_AND_VALID;
-			
-			parentHandler.sendMessage(msg);
-	    }
-		
-	}
-}

=== modified file 'src/org/tomdroid/util/NoteBuilder.java'
--- src/org/tomdroid/util/NoteBuilder.java	2009-06-21 20:25:16 +0000
+++ src/org/tomdroid/util/NoteBuilder.java	2009-06-27 21:04:05 +0000
@@ -60,7 +60,7 @@
 	// the object being built
 	private Note note = new Note();
 	
-	private final String TAG = "NoteBuilder";
+	private final String TAG = Tomdroid.TAG;//"NoteBuilder";
 	
 	// thread related
 	private Thread runner;

# Begin bundle
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

Follow ups