edu.pdx.cecs.orcycle.TripUploader.java Source code

Java tutorial

Introduction

Here is the source code for edu.pdx.cecs.orcycle.TripUploader.java

Source

/**
 *  ORcycle, Copyright 2014, 2015, PSU Transportation, Technology, and People Lab.
 *
 *  ORcycle 2.2.0 has introduced new app features: safety focus with new buttons
 *  to report safety issues and crashes (new questionnaires), expanded trip
 *  questionnaire (adding questions besides trip purpose), app utilization
 *  reminders, app tutorial, and updated font and color schemes.
 *
 *  @author Bryan.Blanc <bryanpblanc@gmail.com>    (code)
 *  @author Miguel Figliozzi <figliozzi@pdx.edu> and ORcycle team (general app
 *  design and features, report questionnaires and new ORcycle features)
 *
 *  For more information on the project, go to
 *  http://www.pdx.edu/transportation-lab/orcycle and http://www.pdx.edu/transportation-lab/app-development
 *
 *  Updated/modified for Oregon pilot study and app deployment.
 *
 *  ORcycle is free software: you can redistribute it and/or modify it under the
 *  terms of the GNU General Public License as published by the Free Software
 *  Foundation, either version 3 of the License, or any later version.
 *  ORcycle is distributed in the hope that it will be useful, but WITHOUT ANY
 *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 *  A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *  You should have received a copy of the GNU General Public License along with
 *  ORcycle. If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************************
 *
 *    Cycle Altanta, Copyright 2012 Georgia Institute of Technology
 *                                    Atlanta, GA. USA
 *
 *   @author Christopher Le Dantec <ledantec@gatech.edu>
 *   @author Anhong Guo <guoanhong15@gmail.com>
 *
 *   Updated/Modified for Atlanta's app deployment. Based on the
 *   CycleTracks codebase for SFCTA.
 *
 *   CycleTracks, Copyright 2009,2010 San Francisco County Transportation Authority
 *                                    San Francisco, CA, USA
 *
 *     @author Billy Charlton <billy.charlton@sfcta.org>
 *
 *   This file is part of CycleTracks.
 *
 *   CycleTracks is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   CycleTracks is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with CycleTracks.  If not, see <http://www.gnu.org/licenses/>.
 */

package edu.pdx.cecs.orcycle;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import java.util.zip.GZIPOutputStream;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Build;
import android.util.Log;
import android.widget.ListView;
import android.widget.Toast;

public class TripUploader extends AsyncTask<Long, Integer, Boolean> {

    private static final String MODULE_TAG = "TripUploader";
    public static final int kSaveProtocolVersion = 3;
    private static final String POST_URL = "http://orcycle2.cecs.pdx.edu/post/";

    // Saving protocol version 3
    public static final String TRIP_COORDS_TIME = "r";
    public static final String TRIP_COORDS_LAT = "l";
    public static final String TRIP_COORDS_LON = "n";
    public static final String TRIP_COORDS_ALT = "a";
    public static final String TRIP_COORDS_SPEED = "s";
    public static final String TRIP_COORDS_HACCURACY = "h";
    public static final String TRIP_COORDS_VACCURACY = "v";
    public static final String TRIP_COORDS_SENSOR_READINGS = "sr";

    public static final String PAUSE_START = "ps";
    public static final String PAUSE_END = "pe";

    public static final String TRIP_COORD_SENSOR_ID = "s_id";
    public static final String TRIP_COORD_SENSOR_TYPE = "s_t";
    public static final String TRIP_COORD_SENSOR_SAMPLES = "s_ns";
    public static final String TRIP_COORD_SENSOR_NUM_VALS = "s_nv";
    public static final String TRIP_COORD_SENSOR_AVG_0 = "s_a0";
    public static final String TRIP_COORD_SENSOR_AVG_1 = "s_a1";
    public static final String TRIP_COORD_SENSOR_AVG_2 = "s_a2";
    public static final String TRIP_COORD_SENSOR_SSD_0 = "s_s0";
    public static final String TRIP_COORD_SENSOR_SSD_1 = "s_s1";
    public static final String TRIP_COORD_SENSOR_SSD_2 = "s_s2";

