/*
* Funambol is a mobile platform developed by Funambol, Inc.
* Copyright (C) 2010 Funambol, Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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 Affero General Public License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA.
*
* You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
* 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by Funambol" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by Funambol".
*/
package de.chbosync.android.syncmlclient.source.pim.note;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import com.funambol.client.source.AppSyncSource;
import com.funambol.common.pim.model.common.Property;
import com.funambol.util.Log;
import com.funambol.util.StringUtil;
import de.chbosync.android.syncmlclient.source.AbstractDataManager;
/**
* This class contains the methods for accessing OINotepad's content provider, e.g. to
* read or update notes stored in OINotepad.
*/
public class OINoteManager extends AbstractDataManager<Note> {
/** Log entries tag */
private static final String TAG_LOG = "OINoteManager";
public static final String AUTHORITY = "org.openintents.notepad";
/** Flag to store if app "OI Notepad" is installed on device (added for ChBoSync). */
protected static boolean sOINotepadInstalled = false;
/**
* Added for ChBoSync; upon startup in method
* {@link de.chbosync.android.syncmlclient.AndroidAppSyncSourceManager.setupNotesSource(AndroidConfiguration)}
* it is checked if "OI Notepad" is installed on the current device or not.
*
* @param isInstalled <tt>true</tt> if "OI Notepad" is installed on the current device,
* <tt>false</tt> otherwise.
*/
public static void setOINotepadInstalled(boolean isInstalled) {
sOINotepadInstalled = isInstalled;
}
/**
* Added for ChBoSync; getter for flag if OINotepad is installed on the current device.
*
* @return <tt>true</tt> if "OI Notepad" is installed on the current device,
* <tt>false</tt> otherwise.
*/
public static boolean getOINotepadInstalled() {
return sOINotepadInstalled;
}
/** Inner class with some static variables containing identifiers for OINotepad's content provider. */
public static final class Notes {
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/notes");
public static final String _ID = "_id";
public static final String TITLE = "title";
public static final String NOTE = "note";
public static final String CREATED_DATE = "created";
public static final String MODIFIED_DATE = "modified";
public static final String TAGS = "tags";
public static final String[] PROJECTION = { _ID, TITLE, NOTE, };
}
/**
* Default constructor.
*
* @param context the Context object
* @param appSource the AppSyncSource object to be related to this manager
*/
public OINoteManager(Context context, AppSyncSource appSource) {
super(context);
//this.appSource = appSource;
}
/**
* Accessor method: get the authority for OINotepad.
*
* @return String the String formatted representation of the authority
*/
protected String getAuthority() {
return AUTHORITY;
}
/**
* Load a particular note entry.
*
* @param key the long formatted entry key to load
* @return Note object related to that entry
* @throws IOException if anything went wrong accessing OINotepad's content provider
*/
public Note load(String key) throws IOException {
if (Log.isLoggable(Log.TRACE)) {
Log.trace(TAG_LOG, "Loading Note: " + key);
}
long id;
try {
id = Long.parseLong(key);
} catch (Exception e) {
Log.error(TAG_LOG, "Invalid key: " + key, e);
throw new IOException("Invalid key: " + key);
}
Note note = new Note();
note.setId(id);
Uri uri = ContentUris.withAppendedId(Notes.CONTENT_URI, id);
Cursor cursor = resolver.query(uri, null, null, null, null);
try {
if(cursor != null && cursor.moveToFirst()) {
loadNoteFields(cursor, note, id);
} else {
// Item not found
throw new IOException("Cannot find note " + key);
}
} finally {
cursor.close();
}
return note;
}
/**
* Add a note to OINotepad.
*
* @param item Note to be added.
*/
@Override
public String add(Note item) throws IOException {
if (Log.isLoggable(Log.TRACE)) {
Log.trace(TAG_LOG, "Adding Note");
}
ContentValues cv = createNoteContentValues(item);
Uri taskUri = resolver.insert(Notes.CONTENT_URI, cv);
long id = Long.parseLong(taskUri.getLastPathSegment());
if (Log.isLoggable(Log.TRACE)) {
Log.trace(TAG_LOG, "The new note has id: " + id);
}
return "" + id;
}
/**
* Method to update a note in OINotepad.
*/
@Override
public void update(String key, Note newItem) throws IOException {
if (Log.isLoggable(Log.TRACE)) {
Log.trace(TAG_LOG, "Updating note: " + key);
}
long id;
try {
id = Long.parseLong(key);
} catch(Exception e) {
Log.error(TAG_LOG, "Invalid item key " + key, e);
throw new IOException("Invalid item key");
}
// If the contact does not exist, then we perform an add
if (!exists(key)) {
if (Log.isLoggable(Log.INFO)) {
Log.info(TAG_LOG, "Tried to update a non existing note. Creating a new one.");
}
add(newItem);
return;
}
ContentValues cv = createNoteContentValues(newItem);
Uri uri = ContentUris.withAppendedId(Notes.CONTENT_URI, id);
resolver.update(uri, cv, null, null);
}
/**
* Delete a note in OINotepad.
*/
public void delete(String key) throws IOException {
if (Log.isLoggable(Log.TRACE)) {
Log.trace(TAG_LOG, "Deleting note with id: " + key);
}
long itemId;
try {
itemId = Long.parseLong(key);
} catch (Exception e) {
Log.error(TAG_LOG, "Invalid item key " + key, e);
throw new IOException("Invalid item key");
}
Uri uri = ContentUris.withAppendedId(Notes.CONTENT_URI, itemId);
int count = resolver.delete(uri, null, null);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug(TAG_LOG, "Deleted note count: " + count);
}
if (count < 0) {
Log.error(TAG_LOG, "Cannot delete note");
throw new IOException("Cannot delete note");
}
}
/**
* Delete all note in OINotepad.
* @throws IOException if anything went wrong accessing OINotepad's content provider.
*/
public void deleteAll() throws IOException {
if (Log.isLoggable(Log.TRACE)) {
Log.trace(TAG_LOG, "Deleting all notes");
}
Enumeration keys = getAllKeys();
while(keys.hasMoreElements()) {
String key = (String)keys.nextElement();
delete(key);
}
}
/**
* Check if a note with the given id exists in OINotepad.
*
* @param id the id which existence is to be checked
* @return true if the given id exists in the db false otherwise
*/
public boolean exists(String key) {
long id;
try {
id = Long.parseLong(key);
} catch (Exception e) {
Log.error(TAG_LOG, "Invalid item key " + key, e);
return false;
}
Uri uri = ContentUris.withAppendedId(Notes.CONTENT_URI, id);
Cursor cur = resolver.query(uri, null, null, null, null);
if(cur == null) {
return false;
}
boolean found = cur.getCount() > 0;
cur.close();
return found;
}
/**
* Get all of the note keys that exist in OINotepad
* @return Enumeration the enumeration object that contains all the notes.
* @throws IOException if anything went wrong accessing OINotepad's content provider.
*/
public Enumeration getAllKeys() throws IOException {
String cols[] = {Notes._ID};
Cursor cursor = resolver.query(Notes.CONTENT_URI, cols, null, null, null);
try {
int size = cursor.getCount();
Vector<String> itemKeys = new Vector<String>(size);
if (!cursor.moveToFirst()) {
return itemKeys.elements();
}
for (int i = 0; i < size; i++) {
String key = cursor.getString(0);
if (Log.isLoggable(Log.TRACE)) {
Log.trace(TAG_LOG, "Found item with key: " + key);
}
itemKeys.addElement(key);
cursor.moveToNext();
}
return itemKeys.elements();
} catch (Exception e) {
Log.error(TAG_LOG, "Cannot get all items keys: ", e);
throw new IOException("Cannot get all items keys");
} finally {
cursor.close();
}
}
public Vector<com.funambol.syncml.protocol.Property> getSupportedProperties() {
// TODO: FIXME
return null;
}
private void loadNoteFields(Cursor cursor, Note note, long key) {
// Load TITLE
String name = cursor.getString(cursor.getColumnIndex(Notes.TITLE));
if(name != null) {
note.setTitle(new Property(name));
}
// Load NOTE
String noteValue = cursor.getString(cursor.getColumnIndex(Notes.NOTE));
if(noteValue != null) {
note.setBody(new Property(noteValue));
}
}
public Vector commit() {
return null;
}
/**
* Putting title and body of the note into an object of class <tt>ContentValues</tt>,
* so it can be inserted into "OI Notepad" via a Content Resolver.
*/
private ContentValues createNoteContentValues(Note note) throws IOException {
ContentValues cv = new ContentValues();
putStringProperty(Notes.TITLE, note.getTitle(), cv);
putStringProperty(Notes.NOTE, note.getBody(), cv);
return cv;
}
/**
* Put a String property to the given ContentValues.
*
* @param column the culumn to be written
* @param property the property to be written into the column
* @param cv the content values related to the property
*/
private void putStringProperty(String column,
Property property, ContentValues cv) {
if(property != null) {
String value = property.getPropertyValueAsString();
if(value != null) {
value = StringUtil.replaceAll(value, "\r\n", "\n");
value = StringUtil.replaceAll(value, "\r", "\n");
cv.put(column, value);
}
}
}
}