org.hfoss.posit.web.Communicator.java Source code

Java tutorial

Introduction

Here is the source code for org.hfoss.posit.web.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.web;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
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.Map;

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.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.hfoss.posit.Find;
import org.hfoss.posit.R;
import org.hfoss.posit.provider.PositDbHelper;
import org.hfoss.posit.utilities.Utils;
import org.hfoss.third.Base64Coder;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.telephony.TelephonyManager;
import android.util.Log;

/**
 * The communication module for POSIT.  Handles most calls to the server to get information regarding
 * projects and finds.
 * 
 * 
 */
public class Communicator {
    private static final String COLUMN_IMEI = "imei";

    /*
     * You should be careful with putting names for server. DO NOT always trust
     * DNS.
     */

    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 String TAG = "Communicator";
    private String responseString;
    private Context mContext;
    private SharedPreferences applicationPreferences;
    private HttpParams mHttpParams;
    private HttpClient mHttpClient;
    private ThreadSafeClientConnManager mConnectionManager;
    public static long mTotalTime = 0;
    private long mStart = 0;

    public Communicator(Context _context) {
        mContext = _context;
        mTotalTime = 0;
        mStart = 0;

        mHttpParams = new BasicHttpParams();
        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(applicationPreferences.getString("AUTHKEY", ""),
                applicationPreferences.getString("SERVER_ADDRESS", server),
                applicationPreferences.getInt("PROJECT_ID", 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;
    }

    /**
     * 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 ArrayList<HashMap<String, Object>> getProjects() {
        Log.i(TAG, "authkey=" + authKey);
        if (authKey.equals("")) {
            Log.e(TAG, "getProjects() authKey == ");
            Utils.showToast(mContext, "Aborting Communicator:\nPhone does not have a valid authKey."
                    + "\nUse settings menu to register phone.");
            return null;
        }
        String url = server + "/api/listOpenProjects?authKey=" + authKey;
        ArrayList<HashMap<String, Object>> list;
        responseString = doHTTPGET(url);
        if (Utils.debug)
            Log.i(TAG, responseString);
        list = new ArrayList<HashMap<String, Object>>();
        try {
            list = (ArrayList<HashMap<String, Object>>) (new ResponseParser(responseString).parse());
        } catch (JSONException e1) {
            Log.i(TAG, "getProjects JSON exception " + e1.getMessage());
            e1.printStackTrace();
            return null;
        }
        return list;
    }

    /**
     * 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 boolean registerDevice(String server, String authKey, String imei) {
        // server = "http://192.168.1.105/posit";
        String url = server + "/api/registerDevice?authKey=" + authKey + "&imei=" + imei;
        Log.i(TAG, "registerDevice URL=" + url);

        try {
            responseString = doHTTPGET(url);
        } catch (Exception e) {
            Utils.showToast(mContext, e.getMessage());
        }

        if (responseString.equals(RESULT_FAIL))
            return false;
        else
            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 boolean sendFind(Find find, String action) {
        boolean success = false;
        String url;
        HashMap<String, String> sendMap = find.getContentMapGuid();
        //Log.i(TAG, "sendFind map = " + sendMap.toString());
        cleanupOnSend(sendMap);
        sendMap.put("imei", imei);
        String guid = sendMap.get(PositDbHelper.FINDS_GUID);

        // Create the url

        if (action.equals("create")) {
            url = server + "/api/createFind?authKey=" + authKey;
        } else {
            url = server + "/api/updateFind?authKey=" + authKey;
        }
        if (Utils.debug) {
            Log.i(TAG, "SendFind=" + sendMap.toString());
        }

        // Send the find
        try {
            responseString = doHTTPPost(url, sendMap);
        } catch (Exception e) {
            Log.i(TAG, e.getMessage());
            Utils.showToast(mContext, e.getMessage());
        }
        if (Utils.debug)
            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'");
            return false;
        } else {
            PositDbHelper dbh = new PositDbHelper(mContext);
            long id = find.getId();
            success = dbh.markFindSynced(id);
            if (Utils.debug)
                Log.i(TAG, "sendfind synced " + id + " " + success);
        }

        if (!success)
            return false;

        // Otherwise send the Find's images

        long id = Long.parseLong(sendMap.get(PositDbHelper.FINDS_ID));
        PositDbHelper dbh = new PositDbHelper(mContext);
        ArrayList<ContentValues> photosList = dbh.getImagesListSinceUpdate(id);

        Log.i(TAG, "sendFind, photosList=" + photosList.toString());

        Iterator<ContentValues> it = photosList.listIterator();
        while (it.hasNext()) {
            ContentValues imageData = it.next();
            Uri uri = Uri.parse(imageData.getAsString(PositDbHelper.PHOTOS_IMAGE_URI));
            String base64Data = convertUriToBase64(uri);
            uri = Uri.parse(imageData.getAsString(PositDbHelper.PHOTOS_THUMBNAIL_URI));
            String base64Thumbnail = convertUriToBase64(uri);
            sendMap = new HashMap<String, String>();
            sendMap.put(COLUMN_IMEI, Utils.getIMEI(mContext));
            sendMap.put(PositDbHelper.FINDS_GUID, guid);

            sendMap.put(PositDbHelper.PHOTOS_IDENTIFIER, imageData.getAsString(PositDbHelper.PHOTOS_IDENTIFIER));
            sendMap.put(PositDbHelper.FINDS_PROJECT_ID, imageData.getAsString(PositDbHelper.FINDS_PROJECT_ID));
            sendMap.put(PositDbHelper.FINDS_TIME, imageData.getAsString(PositDbHelper.FINDS_TIME));
            sendMap.put(PositDbHelper.PHOTOS_MIME_TYPE, imageData.getAsString(PositDbHelper.PHOTOS_MIME_TYPE));

            sendMap.put("mime_type", "image/jpeg");

            sendMap.put(PositDbHelper.PHOTOS_DATA_FULL, base64Data);
            sendMap.put(PositDbHelper.PHOTOS_DATA_THUMBNAIL, base64Thumbnail);
            sendMedia(sendMap);
            //it.next();
        }

        // Update the Synced attribute.
        return true;
    }

    /**
     * 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 void sendMedia(HashMap<String, String> sendMap) {
        Log.i(TAG, "sendMedia, sendMap= " + sendMap);

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

        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("barcode_id"));
        //rMap.put(PositDbHelper.FINDS_GUID, rMap.get("barcode_id"));

        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 sendMap the hashMap of data to send to the server as POST data
     * @return the response from the URL
     */
    private String doHTTPPost(String Uri, HashMap<String, String> sendMap) {

        if (Uri == null)
            throw new NullPointerException("The URL has to be passed");
        String responseString = null;
        HttpPost post = new HttpPost();
        if (Utils.debug)
            Log.i(TAG, "doHTTPPost() URI = " + Uri);
        try {
            post.setURI(new URI(Uri));
        } catch (URISyntaxException e) {
            Log.e(TAG, "URISyntaxException " + e.getMessage());
            e.printStackTrace();
            return e.getMessage();
        }
        List<NameValuePair> nvp = PositHttpUtils.getNameValuePairs(sendMap);
        ResponseHandler<String> responseHandler = new BasicResponseHandler();
        try {
            post.setEntity(new UrlEncodedFormEntity(nvp, HTTP.UTF_8));
        } catch (UnsupportedEncodingException e) {
            Log.e(TAG, "UnsupportedEncodingException " + e.getMessage());
        }
        mStart = System.currentTimeMillis();

        try {
            responseString = mHttpClient.execute(post, responseHandler);
        } catch (ClientProtocolException e) {
            Log.e(TAG, "ClientProtocolExcpetion" + e.getMessage());
            e.printStackTrace();
            return e.getMessage();
        } catch (IOException e) {
            Log.e(TAG, "IOException " + e.getMessage());
            e.printStackTrace();
            return e.getMessage();
        } catch (IllegalStateException e) {
            Log.e(TAG, "IllegalStateException: " + e.getMessage());
            e.printStackTrace();
            return e.getMessage();
        } catch (Exception e) {
            Log.e(TAG, "Exception on HttpPost " + e.getMessage());
            e.printStackTrace();
            return e.getMessage();
        }
        long time = System.currentTimeMillis() - mStart;
        mTotalTime += time;
        Log.i(TAG, "TIME = " + time + " millisecs");

        return responseString;
    }

    /**
     * A wrapper(does some cleanup too) for sending HTTP GET requests to the URI 
     * 
     * @param Uri
     * @return the request from the remote server
     */
    public String doHTTPGET(String Uri) {
        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, e.getMessage());
            e.printStackTrace();
            return "[Error]" + e.getMessage();
        }
        if (Utils.debug) {
            Log.i(TAG, "doHTTPGet Uri = " + Uri);
        }
        ResponseHandler<String> responseHandler = new BasicResponseHandler();
        mStart = System.currentTimeMillis();

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

        long time = System.currentTimeMillis() - mStart;
        mTotalTime += time;
        Log.i(TAG, "TIME = " + time + " millisecs");

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

    /**
     * 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 ContentValues getRemoteFindById(String guid) {
        String url = server + "/api/getFind?guid=" + guid + "&authKey=" + authKey;
        HashMap<String, String> sendMap = new HashMap<String, String>();
        addRemoteIdentificationInfo(sendMap);
        sendMap.put("guid", guid + "");
        String responseString = doHTTPPost(url, sendMap);
        ContentValues cv = new ContentValues();

        Log.i(TAG, "getRemoteFindById = " + responseString);
        try {
            JSONObject jobj = new JSONObject(responseString);
            cv.put(PositDbHelper.FINDS_GUID, jobj.getString("barcode_id"));
            cv.put(PositDbHelper.FINDS_PROJECT_ID, jobj.getInt("project_id"));
            cv.put(PositDbHelper.FINDS_NAME, jobj.getString("name"));
            cv.put(PositDbHelper.FINDS_DESCRIPTION, jobj.getString("description"));
            cv.put(PositDbHelper.FINDS_TIME, jobj.getString("add_time"));
            cv.put(PositDbHelper.FINDS_TIME, jobj.getString("modify_time"));
            cv.put(PositDbHelper.FINDS_LATITUDE, jobj.getDouble("latitude"));
            cv.put(PositDbHelper.FINDS_LONGITUDE, jobj.getDouble("longitude"));
            cv.put(PositDbHelper.FINDS_REVISION, jobj.getInt("revision"));
            return cv;
        } catch (JSONException e) {
            Log.i(TAG, e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            Log.i(TAG, e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 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;
    }

    public String registerExpeditionPoint(double lat, double lng, double alt, long swath, int expedition) {
        if (Utils.debug)
            Log.i(TAG, "registerExpeditionPoint " + lat + " " + lng);
        HashMap<String, String> sendMap = new HashMap<String, String>();
        addRemoteIdentificationInfo(sendMap);
        String addExpeditionUrl = server + "/api/addExpeditionPoint?authKey=" + authKey;
        sendMap.put("lat", "" + lat);
        sendMap.put("lng", lng + "");
        sendMap.put("alt", "" + alt);
        sendMap.put("swath", "" + swath);
        sendMap.put("expeditionId", expedition + "");
        String addExpeditionResponseString = doHTTPPost(addExpeditionUrl, sendMap);
        if (Utils.debug) {
            Log.i(TAG, "response: " + addExpeditionResponseString);
        }
        return addExpeditionResponseString;
    }

    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 addExpeditionResponseString = doHTTPPost(addExpeditionUrl, sendMap);
        if (Utils.debug) {
            Log.i(TAG, "registerExpeditionId, response: " + addExpeditionResponseString);
        }
        try {
            Integer i = Integer.parseInt(addExpeditionResponseString);
            return i;
        } catch (NumberFormatException e) {
            Log.e(TAG, "Invalid response received");
            return -1;
        }
    }
}