    public static final String USER_AGE = "age";
    public static final String USER_EMAIL = "email";
    public static final String USER_GENDER = "gender";
    public static final String USER_ZIP_HOME = "homeZIP";
    public static final String USER_ZIP_WORK = "workZIP";
    public static final String USER_ZIP_SCHOOL = "schoolZIP";
    public static final String USER_CYCLING_FREQUENCY = "cyclingFreq";
    public static final String APP_VERSION = "app_version";

    public static final String USER_ETHNICITY = "ethnicity";
    public static final String USER_INCOME = "income";
    public static final String USER_HHSIZE = "hhSize";
    public static final String USER_HHVEHICLES = "hhVehicles";
    public static final String USER_RIDERTYPE = "rider_type";
    public static final String USER_RIDERHISTORY = "rider_history";

    private static final String FID_QUESTION_ID = "question_id";
    private static final String FID_ANSWER_ID = "answer_id";
    private static final String FID_ANSWER_OTHER_TEXT = "other_text";

    private final Context mCtx;
    private final String userId;
    private final DbAdapter mDb;

    private Map<String, Integer> sensorColumn = null;

    public TripUploader(Context ctx, String userId) {
        super();
        this.mCtx = ctx;
        this.userId = userId;
        this.mDb = new DbAdapter(this.mCtx);
    }

    @SuppressLint("SimpleDateFormat")
    private JSONObject getCoordsJSON(long tripId) throws JSONException {

        JSONObject jsonTripCoords = null;
        JSONArray jsonSensorReadings = null;

        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        mDb.openReadOnly();

        try {
            Cursor cursorTripCoords = mDb.fetchAllCoordsForTrip(tripId);

            // Build the map between JSON field name and phone db field name:
            Map<String, Integer> fieldMap = new HashMap<String, Integer>();

            fieldMap.put(TRIP_COORDS_TIME, cursorTripCoords.getColumnIndex(DbAdapter.K_POINT_TIME));
            fieldMap.put(TRIP_COORDS_LAT, cursorTripCoords.getColumnIndex(DbAdapter.K_POINT_LAT));
            fieldMap.put(TRIP_COORDS_LON, cursorTripCoords.getColumnIndex(DbAdapter.K_POINT_LGT));
            fieldMap.put(TRIP_COORDS_ALT, cursorTripCoords.getColumnIndex(DbAdapter.K_POINT_ALT));
            fieldMap.put(TRIP_COORDS_SPEED, cursorTripCoords.getColumnIndex(DbAdapter.K_POINT_SPEED));
            fieldMap.put(TRIP_COORDS_HACCURACY, cursorTripCoords.getColumnIndex(DbAdapter.K_POINT_ACC));
            fieldMap.put(TRIP_COORDS_VACCURACY, cursorTripCoords.getColumnIndex(DbAdapter.K_POINT_ACC));

            // Build JSON objects for each coordinate:
            jsonTripCoords = new JSONObject();
            double coordTime;
            while (!cursorTripCoords.isAfterLast()) {

                // *****************
                // * Get coordinates
                // *****************

                coordTime = cursorTripCoords.getDouble(fieldMap.get(TRIP_COORDS_TIME));

                JSONObject jsonCoord = new JSONObject();

                jsonCoord.put(TRIP_COORDS_TIME, df.format(coordTime));
                jsonCoord.put(TRIP_COORDS_LAT, cursorTripCoords.getDouble(fieldMap.get(TRIP_COORDS_LAT)) / 1E6);
                jsonCoord.put(TRIP_COORDS_LON, cursorTripCoords.getDouble(fieldMap.get(TRIP_COORDS_LON)) / 1E6);
                jsonCoord.put(TRIP_COORDS_ALT, dr1(cursorTripCoords.getDouble(fieldMap.get(TRIP_COORDS_ALT))));
                jsonCoord.put(TRIP_COORDS_SPEED, dr1(cursorTripCoords.getDouble(fieldMap.get(TRIP_COORDS_SPEED))));
                jsonCoord.put(TRIP_COORDS_HACCURACY,
                        cursorTripCoords.getDouble(fieldMap.get(TRIP_COORDS_HACCURACY)));
                jsonCoord.put(TRIP_COORDS_VACCURACY,
                        cursorTripCoords.getDouble(fieldMap.get(TRIP_COORDS_VACCURACY)));

                // **********************************************************
                // * Get all sensor readings corresponding to this time index
                // **********************************************************

                jsonSensorReadings = getJsonSensorReadings(coordTime);

                if ((null != jsonSensorReadings) && (jsonSensorReadings.length() > 0)) {
                    jsonCoord.put(TRIP_COORDS_SENSOR_READINGS, jsonSensorReadings);
                }

                // ****************************************************
                // * Insert sensor readings into jSON coordinate object
                // ****************************************************

                jsonTripCoords.put(jsonCoord.getString("r"), jsonCoord);

                // move to next coordinate
                cursorTripCoords.moveToNext();
            }
            cursorTripCoords.close();
        } catch (Exception ex) {
            Log.e(MODULE_TAG, ex.getMessage());
        } finally {
            mDb.close();
        }
        return jsonTripCoords;
    }

