Java tutorial
/** * ORcycle, Copyright 2014, 2015, PSU Transportation, Technology, and People Lab. * * @author Robin Murray <robin5@pdx.edu> (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/>. * */ package edu.pdx.cecs.orcycle; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.Map; import java.util.Map.Entry; 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.content.Context; import android.content.SharedPreferences; import android.os.AsyncTask; import android.util.Log; import android.widget.Toast; public class UserInfoUploader extends AsyncTask<Long, Integer, Boolean> { private static final String MODULE_TAG = "UserInfoUploader"; private static final int kSaveProtocolVersion3 = 3; private static final int kSaveProtocolVersion4 = 4; private static final String boundary = "cycle*******notedata*******atlanta"; private static final String lineEnd = "\r\n"; private static final String fieldSep = "--cycle*******notedata*******atlanta\r\n"; 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"; public static final String USER_EMAIL = "email"; public static final String USER_INSTALLED = "installed"; public static final String USER_DEVICE_MODEL = "deviceModel"; public static final String USER_APP_VERSION = "app_version"; private String email; private String installed; // DateTime private String deviceModel; private String appVersion; private Context mCtx = null; private String userId = null; public UserInfoUploader(Context ctx, String userId) { super(); this.mCtx = ctx; this.userId = userId; } /** * * @return * @throws JSONException */ private JSONObject getUserJSON() throws JSONException { JSONObject userJson = new JSONObject(); userJson.put(USER_EMAIL, email); userJson.put(USER_INSTALLED, installed); userJson.put(USER_DEVICE_MODEL, deviceModel); userJson.put(USER_APP_VERSION, appVersion); return userJson; } /** * * @return * @throws JSONException */ private JSONArray getUserResponsesJSON() throws JSONException { // Create a JSON array to hold all of the answers JSONArray jsonAnswers = new JSONArray(); SharedPreferences settings = mCtx.getSharedPreferences(UserInfoActivity.PREFS_USER_INFO_UPLOAD, Context.MODE_PRIVATE); Map<String, ?> prefs = settings.getAll(); String riderTypeOther = null; String occupationOther = null; String bikeTypeOther = null; String genderOther = null; String ethnicityOther = null; for (Entry<String, ?> p : prefs.entrySet()) { int key = Integer.parseInt(p.getKey()); // CharSequence value = (CharSequence) p.getValue(); switch (key) { case UserInfoActivity.PREF_RIDER_TYPE_OTHER: riderTypeOther = (String) p.getValue(); break; case UserInfoActivity.PREF_OCCUPATION_OTHER: occupationOther = (String) p.getValue(); break; case UserInfoActivity.PREF_BIKE_TYPE_OTHER: bikeTypeOther = (String) p.getValue(); break; case UserInfoActivity.PREF_GENDER_OTHER: genderOther = (String) p.getValue(); break; case UserInfoActivity.PREF_ETHNICITY_OTHER: ethnicityOther = (String) p.getValue(); break; } } for (Entry<String, ?> p : prefs.entrySet()) { int key = Integer.parseInt(p.getKey()); // CharSequence value = (CharSequence) p.getValue(); switch (key) { case UserInfoActivity.PREF_EMAIL: email = (String) p.getValue(); break; case UserInfoActivity.PREF_RIDER_ABILITY: putInt(jsonAnswers, DbQuestions.USER_INFO_RIDER_ABILITY, DbAnswers.userInfoRiderAbility, -1, null, p); break; case UserInfoActivity.PREF_RIDER_TYPE: putInt(jsonAnswers, DbQuestions.USER_INFO_RIDER_TYPE, DbAnswers.userInfoRiderType, DbAnswers.userInfoRiderTypeOther, riderTypeOther, p); break; case UserInfoActivity.PREF_CYCLE_FREQUENCY: putInt(jsonAnswers, DbQuestions.USER_INFO_CYCLING_FREQ, DbAnswers.userInfoCyclingFreq, -1, null, p); break; case UserInfoActivity.PREF_CYCLE_WEATHER: putInt(jsonAnswers, DbQuestions.USER_INFO_CYCLING_WEATHER, DbAnswers.userInfoCyclingWeather, -1, null, p); break; case UserInfoActivity.PREF_NUM_BIKES: putInt(jsonAnswers, DbQuestions.USER_INFO_NUM_BIKES, DbAnswers.userInfoNumBikes, -1, null, p); break; case UserInfoActivity.PREF_BIKE_TYPES: putMultiInt(jsonAnswers, DbQuestions.USER_INFO_BIKE_TYPES, DbAnswers.userInfoBikeTypes, DbAnswers.userInfoBikeTypeOther, bikeTypeOther, p); break; case UserInfoActivity.PREF_OCCUPATION: putInt(jsonAnswers, DbQuestions.USER_INFO_OCCUPATION, DbAnswers.userInfoOccupation, DbAnswers.userInfoOccupationOther, occupationOther, p); break; case UserInfoActivity.PREF_AGE: putInt(jsonAnswers, DbQuestions.USER_INFO_AGE, DbAnswers.userInfoAge, -1, null, p); break; case UserInfoActivity.PREF_GENDER: putInt(jsonAnswers, DbQuestions.USER_INFO_GENDER, DbAnswers.userInfoGender, DbAnswers.userInfoGenderOther, genderOther, p); break; case UserInfoActivity.PREF_VEHICLES: putInt(jsonAnswers, DbQuestions.USER_INFO_VEHICLES, DbAnswers.userInfoHHVehicles, -1, null, p); break; case UserInfoActivity.PREF_WORKERS: putInt(jsonAnswers, DbQuestions.USER_INFO_WORKERS, DbAnswers.userInfoHHWorkers, -1, null, p); break; case UserInfoActivity.PREF_ETHNICITY: putInt(jsonAnswers, DbQuestions.USER_INFO_ETHNICITY, DbAnswers.userInfoEthnicity, DbAnswers.userInfoEthnicityOther, ethnicityOther, p); break; case UserInfoActivity.PREF_INCOME: putInt(jsonAnswers, DbQuestions.USER_INFO_INCOME, DbAnswers.userInfoIncome, -1, null, p); break; case UserInfoActivity.PREF_INSTALLED: installed = (String) p.getValue(); break; case UserInfoActivity.PREF_DEVICE_MODEL: deviceModel = (String) p.getValue(); break; case UserInfoActivity.PREF_APP_VERSION: appVersion = (String) p.getValue(); break; } } return jsonAnswers; } /** * * @param jsonAnswers * @param questionId * @param answers * @param entry */ private void putInt(JSONArray jsonAnswers, int questionId, int[] answers, int otherId, String otherText, Entry<String, ?> entry) { try { int index = ((Integer) entry.getValue()).intValue(); if (index >= 0) { int answer = answers[index]; JSONObject json = new JSONObject(); // Place values into the JSON object json.put(FID_QUESTION_ID, questionId); json.put(FID_ANSWER_ID, answer); // Assure that whenever other is selected, there is an entry for other text if ((-1 != otherId) && (answers[index] == otherId)) { if (null == otherText) { json.put(FID_ANSWER_OTHER_TEXT, ""); } else { json.put(FID_ANSWER_OTHER_TEXT, otherText); } } // Place JSON objects into the JSON array jsonAnswers.put(json); } } catch (Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } } /** * * @param jsonAnswers * @param questionId * @param answers * @param entry */ private void putMultiInt(JSONArray jsonAnswers, int questionId, int[] answers, int otherId, String otherText, Entry<String, ?> entry) { try { String text = (String) entry.getValue(); String[] selections = text.split(","); int index; for (String selection : selections) { try { index = Integer.parseInt(selection); if (index >= 0 && index < answers.length) { int answer = answers[index]; JSONObject json = new JSONObject(); // Place values into the JSON object json.put(FID_QUESTION_ID, questionId); json.put(FID_ANSWER_ID, answer); // Assure that whenever other is selected, there is an entry for other text if ((-1 != otherId) && (answers[index] == otherId)) { if (null == otherText) { json.put(FID_ANSWER_OTHER_TEXT, ""); } else { json.put(FID_ANSWER_OTHER_TEXT, otherText); } } // Place JSON objects into the JSON array jsonAnswers.put(json); } else { Log.e(MODULE_TAG, "Index " + index + " is out of bounds."); } } catch (Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } } } catch (Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } } private void putString(JSONArray jsonAnswers, int questionId, int[] answers, Entry<String, ?> entry) { try { JSONObject json = new JSONObject(); String text = (String) entry.getValue(); int answer_id = answers[0]; if (null != text) { text = text.trim(); if (!text.equals("")) { // Place values into the JSON object json.put(FID_QUESTION_ID, questionId); json.put(FID_ANSWER_ID, answer_id); 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()); } } private String getPostDataV3() throws JSONException { JSONArray userResponses = getUserResponsesJSON(); JSONObject user = getUserJSON(); String deviceId = userId; String codedPostData = "&user=" + user.toString() + "&userRespones=" + userResponses.toString() + "&version=" + String.valueOf(kSaveProtocolVersion3) + "&device=" + deviceId; 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 uploadUserInfoV3() { boolean result = false; byte[] postBodyDataZipped; String postBodyData; try { postBodyData = getPostDataV3(); } catch (JSONException e) { e.printStackTrace(); return result; } HttpClient client = new DefaultHttpClient(); final String postUrl = mCtx.getResources().getString(R.string.post_url); HttpPost postRequest = new HttpPost(postUrl); try { // Zip Upload!!! Log.v(MODULE_TAG, "postBodyData: " + postBodyData.toString()); Log.v(MODULE_TAG, "postBodyData Length: " + postBodyData.length()); postBodyDataZipped = compress(postBodyData); Log.v(MODULE_TAG, "postBodyDataZipped: " + postBodyDataZipped); Log.v(MODULE_TAG, "postBodyDataZipped Length: " + String.valueOf(postBodyDataZipped.length)); Log.v(MODULE_TAG, "Initializing HTTP POST request to " + postUrl + " of size " + String.valueOf(postBodyDataZipped.length) + " orig size " + postBodyData.length()); postRequest.setHeader("Cycleatl-Protocol-Version", "3"); postRequest.setHeader("Content-Encoding", "gzip"); postRequest.setHeader("Content-Type", "application/vnd.cycleatl.trip-v3+form"); // postRequest.setHeader("Content-Length",String.valueOf(postBodyDataZipped.length)); postRequest.setEntity(new ByteArrayEntity(postBodyDataZipped)); HttpResponse response = client.execute(postRequest); String responseString = convertStreamToString(response.getEntity().getContent()); // Log.v("httpResponse", responseString); JSONObject responseData = new JSONObject(responseString); if (responseData.getString("status").equals("success")) { // TODO: Record somehow that data was uploaded successfully result = true; } } catch (IllegalStateException e) { e.printStackTrace(); return false; } catch (IOException e) { e.printStackTrace(); return false; } catch (JSONException e) { e.printStackTrace(); return false; } return result; } boolean uploadUserInfoV4() { boolean result = false; final String postUrl = mCtx.getResources().getString(R.string.post_url); try { JSONArray userResponses = getUserResponsesJSON(); URL url = new URL(postUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoInput(true); // Allow Inputs conn.setDoOutput(true); // Allow Outputs conn.setUseCaches(false); // Don't use a Cached Copy conn.setRequestMethod("POST"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.setRequestProperty("ENCTYPE", "multipart/form-data"); conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); conn.setRequestProperty("Cycleatl-Protocol-Version", "4"); DataOutputStream dos = new DataOutputStream(conn.getOutputStream()); JSONObject jsonUser; if (null != (jsonUser = getUserJSON())) { try { String deviceId = userId; dos.writeBytes(fieldSep + ContentField("user") + jsonUser.toString() + "\r\n"); dos.writeBytes( fieldSep + ContentField("version") + String.valueOf(kSaveProtocolVersion4) + "\r\n"); dos.writeBytes(fieldSep + ContentField("device") + deviceId + "\r\n"); dos.writeBytes(fieldSep + ContentField("userResponses") + userResponses.toString() + "\r\n"); dos.writeBytes(fieldSep); dos.flush(); } catch (Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); return false; } finally { dos.close(); } int serverResponseCode = conn.getResponseCode(); String serverResponseMessage = conn.getResponseMessage(); // JSONObject responseData = new JSONObject(serverResponseMessage); Log.v("Jason", "HTTP Response is : " + serverResponseMessage + ": " + serverResponseCode); if (serverResponseCode == 201 || serverResponseCode == 202) { // TODO: Record somehow that data was uploaded successfully result = true; } } else { result = false; } } catch (IllegalStateException e) { e.printStackTrace(); return false; } catch (IOException e) { e.printStackTrace(); return false; } catch (JSONException e) { e.printStackTrace(); return false; } return result; } private String ContentField(String type) { return "Content-Disposition: form-data; name=\"" + type + "\"\r\n\r\n"; } @Override protected Boolean doInBackground(Long... dummy) { Boolean result = false; try { //result = uploadUserInfoV4(); result = uploadUserInfoV4(); } catch (Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } return result; } @Override protected void onPreExecute() { Toast.makeText(mCtx.getApplicationContext(), "Submitting. Thanks for using ORcycle!", Toast.LENGTH_LONG) .show(); } @Override protected void onPostExecute(Boolean result) { try { if (result) { Toast.makeText(mCtx.getApplicationContext(), "User information uploaded successfully.", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(mCtx.getApplicationContext(), "ORcycle couldn't upload the information.", Toast.LENGTH_LONG).show(); } } catch (Exception e) { // Just don't toast if the view has gone out of context } } }