org.hfoss.posit.android.sync.Communicator.java Source code

Java tutorial

Introduction

Here is the source code for org.hfoss.posit.android.sync.Communicator.java

Source

/*
 * File: Communicator.java
 * 
 * Copyright (C) 2009 The Humanitarian FOSS Project (http://www.hfoss.org)
 * 
 * This file is part of POSIT, Portable Open Search and Identification Tool.
 *
 * POSIT is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (LGPL) as published 
 * by the Free Software Foundation; either version 3.0 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 LGPL along with this program; 
 * if not visit http://www.gnu.org/licenses/lgpl.html.
 * 
 */
package org.hfoss.posit.android.sync;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.hfoss.posit.android.Constants;
import org.hfoss.posit.android.api.Find;
import org.hfoss.posit.android.api.FindHistory;
import org.hfoss.posit.android.api.activity.ListProjectsActivity;
import org.hfoss.posit.android.api.authentication.AuthenticatorActivity;
import org.hfoss.posit.android.api.database.DbHelper;
import org.hfoss.posit.android.api.database.DbManager;
import org.hfoss.posit.android.api.plugin.FindPluginManager;
import org.hfoss.posit.android.functionplugin.camera.Camera;
import org.hfoss.posit.android.R;
//import org.hfoss.posit.android.functionplugin.tracker.TrackerActivity;
import org.json.JSONException;
import org.json.JSONObject;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Toast;

/**
 * The communication module for POSIT. Handles most calls to the server to get
 * information regarding projects and finds.
 * 
 * 
 */

//public class Communicator extends OrmLiteBaseActivity<TrackerDbManager> {
public class Communicator {
    private static final String MESSAGE = "message";
    private static final String MESSAGE_CODE = "messageCode";
    private static final String ERROR_MESSAGE = "errorMessage";
    private static final String ERROR_CODE = "errorCode";
    private static final String COLUMN_IMEI = "imei";
    //Start of addition columns for photo table
    private static final String COLUMN_GUID = "guid"; //guid of the find
    private static final String COLUMN_IDENTIFIER = "identifier"; //does not seem to be useful
    private static final String COLUMN_PROJECT_ID = "project_id"; //project id of the find
    private static final String COLUMN_TIMESTAMP = "timestamp"; //if this is not set, it uses the current timestamp
    private static final String COLUMN_MIME_TYPE = "mime_type"; //data type, in this case, "image/jpeg"
    private static final String COLUMN_DATA_FULL = "data_full"; //data for the image, takes Base64 string of image
    private static final String COLUMN_DATA_THUMBNAIL = "data_thumbnail"; //data for the image, take Base 64 string of image
    //End of addition columns for photo table
    public static final int THUMBNAIL_TARGET_SIZE = 320; //width and height of thumbnail data
    public static final int CONNECTION_TIMEOUT = 3000; // millisecs
    public static final int SOCKET_TIMEOUT = 5000;
    public static final String RESULT_FAIL = "false";
    private static String server;
    private static String authKey;
    private static String imei;

    private static int projectId;

    private static final String SERVER_PREF = "serverKey";
    private static final String PROJECT_PREF = "projectKey";

    private static String TAG = "Communicator";
    private static String responseString;
    private Context mContext;
    private SharedPreferences applicationPreferences;
    private HttpParams mHttpParams;
    private static HttpClient mHttpClient;
    private ThreadSafeClientConnManager mConnectionManager;
    public static long mTotalTime = 0;
    private static long mStart = 0;

    //   public void setContext(Context _context) {
    //      mContext = _context;
    //      mTotalTime = 0;
    //      mStart = 0;
    //
    //      mHttpParams = new BasicHttpParams();
    //
    //      // Set the timeout in milliseconds until a connection is established.
    //      HttpConnectionParams.setConnectionTimeout(mHttpParams, CONNECTION_TIMEOUT);
    //      
    //      // Set the default socket timeout (SO_TIMEOUT) 
    //      // in milliseconds which is the timeout for waiting for data.
    //      HttpConnectionParams.setSoTimeout(mHttpParams, SOCKET_TIMEOUT);
    //
    //      SchemeRegistry registry = new SchemeRegistry();
    //      registry.register(new Scheme("http", new PlainSocketFactory(), 80));
    //      mConnectionManager = new ThreadSafeClientConnManager(mHttpParams,
    //            registry);
    //      mHttpClient = new DefaultHttpClient(mConnectionManager, mHttpParams);
    //
    //      PreferenceManager.setDefaultValues(mContext, R.xml.posit_preferences,
    //            false);
    //      applicationPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
    //      setApplicationAttributes(
    //            getAuthKey(mContext),
    //            //applicationPreferences.getString("AUTHKEY", ""), 
    //            applicationPreferences.getString(SERVER_PREF, server), 
    //            applicationPreferences.getInt(PROJECT_PREF, projectId));
    //      TelephonyManager manager = (TelephonyManager) mContext
    //            .getSystemService(Context.TELEPHONY_SERVICE);
    //      imei = manager.getDeviceId();
    //
    //   }
    //   
    //   private void setApplicationAttributes(String aKey, String serverAddress,
    //         int projId) {
    //      authKey = aKey;
    //      server = serverAddress;
    //      projectId = projId;
    //   }

    // /**
    // * Attempts to get the auth token. Apparently this might have to perform a
    // * network request, so you're supposed to use a thread.
    // */
    // public static Thread getAuthToken(final Context context) {
    //
    // final Runnable runnable = new Runnable() {
    // public void run() {
    // AccountManager mAccountManager = AccountManager.get(context);
    //
    // // TODO: again just picking the first account here.. how are you
    // // supposed to handle this?
    // Account[] accounts =
    // mAccountManager.getAccountsByType(SyncAdapter.ACCOUNT_TYPE);
    //
    // try {
    // String authKey = mAccountManager
    // .blockingGetAuthToken(accounts[0],
    // SyncAdapter.AUTHTOKEN_TYPE, true /* notifyAuthFailure */);
    // Log.i(TAG, "AUTH TOKEN: " + authKey);
    // } catch (OperationCanceledException e) {
    // // TODO Auto-generated catch block
    // e.printStackTrace();
    // } catch (AuthenticatorException e) {
    // // TODO Auto-generated catch block
    // e.printStackTrace();
    // } catch (IOException e) {
    // // TODO Auto-generated catch block
    // e.printStackTrace();
    // }
    // }
    // };
    // // run on background thread.
    // return performOnBackgroundThread(runnable);
    // }

    public static boolean isServerReachable(Context context) {
        SharedPreferences applicationPreferences = PreferenceManager.getDefaultSharedPreferences(context);
        String server = applicationPreferences.getString(SERVER_PREF, "");
        String url = server + "/api/isreachable?authKey=" + getAuthKey(context);

        HashMap<String, Object> responseMap = null;
        Log.i(TAG, "is reachable URL=" + url);

        String responseString = null;
        String responseCode = null;
        try {
            responseString = doHTTPGET(url);
            Log.i(TAG, "isreachable response = " + responseString);
            if (responseString.contains("[Error] ")) {
                Log.e(TAG, responseString);
                return false;
            } else {
                ResponseParser parser = new ResponseParser(responseString);
                responseMap = parser.parseObject();
                //responseCode = (String) responseMap.get(MESSAGE_CODE);
            }
        } catch (Exception e) {
            Log.i(TAG, "longinUser catch clause response = " + responseString);
            Toast.makeText(context, e.getMessage() + "", Toast.LENGTH_LONG).show();
            //sendAuthenticationResult(authKey, false, handler, context);
            return false;
        }
        try {
            if (responseMap.containsKey(ERROR_CODE)) {
                return false;
            } else if (responseMap.containsKey(MESSAGE_CODE)) {
                if (responseMap.get(MESSAGE_CODE).equals(Constants.AUTHN_OK)) {
                    return true;
                }
            } else {
                return false;
            }
        } catch (Exception e) {
            Log.e(TAG, "loginUser " + e.getMessage() + " ");
            return false;
        }
        return false;
    }