    /**
     * Get all sensor readings corresponding to this time index
     * @return
     */
    private JSONArray getJsonSensorReadings(double coordTime) {
        JSONArray jsonSensorReadings = null;
        JSONObject jsonSensorReading;
        int numVals;
        Cursor cursorSV = null;

        try {
            if (null != (cursorSV = mDb.fetchSensorValues(coordTime))) {

                // Collect sensor readings into a json object
                jsonSensorReadings = new JSONArray();

                // Construct a map between cursor column index
                if (null == sensorColumn) {
                    sensorColumn = new HashMap<String, Integer>();
                    sensorColumn.put(TRIP_COORD_SENSOR_ID, cursorSV.getColumnIndex(DbAdapter.K_SENSOR_ID));
                    sensorColumn.put(TRIP_COORD_SENSOR_TYPE, cursorSV.getColumnIndex(DbAdapter.K_SENSOR_TYPE));
                    sensorColumn.put(TRIP_COORD_SENSOR_SAMPLES,
                            cursorSV.getColumnIndex(DbAdapter.K_SENSOR_SAMPLES));
                    sensorColumn.put(TRIP_COORD_SENSOR_NUM_VALS,
                            cursorSV.getColumnIndex(DbAdapter.K_SENSOR_NUM_VALS));
                    sensorColumn.put(TRIP_COORD_SENSOR_AVG_0, cursorSV.getColumnIndex(DbAdapter.K_SENSOR_AVG_0));
                    sensorColumn.put(TRIP_COORD_SENSOR_AVG_1, cursorSV.getColumnIndex(DbAdapter.K_SENSOR_AVG_1));
                    sensorColumn.put(TRIP_COORD_SENSOR_AVG_2, cursorSV.getColumnIndex(DbAdapter.K_SENSOR_AVG_2));
                    sensorColumn.put(TRIP_COORD_SENSOR_SSD_0, cursorSV.getColumnIndex(DbAdapter.K_SENSOR_SSD_0));
                    sensorColumn.put(TRIP_COORD_SENSOR_SSD_1, cursorSV.getColumnIndex(DbAdapter.K_SENSOR_SSD_1));
                    sensorColumn.put(TRIP_COORD_SENSOR_SSD_2, cursorSV.getColumnIndex(DbAdapter.K_SENSOR_SSD_2));
                }

                int numSamples;

                while (!cursorSV.isAfterLast()) {

                    jsonSensorReading = new JSONObject();
                    jsonSensorReading.put(TRIP_COORD_SENSOR_ID,
                            cursorSV.getString(sensorColumn.get(TRIP_COORD_SENSOR_ID)));
                    jsonSensorReading.put(TRIP_COORD_SENSOR_TYPE,
                            cursorSV.getInt(sensorColumn.get(TRIP_COORD_SENSOR_TYPE)));
                    jsonSensorReading.put(TRIP_COORD_SENSOR_SAMPLES,
                            (numSamples = cursorSV.getInt(sensorColumn.get(TRIP_COORD_SENSOR_SAMPLES))));

                    numVals = cursorSV.getInt(sensorColumn.get(TRIP_COORD_SENSOR_NUM_VALS));

                    switch (numVals) {

                    case 1:

                        jsonSensorReading.put(TRIP_COORD_SENSOR_AVG_0,
                                dr2(cursorSV.getDouble(sensorColumn.get(TRIP_COORD_SENSOR_AVG_0))));
                        if (numSamples > 0) {
                            jsonSensorReading.put(TRIP_COORD_SENSOR_SSD_0, dr2(Math.sqrt(
                                    cursorSV.getDouble(sensorColumn.get(TRIP_COORD_SENSOR_SSD_0)) / numSamples)));
                        } else {
                            jsonSensorReading.put(TRIP_COORD_SENSOR_SSD_0, 0.0);
                        }
                        break;

                    case 3:

                        jsonSensorReading.put(TRIP_COORD_SENSOR_AVG_0,
                                dr2(cursorSV.getDouble(sensorColumn.get(TRIP_COORD_SENSOR_AVG_0))));
                        jsonSensorReading.put(TRIP_COORD_SENSOR_AVG_1,
                                dr2(cursorSV.getDouble(sensorColumn.get(TRIP_COORD_SENSOR_AVG_1))));
                        jsonSensorReading.put(TRIP_COORD_SENSOR_AVG_2,
                                dr2(cursorSV.getDouble(sensorColumn.get(TRIP_COORD_SENSOR_AVG_2))));
                        if (numSamples > 0) {
                            jsonSensorReading.put(TRIP_COORD_SENSOR_SSD_0, dr2(Math.sqrt(
                                    cursorSV.getDouble(sensorColumn.get(TRIP_COORD_SENSOR_SSD_0)) / numSamples)));
                            jsonSensorReading.put(TRIP_COORD_SENSOR_SSD_1, dr2(Math.sqrt(
                                    cursorSV.getDouble(sensorColumn.get(TRIP_COORD_SENSOR_SSD_1)) / numSamples)));
                            jsonSensorReading.put(TRIP_COORD_SENSOR_SSD_2, dr2(Math.sqrt(
                                    cursorSV.getDouble(sensorColumn.get(TRIP_COORD_SENSOR_SSD_2)) / numSamples)));
                        } else {
                            jsonSensorReading.put(TRIP_COORD_SENSOR_SSD_0, 0.0);
                            jsonSensorReading.put(TRIP_COORD_SENSOR_SSD_1, 0.0);
                            jsonSensorReading.put(TRIP_COORD_SENSOR_SSD_2, 0.0);
                        }
                        break;
                    }

                    jsonSensorReadings.put(jsonSensorReading);
                    cursorSV.moveToNext();
                }
            }
        } catch (Exception ex) {
            Log.e(MODULE_TAG, ex.getMessage());
        } finally {
            if (null != cursorSV) {
                cursorSV.close();
            }
        }
        return jsonSensorReadings;
    }

