diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..9be5dbf
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+TechneauxMobileApp
\ No newline at end of file
diff --git a/.idea/TechneauxMobileApp.iml b/.idea/TechneauxMobileApp.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/TechneauxMobileApp.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..7e63416
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..ca8d54d
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..6d4fafa
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1445364525486
+
+ 1445364525486
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/.gitignore b/Android/TechneauxMobileApp/.gitignore
new file mode 100644
index 0000000..9c4de58
--- /dev/null
+++ b/Android/TechneauxMobileApp/.gitignore
@@ -0,0 +1,7 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
diff --git a/Android/TechneauxMobileApp/.idea/.name b/Android/TechneauxMobileApp/.idea/.name
new file mode 100644
index 0000000..9be5dbf
--- /dev/null
+++ b/Android/TechneauxMobileApp/.idea/.name
@@ -0,0 +1 @@
+TechneauxMobileApp
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/.idea/compiler.xml b/Android/TechneauxMobileApp/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Android/TechneauxMobileApp/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/.idea/copyright/profiles_settings.xml b/Android/TechneauxMobileApp/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Android/TechneauxMobileApp/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/.idea/encodings.xml b/Android/TechneauxMobileApp/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Android/TechneauxMobileApp/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/.idea/gradle.xml b/Android/TechneauxMobileApp/.idea/gradle.xml
new file mode 100644
index 0000000..5f8ec24
--- /dev/null
+++ b/Android/TechneauxMobileApp/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/.idea/misc.xml b/Android/TechneauxMobileApp/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Android/TechneauxMobileApp/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/.idea/modules.xml b/Android/TechneauxMobileApp/.idea/modules.xml
new file mode 100644
index 0000000..a78ec89
--- /dev/null
+++ b/Android/TechneauxMobileApp/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/.idea/runConfigurations.xml b/Android/TechneauxMobileApp/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Android/TechneauxMobileApp/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/.idea/vcs.xml b/Android/TechneauxMobileApp/.idea/vcs.xml
new file mode 100644
index 0000000..6564d52
--- /dev/null
+++ b/Android/TechneauxMobileApp/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/TechneauxMobileApp.iml b/Android/TechneauxMobileApp/TechneauxMobileApp.iml
new file mode 100644
index 0000000..c8e6c4b
--- /dev/null
+++ b/Android/TechneauxMobileApp/TechneauxMobileApp.iml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/app/.gitignore b/Android/TechneauxMobileApp/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Android/TechneauxMobileApp/app/app.iml b/Android/TechneauxMobileApp/app/app.iml
new file mode 100644
index 0000000..1f75cb8
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/app.iml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generateDebugAndroidTestSources
+ generateDebugSources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/app/build.gradle b/Android/TechneauxMobileApp/app/build.gradle
new file mode 100644
index 0000000..4cb5500
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/build.gradle
@@ -0,0 +1,25 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 22
+ buildToolsVersion "23.0.0"
+
+ defaultConfig {
+ applicationId "com.techneaux.techneauxmobileapp"
+ minSdkVersion 15
+ targetSdkVersion 22
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.android.support:appcompat-v7:22.2.0'
+}
diff --git a/Android/TechneauxMobileApp/app/proguard-rules.pro b/Android/TechneauxMobileApp/app/proguard-rules.pro
new file mode 100644
index 0000000..f992690
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in D:\Android SDK/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/Android/TechneauxMobileApp/app/src/androidTest/java/com/techneaux/techneauxmobileapp/ApplicationTest.java b/Android/TechneauxMobileApp/app/src/androidTest/java/com/techneaux/techneauxmobileapp/ApplicationTest.java
new file mode 100644
index 0000000..71d15da
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/androidTest/java/com/techneaux/techneauxmobileapp/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.techneaux.techneauxmobileapp;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/app/src/main/AndroidManifest.xml b/Android/TechneauxMobileApp/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..9374857
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/AndroidManifest.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Android/TechneauxMobileApp/app/src/main/ic_launcher-web.png b/Android/TechneauxMobileApp/app/src/main/ic_launcher-web.png
new file mode 100644
index 0000000..6d3a83f
Binary files /dev/null and b/Android/TechneauxMobileApp/app/src/main/ic_launcher-web.png differ
diff --git a/Android/TechneauxMobileApp/app/src/main/java/com/techneaux/techneauxmobileapp/API_Communications.java b/Android/TechneauxMobileApp/app/src/main/java/com/techneaux/techneauxmobileapp/API_Communications.java
new file mode 100644
index 0000000..74ab984
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/java/com/techneaux/techneauxmobileapp/API_Communications.java
@@ -0,0 +1,247 @@
+package com.techneaux.techneauxmobileapp;
+
+import android.util.Log;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+
+public class API_Communications implements Runnable {
+
+ public static String urlSTR; //url string
+ public static JSONObject json; //json obj
+ public static String result; //result for other activities to pull
+
+ /**
+ * Preconditions: none
+ * Post Conditions: init the url string json object and result variables
+ * Parameters: url string json object and result
+ * Use: when starting API Communications
+ */
+ public API_Communications(String setUrl, JSONObject setJson) {
+ urlSTR = setUrl;
+ json = setJson;
+ result = null;
+
+ }
+
+
+ /**
+ * Preconditions: none
+ * Post Conditions: runs HTTPS Post to API
+ * Parameters: None
+ * Use: when starting API Communications
+ */
+ public void run() {
+
+ postData();
+ }
+
+ /**
+ * Note: Please only use this for development testing (for self signed in certificate). Adding this to the
+ * public application is a serious blunder.
+ *
+ * @author Sandip Jadhav
+ */
+ public static DefaultHttpClient createSSLHTTP() throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
+
+ // Setup a custom SSL Factory object which simply ignore the certificates
+ // validation and accept all type of self signed certificates
+ SSLSocketFactory sslFactory = new SimpleSSLSocketFactory(null);
+ sslFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
+ // Enable HTTP parameters
+ HttpParams params = new BasicHttpParams();
+ HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
+ HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
+
+ // Register the HTTP and HTTPS Protocols. For HTTPS, register our custom SSL Factory object.
+ SchemeRegistry registry = new SchemeRegistry();
+ registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
+ registry.register(new Scheme("https", sslFactory, 443));
+
+ // Create a new connection manager using the newly created registry and then create a new HTTP client
+ // using this connection manager
+ ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
+ DefaultHttpClient client = new DefaultHttpClient(ccm, params);
+
+ // To-do: Get or Post the data using this newly created http client object
+ return client;
+ }
+ /**
+ * Preconditions: none
+ * Post Conditions: HTTPS Post to API
+ * Parameters: None
+ * Use: when sending API Communications
+ */
+ public static void postData() {
+ // Create a new HttpClient and Post Header
+ HttpClient httpclient = new DefaultHttpClient();
+ HttpPost httppost = new HttpPost(urlSTR);
+
+
+ //bypassing ssl self-signed cert
+ try {
+ httpclient = createSSLHTTP();
+ } catch (UnrecoverableKeyException e) {
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (KeyStoreException e) {
+ e.printStackTrace();
+ } catch (KeyManagementException e) {
+ e.printStackTrace();
+ }
+
+
+ try {
+ httppost.setEntity(new ByteArrayEntity(
+ json.toString().getBytes("UTF8"))); //defines httppost body
+ // Execute HTTP Post Request
+ Log.d("API", "json: " + json);
+ Log.d("API", "Count: " + json.toString().length());
+ HttpContext localContext = new BasicHttpContext();
+
+ //set http client params
+ httpclient.getParams().setBooleanParameter("http.protocol.expect-continue", false);
+
+ //get http response from api
+ HttpResponse response = httpclient.execute(httppost, localContext);
+
+
+ // for JSON response :
+ if (response != null) {
+ InputStream is = response.getEntity().getContent(); //get body response
+
+ //decode response into 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();
+ }
+ }
+ //end decode response to string
+ result = sb.toString();
+ Log.d("API", "Result: " + result);
+ Log.d("API", response.getStatusLine().toString());
+
+
+ }
+
+
+ } catch (ClientProtocolException e) {
+ e.printStackTrace();
+ // TODO Auto-generated catch block
+ } catch (IOException e) {
+ e.printStackTrace();
+ // TODO Auto-generated catch block
+ }
+
+
+ }
+
+
+}
+
+
+/**
+ * Note: Please only use this for development testing (for self signed in certificate). Adding this to the
+ * public application is a serious blunder.
+ *
+ * @author Sandip Jadhav
+ */
+
+class SimpleSSLSocketFactory extends org.apache.http.conn.ssl.SSLSocketFactory {
+ private javax.net.ssl.SSLSocketFactory sslFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
+
+ public SimpleSSLSocketFactory(KeyStore truststore)
+ throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+ super(null);
+
+ try {
+ SSLContext context = SSLContext.getInstance("TLS");
+
+ // Create a trust manager that does not validate certificate chains and simply
+ // accept all type of certificates
+ TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return new java.security.cert.X509Certificate[]{};
+ }
+
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ }
+
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ }
+ }};
+
+ // Initialize the socket factory
+ context.init(null, trustAllCerts, new SecureRandom());
+ sslFactory = context.getSocketFactory();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
+ throws IOException, UnknownHostException {
+ return sslFactory.createSocket(socket, host, port, autoClose);
+ }
+
+ @Override
+ public Socket createSocket() throws IOException {
+ return sslFactory.createSocket();
+ }
+}
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/app/src/main/java/com/techneaux/techneauxmobileapp/MainActivity.java b/Android/TechneauxMobileApp/app/src/main/java/com/techneaux/techneauxmobileapp/MainActivity.java
new file mode 100644
index 0000000..d05c8f0
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/java/com/techneaux/techneauxmobileapp/MainActivity.java
@@ -0,0 +1,354 @@
+package com.techneaux.techneauxmobileapp;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+
+public class MainActivity extends AppCompatActivity {
+ private static Button submitLoginInfo; //object to link to the submit login info layout object
+ private static EditText username; //object to link to the username layout object
+ private static EditText password; //object to link to the password layout object
+ private static TextView CSNumber; //object to link to the customer service number layout object
+ public static final String MY_PREFS_NAME = "MyPrefsFile"; //file to store prefs for the app.
+ public static TextView errorText;
+ private ProgressBar spinner;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ //********Initialize the layout objects to a variable for manipulation********
+ submitLoginInfo = (Button) findViewById(R.id.loginSubmit);
+ username = (EditText) findViewById(R.id.username);
+ password = (EditText) findViewById(R.id.password);
+ CSNumber = (TextView) findViewById(R.id.CSNumberText);
+ errorText=(TextView) findViewById(R.id.loginErrorText);
+ spinner = (ProgressBar)findViewById(R.id.loginProgressBar);
+ spinner.setVisibility(View.INVISIBLE);
+
+ setButtons(); //function to initialize the two buttons (submit and call)
+ //********End Initialize the layout objects to a variable for manipulation********
+
+
+ SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
+ username.setText(prefs.getString("companyname",null)); // init username to what was stored
+
+ final View v = findViewById(android.R.id.content);
+
+ if( (prefs.getInt("screen_state",0) == 2 )) //checks saved state from last run time,
+ { // if state = 2, go to Registration
+
+ Intent myIntent = new Intent(v.getContext(), RegistrationActivity.class);
+ startActivityForResult(myIntent, 0);
+ }
+ else if( (prefs.getInt("screen_state",0) == 3 )) { //if 3, go to Ticket Activity
+
+ Intent myIntent = new Intent(v.getContext(), TicketActivity.class);
+ startActivityForResult(myIntent, 0);
+ }
+
+
+
+ }
+
+ /**
+ * Preconditions: None
+ * Post Conditions: Buttons have functional actions upon click
+ * Parameters: None
+ * Use: Only needs to be called once to set the onclick methods.
+ */
+ private void setButtons()
+ {
+ //********Customer Service Call Phone ********
+ CSNumber.setOnClickListener(new Button.OnClickListener() {
+
+ public void onClick(View v) {
+
+ String phone_no = "5045540101"; //number to call
+ Intent callIntent = new Intent(Intent.ACTION_CALL); // making new intent object
+ // to switch screen
+ callIntent.setData(Uri.parse("tel:" + phone_no)); //define screen to switch to with parameters
+ startActivity(callIntent); //switch
+
+ }
+ });
+ //********End Customer Service Call Phone ********
+
+
+ //********Submit Login Info ********
+ submitLoginInfo.setOnClickListener(new Button.OnClickListener() {
+
+ public void onClick(View v) {
+ submitLoginInfo.setText("Logging In...");
+ submitLoginInfo.setEnabled(false);
+ String sUsername = username.getText().toString(); //get text from username layout object
+ String sPassword = password.getText().toString(); //get text from password layout object
+
+ if (sUsername.matches("")) //tests to see if Username field has text
+ {
+ errorText.setText("Username not entered!");
+ submitLoginInfo.setText("Submit");
+ submitLoginInfo.setEnabled(true);
+ return;
+ } else if (sPassword.matches("")) //tests to see if Password field has text
+ {
+ errorText.setText("Password not entered!");
+ submitLoginInfo.setText("Submit");
+ submitLoginInfo.setEnabled(true);
+ return;
+ } else //username & password is accepted, save company name and switch to next screen,
+ {
+
+ JSONObject login = new JSONObject(); //create JSON Obj to hold data for API
+ try {
+ login.put("companyID", sUsername); //push companyID
+ login.put("password", sPassword); //push password
+
+
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ spinner.setVisibility(v.VISIBLE); //have load spinne be visible
+
+ //Code to create a thread to send data to API
+ Thread thread = new Thread(new API_Communications("https://cmps.techneaux.com/login", login));
+ thread.start();
+
+
+ runThread(v, sUsername);//process API data returned
+ return;
+ }
+
+
+ }
+ });
+ //********End Submit Login Info ********
+ }
+ private void runThread(final View v, final String sUsername) {
+
+ new Thread() {
+ public void run() {
+ SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
+
+
+ while (API_Communications.result == null) { // wait for reply from API
+ }
+
+ final String result = API_Communications.result; //get response from API
+ JSONObject obj = null;
+ try { //attempt to make json obj from API Response
+
+ obj = new JSONObject(result);
+
+
+ } catch (Throwable t) { // post error message if api does not give a proper JSON obj
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() { //need to invoke runonUI to set text boxes
+ errorText.setText("Invalid Response from Server, please try again." + result);
+ submitLoginInfo.setText("Submit");
+ submitLoginInfo.setEnabled(true);
+ }
+ });
+
+
+
+ }
+ String error = null; //string to hold error from api json reply
+ if (obj.has("niceMessage")) { //parse a nice debug message error
+ try { //from API to print to screen
+ error = obj.get("niceMessage").toString(); //parse out nicemessage
+
+ } catch (JSONException e) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() { //need to invoke runonUI to set text boxes
+ spinner.setVisibility(v.INVISIBLE);
+ submitLoginInfo.setText("Submit");
+ submitLoginInfo.setEnabled(true);
+
+ }
+ });
+ e.printStackTrace();
+ }
+ }
+ if (obj.has("authKey")) { //check if the object has a field for an authkey
+ if (error == null) { //make sure there is no error from api
+ try {
+ String authKey = obj.get("authKey").toString(); //get authkey from jsonobj
+ editor.putString("authKey", authKey);
+
+ editor.commit();
+ } catch (JSONException e) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() { //need to invoke runonUI to set text boxes
+ spinner.setVisibility(v.INVISIBLE);
+ submitLoginInfo.setText("Submit");
+ submitLoginInfo.setEnabled(true);
+
+ }
+ });
+ e.printStackTrace();
+ }
+ }
+
+ editor.putString("companyname", sUsername); //save username to shared prefs
+ editor.putInt("screen_state", 2); //update app state to 2, so app can jump to the next screen.
+
+ editor.commit();
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() { //need to invoke runonUI to set text boxes
+ spinner.setVisibility(v.INVISIBLE);
+ submitLoginInfo.setText("Submit");
+ submitLoginInfo.setEnabled(true);
+
+ //go to next screen
+ Intent myIntent = new Intent(v.getContext(), RegistrationActivity.class);
+ startActivityForResult(myIntent, 0);
+ }
+ });
+
+ } else {
+ final String finalError = error; //got an error to print to screen
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() { //need to invoke runonUI to set text boxes
+ spinner.setVisibility(v.INVISIBLE);
+ submitLoginInfo.setText("Submit");
+ submitLoginInfo.setEnabled(true);
+ errorText.setText(finalError);
+ }
+ });
+
+ }
+
+ }
+ }.start();//thread starter
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+ /**
+ * Preconditions: None
+ * Post Conditions: login fields are wiped out
+ * Parameters: None
+ * Use: whenever wipe data button is used in app.
+ */
+ public static void wipeData(SharedPreferences.Editor editor)
+ {
+
+
+ editor.putString("ticket_photo", null); //wipe out ticket photo data
+ editor.putString("ticket_location", null); //wipe out ticket location data
+ editor.putInt("screen_state", 0); // wipe app state to defaults
+ editor.putString("emp_firstname", null); //wipe firstname
+ editor.putString("emp_lastname", null); //wipe last name
+ editor.putString("emp_phonenumber", null); // wipe phone number
+ editor.putString("emp_emailaddress", null); //wipe email address
+ editor.putString("companyname", null); //Wipe out company name
+ editor.putString("ticket_description",null);//wipe out ticket description
+ editor.putInt("screen_state", 0); //Wipe out company name
+ editor.putString("authKey",null); //wipe authkey
+ editor.commit(); //save!
+
+
+ username.setText(null); //sets field to empty
+ password.setText(null); //sets field to empty
+ }
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ //preventing default implementation previous to android.os.Build.VERSION_CODES.ECLAIR
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+ @Override
+ public void onBackPressed() {
+ // Do Here what ever you want do on back press;
+ }
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.wipe_data) { //wipes all data from login screen
+ AlertDialog eraseConfirm = eraseDialogAlertMaker(); //dialog before wiping
+ // data to confirm wipe
+
+ eraseConfirm.show(); //show dialog
+
+
+
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ private AlertDialog eraseDialogAlertMaker(){
+
+ AlertDialog eraseDialog =
+
+ new AlertDialog.Builder(this)
+ //set message, title, and icon
+ .setTitle("Erase Data?")
+ .setMessage("Are you sure that you want to erase all stored data?")
+
+ //set three option buttons
+ .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ //whatever should be done when answering "YES" goes here
+ SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
+ wipeData(editor);
+ Toast.makeText(getApplicationContext(), "Data Erased",
+ Toast.LENGTH_LONG).show();
+
+ }
+ })
+ .setNegativeButton("NO", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ //whatever should be done when answering "NO" goes here
+
+
+ }
+ })//setNegativeButton
+
+ .create();
+
+ return eraseDialog;
+ }
+}
diff --git a/Android/TechneauxMobileApp/app/src/main/java/com/techneaux/techneauxmobileapp/RegistrationActivity.java b/Android/TechneauxMobileApp/app/src/main/java/com/techneaux/techneauxmobileapp/RegistrationActivity.java
new file mode 100644
index 0000000..2abc291
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/java/com/techneaux/techneauxmobileapp/RegistrationActivity.java
@@ -0,0 +1,419 @@
+package com.techneaux.techneauxmobileapp;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.text.SpannableString;
+import android.text.style.UnderlineSpan;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class RegistrationActivity extends AppCompatActivity {
+
+ private Button verifyButton; // Create button
+ private static EditText firstname; //object to link to the username layout object
+ private static EditText lastname; //object to link to the password layout object
+ private static EditText phonenumber; //object to link to the username layout object
+ private static EditText emailaddress; //object to link to the password layout object
+ private static TextView loggedInName; //object to link to the companyName layout object
+ private static TextView empError;
+ public static final String MY_PREFS_NAME = "MyPrefsFile"; //file to store prefs for the app.
+ private ProgressBar spinner;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.employee_info);
+ //********Initialize the layout objects to a variable for manipulation********
+
+ verifyButton = (Button) findViewById(R.id.verifyButton);
+ firstname = (EditText) findViewById(R.id.firstname);
+ lastname = (EditText) findViewById(R.id.lastname);
+ phonenumber = (EditText) findViewById(R.id.Phone);
+ emailaddress = (EditText) findViewById(R.id.emailaddress);
+ loggedInName = (TextView) findViewById(R.id.companynameLoggedIn);
+ empError = (TextView) findViewById(R.id.employeeError);
+ spinner = (ProgressBar) findViewById(R.id.employeeProgressBar);
+ spinner.setVisibility(View.INVISIBLE);
+ //********End Initialize the layout objects to a variable for manipulation********
+
+ setButtons(); //set button onclick listeners
+
+ //********Initialize the text boxes to data from shared preferences, if any exist ********
+ SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
+ firstname.setText(prefs.getString("emp_firstname", null)); // init first name to what was stored
+ lastname.setText(prefs.getString("emp_lastname", null)); // init last name to what was stored
+ phonenumber.setText(prefs.getString("emp_phonenumber", null)); // init phone number to what was stored
+ emailaddress.setText(prefs.getString("emp_emailaddress", null)); // init email address to what was stored
+ String mystring = new String("Company Name: " + prefs.getString("companyname", null));
+ SpannableString content = new SpannableString(mystring);
+ content.setSpan(new UnderlineSpan(), 14, mystring.length(), 0);
+ loggedInName.setText(content); // init companyname to what was stored
+ //********End Initialize the text boxes to data from shared preferences, if any exist ********
+
+
+ final View v = findViewById(android.R.id.content);
+
+ if ((prefs.getInt("screen_state", 0) == 3)) { //check if state is 3 to go to ticket screen
+ Intent myIntent = new Intent(v.getContext(), TicketActivity.class);
+ startActivityForResult(myIntent, 0); //go to ticket activity
+ }
+
+ }
+ @Override
+protected void onPause()
+{
+ super.onPause();
+
+ //****Store Data on application minimize to shared prefs ****************
+ SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
+ editor.putString("emp_firstname", firstname.getText().toString());
+ editor.putString("emp_lastname", lastname.getText().toString());
+ editor.putString("emp_phonenumber", phonenumber.getText().toString());
+ editor.putString("emp_emailaddress", emailaddress.getText().toString());
+ editor.commit();
+ //****End Store Data on application minimize to shared prefs ****************
+
+}
+ /**
+ * Preconditions: None
+ * Post Conditions: Buttons have functional actions upon click
+ * Parameters: None
+ * Use: Only needs to be called once to set the onclick methods.
+ */
+ private void setButtons() {
+ // throws a toast saying information has been verfied when Verify button is clicked.
+ verifyButton.setOnClickListener(new Button.OnClickListener() {
+ public void onClick(View v) {
+ verifyButton.setEnabled(false);
+ verifyButton.setText("Verifying...");
+ String sFirstName = firstname.getText().toString(); //get text from username layout object
+ String sLastName = lastname.getText().toString(); //get text from password layout object
+ String sPhone = phonenumber.getText().toString(); //get text from username layout object
+ String sEmail = emailaddress.getText().toString(); //get text from password layout object
+
+ if (sFirstName.matches("")) //tests to see if first name field has text
+ {
+ empError.setText("Employee First Name not entered!");
+ verifyButton.setEnabled(true);
+ verifyButton.setText("Verify");
+ return;
+ }
+ if (sLastName.matches("")) //tests to see if last name field has text
+ {
+ empError.setText("Employee Last Name not entered!");
+ verifyButton.setEnabled(true);
+ verifyButton.setText("Verify");
+ return;
+ }
+ if (sPhone.matches("")) //tests to see if phone number field has text
+ {
+ empError.setText("Phone Number not entered!");
+ verifyButton.setEnabled(true);
+ verifyButton.setText("Verify");
+ return;
+ }
+
+ if (isPhoneValid(sPhone) == false) {
+ empError.setText("Phone Number not valid!");
+
+ verifyButton.setEnabled(true);
+ verifyButton.setText("Verify");
+ return;
+ }
+ if (sEmail.matches("")) //tests to see if email address field has text
+ {
+ empError.setText("Email Address not entered!");
+ verifyButton.setEnabled(true);
+ verifyButton.setText("Verify");
+ return;
+ }
+ if (isEmailValid(sEmail) == false) { //check to see if email is validly formed
+ empError.setText("Email Address not valid!");
+ verifyButton.setEnabled(true);
+ verifyButton.setText("Verify");
+ return;
+ } else {
+
+
+ SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
+ JSONObject EmployeeInfo = new JSONObject(); //create json obj
+ try { //store data to json object to send to API
+ EmployeeInfo.put("authKey", prefs.getString("authKey", null));
+ EmployeeInfo.put("firstName", sFirstName);
+ EmployeeInfo.put("lastName", sLastName);
+ EmployeeInfo.put("email", sEmail);
+ EmployeeInfo.put("phoneNumber", sPhone);
+
+
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ spinner.setVisibility(v.VISIBLE);//make load spinner visible
+
+ Thread thread = new Thread(new API_Communications("https://cmps.techneaux.com/update-employee", EmployeeInfo));
+ thread.start(); //send data to API in other thread
+
+ runThread(sFirstName,sLastName,sEmail,sPhone, v ); //run sep thread to handle data from API
+ return;
+ }
+
+ }
+ });
+ }
+ private void runThread(final String sFirstName, final String sLastName, final String sEmail, final String sPhone, final View v ) {
+
+ new Thread() {
+ public void run() {
+ SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
+
+
+ while (API_Communications.result == null) { //wait for api to respond
+ }
+
+ final String result = API_Communications.result; //get result from api
+
+ JSONObject obj = null; //make json obj to hold api response
+ try { //try to form json from api response
+
+ obj = new JSONObject(result);
+
+
+ } catch (Throwable t) { //incase api response not valid json
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() { //invoke runonUI to alter layout objects
+ verifyButton.setEnabled(true);
+ verifyButton.setText("Verify");
+ empError.setText("Invalid Response from Server, please try again.\n " + result);
+ }
+ });
+
+
+ }
+ String error = null; //string to hold possible error message
+ if (obj.has("niceMessage")) { //check if a nicemessage exists in the json obj
+ try {
+ error = obj.get("niceMessage").toString(); //take error from json to string
+
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+ if (error == null) { // Store data to shared preferences
+ editor.putString("emp_firstname", sFirstName);
+ editor.putString("emp_lastname", sLastName);
+ editor.putString("emp_phonenumber", sPhone);
+ editor.putString("emp_emailaddress", sEmail);
+ editor.putInt("screen_state", 3);
+ editor.commit();
+
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {//invoke runonui to update layout objs
+ verifyButton.setEnabled(true);
+ verifyButton.setText("Verify");
+ spinner.setVisibility(View.INVISIBLE);
+ Intent myIntent = new Intent(v.getContext(), TicketActivity.class);
+ startActivityForResult(myIntent, 0); //go to ticket activity
+ }
+ });
+
+
+ } else {
+ final String finalError = error;
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ verifyButton.setEnabled(true);
+ verifyButton.setText("Verify");
+ spinner.setVisibility(View.INVISIBLE);
+ empError.setText(finalError);
+ }
+ });
+
+ }
+
+
+
+
+
+
+ }
+
+ }.start();
+ }
+ /**
+ * method is used for checking valid phone number format.
+ * Borrowed from http://buzycoder.blogspot.com/2013/06/android-check-if-phone-number-is-valid.html
+ *
+ * @param phone number
+ * @return boolean true for valid false for invalid
+ */
+ static boolean isPhoneValid(String phoneNo) {
+ //validate phone numbers of format "1234567890"
+ if (phoneNo.matches("\\d{10}")) return true;
+ //validating phone number with -, . or spaces
+ else if (phoneNo.matches("\\d{3}[-\\.\\s]\\d{3}[-\\.\\s]\\d{4}")) return true;
+ //validating phone number with extension length from 3 to 5
+ else if (phoneNo.matches("\\d{3}-\\d{3}-\\d{4}\\s(x|(ext))\\d{3,5}")) return true;
+ //validating phone number where area code is in braces ()
+ else if (phoneNo.matches("\\(\\d{3}\\)-\\d{3}-\\d{4}")) return true;
+ //return false if nothing matches the input
+ else return false;
+ }
+
+ /**
+ * method is used for checking valid email id format.
+ * Borrowed from http://stackoverflow.com/questions/6119722/how-to-check-edittexts-text-is-email-address-or-not
+ *
+ * @param email
+ * @return boolean true for valid false for invalid
+ */
+ private static boolean isEmailValid(String email) {
+ boolean isValid = false;
+
+ String expression = "^[\\w\\.-]+@([\\w\\-]+\\.)+[A-Z]{2,4}$";
+ CharSequence inputStr = email;
+
+ Pattern pattern = Pattern.compile(expression, Pattern.CASE_INSENSITIVE);
+ Matcher matcher = pattern.matcher(inputStr);
+ if (matcher.matches()) {
+ isValid = true;
+ }
+ return isValid;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ //preventing default implementation previous to android.os.Build.VERSION_CODES.ECLAIR
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public void onBackPressed() {
+ // Do Here what ever you want do on back press;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ /**
+ * Preconditions: None
+ * Post Conditions: all data fields are wiped out
+ * Parameters: None
+ * Use: whenever wipe data button is used in app.
+ */
+ public static void wipeData(SharedPreferences.Editor editor, SharedPreferences prefs) {
+ if (!(prefs.getInt("screen_state", 0) == 2)) {
+
+ firstname.setText(null); //sets field to empty
+ lastname.setText(null); //sets field to empty
+ phonenumber.setText(null); //sets field to empty
+ emailaddress.setText(null); //sets field to empty
+ }
+ //wipe all shared preferences
+ editor.putString("ticket_photo", null);
+ editor.putString("ticket_location", null);
+ editor.putInt("screen_state", 0);
+ editor.putString("ticket_description", null);//wipe out ticket description
+ editor.putString("emp_firstname", null);
+ editor.putString("emp_lastname", null);
+ editor.putString("emp_phonenumber", null);
+ editor.putString("emp_emailaddress", null);
+ editor.putInt("screen_state", 0);
+ editor.putString("companyname", null); //Wipe out company name
+ editor.putInt("screen_state", 0); //Wipe out company name
+ editor.putString("authKey", null);
+ editor.commit();
+ //end wipe
+
+
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.wipe_data) {
+ AlertDialog eraseConfirm = eraseDialogAlertMaker(); //dialog before wiping
+ // data to confirm wipe
+
+ eraseConfirm.show(); //show dialog
+
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ private AlertDialog eraseDialogAlertMaker(){
+
+ AlertDialog eraseDialog =
+
+ new AlertDialog.Builder(this)
+ //set message, title, and icon
+ .setTitle("Erase Data?")
+ .setMessage("Are you sure that you want to erase all stored data?")
+
+ //set three option buttons
+ .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ //whatever should be done when answering "YES" goes here
+ SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
+ SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
+ MainActivity.wipeData(editor);
+ wipeData(editor, prefs);
+ final View v = findViewById(android.R.id.content);
+ Intent myIntent = new Intent(v.getContext(), MainActivity.class);
+ startActivityForResult(myIntent, 0);
+ Toast.makeText(getApplicationContext(), "Data Erased",
+ Toast.LENGTH_LONG).show();
+
+
+ }
+ })
+ .setNegativeButton("NO", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ //whatever should be done when answering "NO" goes here
+
+
+ }
+ })//setNegativeButton
+
+ .create();
+
+ return eraseDialog;
+ }
+}
diff --git a/Android/TechneauxMobileApp/app/src/main/java/com/techneaux/techneauxmobileapp/TicketActivity.java b/Android/TechneauxMobileApp/app/src/main/java/com/techneaux/techneauxmobileapp/TicketActivity.java
new file mode 100644
index 0000000..0f5d217
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/java/com/techneaux/techneauxmobileapp/TicketActivity.java
@@ -0,0 +1,593 @@
+package com.techneaux.techneauxmobileapp;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.v7.app.AppCompatActivity;
+import android.text.SpannableString;
+import android.text.style.UnderlineSpan;
+import android.util.Base64;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+
+/**
+ * Created by CMD Drake on 10/27/2015.
+ */
+public class TicketActivity extends AppCompatActivity {
+
+ public static final String MY_PREFS_NAME = "MyPrefsFile"; //file to store prefs for the app.
+
+ private static TextView employeename; //object to link to the employeename layout object
+ private static TextView companyname; //object to link to the company name layout object
+ private static TextView phonenumber; //object to link to the phone number layout object
+ private static TextView emailaddress; //object to link to the email address layout object
+
+ private static EditText Location; //object to link to the location layout object
+ private static EditText Description; //object to link to the description layout object
+ private static TextView ErrorTicket; //object to link to the errorticket layout object
+ private static Button clearPhoto; //object to link to the clear photo button layout object
+
+ private static Button SubmitTicketBTN; //object to link to the submit btn layout object
+ private static Button CameraBTN; //object to link to the take a picture btn layout object
+
+ private ProgressBar spinner; //load spinner used to show app is sending data to API
+ private static final int CAMERA_REQUEST = 1888;
+ private static ImageView imageView; //imageview for photo preview
+
+ private static Bitmap photo; //holds bitmap representation of picture taken
+ private String photo_ticket; //holds base64 representation of picture taken
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.ticket_activity);
+
+ //********Initialize the layout objects to a variable for manipulation********
+ employeename = (TextView) findViewById(R.id.ticket_employeename);
+ companyname = (TextView) findViewById(R.id.ticket_companynameLoggedIn);
+ phonenumber = (TextView) findViewById(R.id.ticket_phonenumber);
+ emailaddress = (TextView) findViewById(R.id.ticket_emailaddress);
+ SubmitTicketBTN = (Button) findViewById(R.id.SubmitTicketBTN);
+ CameraBTN = (Button) this.findViewById(R.id.cameraBTN);
+ Location = (EditText) findViewById(R.id.locationsite);
+ Description = (EditText) findViewById(R.id.ticket_description);
+ imageView = (ImageView) this.findViewById(R.id.imageView);
+ ErrorTicket = (TextView) this.findViewById(R.id.ticketError);
+ clearPhoto = (Button) this.findViewById(R.id.clearPhoto);
+ spinner = (ProgressBar) findViewById(R.id.employeeProgressBar);
+ spinner.setVisibility(View.INVISIBLE);
+ //********End Initialize the layout objects to a variable for manipulation********
+
+ setButtons(); //set button onclick listeners
+
+
+ SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
+ //Company name read
+ String mystring = new String("Company Name: " + prefs.getString("companyname", null));
+ SpannableString content = new SpannableString(mystring);
+ content.setSpan(new UnderlineSpan(), 14, mystring.length(), 0);
+ companyname.setText(content);
+ //end company name read
+
+ //employee name read
+ mystring = new String("Employee Name: " + prefs.getString("emp_firstname", null) + " " + prefs.getString("emp_lastname", null));
+ content = new SpannableString(mystring);
+ content.setSpan(new UnderlineSpan(), 15, mystring.length(), 0);
+ employeename.setText(content);
+ //end name read
+
+ //phone number read
+ mystring = new String("Phone Number: " + prefs.getString("emp_phonenumber", null));
+ content = new SpannableString(mystring);
+ content.setSpan(new UnderlineSpan(), 14, mystring.length(), 0);
+ phonenumber.setText(content);
+ //end phone number read
+
+ //email read
+ mystring = new String("Email Address: " + prefs.getString("emp_emailaddress", null));
+ content = new SpannableString(mystring);
+ content.setSpan(new UnderlineSpan(), 15, mystring.length(), 0);
+ emailaddress.setText(content);
+ //email end read
+
+ //location read
+ Location.setText(prefs.getString("ticket_location", null));
+ //end location read
+
+ //location read
+ Description.setText(prefs.getString("ticket_description", null));
+ //end location read
+
+ //read and decode photo into bitmap from shared prefs
+ String base = prefs.getString("ticket_photo", null);
+ photo_ticket = "";
+
+ if (base != null) {
+ photo_ticket = base;
+ byte[] imageAsBytes = Base64.decode(base.getBytes(), Base64.DEFAULT);
+ photo = BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.length);
+ imageView.setImageBitmap(
+ BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.length));
+ }
+ //end read and decode photo into bitmap from shared prefs
+ }
+
+ /**
+ * Preconditions: None
+ * Post Conditions: Buttons have functional actions upon click
+ * Parameters: None
+ * Use: Only needs to be called once to set the onclick methods.
+ */
+ private void setButtons() {
+
+ // throws a toast saying information has been verfied when Verify button is clicked.
+ SubmitTicketBTN.setOnClickListener(new Button.OnClickListener() {
+ public void onClick(View v) {
+ //disable buttons while attempting to talk with API
+ SubmitTicketBTN.setEnabled(false);
+ SubmitTicketBTN.setText("Submitting...");
+ CameraBTN.setEnabled(false);
+ clearPhoto.setEnabled(false);
+ //end disable buttons while attempting to talk with API
+
+ String sDescription = Description.getText().toString(); //get text from username layout object
+ String sLocation = Location.getText().toString(); //get text from password layout object
+
+ if (sDescription.matches("")) //tests to see if Password field has text
+ {
+ ErrorTicket.setText("Description not Entered!");
+ SubmitTicketBTN.setEnabled(true);
+ SubmitTicketBTN.setText("Submit Ticket");
+ CameraBTN.setEnabled(true);
+ clearPhoto.setEnabled(true);
+ return;
+ } else //username & password is accepted, save company name and switch to next screen,
+ {
+ SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
+ JSONObject TicketInfo = new JSONObject(); //make json object
+ try { //populate json object with data for API
+ TicketInfo.put("authKey", prefs.getString("authKey", null));
+ TicketInfo.put("location", sLocation);
+ TicketInfo.put("description", sDescription);
+ TicketInfo.put("photo", photo_ticket);
+
+
+ } catch (JSONException e) {//re-enable all buttons
+ // TODO Auto-generated catch block
+ SubmitTicketBTN.setEnabled(true);
+ CameraBTN.setEnabled(true);
+ clearPhoto.setEnabled(true);
+ SubmitTicketBTN.setText("Submit Ticket");
+ e.printStackTrace();
+ }
+ Log.d("API", "In Ticket: START " + photo_ticket + " END");
+ Log.d("API", "In Ticket Count: " + photo_ticket.length());
+ spinner.setVisibility(v.VISIBLE); //sets spinner loader to visible
+ Thread thread = new Thread(new API_Communications("https://cmps.techneaux.com/submit-ticket", TicketInfo));
+ thread.start(); //send data to API via helper thread
+
+ startTestThread(sLocation); //handle API response on another thread
+ return;
+ }
+
+ }
+
+
+ });
+
+ clearPhoto.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {//listener to clear photo from app
+ SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
+ photo_ticket = "";
+ editor.putString("ticket_photo", null);
+ editor.commit();
+
+ imageView.setImageBitmap(null);
+ }
+ });
+ CameraBTN.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) { //listener to take a picture and store into phone app.
+
+ imageFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/picture.jpg";
+ File imageFile = new File(imageFilePath);
+ Uri imageFileUri = Uri.fromFile(imageFile); // convert path to Uri
+
+ Intent it = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
+ it.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri);
+ startActivityForResult(it, CAMERA_REQUEST); }
+ });
+ imageView.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ loadPhoto(imageView);
+ }
+ });
+
+
+ }
+ private String imageFilePath;
+
+ /**
+ * Preconditions: None
+ * Post Conditions: check for photo and store photo into applicatio
+ * Parameters: requestCode, resultcode, and data
+ * Use: when we need to take a picture.
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (RESULT_OK == resultCode) {
+
+
+ // Decode it for real
+ BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
+ bmpFactoryOptions.inJustDecodeBounds = false;
+
+ //imageFilePath image path which you pass with intent
+ Bitmap bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions);
+
+ Bitmap scaledBitmap = scaleDown(bmp, 1000, true); //scale picture to 1000 pixel resolution
+
+ //convert bitmap to base64
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ scaledBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
+ byte[] byteArray = byteArrayOutputStream.toByteArray();
+ String encoded = Base64.encodeToString(byteArray, Base64.NO_WRAP);
+ //end convert bitmap to base64
+
+ Log.d("API", "Init Photo Size:" + encoded.length());
+ SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
+ editor.putString("ticket_photo", encoded);
+ editor.commit(); // save base64 to shared preferences
+
+
+ photo_ticket = encoded; // set base64 to global var
+
+ // Display it
+ imageView.setImageBitmap(bmp); //set pic taken to the preview image view
+ }
+ }
+ /**
+ * Preconditions: has a bitmap to scale down
+ * Post Conditions: photo scaled to desired size
+ * Parameters: bitmap image, max size of photo, and filter
+ * Use: when we need to take a picture.
+ */
+ public static Bitmap scaleDown(Bitmap realImage, float maxImageSize,
+ boolean filter) {
+ float ratio = Math.min(
+ (float) maxImageSize / realImage.getWidth(),
+ (float) maxImageSize / realImage.getHeight());
+ int width = Math.round((float) ratio * realImage.getWidth());
+ int height = Math.round((float) ratio * realImage.getHeight());
+
+ Bitmap newBitmap = Bitmap.createScaledBitmap(realImage, width,
+ height, filter);
+ return newBitmap;
+ }
+
+@Override
+protected void onPause()
+{
+ super.onPause();
+
+ //store ticket location and description to memory on app minimize
+ SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
+ editor.putString("ticket_location", Location.getText().toString());
+ editor.putString("ticket_description", Description.getText().toString());
+ editor.commit();
+
+
+}
+ /**
+ * Preconditions: data sent to API
+ * Post Conditions: response from API handled
+ * Parameters: location sent to ticket api
+ * Use: when we need to send a ticket
+ */
+ protected void startTestThread(final String sLocation) {
+ Thread t = new Thread() {
+ public void run() {
+ Log.d("API", "In thread");
+ SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
+ final SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
+
+
+ while (API_Communications.result == null) { //wait for API result
+ }
+
+ final String result = API_Communications.result; //get api result
+
+ JSONObject obj = null; //make json obj
+ try {//try to parse api result into json obj
+
+ obj = new JSONObject(result);
+
+
+ } catch (Throwable t) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {//use runonUI to modify layout objects
+ SubmitTicketBTN.setEnabled(true);
+ CameraBTN.setEnabled(true);
+ clearPhoto.setEnabled(true);
+ ErrorTicket.setText("Invalid Response from Server, please try again.\n " + result);
+ SubmitTicketBTN.setText("Submit Ticket");
+
+ }
+ });
+
+
+ }
+ String error = null; //string to hold error message if any from API.
+ if (obj.has("niceMessage")) {//checks for error message
+ try {
+ error = obj.get("niceMessage").toString(); //pulls niceMessage out of json from API
+
+ } catch (JSONException e) {
+ e.printStackTrace();
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() { //alter UI objects
+ CameraBTN.setEnabled(true);
+ clearPhoto.setEnabled(true);
+ SubmitTicketBTN.setEnabled(true);
+ SubmitTicketBTN.setText("Submit Ticket");
+
+ }
+ });
+
+ }
+ }
+ String PhotoStatus = ""; //string to display check if picture is present
+ if (error == null) {//check for no error
+
+ //condition to know no pic present to send
+ if (prefs.getString("ticket_photo", null) == null) {
+ PhotoStatus = "No Photo";
+ } else { //otherwise there is a pic
+ PhotoStatus = "Photo Attached";
+ }
+
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ Description.setText("");
+ }
+ }); //clear description
+
+
+ photo_ticket = "";
+ editor.putString("ticket_photo", null); //wipe photo off memory
+ editor.commit();
+ final String finalPhotoStatus = PhotoStatus;
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() { //do all UI updates to text boxes and make confirmation toast
+ editor.putString("ticket_location", sLocation);
+ editor.commit();
+ ErrorTicket.setText("");
+ CameraBTN.setEnabled(true);
+ clearPhoto.setEnabled(true);
+ imageView.setImageBitmap(null);
+ spinner.setVisibility(View.INVISIBLE);
+ SubmitTicketBTN.setEnabled(true);
+ SubmitTicketBTN.setText("Submit Ticket");
+ Toast.makeText(getApplicationContext(), "Ticket Sent Successfully!\n\n"
+ + companyname.getText().toString() + "\nLocation: "
+ + Location.getText().toString() + "\n" +
+ employeename.getText().toString() + "\n"
+ + phonenumber.getText().toString() + "\n"
+ + emailaddress.getText().toString() + "\nDescription: "
+ + Description.getText().toString() + "\nPhoto: "
+ + finalPhotoStatus,
+ Toast.LENGTH_LONG).show();
+
+ }
+ });
+
+ } else { //error detected, print error to screen
+ final String finalError = error;
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ spinner.setVisibility(View.INVISIBLE);
+ ErrorTicket.setText(finalError);
+ CameraBTN.setEnabled(true);
+ clearPhoto.setEnabled(true);
+ SubmitTicketBTN.setText("Submit Ticket");
+ SubmitTicketBTN.setEnabled(true);
+ }
+ });
+
+
+ }
+ }
+ };
+
+ t.start();
+ }
+ /**
+ * Preconditions: none
+ * Post Conditions: show picture preview in bigger screen
+ * Parameters: imageview photo
+ * Use: when we need to show a picture bigger
+ */
+ private void loadPhoto(ImageView imageView) {
+
+ ImageView tempImageView = imageView;
+
+
+ AlertDialog.Builder imageDialog = new AlertDialog.Builder(this);
+ LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);
+
+ View layout = inflater.inflate(R.layout.photo_preview, null);
+ ImageView image = (ImageView) layout.findViewById(R.id.imageView2);
+ image.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 1500));
+ image.setImageDrawable(tempImageView.getDrawable());
+ imageDialog.setView(layout);
+ imageDialog.create();
+ imageDialog.show();
+ }
+
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ //preventing default implementation previous to android.os.Build.VERSION_CODES.ECLAIR
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public void onBackPressed() {
+ // Do Here what ever you want do on back press;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.ticket_menu, menu);
+ return true;
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.wipe_data) {
+ AlertDialog eraseConfirm = eraseDialogAlertMaker(); //dialog before wiping
+ // data to confirm wipe
+
+ eraseConfirm.show(); //show dialog
+ Toast.makeText(getApplicationContext(), "Data Erased",
+ Toast.LENGTH_LONG).show();
+
+ return true;
+ }
+ if (id == R.id.EmployeeCredentials) {
+ //------Save Location and Description to memory
+ SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
+ editor.putString("ticket_location", Location.getText().toString());
+ editor.putString("ticket_description", Description.getText().toString());
+ editor.putInt("screen_state", 2);
+ editor.commit();
+ //------End Save Location and Description to memory
+
+ //------Go back to registration
+ final View v = findViewById(android.R.id.content);
+ Intent myIntent = new Intent(v.getContext(), RegistrationActivity.class);
+ startActivityForResult(myIntent, 0);
+ finish();
+ //end go back to reg
+
+
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ private AlertDialog eraseDialogAlertMaker(){
+
+ AlertDialog eraseDialog =
+
+ new AlertDialog.Builder(this)
+ //set message, title, and icon
+ .setTitle("Erase Data?")
+ .setMessage("Are you sure that you want to erase all stored data?")
+
+ //set three option buttons
+ .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ //whatever should be done when answering "YES" goes here
+ SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
+ SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
+ try {
+ MainActivity.wipeData(editor);
+ RegistrationActivity.wipeData(editor, prefs);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ wipeData(editor, prefs);
+ final View v = findViewById(android.R.id.content);
+ Intent myIntent = new Intent(v.getContext(), MainActivity.class);
+ startActivityForResult(myIntent, 0);
+
+
+ }
+ })
+ .setNegativeButton("NO", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ //whatever should be done when answering "NO" goes here
+
+
+ }
+ })//setNegativeButton
+
+ .create();
+
+ return eraseDialog;
+ }
+ public static void wipeData(SharedPreferences.Editor editor, SharedPreferences prefs) {
+ //wipe shared preferences
+ editor.putString("ticket_photo", null);
+ editor.putString("ticket_location", null);
+ editor.putInt("screen_state", 0);
+ editor.putString("emp_firstname", null);
+ editor.putString("emp_lastname", null);
+ editor.putString("emp_phonenumber", null);
+ editor.putString("emp_emailaddress", null);
+ editor.putInt("screen_state", 0);
+ editor.putString("companyname", null);
+ editor.putInt("screen_state", 0);
+ editor.putString("ticket_description",null);//wipe out ticket description
+ editor.putString("authKey", null);
+ editor.commit();
+ //end wipe shared preferences
+ if (!(prefs.getInt("screen_state", 0) == 3)) { //if screenstate is 3 wipe local text fields
+
+ Location.setText(null);
+ Description.setText(null);
+ }
+ }
+
+}
diff --git a/Android/TechneauxMobileApp/app/src/main/res/drawable-hdpi/techneauxlogo.png b/Android/TechneauxMobileApp/app/src/main/res/drawable-hdpi/techneauxlogo.png
new file mode 100644
index 0000000..7756ffa
Binary files /dev/null and b/Android/TechneauxMobileApp/app/src/main/res/drawable-hdpi/techneauxlogo.png differ
diff --git a/Android/TechneauxMobileApp/app/src/main/res/drawable-mdpi/techneauxlogo.png b/Android/TechneauxMobileApp/app/src/main/res/drawable-mdpi/techneauxlogo.png
new file mode 100644
index 0000000..7756ffa
Binary files /dev/null and b/Android/TechneauxMobileApp/app/src/main/res/drawable-mdpi/techneauxlogo.png differ
diff --git a/Android/TechneauxMobileApp/app/src/main/res/drawable-xhdpi/techneauxlogo.png b/Android/TechneauxMobileApp/app/src/main/res/drawable-xhdpi/techneauxlogo.png
new file mode 100644
index 0000000..7756ffa
Binary files /dev/null and b/Android/TechneauxMobileApp/app/src/main/res/drawable-xhdpi/techneauxlogo.png differ
diff --git a/Android/TechneauxMobileApp/app/src/main/res/drawable-xxhdpi/phone.png b/Android/TechneauxMobileApp/app/src/main/res/drawable-xxhdpi/phone.png
new file mode 100644
index 0000000..e50ce9c
Binary files /dev/null and b/Android/TechneauxMobileApp/app/src/main/res/drawable-xxhdpi/phone.png differ
diff --git a/Android/TechneauxMobileApp/app/src/main/res/drawable-xxhdpi/techneauxlogo.png b/Android/TechneauxMobileApp/app/src/main/res/drawable-xxhdpi/techneauxlogo.png
new file mode 100644
index 0000000..7756ffa
Binary files /dev/null and b/Android/TechneauxMobileApp/app/src/main/res/drawable-xxhdpi/techneauxlogo.png differ
diff --git a/Android/TechneauxMobileApp/app/src/main/res/layout/activity_main.xml b/Android/TechneauxMobileApp/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..d639444
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,120 @@
+
+
+ // Company Name
+
+
+ // Password
+
+ //Progress Bar
+
+ // Submit button
+
+
+ // Login Error Message
+
+
+ // Phone number
+
+
+ // Customer service info
+
+
+ // Logo
+
+
+
+
+
+
diff --git a/Android/TechneauxMobileApp/app/src/main/res/layout/employee_info.xml b/Android/TechneauxMobileApp/app/src/main/res/layout/employee_info.xml
new file mode 100644
index 0000000..144471c
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/res/layout/employee_info.xml
@@ -0,0 +1,125 @@
+
+
+
+
+ //First Name
+
+
+ //Last Name
+
+
+ //Phone Number
+
+
+ //E-mail address
+
+
+ //Submit Button
+
+ //Progress Bar
+
+ // Error Message
+
+
+
+
+
diff --git a/Android/TechneauxMobileApp/app/src/main/res/layout/photo_preview.xml b/Android/TechneauxMobileApp/app/src/main/res/layout/photo_preview.xml
new file mode 100644
index 0000000..e0aabdf
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/res/layout/photo_preview.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/app/src/main/res/layout/ticket_activity.xml b/Android/TechneauxMobileApp/app/src/main/res/layout/ticket_activity.xml
new file mode 100644
index 0000000..a2b95c4
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/res/layout/ticket_activity.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+ // Location
+
+
+
+ // Description of Issue
+
+
+ // Error message
+
+
+ // Submit Ticket
+
+
+ // Take Photo
+
+
+ //Progress Bar
+
+ // Clear Photo
+
+
+ // Photo preview
+
+
+ // Logo
+
+
+
+
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/app/src/main/res/menu/menu_main.xml b/Android/TechneauxMobileApp/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..bd68963
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,6 @@
+
diff --git a/Android/TechneauxMobileApp/app/src/main/res/menu/ticket_menu.xml b/Android/TechneauxMobileApp/app/src/main/res/menu/ticket_menu.xml
new file mode 100644
index 0000000..9775d10
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/res/menu/ticket_menu.xml
@@ -0,0 +1,9 @@
+
diff --git a/Android/TechneauxMobileApp/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Android/TechneauxMobileApp/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..40b3bd0
Binary files /dev/null and b/Android/TechneauxMobileApp/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Android/TechneauxMobileApp/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Android/TechneauxMobileApp/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..4da55f4
Binary files /dev/null and b/Android/TechneauxMobileApp/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Android/TechneauxMobileApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Android/TechneauxMobileApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..fd430df
Binary files /dev/null and b/Android/TechneauxMobileApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Android/TechneauxMobileApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Android/TechneauxMobileApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..7d7b21e
Binary files /dev/null and b/Android/TechneauxMobileApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Android/TechneauxMobileApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Android/TechneauxMobileApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..fd2168a
Binary files /dev/null and b/Android/TechneauxMobileApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Android/TechneauxMobileApp/app/src/main/res/values-w820dp/dimens.xml b/Android/TechneauxMobileApp/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Android/TechneauxMobileApp/app/src/main/res/values/dimens.xml b/Android/TechneauxMobileApp/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Android/TechneauxMobileApp/app/src/main/res/values/strings.xml b/Android/TechneauxMobileApp/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..fcc67c1
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+ Techneaux Mobile App
+
+ Hello world!
+ Settings
+ Sign Out
+ Employee Data
+
diff --git a/Android/TechneauxMobileApp/app/src/main/res/values/styles.xml b/Android/TechneauxMobileApp/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..766ab99
--- /dev/null
+++ b/Android/TechneauxMobileApp/app/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/Android/TechneauxMobileApp/build.gradle b/Android/TechneauxMobileApp/build.gradle
new file mode 100644
index 0000000..9405f3f
--- /dev/null
+++ b/Android/TechneauxMobileApp/build.gradle
@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.2.3'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
diff --git a/Android/TechneauxMobileApp/gradle.properties b/Android/TechneauxMobileApp/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/Android/TechneauxMobileApp/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/Android/TechneauxMobileApp/gradle/wrapper/gradle-wrapper.jar b/Android/TechneauxMobileApp/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
Binary files /dev/null and b/Android/TechneauxMobileApp/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Android/TechneauxMobileApp/gradle/wrapper/gradle-wrapper.properties b/Android/TechneauxMobileApp/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/Android/TechneauxMobileApp/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/Android/TechneauxMobileApp/gradlew b/Android/TechneauxMobileApp/gradlew
new file mode 100644
index 0000000..91a7e26
--- /dev/null
+++ b/Android/TechneauxMobileApp/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Android/TechneauxMobileApp/gradlew.bat b/Android/TechneauxMobileApp/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/Android/TechneauxMobileApp/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Android/TechneauxMobileApp/settings.gradle b/Android/TechneauxMobileApp/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Android/TechneauxMobileApp/settings.gradle
@@ -0,0 +1 @@
+include ':app'