/**
* 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.DataOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import java.util.Map.Entry;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.util.Log;
import android.widget.Toast;
public class UserFeedbackUploader extends AsyncTask<Long, Integer, Boolean> {
private static final String MODULE_TAG = "UserFeedbackUploader";
private static final int kSaveProtocolVersion4 = 4;
private static final String boundary = "cycle*******notedata*******atlanta";
private static final String fieldSep = "--cycle*******notedata*******atlanta\r\n";
public static final String USER_FEEDBACK = "feedback";
private final String userId;
private Context mCtx = null;
public UserFeedbackUploader(Context ctx, String userId) {
super();
this.mCtx = ctx;
this.userId = userId;
}
/**
*
* @return
* @throws JSONException
*/
private JSONObject getUserJSON() throws JSONException {
String userFeedback = null;
JSONObject userJson = null;
SharedPreferences settings = mCtx.getSharedPreferences(UserFeedbackActivity.PREFS_USER_FEEDBACK_UPLOAD, Context.MODE_PRIVATE);
Map<String, ?> prefs = settings.getAll();
for (Entry<String, ?> p : prefs.entrySet()) {
int key = Integer.parseInt(p.getKey());
// CharSequence value = (CharSequence) p.getValue();
switch (key) {
case UserFeedbackActivity.PREF_FEEDBACK:
userFeedback = (String) p.getValue();
break;
}
}
if (null != userFeedback) {
if (null != (userJson = new JSONObject())) {
userJson.put(USER_FEEDBACK, userFeedback);
}
}
return userJson;
}
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);
}
}
boolean uploadUserInfoV4() {
boolean result = false;
final String postUrl = mCtx.getResources().getString(R.string.post_url);
try {
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);
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();
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
return result;
}
@Override
protected void onPreExecute() {
Toast.makeText(mCtx.getApplicationContext(),
"Submitting feedback. Thanks for using ORcycle!",
Toast.LENGTH_LONG).show();
}
@Override
protected void onPostExecute(Boolean result) {
try {
if (result) {
Toast.makeText(mCtx.getApplicationContext(),
"User feedback 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
}
}
}