package course.labs.alarmslab;
import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.IBinder;
import android.util.Base64;
import android.util.Log;
import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
public class AlarmTweetService extends Service {
// Notification ID to allow for future updates
private static final String TAG = "AlarmTweetService";
// TODO -- Pick one of the following user accounts. Change the
// CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, and ACCESS_TOKEN_SECRET
// to the values for your account
// http://www.twitter.com/CMSC436Tester
// Consumer key: S2xDEU4mJyWKEsMEN9c3g
// Consumer secret: Pz7gH6S4ReftwuKVYEmkVW6TqImoIgbEWE9AFeDBWRc
//
// Access Token: 2151769766-ce3mtIH2bDEY8PZy73BKULWFfBqVnx8xHWeYvkY
// Access Token Secret: W7qEJxvEiWhqQmCuvFbKK6emg0cMqf8SxuABc3N33tz5w
// http://www.twitter.com/CMSC436Tester2
// Consumer key: kCgvgTnJ5nDuJmZrV5kTXA
// Consumer secret: B4qDeJOClLCw67w7G08B4FUXBpS0P3qkX0WSbo0Bjgo
//
// Access Token: 2151777866-VTX8oVguPz6xoTqPpQnDRqJJ7DhYRgkowCn9MCv
// Access Token Secret: H3M8VGanx6euQS336BLqItB7saTy7K89CpxlQ4PqpTkQC
//
// http://www.twitter.com/CMSC436Tester3
// Consumer key: qUkfkuK8xKhxGq3BztWlLw
// Consumer secret: cck2aY4390u8Z9hFRfE7TSRYrATUQG0MkB61xEErDg
//
// Access Token: 2151782420-mHjtmHTQiE4L6nDgIhMbzfDYtjE6JFNK2Bm04nq
// Access Token Secret: Wy9SK20WbZnDjXlxBH1gxQRKNCz9Pi4EoMCyJPGrjjJVY
// Consumer Key
private final static String CONSUMER_KEY = "S2xDEU4mJyWKEsMEN9c3g";
// Consumer Secret
private final static String CONSUMER_SECRET = "Pz7gH6S4ReftwuKVYEmkVW6TqImoIgbEWE9AFeDBWRc";
// Access Token
private static String ACCESS_TOKEN = "2151769766-ce3mtIH2bDEY8PZy73BKULWFfBqVnx8xHWeYvkY";
// Access Token Secret
private static String ACCESS_TOKEN_SECRET = "W7qEJxvEiWhqQmCuvFbKK6emg0cMqf8SxuABc3N33tz5w";
// networking variables
private static SecureRandom mRandom;
public AlarmTweetService() {
super();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getExtras() != null){
mRandom = new SecureRandom();
new NetworkingTask().execute(intent.getExtras().getString(AlarmCreateActivity.TWEET_STRING));
}
return START_STICKY;
}
public class NetworkingTask extends AsyncTask<String, Void, Boolean> {
@Override
protected Boolean doInBackground(String... args) {
try {
return postTweet(args[0]);
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG, "COULDN'T GET IT");
}
return false;
}
@Override
protected void onPostExecute(Boolean result) {
Toast.makeText(getApplicationContext(),
"The Tweet was submitted with result:" + result,
Toast.LENGTH_LONG).show();
}
}
private static boolean postTweet(String status) throws IOException {
HttpsURLConnection connection = null;
String nonce = newNonce();
String timestamp = timestamp();
try {
// URL for updating the Twitter status
URL url = new URL(
"https://api.twitter.com/1.1/statuses/update.json");
//TODO - make connection into an HttpsUrlConnection for url above
connection = (HttpsURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Host", "api.twitter.com");
connection.setRequestProperty("User-Agent", "TwitterNetworkingLab");
connection.setRequestProperty("Authorization", "OAuth "
+ "oauth_consumer_key=\"" + CONSUMER_KEY + "\", "
+ "oauth_nonce=\"" + nonce + "\", " + "oauth_signature=\""
+ signature(status, nonce, timestamp) + "\", "
+ "oauth_signature_method=\"HMAC-SHA1\", "
+ "oauth_timestamp=\"" + timestamp + "\", "
+ "oauth_token=\"" + ACCESS_TOKEN + "\", "
+ "oauth_version=\"1.0\"");
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
Log.d(TAG, connection.getRequestProperties().toString());
// This POST request needs a body
// This is how you write to the body
OutputStreamWriter out = new OutputStreamWriter(
connection.getOutputStream());
// Twitter status is added here.
out.write("status=" + URLEncoder.encode(status, "UTF-8"));
out.flush();
out.close();
String test = "status=" + URLEncoder.encode(status, "UTF-8");
Log.i(TAG, test);
JSONObject response = new JSONObject(read(connection));
if (response != null) {
Log.d(TAG, "The tweet seems to have been posted successfully!");
}
return response != null;
} catch (MalformedURLException e) {
throw new IOException("Invalid URL.", e);
} catch (JSONException e) {
throw new IOException("Invalid response from Twitter", e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
private static String read(HttpsURLConnection connection) {
StringBuffer stringBuffer = new StringBuffer();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line = new String();
while ((line = reader.readLine()) != null) {
stringBuffer.append(line + "\n");
}
} catch (IOException e) {
try {
Log.i(TAG,
"Error reponse from twitter: "
+ connection.getResponseMessage());
} catch (IOException e1) {
e1.printStackTrace();
}
stringBuffer.append("Failed");
}
return stringBuffer.toString();
}
private static String newNonce() {
byte[] random = new byte[32];
mRandom.nextBytes(random);
String nonce = Base64.encodeToString(random, Base64.NO_WRAP);
nonce = nonce.replaceAll("[^a-zA-Z0-9\\s]", "");
return nonce;
}
private static String timestamp() {
Long time = System.currentTimeMillis() / 1000L;
return time.toString();
}
private static String signature(String status, String nonce,
String timestamp) {
String output = "";
// Obviously you can use a TreeMap with concurrency to implement this,
// but this way you can see what is going on
try {
// Gather all parameters used for the status post
String signatureBaseString = "";
String parameterString = "";
String signingKey = "";
String signature = "";
String httpMethod = "POST";
String baseURL = "https://api.twitter.com/1.1/statuses/update.json";
String consumerKey = CONSUMER_KEY;
String encMethod = "HMAC-SHA1";
String token = ACCESS_TOKEN;
String version = "1.0";
String kStatus = "status";
String kKey = "oauth_consumer_key";
String kNonce = "oauth_nonce";
String kSigMeth = "oauth_signature_method";
String kTimestamp = "oauth_timestamp";
String kToken = "oauth_token";
String kVersion = "oauth_version";
// encode the parameters put into the http request header separately
consumerKey = URLEncoder.encode(consumerKey, "UTF-8");
nonce = URLEncoder.encode(nonce, "UTF-8");
encMethod = URLEncoder.encode(encMethod, "UTF-8");
timestamp = URLEncoder.encode(timestamp, "UTF-8");
token = URLEncoder.encode(token, "UTF-8");
version = URLEncoder.encode(version, "UTF-8");
status = URLEncoder.encode(status, "UTF-8");
status = status.replace("+", "%20"); // URLEncoder encodes " " to
// "+", which is incorrect
// for twitter encoding
kKey = URLEncoder.encode(kKey, "UTF-8");
kNonce = URLEncoder.encode(kNonce, "UTF-8");
kSigMeth = URLEncoder.encode(kSigMeth, "UTF-8");
kTimestamp = URLEncoder.encode(kTimestamp, "UTF-8");
kToken = URLEncoder.encode(kToken, "UTF-8");
kVersion = URLEncoder.encode(kVersion, "UTF-8");
kStatus = URLEncoder.encode(kStatus, "UTF-8");
// append the parameters together with = and &
parameterString = kKey + "=" + consumerKey + "&" + kNonce + "="
+ nonce + "&" + kSigMeth + "=" + encMethod + "&"
+ kTimestamp + "=" + timestamp + "&" + kToken + "=" + token
+ "&" + kVersion + "=" + version + "&" + kStatus + "="
+ status;
// build the signature base string
signatureBaseString += httpMethod;
signatureBaseString += "&";
baseURL = URLEncoder.encode(baseURL, "UTF-8");
signatureBaseString += baseURL;
signatureBaseString += "&";
parameterString = URLEncoder.encode(parameterString, "UTF-8");
signatureBaseString += parameterString;
Log.d(TAG, signatureBaseString);
// generate the HMAC-SHA1 signing key with the application and
// account secrets
signingKey = URLEncoder.encode(CONSUMER_SECRET, "UTF-8") + "&"
+ URLEncoder.encode(ACCESS_TOKEN_SECRET, "UTF-8");
// this should be self explanatory
byte[] bytes = signingKey.getBytes();
Key key = new SecretKeySpec(bytes, 0, bytes.length, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(key);
byte[] signatureBytes = mac.doFinal(signatureBaseString.getBytes());
signature = Base64.encodeToString(signatureBytes, Base64.NO_WRAP);
signature = URLEncoder.encode(signature, "UTF-8");
output = signature;
} catch (UnsupportedEncodingException e) {
Log.d(TAG, "Error encoding parameters");
e.printStackTrace();
} catch (InvalidKeyException e) {
Log.d(TAG, "Error encrypting signature");
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
Log.d(TAG, "Invalid algorithm for signature");
e.printStackTrace();
}
return output;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}