com.calebgomer.roadkill_reporter.AsyncReporter.java Source code

Java tutorial

Introduction

Here is the source code for com.calebgomer.roadkill_reporter.AsyncReporter.java

Source

package com.calebgomer.roadkill_reporter;

/*
 * Copyright (C) 2009 
 * Jayesh Salvi <jayesh@altcanvas.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * 
 * 
 * 
 * This file has been modified by Caleb Gomer
 */

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.util.Log;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashMap;

public class AsyncReporter extends AsyncTask<AsyncReporter.Payload, Object, AsyncReporter.Payload> {
    public static final String TAG = "AsyncReporter";

    //tasks
    public static final int WAKE_UP_SERVER = 0;
    public static final int REPORT_RACCOON = 1;
    public static final int GET_PAST_REPORTS = 2;
    public static final int UPDATE_REPORT = 3;
    public static final int DELETE_REPORT = 4;

    //parameters
    public static final String PARAM_REPORT_ID = "repordId";
    public static final String PARAM_ACCURACY = "accuracy";
    public static final String PARAM_LATITUDE = "latitude";
    public static final String PARAM_LONGITUDE = "longitude";
    public static final String PARAM_DISTANCE = "distance";
    public static final String PARAM_AGE = "age";
    public static final String PARAM_DESCRIPTION = "description";
    public static final String PARAM_TESTING = "testing";

    //urls
    public static final String WAKE_UP_URL = "http://roadkill-reporter.herokuapp.com/helloworld";
    public static final String NEW_ID_URL = "http://roadkill-reporter.herokuapp.com/newid";
    public static final String REPORT_RACCOON_URL = "http://roadkill-reporter.herokuapp.com/report";
    public static final String REPORT_RACCOON_URL_TEST = "http://192.168.1.102:5000/report";
    public static final String GET_PAST_REPORTS_URL = "http://roadkill-reporter.herokuapp.com/reports/user/%s";
    public static final String GET_PAST_REPORTS_URL_TEST = "http://192.168.1.102:5000/reports/user/%s";
    public static final String UPDATE_REPORT_URL = "http://roadkill-reporter.herokuapp.com/update_loc/%s/%s/%s/%s";//reportNumber, location.latitude, location.longitude,userId
    public static final String UPDATE_REPORT_URL_TEST = "http://192.168.1.102:5000/update_loc/%s/%s/%s/%s";//reportNumber, location.latitude, location.longitude,userId
    public static final String DELETE_REPORT_URL = "http://roadkill-reporter.herokuapp.com/delete/%s/%s";//,reportNumber,userId
    public static final String DELETE_REPORT_URL_TEST = "http://192.168.1.102:5000/delete/%s/%s";//,reportNumber,userId
    /**
     * various other strings
     */
    //expected server response prefix for JSON data
    public static final String ROADKILL_RESPONSE_PREFIX = "RoadkillReporterData:";
    //key for userid shared preference value
    public static final String USERID = "userid";

    //shortcut static method for creating new AsyncReporters and executing a request
    public static void perform(Payload request) {
        new AsyncReporter().execute(request);
    }

    //shortcut static method for waking up the server
    //this improves roadkill report time later on if the server was sleeping
    public static void wakeUpServer(AsyncCallbackListener callback, Context context) {
        Log.v(TAG, "waking server");
        AsyncReporter.Payload request = new AsyncReporter.Payload(AsyncReporter.WAKE_UP_SERVER, null, callback,
                context);
        AsyncReporter.perform(request);
    }

    /*
    * Runs on GUI thread
    */
    @Override
    protected void onPreExecute() {
        Log.i(TAG, "*******GOING*****");
    }

    /*
    * Runs on GUI thread
    */
    @Override
    public void onPostExecute(AsyncReporter.Payload payload) {
        if (payload.result == null && payload.exception == null)
            payload.exception = new AsyncException("Unknown Error");

        if (payload.callback.isPaused())
            Log.e(TAG, "Activity killed before response could be returned");
        else
            payload.callback.asyncDone(payload);
    }

