Skip to content
Snippets Groups Projects
Commit a332c87e authored by Andrej Rasevic's avatar Andrej Rasevic
Browse files

added Lab7_Alarms student distribution

parent 9f7fd94c
Branches
No related tags found
No related merge requests found
Showing
with 628 additions and 0 deletions
# built application files
*.apk
*.ap_
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Eclipse project files
.classpath
.settings
# Proguard folder generated by Eclipse
proguard/
# Intellij project files
*.iml
*.ipr
*.iws
.idea
.idea/workspace.xml
.gradle
build/
captures/
# Mac files
.DS_Store
# Windows thumbnail db
Thumbs.db
File added
File added
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
defaultConfig {
applicationId "course.labs.alarmslab"
minSdkVersion 21
targetSdkVersion 26
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
dependencies {
compile 'com.android.support:support-v4:26.0.1'
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="course.labs.alarmslab">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light">
<activity
android:name="course.labs.alarmslab.AlarmCreateActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".AlarmTweetService"></service>
</application>
</manifest>
package course.labs.alarmslab;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
public class AlarmCreateActivity extends Activity {
// TODO look at these class/instance variables and see if you could have come up with them
// on your own.
public static final String TWEET_STRING = "TWEET";
private AlarmManager mAlarmManager;
private static final String TAG = "AlarmCreateActivity";
private EditText mTweetTextView, mDelayTextView;
private int mID;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO create reference to ALARM_SERVICE as well as locate necessary text views
}
public void set(View v) {
String tweetText = mTweetTextView.getText().toString();
Long delay = Integer.parseInt(mDelayTextView.getText().toString()) * 1000L;
// TODO create Intents for the alarm service. Additionally you will need to create a
// pending intent that will use the original intent you create to start the AlarmTweetService.
// Use the PendingIntent.getService() method. Be aware you will need to pass a unique value
// for the request code (why?) as well as the flag PendingIntent.FLAG_ONE_SHOT. Why? Could
// be possible midterm questions
// TODO make log statement that tweet was sent to AlarmTweetService
// TODO use AlarmManager set method to set Alarm
}
public void clear(View v) {
// TODO clear views
}
}
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;
}
}
\ No newline at end of file
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_vertical_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".AlarmCreateActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title"
android:paddingBottom="5dp"
android:textSize="20sp" />
<EditText
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="@string/placeholder"
android:inputType="textMultiLine" >
<requestFocus />
</EditText>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="25dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/prompt" />
<EditText
android:id="@+id/time"
android:layout_width="15dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:inputType="number"
android:minLines="1"
android:text="@string/placeholder2" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/prompt2" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="horizontal"
android:paddingTop="25dp" >
<Button
android:id="@+id/set"
style="@android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button"
android:onClick="set" />
<Button
android:id="@+id/clear"
style="@android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/clear"
android:onClick="clear" />
</LinearLayout>
</LinearLayout>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:showAsAction="never"
android:title="@string/action_settings"/>
</menu>
Lab7_Alarms/app/src/main/res/mipmap-hdpi/ic_launcher.png

7.48 KiB

Lab7_Alarms/app/src/main/res/mipmap-mdpi/ic_launcher.png

3.69 KiB

Lab7_Alarms/app/src/main/res/mipmap-xhdpi/ic_launcher.png

12.2 KiB

Lab7_Alarms/app/src/main/res/mipmap-xxhdpi/ic_launcher.png

24.2 KiB

File added
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">AlarmsLab</string>
<string name="action_settings">Settings</string>
<string name="title">Change your Twitter Status</string>
<string name="placeholder">tweet goes here.</string>
<string name="prompt">To post in</string>
<string name="prompt2">seconds</string>
<string name="placeholder2">5</string>
<string name="button">Submit</string>
<string name="clear">Clear</string>
</resources>
<resources>
<!--
Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Light">
<!--
Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
backward-compatibility can go here.
-->
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
</resources>
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
google()
maven {
url "https://maven.google.com"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
}
}
allprojects {
repositories {
jcenter()
google()
}
}
File added
#Thu Oct 05 22:40:13 EDT 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment