diff --git a/Main/AndroidManifest.xml b/Main/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..469d946b244483a40efd63ff2184b264812ae0b4
--- /dev/null
+++ b/Main/AndroidManifest.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Main/gen/gr/grnet/academicid/inspector/BuildConfig.java b/Main/gen/gr/grnet/academicid/inspector/BuildConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..0dc2e2afb309396a9bfb1193ba4fc0b4d6b41c30
--- /dev/null
+++ b/Main/gen/gr/grnet/academicid/inspector/BuildConfig.java
@@ -0,0 +1,8 @@
+/*___Generated_by_IDEA___*/
+
+package gr.grnet.academicid.inspector;
+
+/* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */
+public final class BuildConfig {
+ public final static boolean DEBUG = Boolean.parseBoolean(null);
+}
\ No newline at end of file
diff --git a/Main/gen/gr/grnet/academicid/inspector/Manifest.java b/Main/gen/gr/grnet/academicid/inspector/Manifest.java
new file mode 100644
index 0000000000000000000000000000000000000000..037103ecf20bc18fbe316217d92e2c8085f3854a
--- /dev/null
+++ b/Main/gen/gr/grnet/academicid/inspector/Manifest.java
@@ -0,0 +1,7 @@
+/*___Generated_by_IDEA___*/
+
+package gr.grnet.academicid.inspector;
+
+/* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/Main/gen/gr/grnet/academicid/inspector/R.java b/Main/gen/gr/grnet/academicid/inspector/R.java
new file mode 100644
index 0000000000000000000000000000000000000000..6d597e6094ed7e1da995ac0275766588d9afc3ec
--- /dev/null
+++ b/Main/gen/gr/grnet/academicid/inspector/R.java
@@ -0,0 +1,7 @@
+/*___Generated_by_IDEA___*/
+
+package gr.grnet.academicid.inspector;
+
+/* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/Main/res/drawable-hdpi/action_search.png b/Main/res/drawable-hdpi/action_search.png
new file mode 100644
index 0000000000000000000000000000000000000000..f12e005ebe835c1dd2f6ae324224c3ee296d2d68
Binary files /dev/null and b/Main/res/drawable-hdpi/action_search.png differ
diff --git a/Main/res/drawable-hdpi/action_settings.png b/Main/res/drawable-hdpi/action_settings.png
new file mode 100644
index 0000000000000000000000000000000000000000..3e4580e0534c913abc5560067866b162e2972945
Binary files /dev/null and b/Main/res/drawable-hdpi/action_settings.png differ
diff --git a/Main/res/drawable-hdpi/content_remove.png b/Main/res/drawable-hdpi/content_remove.png
new file mode 100644
index 0000000000000000000000000000000000000000..094eea589246b46e26d3cf02285f26c1abb33700
Binary files /dev/null and b/Main/res/drawable-hdpi/content_remove.png differ
diff --git a/Main/res/drawable-hdpi/grnet_logo.png b/Main/res/drawable-hdpi/grnet_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc4e4607c1b14ac1b92e637157e735d0bc219a30
Binary files /dev/null and b/Main/res/drawable-hdpi/grnet_logo.png differ
diff --git a/Main/res/drawable-hdpi/ic_launcher.png b/Main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..feff32f57f603d21e79db3ad7932e09d3efa7790
Binary files /dev/null and b/Main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/Main/res/drawable-ldpi/action_search.png b/Main/res/drawable-ldpi/action_search.png
new file mode 100644
index 0000000000000000000000000000000000000000..587d9e0bf392fc928947f04293ba009f7fc77b29
Binary files /dev/null and b/Main/res/drawable-ldpi/action_search.png differ
diff --git a/Main/res/drawable-ldpi/action_settings.png b/Main/res/drawable-ldpi/action_settings.png
new file mode 100644
index 0000000000000000000000000000000000000000..d3e42edcb6db096d90d1b28a2d4609d3419c5751
Binary files /dev/null and b/Main/res/drawable-ldpi/action_settings.png differ
diff --git a/Main/res/drawable-ldpi/content_remove.png b/Main/res/drawable-ldpi/content_remove.png
new file mode 100644
index 0000000000000000000000000000000000000000..3336760d5f3efdfefefd6899317c3a09264430f6
Binary files /dev/null and b/Main/res/drawable-ldpi/content_remove.png differ
diff --git a/Main/res/drawable-ldpi/grnet_logo.png b/Main/res/drawable-ldpi/grnet_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc4e4607c1b14ac1b92e637157e735d0bc219a30
Binary files /dev/null and b/Main/res/drawable-ldpi/grnet_logo.png differ
diff --git a/Main/res/drawable-ldpi/ic_launcher.png b/Main/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1959d54fd65e38c346521141a2fb6c4a057da0c
Binary files /dev/null and b/Main/res/drawable-ldpi/ic_launcher.png differ
diff --git a/Main/res/drawable-mdpi/action_search.png b/Main/res/drawable-mdpi/action_search.png
new file mode 100644
index 0000000000000000000000000000000000000000..587d9e0bf392fc928947f04293ba009f7fc77b29
Binary files /dev/null and b/Main/res/drawable-mdpi/action_search.png differ
diff --git a/Main/res/drawable-mdpi/action_settings.png b/Main/res/drawable-mdpi/action_settings.png
new file mode 100644
index 0000000000000000000000000000000000000000..d3e42edcb6db096d90d1b28a2d4609d3419c5751
Binary files /dev/null and b/Main/res/drawable-mdpi/action_settings.png differ
diff --git a/Main/res/drawable-mdpi/content_remove.png b/Main/res/drawable-mdpi/content_remove.png
new file mode 100644
index 0000000000000000000000000000000000000000..3336760d5f3efdfefefd6899317c3a09264430f6
Binary files /dev/null and b/Main/res/drawable-mdpi/content_remove.png differ
diff --git a/Main/res/drawable-mdpi/grnet_logo.png b/Main/res/drawable-mdpi/grnet_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc4e4607c1b14ac1b92e637157e735d0bc219a30
Binary files /dev/null and b/Main/res/drawable-mdpi/grnet_logo.png differ
diff --git a/Main/res/drawable-mdpi/ic_launcher.png b/Main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1959d54fd65e38c346521141a2fb6c4a057da0c
Binary files /dev/null and b/Main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/Main/res/drawable-xhdpi/action_search.png b/Main/res/drawable-xhdpi/action_search.png
new file mode 100644
index 0000000000000000000000000000000000000000..3549f84dd8f5e38665849b46e252bc34f29be027
Binary files /dev/null and b/Main/res/drawable-xhdpi/action_search.png differ
diff --git a/Main/res/drawable-xhdpi/action_settings.png b/Main/res/drawable-xhdpi/action_settings.png
new file mode 100644
index 0000000000000000000000000000000000000000..09b01483454788c435acf715c57d92fc0ead0a7c
Binary files /dev/null and b/Main/res/drawable-xhdpi/action_settings.png differ
diff --git a/Main/res/drawable-xhdpi/content_remove.png b/Main/res/drawable-xhdpi/content_remove.png
new file mode 100644
index 0000000000000000000000000000000000000000..f391760ef134adb96dcce85abb2c5ab776f8e6bd
Binary files /dev/null and b/Main/res/drawable-xhdpi/content_remove.png differ
diff --git a/Main/res/drawable-xhdpi/grnet_logo.png b/Main/res/drawable-xhdpi/grnet_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc4e4607c1b14ac1b92e637157e735d0bc219a30
Binary files /dev/null and b/Main/res/drawable-xhdpi/grnet_logo.png differ
diff --git a/Main/res/drawable-xhdpi/ic_launcher.png b/Main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..77dfe95250ab670b172afa695a08658776a91ebb
Binary files /dev/null and b/Main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/Main/res/layout/change_password.xml b/Main/res/layout/change_password.xml
new file mode 100644
index 0000000000000000000000000000000000000000..aa42507bad1a692f9b44114575852762d6e506f8
--- /dev/null
+++ b/Main/res/layout/change_password.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Main/res/layout/inspection_results.xml b/Main/res/layout/inspection_results.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0b895f26518046f0c4aab3478dde6ae404f68762
--- /dev/null
+++ b/Main/res/layout/inspection_results.xml
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Main/res/layout/main.xml b/Main/res/layout/main.xml
new file mode 100644
index 0000000000000000000000000000000000000000..cab03eceb25f4d89b14e8af8ef844100214ac6da
--- /dev/null
+++ b/Main/res/layout/main.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Main/res/menu/menu_application.xml b/Main/res/menu/menu_application.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8ead7fa6d820cae9c69ebdd3168c97fa7f552c3b
--- /dev/null
+++ b/Main/res/menu/menu_application.xml
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/Main/res/values-el/strings.xml b/Main/res/values-el/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4568dcfa7dd7be7c5881582755b9904b24d363b4
--- /dev/null
+++ b/Main/res/values-el/strings.xml
@@ -0,0 +1,51 @@
+
+
+ Academic ID Inspector
+
+ Όνομα Χρήστη:
+ Κωδικός Πρόσβασης:
+ Είσοδος
+
+ Παρακαλώ περιμένετε…
+ Το όνομα Χρήστη ή ο Κωδικός Πρόσβασης είναι εσφαλμένα
+ Η κάμερα δεν είναι διαθέσιμη
+
+ Δίκτυο μη Διαθέσιμο
+ Για ενεργοποίηση πιέστε Επόμενο
+ Επόμενο
+ Τερματισμός
+
+ Σαρώστε τον κωδικό QR ή εισάγετε τον σειριακό αριθμό
+ Σάρωση QR
+ Εισαγωγή Σειριακού Αρ.
+ Πιέστε ξανά για έξοδο
+
+ ΕΓΚΥΡΟ
+ ΑΚΥΡΟ
+ Σειριακός Αριθμός:
+ Όνομα:
+ Έδρα Σχολής:
+ Κάτοικος:
+ Αιτιολογία:
+
+ Το στοιχείο εισόδου δεν αντιστοιχεί σε έγκυρη ακαδημαϊκή ταυτότητα
+ Παρουσιάστηκε σφάλμα κατά την επιθεώρησης της ακαδημαϊκής ταυτότητας
+
+ Ρυθμίσεις
+
+ Αλλαγή Κωδικού Πρόσβασης
+ Αλλαγή του κωδικού πρόσβασης του χρήστη
+
+ Τρέχων Κωδικός Πρόσβασης:
+ Νέος Κωδικός Πρόσβασης:
+ Επιβεβαίωση Νέου Κωδικού:
+ Ακύρωση
+ Επιβεβαίωση
+
+ Απαιτείται αλλαγή του κωδικού πρόσβασης
+ Ο τρέχων κωδικός πρόσβασης είναι λάθος
+ Οι νέοι κωδικοί πρόσβασης δεν είναι ίδιοι
+ Ο κωδικός πρόσβασης πρέπει να περιέχει τουλάχιστον 4 χαρακτήρες
+ Ο νέος και ο τρέχον κωδικός πρόσβασης δεν πρέπει να είναι ίδιοι
+ Παρουσιάστηκε σφάλμα κατα την διαδικασία αποθήκευσης του νέου κωδικού πρόσβασης
+
\ No newline at end of file
diff --git a/Main/res/values/strings.xml b/Main/res/values/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3d956f57c5c9e3a79cecea9ea30e7aefee393c06
--- /dev/null
+++ b/Main/res/values/strings.xml
@@ -0,0 +1,51 @@
+
+
+ Academic ID Inspector
+
+ Username:
+ Password:
+ Sign In
+
+ Please wait…
+ Incorrect username or password
+ Rear Facing Camera Unavailable
+
+ No Network Connection
+ To enable network connection touch Next
+ Next
+ Close
+
+ Scan QR code or enter serial number
+ Scan QR Code
+ Enter Serial Number
+ Press again to exit
+
+ VALID
+ INVALID
+ Serial Number:
+ Name:
+ University Location:
+ Resident:
+ Reason:
+
+ Input is not a valid Academic ID
+ Error trying to inspect academic ID
+
+ Settings
+
+ Change Password
+ Change the user password
+
+ Current Password:
+ New Password:
+ Confirm New Password:
+ Cancel
+ Confirm
+
+ Password changed required
+ Current password is incorrect
+ The new passwords don\'t match
+ Password must contain at least 4 characters
+ New and current password should not be the same
+ Error trying to update user password
+
\ No newline at end of file
diff --git a/Main/res/values/styles.xml b/Main/res/values/styles.xml
new file mode 100644
index 0000000000000000000000000000000000000000..acec8756c6e83bcb0adf67840dbc15ed6c0a1c8d
--- /dev/null
+++ b/Main/res/values/styles.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Main/res/xml/preferences.xml b/Main/res/xml/preferences.xml
new file mode 100644
index 0000000000000000000000000000000000000000..69bb6f48e5620407252b7a1f48ec5ed0bf2e2b5f
--- /dev/null
+++ b/Main/res/xml/preferences.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/ChangePassword.java b/Main/src/gr/grnet/academicid/inspector/ChangePassword.java
new file mode 100644
index 0000000000000000000000000000000000000000..a30c5b2b3f73721e1cec6e63691d4af82c0e1519
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/ChangePassword.java
@@ -0,0 +1,128 @@
+package gr.grnet.academicid.inspector;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.Toast;
+import gr.grnet.academicid.inspector.domain.Inspector;
+import gr.grnet.academicid.inspector.parser.ChangePasswordResponse;
+import gr.grnet.academicid.inspector.parser.JSONParser;
+import gr.grnet.academicid.inspector.services.ServiceHandler;
+import gr.grnet.academicid.inspector.utilities.Constants;
+import gr.grnet.academicid.inspector.utilities.Tools;
+
+public class ChangePassword extends Activity {
+
+ private EditText etxtCurrentPassword;
+ private EditText etxtNewPassword;
+ private EditText etxtNewPasswordConfirmation;
+
+ private ProgressDialog progressDialog;
+ private Inspector inspector;
+ private String newPasswordHash;
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.change_password);
+
+ etxtCurrentPassword = (EditText) findViewById(R.id.etxt_current_password);
+ etxtNewPassword = (EditText) findViewById(R.id.etxt_new_password);
+ etxtNewPasswordConfirmation = (EditText) findViewById(R.id.etxt_new_password_confirmation);
+
+ inspector = InspectorApplication.getInspector();
+ if (inspector.shouldChangePswAtLogin())
+ Toast.makeText(getApplicationContext(), getString(R.string.msg_password_change_required), Toast.LENGTH_LONG).show();
+ }
+
+ public void cancel(View view) {
+ finish();
+ }
+
+ public void confirm(View view) {
+ // After 'Sign In' button is pressed, hide soft keyboard
+ InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (getCurrentFocus() != null)
+ inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
+
+ String currentPasswordHash = Tools.getHash(etxtCurrentPassword.getText().toString());
+ String newPassword = etxtNewPassword.getText().toString();
+ newPasswordHash = Tools.getHash(newPassword);
+ String newPasswordConfirmationHash = Tools.getHash(etxtNewPasswordConfirmation.getText().toString());
+
+ inspector = InspectorApplication.getInspector();
+
+ // Check if the Current password is the actual password of the user by comparing the hash values.
+ if (!inspector.getPasswordHash().equals(currentPasswordHash)) {
+ Toast.makeText(getApplicationContext(), getString(R.string.msg_incorrect_current_password), Toast.LENGTH_SHORT).show();
+ }
+ // Check if the new password and the new password confirmation match by comparing the hash values.
+ else if (!newPasswordHash.equals(newPasswordConfirmationHash)) {
+ Toast.makeText(getApplicationContext(), getString(R.string.msg_new_passwords_dont_match), Toast.LENGTH_SHORT).show();
+ }
+ // Check if new password is at least 4 chars long.
+ else if (newPassword.length() < 4) {
+ Toast.makeText(getApplicationContext(), getString(R.string.msg_password_too_short), Toast.LENGTH_SHORT).show();
+ }
+ // Check if new password is the same with current password by comparing the hash values.
+ else if (currentPasswordHash.equals(newPasswordHash)) {
+ Toast.makeText(getApplicationContext(), getString(R.string.msg_new_current_password_same), Toast.LENGTH_SHORT).show();
+ } else {
+ new UpdatePassword().execute(inspector.getUsername(), inspector.getPasswordHash(), newPasswordHash);
+ }
+ }
+
+ private class UpdatePassword extends AsyncTask {
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ // Showing progress dialog
+ progressDialog = new ProgressDialog(ChangePassword.this);
+ progressDialog.setMessage(getString(R.string.msg_wait));
+ progressDialog.setCancelable(false);
+ progressDialog.show();
+ }
+
+ @Override
+ protected String doInBackground(String... variables) {
+ return ServiceHandler.makeChangePswCall(variables[0], variables[1], variables[2]);
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ super.onPostExecute(result);
+ // Dismiss the progress dialog
+ if (progressDialog.isShowing())
+ progressDialog.dismiss();
+
+ ChangePasswordResponse changePasswordResponse = JSONParser.getResultOfUpdatePassword(result);
+
+ if (!changePasswordResponse.isSuccessful()) {
+ Toast.makeText(getApplicationContext(), getString(R.string.msg_error_changing_password), Toast.LENGTH_SHORT).show();
+ Log.e(Constants.LOGTAG, "ChangePassword.UpdatePassword.OnPostExecute(): Web service returned unsuccessfully with errorCode: " + changePasswordResponse.getError());
+ } else {
+ Log.i(Constants.LOGTAG, "ChangePassword.UpdatePassword.OnPostExecute(): User " + inspector.getUsername() + " successfully changed his password.");
+
+ inspector = InspectorApplication.getInspector();
+ boolean userChangedPsw = inspector.shouldChangePswAtLogin();
+ inspector.setPasswordHash(newPasswordHash);
+ inspector.setChangePswAtLogin(false);
+ InspectorApplication.setInspector(inspector);
+
+ if (userChangedPsw)
+ startActivity(new Intent(ChangePassword.this, InspectionResults.class));
+ else
+ finish();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/InspectionResults.java b/Main/src/gr/grnet/academicid/inspector/InspectionResults.java
new file mode 100644
index 0000000000000000000000000000000000000000..383ac37d1ee2228c7cee44145e4315feb4e810d1
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/InspectionResults.java
@@ -0,0 +1,265 @@
+package gr.grnet.academicid.inspector;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+import gr.grnet.academicid.inspector.domain.AcademicId;
+import gr.grnet.academicid.inspector.domain.Inspector;
+import gr.grnet.academicid.inspector.parser.InspectAcademicIdResponse;
+import gr.grnet.academicid.inspector.parser.JSONParser;
+import gr.grnet.academicid.inspector.services.ServiceHandler;
+import gr.grnet.academicid.inspector.utilities.Constants;
+import gr.grnet.academicid.inspector.utilities.Tools;
+import org.jetbrains.annotations.NotNull;
+
+public class InspectionResults extends Activity {
+
+ private static final int SCAN_QR = 1;
+ private static final int REFERRAL_QR = 1;
+ private static final int REFERRAL_SERIAL = 2;
+
+ private TextView lblInitMessage;
+ private TextView txtvInspectionResult;
+ private TextView lblSerialNumber;
+ private TextView txtvSerialNumber;
+ private TextView lblConcatName;
+ private TextView txtvConcatName;
+ private TextView lblUniversity;
+ private TextView txtvUniversity;
+ private TextView lblResidence;
+ private TextView txtvResidence;
+ private TextView lblInspectionDescription;
+ private TextView txtvInspectionDescription;
+ private EditText etxtSerialNumber;
+
+ private ProgressDialog progressDialog;
+ private int referral;
+ private boolean doubleBackToExitPressedOnce = false;
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.inspection_results);
+
+ // Assign views to variables
+ lblInitMessage = (TextView) findViewById(R.id.lbl_init_message);
+ txtvInspectionResult = (TextView) findViewById(R.id.txtv_inspection_result);
+
+ lblSerialNumber = (TextView) findViewById(R.id.lbl_serial_number);
+ txtvSerialNumber = (TextView) findViewById(R.id.txtv_serial_number);
+
+ lblConcatName = (TextView) findViewById(R.id.lbl_concat_name);
+ txtvConcatName = (TextView) findViewById(R.id.txtv_concat_name);
+
+ lblUniversity = (TextView) findViewById(R.id.lbl_university);
+ txtvUniversity = (TextView) findViewById(R.id.txtv_university);
+
+ lblResidence = (TextView) findViewById(R.id.lbl_residence);
+ txtvResidence = (TextView) findViewById(R.id.txtv_residence);
+
+ lblInspectionDescription = (TextView) findViewById(R.id.lbl_inspection_description);
+ txtvInspectionDescription = (TextView) findViewById(R.id.txtv_inspection_description);
+
+ etxtSerialNumber = (EditText) findViewById(R.id.etxt_serial_number);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ this.doubleBackToExitPressedOnce = false;
+ }
+
+ // Method called after 'Scan QR' button is pressed
+ public void scanQR(View view) {
+ Intent intent = new Intent(this, ScanQR.class);
+ startActivityForResult(intent, SCAN_QR);
+ }
+
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == SCAN_QR) {
+ if (resultCode == RESULT_OK) {
+ String scanResult = data.getStringExtra(ScanQR.SCAN_RESULT);
+ referral = REFERRAL_QR;
+ validate(scanResult);
+ }
+ }
+ }
+
+ // Method called after 'Magnifier' button is pressed
+ public void checkSerialNo(View view) {
+ // After 'Check Id' button is pressed, hide soft keyboard
+ InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (getCurrentFocus() != null)
+ inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
+
+ String input = etxtSerialNumber.getText().toString();
+ referral = REFERRAL_SERIAL;
+ validate(input);
+ }
+
+ // Method called after 'X' button is pressed
+ public void clearContent(View view) {
+ etxtSerialNumber.setText("");
+ }
+
+ private void validate(String academicId) {
+ if ((academicId.length() == 12) && Tools.isNumericValue(academicId)) {
+ Inspector inspector = InspectorApplication.getInspector();
+ new ValidateAcademicId().execute(inspector.getUsername(), inspector.getPasswordHash(), academicId);
+ } else {
+ // Hide most of the views...
+ lblInitMessage.setVisibility(View.GONE);
+ lblSerialNumber.setVisibility(View.GONE);
+ txtvSerialNumber.setVisibility(View.GONE);
+ lblConcatName.setVisibility(View.GONE);
+ txtvConcatName.setVisibility(View.GONE);
+ lblUniversity.setVisibility(View.GONE);
+ txtvUniversity.setVisibility(View.GONE);
+ lblResidence.setVisibility(View.GONE);
+ txtvResidence.setVisibility(View.GONE);
+ lblInspectionDescription.setVisibility(View.GONE);
+
+ if (referral == REFERRAL_QR)
+ etxtSerialNumber.setText("");
+
+ // ... and present only 'Invalid' message and reason
+ txtvInspectionResult.setVisibility(View.VISIBLE);
+ txtvInspectionResult.setTextAppearance(getApplicationContext(), R.style.intro_blurb_orange);
+ txtvInspectionResult.setText(R.string.lbl_invalid);
+
+ txtvInspectionDescription.setVisibility(View.VISIBLE);
+ txtvInspectionDescription.setText(R.string.msg_invalid_input);
+ }
+ }
+
+ private class ValidateAcademicId extends AsyncTask {
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ // Showing progress dialog
+ progressDialog = new ProgressDialog(InspectionResults.this);
+ progressDialog.setMessage(getString(R.string.msg_wait));
+ progressDialog.setCancelable(false);
+ progressDialog.show();
+ }
+
+ @Override
+ protected String doInBackground(String... variables) {
+ return ServiceHandler.makeInspectAcademicIdCall(variables[0], variables[1], variables[2]);
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ super.onPostExecute(result);
+ // Dismiss the progress dialog
+ if (progressDialog.isShowing())
+ progressDialog.dismiss();
+
+ InspectAcademicIdResponse inspectAcademicIdResponse = JSONParser.getResultOfInspectAcademicId(result);
+
+ if (!inspectAcademicIdResponse.isSuccessful()) {
+ Toast.makeText(getApplicationContext(), R.string.msg_error_inspecting_academicId, Toast.LENGTH_SHORT).show();
+ Log.e(Constants.LOGTAG, "InspectionResults.ValidateAcademicId.onPostExecute(): Web service returned unsuccessfully with errorCode: " + inspectAcademicIdResponse.getError());
+ } else {
+ AcademicId academicId = inspectAcademicIdResponse.getAcademicId();
+
+ lblInitMessage.setVisibility(View.GONE);
+ txtvInspectionResult.setVisibility(View.VISIBLE);
+
+ lblSerialNumber.setVisibility(View.VISIBLE);
+ txtvSerialNumber.setVisibility(View.VISIBLE);
+ txtvSerialNumber.setText(String.valueOf(academicId.getSerialNumber()));
+
+ lblConcatName.setVisibility(View.VISIBLE);
+ txtvConcatName.setVisibility(View.VISIBLE);
+ txtvConcatName.setText(Tools.concatenateName(academicId));
+
+ lblUniversity.setVisibility(View.VISIBLE);
+ txtvUniversity.setVisibility(View.VISIBLE);
+ txtvUniversity.setText(academicId.getUniversityLocation());
+
+ lblResidence.setVisibility(View.VISIBLE);
+ txtvResidence.setVisibility(View.VISIBLE);
+ txtvResidence.setText(academicId.getResidenceLocation());
+
+ if (referral == REFERRAL_QR)
+ etxtSerialNumber.setText("");
+
+
+ if (academicId.isPasoValid() != null && academicId.isPasoValid()) { // If Academic Id is eligible as a a discount voucher
+ // show 'Valid' message...
+ txtvInspectionResult.setTextAppearance(getApplicationContext(), R.style.intro_blurb_green);
+ txtvInspectionResult.setText(R.string.lbl_valid);
+
+ // ... and hide inspection description view (which is empty)
+ lblInspectionDescription.setVisibility(View.GONE);
+ txtvInspectionDescription.setVisibility(View.GONE);
+
+ } else { // If academic Id is not eligible as a discount voucher
+ // show 'Invalid' message...
+ txtvInspectionResult.setTextAppearance(getApplicationContext(), R.style.intro_blurb_orange);
+ txtvInspectionResult.setText(R.string.lbl_invalid);
+
+ if (academicId.thereIsValidationError()) { // ... and if there is a reason why it not valid
+ // then display it
+ lblInspectionDescription.setVisibility(View.VISIBLE);
+ lblInspectionDescription.setTextAppearance(getApplicationContext(), R.style.label_orange);
+
+ txtvInspectionDescription.setVisibility(View.VISIBLE);
+ txtvInspectionDescription.setText(academicId.getValidationError());
+ } else { // or make view invisible
+ lblInspectionDescription.setVisibility(View.GONE);
+ txtvInspectionDescription.setVisibility(View.GONE);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.menu_application, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, @NotNull MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_settings:
+ startActivity(new Intent(this, Preferences.class));
+ return true;
+ default:
+ return super.onMenuItemSelected(featureId, item);
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (doubleBackToExitPressedOnce) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ startActivity(intent);
+ finish();
+ } else {
+ this.doubleBackToExitPressedOnce = true;
+ Toast.makeText(this, R.string.lbl_exit, Toast.LENGTH_SHORT).show();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/InspectorApplication.java b/Main/src/gr/grnet/academicid/inspector/InspectorApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..3dcbe7169f2c52f97f8a9c99e4ec560db2c6185a
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/InspectorApplication.java
@@ -0,0 +1,48 @@
+package gr.grnet.academicid.inspector;
+
+import android.app.Application;
+import android.content.Context;
+import gr.grnet.academicid.inspector.domain.Inspector;
+import gr.grnet.academicid.inspector.utilities.SharedPrefs;
+
+public class InspectorApplication extends Application {
+
+ private static Context context;
+ private static Inspector inspector;
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ public InspectorApplication() {
+ super();
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ InspectorApplication.context = getApplicationContext();
+ }
+
+ @Override
+ public void onTerminate() {
+ super.onTerminate();
+ SharedPrefs.ClearInspectorFromPrefs();
+ }
+
+ public static Context getAppContext() {
+ return InspectorApplication.context;
+ }
+
+ public static Inspector getInspector() {
+ // Check if object has been deleted from Android garbage collector and if it has, recall it from Shared Preferences
+ if (inspector == null) {
+ inspector = SharedPrefs.getInspectorFromPrefs();
+ }
+ return inspector;
+ }
+
+ public static void setInspector(Inspector inspector) {
+ InspectorApplication.inspector = inspector;
+ //Save object to Shared Preferences to prevent NPE when app resuming and android garbage collector has cleared this
+ SharedPrefs.setInspectorToPrefs(inspector);
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/MainActivity.java b/Main/src/gr/grnet/academicid/inspector/MainActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..445fef631dd1b0e35153e0bdfc70fe2dc421d07c
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/MainActivity.java
@@ -0,0 +1,125 @@
+package gr.grnet.academicid.inspector;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.Toast;
+import gr.grnet.academicid.inspector.domain.Inspector;
+import gr.grnet.academicid.inspector.parser.LoadUserResponse;
+import gr.grnet.academicid.inspector.services.ServiceHandler;
+import gr.grnet.academicid.inspector.parser.JSONParser;
+import gr.grnet.academicid.inspector.utilities.Constants;
+import gr.grnet.academicid.inspector.utilities.Tools;
+
+public class MainActivity extends Activity {
+
+ private EditText etxtUsername;
+ private EditText etxtPassword;
+
+ private ProgressDialog progressDialog;
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ etxtUsername = (EditText) findViewById(R.id.etxt_username);
+ etxtPassword = (EditText) findViewById(R.id.etxt_password);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (!Tools.isNetworkAvailable()) {
+ AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
+ alertDialog.setTitle(getString(R.string.title_no_net_available));
+ alertDialog.setMessage(getString(R.string.msg_no_net_available));
+
+ alertDialog.setPositiveButton(getString(R.string.action_next), new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
+ }
+ });
+ alertDialog.setNegativeButton(getString(R.string.action_close), new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ });
+ alertDialog.show();
+ }
+ }
+
+ // Method called after 'Sign In' button is pressed
+ public void signIn(View view) {
+ // After 'Sign In' button is pressed, hide soft keyboard
+ InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (getCurrentFocus() != null)
+ inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
+
+ String username = etxtUsername.getText().toString();
+ String password = etxtPassword.getText().toString();
+
+ // Password is hashed and passed to async task for authorization
+ new Authenticate().execute(username, Tools.getHash(password));
+ }
+
+ // Inner class to perform the network connection asynchronously
+ private class Authenticate extends AsyncTask {
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ // Showing progress dialog
+ progressDialog = new ProgressDialog(MainActivity.this);
+ progressDialog.setMessage(getString(R.string.msg_wait));
+ progressDialog.setCancelable(false);
+ progressDialog.show();
+ }
+
+ @Override
+ protected String doInBackground(String... credentials) {
+ return ServiceHandler.makeSingInCall(credentials[0], credentials[1]);
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ super.onPostExecute(result);
+ // Dismiss the progress dialog
+ if (progressDialog.isShowing())
+ progressDialog.dismiss();
+
+ LoadUserResponse loadUserResponse = JSONParser.getResultOfLoadUser(result);
+
+ if (!loadUserResponse.isSuccessful()) {
+ Toast.makeText(getApplicationContext(), getString(R.string.msg_incorrect_credentials), Toast.LENGTH_SHORT).show();
+ Log.e(Constants.LOGTAG, "MainActivity.Authenticate.OnPostExecute(): Web service returned unsuccessfully with errorCode: " + loadUserResponse.getError());
+ } else {
+ Inspector inspector = loadUserResponse.getInspector();
+ String password = etxtPassword.getText().toString();
+ inspector.setPasswordHash(Tools.getHash(password));
+ InspectorApplication.setInspector(inspector);
+
+ Log.i(Constants.LOGTAG, "User: " + inspector.getUsername() + "successfully logged in");
+
+ // Check if user should change his password and redirect him to that view
+ if (inspector.shouldChangePswAtLogin()) {
+ startActivity(new Intent(MainActivity.this, ChangePassword.class));
+ } else {
+ startActivity(new Intent(MainActivity.this, InspectionResults.class));
+ }
+ }
+ }
+ }
+}
diff --git a/Main/src/gr/grnet/academicid/inspector/Preferences.java b/Main/src/gr/grnet/academicid/inspector/Preferences.java
new file mode 100644
index 0000000000000000000000000000000000000000..230b15aaf7573e25341edce0096ecde51ac87d27
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/Preferences.java
@@ -0,0 +1,11 @@
+package gr.grnet.academicid.inspector;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+public class Preferences extends PreferenceActivity {
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences);
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/ScanQR.java b/Main/src/gr/grnet/academicid/inspector/ScanQR.java
new file mode 100644
index 0000000000000000000000000000000000000000..3fbbd45b698fc77fd1572319d60faf1e3d52a8bc
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/ScanQR.java
@@ -0,0 +1,60 @@
+package gr.grnet.academicid.inspector;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
+import com.dm.zbar.android.scanner.ZBarConstants;
+import com.dm.zbar.android.scanner.ZBarScannerActivity;
+import gr.grnet.academicid.inspector.utilities.Constants;
+import gr.grnet.academicid.inspector.utilities.Tools;
+import net.sourceforge.zbar.Symbol;
+
+public class ScanQR extends Activity {
+
+ private static final int ZBAR_SCANNER_REQUEST = 0;
+ private static final int ZBAR_QR_SCANNER_REQUEST = 1;
+
+ public static final String SCAN_RESULT = "ScanResult";
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (Tools.isCameraAvailable()) {
+ Intent intent = new Intent(this, ZBarScannerActivity.class);
+ intent.putExtra(ZBarConstants.SCAN_MODES, new int[]{Symbol.QRCODE});
+ startActivityForResult(intent, ZBAR_SCANNER_REQUEST);
+ } else {
+ Toast.makeText(this, getString(R.string.msg_no_camera_available), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case ZBAR_SCANNER_REQUEST:
+ case ZBAR_QR_SCANNER_REQUEST:
+ if (resultCode == RESULT_OK) {
+ String scanResult = data.getStringExtra(ZBarConstants.SCAN_RESULT);
+
+ Intent intent = new Intent();
+ intent.putExtra(SCAN_RESULT, scanResult);
+
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ } else if (resultCode == RESULT_CANCELED && data != null) {
+ String error = data.getStringExtra(ZBarConstants.ERROR_INFO);
+ if (!TextUtils.isEmpty(error)) {
+ Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
+ Log.e(Constants.LOGTAG, "ScanQR.onActivityResult(): " + error);
+ }
+ }
+ break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/domain/AcademicId.java b/Main/src/gr/grnet/academicid/inspector/domain/AcademicId.java
new file mode 100644
index 0000000000000000000000000000000000000000..bbc95c37fdf1489468e06fd05a8e92b1c23af979
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/domain/AcademicId.java
@@ -0,0 +1,114 @@
+package gr.grnet.academicid.inspector.domain;
+
+
+@SuppressWarnings("UnusedDeclaration")
+public class AcademicId {
+
+ private long serialNumber;
+ private String greekFirstname;
+ private String greekLastname;
+ private String latinFirstname;
+ private String latinLastname;
+ private String universityLocation;
+ private String residenceLocation;
+ private String pasoValidity;
+ private boolean webSuccess;
+ private String validationError;
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ public AcademicId() {
+ }
+
+ public long getSerialNumber() {
+ return serialNumber;
+ }
+
+ public void setSerialNumber(long serialNumber) {
+ this.serialNumber = serialNumber;
+ }
+
+ public String getGreekFirstname() {
+ return greekFirstname;
+ }
+
+ public void setGreekFirstname(String greekFirstname) {
+ this.greekFirstname = greekFirstname;
+ }
+
+ public String getGreekLastname() {
+ return greekLastname;
+ }
+
+ public void setGreekLastname(String greekLastname) {
+ this.greekLastname = greekLastname;
+ }
+
+ public String getLatinFirstname() {
+ return latinFirstname;
+ }
+
+ public void setLatinFirstname(String latinFirstname) {
+ this.latinFirstname = latinFirstname;
+ }
+
+ public String getLatinLastname() {
+ return latinLastname;
+ }
+
+ public void setLatinLastname(String latinLastname) {
+ this.latinLastname = latinLastname;
+ }
+
+ public String getUniversityLocation() {
+ return universityLocation;
+ }
+
+ public void setUniversityLocation(String universityLocation) {
+ this.universityLocation = universityLocation;
+ }
+
+ public String getResidenceLocation() {
+ return residenceLocation;
+ }
+
+ public void setResidenceLocation(String residenceLocation) {
+ this.residenceLocation = residenceLocation;
+ }
+
+ public String getPasoValidity() {
+ return pasoValidity;
+ }
+
+ public void setPasoValidity(String pasoValidity) {
+ this.pasoValidity = pasoValidity;
+ }
+
+ public boolean isWebSuccess() {
+ return webSuccess;
+ }
+
+ public void setWebSuccess(boolean webSuccess) {
+ this.webSuccess = webSuccess;
+ }
+
+ public String getValidationError() {
+ return validationError;
+ }
+
+ public void setValidationError(String validationError) {
+ this.validationError = validationError;
+ }
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ public Boolean isPasoValid() {
+ if (this.getPasoValidity().equals("Ναι")) return true;
+ if (this.getPasoValidity().equals("Όχι")) return false;
+ return null;
+ }
+
+ public boolean thereIsValidationError() {
+ return ((validationError != null) && (!validationError.equals("")) && (!validationError.equals("null")));
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/domain/Inspector.java b/Main/src/gr/grnet/academicid/inspector/domain/Inspector.java
new file mode 100644
index 0000000000000000000000000000000000000000..d06a334b4e7e84618a287a609ddb66a41301bde8
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/domain/Inspector.java
@@ -0,0 +1,91 @@
+package gr.grnet.academicid.inspector.domain;
+
+public class Inspector {
+
+ public static final String FIRSTNAME = "firstname";
+ public static final String LASTNAME = "lastname";
+ public static final String USERNAME = "username";
+ public static final String PASSWORD_HASH = "passwordHash";
+ public static final String CHANGE_PSW_AT_LOGIN = "changePassword";
+ public static final String ORG_ID = "orgId";
+ public static final String ORG_NAME = "orgName";
+ public static final String ORG_DESCRIPTION = "orgDescription";
+
+ private String firstname;
+ private String lastname;
+ private String username;
+ private String passwordHash;
+ private boolean changePswAtLogin;
+ private Long orgId;
+ private String orgName;
+ private String orgDescription;
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ public Inspector() {
+ }
+
+ public String getFirstname() {
+ return firstname;
+ }
+
+ public void setFirstname(String firstname) {
+ this.firstname = firstname;
+ }
+
+ public String getLastname() {
+ return lastname;
+ }
+
+ public void setLastname(String lastname) {
+ this.lastname = lastname;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPasswordHash() {
+ return passwordHash;
+ }
+
+ public void setPasswordHash(String passwordHash) {
+ this.passwordHash = passwordHash;
+ }
+
+ public boolean shouldChangePswAtLogin() {
+ return changePswAtLogin;
+ }
+
+ public void setChangePswAtLogin(boolean changePswAtLogin) {
+ this.changePswAtLogin = changePswAtLogin;
+ }
+
+ public Long getOrgId() {
+ return orgId;
+ }
+
+ public void setOrgId(Long orgId) {
+ this.orgId = orgId;
+ }
+
+ public String getOrgName() {
+ return orgName;
+ }
+
+ public void setOrgName(String orgName) {
+ this.orgName = orgName;
+ }
+
+ public String getOrgDescription() {
+ return orgDescription;
+ }
+
+ public void setOrgDescription(String orgDescription) {
+ this.orgDescription = orgDescription;
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/parser/ChangePasswordResponse.java b/Main/src/gr/grnet/academicid/inspector/parser/ChangePasswordResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae177f0f88866a6ff462babf8f94603236d9dc67
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/parser/ChangePasswordResponse.java
@@ -0,0 +1,35 @@
+package gr.grnet.academicid.inspector.parser;
+
+@SuppressWarnings("UnusedDeclaration")
+public class ChangePasswordResponse {
+
+ private String response;
+ private String error;
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ public ChangePasswordResponse(String response, String error) {
+ this.response = response;
+ this.error = error;
+ }
+
+ public String getResponse() {
+ return response;
+ }
+
+ public void setResponse(String response) {
+ this.response = response;
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ public void setError(String error) {
+ this.error = error;
+ }
+
+ public boolean isSuccessful() {
+ return (response.equals(JSONParser.RESPONSE_SUCCESSFUL));
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/parser/InspectAcademicIdResponse.java b/Main/src/gr/grnet/academicid/inspector/parser/InspectAcademicIdResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..d01ef658185689e490440f7b88810216c2ae75a5
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/parser/InspectAcademicIdResponse.java
@@ -0,0 +1,47 @@
+package gr.grnet.academicid.inspector.parser;
+
+import gr.grnet.academicid.inspector.domain.AcademicId;
+
+@SuppressWarnings("UnusedDeclaration")
+public class InspectAcademicIdResponse {
+
+ private String response;
+ private String error;
+ private AcademicId academicId;
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ public InspectAcademicIdResponse(String response, String error, AcademicId academicId) {
+ this.response = response;
+ this.error = error;
+ this.academicId = academicId;
+ }
+
+ public String getResponse() {
+ return response;
+ }
+
+ public void setResponse(String response) {
+ this.response = response;
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ public void setError(String error) {
+ this.error = error;
+ }
+
+ public AcademicId getAcademicId() {
+ return academicId;
+ }
+
+ public void setAcademicId(AcademicId academicId) {
+ this.academicId = academicId;
+ }
+
+ public boolean isSuccessful() {
+ return (response.equals(JSONParser.RESPONSE_SUCCESSFUL));
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/parser/JSONParser.java b/Main/src/gr/grnet/academicid/inspector/parser/JSONParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..48b0185ea17b9c5d660b2a5d0a4e5a59dcb4f86e
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/parser/JSONParser.java
@@ -0,0 +1,123 @@
+package gr.grnet.academicid.inspector.parser;
+
+import android.util.Log;
+import gr.grnet.academicid.inspector.domain.*;
+import gr.grnet.academicid.inspector.utilities.Constants;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class JSONParser {
+
+ // JSON Tags
+ private static final String TAG_RESPONSE = "response";
+ private static final String TAG_RESPONSE_ERROR = "errorReason";
+
+ public static final String RESPONSE_SUCCESSFUL = "SUCCESS";
+ public static final String RESPONSE_FAILED = "FAILURE";
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ /**
+ * Prevents this class from being instantiated.
+ */
+ private JSONParser() {
+ }
+
+ /**
+ * This method parses a json string and creates a {@link gr.grnet.academicid.inspector.parser.LoadUserResponse} object.
+ * It is called after {@link gr.grnet.academicid.inspector.services.ServiceHandler#makeSingInCall(String, String)}
+ *
+ * @param jsonString The string that should be parsed.
+ * @return A {@link gr.grnet.academicid.inspector.parser.LoadUserResponse} object.
+ */
+ public static LoadUserResponse getResultOfLoadUser(String jsonString) {
+ try {
+ JSONObject jsonObject = new JSONObject(jsonString);
+ String response = jsonObject.getString(TAG_RESPONSE);
+
+ if (response.equals(RESPONSE_SUCCESSFUL)) {
+ Inspector inspector = new Inspector();
+
+ JSONObject user = jsonObject.getJSONObject("user");
+ inspector.setFirstname(user.getString("firstName"));
+ inspector.setLastname(user.getString("lastName"));
+ inspector.setUsername(user.getString("username"));
+ inspector.setChangePswAtLogin(user.getBoolean("changePassword"));
+
+ JSONObject organization = user.getJSONObject("organization");
+ inspector.setOrgId(organization.getLong("id"));
+ inspector.setOrgName(organization.getString("name"));
+ inspector.setOrgDescription(organization.getString("description"));
+
+ return new LoadUserResponse(RESPONSE_SUCCESSFUL, null, inspector);
+ } else {
+ String error = jsonObject.getString(TAG_RESPONSE_ERROR);
+ return new LoadUserResponse(RESPONSE_FAILED, error, null);
+ }
+ } catch (JSONException e) {
+ Log.e(Constants.LOGTAG, "JSONParser.getResultOfLoadUser(): Error while parsing string: " + jsonString);
+ return null;
+ }
+ }
+
+ /**
+ * This method parses a json string and creates a {@link gr.grnet.academicid.inspector.parser.LoadUserResponse} object.
+ * It is called after {@link gr.grnet.academicid.inspector.services.ServiceHandler#makeInspectAcademicIdCall(String, String, String)}
+ *
+ * @param jsonString The string that should be parsed.
+ * @return A {@link gr.grnet.academicid.inspector.parser.InspectAcademicIdResponse} object.
+ */
+ public static InspectAcademicIdResponse getResultOfInspectAcademicId(String jsonString) {
+ try {
+ JSONObject jsonObject = new JSONObject(jsonString);
+ String response = jsonObject.getString(TAG_RESPONSE);
+
+ if (response.equals(RESPONSE_SUCCESSFUL)) {
+ AcademicId academicId = new AcademicId();
+
+ JSONObject inspectionResult = jsonObject.getJSONObject("inspectionResult");
+ academicId.setSerialNumber(inspectionResult.getLong("academicId"));
+ academicId.setGreekFirstname(inspectionResult.getString("greekFirstName"));
+ academicId.setGreekLastname(inspectionResult.getString("greekLastName"));
+ academicId.setLatinFirstname(inspectionResult.getString("latinFirstName"));
+ academicId.setLatinLastname(inspectionResult.getString("latinLastName"));
+ academicId.setUniversityLocation(inspectionResult.getString("universityLocation"));
+ academicId.setResidenceLocation(inspectionResult.getString("residenceLocation"));
+ academicId.setPasoValidity(inspectionResult.getString("pasoValidity"));
+ academicId.setWebSuccess(inspectionResult.getBoolean("webServiceSuccess"));
+ academicId.setValidationError(inspectionResult.getString("validationError"));
+
+ return new InspectAcademicIdResponse(RESPONSE_SUCCESSFUL, null, academicId);
+ } else {
+ String error = jsonObject.getString(TAG_RESPONSE_ERROR);
+ return new InspectAcademicIdResponse(RESPONSE_FAILED, error, null);
+ }
+ } catch (JSONException e) {
+ Log.e(Constants.LOGTAG, "JSONParser.getResultOfInspectAcademicId(): Error while parsing string: " + jsonString);
+ return null;
+ }
+ }
+
+ /**
+ * This method parses a json string and creates a {@link gr.grnet.academicid.inspector.parser.ChangePasswordResponse} object.
+ *
+ * @param jsonString The string that should be parsed.
+ * @return A {@link gr.grnet.academicid.inspector.parser.ChangePasswordResponse} object.
+ */
+ public static ChangePasswordResponse getResultOfUpdatePassword(String jsonString) {
+ try {
+ JSONObject jsonObject = new JSONObject(jsonString);
+ String response = jsonObject.getString(TAG_RESPONSE);
+
+ if (response.equals(RESPONSE_SUCCESSFUL)) {
+ return new ChangePasswordResponse(RESPONSE_SUCCESSFUL, null);
+ } else {
+ String error = jsonObject.getString(TAG_RESPONSE_ERROR);
+ return new ChangePasswordResponse(RESPONSE_FAILED, error);
+ }
+ } catch (JSONException e) {
+ Log.e(Constants.LOGTAG, "JSONParser.getResultOfUpdatePassword(): Error while parsing string: " + jsonString);
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/parser/LoadUserResponse.java b/Main/src/gr/grnet/academicid/inspector/parser/LoadUserResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..cdd61dedd9c9ae6868d90a0246924b63fa6d77b2
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/parser/LoadUserResponse.java
@@ -0,0 +1,47 @@
+package gr.grnet.academicid.inspector.parser;
+
+import gr.grnet.academicid.inspector.domain.Inspector;
+
+@SuppressWarnings("UnusedDeclaration")
+public class LoadUserResponse {
+
+ private String response;
+ private String error;
+ private Inspector inspector;
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ public LoadUserResponse(String response, String error, Inspector inspector) {
+ this.response = response;
+ this.error = error;
+ this.inspector = inspector;
+ }
+
+ public String getResponse() {
+ return response;
+ }
+
+ public void setResponse(String response) {
+ this.response = response;
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ public void setError(String error) {
+ this.error = error;
+ }
+
+ public Inspector getInspector() {
+ return inspector;
+ }
+
+ public void setInspector(Inspector inspector) {
+ this.inspector = inspector;
+ }
+
+ public boolean isSuccessful() {
+ return (response.equals(JSONParser.RESPONSE_SUCCESSFUL));
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/services/ServiceHandler.java b/Main/src/gr/grnet/academicid/inspector/services/ServiceHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..67eb0712668f30023732d3c500542bf4ad505467
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/services/ServiceHandler.java
@@ -0,0 +1,143 @@
+package gr.grnet.academicid.inspector.services;
+
+import android.util.Log;
+import gr.grnet.academicid.inspector.utilities.Constants;
+import gr.grnet.academicid.inspector.utilities.Tools;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+
+public class ServiceHandler {
+
+ // The URL of the server that handles the requests
+ private static final String URL = "http://academicidapp.grnet.gr:8080/";
+// private static final String URL = "http://academicidappbuilder.grnet.gr:8080/";
+
+ // Path of web services
+ private static final String PATH = "admin/web/ws/users/";
+
+ // Each string represents a different call to a web service
+ private static final String LOAD_USER = "loadUser"; // Get method
+ private static final String INSPECT_ID = "inspectAcademicID"; // Post method
+ private static final String CHANGE_PSW = "updatePassword"; // Post method
+
+ // This is the content type dictated by server side
+ private static final String DEFAULT_CONTENT_TYPE = "application/json";
+
+ // This is the accepted encoding dictated by server side
+ private static final String ACCEPTED_ENCODING = "application/json";
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ /**
+ * Prevents this class from being instantiated.
+ */
+ private ServiceHandler() {
+ }
+
+ /**
+ * This method verifies that a user is a valid user.
+ *
+ * @param username The username of the user.
+ * @param passwordHash The password of the user hashed by {@link Tools#getHash(String)}.
+ * @return A json string.
+ */
+ public static String makeSingInCall(String username, String passwordHash) {
+ try {
+ HttpGet httpMethod = new HttpGet(URL + PATH + LOAD_USER);
+ httpMethod.addHeader("Content-Type", DEFAULT_CONTENT_TYPE);
+ httpMethod.addHeader("Accept-Encoding", ACCEPTED_ENCODING);
+
+ String credentialsEncoded = Tools.encodeBase64(username, passwordHash);
+ httpMethod.addHeader("Authorization", "Basic " + credentialsEncoded);
+
+ DefaultHttpClient client = new DefaultHttpClient();
+ HttpResponse httpResponse = client.execute(httpMethod);
+ HttpEntity httpEntity = httpResponse.getEntity();
+
+ return EntityUtils.toString(httpEntity);
+ } catch (IOException e) {
+ Log.e(Constants.LOGTAG, "ServiceHandler.makeSignInCall(): Error while user: " + username + " trying to sign in ", e);
+ return null;
+ }
+ }
+
+ /**
+ * This method checks the validity of an AcademicID as a reduced price voucher (PASO).
+ *
+ * @param username The username of the user performing the check.
+ * @param passwordHash The password (hashed) of the user performing the request.
+ * @param academicId The AcademicID serial number that is checked.
+ * @return A json string.
+ */
+ public static String makeInspectAcademicIdCall(String username, String passwordHash, String academicId) {
+ try {
+ HttpPost httpMethod = new HttpPost(URL + PATH + INSPECT_ID);
+ httpMethod.addHeader("Content-Type", DEFAULT_CONTENT_TYPE);
+ httpMethod.addHeader("Accept-Encoding", ACCEPTED_ENCODING);
+
+ String credentialsEncoded = Tools.encodeBase64(username, passwordHash);
+ httpMethod.addHeader("Authorization", "Basic " + credentialsEncoded);
+
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("SubmissionCode", academicId);
+ httpMethod.setEntity(new StringEntity(jsonObject.toString()));
+
+ DefaultHttpClient client = new DefaultHttpClient();
+ HttpResponse httpResponse = client.execute(httpMethod);
+ HttpEntity httpEntity = httpResponse.getEntity();
+
+ return EntityUtils.toString(httpEntity);
+ } catch (JSONException e) {
+ Log.e(Constants.LOGTAG, "ServiceHandler.makeInspectAcademicIdCall(): Error while trying to construct JSON Offer", e);
+ return null;
+ } catch (IOException e) {
+ Log.e(Constants.LOGTAG, "ServiceHandler.makeInspectAcademicIdCall(): Error while user " + username + " trying to validate AcademicID: " + academicId, e);
+ return null;
+ }
+ }
+
+ /**
+ * This method performs a password change request.
+ *
+ * @param username The username of the user.
+ * @param passwordHash The password of the user hashed by {@link Tools#getHash(String)}.
+ * @param newPasswordHash The new password of the user hashed by {@link Tools#getHash(String)}.
+ * @return A json string.
+ */
+ public static String makeChangePswCall(String username, String passwordHash, String newPasswordHash) {
+ try {
+ HttpPost httpMethod = new HttpPost(URL + PATH + CHANGE_PSW);
+ httpMethod.addHeader("Content-Type", DEFAULT_CONTENT_TYPE);
+ httpMethod.addHeader("Accept-Encoding", ACCEPTED_ENCODING);
+
+ String credentialsEncoded = Tools.encodeBase64(username, passwordHash);
+ httpMethod.addHeader("Authorization", "Basic " + credentialsEncoded);
+
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("newPassword", newPasswordHash);
+
+ httpMethod.setEntity(new StringEntity(jsonObject.toString()));
+
+ DefaultHttpClient client = new DefaultHttpClient();
+ HttpResponse httpResponse = client.execute(httpMethod);
+ HttpEntity httpEntity = httpResponse.getEntity();
+
+ return EntityUtils.toString(httpEntity);
+ } catch (JSONException e) {
+ Log.e(Constants.LOGTAG, "ServiceHandler.makeChangePswCall(): Error while trying to construct JSON string", e);
+ return null;
+ } catch (IOException e) {
+ Log.e(Constants.LOGTAG, "ServiceHandler.makeChangePswCall(): Error while user: " + username + " trying to change password", e);
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/utilities/Constants.java b/Main/src/gr/grnet/academicid/inspector/utilities/Constants.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d984e78b100961ac378c91165fc860784882f8e
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/utilities/Constants.java
@@ -0,0 +1,24 @@
+package gr.grnet.academicid.inspector.utilities;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Constants globally available in Inspector application.
+ */
+public class Constants {
+
+ public static final String LOGTAG = "Inspector";
+
+ // The message digest algorithm that is used to hash user password.
+ public static final String DIGEST_ALGORITHM = "MD5";
+
+ // An academicId is invalid if:
+ // (response=success && pasovalidity==false) OR (response=success && webservicesuccess==false && validationerror ΙΝ ValidationErrorList
+ public static List ValidationErrorList = Arrays.asList(
+ "Δεν βρέθηκε αίτηση με το 12ψήφιο κωδικό που εισάγατε",
+ "Ο 12ψήφιος που εισάγατε δεν αντιστοιχεί σε αίτηση φοιτητή",
+ "Η Ακαδημαϊκή Ταυτότητα δεν έχει υποβληθεί οριστικά από το φοιτητή",
+ "Η Ακαδημαϊκή Ταυτότητα έχει απορριφθεί από τη γραμματεία"
+ );
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/utilities/SharedPrefs.java b/Main/src/gr/grnet/academicid/inspector/utilities/SharedPrefs.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed02da47f5a195ae579d1085d77b357fec4727b8
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/utilities/SharedPrefs.java
@@ -0,0 +1,79 @@
+package gr.grnet.academicid.inspector.utilities;
+
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import gr.grnet.academicid.inspector.InspectorApplication;
+import gr.grnet.academicid.inspector.domain.Inspector;
+
+/**
+ * Helper class for handling Preferences.
+ */
+public class SharedPrefs {
+
+ /**
+ * Prevents this class from being instantiated.
+ */
+ private SharedPrefs() {
+ }
+
+ /**
+ * This method retrieves an Inspector object from Shared Preferences.
+ *
+ * @return An Inspector object.
+ */
+ public static Inspector getInspectorFromPrefs() {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(InspectorApplication.getAppContext());
+
+ Inspector inspector = new Inspector();
+ inspector.setFirstname(preferences.getString(Inspector.FIRSTNAME, ""));
+ inspector.setLastname(preferences.getString(Inspector.LASTNAME, ""));
+ inspector.setUsername(preferences.getString(Inspector.USERNAME, ""));
+ inspector.setPasswordHash(preferences.getString(Inspector.PASSWORD_HASH, ""));
+ inspector.setChangePswAtLogin(preferences.getBoolean(Inspector.CHANGE_PSW_AT_LOGIN, false));
+ inspector.setOrgId(preferences.getLong(Inspector.ORG_ID, 0));
+ inspector.setOrgName(preferences.getString(Inspector.ORG_NAME, ""));
+ inspector.setOrgDescription(preferences.getString(Inspector.ORG_DESCRIPTION, ""));
+
+ return inspector;
+ }
+
+ /**
+ * This method stores Inspector object in Shared Preferences.
+ *
+ * @param inspector The Inspector object that will be saved in Shared Preferences.
+ */
+ public static void setInspectorToPrefs(Inspector inspector) {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(InspectorApplication.getAppContext());
+ SharedPreferences.Editor preferencesEditor = preferences.edit();
+
+ preferencesEditor.putString(Inspector.FIRSTNAME, inspector.getFirstname());
+ preferencesEditor.putString(Inspector.LASTNAME, inspector.getLastname());
+ preferencesEditor.putString(Inspector.USERNAME, inspector.getUsername());
+ preferencesEditor.putString(Inspector.PASSWORD_HASH, inspector.getPasswordHash());
+ preferencesEditor.putBoolean(Inspector.CHANGE_PSW_AT_LOGIN, inspector.shouldChangePswAtLogin());
+ preferencesEditor.putLong(Inspector.ORG_ID, inspector.getOrgId());
+ preferencesEditor.putString(Inspector.ORG_NAME, inspector.getOrgName());
+ preferencesEditor.putString(Inspector.ORG_DESCRIPTION, inspector.getOrgDescription());
+
+ preferencesEditor.commit();
+ }
+
+ /**
+ * This method clear Inspector values from Shared Preferences.
+ */
+ public static void ClearInspectorFromPrefs() {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(InspectorApplication.getAppContext());
+ SharedPreferences.Editor preferencesEditor = preferences.edit();
+
+ preferencesEditor.remove(Inspector.FIRSTNAME);
+ preferencesEditor.remove(Inspector.LASTNAME);
+ preferencesEditor.remove(Inspector.USERNAME);
+ preferencesEditor.remove(Inspector.PASSWORD_HASH);
+ preferencesEditor.remove(Inspector.CHANGE_PSW_AT_LOGIN);
+ preferencesEditor.remove(Inspector.ORG_ID);
+ preferencesEditor.remove(Inspector.ORG_NAME);
+ preferencesEditor.remove(Inspector.ORG_DESCRIPTION);
+
+ preferencesEditor.commit();
+ }
+}
\ No newline at end of file
diff --git a/Main/src/gr/grnet/academicid/inspector/utilities/Tools.java b/Main/src/gr/grnet/academicid/inspector/utilities/Tools.java
new file mode 100644
index 0000000000000000000000000000000000000000..959b1f0cd1bcb8cc0fa39377cbe7c82cb73da65d
--- /dev/null
+++ b/Main/src/gr/grnet/academicid/inspector/utilities/Tools.java
@@ -0,0 +1,117 @@
+package gr.grnet.academicid.inspector.utilities;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.util.Base64;
+import android.util.Log;
+import gr.grnet.academicid.inspector.InspectorApplication;
+import gr.grnet.academicid.inspector.domain.AcademicId;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Helper class with utility functionality.
+ */
+public class Tools {
+
+ /**
+ * Prevents this class from being instantiated.
+ */
+ private Tools() {
+ }
+
+ /**
+ * This method verifies that the device has network connectivity.
+ *
+ * @return True if the device has network connectivity.
+ */
+ public static boolean isNetworkAvailable() {
+ ConnectivityManager connectivityManager = (ConnectivityManager) InspectorApplication.getAppContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
+ return (activeNetworkInfo != null && activeNetworkInfo.isConnected());
+ }
+
+ /**
+ * This method verifies that a camera is available.
+ *
+ * @return True if the camera of the device is available.
+ */
+ public static boolean isCameraAvailable() {
+ PackageManager packageManager = InspectorApplication.getAppContext().getPackageManager();
+ return (packageManager != null && packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA));
+ }
+
+ /**
+ * This method verifies that the passed parameter is numeric.
+ *
+ * @param numStr Contains the questioned value.
+ * @return True if numStr is numeric.
+ */
+ public static boolean isNumericValue(String numStr) {
+ try {
+ //noinspection ResultOfMethodCallIgnored
+ Long.parseLong(numStr);
+ return true;
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ }
+
+ /**
+ * This method calculates the hash value of the parameter passed based on the DIGEST_ALGORITHM that is used.
+ *
+ * @param message The plain message that will be hashed
+ * @return The hash equivalent of message according to digest algorithm that was used.
+ */
+ public static String getHash(String message) {
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance(Constants.DIGEST_ALGORITHM);
+ byte[] hashedArray = messageDigest.digest(message.getBytes());
+ BigInteger hashedNumber = new BigInteger(1, hashedArray);
+
+ return hashedNumber.toString(16);
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(Constants.LOGTAG, "Tools.getHash(): The designated algorithm: " + Constants.DIGEST_ALGORITHM + " can't be found.", e);
+ return null;
+ }
+ }
+
+ /**
+ * This method concatenates the two parameters passed and encodes them using Base64 scheme.
+ *
+ * @param str1 This first parameter that needs to be encoded.
+ * @param str2 This second parameter that needs to be encoded.
+ * @return a encoded with Base64 scheme String.
+ */
+ public static String encodeBase64(String str1, String str2) {
+ byte[] authEncBytes = Base64.encode((str1 + ":" + str2).getBytes(), Base64.NO_WRAP);
+ return new String(authEncBytes);
+ }
+
+ /**
+ * This method constructs a 2+2 letter concatenated string from firstname and lastname.
+ * Method checks if greek name exists and if not proceeds with latin name.
+ *
+ * @param academicId the object containing the first and lastname that should be concatenated to 2+2 letters.
+ * @return a 2+2 concatenated string containing the first two letters from firstname and lastname.
+ */
+ public static String concatenateName(AcademicId academicId) {
+ if (academicId == null) {
+ return null;
+
+ } else if ((academicId.getGreekFirstname() != null) && (academicId.getGreekFirstname().length() != 0) &&
+ (academicId.getGreekLastname() != null) && (academicId.getGreekLastname().length() != 0)) {
+ return (academicId.getGreekFirstname().substring(0, 2) + " " + academicId.getGreekLastname().substring(0, 2));
+
+ } else if ((academicId.getLatinFirstname() != null) && (academicId.getLatinFirstname().length() != 0) &&
+ (academicId.getLatinLastname() != null) && (academicId.getLatinLastname().length() != 0))
+ return (academicId.getLatinFirstname().substring(0, 2) + " " + academicId.getLatinLastname().substring(0, 2));
+
+ else
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/ZBarScannerLibrary/AndroidManifest.xml b/ZBarScannerLibrary/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5d5925b440dbec519e525d9f6dc5b4b784d8d4e0
--- /dev/null
+++ b/ZBarScannerLibrary/AndroidManifest.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ZBarScannerLibrary/build.xml b/ZBarScannerLibrary/build.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b72a8d3ba15328b264f9c9e839e56beefa0bc2f0
--- /dev/null
+++ b/ZBarScannerLibrary/build.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ZBarScannerLibrary/gen/com/dm/zbar/android/scanner/BuildConfig.java b/ZBarScannerLibrary/gen/com/dm/zbar/android/scanner/BuildConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..5cdff0b68a4c51e48ed1a40f69c80ba8e4fbdbe3
--- /dev/null
+++ b/ZBarScannerLibrary/gen/com/dm/zbar/android/scanner/BuildConfig.java
@@ -0,0 +1,8 @@
+/*___Generated_by_IDEA___*/
+
+package com.dm.zbar.android.scanner;
+
+/* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */
+public final class BuildConfig {
+ public final static boolean DEBUG = Boolean.parseBoolean(null);
+}
\ No newline at end of file
diff --git a/ZBarScannerLibrary/gen/com/dm/zbar/android/scanner/Manifest.java b/ZBarScannerLibrary/gen/com/dm/zbar/android/scanner/Manifest.java
new file mode 100644
index 0000000000000000000000000000000000000000..54ea9b1c6ed0fedbcb8e78a83d2739c2f54dc274
--- /dev/null
+++ b/ZBarScannerLibrary/gen/com/dm/zbar/android/scanner/Manifest.java
@@ -0,0 +1,7 @@
+/*___Generated_by_IDEA___*/
+
+package com.dm.zbar.android.scanner;
+
+/* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/ZBarScannerLibrary/gen/com/dm/zbar/android/scanner/R.java b/ZBarScannerLibrary/gen/com/dm/zbar/android/scanner/R.java
new file mode 100644
index 0000000000000000000000000000000000000000..8eaeca3fa6ac8d1902648e387ffe2901d55ae6c1
--- /dev/null
+++ b/ZBarScannerLibrary/gen/com/dm/zbar/android/scanner/R.java
@@ -0,0 +1,7 @@
+/*___Generated_by_IDEA___*/
+
+package com.dm.zbar.android.scanner;
+
+/* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/ZBarScannerLibrary/libs/armeabi-v7a/libiconv.so b/ZBarScannerLibrary/libs/armeabi-v7a/libiconv.so
new file mode 100644
index 0000000000000000000000000000000000000000..2bcbb70c3079cdd35455410d069a23f6c8d0832a
Binary files /dev/null and b/ZBarScannerLibrary/libs/armeabi-v7a/libiconv.so differ
diff --git a/ZBarScannerLibrary/libs/armeabi-v7a/libzbarjni.so b/ZBarScannerLibrary/libs/armeabi-v7a/libzbarjni.so
new file mode 100644
index 0000000000000000000000000000000000000000..2693dbb140b58528bc90aa03c2a98c898fdfed05
Binary files /dev/null and b/ZBarScannerLibrary/libs/armeabi-v7a/libzbarjni.so differ
diff --git a/ZBarScannerLibrary/libs/armeabi/libiconv.so b/ZBarScannerLibrary/libs/armeabi/libiconv.so
new file mode 100644
index 0000000000000000000000000000000000000000..9c7150de295ff7a7fded8f00fcdb275a8ec15bf7
Binary files /dev/null and b/ZBarScannerLibrary/libs/armeabi/libiconv.so differ
diff --git a/ZBarScannerLibrary/libs/armeabi/libzbarjni.so b/ZBarScannerLibrary/libs/armeabi/libzbarjni.so
new file mode 100644
index 0000000000000000000000000000000000000000..3d4a8ac3232507703b539320c6262ad0c8aacee7
Binary files /dev/null and b/ZBarScannerLibrary/libs/armeabi/libzbarjni.so differ
diff --git a/ZBarScannerLibrary/libs/x86/libiconv.so b/ZBarScannerLibrary/libs/x86/libiconv.so
new file mode 100644
index 0000000000000000000000000000000000000000..6ab43e51d4793851deb215cfe04dba85a733d759
Binary files /dev/null and b/ZBarScannerLibrary/libs/x86/libiconv.so differ
diff --git a/ZBarScannerLibrary/libs/x86/libzbarjni.so b/ZBarScannerLibrary/libs/x86/libzbarjni.so
new file mode 100644
index 0000000000000000000000000000000000000000..d64f517ad58295affd786686c83667b0b7d0dccb
Binary files /dev/null and b/ZBarScannerLibrary/libs/x86/libzbarjni.so differ
diff --git a/ZBarScannerLibrary/libs/zbar.jar b/ZBarScannerLibrary/libs/zbar.jar
new file mode 100644
index 0000000000000000000000000000000000000000..7d50b957809c65f37e5606988e9fc06410873029
Binary files /dev/null and b/ZBarScannerLibrary/libs/zbar.jar differ
diff --git a/ZBarScannerLibrary/project.properties b/ZBarScannerLibrary/project.properties
new file mode 100644
index 0000000000000000000000000000000000000000..dfa4dd0977fb676f5e209bda13e5a285ecff5c71
--- /dev/null
+++ b/ZBarScannerLibrary/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-16
+android.library=true
diff --git a/ZBarScannerLibrary/res/drawable-hdpi/ic_launcher.png b/ZBarScannerLibrary/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..96a442e5b8e9394ccf50bab9988cb2316026245d
Binary files /dev/null and b/ZBarScannerLibrary/res/drawable-hdpi/ic_launcher.png differ
diff --git a/ZBarScannerLibrary/res/drawable-ldpi/ic_launcher.png b/ZBarScannerLibrary/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..99238729d8753585237a65b91c7cde426c90baef
Binary files /dev/null and b/ZBarScannerLibrary/res/drawable-ldpi/ic_launcher.png differ
diff --git a/ZBarScannerLibrary/res/drawable-mdpi/ic_launcher.png b/ZBarScannerLibrary/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..359047dfa4ed206e41e2354f9c6b307e713efe32
Binary files /dev/null and b/ZBarScannerLibrary/res/drawable-mdpi/ic_launcher.png differ
diff --git a/ZBarScannerLibrary/res/drawable-xhdpi/ic_launcher.png b/ZBarScannerLibrary/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..71c6d760f05183ef8a47c614d8d13380c8528499
Binary files /dev/null and b/ZBarScannerLibrary/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/ZBarScannerLibrary/res/values/strings.xml b/ZBarScannerLibrary/res/values/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e73122b930392a0a22ba5640f0171e53ae913f98
--- /dev/null
+++ b/ZBarScannerLibrary/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ ZBar Scanner
+
diff --git a/ZBarScannerLibrary/src/com/dm/zbar/android/scanner/CameraPreview.java b/ZBarScannerLibrary/src/com/dm/zbar/android/scanner/CameraPreview.java
new file mode 100644
index 0000000000000000000000000000000000000000..fa08c5c9ba61ac5786ecde3ec128e9514d6066ba
--- /dev/null
+++ b/ZBarScannerLibrary/src/com/dm/zbar/android/scanner/CameraPreview.java
@@ -0,0 +1,188 @@
+package com.dm.zbar.android.scanner;
+
+import android.content.Context;
+import android.hardware.Camera;
+import android.hardware.Camera.AutoFocusCallback;
+import android.hardware.Camera.PreviewCallback;
+import android.hardware.Camera.Size;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import java.io.IOException;
+import java.util.List;
+
+class CameraPreview extends ViewGroup implements SurfaceHolder.Callback {
+ private final String TAG = "CameraPreview";
+
+ SurfaceView mSurfaceView;
+ SurfaceHolder mHolder;
+ Size mPreviewSize;
+ List mSupportedPreviewSizes;
+ Camera mCamera;
+ PreviewCallback mPreviewCallback;
+ AutoFocusCallback mAutoFocusCallback;
+
+ boolean portrait = false;
+
+ CameraPreview(Context context, PreviewCallback previewCallback, AutoFocusCallback autoFocusCb) {
+ super(context);
+
+ mPreviewCallback = previewCallback;
+ mAutoFocusCallback = autoFocusCb;
+ mSurfaceView = new SurfaceView(context);
+ addView(mSurfaceView);
+
+ // Install a SurfaceHolder.Callback so we get notified when the
+ // underlying surface is created and destroyed.
+ mHolder = mSurfaceView.getHolder();
+ mHolder.addCallback(this);
+ mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+ }
+
+ public void setCamera(Camera camera) {
+ mCamera = camera;
+ if (mCamera != null) {
+ mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
+ requestLayout();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // We purposely disregard child measurements because act as a
+ // wrapper to a SurfaceView that centers the camera preview instead
+ // of stretching it.
+ final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
+ final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
+ setMeasuredDimension(width, height);
+
+ // Check if the device is in portrait mode
+ portrait = (height > width);
+
+ if (mSupportedPreviewSizes != null) {
+ if (portrait)
+ mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, height, width);
+ else
+ mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ if (changed && getChildCount() > 0) {
+ final View child = getChildAt(0);
+
+ final int width = r - l;
+ final int height = b - t;
+
+ int previewWidth = width;
+ int previewHeight = height;
+ if (mPreviewSize != null) {
+ if (portrait) {
+ previewWidth = mPreviewSize.height;
+ previewHeight = mPreviewSize.width;
+ } else {
+ previewWidth = mPreviewSize.width;
+ previewHeight = mPreviewSize.height;
+ }
+ }
+
+ // Center the child SurfaceView within the parent.
+ if (width * previewHeight > height * previewWidth) {
+ final int scaledChildWidth = previewWidth * height / previewHeight;
+ child.layout((width - scaledChildWidth) / 2, 0,
+ (width + scaledChildWidth) / 2, height);
+ } else {
+ final int scaledChildHeight = previewHeight * width / previewWidth;
+ child.layout(0, (height - scaledChildHeight) / 2,
+ width, (height + scaledChildHeight) / 2);
+ }
+ }
+ }
+
+ public void hideSurfaceView() {
+ mSurfaceView.setVisibility(View.INVISIBLE);
+ }
+
+ public void showSurfaceView() {
+ mSurfaceView.setVisibility(View.VISIBLE);
+ }
+
+ public void surfaceCreated(SurfaceHolder holder) {
+ // The Surface has been created, acquire the camera and tell it where
+ // to draw.
+ try {
+ if (mCamera != null) {
+ mCamera.setPreviewDisplay(holder);
+ }
+ } catch (IOException exception) {
+ Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
+ }
+ }
+
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // Surface will be destroyed when we return, so stop the preview.
+ if (mCamera != null) {
+ mCamera.cancelAutoFocus();
+ mCamera.stopPreview();
+ }
+ }
+
+
+ private Size getOptimalPreviewSize(List sizes, int w, int h) {
+ final double ASPECT_TOLERANCE = 0.1;
+ double targetRatio = (double) w / h;
+ if (sizes == null) return null;
+
+ Size optimalSize = null;
+ double minDiff = Double.MAX_VALUE;
+
+ int targetHeight = h;
+
+ // Try to find an size match aspect ratio and size
+ for (Size size : sizes) {
+ double ratio = (double) size.width / size.height;
+ if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
+ if (Math.abs(size.height - targetHeight) < minDiff) {
+ optimalSize = size;
+ minDiff = Math.abs(size.height - targetHeight);
+ }
+ }
+
+ // Cannot find the one match the aspect ratio, ignore the requirement
+ if (optimalSize == null) {
+ minDiff = Double.MAX_VALUE;
+ for (Size size : sizes) {
+ if (Math.abs(size.height - targetHeight) < minDiff) {
+ optimalSize = size;
+ minDiff = Math.abs(size.height - targetHeight);
+ }
+ }
+ }
+ return optimalSize;
+ }
+
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ if (holder.getSurface() == null) {
+ // preview surface does not exist
+ return;
+ }
+
+ if (mCamera != null) {
+ // Now that the size is known, set up the camera parameters and begin
+ // the preview.
+ Camera.Parameters parameters = mCamera.getParameters();
+ parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
+ requestLayout();
+
+ if (portrait) mCamera.setDisplayOrientation(90);
+ mCamera.setParameters(parameters);
+ mCamera.setPreviewCallback(mPreviewCallback);
+ mCamera.startPreview();
+ mCamera.autoFocus(mAutoFocusCallback);
+ }
+ }
+
+}
diff --git a/ZBarScannerLibrary/src/com/dm/zbar/android/scanner/ZBarConstants.java b/ZBarScannerLibrary/src/com/dm/zbar/android/scanner/ZBarConstants.java
new file mode 100644
index 0000000000000000000000000000000000000000..f96037f1b5053b90d20a6280ed3a3eb976559392
--- /dev/null
+++ b/ZBarScannerLibrary/src/com/dm/zbar/android/scanner/ZBarConstants.java
@@ -0,0 +1,8 @@
+package com.dm.zbar.android.scanner;
+
+public interface ZBarConstants {
+ public static final String SCAN_MODES = "SCAN_MODES";
+ public static final String SCAN_RESULT = "SCAN_RESULT";
+ public static final String SCAN_RESULT_TYPE = "SCAN_RESULT_TYPE";
+ public static final String ERROR_INFO = "ERROR_INFO";
+}
diff --git a/ZBarScannerLibrary/src/com/dm/zbar/android/scanner/ZBarScannerActivity.java b/ZBarScannerLibrary/src/com/dm/zbar/android/scanner/ZBarScannerActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..7d21992203dd062028a381b8566c4651c457d1fc
--- /dev/null
+++ b/ZBarScannerLibrary/src/com/dm/zbar/android/scanner/ZBarScannerActivity.java
@@ -0,0 +1,177 @@
+package com.dm.zbar.android.scanner;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.hardware.Camera;
+import android.os.Bundle;
+import android.os.Handler;
+import android.text.TextUtils;
+import gr.grnet.academicid.inspector.InspectionResults;
+import net.sourceforge.zbar.Config;
+import net.sourceforge.zbar.Image;
+import net.sourceforge.zbar.ImageScanner;
+import net.sourceforge.zbar.Symbol;
+import net.sourceforge.zbar.SymbolSet;
+
+public class ZBarScannerActivity extends Activity implements Camera.PreviewCallback, ZBarConstants {
+
+ private CameraPreview mPreview;
+ private Camera mCamera;
+ private ImageScanner mScanner;
+ private Handler mAutoFocusHandler;
+ private boolean mPreviewing = true;
+
+ static {
+ System.loadLibrary("iconv");
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (!isCameraAvailable()) {
+ // Cancel request if there is no rear-facing camera.
+ cancelRequest();
+ return;
+ }
+
+ // Hide the window title.
+// requestWindowFeature(Window.FEATURE_NO_TITLE);
+// getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ mAutoFocusHandler = new Handler();
+
+ // Create and configure the ImageScanner;
+ setupScanner();
+
+ // Create a RelativeLayout container that will hold a SurfaceView,
+ // and set it as the content of our activity.
+ mPreview = new CameraPreview(this, this, autoFocusCB);
+ setContentView(mPreview);
+ }
+
+ public void setupScanner() {
+ mScanner = new ImageScanner();
+ mScanner.setConfig(0, Config.X_DENSITY, 3);
+ mScanner.setConfig(0, Config.Y_DENSITY, 3);
+
+ int[] symbols = getIntent().getIntArrayExtra(SCAN_MODES);
+ if (symbols != null) {
+ mScanner.setConfig(Symbol.NONE, Config.ENABLE, 0);
+ for (int symbol : symbols) {
+ mScanner.setConfig(symbol, Config.ENABLE, 1);
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // Open the default i.e. the first rear facing camera.
+ mCamera = Camera.open();
+ if (mCamera == null) {
+ // Cancel request if mCamera is null.
+ cancelRequest();
+ return;
+ }
+
+ mPreview.setCamera(mCamera);
+ mPreview.showSurfaceView();
+
+ mPreviewing = true;
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ // Because the Camera object is a shared resource, it's very
+ // important to release it when the activity is paused.
+ if (mCamera != null) {
+ mPreview.setCamera(null);
+ mCamera.cancelAutoFocus();
+ mCamera.setPreviewCallback(null);
+ mCamera.stopPreview();
+ mCamera.release();
+
+ // According to Jason Kuang on http://stackoverflow.com/questions/6519120/how-to-recover-camera-preview-from-sleep,
+ // there might be surface recreation problems when the device goes to sleep. So lets just hide it and
+ // recreate on resume
+ mPreview.hideSurfaceView();
+
+ mPreviewing = false;
+ mCamera = null;
+ }
+ }
+
+ public boolean isCameraAvailable() {
+ PackageManager pm = getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
+ }
+
+ public void cancelRequest() {
+ Intent dataIntent = new Intent();
+ dataIntent.putExtra(ERROR_INFO, "Camera unavailable");
+ setResult(Activity.RESULT_CANCELED, dataIntent);
+ finish();
+ }
+
+ public void onPreviewFrame(byte[] data, Camera camera) {
+ Camera.Parameters parameters = camera.getParameters();
+ Camera.Size size = parameters.getPreviewSize();
+
+ Image barcode = new Image(size.width, size.height, "Y800");
+ barcode.setData(data);
+
+ int result = mScanner.scanImage(barcode);
+
+ if (result != 0) {
+ mCamera.cancelAutoFocus();
+ mCamera.setPreviewCallback(null);
+ mCamera.stopPreview();
+ mPreviewing = false;
+ SymbolSet syms = mScanner.getResults();
+ for (Symbol sym : syms) {
+ String symData = sym.getData();
+ if (!TextUtils.isEmpty(symData)) {
+ Intent dataIntent = new Intent();
+ dataIntent.putExtra(SCAN_RESULT, symData);
+ dataIntent.putExtra(SCAN_RESULT_TYPE, sym.getType());
+ setResult(Activity.RESULT_OK, dataIntent);
+ finish();
+ break;
+ }
+ }
+ }
+ }
+
+ private Runnable doAutoFocus = new Runnable() {
+ public void run() {
+ if (mCamera != null && mPreviewing) {
+ mCamera.autoFocus(autoFocusCB);
+ }
+ }
+ };
+
+ // Mimic continuous auto-focusing
+ Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
+ public void onAutoFocus(boolean success, Camera camera) {
+ mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
+ }
+ };
+
+ @Override
+ public void onBackPressed() {
+ super.onPause();
+ // Starting InspectionResults activity
+ Intent intent = new Intent(this, InspectionResults.class);
+
+ // Pop the existing activity instead of creating a new one
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ startActivity(intent);
+ }
+}
+