    /*
    * Runs on background thread
    */
    @Override
    public AsyncReporter.Payload doInBackground(AsyncReporter.Payload... _params) {
        Log.d(TAG, "*******BACKGROUND********");

        AsyncReporter.Payload payload = _params[0];

        //stop now if there is no internet connection
        if (!isOnline(payload.context.getApplicationContext())) {
            payload.exception = new AsyncException("No Internet Connection");
            return payload;
        }

        //skip the rest if we're trying to wake up the server
        if (payload.taskType == WAKE_UP_SERVER) {
            payload.result = wakeServer(payload.context);
            return payload;
        }

        String userid = getUserid(payload.context);
        if (userid == null) {
            payload.exception = new AsyncException("We're having some strange errors, please try that again.");
            return payload;
        }

        payload.result = "";
        HashMap<String, String> params = payload.params;
        String reportId = params.get(PARAM_REPORT_ID);
        String lat = params.get(PARAM_LATITUDE);
        String lon = params.get(PARAM_LONGITUDE);
        String distance = params.get(PARAM_DISTANCE);
        String accuracy = params.get(PARAM_ACCURACY);
        String age = params.get(PARAM_AGE);
        String description = params.get(PARAM_DESCRIPTION);
        String isTesting = params.get(PARAM_TESTING);
        boolean testing = isTesting != null && isTesting.equals("y");

        boolean valid = false;

        switch (payload.taskType) {

        case REPORT_RACCOON:
            valid = lat != null && lon != null && accuracy != null && age != null;

            if (valid)
                payload.result = reportRaccoon(lat, lon, accuracy, age, userid, description, testing);
            else
                payload.exception = new AsyncException("Can't create that report");
            break;

        case GET_PAST_REPORTS:
            payload.result = getPastReports(userid);
            break;

        case UPDATE_REPORT:
            valid = lat != null && lon != null && reportId != null;
            if (valid)
                payload.result = updateReport(reportId, lat, lon, userid);
            else
                payload.exception = new AsyncException("Can't update that report");
            break;

        case DELETE_REPORT:
            valid = userid != null && reportId != null;
            if (valid)
                payload.result = deleteReport(reportId, userid);
            else
                payload.exception = new AsyncException("Can't delete that report");
            break;

        default:
            payload.exception = new AsyncException("[" + payload.taskType + "] is not a valid task ID");
            break;
        }

        return payload;
    }

    public static class Payload {
        public int taskType;
        public AsyncCallbackListener callback;
        public Context context;
        public HashMap<String, String> params;
        public String result;
        public Exception exception;

        public Payload(int taskType, HashMap<String, String> params, AsyncCallbackListener callback,
                Context context) {
            this.taskType = taskType;
            this.callback = callback;
            this.params = params;
            this.context = context;
        }

        public String errorString() {
            return this.exception.getMessage() + " [ID-" + this.taskType + "-T]";
        }
    }

    public String wakeServer(Context contextForUserid) {
        try {
            JSONObject wakeUpResponse = new JSONObject(get(WAKE_UP_URL));
            String world = wakeUpResponse.getString("hello");
            if (world.equals("world")) {
                //server is awake and functioning!
                if (isNewUser(contextForUserid)) {
                    String userid = getUserid(contextForUserid);
                    if (userid == null) {
                        return "{\"status\":\"online\", \"user\":\"none\"}";
                    } else {
                        return "{\"status\":\"online\", \"user\":\"new\"}";
                    }
                } else {
                    return "{\"status\":\"online\", \"user\":\"existing\"}";
                }
            }
        } catch (JSONException e) {
            //woah there buddy
            Log.e(TAG, "Bad JSON while requesting new userid");
        }
        return "{\"status\":\"offline\", \"user\":\"\"}";
    }

    public String getUserid(Context contextForUserid) {
        String userid = PreferenceManager.getDefaultSharedPreferences(contextForUserid).getString(USERID, null);
        if (userid == null) {
            userid = getNewUserid();
            if (userid != null) {
                PreferenceManager.getDefaultSharedPreferences(contextForUserid).edit().putString(USERID, userid)
                        .commit();
            }
        }
        return userid;
    }

    public boolean isNewUser(Context contextForUserid) {
        return (null == PreferenceManager.getDefaultSharedPreferences(contextForUserid).getString(USERID, null));
    }

    public String getNewUserid() {
        Log.d(TAG, "requesting new userid");
        String userid = null;
        try {
            JSONObject newIdResponse = new JSONObject(get(NEW_ID_URL));
            String status = newIdResponse.getString("status");
            String uuid = newIdResponse.getString(("uuid"));
            if (status.equals(("UUID")) && uuid != null) {
                userid = uuid;
            }
        } catch (JSONException e) {
            //wow, server glitch. oops.
            Log.e(TAG, "Bad JSON while requesting new userid");
            userid = null;
        }
        return userid;
    }