    /**
     * Rounds double value to two decimal places.
     * @param value to round.
     * @return value rounded to two decimal places.
     */
    private double dr1(double value) {
        return Math.round(value * 10.0) / 10.0;
    }

    /**
     * Rounds double value to two decimal places.
     * @param value to round.
     * @return value rounded to two decimal places.
     */
    private double dr2(double value) {
        return Math.round(value * 100.0) / 100.0;
    }

    @SuppressLint("SimpleDateFormat")
    private JSONArray getPausesJSON(long tripId) throws JSONException {

        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        mDb.openReadOnly();

        Cursor pausesCursor = mDb.fetchPauses(tripId);

        // Build the map between JSON fieldname and phone db fieldname:
        Map<String, Integer> fieldMap = new HashMap<String, Integer>();
        fieldMap.put(PAUSE_START, pausesCursor.getColumnIndex(DbAdapter.K_PAUSE_START_TIME));
        fieldMap.put(PAUSE_END, pausesCursor.getColumnIndex(DbAdapter.K_PAUSE_END_TIME));

        // Build JSON objects for each coordinate:
        JSONArray jsonPauses = new JSONArray();
        while (!pausesCursor.isAfterLast()) {

            JSONObject json = new JSONObject();

            try {
                json.put(PAUSE_START, df.format(pausesCursor.getDouble(fieldMap.get(PAUSE_START))));
                json.put(PAUSE_END, df.format(pausesCursor.getDouble(fieldMap.get(PAUSE_END))));

                jsonPauses.put(json);
            } catch (Exception ex) {
                Log.e(MODULE_TAG, ex.getMessage());
            }
            pausesCursor.moveToNext();
        }
        pausesCursor.close();
        mDb.close();
        return jsonPauses;
    }

