package com.umich.umd.obdpractice;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.InputStreamEntity;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableNotifiedException;
import com.google.api.client.http.InputStreamContent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
public class PutHTTPTask extends AsyncTask<String, Void, String> {
// Debug tag for identifying from which activity debug message
// originated
private static final String DEBUG_TAG = "PutAsyncUpload";
/** E-mail address of the service account. */
private static String SERVICE_ACCOUNT_EMAIL;
/** Google Cloud Storage URI */
private static final String GCS_URI = "http://storage.googleapis.com/";
private static final String HOST = "storage.googleapis.com";
/** Bucket to list. */
private static final String BUCKET_NAME = "obd_data";
/** Global configuration of Google Cloud Storage OAuth 2.0 scope. */
private static final String STORAGE_SCOPE = "oauth2:https://www.googleapis.com/auth/devstorage.read_write";
/*
* /** Global instance of the HTTP transport. private static final
* HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
*
* /** Global instance of the JSON factory. private static final JsonFactory
* JSON_FACTORY = new JacksonFactory();
*
* /** Information for making application based calls to Google APIs private
* static final String CALLBACK_URL = "urn:ietf:wg:oauth:2.0:oob"; private
* static final String SIMPLE_API_KEY =
* "AIzaSyCQ492-1MwRlAI2zKRCv0kAXfFHQX9Q0S4";
*
* private static final String CLIENT_ID =
* "809398875393.apps.googleusercontent.com"; private static final String
* CLIENT_SECRET =
* "{\"installed\":{\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\","
* +
* "\"token_uri\":\"https://accounts.google.com/o/oauth2/token\",\"client_email\":\"\",\"redirect_uris\":"
* +
* "[\"urn:ietf:wg:oauth:2.0:oob\",\"oob\"],\"client_x509_cert_url\":\"\",\"client_id\":"
* +
* "\"809398875393.apps.googleusercontent.com\",\"auth_provider_x509_cert_url\":"
* + "\"https://www.googleapis.com/oauth2/v1/certs\"}}";
*/
private final Context cloudContext;
private final CloudFileUploadActivity cupActivity;
public PutHTTPTask(CloudFileUploadActivity cupAct, Context cloudActContext,
String authenticationEmail) {
super();
SERVICE_ACCOUNT_EMAIL = authenticationEmail;
this.cloudContext = cloudActContext;
cupActivity = cupAct;
}
@Override
protected String doInBackground(String... fileNames) {
String output = null;
for (String fN : fileNames) {
try {
output = fileUpload(fN, cloudContext);
} catch (IOException e) {
e.printStackTrace();
}
}
//cupActivity.updateDisplay(output);
return output;
}
/**
* Method to perform upload of file to the Google Cloud Storage Executes
* token authentication, put file into cloud, and grabbing response
*
* @param fileName
* The name of the file to upload to the cloud
* @param cloudActContext
* The Conext of the CloudUploadFile Activity
* @return The response from the post request in string format
* @throws IOException
*/
public String fileUpload(String fileName, Context cloudActContext)
throws IOException {
/*
* Preconditions.checkArgument( !SERVICE_ACCOUNT_EMAIL.startsWith("[["),
* "Please enter your service account e-mail from the Google APIs " +
* "Console to the SERVICE_ACCOUNT_EMAIL constant in %s",
* CloudManager.class.getName());
* Preconditions.checkArgument(!BUCKET_NAME.startsWith("[["),
* "Please enter your desired Google Cloud Storage bucket name " +
* "to the BUCKET_NAME constant in %s", CloudManager.class.getName());
* String p12Content = Files.readFirstLine(new File("key.p12"),
* Charset.defaultCharset());
* Preconditions.checkArgument(!p12Content.startsWith("Please"),
* p12Content);
*/
// String authorizeURL = new GoogleAuthorizationRequestURL(CLIENT_ID,
// CALLBACK_URL,STORAGE_SCOPE);
// Fetch Google Authentication Token
String token = fetchToken();
Log.d(DEBUG_TAG, fileName);
// input stream from file to upload to cloud
FileInputStream fis = null;
// Will hold size of the file
long byteCount;
// InputStreamEntity for AndroidHttpClient
// InputStreamEntity fileStreamE = null;
// HttpURL Connection and DataOutputStream for
// Streaming large file chunks in streaming mode
HttpURLConnection httpConnection = null;
DataOutputStream outputStream = null;
int bytesRead, bytesAvailable, bufferSize;
byte[] upBuffer;
int maxBufferSize = 1 * 1024;
final SimpleDateFormat sdf = new SimpleDateFormat(
"EEE, dd MMM yyyy HH:mm:ss z",Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
URL url = null;
/**
* ---- Beginning of Actual Connection and Upload Process ----
*/
try {
// Open input stream from to upload
fis = cloudActContext.openFileInput(fileName);
// Get the size of the file for transmission purposes
byteCount = fis.getChannel().size();
Log.d(DEBUG_TAG, "File Size: " + byteCount);
// Create new HttpContent entity from file Input Stream
// for AndroidHttpClient
//fileStreamE = new InputStreamEntity(fis, -1);
String URI = GCS_URI + BUCKET_NAME + "/" + fileName;
url = new URL(URI);
URI uri = new URI(url.getProtocol(), url.getUserInfo(),
url.getHost(), url.getPort(), url.getPath(),
url.getQuery(), url.getRef());
url = uri.toURL();
URI = uri.toString();
// /**
// * Setup HttpClient and Post connection with appropriate headers
// for
// * Cloud Connection
// */
// AndroidHttpClient client = AndroidHttpClient.newInstance("OBD");
// Log.d(DEBUG_TAG, URI);
// HttpPut put = new HttpPut(URI);
// Log.d(DEBUG_TAG, "OAuth " + token);
// put.setHeader("Authorization", "OAuth " + token);
// put.setHeader("Content-Length", Long.toString(byteCount));
// Calendar cal = new
// GregorianCalendar(TimeZone.getTimeZone("GMT"));
// cal.setTimeZone(TimeZone.getTimeZone("GMT"));
// String dateStr = cal.getTime().toString();
// Log.d(DEBUG_TAG, dateStr);
// put.setHeader("Date", dateStr);
// Log.d(DEBUG_TAG, HOST);
// put.setHeader("Host", HOST);
// put.setHeader("Content-Type", "text/plain");
// put.setHeader("x-goog-api-version", "2");
// put.setEntity(fileStreamE);
// Log.d(DEBUG_TAG, put.toString());
// Log.d(DEBUG_TAG, put.getRequestLine().toString());
//
// HttpResponse response = client.execute(put);
/**
* HttpURLConnection based version. Stopped due to problems
* appending data to put request
*/
httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.setRequestMethod("PUT");
httpConnection.setDoInput(true);
httpConnection.setDoOutput(true);
Log.d(DEBUG_TAG, "OAuth " + token);
httpConnection
.addRequestProperty("Authorization", "OAuth " + token);
httpConnection.addRequestProperty("Content-Length",
Long.toString(byteCount));
// format time using SimpleDateFormat from above
String dateStr = sdf.format(new Date());
Log.d(DEBUG_TAG, dateStr);
httpConnection.addRequestProperty("Date", dateStr);
httpConnection.addRequestProperty("Host", HOST);
httpConnection.addRequestProperty("Content-Type", "text/plain");
httpConnection.addRequestProperty("x-goog-api-version", "2");
// httpConnection.addRequestProperty("Connection", "Keep-Alive");
outputStream = new DataOutputStream(
httpConnection.getOutputStream());
bytesAvailable = fis.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
upBuffer = new byte[bufferSize];
bytesRead = fis.read(upBuffer, 0, bufferSize);
Log.d(DEBUG_TAG + " upLength", bytesAvailable + "");
try {
while (bytesRead > 0) {
try {
outputStream.write(upBuffer, 0, bufferSize);
} catch (OutOfMemoryError e) {
e.printStackTrace();
return "OutOfMemoryError on Upload";
}
bytesAvailable = fis.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fis.read(upBuffer, 0, bufferSize);
}
} catch (Exception e) {
e.printStackTrace();
return "Unhandled Exception Error";
} finally {
outputStream.flush();
outputStream.close();
outputStream = null;
}
/**
* HttpURLConnection version of response handler
* Consider using httpConnection.getContent() in future versions
*/
int respCode = httpConnection.getResponseCode();
Log.i("GCS_RC", "" + respCode);
if (respCode == 200) {
onSuccess(""+respCode);
getHeaders(httpConnection.getHeaderFields());
// Get URLConnection input stream
InputStream is = httpConnection.getInputStream();
// Use thread-safe string buffer to build response
StringBuffer responseString = new StringBuffer("");
BufferedReader buffer = new BufferedReader(
new InputStreamReader(is));
String s = "";
while ((s = buffer.readLine()) != null)
responseString.append(s + "\n");
Log.d(DEBUG_TAG, "Response: " + responseString.toString());
return responseString.toString();
} else if (respCode == 401) {
GoogleAuthUtil.invalidateToken(cupActivity, token);
onError("Server auth error, please try again.", null);
Log.e(DEBUG_TAG, "Server auth error: "
+ httpConnection.getResponseMessage());
return null;
} else {
onError("Server returned code: " + respCode,null);
Log.e(DEBUG_TAG, "Server returned the following error code: "
+ respCode);
Log.e(DEBUG_TAG, "Connection Error: "
+ httpConnection.getResponseMessage());
return null;
}
/**
* AndroidHttpClient version of Response Handler
*/
/*int respCode = response.getStatusLine().getStatusCode();
if (respCode == 200) {
InputStream is = response.getEntity().getContent();
StringBuffer responseString = new StringBuffer("");
BufferedReader buffer = new BufferedReader(
new InputStreamReader(is));
String s = "";
while ((s = buffer.readLine()) != null)
responseString.append(s + "\n");
return responseString.toString();
} else if (respCode == 401) {
GoogleAuthUtil.invalidateToken(cupActivity, token);
onError("Server auth error, please try again.", null);
Log.e(DEBUG_TAG, "Server auth error: "
+ response.getStatusLine().getReasonPhrase());
return null;
} else {
Log.e(DEBUG_TAG, "Server returned the following error code: "
+ respCode);
return null;
}*/
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
} finally {
if (fis != null)
fis.close();
}
return null;
}
private void getHeaders(Map<String, List<String>> headerFields) {
cupActivity.updateDisplay(headerFields);
}
/**
* Grab Google OAuth 2.0 Authentication token using Google Play Services API
* for Google Cloud Storage
*
* @return The authentication token string to append to request header/query
* @throws IOException
* Don't know when an IOException might be thrown
*/
protected String fetchToken() throws IOException {
try {
return GoogleAuthUtil.getTokenWithNotification(cloudContext,
SERVICE_ACCOUNT_EMAIL, STORAGE_SCOPE, null,
makeCallback(SERVICE_ACCOUNT_EMAIL));
} catch (UserRecoverableNotifiedException userRecoverableException) {
// Unable to authenticate, but the user can fix this.
// Forward the user to the appropriate activity.
onError("Could not fetch token.", null);
} catch (GoogleAuthException fatalException) {
onError("Unrecoverable error " + fatalException.getMessage(),
fatalException);
}
return null;
}
/**
* Callback function to CloudFileUploadActivity if authentication process
* throws an unrecoverable error
*
* @param sERVICE_ACCOUNT_EMAIL2
* The email account to be authenticated
* @return An intent containing information of activity to make callback to
*/
private Intent makeCallback(String sERVICE_ACCOUNT_EMAIL2) {
Intent intent = new Intent();
intent.setAction(" om.umich.umd.obdpractice.Callback");
intent.putExtra(CloudManager.ACCOUNT_TAG, sERVICE_ACCOUNT_EMAIL2);
return intent;
}
protected void onError(String msg, Exception e) {
if (e != null) {
Log.e(DEBUG_TAG, "Exception: ", e);
}
cupActivity.show(msg); // will be run in UI thread
}
protected void onSuccess(String respCode){
cupActivity.show(respCode);
}
/**
* Note: Make sure that the receiver can be called from outside the app. You
* can do that by adding android:exported="true" in the manifest file.
*/
public static class CallbackReceiver extends BroadcastReceiver {
public static final String TAG = "CallbackReceiver";
@Override
public void onReceive(Context context, Intent callback) {
Bundle extras = callback.getExtras();
Intent intent = new Intent(context, CloudFileUploadActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtras(extras);
Log.i(TAG, "Received broadcast. Resurrecting activity");
context.startActivity(intent);
}
}
}