    public String reportRaccoon(String lat, String lon, String accuracy, String age, String username,
            String description, boolean testing) {
        String command = "/" + lat + "/" + lon + "/" + accuracy + "/" + age + "/"
                + (username == null ? "" : username) + "/" + (description == null ? "" : description);
        command = (testing ? REPORT_RACCOON_URL_TEST : REPORT_RACCOON_URL) + command;

        Log.d(TAG, command);
        return post(command);
    }

    public String getPastReports(String userid) {
        String command = String.format(GET_PAST_REPORTS_URL, userid);
        Log.d(TAG, command);
        return get(command);
    }

    public String updateReport(String reportId, String lat, String lon, String userid) {
        String command = String.format(UPDATE_REPORT_URL, reportId, lat, lon, userid);
        Log.d(TAG, command);
        return post(command);
    }

    public String deleteReport(String reportId, String userid) {
        String command = String.format(DELETE_REPORT_URL, reportId, userid);
        Log.d(TAG, command);
        return get(command);
    }

    public boolean isOnline(Context mainContext) {
        ConnectivityManager cm = (ConnectivityManager) mainContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = cm.getActiveNetworkInfo();
        return (info != null && info.isConnected());
    }

    private static String post(String command) {
        return getJSON(new HttpPost(command));
    }

    private static String get(String command) {
        return getJSON(new HttpGet(command));
    }

    private static String getJSON(HttpUriRequest request) {

        HttpClient client = new DefaultHttpClient();
        //    HttpPost post = new HttpPost(command);
        HttpResponse response;
        try {
            response = client.execute(request);
        } catch (ClientProtocolException e2) {
            e2.printStackTrace();
            return "Connection Failed (Client)";
        } catch (IOException e2) {
            e2.printStackTrace();
            return "Connection Failed (I/O)";
        }
        String response_text = null;
        HttpEntity entity = null;
        try {
            entity = response.getEntity();
            response_text = _getResponseBody(entity);
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            if (entity != null) {
                try {
                    entity.consumeContent();
                } catch (IOException e1) {
                }
            }
        }
        return valid(response_text);
    }

    private static String _getResponseBody(final HttpEntity entity) throws IOException, ParseException {
        if (entity == null) {
            throw new IllegalArgumentException("HTTP entity may not be null");
        }
        InputStream instream = entity.getContent();
        if (instream == null) {
            return "";
        }
        if (entity.getContentLength() > Integer.MAX_VALUE) {
            throw new IllegalArgumentException("HTTP entity too large to be buffered in memory");
        }
        String charset = getContentCharSet(entity);
        if (charset == null) {
            charset = HTTP.DEFAULT_CONTENT_CHARSET;
        }
        Reader reader = new InputStreamReader(instream, charset);
        StringBuilder buffer = new StringBuilder();
        try {
            char[] tmp = new char[1024];
            int l;
            while ((l = reader.read(tmp)) != -1) {
                buffer.append(tmp, 0, l);
            }
        } finally {
            reader.close();
        }
        return buffer.toString();
    }

    private static String getContentCharSet(final HttpEntity entity) throws ParseException {
        if (entity == null) {
            throw new IllegalArgumentException("HTTP entity may not be null");
        }
        String charset = null;
        if (entity.getContentType() != null) {
            HeaderElement values[] = entity.getContentType().getElements();
            if (values.length > 0) {
                NameValuePair param = values[0].getParameterByName("charset");
                if (param != null) {
                    charset = param.getValue();
                }
            }
        }
        return charset;
    }

    /**
     * makes sure the server's response is valid before returning it
     */
    private static String valid(String response) {
        if (response.length() >= 21 && response.substring(0, 21).equals(ROADKILL_RESPONSE_PREFIX)) {
            return response.substring(21);
        } else {
            Log.d(TAG, "response: '" + response + "' *****NOT VALID*****");
            return "error";
        }
    }

    public interface AsyncCallbackListener {
        public void asyncDone(AsyncReporter.Payload response);

        public Context getApplicationContext();

        public boolean isPaused();
    }

    private class AsyncException extends Exception {

        public String msg = null;

        public AsyncException(String msg) {
            super(msg);
            this.msg = msg;
        }

        @Override
        public String toString() {
            return msg;
        }
    }

}