    private JSONArray getTripResponsesJSON(long tripId) throws JSONException {

        // Create a JSON array to hold all of the answers
        JSONArray jsonAnswers = new JSONArray();

        mDb.openReadOnly();
        try {
            Cursor answers = mDb.fetchTripAnswers(tripId);

            int questionColumn = answers.getColumnIndex(DbAdapter.K_ANSWER_QUESTION_ID);
            int answerColumn = answers.getColumnIndex(DbAdapter.K_ANSWER_ANSWER_ID);
            int otherText = answers.getColumnIndex(DbAdapter.K_ANSWER_OTHER_TEXT);
            String text;

            // Cycle thru the database entries
            while (!answers.isAfterLast()) {

                // For each row, construct a JSON object
                JSONObject json = new JSONObject();

                try {
                    // Place values into the JSON object
                    json.put(FID_QUESTION_ID, answers.getInt(questionColumn));
                    json.put(FID_ANSWER_ID, answers.getInt(answerColumn));

                    if (null != (text = answers.getString(otherText))) {
                        text = text.trim();
                        if (!text.equals("")) {
                            json.put(FID_ANSWER_OTHER_TEXT, text);
                        }
                    }
                    // Place JSON objects into the JSON array
                    jsonAnswers.put(json);
                } catch (Exception ex) {
                    Log.e(MODULE_TAG, ex.getMessage());
                }
                // Move to next row
                answers.moveToNext();
            }
            answers.close();
        } catch (Exception ex) {
            Log.e(MODULE_TAG, ex.getMessage());
        } finally {
            mDb.close();
        }
        return jsonAnswers;
    }

    @SuppressLint("SimpleDateFormat")
    private Vector<String> getTripData(long tripId) {
        Vector<String> tripData = new Vector<String>();
        mDb.openReadOnly();
        Cursor tripCursor = mDb.fetchTrip(tripId);

        String note = tripCursor.getString(tripCursor.getColumnIndex(DbAdapter.K_TRIP_NOTE));
        String purpose = tripCursor.getString(tripCursor.getColumnIndex(DbAdapter.K_TRIP_PURP));
        Double startTime = tripCursor.getDouble(tripCursor.getColumnIndex(DbAdapter.K_TRIP_START));
        Double endTime = tripCursor.getDouble(tripCursor.getColumnIndex(DbAdapter.K_TRIP_END));
        tripCursor.close();
        mDb.close();

        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        tripData.add(note);
        tripData.add(purpose);
        tripData.add(df.format(startTime));
        tripData.add(df.format(endTime));

        return tripData;
    }

    public String getAppVersion() {
        String versionName = "";
        int versionCode = 0;

        try {
            PackageInfo pInfo = mCtx.getPackageManager().getPackageInfo(mCtx.getPackageName(), 0);
            versionName = pInfo.versionName;
            versionCode = pInfo.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

        String systemVersion = Build.VERSION.RELEASE;

        String manufacturer = Build.MANUFACTURER;
        String model = Build.MODEL;
        if (model.startsWith(manufacturer)) {
            return versionName + " (" + versionCode + ") on Android " + systemVersion + " " + capitalize(model);
        } else {
            return versionName + " (" + versionCode + ") on Android " + systemVersion + " "
                    + capitalize(manufacturer) + " " + model;
        }
    }

    private String capitalize(String s) {
        if (s == null || s.length() == 0) {
            return "";
        }
        char first = s.charAt(0);
        if (Character.isUpperCase(first)) {
            return s;
        } else {
            return Character.toUpperCase(first) + s.substring(1);
        }
    }

    private String getPostData(long tripId) throws JSONException {
        JSONObject coords = getCoordsJSON(tripId);
        JSONArray pauses = getPausesJSON(tripId);
        //JSONObject user = getUserJSON();
        JSONArray tripResponses = getTripResponsesJSON(tripId);
        String deviceId = userId;
        Vector<String> tripData = getTripData(tripId);
        String notes = tripData.get(0);
        String purpose = tripData.get(1);
        String startTime = tripData.get(2);

        String codedPostData = "purpose=" + purpose + "&tripid=" + String.valueOf(tripId) +
        // "&user=" + user.toString() +
                "&notes=" + notes + "&coords=" + coords.toString() + "&pauses=" + pauses.toString() + "&version="
                + String.valueOf(kSaveProtocolVersion) + "&start=" + startTime + "&device=" + deviceId
                + "&tripResponses=" + tripResponses.toString();
        return codedPostData;
    }

    private static String convertStreamToString(InputStream is) {
        /*
         * To convert the InputStream to String we use the
         * BufferedReader.readLine() method. We iterate until the BufferedReader
         * return null which means there's no more data to read. Each line will
         * appended to a StringBuilder and returned as String.
         */
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();

        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }

    public static byte[] compress(String string) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream(string.length());
        GZIPOutputStream gos = new GZIPOutputStream(os);
        gos.write(string.getBytes());
        gos.close();
        byte[] compressed = os.toByteArray();
        os.close();
        return compressed;
    }