    /**
     * Removes an account. This should be called when, e.g., the user changes
     * to a new server.
     * @param context
     * @param accountType
     */
    public static void removeAccount(Context context, String accountType) {
        AccountManager am = AccountManager.get(context);
        am.invalidateAuthToken(accountType, SyncAdapter.AUTHTOKEN_TYPE);
        Account[] accounts = am.getAccountsByType(accountType);
        if (accounts.length != 0)
            am.removeAccount(accounts[0], null, null);
        //String authkey = getAuthKey(context);
        //return authkey == null;
    }

    public static String getAuthKey(Context context) {
        AccountManager accountManager = AccountManager.get(context);

        // TODO: again just picking the first account here.. how are you
        // supposed to handle this?
        Account[] accounts = accountManager.getAccountsByType(SyncAdapter.ACCOUNT_TYPE);

        if (accounts.length == 0)
            return null;

        String authKey = null;
        try {
            authKey = accountManager.blockingGetAuthToken(accounts[0], SyncAdapter.AUTHTOKEN_TYPE,
                    true /* notifyAuthFailure */);
        } catch (OperationCanceledException e) {
            Log.e(TAG, "getAuthKey(), cancelled during request: " + e.getMessage());
            e.printStackTrace();
        } catch (AuthenticatorException e) {
            Log.e(TAG, "getAuthKey(), authentication exception: " + e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            Log.e(TAG, "getAuthKey() IOException" + e.getMessage());
            e.printStackTrace();
        } catch (IllegalStateException e) {
            Log.e(TAG, "getAuthKey() IllegalStateException" + e.getMessage());
            e.printStackTrace();
        }
        return authKey;
    }

    /**
     * NOTE: Calls doHTTPGet
     * 
     * Get all open projects from the server. Eventually, the goal is to be able
     * to get different types of projects depending on the privileges of the
     * user.
     * 
     * @return a list of all the projects and their information, encoded as maps
     * @throws JSONException
     */
    public static ArrayList<HashMap<String, Object>> getProjects(Handler handler, Context context) {

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        String server = prefs.getString(SERVER_PREF, "");

        String authKey = getAuthKey(context);

        if (authKey != null) {

            String url = server + "/api/listMyProjects?authKey=" + authKey;

            ArrayList<HashMap<String, Object>> list;
            String responseString = doHTTPGET(url);
            Log.i(TAG, responseString);

            if (responseString.contains("Error")) {
                return null;
            }
            list = new ArrayList<HashMap<String, Object>>();
            try {
                list = (ArrayList<HashMap<String, Object>>) (new ResponseParser(responseString).parseList());
            } catch (JSONException e) {
                Log.i(TAG, "getProjects JSON exception " + e.getMessage());
                return null;
            }
            sendProjectsResult(list, true, handler, context);
            return list;
        } else {
            Log.e(TAG, "authKey is null.");
            return null;
        }
    }

    /**
     * Registers the phone being used with the given server address, the
     * authentication key, and the phone's imei
     * 
     * @param server
     * @param authKey
     * @param imei
     * @return whether the registration was successful
     */
    public String registerDevice(String server, String authKey, String imei) {
        String url = server + "/api/registerDevice?authKey=" + authKey + "&imei=" + imei;
        Log.i(TAG, "registerDevice URL=" + url);

        String responseString = null;
        try {
            responseString = doHTTPGET(url);
        } catch (Exception e) {
            // Toast.makeText(mContext, e.getMessage(),
            // Toast.LENGTH_LONG).show();
        }
        Log.i(TAG, responseString);
        if (responseString.equals(RESULT_FAIL))
            return null;
        else {
            return responseString;
        }
    }

    /**
     * Registers the phone being used with the given server address, email,
     * password and imei.
     * 
     * @param email
     *            email/username
     * @param password
     *            the password
     * @param imei
     * @param handler
     *            the handler instance from the UI thread
     * @param context
     *            the context of the calling activity
     * @return the result
     */
    public static String loginUser(String email, String password, String imei, Handler handler, Context context) {

        SharedPreferences applicationPreferences = PreferenceManager.getDefaultSharedPreferences(context);
        String server = applicationPreferences.getString(SERVER_PREF, "");

        String url = server + "/api/login";

        HashMap<String, Object> responseMap = null;
        Log.i(TAG, "loginUser URL=" + url);

        List<NameValuePair> sendList = new ArrayList<NameValuePair>();
        sendList.add(new BasicNameValuePair("email", email));
        sendList.add(new BasicNameValuePair("password", password));
        sendList.add(new BasicNameValuePair("imei", imei));

        String responseString = null;
        String authKey = null;
        try {
            responseString = doHTTPPost(url, sendList);
            Log.i(TAG, "longinUser response = " + responseString);
            if (responseString.contains("[Error] ")) {
                Log.e(TAG, responseString);
                return null;
            } else {
                ResponseParser parser = new ResponseParser(responseString);
                responseMap = parser.parseObject();
                authKey = (String) responseMap.get(MESSAGE);
            }
        } catch (Exception e) {
            Log.i(TAG, "longinUser catch clause response = " + responseString);
            Toast.makeText(context, e.getMessage() + "", Toast.LENGTH_LONG).show();
            sendAuthenticationResult(authKey, false, handler, context);
            return null;
        }
        try {
            if (responseMap.containsKey(ERROR_CODE)) {
                sendAuthenticationResult(authKey, false, handler, context);
                return null;
            } else if (responseMap.containsKey(MESSAGE_CODE)) {
                if (responseMap.get(MESSAGE_CODE).equals(Constants.AUTHN_OK)) {
                    sendAuthenticationResult(authKey, true, handler, context);
                    return authKey;
                }
            } else {
                sendAuthenticationResult(authKey, false, handler, context);
                return null;
            }
        } catch (Exception e) {
            Log.e(TAG, "loginUser " + e.getMessage() + " ");
            sendAuthenticationResult(authKey, false, handler, context);
            return null;
        }
        sendAuthenticationResult(authKey, false, handler, context);
        return null;
    }

    /**
     * Attempts to authenticate the user credentials on the server.
     * 
     * @param email
     *            The user's username
     * @param password
     *            The user's password to be authenticated
     * @param imei
     *            the phone's IMEI
     * @param handler
     *            The main UI thread's handler instance.
     * @param context
     *            The caller Activity's context
     * @return Thread The thread on which the network mOperations are executed.
     */
    public static Thread attemptAuth(final String email, final String password, final String imei,
            final Handler handler, final Context context) {

        final Runnable runnable = new Runnable() {
            public void run() {
                loginUser(email, password, imei, handler, context);
            }
        };
        // run on background thread.
        return performOnBackgroundThread(runnable);
    }

    /**
     * Attempts to get the user's projects from the server.
     * 
     * @param handler
     *            The main UI thread's handler instance.
     * @param context
     *            The caller Activity's context
     * @return Thread The thread on which the network mOperations are executed.
     */
    public static Thread attemptGetProjects(final Handler handler, final Context context) {

        final Runnable runnable = new Runnable() {
            ArrayList<HashMap<String, Object>> projectList;

            public void run() {
                projectList = getProjects(handler, context);
            }
        };
        // run on background thread.
        return performOnBackgroundThread(runnable);
    }

    // /**
    // * Attempts to get changed finds from the server.
    // *
    // * @param handler
    // * The main UI thread's handler instance.
    // * @param context
    // * The caller Activity's context
    // * @return Thread The thread on which the network mOperations are
    // executed.
    // */
    // public static Thread attemptGetChangedFinds(final Handler handler, final
    // Context context) {
    //
    // final Runnable runnable = new Runnable() {
    // //ArrayList<Integer> finds;
    // String finds;
    // public void run() {
    // finds = getServerFindsNeedingSync(handler, context);
    // }
    // };
    // // run on background thread.
    // return performOnBackgroundThread(runnable);
    // }
    /**
     * Executes the network requests on a separate thread.
     * 
     * @param runnable
     *            The runnable instance containing network mOperations to be
     *            executed.
     */
    public static Thread performOnBackgroundThread(final Runnable runnable) {
        final Thread t = new Thread() {
            @Override
            public void run() {
                try {
                    runnable.run();
                } finally {
                }
            }
        };
        t.start();
        return t;
    }

    // /**
    // * Sends the result of a getChangedFinds request from server back to the
    // caller
    // * main UI thread through its handler.
    // *
    // * @param projects
    // * the list of projects gotten from server
    // * @param result
    // * The boolean holding authentication result
    // * @param authToken
    // * The auth token returned from the server for this account.
    // * @param handler
    // * The main UI thread's handler instance.
    // * @param context
    // * The caller Activity's context.
    // */
    // private static void sendFindsResult(final String finds, final Boolean
    // result,
    // final Handler handler, final Context context) {
    // if (handler == null || context == null) {
    // return;
    // }
    // handler.post(new Runnable() {
    // public void run() {
    // ((ListFindsActivity) context).onGetChangedFindsResult(finds);
    // }
    // });
    // }

    /**
     * Sends the result of a getProjects request from server back to the caller
     * main UI thread through its handler.
     * 
     * @param projects
     *            the list of projects gotten from server
     * @param result
     *            The boolean holding authentication result
     * @param authToken
     *            The auth token returned from the server for this account.
     * @param handler
     *            The main UI thread's handler instance.
     * @param context
     *            The caller Activity's context.
     */
    private static void sendProjectsResult(final ArrayList<HashMap<String, Object>> projects, final Boolean result,
            final Handler handler, final Context context) {
        if (handler == null || context == null) {
            return;
        }
        handler.post(new Runnable() {
            public void run() {
                ((ListProjectsActivity) context).onShowProjectsResult(projects, result);
            }
        });
    }

    /**
     * Sends the authentication response from server back to the caller main UI
     * thread through its handler.
     * 
     * @param authKey
     *            the auth key obtained from the server
     * @param result
     *            The boolean holding authentication result
     * @param authToken
     *            The auth token returned from the server for this account.
     * @param handler
     *            The main UI thread's handler instance.
     * @param context
     *            The caller Activity's context.
     */
    private static void sendAuthenticationResult(final String authKey, final Boolean result, final Handler handler,
            final Context context) {
        if (handler == null || context == null) {
            return;
        }
        handler.post(new Runnable() {
            public void run() {
                ((AuthenticatorActivity) context).onAuthenticationResult(result, authKey);
            }
        });
    }

    public String createProject(Context context, String server, String projectName, String projectDescription,
            String authKey) {
        String url = server + "/api/newProject?authKey=" + authKey;

        List<NameValuePair> nvp = new ArrayList<NameValuePair>();

        nvp.add(new BasicNameValuePair("name", projectName));
        nvp.add(new BasicNameValuePair("description", projectDescription));

        HashMap<String, Object> responseMap = null;
        Log.i(TAG, "Create Project URL=" + url);

        String responseString = null;

        try {
            responseString = doHTTPPost(url, nvp);
            Log.i(TAG, responseString);
            if (responseString.contains("[ERROR]")) {
                Toast.makeText(context, responseString, Toast.LENGTH_LONG).show();
                return Constants.AUTHN_FAILED + ":" + "Error";
            }
            ResponseParser parser = new ResponseParser(responseString);
            responseMap = parser.parseObject();
        } catch (Exception e) {
            Toast.makeText(context, e.getMessage() + "", Toast.LENGTH_LONG).show();
        }
        try {
            if (responseMap.containsKey(ERROR_CODE))
                return responseMap.get(ERROR_CODE) + ":" + responseMap.get(ERROR_MESSAGE);
            else if (responseMap.containsKey(MESSAGE_CODE)) {
                return (String) responseMap.get(MESSAGE);

            } else {
                return "Malformed message from server.";
            }
        } catch (Exception e) {
            Log.e(TAG, "createProject " + e.getMessage());
            return e.getMessage();
        }
    }

    //
    // public String registerUser(String server, String firstname,
    // String lastname, String email, String password, String check,
    // String imei) {
    // String url = server + "/api/registerUser";
    // Log.i(TAG, "registerUser URL=" + url + "&imei=" + imei);
    // HashMap<String, String> sendMap = new HashMap<String, String>();
    // sendMap.put("email", email);
    // sendMap.put("password1", password);
    // sendMap.put("password2", check);
    // sendMap.put("firstname", firstname);
    // sendMap.put("lastname", lastname);
    // try {
    // responseString = doHTTPPost(url, sendMap);
    // Log.i(TAG, "registerUser Httpost responseString = "
    // + responseString);
    // if (responseString.contains("[ERROR]")) {
    // Toast.makeText(mContext,
    // Constants.AUTHN_FAILED + ":" + responseString,
    // Toast.LENGTH_LONG).show();
    // return Constants.AUTHN_FAILED + ":" + responseString;
    // }
    // ResponseParser parser = new ResponseParser(responseString);
    // HashMap<String, Object> responseMap = parser.parseObject();
    // if (responseMap.containsKey(ERROR_CODE))
    // return responseMap.get(ERROR_CODE) + ":"
    // + responseMap.get(ERROR_MESSAGE);
    // else if (responseMap.containsKey(MESSAGE_CODE)) {
    // if (responseMap.get(MESSAGE_CODE).equals(Constants.AUTHN_OK)) {
    // return Constants.AUTHN_OK + ":" + responseMap.get(MESSAGE);
    // }
    // } else {
    // return Constants.AUTHN_FAILED + ":"
    // + "Malformed message from the server.";
    // }
    // } catch (Exception e) {
    // Log.e(TAG, "registerUser " + e.getMessage() + " ");
    // return Constants.AUTHN_FAILED + ":" + e.getMessage();
    // }
    // return null;
    // }

    /**
     * Returns a list of guIds for server finds that need syncing.
     * 
     * @return
     */
    public static String getServerFindsNeedingSync(Context context, String authKey) {
        String response = "";
        String url = "";

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        String server = prefs.getString(SERVER_PREF, "");
        int projectId = prefs.getInt(PROJECT_PREF, 0);

        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        String imei = telephonyManager.getDeviceId();

        url = server + "/api/getDeltaFindsIds?authKey=" + authKey + "&imei=" + imei + "&projectId=" + projectId;
        Log.i(TAG, "getDeltaFindsIds URL=" + url);

        try {
            response = doHTTPGET(url);
        } catch (Exception e) {
            Log.i(TAG, e.getMessage());
        }
        Log.i(TAG, "serverFindsNeedingSync = " + response);

        return response;
    }

    /**
     * 
     * Retrieve finds from the server using a Communicator.
     * 
     * @param serverGuids
     * @return
     */
    public static boolean getFindsFromServer(Context context, String authKey, String serverGuids) {
        String guid;
        int rows = 0;
        StringTokenizer st = new StringTokenizer(serverGuids, ",");

        while (st.hasMoreElements()) {
            guid = st.nextElement().toString();
            ContentValues cv = getRemoteFindById(context, authKey, guid);

            if (cv == null) {
                return false; // Shouldn't be null--we know its ID
            } else {
                Log.i(TAG, cv.toString());
                try {

                    // Find out what Find class POSIT is configured for
                    Class<? extends Find> findClass = FindPluginManager.mFindPlugin.getmFindClass();

                    Log.i(TAG, "Find class = " + findClass.getSimpleName());

                    // Update the DB
                    Find find = DbHelper.getDbManager(context).getFindByGuid(guid);
                    if (find != null) {
                        Log.i(TAG, "Updating existing find: " + find.getId());
                        Find updatedFind = findClass.newInstance();
                        updatedFind.updateObject(cv);

                        //                  Find updatedFind = (OutsideInFind)find;
                        //                  ((OutsideInFind) updatedFind).updateObject(cv);
                        updatedFind.setId(find.getId());
                        rows = DbHelper.getDbManager(context).updateWithoutHistory(updatedFind);
                    } else {
                        //   find = new OutsideInFind();
                        find = findClass.newInstance();
                        Log.i(TAG, "Inserting new find: " + find.getId());
                        find.updateObject(cv);
                        //                  ((OutsideInFind) find).updateObject(cv);
                        Log.i(TAG, "Adding a new find " + find);
                        rows = DbHelper.getDbManager(context).insertWithoutHistory(find);
                    }
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        DbHelper.releaseDbManager();
        return rows > 0;
    }

    public static boolean recordSync(Context context, String authKey) {
        // Record the synchronization in the server's sync_history table

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        String server = prefs.getString(SERVER_PREF, "");
        int projectId = prefs.getInt(context.getString(R.string.projectPref), 0);

        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        String imei = telephonyManager.getDeviceId();

        String url = server + "/api/recordSync?authKey=" + authKey + "&imei=" + imei + "&projectId=" + projectId;
        Log.i(TAG, "recordSync URL=" + url);
        String responseString = "";

        try {
            responseString = doHTTPGET(url);
        } catch (Exception e) {
            Log.i(TAG, e.getMessage());
            e.printStackTrace();
            return false;
        }
        Log.i(TAG, "HTTPGet recordSync response = " + responseString);
        return true;
    }

    /*
     * TODO: This method is a little long and could be split up. Send one find
     * to the server, including its images.
     * 
     * @param find a reference to the Find object
     * 
     * @param action -- either 'create' or 'update'
     */
    public static boolean sendFind(Find find, Context context, String authToken) {
        String url = "";
        boolean success = false;

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        String server = prefs.getString(SERVER_PREF, "");

        if (find.getAction().equals(FindHistory.ACTION_CREATE))
            url = server + "/api/createFind?authKey=" + authToken;
        else if (find.getAction().equals(FindHistory.ACTION_UPDATE))
            url = server + "/api/updateFind?authKey=" + authToken;
        else {
            Log.e(TAG, "Find object does not contain an appropriate action: " + find);
            return false;
        }

        Log.i(TAG, "SendFind=" + find);

        List<NameValuePair> pairs = getNameValuePairs(find);

        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        String imei = telephonyManager.getDeviceId();

        BasicNameValuePair pair = new BasicNameValuePair("imei", imei);
        pairs.add(pair);
        Log.i(TAG, "pairs: " + pairs);
        String responseString = null;

        // Send the find
        try {
            responseString = doHTTPPost(url, pairs);
            DbHelper.getDbManager(context).updateStatus(find, Constants.TRANSACTING);
            DbHelper.getDbManager(context).updateSyncOperation(find, Constants.POSTING);
        } catch (Exception e) {
            Log.i(TAG, e.getMessage());
            Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
            DbHelper.getDbManager(context).updateStatus(find, Constants.FAILED);
            success = false;
        }

        Log.i(TAG, "sendFind.ResponseString: " + responseString);

        // If the update failed return false
        if (responseString.indexOf("True") == -1) {
            Log.i(TAG, "sendFind result doesn't contain 'True'");
            DbHelper.getDbManager(context).updateStatus(find, Constants.FAILED);
            success = false;
        } else {
            DbHelper.getDbManager(context).updateStatus(find, Constants.SUCCEEDED);
            Log.i(TAG, "sendFind() synced find id: " + find.getId());
            success = true;
        }

        if (!success) {
            //don't bother sending the images if we can't save the find
            return false;
        }

        //Check if the image is out of sync and needs to be sent
        if (Camera.isPhotoSynced(find, context) == false) {
            //We have an image to send!
            //Get the image string
            String fullPicStr = Camera.getPhotoAsString(find.getGuid(), context);
            //Get the thumbnail version of it too
            String thumbPicStr = Camera.getPhotoThumbAsString(find.getGuid(), context);
            //fill in the data needed to send to the photo table
            HashMap<String, String> sendMap = new HashMap<String, String>();
            sendMap.put(COLUMN_IMEI, imei);
            sendMap.put(COLUMN_GUID, find.getGuid());
            sendMap.put(COLUMN_IDENTIFIER, Integer.toString(find.getId()));
            sendMap.put(COLUMN_PROJECT_ID, Integer.toString(find.getProject_id()));
            //          sendMap.put("COLUMN_TIMESTAMP",find.getTime()); //uses current timestamp if not set
            //          sendMap.put("mine_type",imageData.getAsString(PositDbHelper.PHOTOS_MIME_TYPE));      
            sendMap.put(COLUMN_MIME_TYPE, "image/jpeg");
            sendMap.put(COLUMN_DATA_FULL, fullPicStr);
            sendMap.put(COLUMN_DATA_THUMBNAIL, thumbPicStr);

            //ready to send the image to the server
            sendMedia(sendMap, context);
        }

        DbHelper.releaseDbManager();
        return success;
    }

    /**
     * Sends finds to the server. Uses a Communicator.
     * 
     * @param phoneGuids
     * @return
     */
    public static boolean sendFindsToServer(List<Find> finds, Context context, String authToken) {
        boolean success = false;
        // StringTokenizer st = new StringTokenizer(phoneGuids, ",");
        // String str, guid, action;
        // while (st.hasMoreElements()) {
        // str = st.nextElement().toString();
        // int indx = str.indexOf(':');
        // guid = str.substring(0, indx);
        // action = str.substring(indx + 1);

        // Find find = new Find(mContext, guid); // Create a Find object
        for (Find find : finds) {
            try {
                if (find.getAction().equals("delete"))
                    Log.i(TAG, "Ignoring deletions");
                else {
                    Log.i(TAG, "sending Find=" + find);
                    success = sendFind(find, context, authToken);
                }
            } catch (Exception e) {
                Log.i(TAG, e.toString());
                e.printStackTrace();
                success = false;
                // mHandler.sendEmptyMessage(NETWORKERROR);
            }
            if (!success) {
                // mHandler.sendEmptyMessage(SYNCERROR);
            }
        }

        return success;
    }

    // /**
    // * Sends an image (or sound file or video) to the server.
    // *
    // * @param identifier
    // * @param findId
    // * the guid of the associated find
    // * @param data
    // * @param mimeType
    // */
    public static void sendMedia(HashMap<String, String> sendMap, Context context) {
        Log.i(TAG, "sendMedia, sendMap= " + sendMap);

        SharedPreferences applicationPreferences = PreferenceManager.getDefaultSharedPreferences(context);
        String server = applicationPreferences.getString(SERVER_PREF, "");

        String url = server + "/api/attachPicture?authKey=" + getAuthKey(context);

        responseString = doHTTPPost(url, sendMap);
        //      if (Utils.debug)
        //         Log.i(TAG, "sendImage.ResponseString: " + responseString);
    }
    //
    // /**
    // * Converts a uri to a base64 encoded String for transmission to server.
    // *
    // * @param uri
    // * @return
    // */
    // private String convertUriToBase64(Uri uri) {
    // ByteArrayOutputStream imageByteStream = new ByteArrayOutputStream();
    // byte[] imageByteArray = null;
    // Bitmap bitmap = null;
    //
    // try {
    // bitmap = android.provider.MediaStore.Images.Media.getBitmap(
    // mContext.getContentResolver(), uri);
    // } catch (FileNotFoundException e) {
    // e.printStackTrace();
    // } catch (IOException e) {
    // e.printStackTrace();
    // }
    //
    // if (bitmap == null) {
    // Log.d(TAG, "No bitmap");
    // }
    // // Compress bmp to jpg, write to the byte output stream
    // bitmap.compress(Bitmap.CompressFormat.JPEG, 80, imageByteStream);
    // // Turn the byte stream into a byte array
    // imageByteArray = imageByteStream.toByteArray();
    // char[] base64 = Base64Coder.encode(imageByteArray);
    // String base64String = new String(base64);
    // return base64String;
    // }
    //
    // /**
    // * cleanup the item key,value pairs so that we can send the data.
    // *
    // * @param sendMap
    // */
    // private void cleanupOnSend(HashMap<String, String> sendMap) {
    // addRemoteIdentificationInfo(sendMap);
    // }
    //
    // /**
    // * Add the standard values to our request. We might as well use this as
    // * initializer for our requests.
    // *
    // * @param sendMap
    // */
    // private void addRemoteIdentificationInfo(HashMap<String, String> sendMap)
    // {
    // // sendMap.put(COLUMN_APP_KEY, appKey);
    // sendMap.put(COLUMN_IMEI, Utils.getIMEI(mContext));
    // }
    //
    // /**
    // * cleanup the item key,value pairs so that we can receive and save to the
    // * internal database
    // *
    // * @param rMap
    // */
    // public static void cleanupOnReceive(HashMap<String, Object> rMap) {
    // rMap.put(PositDbHelper.FINDS_SYNCED, PositDbHelper.FIND_IS_SYNCED);
    // rMap.put(PositDbHelper.FINDS_GUID, rMap.get("guid"));
    // // rMap.put(PositDbHelper.FINDS_GUID, rMap.get("guid"));
    //
    // rMap.put(PositDbHelper.FINDS_PROJECT_ID, projectId);
    // if (rMap.containsKey("add_time")) {
    // rMap.put(PositDbHelper.FINDS_TIME, rMap.get("add_time"));
    // rMap.remove("add_time");
    // }
    // if (rMap.containsKey("images")) {
    // if (Utils.debug)
    // Log.d(TAG, "contains image key");
    // rMap.put(PositDbHelper.PHOTOS_IMAGE_URI, rMap.get("images"));
    // rMap.remove("images");
    // }
    // }

    /**
     * Sends a HttpPost request to the given URL. Any JSON
     * 
     * @param Uri, the URL to send to/receive from
     * @param pairs, a list of attribute/value pairs
     * @return the response from the URL
     */
    public static String doHTTPPost(String Uri, List<NameValuePair> pairs) {
        BasicHttpParams mHttpParams = new BasicHttpParams();

        // Set the timeout in milliseconds until a connection is established.
        HttpConnectionParams.setConnectionTimeout(mHttpParams, CONNECTION_TIMEOUT);

        // Set the default socket timeout (SO_TIMEOUT)
        // in milliseconds which is the timeout for waiting for data.
        HttpConnectionParams.setSoTimeout(mHttpParams, SOCKET_TIMEOUT);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", new PlainSocketFactory(), 80));
        ThreadSafeClientConnManager mConnectionManager = new ThreadSafeClientConnManager(mHttpParams, registry);
        DefaultHttpClient mHttpClient = new DefaultHttpClient(mConnectionManager, mHttpParams);

        if (Uri == null)
            throw new NullPointerException("The URL has to be passed");
        String responseString = null;
        HttpPost post = new HttpPost();

        Log.i(TAG, "doHTTPPost() URI = " + Uri);
        try {
            post.setURI(new URI(Uri));
        } catch (URISyntaxException e) {
            Log.e(TAG, "URISyntaxException " + e.getMessage());
            e.printStackTrace();
            return "[Error] " + e.getMessage();
        }

        ResponseHandler<String> responseHandler = new BasicResponseHandler();
        try {
            post.setEntity(new UrlEncodedFormEntity(pairs, HTTP.UTF_8));
        } catch (UnsupportedEncodingException e) {
            Log.e(TAG, "UnsupportedEncodingException " + e.getMessage());
            return "[Error] " + e.getMessage();
        }

        try {
            responseString = mHttpClient.execute(post, responseHandler);
            Log.d(TAG, "doHTTPpost responseString = " + responseString);
        } catch (ClientProtocolException e) {
            Log.e(TAG, "ClientProtocolException" + e.getMessage());
            e.printStackTrace();
            return "[Error] " + e.getMessage();
        } catch (IOException e) {
            Log.e(TAG, "IOException " + e.getMessage());
            e.printStackTrace();
            return "[Error] " + e.getMessage();
        } catch (IllegalStateException e) {
            Log.e(TAG, "IllegalStateException: " + e.getMessage());
            e.printStackTrace();
            return "[Error] " + e.getMessage();
        } catch (Exception e) {
            Log.e(TAG, "Exception on HttpPost " + e.getMessage());
            e.printStackTrace();
            return "[Error] " + e.getMessage();
        }

        Log.i(TAG, "doHTTPpost response = " + responseString);

        return responseString;
    }

    /**
     * Sends a HttpPost request to the given URL. Any JSON
     * 
     * @param Uri, the URL to send to/receive from
     * @param sendMap, the hashMap of data to send to the server as POST data
     * @return the response from the URL
     */
    public static String doHTTPPost(String Uri, HashMap<String, String> sendMap) {
        return doHTTPPost(Uri, getNameValuePairs(sendMap));
    }

    // public boolean projectExists(String projectId, String server){
    // String url =
    // server+"/api/projectExists?authKey="+authKey+"&projectId="+projectId;
    // Log.i(TAG, url);
    // String response = doHTTPGET(url);
    // Log.i(TAG, "projectExists response = " + response);
    //
    // if(response.equals("true"))
    // return true;
    // if(response.equals("false"))
    // return false;
    // return false;
    // }
    /**
     * A wrapper(does some cleanup too) for sending HTTP GET requests to the URI
     * 
     * @param Uri
     * @return the request from the remote server
     */
    public static String doHTTPGET(String Uri) {
        BasicHttpParams mHttpParams = new BasicHttpParams();

        // Set the timeout in milliseconds until a connection is established.
        HttpConnectionParams.setConnectionTimeout(mHttpParams, CONNECTION_TIMEOUT);

        // Set the default socket timeout (SO_TIMEOUT)
        // in milliseconds which is the timeout for waiting for data.
        HttpConnectionParams.setSoTimeout(mHttpParams, SOCKET_TIMEOUT);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", new PlainSocketFactory(), 80));
        ThreadSafeClientConnManager mConnectionManager = new ThreadSafeClientConnManager(mHttpParams, registry);
        DefaultHttpClient mHttpClient = new DefaultHttpClient(mConnectionManager, mHttpParams);

        if (Uri == null)
            throw new NullPointerException("The URL has to be passed");
        String responseString = null;
        HttpGet httpGet = new HttpGet();

        try {
            httpGet.setURI(new URI(Uri));
        } catch (URISyntaxException e) {
            Log.e(TAG, "doHTTPGet " + e.getMessage());
            e.printStackTrace();
            return "[Error] " + e.getMessage();
        }

        Log.i(TAG, "doHTTPGet Uri = " + Uri);

        ResponseHandler<String> responseHandler = new BasicResponseHandler();

        try {
            responseString = mHttpClient.execute(httpGet, responseHandler);
        } catch (ClientProtocolException e) {
            Log.e(TAG, "ClientProtocolException" + e.getMessage());
            return "[Error] " + e.getMessage();
        } catch (SocketTimeoutException e) {
            Log.e(TAG, "[Error: SocketTimeoutException]" + e.getMessage());
            return "[Error] " + e.getMessage();
        } catch (IOException e) {
            Log.e(TAG, e.getMessage());
            return "[Error] " + e.getMessage();
        } catch (Exception e) {
            Log.e(TAG, e.getMessage() + "what");
            return "[Error] " + e.getMessage();
        }

        Log.i(TAG, "doHTTPGet Response: " + responseString);
        return responseString;
    }

    public static List<NameValuePair> getNameValuePairs(Find find) {
        // Get fields from both class and superclass
        List<NameValuePair> pairs = null;
        if (find.getClass().getName().equals(Find.class.getName())) { // For basic POSIT
            pairs = getNameValuePairs(find, find.getClass());
        } else { // For find extensions
            String extendedDataPairs = getNameValuePairs(find, find.getClass()).toString();
            pairs = getNameValuePairs(find, find.getClass().getSuperclass());
            pairs.add(new BasicNameValuePair("data", extendedDataPairs));
        }
        return pairs;
    }

    /**
     * Returns a list on name/value pairs for the Find.  Should work for Plugin Finds as
     * well as Basic Finds.  
     * @param find
     * @param clazz
     * @return
     */
    private static List<NameValuePair> getNameValuePairs(Find find, Class clazz) {
        Field[] fields = clazz.getDeclaredFields();

        List<NameValuePair> nvp = new ArrayList<NameValuePair>();
        String methodName = "";
        String value = "";

        for (Field field : fields) {
            //         Log.i(TAG, "class= " + clazz + " field = " + field);
            if (!Modifier.isFinal(field.getModifiers())) {
                String key = field.getName();
                methodName = "get" + key.substring(0, 1).toUpperCase() + key.substring(1);
                value = "";

                try {
                    Class returnType = clazz.getDeclaredMethod(methodName, null).getReturnType();
                    if (returnType.equals(String.class))
                        value = (String) clazz.getDeclaredMethod(methodName, null).invoke(find, (Object[]) null);
                    else if (returnType.equals(int.class))
                        value = String.valueOf(
                                (Integer) clazz.getDeclaredMethod(methodName, null).invoke(find, (Object[]) null));
                    else if (returnType.equals(double.class))
                        value = String.valueOf(
                                (Double) clazz.getDeclaredMethod(methodName, null).invoke(find, (Object[]) null));
                    else if (returnType.equals(boolean.class))
                        value = String.valueOf(
                                (Boolean) clazz.getDeclaredMethod(methodName, null).invoke(find, (Object[]) null));

                } catch (IllegalArgumentException e) {
                    Log.e(TAG, e + ": " + e.getMessage());
                } catch (SecurityException e) {
                    Log.e(TAG, e + ": " + e.getMessage());
                } catch (IllegalAccessException e) {
                    Log.e(TAG, e + ": " + e.getMessage());
                } catch (InvocationTargetException e) {
                    Log.e(TAG, e + ": " + e.getMessage());
                } catch (NoSuchMethodException e) {
                    Log.e(TAG, e + ": " + e.getMessage());
                }
                nvp.add(new BasicNameValuePair(key, value));
            }
        }
        return nvp;

    }

    /**
     * Pull the remote find from the server using the guid provided.
     * 
     * @param guid
     *            , a globally unique identifier
     * @return an associative list of attribute/value pairs
     */
    public static ContentValues getRemoteFindById(Context context, String authKey, String guid) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        String server = prefs.getString(SERVER_PREF, "");

        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        String imei = telephonyManager.getDeviceId();

        String url = server + "/api/getFind?guid=" + guid + "&authKey=" + authKey;
        List<NameValuePair> pairs = new ArrayList<NameValuePair>();
        pairs.add(new BasicNameValuePair("guid", guid));
        pairs.add(new BasicNameValuePair("imei", imei));

        String responseString = doHTTPPost(url, pairs);
        ContentValues cv = new ContentValues();

        Log.i(TAG, "getRemoteFindById = " + responseString);
        try {
            JSONObject jobj = new JSONObject(responseString);
            String findJson = jobj.getString("find");
            JSONObject find = new JSONObject(findJson);
            cv.put(Find.GUID, find.getString(Find.GUID));
            cv.put(Find.PROJECT_ID, find.getInt(Find.PROJECT_ID));
            cv.put(Find.NAME, find.getString(Find.NAME));
            cv.put(Find.DESCRIPTION, find.getString(Find.DESCRIPTION));
            // FIXME add add_time and modify_time for this
            cv.put(Find.TIME, find.getString("add_time"));
            cv.put(Find.TIME, find.getString("modify_time"));
            cv.put(Find.LATITUDE, find.getDouble(Find.LATITUDE));
            cv.put(Find.LONGITUDE, find.getDouble(Find.LONGITUDE));
            cv.put(Find.REVISION, find.getInt(Find.REVISION));

            // Does this find have an image?
            if (jobj.has("images")) {
                String imageIds = jobj.getString("images");
                Log.i(TAG, "imageIds = " + imageIds);

                //check to see if we actually have an image to fetch
                String imageId = parseImageIds(imageIds); //this returns at most one image id

                //we have an image to fetch!
                if (imageId != null) {
                    if (getImageOnServer(imageId, context)) {
                        //success
                        Log.i(TAG, "Successfully retrieve image for " + find.getString(Find.GUID));
                    } else {
                        //failed
                        Log.i(TAG, "Failed to retrieve image for " + find.getString(Find.GUID));
                    }
                }
            }

            // Is this a extended find?
            if (jobj.has(Find.EXTENSION)) {
                String extradata = jobj.getString(Find.EXTENSION);
                Log.i(TAG, "extradata = " + extradata);
                if (!extradata.equals("null"))
                    addExtraDataToContentValues(cv, extradata);
            }

            return cv;
        } catch (JSONException e) {
            Log.i(TAG, "JSONException " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            Log.i(TAG, "Exception " + e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    /**
     * The data has the form: ["1","2", ...] or '[]'
     * @param data
     * the list of image ids
     * @return the last image id in the list or null
     */
    private static String parseImageIds(String data) {
        Log.i(TAG, "imageIdData = " + data + " " + data.length());
        if (data.equals("[]")) {
            return null;
        }
        data = data.trim();
        data = data.substring(1, data.length() - 1); //removes brackets
        StringTokenizer st = new StringTokenizer(data, ","); //in the form "123"
        String imgId = null; //only care about one image for this version of posit
        while (st.hasMoreElements()) {
            imgId = (String) st.nextElement();
            Log.i(TAG, "Is this with quotes: " + imgId);
            imgId = imgId.substring(1, imgId.indexOf('"', 1)); // removes quotes. find the second quote in the string
            Log.i(TAG, "Is this without quotes: " + imgId);
        }
        Log.i(TAG, "Planning to fetch imageId " + imgId + " for a find");
        return imgId;
    }

    /**
    * Retrieve the specified image id from the server and save it to the phone
    * @param imageId
    * the id of the image to query
    * @param context
    * the application context
    * @return true if successful, false otherwise
    */
    static boolean getImageOnServer(String imageId, Context context) throws FileNotFoundException, IOException {
        SharedPreferences applicationPreferences = PreferenceManager.getDefaultSharedPreferences(context);
        String server = applicationPreferences.getString(SERVER_PREF, "");

        String imageUrl = server + "/api/getPicture?id=" + imageId + "&authKey=" + getAuthKey(context);

        HashMap<String, String> sendMap = new HashMap<String, String>();
        sendMap.put(COLUMN_IMEI, getIMEI(context));

        String imageResponseString = doHTTPPost(imageUrl, sendMap);
        if (imageResponseString.equals(RESULT_FAIL)) {
            return false;
        } else {
            //we got the image data!
            Log.i(TAG, "imageResponseString = " + imageResponseString);
            //parse to get the guid and base64 string of the image
            //then just save it to the phone's internal memory
            try {
                JSONObject jobj = new JSONObject(imageResponseString);
                String guid = jobj.getString(Find.GUID);
                String imgData = jobj.getString("data_full");

                //save the Base64 string to internal memory
                Camera.savePhoto(guid, imgData, context);
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return true; //success!
        }
    }

    /**
     * The data has the form: [attr=value, ...] or 'null'
     * @param cv
     * @param data
     */
    static private void addExtraDataToContentValues(ContentValues cv, String data) {
        Log.i(TAG, "data = " + data + " " + data.length());
        if (data.equals("null"))
            return;
        data = data.trim();
        data = data.substring(1, data.length() - 1);
        StringTokenizer st = new StringTokenizer(data, ",");
        while (st.hasMoreElements()) {
            String attrvalpair = (String) st.nextElement();
            String attr = attrvalpair.substring(0, attrvalpair.indexOf("="));
            attr = attr.trim();
            String val = attrvalpair.substring(attrvalpair.indexOf("=") + 1);
            val = val.trim();
            Log.i(TAG, "Putting " + attr + "=" + val + " into CV");
            if (Integer.getInteger(val) != null)
                cv.put(attr, Integer.parseInt(val));
            //         else if (Boolean.getBoolean(val) != null)
            //            cv.put(attr, Boolean.parseBoolean(val))
            //         else if (Double.getDouble(val) != null))
            //            cv.put(attr, Double.parseDouble(val))
            else
                cv.put(attr, val);
        }

    }

    /**
     * Registers a new expedition with the server.
     * @param projectId  Posit's current project id.
     * @return Returns the expedition number received from the server or -1 if something
     * goes wrong.
     */
    public int registerExpeditionId(Context context, int projectId) {
        mContext = context;

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        String server = prefs.getString(SERVER_PREF, "");

        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        String imei = telephonyManager.getDeviceId();

        HashMap<String, String> sendMap = new HashMap<String, String>();
        addRemoteIdentificationInfo(sendMap);
        String addExpeditionUrl = server + "/api/addExpedition?authKey=" + getAuthKey(context);
        sendMap.put("projectId", "" + projectId);
        Log.i(TAG, "URL=" + addExpeditionUrl + " projectId = " + projectId);
        String response = doHTTPPost(addExpeditionUrl, sendMap);
        Log.d(TAG, "registerExpeditionId response = " + response);

        // The server should return an expedition number if everything goes ok.  If 
        //  an error occurs, it will return an error message that cannot parse to an int
        //  which will cause an exception here.
        try {
            Integer i = Integer.parseInt(response);
            return i;
        } catch (NumberFormatException e) {
            Log.e(TAG, "Communicator, registerExpeditionId, Invalid response received");
            return -1;
        }
    }

    private void addRemoteIdentificationInfo(HashMap<String, String> sendMap) {
        // sendMap.put(COLUMN_APP_KEY, appKey);
        sendMap.put(COLUMN_IMEI, getIMEI(mContext));
    }

    /**
     * Gets the unique IMEI code for the phone used for identification
     * The phone should have proper permissions (READ_PHONE_STATE) to be able to get this data.
     */
    public static String getIMEI(Context mContext) {
        TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
        return tm.getDeviceId();
    }

    public static List<NameValuePair> getNameValuePairs(HashMap<String, String> nameValuesMap) {
        Iterator<String> iter = nameValuesMap.keySet().iterator();
        List<NameValuePair> nvp = new ArrayList<NameValuePair>();
        while (iter.hasNext()) {
            String key = iter.next();
            String value = nameValuesMap.get(key);
            nvp.add(new BasicNameValuePair(key, value));
        }
        return nvp;
    }

    /**
     * Sends a GPS point and associated data to the Posit server. Called from 
     *  Tracker Activity or TrackerBackgroundService.  
     */
    public String registerExpeditionPoint(Context context, double lat, double lng, double alt, int swath,
            int expedition, long time) {

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        String server = prefs.getString(SERVER_PREF, "");

        //long swath, int expedition) {
        Log.i(TAG, "Communicator, registerExpeditionPoint " + lat + " " + lng + " " + time);
        HashMap<String, String> sendMap = new HashMap<String, String>();
        addRemoteIdentificationInfo(sendMap);
        Log.i(TAG, "Sendmap= " + sendMap.toString());
        String addExpeditionUrl = server + "/api/addExpeditionPoint?authKey=" + this.getAuthKey(context);
        sendMap.put(DbManager.GPS_POINT_LATITUDE, "" + lat);
        sendMap.put(DbManager.GPS_POINT_LONGITUDE, lng + "");
        sendMap.put(DbManager.GPS_POINT_ALTITUDE, "" + alt);
        sendMap.put(DbManager.GPS_POINT_SWATH, "" + swath);
        sendMap.put(DbManager.EXPEDITION, expedition + "");
        sendMap.put(DbManager.GPS_TIME, time + "");
        Log.i(TAG, "Sendmap= " + sendMap.toString());

        String response = doHTTPPost(addExpeditionUrl, sendMap);
        Log.i(TAG, "Communicator, registerExpeditionPoint, response: " + response);
        return response;
    }

    // /**
    // * Get an image from the server using the guid as Key.
    // *
    // * @param guid
    // * the Find's globally unique Id
    // */
    // public ArrayList<HashMap<String, String>> getRemoteFindImages(String
    // guid) {
    // ArrayList<HashMap<String, String>> imagesMap = null;
    // // ArrayList<HashMap<String, String>> imagesMap = null;
    // String imageUrl = server + "/api/getPicturesByFind?findId=" + guid
    // + "&authKey=" + authKey;
    // HashMap<String, String> sendMap = new HashMap<String, String>();
    // Log.i(TAG, "getRemoteFindImages, sendMap=" + sendMap.toString());
    // sendMap.put(PositDbHelper.FINDS_GUID, guid);
    // addRemoteIdentificationInfo(sendMap);
    // try {
    // String imageResponseString = doHTTPPost(imageUrl, sendMap);
    // Log.i(TAG, "getRemoteFindImages, response=" + imageResponseString);
    //
    // if (!imageResponseString.equals(RESULT_FAIL)) {
    // JSONArray jsonArr = new JSONArray(imageResponseString);
    // imagesMap = new ArrayList<HashMap<String, String>>();
    // // imagesMap = new ArrayList<HashMap<String, String>>();
    //
    // for (int i = 0; i < jsonArr.length(); i++) {
    // JSONObject jsonObj = jsonArr.getJSONObject(i);
    // if (Utils.debug)
    // Log.i(TAG, "JSON Image Response String: "
    // + jsonObj.toString());
    // // imagesMap.add((HashMap<String, String>) jsonArr.get(i));
    // Iterator<String> iterKeys = jsonObj.keys();
    // HashMap<String, String> map = new HashMap<String, String>();
    // while (iterKeys.hasNext()) {
    // String key = iterKeys.next();
    // map.put(key, jsonObj.getString(key));
    // }
    // imagesMap.add(map);
    // }
    // }
    // } catch (Exception e) {
    // Log.i(TAG, e.getMessage());
    // e.printStackTrace();
    // }
    // if (imagesMap != null && Utils.debug)
    // Log
    // .i(TAG, "getRemoteFindImages, imagesMap="
    // + imagesMap.toString());
    // else
    // Log.i(TAG, "getRemoteFindImages, imagesMap= null");
    // return imagesMap;
    // }
    //
    // /**
    // * Checks if a given image already exists on the server. Allows for
    // quicker
    // * syncing to the server, as this allows the application to bypass
    // * converting from a bitmap to base64 to send to the server
    // *
    // * @param imageId
    // * the id of the image to query
    // * @return whether the image already exists on the server
    // */
    // public boolean imageExistsOnServer(int imageId) {
    // HashMap<String, String> sendMap = new HashMap<String, String>();
    // addRemoteIdentificationInfo(sendMap);
    // String imageUrl = server + "/api/getPicture?id=" + imageId
    // + "&authKey=" + authKey;
    // String imageResponseString = doHTTPPost(imageUrl, sendMap);
    // if (imageResponseString.equals(RESULT_FAIL))
    // return false;
    // else
    // return true;
    // }
    //
    // // public String registerExpeditionPoint(double lat, double lng, int
    // expedition) {
    // // String result = doHTTPGET(server + "/api/addExpeditionPoint?authKey="
    // // + authKey + "&lat=" + lat + "&lng=" + lng + "&expedition="
    // // + expedition);
    // // return result;
    // // }
    //
    //
    // /**
    // * Sends a GPS point and associated data to the Posit server. Called from
    // * Tracker Activity or TrackerBackgroundService.
    // */
    // public String registerExpeditionPoint(double lat, double lng, double alt,
    // int swath, int expedition, long time) {
    // //long swath, int expedition) {
    // // if (Utils.debug)
    // // Log.i(TrackerActivity.TAG, "Communicator, registerExpeditionPoint " +
    // lat + " " + lng + " " + time);
    // HashMap<String, String> sendMap = new HashMap<String, String>();
    // addRemoteIdentificationInfo(sendMap);
    // String addExpeditionUrl = server + "/api/addExpeditionPoint?authKey="
    // + authKey;
    // sendMap.put(PositDbHelper.GPS_POINT_LATITUDE, "" + lat);
    // sendMap.put(PositDbHelper.GPS_POINT_LONGITUDE, lng + "");
    // sendMap.put(PositDbHelper.GPS_POINT_ALTITUDE, "" + alt);
    // sendMap.put(PositDbHelper.GPS_POINT_SWATH, "" + swath);
    // sendMap.put(PositDbHelper.EXPEDITION, expedition + "");
    // sendMap.put(PositDbHelper.GPS_TIME, time + "");
    // String response = doHTTPPost(addExpeditionUrl, sendMap);
    // // if (Utils.debug) {
    // // Log.i(TrackerActivity.TAG,
    // "Communicator, registerExpeditionPoint, response: " +
    // addExpeditionResponseString);
    // // }
    // return response;
    // }
    //
    // /**
    // * Registers a new expedition with the server.
    // * @param projectId Posit's current project id.
    // * @return Returns the expedition number received from the server or -1 if
    // something
    // * goes wrong.
    // */
    // public int registerExpeditionId(int projectId) {
    // HashMap<String, String> sendMap = new HashMap<String, String>();
    // addRemoteIdentificationInfo(sendMap);
    // String addExpeditionUrl = server + "/api/addExpedition?authKey="
    // + authKey;
    // sendMap.put("projectId", "" + projectId);
    // String response = doHTTPPost(addExpeditionUrl, sendMap);
    // Log.d(TAG,"registerExpeditionId response = " + response);
    // // if (Utils.debug) {
    // // Log.i(TrackerActivity.TAG,
    // "Communicator, registerExpeditionId, response: "
    // // + addExpeditionResponseString);
    // // }
    // // The server should return an expedition number if everything goes ok.
    // If
    // // an error occurs, it will return an error message that cannot parse to
    // an int
    // // which will cause an exception here.
    // try {
    // Integer i = Integer.parseInt(response);
    // return i;
    // } catch (NumberFormatException e) {
    // Log.e(TrackerActivity.TAG,
    // "Communicator, registerExpeditionId, Invalid response received");
    // return -1;
    // }
    // }
}