diff --git a/Main/AndroidManifest.xml b/Main/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..0f91d70937c7c7d5b3804a5560f55d5b956f661f --- /dev/null +++ b/Main/AndroidManifest.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="gr.grnet.academicid.merchant" + android:versionCode="1" + android:versionName="1.0"> + <uses-sdk android:minSdkVersion="8" + android:targetSdkVersion="16"/> + + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> + <uses-permission android:name="android.permission.CAMERA"/> + <uses-permission android:name="android.permission.INTERNET"/> + + <uses-feature android:name="android.hardware.camera" android:required="false"/> + <uses-feature android:name="android.hardware.camera.autofocus"/> + + + <application android:label="@string/app_name" + android:name=".MerchantApplication" + android:icon="@drawable/ic_launcher" + android:allowBackup="true"> + <activity android:name=".MainActivity" + android:label="@string/app_name" + android:screenOrientation="portrait" + android:configChanges="keyboardHidden|orientation|screenSize" + android:windowSoftInputMode="stateHidden"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + <activity android:name=".OfferList" + android:screenOrientation="portrait" + android:configChanges="keyboardHidden|orientation|screenSize"/> + <activity android:name=".SingleOffer" + android:screenOrientation="portrait" + android:configChanges="keyboardHidden|orientation|screenSize" + android:windowSoftInputMode="stateHidden"/> + <activity android:name=".ScanQR" + android:screenOrientation="portrait" + android:configChanges="keyboardHidden|orientation|screenSize"/> + <activity android:name="com.dm.zbar.android.scanner.ZBarScannerActivity" + android:screenOrientation="portrait" + android:configChanges="keyboardHidden|orientation|screenSize"/> + <activity android:name=".Preferences" + android:screenOrientation="portrait" + android:configChanges="keyboardHidden|orientation|screenSize"/> + <activity android:name=".ChangePassword" + android:screenOrientation="portrait" + android:configChanges="keyboardHidden|orientation|screenSize" + android:windowSoftInputMode="stateHidden"/> + </application> +</manifest> diff --git a/Main/gen/gr/grnet/academicid/merchant/BuildConfig.java b/Main/gen/gr/grnet/academicid/merchant/BuildConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..8f261a820e98b2166f62a308db3023afc4cb8890 --- /dev/null +++ b/Main/gen/gr/grnet/academicid/merchant/BuildConfig.java @@ -0,0 +1,8 @@ +/*___Generated_by_IDEA___*/ + +package gr.grnet.academicid.merchant; + +/* 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/merchant/Manifest.java b/Main/gen/gr/grnet/academicid/merchant/Manifest.java new file mode 100644 index 0000000000000000000000000000000000000000..de6dd25d0cf1e21e99bc66ab129ab9416429626a --- /dev/null +++ b/Main/gen/gr/grnet/academicid/merchant/Manifest.java @@ -0,0 +1,7 @@ +/*___Generated_by_IDEA___*/ + +package gr.grnet.academicid.merchant; + +/* 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/merchant/R.java b/Main/gen/gr/grnet/academicid/merchant/R.java new file mode 100644 index 0000000000000000000000000000000000000000..27db171e2a5bc9f6511a1176561813661264129d --- /dev/null +++ b/Main/gen/gr/grnet/academicid/merchant/R.java @@ -0,0 +1,7 @@ +/*___Generated_by_IDEA___*/ + +package gr.grnet.academicid.merchant; + +/* 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..3496c98ec34de5a17935857cb51a65741f6d7598 Binary files /dev/null and b/Main/res/drawable-hdpi/ic_launcher.png differ diff --git a/Main/res/drawable-hdpi/rating_important.png b/Main/res/drawable-hdpi/rating_important.png new file mode 100644 index 0000000000000000000000000000000000000000..11f8641448683e8d59f7ba0064d211ee9dac0116 Binary files /dev/null and b/Main/res/drawable-hdpi/rating_important.png differ diff --git a/Main/res/drawable-hdpi/rating_not_important.png b/Main/res/drawable-hdpi/rating_not_important.png new file mode 100644 index 0000000000000000000000000000000000000000..7259b06bccca43ceb26fbad5e71dac4bb468704c Binary files /dev/null and b/Main/res/drawable-hdpi/rating_not_important.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..aed039e191b485f83122c3ccc9f10af342732003 Binary files /dev/null and b/Main/res/drawable-ldpi/ic_launcher.png differ diff --git a/Main/res/drawable-ldpi/rating_important.png b/Main/res/drawable-ldpi/rating_important.png new file mode 100644 index 0000000000000000000000000000000000000000..504785fb8924159dbf0778e08bb6bb176ebf90b8 Binary files /dev/null and b/Main/res/drawable-ldpi/rating_important.png differ diff --git a/Main/res/drawable-ldpi/rating_not_important.png b/Main/res/drawable-ldpi/rating_not_important.png new file mode 100644 index 0000000000000000000000000000000000000000..3925deb9e95d0f20187f4b0c97cdefe63cc3d791 Binary files /dev/null and b/Main/res/drawable-ldpi/rating_not_important.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..aed039e191b485f83122c3ccc9f10af342732003 Binary files /dev/null and b/Main/res/drawable-mdpi/ic_launcher.png differ diff --git a/Main/res/drawable-mdpi/rating_important.png b/Main/res/drawable-mdpi/rating_important.png new file mode 100644 index 0000000000000000000000000000000000000000..504785fb8924159dbf0778e08bb6bb176ebf90b8 Binary files /dev/null and b/Main/res/drawable-mdpi/rating_important.png differ diff --git a/Main/res/drawable-mdpi/rating_not_important.png b/Main/res/drawable-mdpi/rating_not_important.png new file mode 100644 index 0000000000000000000000000000000000000000..3925deb9e95d0f20187f4b0c97cdefe63cc3d791 Binary files /dev/null and b/Main/res/drawable-mdpi/rating_not_important.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..809f8d52ed65794b65d27b5cde577880e0d52709 Binary files /dev/null and b/Main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/Main/res/drawable-xhdpi/rating_important.png b/Main/res/drawable-xhdpi/rating_important.png new file mode 100644 index 0000000000000000000000000000000000000000..7576cc1e2d53878b62ddad259ed31b3da7215687 Binary files /dev/null and b/Main/res/drawable-xhdpi/rating_important.png differ diff --git a/Main/res/drawable-xhdpi/rating_not_important.png b/Main/res/drawable-xhdpi/rating_not_important.png new file mode 100644 index 0000000000000000000000000000000000000000..3c618a129e6096a1ec95b26df4ee0e8a0a2267a3 Binary files /dev/null and b/Main/res/drawable-xhdpi/rating_not_important.png differ diff --git a/Main/res/layout/change_password.xml b/Main/res/layout/change_password.xml new file mode 100644 index 0000000000000000000000000000000000000000..02770e6a74cd6102f63718586696c8eb9ef87465 --- /dev/null +++ b/Main/res/layout/change_password.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <LinearLayout + android:orientation="vertical" + android:layout_height="wrap_content" + android:layout_width="match_parent"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="30dp" + android:layout_marginBottom="3dp" + android:layout_marginLeft="50dp" + android:text="@string/lbl_current_password"/> + <EditText + android:id="@+id/etxt_current_password" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="40dp" + android:layout_marginRight="40dp" + android:contentDescription="@null" + android:inputType="textPassword"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="40dp" + android:layout_marginBottom="3dp" + android:layout_marginLeft="50dp" + android:text="@string/lbl_new_password"/> + <EditText + android:id="@+id/etxt_new_password" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="40dp" + android:layout_marginRight="40dp" + android:contentDescription="@null" + android:inputType="textPassword"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:layout_marginBottom="3dp" + android:layout_marginLeft="50dp" + android:text="@string/lbl_new_password_confirmation"/> + <EditText + android:id="@+id/etxt_new_password_confirmation" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="40dp" + android:layout_marginRight="40dp" + android:contentDescription="@null" + android:inputType="textPassword"/> + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="30dp" + android:layout_marginLeft="15dp" + android:layout_marginRight="15dp"> + <Button + android:id="@+id/btn_cancel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginRight="3dp" + android:text="@string/lbl_cancel" + android:onClick="cancel"/> + <Button + android:id="@+id/btn_confirm" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginLeft="3dp" + android:text="@string/lbl_confirm" + android:onClick="confirm"/> + </LinearLayout> + </LinearLayout> +</ScrollView> + diff --git a/Main/res/layout/main.xml b/Main/res/layout/main.xml new file mode 100644 index 0000000000000000000000000000000000000000..c703e86889a56b1709c60c0cc57c175959bc329f --- /dev/null +++ b/Main/res/layout/main.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> + +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <LinearLayout + android:orientation="vertical" + android:layout_height="wrap_content" + android:layout_width="match_parent"> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginTop="20dp" + android:contentDescription="@null" + android:src="@drawable/ic_launcher"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="30dp" + android:layout_marginBottom="3dp" + android:layout_marginLeft="50dp" + android:text="@string/lbl_username"/> + <EditText + android:id="@+id/etxt_username" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="40dp" + android:layout_marginRight="40dp" + android:inputType="text|textNoSuggestions"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:layout_marginBottom="3dp" + android:layout_marginLeft="50dp" + android:text="@string/lbl_password"/> + <EditText + android:id="@+id/etxt_password" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="40dp" + android:layout_marginRight="40dp" + android:inputType="textPassword"/> + <Button + android:id="@+id/btn_sign_in" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginTop="10dp" + android:text="@string/lbl_sign_in" + android:onClick="signIn"/> + </LinearLayout> +</ScrollView> + diff --git a/Main/res/layout/offer_list.xml b/Main/res/layout/offer_list.xml new file mode 100644 index 0000000000000000000000000000000000000000..43f5a5377c441ac66a63637e73a429f31eecc6fd --- /dev/null +++ b/Main/res/layout/offer_list.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <TextView + android:id="@+id/txt_offers_header" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:paddingTop="5dp" + android:paddingBottom="5dp" + android:textAppearance="@style/header_green" + android:text="@string/lbl_active_offers" + /> + <ListView + android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + <TextView + android:id="@+id/empty" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_horizontal" + android:paddingTop="10dp" + android:textAppearance="@style/title_smart" + android:text="@string/lbl_no_offers"/> +</LinearLayout> \ No newline at end of file diff --git a/Main/res/layout/offer_list_item.xml b/Main/res/layout/offer_list_item.xml new file mode 100644 index 0000000000000000000000000000000000000000..0431c2fe3f9afeffec61f75c420d8403a788fa35 --- /dev/null +++ b/Main/res/layout/offer_list_item.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="10dp" + android:paddingLeft="10dp" + android:paddingRight="10dp"> + + <!-- Title Label --> + <TextView + android:id="@+id/txtv_offer_item_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingBottom="2dp" + android:paddingTop="6dp" + android:textAppearance="@style/list_title"/> + + <!-- Description label --> + <TextView + android:id="@+id/txtv_offer_item_description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingBottom="2dp" + android:textAppearance="@style/list_item"/> +</LinearLayout> \ No newline at end of file diff --git a/Main/res/layout/single_offer.xml b/Main/res/layout/single_offer.xml new file mode 100644 index 0000000000000000000000000000000000000000..c6263df67450a79b8cd0750918bd7c87b3f198ee --- /dev/null +++ b/Main/res/layout/single_offer.xml @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="10dp"> + <ScrollView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <!-- Title Label --> + <TextView android:id="@+id/txtv_offer_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingBottom="10dp" + android:textSize="25sp" + android:textStyle="bold" + android:textColor="#43bd00"/> + <!-- Description Label --> + <TextView android:id="@+id/txtv_offer_description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textColor="#acacac"/> + + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:paddingTop="20dp" + android:paddingBottom="20dp"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/list_title" + android:paddingRight="5dp" + android:text="@string/lbl_available_from"/> + <TextView + android:id="@+id/txtv_offer_startDate" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="5dp" + android:paddingLeft="5dp" + android:textAppearance="@style/list_title" + android:text="@string/lbl_available_to"/> + <TextView + android:id="@+id/txtv_offer_endDate" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + </LinearLayout> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/list_title" + android:text="@string/lbl_eligible_to"/> + <TextView + android:id="@+id/txtv_offer_criteria" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <TextView + android:id="@+id/txtv_offer_inspectionResult" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_marginTop="50dp" + android:visibility="gone"/> + <TextView + android:id="@+id/txtv_offer_inspectionResultError" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone"/> + </LinearLayout> + </ScrollView> + <Button + android:id="@+id/btn_scan_qr" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:text="@string/lbl_scan_qr" + android:onClick="scanQR"/> + <RelativeLayout + android:id="@+id/rellay_serial_input" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="5dp"> + <ImageButton + android:id="@+id/btn_check_serial" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:contentDescription="@null" + android:src="@drawable/action_search" + android:onClick="checkSerialNo"/> + <EditText + android:id="@+id/etxt_serial_number" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_toLeftOf="@id/btn_check_serial" + android:layout_toRightOf="@+id/btn_clear_serial" + android:layout_alignBottom="@id/btn_check_serial" + android:layout_marginTop="4dp" + android:imeOptions="actionDone" + android:inputType="number" + android:maxLength="12" + android:hint="@string/lbl_enter_serial"/> + <ImageButton + android:id="@+id/btn_clear_serial" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:contentDescription="@null" + android:src="@drawable/content_remove" + android:onClick="clearContent"/> + </RelativeLayout> +</LinearLayout> diff --git a/Main/res/menu/menu_offer_list.xml b/Main/res/menu/menu_offer_list.xml new file mode 100644 index 0000000000000000000000000000000000000000..4097ec36707fa670326d4346a0cc5073baea5998 --- /dev/null +++ b/Main/res/menu/menu_offer_list.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/menu_toggle_offers" + android:icon="@drawable/rating_not_important" + android:title="@string/lbl_inactive_offers"/> + <item + android:id="@+id/menu_settings" + android:icon="@drawable/action_settings" + android:title="@string/menu_settings"/> +</menu> \ No newline at end of file diff --git a/Main/res/menu/menu_scan_qr.xml b/Main/res/menu/menu_scan_qr.xml new file mode 100644 index 0000000000000000000000000000000000000000..8ead7fa6d820cae9c69ebdd3168c97fa7f552c3b --- /dev/null +++ b/Main/res/menu/menu_scan_qr.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/menu_settings" + android:icon="@drawable/action_settings" + android:title="@string/menu_settings"/> +</menu> \ No newline at end of file diff --git a/Main/res/menu/menu_single_offer.xml b/Main/res/menu/menu_single_offer.xml new file mode 100644 index 0000000000000000000000000000000000000000..74a001f30b602c960c9cc043f8d929b375b2b7f0 --- /dev/null +++ b/Main/res/menu/menu_single_offer.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/menu_offers" + android:icon="@drawable/rating_important" + android:title="@string/menu_offers"/> + <item + android:id="@+id/menu_settings" + android:icon="@drawable/action_settings" + android:title="@string/menu_settings"/> +</menu> \ 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..9abb1a56f0f85a4351203852df18bc24b59492cb --- /dev/null +++ b/Main/res/values-el/strings.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Academic ID Merchant</string> + + <string name="lbl_username">ΞΞ½ΞΏΞΌΞ± Ξ§ΟΞ�ΟΟΞ·:</string> + <string name="lbl_password">ΞΟδικΟΟ Ξ ΟΟΟΞ²Ξ±ΟΞ·Ο:</string> + <string name="lbl_sign_in">ΞΞ―ΟοδοΟ</string> + + <string name="title_no_net_available">ΞΞ―ΞΊΟΟ ΞΏ ΞΌΞ· ΞΞΉΞ±ΞΈΞΟΞΉΞΌΞΏ</string> + <string name="msg_no_net_available">ΞΞΉΞ± ΡνΡΟΞ³ΞΏΟΞΏΞ―Ξ·ΟΞ· ΟΞΉΞΟΟΞ΅ ΞΟΟμΡνο</string> + <string name="action_next">ΞΟΟμΡνο</string> + <string name="action_close">΀ΡΟΞΌΞ±ΟΞΉΟΞΌΟΟ</string> + + <string name="msg_wait">Ξ Ξ±ΟΞ±ΞΊΞ±Ξ»Ο ΟΞ΅ΟΞΉΞΌΞΞ½Ξ΅ΟΞ΅β¦</string> + <string name="msg_incorrect_credentials">΀ο ΟΞ½ΞΏΞΌΞ± Ξ§ΟΞ�ΟΟΞ· Ξ� ΞΏ ΞΟδικΟΟ Ξ ΟΟΟΞ²Ξ±ΟΞ·Ο Ξ΅Ξ―Ξ½Ξ±ΞΉ Ξ΅ΟΟαλμΞΞ½Ξ±</string> + <string name="msg_no_camera_available">ΠκάμΡΟΞ± δΡν Ρίναι διαθΞΟΞΉΞΌΞ·</string> + + <string name="lbl_active_offers">ΞΞ½Ξ΅ΟΞ³ΞΟ Ξ ΟΞΏΟΟΞΏΟΞΟ</string> + <string name="lbl_inactive_offers">ΞνΡνΡΟΞ³ΞΟ Ξ ΟΞΏΟΟΞΏΟΞΟ</string> + <string name="lbl_no_offers">ΞΡν Ο ΟΞ¬ΟΟΞΏΟ Ξ½ διαθΡΟΞΉΞΌΞ΅Ο ΟΟΞΏΟΟΞΏΟΞΟ</string> + <string name="msg_error_retrieving_offers">Ξ Ξ±ΟΞΏΟ ΟΞΉΞ¬ΟΟΞ·ΞΊΞ΅ ΟΟάλμα ΞΊΞ±ΟΞ¬ ΟΞ·Ξ½ ανάκΟΞ·ΟΞ· ΟΟΞ½ διαθΞΟΞΉΞΌΟΞ½ ΟΟΞΏΟΟΞΏΟΟΞ½</string> + + <string name="lbl_available_from">ΞΞΉΞ±ΞΈΞΟΞΉΞΌΞ· Ξ±ΟΟ:</string> + <string name="lbl_available_to">ΞΟΟ:</string> + <string name="lbl_eligible_to">ΞΞΉΞΊΞ±ΞΉΞΏΟΟΞΏΞΉ:</string> + <string name="msg_eligible_for_offer">ΞΟΞΉΞΈΞΌΟΟ Ξ±ΞΊΞ±Ξ΄Ξ·ΞΌΞ±ΞΉΞΊΞ�Ο ΟΞ±Ο ΟΟΟΞ·ΟΞ±Ο: SERIAL_TAG\nΞ ΞΊΞ¬ΟΞΏΟΞΏΟ Ξ΄ΞΉΞΊΞ±ΞΉΞΏΟΟΞ±ΞΉ ΟΞ·Ξ½ ΟΟΞΏΟΟΞΏΟΞ¬</string> + <string name="msg_not_eligible_for_offer">ΞΟΞΉΞΈΞΌΟΟ Ξ±ΞΊΞ±Ξ΄Ξ·ΞΌΞ±ΞΉΞΊΞ�Ο ΟΞ±Ο ΟΟΟΞ·ΟΞ±Ο: SERIAL_TAG\nΞ ΞΊΞ¬ΟΞΏΟΞΏΟ Ξ΄Ξ΅Ξ½ δικαιοΟΟΞ±ΞΉΟΞ·Ξ½ ΟΟΞΏΟΟΞΏΟΞ¬</string> + <string name="msg_invalid_input">΀ο ΟΟΞΏΞΉΟΡίο ΡιΟΟΞ΄ΞΏΟ Ξ΄Ξ΅Ξ½ Ξ±Ξ½ΟΞΉΟΟΞΏΞΉΟΡί ΟΞ΅ ΞΞ³ΞΊΟ ΟΞ· ακαδημαΟΞΊΞ� ΟΞ±Ο ΟΟΟΞ·ΟΞ±</string> + <string name="msg_error_validating_offer">Ξ Ξ±ΟΞΏΟ ΟΞΉΞ¬ΟΟΞ·ΞΊΞ΅ ΟΟάλμα ΞΊΞ±ΟΞ¬ ΟΞΏΞ½ ΞλΡγΟΞΏ ΟΞ·Ο Ξ΄ΞΉΞ±ΞΈΞ΅ΟΞΉΞΌΟΟΞ·ΟΞ±Ο ΟΞ·Ο ΟΟΞΏΟΟΞΏΟΞ¬Ο</string> + + <string name="lbl_scan_qr">ΣάΟΟΟΞ· QR</string> + <string name="lbl_enter_serial">ΞΞΉΟΞ±Ξ³ΟΞ³Ξ� ΣΡιΟΞΉΞ±ΞΊΞΏΟ ΞΟ.</string> + <string name="lbl_exit">Ξ ΞΉΞΟΟΞ΅ ΞΎΞ±Ξ½Ξ¬ Ξ³ΞΉΞ± Ξξοδο</string> + + <string name="menu_offers">ΞΞ½Ξ΅ΟΞ³ΞΟ Ξ ΟΞΏΟΟΞΏΟΞΟ</string> + <string name="menu_settings">Ξ‘Ο ΞΈΞΌΞ―ΟΡιΟ</string> + + <string name="title_change_password">ΞλλαγΞ� ΞΟΞ΄ΞΉΞΊΞΏΟ Ξ ΟΟΟΞ²Ξ±ΟΞ·Ο</string> + <string name="msg_change_password">ΞλλαγΞ� ΟΞΏΟ ΞΊΟΞ΄ΞΉΞΊΞΏΟ ΟΟΟΟΞ²Ξ±ΟΞ·Ο ΟΞΏΟ ΟΟΞ�ΟΟΞ·</string> + + <string name="lbl_current_password">Ξ€ΟΞΟΟΞ½ ΞΟδικΟΟ Ξ ΟΟΟΞ²Ξ±ΟΞ·Ο:</string> + <string name="lbl_new_password">ΞΞΞΏΟ ΞΟδικΟΟ Ξ ΟΟΟΞ²Ξ±ΟΞ·Ο:</string> + <string name="lbl_new_password_confirmation">ΞΟιβΡβαίΟΟΞ· ΞΞΞΏΟ ΞΟδικοΟ:</string> + <string name="lbl_cancel">ΞΞΊΟΟΟΟΞ·</string> + <string name="lbl_confirm">ΞΟιβΡβαίΟΟΞ·</string> + + <string name="msg_password_change_required">ΞΟΞ±ΞΉΟΡίΟΞ±ΞΉ αλλαγΞ� ΟΞΏΟ ΞΊΟΞ΄ΞΉΞΊΞΏΟ ΟΟΟΟΞ²Ξ±ΟΞ·Ο</string> + <string name="msg_incorrect_current_password">Ξ ΟΟΞΟΟΞ½ ΞΊΟδικΟΟ ΟΟΟΟΞ²Ξ±ΟΞ·Ο Ξ΅Ξ―Ξ½Ξ±ΞΉ λάθοΟ</string> + <string name="msg_new_passwords_dont_match">ΞΞΉ Ξ½ΞΞΏΞΉ ΞΊΟδικοί ΟΟΟΟΞ²Ξ±ΟΞ·Ο Ξ΄Ξ΅Ξ½ Ρίναι ίδιοι</string> + <string name="msg_password_too_short">Ξ ΞΊΟδικΟΟ ΟΟΟΟΞ²Ξ±ΟΞ·Ο ΟΟΞΟΡι Ξ½Ξ± ΟΞ΅ΟΞΉΞΟΡι ΟΞΏΟ Ξ»Ξ¬ΟΞΉΟΟΞΏΞ½ 4 ΟΞ±ΟΞ±ΞΊΟΞ�ΟΞ΅Ο</string> + <string name="msg_new_current_password_same">Ξ Ξ½ΞΞΏΟ ΞΊΞ±ΞΉ ΞΏ ΟΟΞΟΞΏΞ½ ΞΊΟδικΟΟ ΟΟΟΟΞ²Ξ±ΟΞ·Ο Ξ΄Ξ΅Ξ½ ΟΟΞΟΡι Ξ½Ξ± Ρίναι ίδιοι</string> + <string name="msg_error_changing_password">Ξ Ξ±ΟΞΏΟ ΟΞΉΞ¬ΟΟΞ·ΞΊΞ΅ ΟΟάλμα ΞΊΞ±ΟΞ± ΟΞ·Ξ½ διαδικαΟΞ―Ξ± Ξ±ΟΞΏΞΈΞ�ΞΊΞ΅Ο ΟΞ·Ο ΟΞΏΟ Ξ½ΞΞΏΟ ΞΊΟΞ΄ΞΉΞΊΞΏΟ ΟΟΟΟΞ²Ξ±ΟΞ·Ο</string> +</resources> diff --git a/Main/res/values/strings.xml b/Main/res/values/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..25b8f6f5ecc6023d30ccf58750f5377080726cbf --- /dev/null +++ b/Main/res/values/strings.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Academic ID Merchant</string> + + <string name="lbl_username">Username:</string> + <string name="lbl_password">Password:</string> + <string name="lbl_sign_in">Sign In</string> + + <string name="title_no_net_available">No Network Connection</string> + <string name="msg_no_net_available">To enable network connection touch Next</string> + <string name="action_next">Next</string> + <string name="action_close">Close</string> + + <string name="msg_wait">Please waitβ¦</string> + <string name="msg_incorrect_credentials">Incorrect username or password</string> + <string name="msg_no_camera_available">Rear Facing Camera Unavailable</string> + + <string name="lbl_active_offers">Active Offers</string> + <string name="lbl_inactive_offers">Inactive Offers</string> + <string name="lbl_no_offers">No Available Offers</string> + <string name="msg_error_retrieving_offers">Error trying to retrieve available offers</string> + + <string name="lbl_available_from">Available from:</string> + <string name="lbl_available_to">To:</string> + <string name="lbl_eligible_to">Eligible to:</string> + <string name="msg_eligible_for_offer">Academic Id: SERIAL_TAG\nEligible for this offer</string> + <string name="msg_not_eligible_for_offer">Academic Id: SERIAL_TAG\nNot eligible for this offer</string> + <string name="msg_invalid_input">The input is not a valid academic ID</string> + <string name="msg_error_validating_offer">Error trying to validate offer</string> + + <string name="lbl_scan_qr">Scan QR Code</string> + <string name="lbl_enter_serial">Enter Serial Number</string> + <string name="lbl_exit">Press again to exit</string> + + <string name="menu_offers">Active Offers</string> + <string name="menu_settings">Settings</string> + + <string name="title_change_password">Change Password</string> + <string name="msg_change_password">Change the user password</string> + + <string name="lbl_current_password">Current Password:</string> + <string name="lbl_new_password">New Password:</string> + <string name="lbl_new_password_confirmation">Confirm New Password:</string> + <string name="lbl_cancel">Cancel</string> + <string name="lbl_confirm">Confirm</string> + + <string name="msg_password_change_required">Required password change</string> + <string name="msg_incorrect_current_password">Current password is incorrect</string> + <string name="msg_new_passwords_dont_match">The new passwords don\'t match</string> + <string name="msg_password_too_short">Password must contain at least 4 characters</string> + <string name="msg_new_current_password_same">New and current password should not be the same</string> + <string name="msg_error_changing_password">Error trying to update user password</string> +</resources> diff --git a/Main/res/values/styles.xml b/Main/res/values/styles.xml new file mode 100644 index 0000000000000000000000000000000000000000..ac33aa66902e1016d5095c684277a82b65805073 --- /dev/null +++ b/Main/res/values/styles.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <style name="header_green"> + <item name="android:textSize">30sp</item> + <item name="android:textColor">#00ff00</item> + <item name="android:textStyle">bold</item> + <item name="android:singleLine">true</item> + </style> + <style name="header_orange"> + <item name="android:textSize">30sp</item> + <item name="android:textColor">#ee7620</item> + <item name="android:textStyle">bold</item> + <item name="android:singleLine">true</item> + </style> + + <style name="list_title"> + <item name="android:textSize">16sp</item> + <item name="android:textColor">#ffffffff</item> + <item name="android:textStyle">bold</item> + </style> + <style name="list_item"> + <item name="android:textColor">#acacac</item> + </style> + + <style name="title_green"> + <item name="android:textSize">25sp</item> + <item name="android:textColor">#00ff00</item> + <item name="android:textStyle">bold</item> + <item name="android:singleLine">true</item> + </style> + <style name="title_orange"> + <item name="android:textSize">25sp</item> + <item name="android:textColor">#ee7620</item> + <item name="android:textStyle">bold</item> + <item name="android:singleLine">true</item> + </style> + <style name="title_smart"> + <item name="android:textSize">20sp</item> + <item name="android:textStyle">bold|italic</item> + </style> + + <style name="color_green"> + <item name="android:textColor">#ff008000</item> + <item name="android:textStyle">bold</item> + <item name="android:singleLine">true</item> + </style> + <style name="color_orange"> + <item name="android:textColor">#ff800000</item> + <item name="android:textStyle">bold</item> + <item name="android:singleLine">true</item> + </style> +</resources> \ 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..e6ca730e4126cc4b830c9a92ec90de49ebff8ff4 --- /dev/null +++ b/Main/res/xml/preferences.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> + +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> + <PreferenceCategory android:title="@string/menu_settings"> + <PreferenceScreen + android:title="@string/title_change_password" + android:summary="@string/msg_change_password"> + <intent android:action="android.intent.action.VIEW" + android:targetPackage="gr.grnet.academicid.merchant" + android:targetClass="gr.grnet.academicid.merchant.ChangePassword"/> + </PreferenceScreen> + </PreferenceCategory> +</PreferenceScreen> \ No newline at end of file diff --git a/Main/src/gr/grnet/academicid/merchant/ChangePassword.java b/Main/src/gr/grnet/academicid/merchant/ChangePassword.java new file mode 100644 index 0000000000000000000000000000000000000000..248a0811c53afe18169cf49ab4d0ef27f916b42c --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/ChangePassword.java @@ -0,0 +1,130 @@ +package gr.grnet.academicid.merchant; + +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.merchant.domain.Inspector; +import gr.grnet.academicid.merchant.parser.ChangePasswordResponse; +import gr.grnet.academicid.merchant.parser.JSONParser; +import gr.grnet.academicid.merchant.services.ServiceHandler; +import gr.grnet.academicid.merchant.utilities.Constants; +import gr.grnet.academicid.merchant.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 = MerchantApplication.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 = MerchantApplication.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<String, Void, String> { + + @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 = MerchantApplication.getInspector(); + boolean userChangedPsw = inspector.shouldChangePswAtLogin(); + inspector.setPasswordHash(newPasswordHash); + inspector.setChangePswAtLogin(false); + MerchantApplication.setInspector(inspector); + + if (userChangedPsw) + startActivity(new Intent(ChangePassword.this, OfferList.class)); + else + finish(); + finish(); + } + } + } +} \ No newline at end of file diff --git a/Main/src/gr/grnet/academicid/merchant/MainActivity.java b/Main/src/gr/grnet/academicid/merchant/MainActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..df3a626fc84be0dc407839407c441d1b06d0effd --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/MainActivity.java @@ -0,0 +1,124 @@ +package gr.grnet.academicid.merchant; + +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.merchant.domain.Inspector; +import gr.grnet.academicid.merchant.parser.LoadUserResponse; +import gr.grnet.academicid.merchant.services.ServiceHandler; +import gr.grnet.academicid.merchant.parser.JSONParser; +import gr.grnet.academicid.merchant.utilities.Constants; +import gr.grnet.academicid.merchant.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<String, Void, String> { + + @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)); + MerchantApplication.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, OfferList.class)); + } + } + } +} diff --git a/Main/src/gr/grnet/academicid/merchant/MerchantApplication.java b/Main/src/gr/grnet/academicid/merchant/MerchantApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..07a5ecfc64275dd57860c920365a99782f69f0a9 --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/MerchantApplication.java @@ -0,0 +1,58 @@ +package gr.grnet.academicid.merchant; + +import android.app.Application; +import android.content.Context; +import gr.grnet.academicid.merchant.domain.Inspector; +import gr.grnet.academicid.merchant.domain.Offer; +import gr.grnet.academicid.merchant.utilities.SharedPrefs; + +public class MerchantApplication extends Application { + + private static Context context; + private static Inspector inspector; + private static Offer selectedOffer; + +//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ + + public MerchantApplication() { + super(); + } + + @Override + public void onCreate() { + super.onCreate(); + MerchantApplication.context = getApplicationContext(); + } + + @Override + public void onTerminate() { + super.onTerminate(); + SharedPrefs.ClearInspectorFromPrefs(); + } + + public static Context getAppContext() { + return MerchantApplication.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) { + MerchantApplication.inspector = inspector; + //Save object to Shared Preferences to prevent NPE when app resuming and android garbage collector has cleared this + SharedPrefs.setInspectorToPrefs(inspector); + } + + public static Offer getSelectedOffer() { + return selectedOffer; + } + + public static void setSelectedOffer(Offer selectedOffer) { + MerchantApplication.selectedOffer = selectedOffer; + } +} diff --git a/Main/src/gr/grnet/academicid/merchant/OfferList.java b/Main/src/gr/grnet/academicid/merchant/OfferList.java new file mode 100644 index 0000000000000000000000000000000000000000..b08873b596c3ba619bb2a63af1136dd5bc27f63c --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/OfferList.java @@ -0,0 +1,193 @@ +package gr.grnet.academicid.merchant; + +import android.app.ListActivity; +import android.app.ProgressDialog; +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.widget.*; +import gr.grnet.academicid.merchant.domain.Inspector; +import gr.grnet.academicid.merchant.domain.Offer; +import gr.grnet.academicid.merchant.parser.JSONParser; +import gr.grnet.academicid.merchant.parser.LoadProviderOffersResponse; +import gr.grnet.academicid.merchant.services.ServiceHandler; +import gr.grnet.academicid.merchant.utilities.Constants; +import gr.grnet.academicid.merchant.utilities.Tools; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class OfferList extends ListActivity { + + private ListView listView; + private TextView txtvOffersHeader; + + private ProgressDialog progressDialog; + private List<Offer> activeOfferList; + private List<Offer> inactiveOfferList; + private boolean showingActive; + private boolean doubleBackToExitPressedOnce = false; + +//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.offer_list); + + txtvOffersHeader = (TextView) findViewById(R.id.txt_offers_header); + + listView = getListView(); + + // Listview on item click listener + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + + Offer offer = (showingActive ? activeOfferList.get(position) : inactiveOfferList.get(position)); + + // Store selected offer for returning to Single Offer when pressing back in camera preview + MerchantApplication.setSelectedOffer(offer); + + // Starting single offer activity + Intent intent = new Intent(OfferList.this, SingleOffer.class); + intent.putExtra(Offer.ID, offer.getId()); + intent.putExtra(Offer.TITLE, offer.getTitle()); + intent.putExtra(Offer.DESCRIPTION, offer.getDescription()); + intent.putExtra(Offer.STATUS, offer.getStatus()); + intent.putExtra(Offer.START_DATE, offer.getStartDate()); + intent.putExtra(Offer.END_DATE, offer.getEndDate()); + intent.putExtra(Offer.BENEFICIARIES, offer.getBeneficiaries()); + intent.putExtra(Offer.CRITERIA, offer.getCriteria()); + startActivity(intent); + } + }); + + // Get inspector object from application class + Inspector inspector = MerchantApplication.getInspector(); + new RetrieveOffers().execute(inspector.getUsername(), inspector.getPasswordHash()); + } + + @Override + protected void onResume() { + super.onResume(); + this.doubleBackToExitPressedOnce = false; + } + + private class RetrieveOffers extends AsyncTask<String, Void, String> { + + @Override + protected void onPreExecute() { + super.onPreExecute(); + // Showing progress dialog + progressDialog = new ProgressDialog(OfferList.this); + progressDialog.setMessage(getString(R.string.msg_wait)); + progressDialog.setCancelable(false); + progressDialog.show(); + } + + @Override + protected String doInBackground(String... credentials) { + return ServiceHandler.makeLoadProviderOffersCall(credentials[0], credentials[1]); + } + + @Override + protected void onPostExecute(String result) { + super.onPostExecute(result); + // Dismiss the progress dialog + if (progressDialog.isShowing()) + progressDialog.dismiss(); + + LoadProviderOffersResponse loadProviderOffersResponse = JSONParser.getResultOfLoadProviderOffers(result); + + if (!loadProviderOffersResponse.isSuccessful()) { + Toast.makeText(getApplicationContext(), getString(R.string.msg_error_retrieving_offers), Toast.LENGTH_SHORT).show(); + Log.e(Constants.LOGTAG, "OfferList.RetrieveOffers.OnPostExecute(): Web service returned unsuccessfully with errorCode: " + loadProviderOffersResponse.getError()); + } else { + List<Offer> offerList = loadProviderOffersResponse.getOffers(); + activeOfferList = new ArrayList<Offer>(); + inactiveOfferList = new ArrayList<Offer>(); + + // Split the offer list in active and inactive offers + Tools.splitOffers(offerList, activeOfferList, inactiveOfferList); + + // Set flag to show the active offers by default + showingActive = true; + + // Set the view that should be displayed if no offer exists + listView.setEmptyView(findViewById(R.id.empty)); + + // Prepare the list with offers that will be displayed + List<Map<String, String>> adapterOfferList = Tools.prepareListForAdapter(activeOfferList); + ListAdapter adapter = new SimpleAdapter(OfferList.this, adapterOfferList, R.layout.offer_list_item, new String[]{Offer.TITLE, Offer.DESCRIPTION}, new int[]{R.id.txtv_offer_item_title, R.id.txtv_offer_item_description}); + setListAdapter(adapter); + } + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.menu_offer_list, menu); + return true; + } + + @Override + public boolean onMenuItemSelected(int featureId, @NotNull MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_toggle_offers: + if (showingActive) { + showingActive = false; + + item.setTitle(getString(R.string.lbl_active_offers)); + item.setIcon(R.drawable.rating_important); + + txtvOffersHeader.setText(R.string.lbl_inactive_offers); + txtvOffersHeader.setTextAppearance(this, R.style.header_orange); + + List<Map<String, String>> adapterOfferList = Tools.prepareListForAdapter(inactiveOfferList); + ListAdapter adapter = new SimpleAdapter(OfferList.this, adapterOfferList, R.layout.offer_list_item, new String[]{Offer.TITLE, Offer.DESCRIPTION}, new int[]{R.id.txtv_offer_item_title, R.id.txtv_offer_item_description}); + setListAdapter(adapter); + } else { + showingActive = true; + + item.setTitle(getString(R.string.lbl_inactive_offers)); + item.setIcon(R.drawable.rating_not_important); + + txtvOffersHeader.setText(R.string.lbl_active_offers); + txtvOffersHeader.setTextAppearance(this, R.style.header_green); + + List<Map<String, String>> adapterOfferList = Tools.prepareListForAdapter(activeOfferList); + ListAdapter adapter = new SimpleAdapter(OfferList.this, adapterOfferList, R.layout.offer_list_item, new String[]{Offer.TITLE, Offer.DESCRIPTION}, new int[]{R.id.txtv_offer_item_title, R.id.txtv_offer_item_description}); + setListAdapter(adapter); + } + return true; + 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/merchant/Preferences.java b/Main/src/gr/grnet/academicid/merchant/Preferences.java new file mode 100644 index 0000000000000000000000000000000000000000..75258a6286aef8b480a4dff14520f90fa7168485 --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/Preferences.java @@ -0,0 +1,11 @@ +package gr.grnet.academicid.merchant; + +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/merchant/ScanQR.java b/Main/src/gr/grnet/academicid/merchant/ScanQR.java new file mode 100644 index 0000000000000000000000000000000000000000..734fc9a38969ba54db4016fe150f421c95a3f1ad --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/ScanQR.java @@ -0,0 +1,60 @@ +package gr.grnet.academicid.merchant; + +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.merchant.utilities.Constants; +import gr.grnet.academicid.merchant.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/merchant/SingleOffer.java b/Main/src/gr/grnet/academicid/merchant/SingleOffer.java new file mode 100644 index 0000000000000000000000000000000000000000..e2f0112a9e257f714cd8cf35d00c1398b159ad3b --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/SingleOffer.java @@ -0,0 +1,226 @@ +package gr.grnet.academicid.merchant; + +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.*; +import gr.grnet.academicid.merchant.domain.Inspector; +import gr.grnet.academicid.merchant.domain.Offer; +import gr.grnet.academicid.merchant.parser.InspectProviderOfferResponse; +import gr.grnet.academicid.merchant.parser.JSONParser; +import gr.grnet.academicid.merchant.services.ServiceHandler; +import gr.grnet.academicid.merchant.utilities.Constants; +import gr.grnet.academicid.merchant.utilities.Tools; +import org.jetbrains.annotations.NotNull; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class SingleOffer extends Activity { + + private static final int SCAN_QR = 1; + private static final String TAG = "SERIAL_TAG"; + + private TextView txtvInspectionResult; + private TextView txtvInspectionResultError; + + private ProgressDialog progressDialog; + private long offerId; + private int offerStatus; + private int offerBeneficiaries; + +//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.single_offer); + + // Assign views to variables + TextView txtvOfferTitle = (TextView) findViewById(R.id.txtv_offer_title); + TextView txtvOfferDescription = (TextView) findViewById(R.id.txtv_offer_description); + TextView txtvOfferStartDate = (TextView) findViewById(R.id.txtv_offer_startDate); + TextView txtvOfferEndDate = (TextView) findViewById(R.id.txtv_offer_endDate); + TextView txtOfferCriteria = (TextView) findViewById(R.id.txtv_offer_criteria); + + txtvInspectionResult = (TextView) findViewById(R.id.txtv_offer_inspectionResult); + txtvInspectionResultError = (TextView) findViewById(R.id.txtv_offer_inspectionResultError); + + Button btnScanQR = (Button) findViewById(R.id.btn_scan_qr); + RelativeLayout rellaySerialInput = (RelativeLayout) findViewById(R.id.rellay_serial_input); + + + DateFormat format = new SimpleDateFormat("dd/MM/yyyy"); + + // getting intent data + Intent intent = getIntent(); + + // Get values from intent extras + offerId = intent.getLongExtra(Offer.ID, -1L); + String offerTitle = intent.getStringExtra(Offer.TITLE); + String offerDescription = intent.getStringExtra(Offer.DESCRIPTION); + offerStatus = intent.getIntExtra(Offer.STATUS, 0); + offerBeneficiaries = intent.getIntExtra(Offer.BENEFICIARIES, 0); + Long offerStartDate = intent.getLongExtra(Offer.START_DATE, 0L); + Long offerEndDate = intent.getLongExtra(Offer.END_DATE, 0L); + String offerCriteria = intent.getStringExtra(Offer.CRITERIA); + + // Format values to display them on screen + String offerStartDateStr = format.format(new Date(offerStartDate)); + String offerEndDateStr = format.format(new Date(offerEndDate)); + String newOfferCriteria = offerCriteria.replaceAll("<br>", "\n"); + + // Displaying offer values on screen + txtvOfferTitle.setText(offerTitle); + txtvOfferDescription.setText(offerDescription); + txtvOfferStartDate.setText(offerStartDateStr); + txtvOfferEndDate.setText(offerEndDateStr); + txtOfferCriteria.setText(newOfferCriteria); + + // If offer is active then show ScanQR and InputSerial views for validating an offer + if (offerStatus == Offer.ACTIVE) { + txtvOfferTitle.setTextAppearance(this, R.style.title_green); + } else { + txtvOfferTitle.setTextAppearance(this, R.style.title_orange); + + btnScanQR.setVisibility(View.GONE); + rellaySerialInput.setVisibility(View.GONE); + } + } + + 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); + validate(scanResult); + } + } + } + + // Method called after 'Check Serial Number' 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 = ((EditText) findViewById(R.id.etxt_serial_number)).getText().toString(); + validate(input); + } + + private void validate(String academicId) { + // The passed parameter should be a 12 digits numeric value. Otherwise show invalid Academic ID without making an network call + if ((academicId.length() == 12) && Tools.isNumericValue(academicId)) { + Inspector inspector = MerchantApplication.getInspector(); + Offer offer = new Offer(); + + offer.setId(offerId); + offer.setStatus(offerStatus); + offer.setBeneficiaries(offerBeneficiaries); + offer.setAvailableAllAcademics(true); + + new ValidateOffer().execute(inspector.getUsername(), inspector.getPasswordHash(), offer, academicId); + } else { + txtvInspectionResult.setVisibility(View.VISIBLE); + txtvInspectionResult.setTextAppearance(this, R.style.color_orange); + txtvInspectionResult.setText(R.string.msg_invalid_input); + } + } + + private class ValidateOffer extends AsyncTask<Object, Void, String> { + + @Override + protected void onPreExecute() { + super.onPreExecute(); + // Showing progress dialog + progressDialog = new ProgressDialog(SingleOffer.this); + progressDialog.setMessage(getString(R.string.msg_wait)); + progressDialog.setCancelable(false); + progressDialog.show(); + } + + @Override + protected String doInBackground(Object... variables) { + return ServiceHandler.makeInspectProviderOfferCall((String) variables[0], (String) variables[1], (Offer) variables[2], (String) variables[3]); + } + + @Override + protected void onPostExecute(String result) { + super.onPostExecute(result); + // Dismiss the progress dialog + if (progressDialog.isShowing()) + progressDialog.dismiss(); + + InspectProviderOfferResponse inspectProviderOfferResponse = JSONParser.getResultsOfInspectProviderOffer(result); + + if (!inspectProviderOfferResponse.isSuccessful()) { + Toast.makeText(getApplicationContext(), getString(R.string.msg_error_validating_offer), Toast.LENGTH_SHORT).show(); + Log.e(Constants.LOGTAG, "SingleOffer.ValidateOffer.OnPostExecute(): Web service returned unsuccessfully with errorCode: " + inspectProviderOfferResponse.getError()); + } else { + String inspectionResultBuilder; + + if (inspectProviderOfferResponse.getOfferValidity()) { + inspectionResultBuilder = getResources().getString(R.string.msg_eligible_for_offer); + + txtvInspectionResult.setTextAppearance(SingleOffer.this, R.style.color_green); + txtvInspectionResultError.setVisibility(View.GONE); + } else { + inspectionResultBuilder = getResources().getString(R.string.msg_not_eligible_for_offer); + + txtvInspectionResult.setTextAppearance(SingleOffer.this, R.style.color_orange); + txtvInspectionResultError.setVisibility(View.VISIBLE); + txtvInspectionResultError.setText(inspectProviderOfferResponse.getErrorDescription()); + } + + txtvInspectionResult.setVisibility(View.VISIBLE); + inspectionResultBuilder = inspectionResultBuilder.replace(TAG, inspectProviderOfferResponse.getAcademicId().toString()); + txtvInspectionResult.setText(inspectionResultBuilder); + } + } + } + + // Method called after 'Clear Content' button is pressed + public void clearContent(View view) { + EditText serialInput = (EditText) findViewById(R.id.etxt_serial_number); + serialInput.setText(""); + + txtvInspectionResult.setVisibility(View.GONE); + txtvInspectionResultError.setVisibility(View.GONE); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.menu_single_offer, menu); + return true; + } + + @Override + public boolean onMenuItemSelected(int featureId, @NotNull MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_offers: + startActivity(new Intent(this, OfferList.class)); + return true; + case R.id.menu_settings: + startActivity(new Intent(this, Preferences.class)); + return true; + default: + return super.onMenuItemSelected(featureId, item); + } + } +} \ No newline at end of file diff --git a/Main/src/gr/grnet/academicid/merchant/domain/Inspector.java b/Main/src/gr/grnet/academicid/merchant/domain/Inspector.java new file mode 100644 index 0000000000000000000000000000000000000000..3a687cf1ad541524c183559432088c1ede13ec2f --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/domain/Inspector.java @@ -0,0 +1,92 @@ +package gr.grnet.academicid.merchant.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; + } +} diff --git a/Main/src/gr/grnet/academicid/merchant/domain/Offer.java b/Main/src/gr/grnet/academicid/merchant/domain/Offer.java new file mode 100644 index 0000000000000000000000000000000000000000..336eb0adfb56b260517eb977172c7f84e08b1966 --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/domain/Offer.java @@ -0,0 +1,134 @@ +package gr.grnet.academicid.merchant.domain; + +import java.util.List; + +@SuppressWarnings("UnusedDeclaration") +public class Offer { + + public static final String ID = "id"; + public static final String TITLE = "title"; + public static final String DESCRIPTION = "description"; + public static final String STATUS = "status"; + public static final String START_DATE = "startDate"; + public static final String END_DATE = "endDate"; + public static final String BENEFICIARIES = "beneficiaries"; + public static final String AVAILABLE_ALL_PREFECTURES = "availableAllPrefectures"; + public static final String AVAILABLE_ALL_ACADEMICS = "availableAllAcademics"; + public static final String CRITERIA = "criteria"; + + public static final int ACTIVE = 3; + + private long id; + private String title; + private String description; + private int status; + private long startDate; + private Long endDate; + private int beneficiaries; + private boolean availableAllPrefectures; + private List<Integer> prefectures; + private boolean availableAllAcademics; + private List<Integer> academics; + private String criteria; + +//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ + + public Offer() { + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public long getStartDate() { + return startDate; + } + + public void setStartDate(long startDate) { + this.startDate = startDate; + } + + public Long getEndDate() { + return endDate; + } + + public void setEndDate(Long endDate) { + this.endDate = endDate; + } + + public int getBeneficiaries() { + return beneficiaries; + } + + public void setBeneficiaries(int beneficiaries) { + this.beneficiaries = beneficiaries; + } + + public boolean isAvailableAllPrefectures() { + return availableAllPrefectures; + } + + public void setAvailableAllPrefectures(boolean availableAllPrefectures) { + this.availableAllPrefectures = availableAllPrefectures; + } + + public List<Integer> getPrefectures() { + return prefectures; + } + + public void setPrefectures(List<Integer> prefectures) { + this.prefectures = prefectures; + } + + public boolean isAvailableAllAcademics() { + return availableAllAcademics; + } + + public void setAvailableAllAcademics(boolean availableAllAcademics) { + this.availableAllAcademics = availableAllAcademics; + } + + public List<Integer> getAcademics() { + return academics; + } + + public void setAcademics(List<Integer> academics) { + this.academics = academics; + } + + public String getCriteria() { + return criteria; + } + + public void setCriteria(String criteria) { + this.criteria = criteria; + } +} diff --git a/Main/src/gr/grnet/academicid/merchant/parser/ChangePasswordResponse.java b/Main/src/gr/grnet/academicid/merchant/parser/ChangePasswordResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..6f671cc631e6d53ef8f058543e37a7f0b7b27ae3 --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/parser/ChangePasswordResponse.java @@ -0,0 +1,35 @@ +package gr.grnet.academicid.merchant.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)); + } +} diff --git a/Main/src/gr/grnet/academicid/merchant/parser/InspectProviderOfferResponse.java b/Main/src/gr/grnet/academicid/merchant/parser/InspectProviderOfferResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..7baceb2d9b570169659460af4fb51321ab51bd32 --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/parser/InspectProviderOfferResponse.java @@ -0,0 +1,73 @@ +package gr.grnet.academicid.merchant.parser; + +@SuppressWarnings("UnusedDeclaration") +public class InspectProviderOfferResponse { + + private String response; + private String error; + private Long academicId; + private Boolean offerValidity; + private String errorDescription; + +//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ + + public InspectProviderOfferResponse(String response, String error) { + this.response = response; + this.error = error; + this.academicId = null; + this.offerValidity = null; + this.errorDescription = null; + } + + public InspectProviderOfferResponse(String response, Long academicId, Boolean offerValidity, String errorDescription) { + this.response = response; + this.error = null; + this.academicId = academicId; + this.offerValidity = offerValidity; + this.errorDescription = errorDescription; + } + + 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 Long getAcademicId() { + return academicId; + } + + public void setAcademicId(Long academicId) { + this.academicId = academicId; + } + + public Boolean getOfferValidity() { + return offerValidity; + } + + public void setOfferValidity(Boolean offerValidity) { + this.offerValidity = offerValidity; + } + + public String getErrorDescription() { + return errorDescription; + } + + public void setErrorDescription(String errorDescription) { + this.errorDescription = errorDescription; + } + + public boolean isSuccessful() { + return (response.equals(JSONParser.RESPONSE_SUCCESSFUL)); + } +} diff --git a/Main/src/gr/grnet/academicid/merchant/parser/JSONParser.java b/Main/src/gr/grnet/academicid/merchant/parser/JSONParser.java new file mode 100644 index 0000000000000000000000000000000000000000..399b8a272991cace0c43bcd0f18e97943ea8de47 --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/parser/JSONParser.java @@ -0,0 +1,163 @@ +package gr.grnet.academicid.merchant.parser; + +import android.util.Log; +import gr.grnet.academicid.merchant.domain.*; +import gr.grnet.academicid.merchant.utilities.Constants; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +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.merchant.parser.LoadUserResponse} object. + * It is called after {@link gr.grnet.academicid.merchant.services.ServiceHandler#makeSingInCall(String, String)} + * + * @param jsonString The string that should be parsed. + * @return A {@link gr.grnet.academicid.merchant.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.merchant.parser.LoadProviderOffersResponse} object + * It is called after {@link gr.grnet.academicid.merchant.services.ServiceHandler#makeLoadProviderOffersCall(String, String)} + * + * @param jsonString The string that should be parsed. + * @return A {@link gr.grnet.academicid.merchant.parser.LoadProviderOffersResponse} object. + */ + public static LoadProviderOffersResponse getResultOfLoadProviderOffers(String jsonString) { + try { + JSONObject jsonObject = new JSONObject(jsonString); + String response = jsonObject.getString(TAG_RESPONSE); + + if (response.equals(RESPONSE_SUCCESSFUL)) { + List<Offer> offerList = new ArrayList<Offer>(); + JSONArray jsonArray = jsonObject.getJSONArray("discountOffers"); + + for (int i = 0; i < jsonArray.length(); i++) { + Offer offer = new Offer(); + JSONObject jsonOffer = jsonArray.getJSONObject(i); + + offer.setId(jsonOffer.getLong("offerId")); + offer.setTitle(jsonOffer.getString("title")); + offer.setDescription(jsonOffer.getString("description")); + offer.setStatus(jsonOffer.getInt("offerStatus")); + offer.setStartDate(jsonOffer.getLong("startDate")); + offer.setEndDate(jsonOffer.getLong("endDate")); + offer.setBeneficiaries(jsonOffer.getInt("beneficiaries")); + offer.setAvailableAllPrefectures(jsonOffer.getBoolean("availableToAllPrefectures")); + offer.setAvailableAllAcademics(jsonOffer.getBoolean("availableToAllAcademics")); + offer.setCriteria(jsonOffer.getString("criteria")); + + offerList.add(offer); + } + + return new LoadProviderOffersResponse(RESPONSE_SUCCESSFUL, null, offerList); + } else { + String error = jsonObject.getString(TAG_RESPONSE_ERROR); + return new LoadProviderOffersResponse(RESPONSE_FAILED, error, null); + } + } catch (JSONException e) { + Log.e(Constants.LOGTAG, "JSONParser.LoadProviderOffersResponse(): Error while parsing string: " + jsonString); + return null; + } + } + + /** + * This method parses a json string and creates a {@link gr.grnet.academicid.merchant.parser.InspectProviderOfferResponse} object + * It is called after {@link gr.grnet.academicid.merchant.services.ServiceHandler#makeInspectProviderOfferCall(String, String, gr.grnet.academicid.merchant.domain.Offer, String)} + * + * @param jsonString The string that should be parsed. + * @return A {@link gr.grnet.academicid.merchant.parser.InspectProviderOfferResponse} object. + */ + public static InspectProviderOfferResponse getResultsOfInspectProviderOffer(String jsonString) { + try { + JSONObject jsonObject = new JSONObject(jsonString); + String response = jsonObject.getString(TAG_RESPONSE); + + if (response.equals(RESPONSE_SUCCESSFUL)) { + JSONObject offerInspection = jsonObject.getJSONObject("inspectionResult"); + Long academicId = Long.valueOf(offerInspection.getString("academicId")); + Boolean validOffer = offerInspection.getBoolean("valid"); + String errorDescription = offerInspection.getString("error"); + + return new InspectProviderOfferResponse(RESPONSE_SUCCESSFUL, academicId, validOffer, errorDescription); + } else { + String error = jsonObject.getString(TAG_RESPONSE_ERROR); + return new InspectProviderOfferResponse(RESPONSE_FAILED, error); + } + } catch (JSONException e) { + Log.e(Constants.LOGTAG, "JSONParser.LoadProviderOffersResponse(): Error while parsing string: " + jsonString); + return null; + } + } + + /** + * This method parses a json string and creates a {@link gr.grnet.academicid.merchant.parser.ChangePasswordResponse} object. + * + * @param jsonString The string that should be parsed. + * @return A {@link gr.grnet.academicid.merchant.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; + } + } +} diff --git a/Main/src/gr/grnet/academicid/merchant/parser/LoadProviderOffersResponse.java b/Main/src/gr/grnet/academicid/merchant/parser/LoadProviderOffersResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..66a5f06c7198a92bea9eb68edaad17fc48b6e40a --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/parser/LoadProviderOffersResponse.java @@ -0,0 +1,49 @@ +package gr.grnet.academicid.merchant.parser; + +import gr.grnet.academicid.merchant.domain.Offer; + +import java.util.List; + +@SuppressWarnings("UnusedDeclaration") +public class LoadProviderOffersResponse { + + private String response; + private String error; + private List<Offer> offers; + +//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ + + public LoadProviderOffersResponse(String response, String error, List<Offer> offers) { + this.response = response; + this.error = error; + this.offers = offers; + } + + 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 List<Offer> getOffers() { + return offers; + } + + public void setOffers(List<Offer> offers) { + this.offers = offers; + } + + public boolean isSuccessful() { + return (response.equals(JSONParser.RESPONSE_SUCCESSFUL)); + } +} diff --git a/Main/src/gr/grnet/academicid/merchant/parser/LoadUserResponse.java b/Main/src/gr/grnet/academicid/merchant/parser/LoadUserResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..9b99e3bc20eaad9574dec83c848fc11c43fc116d --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/parser/LoadUserResponse.java @@ -0,0 +1,47 @@ +package gr.grnet.academicid.merchant.parser; + +import gr.grnet.academicid.merchant.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)); + } +} diff --git a/Main/src/gr/grnet/academicid/merchant/services/ServiceHandler.java b/Main/src/gr/grnet/academicid/merchant/services/ServiceHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..553a2f8ef2079d55bd0d98c1e77501a297cb039d --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/services/ServiceHandler.java @@ -0,0 +1,178 @@ +package gr.grnet.academicid.merchant.services; + +import android.util.Log; +import gr.grnet.academicid.merchant.domain.Offer; +import gr.grnet.academicid.merchant.utilities.Constants; +import gr.grnet.academicid.merchant.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 LOAD_PROVIDER_OFFERS = "loadProviderOffers"; // Get method + private static final String INSPECT_PROVIDER_OFFER = "inspectProviderOffer"; // 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 retrieves the offers of the provider that user belongs to. + * + * @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 makeLoadProviderOffersCall(String username, String passwordHash) { + try { + HttpGet httpMethod = new HttpGet(URL + PATH + LOAD_PROVIDER_OFFERS); + 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.makeLoadProviderOffersCall(): Error while user: " + username + " trying to retrieve provider offers", e); + return null; + } + } + + /** + * This method verifies if an owner of an academic ID is eligible of an offer. + * + * @param username The username of the user. + * @param passwordHash The password of the user hashed by {@link Tools#getHash(String)}. + * @param offer The offer for which we enquire if the owner of the academic Id is eligible. + * @param academicId The serial number of the Academic ID card. + * @return a json string. + */ + public static String makeInspectProviderOfferCall(String username, String passwordHash, Offer offer, String academicId) { + try { + HttpPost httpMethod = new HttpPost(URL + PATH + INSPECT_PROVIDER_OFFER); + 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 jsonOffer = new JSONObject(); + jsonOffer.put("offerId", offer.getId()); + jsonOffer.put("offerStatus", offer.getStatus()); + jsonOffer.put("beneficiaries", offer.getBeneficiaries()); + jsonOffer.put("availableToAllAcademics", offer.isAvailableAllAcademics()); + jsonOffer.put("academicID", academicId); + + httpMethod.setEntity(new StringEntity(jsonOffer.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.makeInspectProviderOfferCall(): Error while trying to construct JSON Offer", e); + return null; + } catch (IOException e) { + Log.e(Constants.LOGTAG, "ServiceHandler.makeInspectProviderOfferCall(): Error while user: " + username + " trying to validate AcademicID: " + academicId + " with offer: " + offer.getId(), 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; + } + } +} diff --git a/Main/src/gr/grnet/academicid/merchant/utilities/Constants.java b/Main/src/gr/grnet/academicid/merchant/utilities/Constants.java new file mode 100644 index 0000000000000000000000000000000000000000..87f2ca80029fe588f7366823dd7cbdacebc5f865 --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/utilities/Constants.java @@ -0,0 +1,12 @@ +package gr.grnet.academicid.merchant.utilities; + +/** + * Constant globally available in Inspector application. + */ +public class Constants { + + public static final String LOGTAG = "Merchant"; + + // The message digest algorithm that is used to hash user password. + public static final String DIGEST_ALGORITHM = "MD5"; +} diff --git a/Main/src/gr/grnet/academicid/merchant/utilities/SharedPrefs.java b/Main/src/gr/grnet/academicid/merchant/utilities/SharedPrefs.java new file mode 100644 index 0000000000000000000000000000000000000000..dd15f611b159dfe9e59dc4672c30c2aca2c8d8fa --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/utilities/SharedPrefs.java @@ -0,0 +1,79 @@ +package gr.grnet.academicid.merchant.utilities; + +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import gr.grnet.academicid.merchant.MerchantApplication; +import gr.grnet.academicid.merchant.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(MerchantApplication.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(MerchantApplication.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(MerchantApplication.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(); + } +} diff --git a/Main/src/gr/grnet/academicid/merchant/utilities/Tools.java b/Main/src/gr/grnet/academicid/merchant/utilities/Tools.java new file mode 100644 index 0000000000000000000000000000000000000000..bac41d3f237e2a71b267b8a8b70d39294a31fe12 --- /dev/null +++ b/Main/src/gr/grnet/academicid/merchant/utilities/Tools.java @@ -0,0 +1,133 @@ +package gr.grnet.academicid.merchant.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.merchant.MerchantApplication; +import gr.grnet.academicid.merchant.domain.Offer; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 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) MerchantApplication.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 = MerchantApplication.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 splits the offers provided with offerList param to two lists based on whether the offer is active or not. + * + * @param offerList the original list containing both active and inactive offers. + * @param activeOfferList the list containing only active offers. + * @param inactiveOfferList the list containing only inactive offers. + */ + public static void splitOffers(List<Offer> offerList, List<Offer> activeOfferList, List<Offer> inactiveOfferList) { + for (Offer offer : offerList) { + if (offer.getStatus() == 3) //Status code 3 designates Active Offers + activeOfferList.add(offer); + else + inactiveOfferList.add(offer); + } + } + + /** + * This method gets a list with offers and prepares it to be fed in a list adapter. + * + * @param offerList the original list with offers. + * @return a List < Map < String,String>> for the ListAdapter. + */ + public static List<Map<String, String>> prepareListForAdapter(List<Offer> offerList) { + List<Map<String, String>> adapterList = new ArrayList<Map<String, String>>(); + + for (Offer offer : offerList) { + Map<String, String> adapterOffer = new HashMap<String, String>(); + adapterOffer.put(Offer.TITLE, offer.getTitle()); + adapterOffer.put(Offer.DESCRIPTION, offer.getDescription()); + + adapterList.add(adapterOffer); + } + return adapterList; + } +} 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.dm.zbar.android.scanner" + android:versionCode="1" + android:versionName="1.0"> + + <uses-permission android:name="android.permission.CAMERA"/> + <uses-feature android:name="android.hardware.camera" /> + <uses-sdk android:minSdkVersion="8" /> + + <application android:label="@string/app_name" android:icon="@drawable/ic_launcher"> + <activity android:name="ZBarScannerActivity" + android:screenOrientation="landscape" + android:label="@string/app_name" /> + </application> +</manifest> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">ZBar Scanner</string> +</resources> 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<Size> 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<Size> 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..b6790c6cba9744e0de5540721ffd4bab13995540 --- /dev/null +++ b/ZBarScannerLibrary/src/com/dm/zbar/android/scanner/ZBarScannerActivity.java @@ -0,0 +1,188 @@ +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.merchant.*; +import gr.grnet.academicid.merchant.domain.Offer; +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() { + Offer offer = MerchantApplication.getSelectedOffer(); + + // Starting single offer activity + Intent intent = new Intent(this, SingleOffer.class); + + intent.putExtra(Offer.ID, offer.getId()); + intent.putExtra(Offer.TITLE, offer.getTitle()); + intent.putExtra(Offer.DESCRIPTION, offer.getDescription()); + intent.putExtra(Offer.STATUS, offer.getStatus()); + intent.putExtra(Offer.BENEFICIARIES, offer.getBeneficiaries()); + intent.putExtra(Offer.START_DATE, offer.getStartDate()); + intent.putExtra(Offer.END_DATE, offer.getEndDate()); + intent.putExtra(Offer.CRITERIA, offer.getCriteria()); + + // Pop the existing activity instead of creating a new one + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + + startActivity(intent); + } +} +