    boolean uploadOneTrip(long currentTripId) {
        boolean result = false;

        byte[] postBodyDataZipped;

        String postBodyData;
        try {
            postBodyData = getPostData(currentTripId);
        } catch (JSONException ex) {
            ex.printStackTrace();
            return result;
        }

        HttpClient client = new DefaultHttpClient();
        HttpPost postRequest = new HttpPost(POST_URL);

        try {
            // Zip Upload!!!
            postBodyDataZipped = compress(postBodyData);
            postRequest.setHeader("Cycleatl-Protocol-Version", "3");
            postRequest.setHeader("Content-Encoding", "gzip");
            postRequest.setHeader("Content-Type", "application/vnd.cycleatl.trip-v3+form");
            postRequest.setEntity(new ByteArrayEntity(postBodyDataZipped));

            HttpResponse response = client.execute(postRequest);
            String responseString = convertStreamToString(response.getEntity().getContent());

            JSONObject responseData = new JSONObject(responseString);

            String responseStatus = responseData.getString("status");

            if (responseStatus.equals("success")) {
                mDb.open();
                mDb.updateTripStatus(currentTripId, TripData.STATUS_SENT);
                mDb.close();
                result = true;
            } else {
                Log.e(MODULE_TAG, "Server response status: " + responseStatus);
            }
        } catch (IllegalStateException ex) {
            ex.printStackTrace();
            return false;
        } catch (IOException ex) {
            ex.printStackTrace();
            return false;
        } catch (JSONException ex) {
            ex.printStackTrace();
            return false;
        }
        return result;
    }

    @Override
    protected Boolean doInBackground(Long... tripid) {
        // First, send the trip user asked for:
        Boolean result = true;
        if (tripid.length != 0) {
            result = uploadOneTrip(tripid[0]);
        }

        // Then, automatically try and send previously-completed trips
        // that were not sent successfully.
        Vector<Long> unsentTrips = new Vector<Long>();

        mDb.openReadOnly();
        Cursor cur = mDb.fetchUnsentTrips();
        if (cur != null && cur.getCount() > 0) {
            // pd.setMessage("Sent. You have previously unsent trips; submitting those now.");
            while (!cur.isAfterLast()) {
                unsentTrips.add(Long.valueOf(cur.getLong(0)));
                cur.moveToNext();
            }
            cur.close();
        }
        mDb.close();

        for (Long trip : unsentTrips) {
            result &= uploadOneTrip(trip);
        }
        return result;
    }

    @Override
    protected void onPreExecute() {
        Toast.makeText(mCtx.getApplicationContext(), "Submitting. Thanks for using ORcycle!", Toast.LENGTH_LONG)
                .show();
    }

    private SavedTripsAdapter mSavedTripsAdapter;

    public SavedTripsAdapter setSavedTripsAdapter(SavedTripsAdapter mSavedTripsAdapter) {
        this.mSavedTripsAdapter = mSavedTripsAdapter;
        return mSavedTripsAdapter;
    }

    private FragmentSavedTripsSection fragmentSavedTripsSection;

    public FragmentSavedTripsSection setFragmentSavedTripsSection(
            FragmentSavedTripsSection fragmentSavedTripsSection) {
        this.fragmentSavedTripsSection = fragmentSavedTripsSection;
        return fragmentSavedTripsSection;
    }

    private ListView listSavedTrips;

    public ListView setListView(ListView listSavedTrips) {
        this.listSavedTrips = listSavedTrips;
        return listSavedTrips;
    }

    @Override
    protected void onPostExecute(Boolean result) {
        try {
            if (mSavedTripsAdapter != null) {
                mSavedTripsAdapter.notifyDataSetChanged();
            }

            if (fragmentSavedTripsSection != null) {
                listSavedTrips.invalidate();
                fragmentSavedTripsSection.populateTripList(listSavedTrips);
            }
            if (result) {
                Toast.makeText(mCtx.getApplicationContext(), "Trip uploaded successfully.", Toast.LENGTH_SHORT)
                        .show();
            } else {
                Toast.makeText(mCtx.getApplicationContext(),
                        "ORcycle couldn't upload the trip, and will retry when your next trip is completed.",
                        Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            // Just don't toast if the view has gone out of context
        }
    }
}