From eb02e85eb95d2b28457f1858fdbd38629c89933f Mon Sep 17 00:00:00 2001 From: "saad.siddiq" Date: Tue, 27 Jan 2026 10:08:35 +0500 Subject: [PATCH] Initial commit --- .gitignore | 15 + .idea/.gitignore | 3 + .idea/.name | 1 + .idea/compiler.xml | 6 + .idea/deploymentTargetSelector.xml | 10 + .idea/gradle.xml | 19 + .idea/migrations.xml | 10 + .idea/misc.xml | 9 + .idea/vcs.xml | 6 + app/.gitignore | 7 + app/build.gradle | 57 ++ app/proguard-rules.pro | 21 + .../qc_android/ExampleInstrumentedTest.java | 26 + app/src/main/AndroidManifest.xml | 67 +++ .../activities/DashboardActivity.java | 175 ++++++ .../qc_android/activities/LoginActivity.java | 254 +++++++++ .../activities/QCTerryActivity.java | 43 ++ .../adapters/CheckPointAdapter.java | 201 +++++++ .../qc_android/adapters/ImageAdapter.java | 84 +++ .../qc_android/adapters/PagerAdapter.java | 62 +++ .../qc_android/apiservice/ApiService.java | 53 ++ .../apiservice/ApiServiceFactory.java | 17 + .../qc_android/apiservice/GiteaClient.java | 27 + .../apiservice/GiteaRestService.java | 16 + .../qc_android/apiservice/RetrofitClient.java | 49 ++ .../apiservice/SSLCheckHttpClient.java | 52 ++ .../qc_android/db/CheckpointRepository.java | 83 +++ .../qc_android/db/DatabaseHelper.java | 213 ++++++++ .../qc_android/fragments/DraftFragment.java | 195 +++++++ .../fragments/FragmentQcTerryOne.java | 293 ++++++++++ .../fragments/FragmentQcTerryThree.java | 134 +++++ .../fragments/FragmentQcTerryTwo.java | 509 ++++++++++++++++++ .../qc_android/fragments/ReportsFragment.java | 162 ++++++ .../qc_android/helper/DateTimeUtils.java | 22 + .../qc_android/helper/Helper.java | 459 ++++++++++++++++ .../qc_android/helper/Preference.java | 82 +++ .../qc_android/helper/PropertyReader.java | 24 + .../qc_android/helper/Release.java | 69 +++ .../qc_android/helper/ReleaseAsset.java | 95 ++++ .../models/InspectionCheckPoint.java | 118 ++++ .../qc_android/utils/DatabaseExecutor.java | 19 + .../qc_android/utils/FileUtils.java | 98 ++++ .../utils/FullScreenImageDialog.java | 46 ++ .../utils/NonSwipeableViewPager.java | 33 ++ .../utils/ProgressDialogFragment.java | 37 ++ .../qc_android/utils/SelectListener.java | 7 + .../jackson/LocalDateTimeDeserializer.java | 35 ++ .../jackson/LocalDateTimeSerializer.java | 25 + .../jackson/ZonedDateTimeDeserializer.java | 35 ++ .../viewmodels/DataEntryViewModel.java | 90 ++++ .../viewmodels/ServiceViewModel.java | 278 ++++++++++ .../main/res/anim/bs_list_layout_fade_in.xml | 6 + app/src/main/res/anim/fade_in.xml | 11 + app/src/main/res/anim/fade_out.xml | 11 + app/src/main/res/anim/grow_from_bottom.xml | 14 + app/src/main/res/anim/grow_from_top.xml | 15 + app/src/main/res/anim/popup_hide.xml | 8 + app/src/main/res/anim/popup_show.xml | 8 + app/src/main/res/anim/slide_down.xml | 3 + app/src/main/res/anim/slide_in_bottom.xml | 9 + app/src/main/res/anim/slide_in_left.xml | 11 + app/src/main/res/anim/slide_in_left_menu.xml | 8 + app/src/main/res/anim/slide_in_right.xml | 11 + app/src/main/res/anim/slide_in_right_menu.xml | 8 + app/src/main/res/anim/slide_in_top.xml | 9 + app/src/main/res/anim/slide_out_bottom.xml | 9 + app/src/main/res/anim/slide_out_left.xml | 11 + app/src/main/res/anim/slide_out_right.xml | 11 + app/src/main/res/anim/slide_out_top.xml | 9 + app/src/main/res/anim/slide_up.xml | 5 + app/src/main/res/color/tab_text_selector.xml | 5 + app/src/main/res/drawable/arrow_back.xml | 5 + .../main/res/drawable/circle_background.xml | 5 + app/src/main/res/drawable/custom_button.xml | 9 + app/src/main/res/drawable/hse_login.jpg | Bin 0 -> 12940 bytes app/src/main/res/drawable/ic_add.xml | 5 + app/src/main/res/drawable/ic_close.xml | 5 + app/src/main/res/drawable/ic_delete.xml | 5 + app/src/main/res/drawable/ic_email.xml | 5 + app/src/main/res/drawable/ic_launcher.webp | Bin 0 -> 1140 bytes .../res/drawable/ic_launcher_background.xml | 170 ++++++ .../res/drawable/ic_launcher_foreground.xml | 30 ++ app/src/main/res/drawable/ic_password.xml | 5 + app/src/main/res/drawable/icon_update.xml | 5 + app/src/main/res/drawable/image_picker.xml | 5 + app/src/main/res/drawable/img_load.xml | 12 + app/src/main/res/drawable/qc.png | Bin 0 -> 4679 bytes .../main/res/drawable/rounded_btn_login.xml | 7 + app/src/main/res/drawable/rounded_white.xml | 13 + .../res/drawable/tab_background_selector.xml | 5 + .../res/layout-sw800dp/activity_dashboard.xml | 111 ++++ .../res/layout-sw800dp/activity_login.xml | 119 ++++ .../res/layout-sw800dp/activity_qcterry.xml | 18 + .../main/res/layout-sw800dp/check_point.xml | 96 ++++ .../custom_layout_for_image.xml | 87 +++ .../main/res/layout-sw800dp/custom_tab.xml | 20 + .../dialog_fullscreen_image.xml | 31 ++ .../res/layout-sw800dp/dialog_progress.xml | 15 + .../res/layout-sw800dp/fragment_draft.xml | 36 ++ .../layout-sw800dp/fragment_qc_terry_one.xml | 109 ++++ .../fragment_qc_terry_three.xml | 81 +++ .../layout-sw800dp/fragment_qc_terry_two.xml | 25 + .../res/layout-sw800dp/fragment_reports.xml | 36 ++ .../main/res/layout-sw800dp/item_image.xml | 13 + .../main/res/layout/activity_dashboard.xml | 57 ++ app/src/main/res/layout/activity_login.xml | 112 ++++ app/src/main/res/layout/activity_qcterry.xml | 10 + app/src/main/res/layout/check_point.xml | 148 +++++ .../res/layout/custom_layout_for_image.xml | 87 +++ app/src/main/res/layout/custom_tab.xml | 21 + .../res/layout/dialog_fullscreen_image.xml | 31 ++ app/src/main/res/layout/dialog_progress.xml | 15 + app/src/main/res/layout/fragment_draft.xml | 38 ++ .../main/res/layout/fragment_qc_terry_one.xml | 109 ++++ .../res/layout/fragment_qc_terry_three.xml | 81 +++ .../main/res/layout/fragment_qc_terry_two.xml | 66 +++ app/src/main/res/layout/fragment_reports.xml | 38 ++ app/src/main/res/layout/item_image.xml | 13 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 6 + app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes app/src/main/res/navigation/nav_graph.xml | 34 ++ app/src/main/res/raw/application.properties | 1 + app/src/main/res/values-hdpi/meta.xml | 5 + app/src/main/res/values-hdpi/sdp.xml | 303 +++++++++++ app/src/main/res/values-land/dimens.xml | 3 + app/src/main/res/values-mdpi/meta.xml | 5 + app/src/main/res/values-mdpi/sdp.xml | 303 +++++++++++ app/src/main/res/values-night/themes.xml | 19 + app/src/main/res/values-sw300dp/meta.xml | 5 + app/src/main/res/values-sw300dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw330dp/meta.xml | 5 + app/src/main/res/values-sw330dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw360dp/meta.xml | 5 + app/src/main/res/values-sw360dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw390dp/meta.xml | 5 + app/src/main/res/values-sw390dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw420dp/meta.xml | 5 + app/src/main/res/values-sw420dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw450dp/meta.xml | 5 + app/src/main/res/values-sw450dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw480dp/meta.xml | 5 + app/src/main/res/values-sw480dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw510dp/meta.xml | 5 + app/src/main/res/values-sw510dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw540dp/meta.xml | 5 + app/src/main/res/values-sw540dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw570dp/meta.xml | 5 + app/src/main/res/values-sw570dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw600dp/meta.xml | 5 + app/src/main/res/values-sw600dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw630dp/meta.xml | 5 + app/src/main/res/values-sw630dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw660dp/meta.xml | 5 + app/src/main/res/values-sw660dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw690dp/meta.xml | 5 + app/src/main/res/values-sw690dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw720dp/meta.xml | 5 + app/src/main/res/values-sw720dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw750dp/meta.xml | 5 + app/src/main/res/values-sw750dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-sw780dp/meta.xml | 5 + app/src/main/res/values-sw780dp/sdp.xml | 303 +++++++++++ app/src/main/res/values-w1240dp/dimens.xml | 3 + app/src/main/res/values-w600dp/dimens.xml | 3 + app/src/main/res/values-xhdpi/meta.xml | 2 + app/src/main/res/values-xhdpi/sdp.xml | 303 +++++++++++ app/src/main/res/values-xlarge/meta.xml | 5 + app/src/main/res/values-xlarge/sdp.xml | 303 +++++++++++ app/src/main/res/values-xxhdpi/meta.xml | 2 + app/src/main/res/values-xxhdpi/sdp.xml | 304 +++++++++++ app/src/main/res/values-xxxhdpi/meta.xml | 2 + app/src/main/res/values-xxxhdpi/sdp.xml | 304 +++++++++++ app/src/main/res/values/colors.xml | 384 +++++++++++++ app/src/main/res/values/dimens.xml | 3 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/values/themes.xml | 21 + app/src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + app/src/main/res/xml/file_paths.xml | 4 + .../main/res/xml/network_security_config.xml | 9 + .../qc_android/ExampleUnitTest.java | 17 + build.gradle | 4 + gradle.properties | 21 + gradle/libs.versions.toml | 34 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 185 +++++++ gradlew.bat | 89 +++ settings.gradle | 27 + 199 files changed, 14538 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/compiler.xml create mode 100644 .idea/deploymentTargetSelector.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/migrations.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/androidTest/java/com/utopiaindustries/qc_android/ExampleInstrumentedTest.java create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/activities/DashboardActivity.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/activities/LoginActivity.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/activities/QCTerryActivity.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/adapters/CheckPointAdapter.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/adapters/ImageAdapter.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/adapters/PagerAdapter.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/apiservice/ApiService.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/apiservice/ApiServiceFactory.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/apiservice/GiteaClient.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/apiservice/GiteaRestService.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/apiservice/RetrofitClient.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/apiservice/SSLCheckHttpClient.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/db/CheckpointRepository.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/db/DatabaseHelper.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/fragments/DraftFragment.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/fragments/FragmentQcTerryOne.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/fragments/FragmentQcTerryThree.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/fragments/FragmentQcTerryTwo.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/fragments/ReportsFragment.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/helper/DateTimeUtils.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/helper/Helper.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/helper/Preference.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/helper/PropertyReader.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/helper/Release.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/helper/ReleaseAsset.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/models/InspectionCheckPoint.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/utils/DatabaseExecutor.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/utils/FileUtils.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/utils/FullScreenImageDialog.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/utils/NonSwipeableViewPager.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/utils/ProgressDialogFragment.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/utils/SelectListener.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/utils/jackson/LocalDateTimeDeserializer.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/utils/jackson/LocalDateTimeSerializer.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/utils/jackson/ZonedDateTimeDeserializer.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/viewmodels/DataEntryViewModel.java create mode 100644 app/src/main/java/com/utopiaindustries/qc_android/viewmodels/ServiceViewModel.java create mode 100644 app/src/main/res/anim/bs_list_layout_fade_in.xml create mode 100644 app/src/main/res/anim/fade_in.xml create mode 100644 app/src/main/res/anim/fade_out.xml create mode 100644 app/src/main/res/anim/grow_from_bottom.xml create mode 100644 app/src/main/res/anim/grow_from_top.xml create mode 100644 app/src/main/res/anim/popup_hide.xml create mode 100644 app/src/main/res/anim/popup_show.xml create mode 100644 app/src/main/res/anim/slide_down.xml create mode 100644 app/src/main/res/anim/slide_in_bottom.xml create mode 100644 app/src/main/res/anim/slide_in_left.xml create mode 100644 app/src/main/res/anim/slide_in_left_menu.xml create mode 100644 app/src/main/res/anim/slide_in_right.xml create mode 100644 app/src/main/res/anim/slide_in_right_menu.xml create mode 100644 app/src/main/res/anim/slide_in_top.xml create mode 100644 app/src/main/res/anim/slide_out_bottom.xml create mode 100644 app/src/main/res/anim/slide_out_left.xml create mode 100644 app/src/main/res/anim/slide_out_right.xml create mode 100644 app/src/main/res/anim/slide_out_top.xml create mode 100644 app/src/main/res/anim/slide_up.xml create mode 100644 app/src/main/res/color/tab_text_selector.xml create mode 100644 app/src/main/res/drawable/arrow_back.xml create mode 100644 app/src/main/res/drawable/circle_background.xml create mode 100644 app/src/main/res/drawable/custom_button.xml create mode 100644 app/src/main/res/drawable/hse_login.jpg create mode 100644 app/src/main/res/drawable/ic_add.xml create mode 100644 app/src/main/res/drawable/ic_close.xml create mode 100644 app/src/main/res/drawable/ic_delete.xml create mode 100644 app/src/main/res/drawable/ic_email.xml create mode 100644 app/src/main/res/drawable/ic_launcher.webp create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 app/src/main/res/drawable/ic_password.xml create mode 100644 app/src/main/res/drawable/icon_update.xml create mode 100644 app/src/main/res/drawable/image_picker.xml create mode 100644 app/src/main/res/drawable/img_load.xml create mode 100644 app/src/main/res/drawable/qc.png create mode 100644 app/src/main/res/drawable/rounded_btn_login.xml create mode 100644 app/src/main/res/drawable/rounded_white.xml create mode 100644 app/src/main/res/drawable/tab_background_selector.xml create mode 100644 app/src/main/res/layout-sw800dp/activity_dashboard.xml create mode 100644 app/src/main/res/layout-sw800dp/activity_login.xml create mode 100644 app/src/main/res/layout-sw800dp/activity_qcterry.xml create mode 100644 app/src/main/res/layout-sw800dp/check_point.xml create mode 100644 app/src/main/res/layout-sw800dp/custom_layout_for_image.xml create mode 100644 app/src/main/res/layout-sw800dp/custom_tab.xml create mode 100644 app/src/main/res/layout-sw800dp/dialog_fullscreen_image.xml create mode 100644 app/src/main/res/layout-sw800dp/dialog_progress.xml create mode 100644 app/src/main/res/layout-sw800dp/fragment_draft.xml create mode 100644 app/src/main/res/layout-sw800dp/fragment_qc_terry_one.xml create mode 100644 app/src/main/res/layout-sw800dp/fragment_qc_terry_three.xml create mode 100644 app/src/main/res/layout-sw800dp/fragment_qc_terry_two.xml create mode 100644 app/src/main/res/layout-sw800dp/fragment_reports.xml create mode 100644 app/src/main/res/layout-sw800dp/item_image.xml create mode 100644 app/src/main/res/layout/activity_dashboard.xml create mode 100644 app/src/main/res/layout/activity_login.xml create mode 100644 app/src/main/res/layout/activity_qcterry.xml create mode 100644 app/src/main/res/layout/check_point.xml create mode 100644 app/src/main/res/layout/custom_layout_for_image.xml create mode 100644 app/src/main/res/layout/custom_tab.xml create mode 100644 app/src/main/res/layout/dialog_fullscreen_image.xml create mode 100644 app/src/main/res/layout/dialog_progress.xml create mode 100644 app/src/main/res/layout/fragment_draft.xml create mode 100644 app/src/main/res/layout/fragment_qc_terry_one.xml create mode 100644 app/src/main/res/layout/fragment_qc_terry_three.xml create mode 100644 app/src/main/res/layout/fragment_qc_terry_two.xml create mode 100644 app/src/main/res/layout/fragment_reports.xml create mode 100644 app/src/main/res/layout/item_image.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/navigation/nav_graph.xml create mode 100644 app/src/main/res/raw/application.properties create mode 100644 app/src/main/res/values-hdpi/meta.xml create mode 100644 app/src/main/res/values-hdpi/sdp.xml create mode 100644 app/src/main/res/values-land/dimens.xml create mode 100644 app/src/main/res/values-mdpi/meta.xml create mode 100644 app/src/main/res/values-mdpi/sdp.xml create mode 100644 app/src/main/res/values-night/themes.xml create mode 100644 app/src/main/res/values-sw300dp/meta.xml create mode 100644 app/src/main/res/values-sw300dp/sdp.xml create mode 100644 app/src/main/res/values-sw330dp/meta.xml create mode 100644 app/src/main/res/values-sw330dp/sdp.xml create mode 100644 app/src/main/res/values-sw360dp/meta.xml create mode 100644 app/src/main/res/values-sw360dp/sdp.xml create mode 100644 app/src/main/res/values-sw390dp/meta.xml create mode 100644 app/src/main/res/values-sw390dp/sdp.xml create mode 100644 app/src/main/res/values-sw420dp/meta.xml create mode 100644 app/src/main/res/values-sw420dp/sdp.xml create mode 100644 app/src/main/res/values-sw450dp/meta.xml create mode 100644 app/src/main/res/values-sw450dp/sdp.xml create mode 100644 app/src/main/res/values-sw480dp/meta.xml create mode 100644 app/src/main/res/values-sw480dp/sdp.xml create mode 100644 app/src/main/res/values-sw510dp/meta.xml create mode 100644 app/src/main/res/values-sw510dp/sdp.xml create mode 100644 app/src/main/res/values-sw540dp/meta.xml create mode 100644 app/src/main/res/values-sw540dp/sdp.xml create mode 100644 app/src/main/res/values-sw570dp/meta.xml create mode 100644 app/src/main/res/values-sw570dp/sdp.xml create mode 100644 app/src/main/res/values-sw600dp/meta.xml create mode 100644 app/src/main/res/values-sw600dp/sdp.xml create mode 100644 app/src/main/res/values-sw630dp/meta.xml create mode 100644 app/src/main/res/values-sw630dp/sdp.xml create mode 100644 app/src/main/res/values-sw660dp/meta.xml create mode 100644 app/src/main/res/values-sw660dp/sdp.xml create mode 100644 app/src/main/res/values-sw690dp/meta.xml create mode 100644 app/src/main/res/values-sw690dp/sdp.xml create mode 100644 app/src/main/res/values-sw720dp/meta.xml create mode 100644 app/src/main/res/values-sw720dp/sdp.xml create mode 100644 app/src/main/res/values-sw750dp/meta.xml create mode 100644 app/src/main/res/values-sw750dp/sdp.xml create mode 100644 app/src/main/res/values-sw780dp/meta.xml create mode 100644 app/src/main/res/values-sw780dp/sdp.xml create mode 100644 app/src/main/res/values-w1240dp/dimens.xml create mode 100644 app/src/main/res/values-w600dp/dimens.xml create mode 100644 app/src/main/res/values-xhdpi/meta.xml create mode 100644 app/src/main/res/values-xhdpi/sdp.xml create mode 100644 app/src/main/res/values-xlarge/meta.xml create mode 100644 app/src/main/res/values-xlarge/sdp.xml create mode 100644 app/src/main/res/values-xxhdpi/meta.xml create mode 100644 app/src/main/res/values-xxhdpi/sdp.xml create mode 100644 app/src/main/res/values-xxxhdpi/meta.xml create mode 100644 app/src/main/res/values-xxxhdpi/sdp.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/dimens.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 app/src/main/res/xml/file_paths.xml create mode 100644 app/src/main/res/xml/network_security_config.xml create mode 100644 app/src/test/java/com/utopiaindustries/qc_android/ExampleUnitTest.java create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/libs.versions.toml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..896ea19 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +QC-Android-App \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..0897082 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..8978d23 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..70acd67 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,7 @@ +*.iml +.gradle +/local.properties +/.idea +/build +/captures +.externalNativeBuild diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..49eb671 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,57 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'com.utopiaindustries.qc_android' + compileSdk 34 + + defaultConfig { + applicationId "com.utopiaindustries.qc_android" + minSdk 24 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation libs.activity + implementation libs.constraintlayout + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core + + implementation libs.retrofit + implementation libs.converter.gson + implementation libs.glide + implementation libs.easypermissions + implementation libs.preference + + implementation 'com.github.MikeOrtiz:TouchImageView:3.6' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.0.1' + implementation 'com.squareup.retrofit2:converter-jackson:2.9.0' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3' + + implementation 'androidx.navigation:navigation-fragment:2.4.0' + implementation 'androidx.navigation:navigation-ui:2.4.0' + + implementation(libs.room.runtime) + annotationProcessor(libs.room.compiler) +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/utopiaindustries/qc_android/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/utopiaindustries/qc_android/ExampleInstrumentedTest.java new file mode 100644 index 0000000..30bba0d --- /dev/null +++ b/app/src/androidTest/java/com/utopiaindustries/qc_android/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.utopiaindustries.qc_android; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.utopiaindustries.qc_android", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7aafaf6 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/utopiaindustries/qc_android/activities/DashboardActivity.java b/app/src/main/java/com/utopiaindustries/qc_android/activities/DashboardActivity.java new file mode 100644 index 0000000..a444f9b --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/activities/DashboardActivity.java @@ -0,0 +1,175 @@ +package com.utopiaindustries.qc_android.activities; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Bundle; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Window; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.Toast; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.core.view.WindowInsetsControllerCompat; +import androidx.lifecycle.ViewModelProvider; +import androidx.viewpager.widget.ViewPager; + +import com.utopiaindustries.qc_android.R; +import com.utopiaindustries.qc_android.adapters.PagerAdapter; + + +import com.google.android.material.tabs.TabLayout; +import com.utopiaindustries.qc_android.helper.Helper; +import com.utopiaindustries.qc_android.utils.ProgressDialogFragment; +import com.utopiaindustries.qc_android.viewmodels.ServiceViewModel; + +public class DashboardActivity extends AppCompatActivity { + + Button btnCreateReport, btnFetchProduct, btnLogout, btnAppUpdate, btnReportUpload; + ServiceViewModel serviceViewModel; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_dashboard); + + setStatusBarColor(); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + + if (!Helper.isNetworkConnected(this)) { + Toast.makeText(this, "No Internet Connection", Toast.LENGTH_LONG).show(); + } + + initializeLayout(); + + btnCreateReport.setOnClickListener(v -> { + Intent intent = new Intent(this, QCTerryActivity.class); + startActivity(intent); + }); + btnFetchProduct.setOnClickListener(v -> { + + if (Helper.isNetworkConnected(this)) { + + } + }); + btnLogout.setOnClickListener(v -> {}); + btnAppUpdate.setOnClickListener(v -> {}); + btnReportUpload.setOnClickListener(v -> {}); + + + + } + + public void initializeLayout() { + btnCreateReport = findViewById(R.id.btn_create_report); + btnFetchProduct = findViewById(R.id.btn_fetch_product); + btnLogout = findViewById(R.id.btn_logout); + btnAppUpdate = findViewById(R.id.btn_app_update); + btnReportUpload = findViewById(R.id.btn_reports_upload); + + serviceViewModel = new ViewModelProvider(this).get(ServiceViewModel.class); + + serviceViewModel.getLoadingState().observe(this, isLoading -> { + if (isLoading != null && isLoading) { + showProgressDialog(); + } else { + dismissProgressDialog(); + } + }); + + serviceViewModel.getErrorMessage().observe(this, errorResponse -> { + if (errorResponse.isEmpty()) { + Toast.makeText(this, "Something went wrong", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(this, errorResponse, Toast.LENGTH_SHORT).show(); + } + + }); + + serviceViewModel.getCheckpointsList().observe(this, checkpointsList -> { + if (!checkpointsList.isEmpty()) { + + } else { + Toast.makeText(this, "List is empty", Toast.LENGTH_SHORT).show(); + } + }); + + + } + + private void getScreenSize() { + DisplayMetrics metrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getRealMetrics(metrics); + + int widthPixels = metrics.widthPixels; + int heightPixels = metrics.heightPixels; + + Log.e("Width: ",""+widthPixels); + Log.e("Height: ",""+heightPixels); + + float widthInches = widthPixels / metrics.xdpi; + float heightInches = heightPixels / metrics.ydpi; + + double diagonalInches = Math.sqrt( + Math.pow(widthInches, 2) + + Math.pow(heightInches, 2) + ); + + Log.e("Diagonal: ",""+diagonalInches); + + int sw = getResources().getConfiguration().smallestScreenWidthDp; + Log.e("SWDP", "Smallest width dp = " + sw); + } + + private void setStatusBarColor() { + Window window = getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + + window.setStatusBarColor(ContextCompat.getColor(this, R.color.theme_color)); + + WindowInsetsControllerCompat controller = + WindowCompat.getInsetsController(window, window.getDecorView()); + controller.setAppearanceLightStatusBars(false); + } + + public void setAppVersion() { + PackageManager packageManager = getPackageManager(); + try { + PackageInfo info = packageManager.getPackageInfo( getPackageName(), 0 ); + //txtVersion.setText(String.format("Version: %s", info.versionName)); + + } catch ( Exception e ) { + e.printStackTrace(); + } + } + + public void showProgressDialog() { + ProgressDialogFragment progressDialog = new ProgressDialogFragment(); + progressDialog.setCancelable(false); + progressDialog.show(getSupportFragmentManager(), "progressDialog"); + } + + public void dismissProgressDialog() { + ProgressDialogFragment progressDialog = (ProgressDialogFragment) getSupportFragmentManager().findFragmentByTag("progressDialog"); + if (progressDialog != null) { + progressDialog.dismiss(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/utopiaindustries/qc_android/activities/LoginActivity.java b/app/src/main/java/com/utopiaindustries/qc_android/activities/LoginActivity.java new file mode 100644 index 0000000..02bb919 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/activities/LoginActivity.java @@ -0,0 +1,254 @@ +package com.utopiaindustries.qc_android.activities; + +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.lifecycle.ViewModelProvider; + +import com.utopiaindustries.qc_android.R; +import com.utopiaindustries.qc_android.apiservice.ApiService; +import com.utopiaindustries.qc_android.apiservice.ApiServiceFactory; +import com.utopiaindustries.qc_android.apiservice.GiteaClient; +import com.utopiaindustries.qc_android.apiservice.GiteaRestService; +import com.utopiaindustries.qc_android.helper.Helper; +import com.utopiaindustries.qc_android.helper.Preference; +import com.utopiaindustries.qc_android.helper.PropertyReader; +import com.utopiaindustries.qc_android.helper.Release; +import com.utopiaindustries.qc_android.utils.ProgressDialogFragment; +import com.utopiaindustries.qc_android.viewmodels.ServiceViewModel; + +import java.util.Properties; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class LoginActivity extends AppCompatActivity { + + EditText tfEmail, tfPassword; + Button btnLogin; + ServiceViewModel serviceViewModel; + ApiService apiService; + TextView txtVersion; + ImageView imgCheckUpdate; + private String currentVersionName; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_login); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + + /*ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets bars = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.ime()); + v.setPadding(bars.left, bars.top, bars.right, bars.bottom); + return insets; + });*/ + + if (!Helper.isNetworkConnected(this)) { + Toast.makeText(this, "No Internet Connection", Toast.LENGTH_LONG).show(); + } + + initializeLayout(); + + btnLogin.setOnClickListener(v -> { + Preference.setMyBooleanPref(Helper.project_file, "isLoggedIn", getApplicationContext(), true); + Preference.setMyStringPref(Helper.project_file,Helper.logInUser,this,tfEmail.getText().toString()); + + Intent intent = new Intent(this, DashboardActivity.class); + startActivity(intent); + overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); + finish(); + /*if (isValidate()) { + loginViewModel.isUserAuthenticated(tfEmail.getText().toString().trim(), + tfPassword.getText().toString(), + new String[]{"ROLE_UIM_HSE_APP_ACCESS_YES"}); + + *//*Preference.setMyBooleanPref(Helper.project_file, "isLoggedIn", getApplicationContext(), true); + Preference.setMyStringPref(Helper.project_file,Helper.logInUser,this,tfEmail.getText().toString()); + + Intent intent = new Intent(this, DashboardActivity.class); + startActivity(intent); + overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); + finish();*//* + }*/ + }); + + imgCheckUpdate.setOnClickListener(v -> { + if (!Helper.isNetworkConnected(this)) { + Toast.makeText(this, "No Internet Connection", Toast.LENGTH_LONG).show(); + } + else { + checkForUpdates(); + } + }); + } + + public void setAppVersion() { + PackageManager packageManager = getPackageManager(); + try { + PackageInfo info = packageManager.getPackageInfo( getPackageName(), 0 ); + txtVersion.setText(String.format("Version: %s", info.versionName)); + + String currentVersion = String.format("v-%s", String.valueOf(info.versionName)); + //StorageManager.getInstance().setAppVersion(currentVersion); + + } catch ( Exception e ) { + e.printStackTrace(); + } + } + + public void initializeLayout() { + tfEmail = findViewById(R.id.tf_email); + tfPassword = findViewById(R.id.tf_password); + btnLogin = findViewById(R.id.btn_login); + txtVersion = findViewById(R.id.txt_version); + imgCheckUpdate = findViewById(R.id.img_check_update); + apiService = ApiServiceFactory.getApiService(); + + serviceViewModel = new ViewModelProvider(this).get(ServiceViewModel.class); + + serviceViewModel.getLoadingState().observe(this, isLoading -> { + if (isLoading != null && isLoading) { + showProgressDialog(); + } else { + dismissProgressDialog(); + } + }); + + serviceViewModel.getErrorMessage().observe(this, errorResponse -> { + if (errorResponse.isEmpty()) { + Toast.makeText(this, "Something went wrong", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(this, errorResponse, Toast.LENGTH_SHORT).show(); + } + + }); + + serviceViewModel.getLoginUser().observe(this, loginUser -> { + if (loginUser) { + Preference.setMyBooleanPref(Helper.project_file, "isLoggedIn", getApplicationContext(), true); + Preference.setMyStringPref(Helper.project_file, Helper.logInUser, this, tfEmail.getText().toString()); + + Intent intent = new Intent(this, DashboardActivity.class); + startActivity(intent); + finish(); + overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); + } else { + Toast.makeText(this, "Login Failed", Toast.LENGTH_SHORT).show(); + } + }); + + setAppVersion(); + } + + public boolean isValidate() { + boolean returnValue = true; + String message = ""; + + if (tfPassword.getText().toString().isEmpty()) { + message = "Please enter password."; + returnValue = false; + } + + /*if (!Helper.isValidEmail(tfEmail.getText().toString())) { + message = "Please enter valid email."; + returnValue = false; + }*/ + + if (tfEmail.getText().toString().isEmpty()) { + message = "Please enter user name."; + returnValue = false; + } + + if (!returnValue) { + Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); + } + + return returnValue; + } + + public void showProgressDialog() { + ProgressDialogFragment progressDialog = new ProgressDialogFragment(); + progressDialog.setCancelable(false); + progressDialog.show(getSupportFragmentManager(), "progressDialog"); + } + + public void dismissProgressDialog() { + ProgressDialogFragment progressDialog = (ProgressDialogFragment) getSupportFragmentManager().findFragmentByTag("progressDialog"); + if (progressDialog != null) { + progressDialog.dismiss(); + } + } + + /** + * check for updates + */ + private void checkForUpdates() { + PackageManager packageManager = getPackageManager(); + try { + PackageInfo info = packageManager.getPackageInfo( getPackageName(), 0 ); + currentVersionName = info.versionName; + + } catch ( Exception e ) { + e.printStackTrace(); + } + // adjust views + showProgressDialog(); + // get properties + Properties properties = PropertyReader.getProperties( LoginActivity.this ); + String token = properties.getProperty( "gitea_access_token" ); + // send request + GiteaRestService restService = GiteaClient.getInstance().create( GiteaRestService.class ); + Call call = restService.getLatestRelease( token ); + call.enqueue( new Callback() { + @Override + public void onResponse( Call call, Response response ) { + if ( response.body() != null ) { + Release release = response.body(); + try { + float releaseVersion = Float.parseFloat( release.getName().replace( "v", "" ) ); + float currentVersion = Float.parseFloat( currentVersionName.replace( "v", "" ) ); + if ( releaseVersion > currentVersion && !Helper.isNullOrEmpty( release.getDownloadUrl() ) ) { + String msg = String.format( "Updating to version %s", release.getName() ); + Toast.makeText( LoginActivity.this, msg, Toast.LENGTH_LONG ).show(); + Intent intent = new Intent( Intent.ACTION_VIEW, Uri.parse( release.getDownloadUrl() ) ); + startActivity( intent ); + //startApkDownloadAndPerformUpdate( release.getDownloadUrl(), release.getName() ); + } else { + Toast.makeText( LoginActivity.this, "Latest version is already installed", Toast.LENGTH_LONG ).show(); + } + } catch ( Exception e ) { + Toast.makeText( LoginActivity.this, e.getMessage(), Toast.LENGTH_LONG ).show(); + e.printStackTrace(); + } + } + dismissProgressDialog(); + } + + @Override + public void onFailure(Call call, Throwable t ) { + dismissProgressDialog(); + t.printStackTrace(); + } + } ); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/utopiaindustries/qc_android/activities/QCTerryActivity.java b/app/src/main/java/com/utopiaindustries/qc_android/activities/QCTerryActivity.java new file mode 100644 index 0000000..bc58cd1 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/activities/QCTerryActivity.java @@ -0,0 +1,43 @@ +package com.utopiaindustries.qc_android.activities; + +import android.os.Bundle; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; + +import com.utopiaindustries.qc_android.R; + +public class QCTerryActivity extends AppCompatActivity { + + private NavController navController; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_qcterry); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + + initializeLayout(); + } + + private void initializeLayout() { + + // Setup Navigation + navController = Navigation.findNavController(this, R.id.nav_host_fragment); + } + + @Override + public boolean onSupportNavigateUp() { + return navController.navigateUp() || super.onSupportNavigateUp(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/utopiaindustries/qc_android/adapters/CheckPointAdapter.java b/app/src/main/java/com/utopiaindustries/qc_android/adapters/CheckPointAdapter.java new file mode 100644 index 0000000..c68b217 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/adapters/CheckPointAdapter.java @@ -0,0 +1,201 @@ +package com.utopiaindustries.qc_android.adapters; + +import android.content.Context; +import android.net.Uri; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.utopiaindustries.qc_android.R; +import com.utopiaindustries.qc_android.models.InspectionCheckPoint; + +import java.util.List; + +public class CheckPointAdapter extends RecyclerView.Adapter { + + private Context context; + private List checkPointList; + private OnCheckPointClickListener listener; + ImageAdapter imageAdapter; + + public interface OnCheckPointClickListener { + void onImagePickerClicked(int position); + void onImageClicked(int parentPosition, int imagePosition, Uri imageUri); + void onImageDeleteClicked(int parentPosition, int imagePosition); + void onDeleteAllImagesClicked(int position); + void onRemarksChanged(int position, String remarks); + void onCheckBoxChanged(int position, boolean isOkChecked, boolean isNoChecked); + } + + public CheckPointAdapter(Context context, List checkPointList, + OnCheckPointClickListener listener) { + this.context = context; + this.checkPointList = checkPointList; + this.listener = listener; + } + + @NonNull + @Override + public CheckPointViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from( parent.getContext() ).inflate(R.layout.check_point, parent, false); + return new CheckPointViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull CheckPointViewHolder holder, int position) { + InspectionCheckPoint item = checkPointList.get(position); + + // Set title + holder.tvTitle.setText(item.getTitle()); + + // Set remarks + holder.etRemarks.setText(item.getRemarks()); + + // Set checkboxes (mutually exclusive) + holder.cbOk.setChecked(item.isOkChecked()); + holder.cbNo.setChecked(item.isNoChecked()); + + // Setup nested RecyclerView for images + setupImagesRecyclerView(holder, position, item); + + // Setup listeners + setupListeners(holder, position); + } + + private void setupImagesRecyclerView(CheckPointViewHolder holder, int parentPosition, + InspectionCheckPoint item) { + // Create and setup images adapter + /*ImageAdapter imagesAdapter = new ImageAdapter(item.getImages(), + new ImageAdapter.OnImageClickListener() { + @Override + public void onImageClick(int imagePosition, Uri imageUri) { + if (listener != null) { + listener.onImageClicked(parentPosition, imagePosition, imageUri); + } + } + + @Override + public void onImageDelete(int imagePosition) { + if (listener != null) { + listener.onImageDeleteClicked(parentPosition, imagePosition); + } + } + });*/ + imageAdapter = new ImageAdapter(item.getImages(), context); + holder.imageRecyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)); + holder.imageRecyclerView.setAdapter(imageAdapter); + } + + private void setupListeners(CheckPointViewHolder holder, int position) { + // Remarks text change listener + holder.etRemarks.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + if (listener != null) { + listener.onRemarksChanged(position, s.toString()); + } + } + }); + + // OK checkbox listener + holder.cbOk.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + holder.cbNo.setChecked(false); + checkPointList.get(position).setOkChecked(true); + checkPointList.get(position).setNoChecked(false); + } else { + checkPointList.get(position).setOkChecked(false); + } + + if (listener != null) { + listener.onCheckBoxChanged(position, + checkPointList.get(position).isOkChecked(), + checkPointList.get(position).isNoChecked()); + } + } + }); + + // NO checkbox listener + holder.cbNo.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + holder.cbOk.setChecked(false); + checkPointList.get(position).setNoChecked(true); + checkPointList.get(position).setOkChecked(false); + } else { + checkPointList.get(position).setNoChecked(false); + } + + if (listener != null) { + listener.onCheckBoxChanged(position, + checkPointList.get(position).isOkChecked(), + checkPointList.get(position).isNoChecked()); + } + } + }); + + // Image picker button + holder.btnImagePicker.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) { + listener.onImagePickerClicked(position); + } + } + }); + + // Delete all images button + holder.btnDeleteAllImages.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) { + listener.onDeleteAllImagesClicked(position); + } + } + }); + } + + @Override + public int getItemCount() { + return checkPointList.size(); + } + + public static class CheckPointViewHolder extends RecyclerView.ViewHolder { + TextView tvTitle; + CheckBox cbOk, cbNo; + EditText etRemarks; + ImageButton btnImagePicker, btnDeleteAllImages; + RecyclerView imageRecyclerView; + + public CheckPointViewHolder(@NonNull View itemView) { + super(itemView); + tvTitle = itemView.findViewById(R.id.check_point_title); + cbOk = itemView.findViewById(R.id.check_point_ok); + cbNo = itemView.findViewById(R.id.check_point_no); + etRemarks = itemView.findViewById(R.id.check_point_remarks); + imageRecyclerView = itemView.findViewById(R.id.image_recycler_view); + btnImagePicker = itemView.findViewById(R.id.image_picker); + btnDeleteAllImages = itemView.findViewById(R.id.delete_image); + } + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/adapters/ImageAdapter.java b/app/src/main/java/com/utopiaindustries/qc_android/adapters/ImageAdapter.java new file mode 100644 index 0000000..41263d9 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/adapters/ImageAdapter.java @@ -0,0 +1,84 @@ +package com.utopiaindustries.qc_android.adapters; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.RecyclerView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import com.utopiaindustries.qc_android.R; +import com.utopiaindustries.qc_android.utils.FullScreenImageDialog; + +import java.util.List; + +public class ImageAdapter extends RecyclerView.Adapter { + + //private byte[] file; + //private List imageList; + List imageList; + private Context context; + + public ImageAdapter(List imageList, Context context) { + this.imageList = imageList; + this.context = context; + } + + @NonNull + @Override + public ImageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false); + return new ImageViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ImageViewHolder holder, int position) { + //holder.imageView.setImageBitmap(null); + holder.imageView.setImageResource(R.drawable.img_load); + //Bitmap bitmap = BitmapFactory.decodeByteArray( imageList.get(position), 0 , imageList.get(position).length ); + //holder.imageView.setImageBitmap( bitmap ); + + /*Glide.with(context) + .load(bitmap) // Load the Bitmap directly + .placeholder(R.drawable.img_load) + .apply(new RequestOptions().centerCrop()) // Optional: apply transformations like center crop + .into(holder.imageView);*/ + + Glide.with(context) + .load(imageList.get(position)) // Glide will handle the decoding and placeholder + .placeholder(R.drawable.img_load) + .apply(new RequestOptions().centerCrop()) + .into(holder.imageView); + + Bitmap bitmap = BitmapFactory.decodeByteArray(imageList.get(position), 0, imageList.get(position).length); + + holder.imageView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + FullScreenImageDialog dialog = new FullScreenImageDialog(bitmap); + dialog.show(((AppCompatActivity) v.getContext()).getSupportFragmentManager(), "FullScreenDialog"); + } + }); + } + + @Override + public int getItemCount() { + return (imageList != null) ? imageList.size() : 0; + } + + public class ImageViewHolder extends RecyclerView.ViewHolder { + ImageView imageView; + + public ImageViewHolder(@NonNull View itemView) { + super(itemView); + imageView = itemView.findViewById(R.id.imageView); + } + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/adapters/PagerAdapter.java b/app/src/main/java/com/utopiaindustries/qc_android/adapters/PagerAdapter.java new file mode 100644 index 0000000..16de0ae --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/adapters/PagerAdapter.java @@ -0,0 +1,62 @@ +package com.utopiaindustries.qc_android.adapters; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; + +import com.utopiaindustries.qc_android.R; +import com.utopiaindustries.qc_android.fragments.DraftFragment; +import com.utopiaindustries.qc_android.fragments.ReportsFragment; + +@SuppressWarnings("deprecation") +public class PagerAdapter extends FragmentStatePagerAdapter { + + String tabTitles[] = new String[]{"Submitted Reports", "Editable / Draft Reports"}; + Context context; + + public PagerAdapter(FragmentManager fm, Context context) { + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + this.context = context; + } + + @Override + public int getCount() { + return tabTitles.length; + } + + @NonNull + @Override + public Fragment getItem(int position) { + + switch (position) { + case 0: + return new ReportsFragment(); + case 1: + return new DraftFragment(); + } + return null; + } + + @Override + public CharSequence getPageTitle(int position) { + // Generate title based on item position + // return tabTitles[position]; + return null; + } + + public View getTabView(int position) { + View tab = LayoutInflater.from(context).inflate(R.layout.custom_tab, null); + + TextView tv = (TextView) tab.findViewById(R.id.txt_name); + tv.setText(tabTitles[position]); + // tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, context.getResources().getDimension(R.dimen.tab_text_size)); + + return tab; + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/apiservice/ApiService.java b/app/src/main/java/com/utopiaindustries/qc_android/apiservice/ApiService.java new file mode 100644 index 0000000..f232169 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/apiservice/ApiService.java @@ -0,0 +1,53 @@ +package com.utopiaindustries.qc_android.apiservice; + +import com.utopiaindustries.qc_android.models.InspectionCheckPoint; + +import java.util.List; + +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.Query; + +public interface ApiService { + + /*@GET("rest/uic/hse/get-hse-report-data") + Call getHseData();*/ + + /*@GET("rest/uic/hse/getHseReports") + Call getHseReportList( + @Query("username") String username + );*/ + + /*@GET("rest/uic/hse/get-hse-employee-by-id") + Call getEmployeeData( + @Query("employee-id") String empId + );*/ + + /*@GET("rest/uic/hse/get-daily-wages-contractor-data-by-visit-id") + Call getDailyWageData( + @Query("visit-id") String visitorId + );*/ + + /*@POST("rest/uic/hse/save-hse-report") + Call saveHseReport( + @Body HseReportRequest request + );*/ + + @POST("rest/authentication/authenticate-user") + Call isUserAuthenticated( + @Query("username") String username, + @Query("password") String password, + @Query("roles") String[] roles + ); + + /*@POST("rest/uic/hse/save-draft-count") + Call sendDraftCount( + @Query("username") String username, + @Query("count") String password + );*/ + + @GET("rest/uic/inspection-report/checkpoints") + Call> fetchCheckPoints(); +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/apiservice/ApiServiceFactory.java b/app/src/main/java/com/utopiaindustries/qc_android/apiservice/ApiServiceFactory.java new file mode 100644 index 0000000..ba03d99 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/apiservice/ApiServiceFactory.java @@ -0,0 +1,17 @@ +package com.utopiaindustries.qc_android.apiservice; + +import retrofit2.Retrofit; + +public class ApiServiceFactory { + + private static ApiService apiService; + + public synchronized static ApiService getApiService() { + if ( apiService == null ) { + Retrofit retrofit = RetrofitClient.getClient(); + apiService = retrofit.create( ApiService.class ); + } + return apiService; + } + +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/apiservice/GiteaClient.java b/app/src/main/java/com/utopiaindustries/qc_android/apiservice/GiteaClient.java new file mode 100644 index 0000000..5950736 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/apiservice/GiteaClient.java @@ -0,0 +1,27 @@ +package com.utopiaindustries.qc_android.apiservice; + +import retrofit2.Retrofit; +import retrofit2.converter.jackson.JacksonConverterFactory; + +public class GiteaClient { + private static Retrofit retrofit; + private static final String BASE_URL = "https://git.utopiadeals.com/api/v1/repos/UIND/HSE-Android-Releases/"; + + /** + * get retrofit instance + */ + public static Retrofit getInstance() { + if ( retrofit == null ) { + try { + retrofit = new Retrofit.Builder() + .baseUrl( BASE_URL ) + .addConverterFactory( JacksonConverterFactory.create() ) + .client( SSLCheckHttpClient.getOkHttpClient() ) + .build(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return retrofit; + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/apiservice/GiteaRestService.java b/app/src/main/java/com/utopiaindustries/qc_android/apiservice/GiteaRestService.java new file mode 100644 index 0000000..958d1b6 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/apiservice/GiteaRestService.java @@ -0,0 +1,16 @@ +package com.utopiaindustries.qc_android.apiservice; + +import com.utopiaindustries.qc_android.helper.Release; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Query; + +public interface GiteaRestService { + + /** + * get latest release + */ + @GET( "releases/latest" ) + Call getLatestRelease(@Query( "access_token" ) String accessToken ); +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/apiservice/RetrofitClient.java b/app/src/main/java/com/utopiaindustries/qc_android/apiservice/RetrofitClient.java new file mode 100644 index 0000000..ba57a4f --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/apiservice/RetrofitClient.java @@ -0,0 +1,49 @@ +package com.utopiaindustries.qc_android.apiservice; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.concurrent.TimeUnit; + +import okhttp3.OkHttpClient; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +public class RetrofitClient { + + //Live url + //private final static String BASE_URL = "https://portal.utopiaindustries.pk/uind/"; + + //Test Url + private final static String BASE_URL = "http://192.168.90.22:8081/uind/"; + + private static Retrofit retrofit; + + public synchronized static Retrofit getClient() { + + Gson gson = new GsonBuilder() + //.registerTypeAdapter(Boolean.class, new BooleanTypeAdapter()) + .create(); + + + if (retrofit == null) { + try { + + OkHttpClient okHttpClient = new OkHttpClient.Builder() + .connectTimeout(60, TimeUnit.SECONDS) + .readTimeout(60, TimeUnit.SECONDS) + .writeTimeout(60, TimeUnit.SECONDS) + .build(); + + retrofit = new Retrofit.Builder() + .baseUrl(BASE_URL) + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return retrofit; + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/apiservice/SSLCheckHttpClient.java b/app/src/main/java/com/utopiaindustries/qc_android/apiservice/SSLCheckHttpClient.java new file mode 100644 index 0000000..4708a4c --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/apiservice/SSLCheckHttpClient.java @@ -0,0 +1,52 @@ +package com.utopiaindustries.qc_android.apiservice; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import okhttp3.OkHttpClient; + +public class SSLCheckHttpClient { + public static OkHttpClient getOkHttpClient() throws NoSuchAlgorithmException, KeyManagementException { + // Create a trust manager that does not validate certificate chains + final TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } + }; + + // Install the all-trusting trust manager + final SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + + // Create an ssl socket factory with our all-trusting manager + final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + + return new OkHttpClient.Builder() + .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]) + .hostnameVerifier((hostname, session) -> true) + .connectTimeout(60, TimeUnit.SECONDS) + .readTimeout(60, TimeUnit.SECONDS) + .writeTimeout(60, TimeUnit.SECONDS) + .build(); + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/db/CheckpointRepository.java b/app/src/main/java/com/utopiaindustries/qc_android/db/CheckpointRepository.java new file mode 100644 index 0000000..de66288 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/db/CheckpointRepository.java @@ -0,0 +1,83 @@ +package com.utopiaindustries.qc_android.db; + +import android.annotation.SuppressLint; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import com.utopiaindustries.qc_android.models.InspectionCheckPoint; +import com.utopiaindustries.qc_android.utils.DatabaseExecutor; + +import java.util.ArrayList; +import java.util.List; + +public class CheckpointRepository { + + private final DatabaseHelper dbHelper; + private final SQLiteDatabase database; + //private final ExecutorService executorService; + + public CheckpointRepository(Context context ) { + dbHelper = new DatabaseHelper ( context ); + database = dbHelper.getWritableDatabase(); + //executorService = Executors.newSingleThreadExecutor(); + } + + /* + * Insert list of checkpoints in batch + * */ + public void insert( List checkPoints ) { + DatabaseExecutor.getExecutor().execute(() -> { + database.beginTransaction(); + try { + for ( InspectionCheckPoint checkPoint : checkPoints ) { + ContentValues values = new ContentValues(); + values.put(DatabaseHelper.CHECKPOINT_COLUMN_ID, checkPoint.getId() ); + values.put(DatabaseHelper.CHECKPOINT_COLUMN_TITLE, checkPoint.getTitle() ); + values.put(DatabaseHelper.CHECKPOINT_COLUMN_CATEGORY, checkPoint.getCategory() ); + database.insertWithOnConflict(DatabaseHelper.CHECKPOINT_TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE); + } + database.setTransactionSuccessful(); + } finally { + database.endTransaction(); + close(); + } + }); + } + + + @SuppressLint("Range") + public List findAll( ) { + Cursor cursor = database.query( DatabaseHelper.CHECKPOINT_TABLE_NAME, + null, + null, + null, + null, + null, + null); + + List checkPoints = new ArrayList<>(); + if (cursor != null && cursor.moveToFirst()) { + do { + InspectionCheckPoint checkPoint = new InspectionCheckPoint(); + checkPoint.setId(cursor.getLong( cursor.getColumnIndex(DatabaseHelper.CHECKPOINT_COLUMN_ID ))); + checkPoint.setTitle(cursor.getString(cursor.getColumnIndex(DatabaseHelper.CHECKPOINT_COLUMN_TITLE) )); + checkPoint.setCategory(cursor.getString(cursor.getColumnIndex(DatabaseHelper.CHECKPOINT_COLUMN_CATEGORY ))); + checkPoints.add( checkPoint ); + } while ( cursor.moveToNext() ); + cursor.close(); + } + close(); + return checkPoints; + } + + /* + * Close the database + */ + public void close() { + dbHelper.close(); + //executorService.shutdown(); + } + +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/db/DatabaseHelper.java b/app/src/main/java/com/utopiaindustries/qc_android/db/DatabaseHelper.java new file mode 100644 index 0000000..62da543 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/db/DatabaseHelper.java @@ -0,0 +1,213 @@ +package com.utopiaindustries.qc_android.db; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +public class DatabaseHelper extends SQLiteOpenHelper { + + private static final int DATABASE_VERSION = 1; + + private static final String DATABASE_NAME = "inventory"; + // Table and columns + public static final String PRODUCT_TABLE_NAME = "product"; + public static final String PRODUCT_COLUMN_ASIN = "asin"; + public static final String PRODUCT_COLUMN_PARENT_ASIN = "parentAsin"; + public static final String PRODUCT_COLUMN_MARKETPLACE = "marketplace"; + public static final String PRODUCT_COLUMN_SKU = "sku"; + public static final String PRODUCT_COLUMN_TITLE = "title"; + public static final String PRODUCT_COLUMN_HS_CODE = "hsCode"; + public static final String PRODUCT_COLUMN_MODEL_NUMBER = "modelNumber"; + public static final String PRODUCT_COLUMN_ITEM_PER_BOX = "itemPerBox"; + public static final String PRODUCT_COLUMN_SM_COLOR = "smColor"; + public static final String PRODUCT_COLUMN_SM_SIZE = "smSize"; + public static final String PRODUCT_COLUMN_SM_ITEM_NAME = "smItemName"; + public static final String PRODUCT_COLUMN_ITEM_PER_PACK = "itemPerPack"; + public static final String PRODUCT_COLUMN_INVENTORY = "inventory"; + public static final String PRODUCT_COLUMN_CATEGORY = "category"; + public static final String PRODUCT_COLUMN_FNSKU = "fnsku"; + + //Table and Column for Inspection Label + public static final String INSPECTION_LABEL_TABLE_NAME = "inspection_label"; + public static final String INSPECTION_LABEL_COLUMN_ID = "id"; + public static final String INSPECTION_LABEL_COLUMN_MIN_LOT_SIZE = "minLotSize"; + public static final String INSPECTION_LABEL_COLUMN_MAX_LOT_SIZE = "maxLotSize"; + public static final String INSPECTION_LABEL_COLUMN_INSPECTION_LEVEL = "inspectionLevel"; + public static final String INSPECTION_LABEL_COLUMN_SAMPLE_CODE = "sampleCode"; + + //Table and Column for Quality Label + public static final String QUALITY_LABEL_TABLE_NAME = "quality_label"; + public static final String QUALITY_LABEL_COLUMN_ID = "id"; + public static final String QUALITY_LABEL_COLUMN_SAMPLE_CODE = "sampleCode"; + public static final String QUALITY_LABEL_COLUMN_SAMPLE_SIZE = "sampleSize"; + public static final String QUALITY_LABEL_COLUMN_QUALITY_LEVEL = "qualityLevel"; + public static final String QUALITY_LABEL_COLUMN_NO_ACCEPTED_DEFECTS = "noOfAcceptedDefects"; + + /* + * dimension table + */ + public static final String INSPECTION_DIMENSION_TABLE_NAME = "dimension"; + public static final String DIMENSION_COLUMN_ID = "id"; + public static final String DIMENSION_COLUMN_TITLE = "title"; + public static final String DIMENSION_COLUMN_CATEGORY = "category"; + + /* + * checkpoints table + * */ + public static final String CHECKPOINT_TABLE_NAME = "checkpoint"; + public static final String CHECKPOINT_COLUMN_ID = "id"; + public static final String CHECKPOINT_COLUMN_TITLE = "title"; + public static final String CHECKPOINT_COLUMN_CATEGORY = "category"; + + /* + * defect table + * */ + public static final String DEFECT_TABLE_NAME = "defect"; + public static final String DEFECT_COLUMN_DEFECT = "defect"; + public static final String DEFECT_COLUMN_CATEGORY = "category"; + + /* + * unit table + * */ + public static final String UNIT_TABLE_NAME = "unit"; + public static final String UNIT_COLUMN_ID = "id"; + public static final String UNIT_COLUMN_TITLE = "title"; + + /* + * report table + * */ + public static final String REPORT_TABLE_NAME = "report"; + public static final String REPORT_COLUMN_ID = "id"; + public static final String REPORT_COLUMN_CONTENT = "content"; + public static final String REPORT_COLUMN_SYNCED = "synced"; + + /* + * draft report table + * */ + public static final String DRAFT_REPORT_TABLE_NAME = "draft_report"; + public static final String DRAFT_REPORT_COLUMN_ID = "id"; + public static final String DRAFT_REPORT_COLUMN_CONTENT = "content"; + //public static final String DRAFT_REPORT_COLUMN_FNSKU = "fnsku"; + public static final String DRAFT_REPORT_COLUMN_SYNCED = "synced"; + + + /* + * sku checkpoints standard + * */ + public static final String CHECKPOINT_SKU_TABLE_NAME = "checkpoint_sku"; + public static final String CHECKPOINT_SKU_SKU = "sku"; + public static final String CHECKPOINT_SKU_MARKETPLACE = "marketplace"; + public static final String CHECKPOINT_SKU_CHECKPOINT_ID = "checkpoint_id"; + public static final String CHECKPOINT_SKU_STANDARD = "standard"; + + public DatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + // creating product table + String CREATE_PRODUCT_TABLE = "CREATE TABLE " + PRODUCT_TABLE_NAME + "(" + + PRODUCT_COLUMN_ASIN + " TEXT," + + PRODUCT_COLUMN_PARENT_ASIN + " TEXT," + + PRODUCT_COLUMN_MARKETPLACE + " TEXT," + + PRODUCT_COLUMN_SKU + " TEXT," + + PRODUCT_COLUMN_TITLE + " TEXT," + + PRODUCT_COLUMN_HS_CODE + " TEXT," + + PRODUCT_COLUMN_MODEL_NUMBER + " TEXT," + + PRODUCT_COLUMN_ITEM_PER_BOX + " INTEGER," + + PRODUCT_COLUMN_SM_COLOR + " TEXT," + + PRODUCT_COLUMN_SM_SIZE + " TEXT," + + PRODUCT_COLUMN_SM_ITEM_NAME + " TEXT," + + PRODUCT_COLUMN_ITEM_PER_PACK + " INTEGER," + + PRODUCT_COLUMN_INVENTORY + " INTEGER," + + PRODUCT_COLUMN_CATEGORY + " TEXT," + + PRODUCT_COLUMN_FNSKU + " TEXT," + + "PRIMARY KEY (" + PRODUCT_COLUMN_ASIN + ", " + PRODUCT_COLUMN_MARKETPLACE + ", " + PRODUCT_COLUMN_SKU + ")" + + ")"; + + + String CREATE_INSPECTION_DIMENSION_TABLE = "CREATE TABLE " + INSPECTION_DIMENSION_TABLE_NAME + " (" + + DIMENSION_COLUMN_ID + " INTEGER PRIMARY KEY," + + DIMENSION_COLUMN_TITLE + " TEXT NOT NULL," + + DIMENSION_COLUMN_CATEGORY + " TEXT NOT NULL);"; + + String CREATE_CHECKPOINT_TABLE = "CREATE TABLE " + CHECKPOINT_TABLE_NAME + " (" + + CHECKPOINT_COLUMN_ID + " INTEGER PRIMARY KEY," + + CHECKPOINT_COLUMN_TITLE + " TEXT NOT NULL," + + CHECKPOINT_COLUMN_CATEGORY + " TEXT NOT NULL);"; + + String CREATE_DEFECT_TABLE = "CREATE TABLE " + DEFECT_TABLE_NAME + " (" + + DEFECT_COLUMN_DEFECT + " TEXT NOT NULL," + + DEFECT_COLUMN_CATEGORY + " TEXT NOT NULL," + + "PRIMARY KEY (" + DEFECT_COLUMN_DEFECT + ", " + DEFECT_COLUMN_CATEGORY + ")" + + ")"; + + + String CREATE_UNIT_TABLE = "CREATE TABLE " + UNIT_TABLE_NAME + " (" + + UNIT_COLUMN_ID + " INTEGER PRIMARY KEY," + + UNIT_COLUMN_TITLE + " TEXT NOT NULL )"; + + String CREATE_REPORT_TABLE = "CREATE TABLE " + REPORT_TABLE_NAME + " (" + + REPORT_COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + REPORT_COLUMN_CONTENT + " TEXT NOT NULL," + + REPORT_COLUMN_SYNCED + " INTEGER DEFAULT 0 CHECK(synced IN (0, 1))" + + ")"; + + String CREATE_DRAFT_REPORT_TABLE = "CREATE TABLE " + DRAFT_REPORT_TABLE_NAME + " (" + + DRAFT_REPORT_COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + DRAFT_REPORT_COLUMN_CONTENT + " TEXT NOT NULL, " + + // DRAFT_REPORT_COLUMN_FNSKU + " TEXT NOT NULL," + + DRAFT_REPORT_COLUMN_SYNCED + " INTEGER DEFAULT 0 CHECK(synced IN (0, 1))" + + ")"; + + String CREATE_SKU_CHECKPOINT_TABLE = "CREATE TABLE " + CHECKPOINT_SKU_TABLE_NAME + " (" + + CHECKPOINT_SKU_SKU + " TEXT NOT NULL," + + CHECKPOINT_SKU_MARKETPLACE + " TEXT NOT NULL," + + CHECKPOINT_SKU_CHECKPOINT_ID + " INTEGER NOT NULL," + + CHECKPOINT_SKU_STANDARD + " TEXT," + + "PRIMARY KEY (" + CHECKPOINT_SKU_SKU + ", " + CHECKPOINT_SKU_MARKETPLACE + ", " + CHECKPOINT_SKU_CHECKPOINT_ID + "))"; + + String CREATE_INSPECTION_LABEL_TABLE = "CREATE TABLE " + INSPECTION_LABEL_TABLE_NAME + " (" + + INSPECTION_LABEL_COLUMN_ID + " INTEGER NOT NULL," + + INSPECTION_LABEL_COLUMN_MIN_LOT_SIZE + " INTEGER NOT NULL," + + INSPECTION_LABEL_COLUMN_MAX_LOT_SIZE + " INTEGER NOT NULL," + + INSPECTION_LABEL_COLUMN_INSPECTION_LEVEL + " INTEGER NOT NULL," + + INSPECTION_LABEL_COLUMN_SAMPLE_CODE + " TEXT NOT NULL )"; + + String CREATE_QUALITY_LABEL_TABLE = "CREATE TABLE " + QUALITY_LABEL_TABLE_NAME + " (" + + QUALITY_LABEL_COLUMN_ID + " INTEGER NOT NULL," + + QUALITY_LABEL_COLUMN_SAMPLE_CODE + " TEXT NOT NULL," + + QUALITY_LABEL_COLUMN_SAMPLE_SIZE + " INTEGER NOT NULL," + + QUALITY_LABEL_COLUMN_QUALITY_LEVEL + " REAL NOT NULL," + + QUALITY_LABEL_COLUMN_NO_ACCEPTED_DEFECTS + " INTEGER NOT NULL )"; + + + + db.execSQL( CREATE_PRODUCT_TABLE ); + db.execSQL( CREATE_INSPECTION_DIMENSION_TABLE ); + db.execSQL( CREATE_CHECKPOINT_TABLE ); + db.execSQL( CREATE_DEFECT_TABLE ); + db.execSQL( CREATE_UNIT_TABLE ); + db.execSQL( CREATE_REPORT_TABLE ); + db.execSQL( CREATE_DRAFT_REPORT_TABLE); + db.execSQL( CREATE_SKU_CHECKPOINT_TABLE ); + db.execSQL( CREATE_INSPECTION_LABEL_TABLE ); + db.execSQL( CREATE_QUALITY_LABEL_TABLE ); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + db.execSQL( "DROP TABLE IF EXISTS " + PRODUCT_TABLE_NAME); + db.execSQL( "DROP TABLE IF EXISTS " + INSPECTION_DIMENSION_TABLE_NAME ); + db.execSQL( "DROP TABLE IF EXISTS " + CHECKPOINT_TABLE_NAME ); + db.execSQL( "DROP TABLE IF EXISTS " + DEFECT_TABLE_NAME ); + db.execSQL( "DROP TABLE IF EXISTS " + UNIT_TABLE_NAME ); + db.execSQL( "DROP TABLE IF EXISTS " + REPORT_TABLE_NAME ); + db.execSQL( "DROP TABLE IF EXISTS " + DRAFT_REPORT_TABLE_NAME ); + db.execSQL( "DROP TABLE IF EXISTS " + CHECKPOINT_SKU_TABLE_NAME ); + db.execSQL( "DROP TABLE IF EXISTS " + INSPECTION_LABEL_TABLE_NAME ); + db.execSQL( "DROP TABLE IF EXISTS " + QUALITY_LABEL_TABLE_NAME ); + onCreate(db); + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/fragments/DraftFragment.java b/app/src/main/java/com/utopiaindustries/qc_android/fragments/DraftFragment.java new file mode 100644 index 0000000..54eb00c --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/fragments/DraftFragment.java @@ -0,0 +1,195 @@ +package com.utopiaindustries.qc_android.fragments; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.utopiaindustries.qc_android.R; +import com.utopiaindustries.qc_android.utils.SelectListener; + +import java.io.ByteArrayInputStream; +import java.io.ObjectInputStream; +import java.util.ArrayList; + +public class DraftFragment extends Fragment implements SelectListener { + + RecyclerView recyclerView; + TextView emptyReportTextView; + //private Store store; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + + View view = inflater.inflate(R.layout.fragment_draft , container, false ); + + initializeViews( view ); + + return view; + } + + private void initializeViews( View view ) { + recyclerView = view.findViewById( R.id.draft_recyclerview ); + emptyReportTextView = view.findViewById(R.id.empty_report_text); + + recyclerView.setLayoutManager( new LinearLayoutManager( getContext() ) ); + //store = Store.getInstance(); + + //refreshReportData(); + } + + @Override + public void onResume() { + super.onResume(); + + //refreshReportData(); + } + + /*public void refreshReportData() { + try { + ReportRepository repository = new ReportRepository(getContext()); + List reportWrappers = repository.findAllDraftReports(); + List reports = getReports(reportWrappers); + + // --- Filter expired reports --- + Iterator itWrapper = reportWrappers.iterator(); + Iterator itReport = reports.iterator(); + + while (itWrapper.hasNext() && itReport.hasNext()) { + InspectionReportWrapper wrapper = itWrapper.next(); + InspectionReport report = itReport.next(); + + if (report.getGeneratedAt() != null) { + if (DateTimeUtils.isOutside24Hours(report.getGeneratedAt())) { + Log.e("Outside-24-hours", "*****"); + // Delete from DB + repository.deleteDraftRecordById(wrapper.getId()); + + // Remove from both lists + itWrapper.remove(); + itReport.remove(); + } else { + Log.e("Inside-24-hours", "*****"); + } + } + } + + DraftReportAdapter adapter = new DraftReportAdapter(reportWrappers, this, getContext(), reports); + recyclerView.setAdapter(adapter); + + if (reportWrappers.isEmpty()) { + emptyReportTextView.setVisibility(View.VISIBLE); + } else { + emptyReportTextView.setVisibility(View.INVISIBLE); + } + } catch (Exception e) { + Log.e("Exception: ", "change"); + } + }*/ + + /*public void refreshReportDataWithSaturdayException() { + try { + ReportRepository repository = new ReportRepository(getContext()); + List reportWrappers = repository.findAllDraftReports(); + List reports = getReports(reportWrappers); + + Iterator itWrapper = reportWrappers.iterator(); + Iterator itReport = reports.iterator(); + + while (itWrapper.hasNext() && itReport.hasNext()) { + InspectionReportWrapper wrapper = itWrapper.next(); + InspectionReport report = itReport.next(); + + String generatedAtString = report.getGeneratedAt(); + if (generatedAtString != null && !generatedAtString.isEmpty()) { + + // Check if report should be deleted + boolean shouldDelete = DateTimeUtils.isReportExpired(generatedAtString); + + if (shouldDelete) { + Log.e("DELETE", "Deleting report: " + generatedAtString); + + // Delete from database + repository.deleteDraftRecordById(wrapper.getId()); + + // Remove from lists + itWrapper.remove(); + itReport.remove(); + } else { + Log.e("KEEP", "Keeping report: " + generatedAtString); + } + } + } + + // Update UI + DraftReportAdapter adapter = new DraftReportAdapter(reportWrappers, this, getContext(), reports); + recyclerView.setAdapter(adapter); + + emptyReportTextView.setVisibility(reportWrappers.isEmpty() ? View.VISIBLE : View.INVISIBLE); + + } catch (Exception e) { + Log.e("refreshReportData", "Error: ", e); + } + }*/ + + /*@Override + public void onItemClicked(InspectionReportWrapper reportWrapper) { + //Toast.makeText(getContext(), String.valueOf(reportWrapper.getId()), Toast.LENGTH_SHORT).show(); + + SharedPreferences sharedPreferences = getContext().getSharedPreferences("login_prefs", Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPreferences.edit(); + + editor.putString("draftReportId", String.valueOf(reportWrapper.getId())); + editor.apply(); + + Intent intent = new Intent( getContext(), MainActivity.class ); + intent.putExtra("fromClass", "Draft"); + intent.putExtra("draftID", String.valueOf(reportWrapper.getId())); + startActivity( intent ); + }*/ + + /*private List getReports(List wrappers ) { + List reports = new ArrayList<>(); + if( ! wrappers.isEmpty() ){ + for( InspectionReportWrapper wrapper : wrappers ){ + try { + // get file from path + byte[] result = FileUtils.readFile( wrapper.getContent() ); + *//*FileUtils.copyFile(this, wrapper.getContent(), "Reports"); + Object obj = FileUtils.readSerializedObject(wrapper.getContent()); + if (obj != null) { + FileUtils.writeObjectToReadableFile(this, obj, "report_readable.txt"); + }*//* + ByteArrayInputStream byteIn = new ByteArrayInputStream( result ); + ObjectInputStream in = new ObjectInputStream(byteIn); + InspectionReport report = ( InspectionReport ) in.readObject(); + report.setWrapperId( wrapper.getId() ); + report.setFilePath( wrapper.getContent() ); + reports.add( report ); + } catch (Exception | OutOfMemoryError e){ + e.printStackTrace(); + } + } + } + return reports; + }*/ + + +} \ No newline at end of file diff --git a/app/src/main/java/com/utopiaindustries/qc_android/fragments/FragmentQcTerryOne.java b/app/src/main/java/com/utopiaindustries/qc_android/fragments/FragmentQcTerryOne.java new file mode 100644 index 0000000..b96073b --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/fragments/FragmentQcTerryOne.java @@ -0,0 +1,293 @@ +package com.utopiaindustries.qc_android.fragments; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.MediaStore; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.Toast; + +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.core.content.FileProvider; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.Navigation; + +import com.utopiaindustries.qc_android.R; +import com.utopiaindustries.qc_android.viewmodels.DataEntryViewModel; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Map; + +public class FragmentQcTerryOne extends Fragment { + + private EditText edtFirstName, edtLastName, edtEmail; + private Button btnNext; + private DataEntryViewModel viewModel; + + private ActivityResultLauncher requestMultiplePermissionsLauncher; + // Activity Result Launchers + private ActivityResultLauncher cameraLauncher; + private ActivityResultLauncher galleryLauncher; + + private ImageView imageView; + private Button btnCamera, btnGallery; + private Uri photoUri; + + private static final int PENDING_CAMERA = 1; + private static final int PENDING_GALLERY = 2; + private int pendingAction = 0; + + + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + // Inflate the layout + View view = inflater.inflate(R.layout.fragment_qc_terry_one, container, false); + + // Initialize views + edtFirstName = view.findViewById(R.id.edtFirstName); + edtLastName = view.findViewById(R.id.edtLastName); + edtEmail = view.findViewById(R.id.edtEmail); + btnNext = view.findViewById(R.id.btnNext); + + imageView = view.findViewById(R.id.imageView); + btnCamera = view.findViewById(R.id.btnCamera); + btnGallery = view.findViewById(R.id.btnGallery); + + return view; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + // Initialize ViewModel + viewModel = new ViewModelProvider(requireActivity()).get(DataEntryViewModel.class); + + // Load existing data if any + if (viewModel.getFirstName().getValue() != null) { + edtFirstName.setText(viewModel.getFirstName().getValue()); + } + if (viewModel.getLastName().getValue() != null) { + edtLastName.setText(viewModel.getLastName().getValue()); + } + if (viewModel.getEmail().getValue() != null) { + edtEmail.setText(viewModel.getEmail().getValue()); + } + + if (viewModel.getSelectedImageUri().getValue() != null) { + photoUri = viewModel.getSelectedImageUri().getValue(); + imageView.setImageURI(photoUri); + } + + // Setup button click + btnNext.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Save data to ViewModel + viewModel.setFirstName(edtFirstName.getText().toString()); + viewModel.setLastName(edtLastName.getText().toString()); + viewModel.setEmail(edtEmail.getText().toString()); + + // Navigate to next screen + Navigation.findNavController(v) + .navigate(R.id.action_fragmentOne_to_fragmentTwo); + } + }); + + // Initialize Activity Result Launchers + initializeLaunchers(); + + btnCamera.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + checkPermissionAndOpenCamera(); + } + }); + + btnGallery.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + checkPermissionAndOpenGallery(); + } + }); + } + + private void initializeLaunchers() { + + requestMultiplePermissionsLauncher = registerForActivityResult( + new ActivityResultContracts.RequestMultiplePermissions(), + new ActivityResultCallback>() { + @Override + public void onActivityResult(Map result) { + boolean allGranted = true; + for (Boolean granted : result.values()) { + if (!granted) { + allGranted = false; + break; + } + } + + if (allGranted) { + // All permissions granted, proceed based on what was requested + if (pendingAction == PENDING_CAMERA) { + openCamera(); + } else if (pendingAction == PENDING_GALLERY) { + openGallery(); + } + } else { + // Some permissions denied + Toast.makeText(getContext(), "Permissions denied", Toast.LENGTH_SHORT).show(); + } + } + }); + // Camera launcher + cameraLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == getActivity().RESULT_OK && photoUri != null) { + imageView.setImageURI(photoUri); + viewModel.setSelectedImageUri(photoUri); + } + }); + + // Gallery launcher + galleryLauncher = registerForActivityResult( + new ActivityResultContracts.GetContent(), + uri -> { + if (uri != null) { + imageView.setImageURI(uri); + viewModel.setSelectedImageUri(uri); + } + }); + } + + /** + * Check permissions and open camera + */ + private void checkPermissionAndOpenCamera() { + String[] requiredPermissions = getRequiredCameraPermissions(); + + if (hasPermissions(requiredPermissions)) { + openCamera(); + } else { + pendingAction = PENDING_CAMERA; + requestMultiplePermissionsLauncher.launch(requiredPermissions); + } + } + + /** + * Check permissions and open gallery + */ + private void checkPermissionAndOpenGallery() { + String[] requiredPermissions = getRequiredGalleryPermissions(); + + if (hasPermissions(requiredPermissions)) { + openGallery(); + } else { + pendingAction = PENDING_GALLERY; + requestMultiplePermissionsLauncher.launch(requiredPermissions); + } + } + + /** + * Get required permissions for camera + */ + private String[] getRequiredCameraPermissions() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // Android 13+ - Camera only (storage permission not needed for camera) + return new String[]{Manifest.permission.CAMERA}; + } else { + // Android 12 and below - Camera + storage for saving photo + return new String[]{ + Manifest.permission.CAMERA, + Manifest.permission.WRITE_EXTERNAL_STORAGE + }; + } + } + + /** + * Get required permissions for gallery + */ + private String[] getRequiredGalleryPermissions() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // Android 13+ uses READ_MEDIA_IMAGES + return new String[]{Manifest.permission.READ_MEDIA_IMAGES}; + } else { + // Android 12 and below use READ_EXTERNAL_STORAGE + return new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}; + } + } + + /** + * Check if all permissions are granted + */ + private boolean hasPermissions(String[] permissions) { + for (String permission : permissions) { + if (ContextCompat.checkSelfPermission(requireContext(), permission) + != PackageManager.PERMISSION_GRANTED) { + return false; + } + } + return true; + } + + private void openCamera() { + try { + File photoFile = createImageFile(); + photoUri = FileProvider.getUriForFile( + requireContext(), + requireContext().getPackageName() + ".provider", + photoFile + ); + + Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); + cameraLauncher.launch(cameraIntent); + + } catch (IOException e) { + e.printStackTrace(); + Toast.makeText(getContext(), "Error creating file", Toast.LENGTH_SHORT).show(); + } + } + + private void openGallery() { + galleryLauncher.launch("image/*"); + } + + private File createImageFile() throws IOException { + // Create an image file name + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); + String imageFileName = "JPEG_" + timeStamp + "_"; + + File storageDir = requireContext().getExternalFilesDir(null); + File image = File.createTempFile( + imageFileName, /* prefix */ + ".jpg", /* suffix */ + storageDir /* directory */ + ); + + return image; + } + +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/fragments/FragmentQcTerryThree.java b/app/src/main/java/com/utopiaindustries/qc_android/fragments/FragmentQcTerryThree.java new file mode 100644 index 0000000..0e168dc --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/fragments/FragmentQcTerryThree.java @@ -0,0 +1,134 @@ +package com.utopiaindustries.qc_android.fragments; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; + +import com.utopiaindustries.qc_android.R; +import com.utopiaindustries.qc_android.viewmodels.DataEntryViewModel; + +public class FragmentQcTerryThree extends Fragment { + + private EditText edtCompany, edtPosition, edtSalary; + private Button btnSubmit; + private TextView tvSummary; + private DataEntryViewModel viewModel; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_qc_terry_three, container, false); + + edtCompany = view.findViewById(R.id.edtCompany); + edtPosition = view.findViewById(R.id.edtPosition); + edtSalary = view.findViewById(R.id.edtSalary); + btnSubmit = view.findViewById(R.id.btnSubmit); + tvSummary = view.findViewById(R.id.tvSummary); + + return view; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + viewModel = new ViewModelProvider(requireActivity()).get(DataEntryViewModel.class); + + // Load existing data + if (viewModel.getCompany().getValue() != null) { + edtCompany.setText(viewModel.getCompany().getValue()); + } + if (viewModel.getPosition().getValue() != null) { + edtPosition.setText(viewModel.getPosition().getValue()); + } + + // Display summary from previous screens + displaySummary(); + + btnSubmit.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Validate input + if (edtCompany.getText().toString().trim().isEmpty()) { + edtCompany.setError("Company is required"); + return; + } + + if (edtPosition.getText().toString().trim().isEmpty()) { + edtPosition.setError("Position is required"); + return; + } + + // Save final data + viewModel.setCompany(edtCompany.getText().toString()); + viewModel.setPosition(edtPosition.getText().toString()); + + // Submit data + submitData(); + } + }); + } + + private void displaySummary() { + StringBuilder summary = new StringBuilder(); + summary.append("Summary:\n\n"); + + if (viewModel.getFirstName().getValue() != null && + !viewModel.getFirstName().getValue().isEmpty()) { + summary.append("Name: ") + .append(viewModel.getFirstName().getValue()) + .append(" ") + .append(viewModel.getLastName().getValue()) + .append("\n"); + } + + if (viewModel.getEmail().getValue() != null && + !viewModel.getEmail().getValue().isEmpty()) { + summary.append("Email: ") + .append(viewModel.getEmail().getValue()) + .append("\n"); + } + + if (viewModel.getAddress().getValue() != null && + !viewModel.getAddress().getValue().isEmpty()) { + summary.append("Address: ") + .append(viewModel.getAddress().getValue()) + .append("\n"); + } + + if (viewModel.getCity().getValue() != null && + !viewModel.getCity().getValue().isEmpty()) { + summary.append("City: ") + .append(viewModel.getCity().getValue()) + .append("\n"); + } + + tvSummary.setText(summary.toString()); + } + + private void submitData() { + // Show success message + Toast.makeText(getActivity(), "Data submitted successfully!", Toast.LENGTH_SHORT).show(); + + // Update UI + btnSubmit.setText("Submitted ✓"); + btnSubmit.setEnabled(false); + btnSubmit.setBackgroundColor(getResources().getColor(android.R.color.darker_gray)); + + // You would typically: + // 1. Send data to API/backend + // 2. Save to database + // 3. Navigate to success screen + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/fragments/FragmentQcTerryTwo.java b/app/src/main/java/com/utopiaindustries/qc_android/fragments/FragmentQcTerryTwo.java new file mode 100644 index 0000000..79ec22c --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/fragments/FragmentQcTerryTwo.java @@ -0,0 +1,509 @@ +package com.utopiaindustries.qc_android.fragments; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.MediaStore; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; +import androidx.core.content.FileProvider; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.Navigation; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.utopiaindustries.qc_android.R; +import com.utopiaindustries.qc_android.adapters.CheckPointAdapter; +import com.utopiaindustries.qc_android.models.InspectionCheckPoint; +import com.utopiaindustries.qc_android.viewmodels.DataEntryViewModel; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.OnCheckPointClickListener{ + + private Button btnNext; + private DataEntryViewModel viewModel; + private CheckPointAdapter adapter; + private List checkPointList = new ArrayList<>(); + private RecyclerView recyclerView; + + private ActivityResultLauncher requestMultiplePermissionsLauncher; + // Activity Result Launchers + private ActivityResultLauncher cameraLauncher; + private ActivityResultLauncher galleryLauncher; + + private Uri photoUri; + + private static final int PENDING_CAMERA = 1; + private static final int PENDING_GALLERY = 2; + private int pendingAction = 0; + + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_qc_terry_two, container, false); + + initializeLayouts(view); + + return view; + } + + public void initializeLayouts(View view) { + recyclerView = view.findViewById(R.id.check_point_recycler_view); + btnNext = view.findViewById(R.id.btnNext); + + // Setup RecyclerView + recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); + adapter = new CheckPointAdapter(getActivity(), checkPointList, this); + recyclerView.setAdapter(adapter); + + //loadCheckPoints(); + } + + private void loadCheckPoints() { + checkPointList.clear(); + + // Item 1: Using 7-field constructor + List item1Images = createImages("Helmet", 2); + InspectionCheckPoint item1 = new InspectionCheckPoint( + 1, // id + "Safety Helmet Check", // title + "Safety", // category + true, // isOkChecked + false, // isNoChecked + "All workers wearing helmets properly", // remarks + item1Images // images + ); + checkPointList.add(item1); + + // Item 2: NO checked with single image + List item2Images = new ArrayList<>(); + item2Images.add(createSampleImageBytes("Fire_Extinguisher")); + InspectionCheckPoint item2 = new InspectionCheckPoint( + 2, + "Fire Extinguisher Check", + "Safety", + false, + true, + "Fire extinguisher expired, needs replacement", + item2Images + ); + checkPointList.add(item2); + + // Item 3: Quality check with multiple images + List item3Images = createImages("Material", 3); + InspectionCheckPoint item3 = new InspectionCheckPoint( + 3, + "Material Quality Inspection", + "Quality", + true, + false, + "Material meets all specifications", + item3Images + ); + checkPointList.add(item3); + + // Item 4: NO status with many images + List item4Images = createImages("Weld_Defect", 5); + InspectionCheckPoint item4 = new InspectionCheckPoint( + 4, + "Weld Quality Check", + "Quality", + false, + true, + "Poor weld quality, needs rework", + item4Images + ); + checkPointList.add(item4); + + // Item 5: Pending (no status selected), no images + InspectionCheckPoint item5 = new InspectionCheckPoint( + 5, + "Electrical Panel Inspection", + "Electrical", + false, + false, + "Awaiting electrician's review", + new ArrayList<>() // Empty images list + ); + checkPointList.add(item5); + + // Item 6: OK checked with one image + List item6Images = new ArrayList<>(); + item6Images.add(createSampleImageBytes("Wiring")); + InspectionCheckPoint item6 = new InspectionCheckPoint( + 6, + "Wiring Safety Check", + "Electrical", + true, + false, + "All wiring properly insulated and secured", + item6Images + ); + checkPointList.add(item6); + + // Item 8: NO checked, no remarks + List item8Images = createImages("Missing_Guard", 2); + InspectionCheckPoint item8 = new InspectionCheckPoint( + 8, + "Machine Guarding Check", + "Mechanical", + false, + true, + null, // No remarks + item8Images + ); + checkPointList.add(item8); + + // Item 9: OK with default empty images + InspectionCheckPoint item9 = new InspectionCheckPoint( + 9, + "Structural Integrity", + "Civil", + true, + false, + "Structure stable, no cracks observed", + null // Will be converted to empty list in constructor + ); + checkPointList.add(item9); + + // Add more items using loops + for (int i = 11; i <= 15; i++) { + String[] categories = {"Safety", "Quality", "Electrical", "General"}; + String category = categories[(i - 11) % categories.length]; + + List images = new ArrayList<>(); + int imageCount = (i % 3); // 0, 1, or 2 images + for (int j = 0; j < imageCount; j++) { + images.add(createSampleImageBytes("CP" + i + "_Img" + j)); + } + + boolean isOkChecked = (i % 2 == 0); + boolean isNoChecked = (i % 3 == 0); + + InspectionCheckPoint item = new InspectionCheckPoint( + i, + "Check Point " + i, + category, + isOkChecked, + isNoChecked, + "Remarks for check point " + i, + images + ); + checkPointList.add(item); + } + + adapter.notifyDataSetChanged(); + } + + private List createImages(String prefix, int count) { + List images = new ArrayList<>(); + for (int i = 1; i <= count; i++) { + images.add(createSampleImageBytes(prefix + "_" + i)); + } + return images; + } + + private byte[] createSampleImageBytes(String imageName) { + String imageData = "SAMPLE_IMAGE[" + imageName + "]_" + System.currentTimeMillis(); + return imageData.getBytes(); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + viewModel = new ViewModelProvider(requireActivity()).get(DataEntryViewModel.class); + + // Load existing data + /*if (viewModel.getAddress().getValue() != null) { + edtAddress.setText(viewModel.getAddress().getValue()); + } + if (viewModel.getCity().getValue() != null) { + edtCity.setText(viewModel.getCity().getValue()); + }*/ + + btnNext.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Save data + /*viewModel.setAddress(edtAddress.getText().toString()); + viewModel.setCity(edtCity.getText().toString());*/ + + // Navigate to next + Navigation.findNavController(v) + .navigate(R.id.action_fragmentTwo_to_fragmentThree); + } + }); + } + + @Override + public void onImagePickerClicked(int position) { + // Handle image picker click + //openImagePicker(position); + } + + @Override + public void onImageClicked(int parentPosition, int imagePosition, Uri imageUri) { + // Handle image click - maybe show in full screen + // showImageFullScreen(imageUri); + } + + @Override + public void onImageDeleteClicked(int parentPosition, int imagePosition) { + // Remove image from checkpoint + //adapter.removeImageFromCheckPoint(parentPosition, imagePosition); + } + + @Override + public void onDeleteAllImagesClicked(int position) { + + } + + @Override + public void onRemarksChanged(int position, String remarks) { + // Update remarks in the model + InspectionCheckPoint item = checkPointList.get(position); + item.setRemarks(remarks); + } + + @Override + public void onCheckBoxChanged(int position, boolean isOkChecked, boolean isNoChecked) { + // Handle checkbox changes + InspectionCheckPoint item = checkPointList.get(position); + item.setOkChecked(isOkChecked); + item.setNoChecked(isNoChecked); + } + + private void openImagePicker(int position) { + // Open camera or gallery + // After getting image URI: + // Uri imageUri = ...; + // adapter.addImageToCheckPoint(position, imageUri); + } + + public void alertForPictures(Context con) { + ViewGroup viewGroup = requireActivity().findViewById(android.R.id.content); + + TextView dialogCameraBtn, dialogGalleryBtn; + + AlertDialog.Builder builder = new AlertDialog.Builder(con); + View view1 = LayoutInflater.from(con).inflate(R.layout.custom_layout_for_image, viewGroup, false); + builder.setCancelable(true); + builder.setView(view1); + + dialogCameraBtn = view1.findViewById(R.id.dialog_camera_btn); + dialogGalleryBtn = view1.findViewById(R.id.dialog_gallery_btn); + + AlertDialog alertDialog = builder.create(); + alertDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + + dialogCameraBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + alertDialog.dismiss(); + checkPermissionAndOpenCamera(); + } + }); + + dialogGalleryBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + alertDialog.dismiss(); + checkPermissionAndOpenGallery(); + } + }); + + alertDialog.show(); + } + + private void initializeLaunchers() { + + requestMultiplePermissionsLauncher = registerForActivityResult( + new ActivityResultContracts.RequestMultiplePermissions(), + new ActivityResultCallback>() { + @Override + public void onActivityResult(Map result) { + boolean allGranted = true; + for (Boolean granted : result.values()) { + if (!granted) { + allGranted = false; + break; + } + } + + if (allGranted) { + // All permissions granted, proceed based on what was requested + if (pendingAction == PENDING_CAMERA) { + openCamera(); + } else if (pendingAction == PENDING_GALLERY) { + openGallery(); + } + } else { + // Some permissions denied + Toast.makeText(getContext(), "Permissions denied", Toast.LENGTH_SHORT).show(); + } + } + }); + // Camera launcher + cameraLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == getActivity().RESULT_OK && photoUri != null) { + //imageView.setImageURI(photoUri); + viewModel.setSelectedImageUri(photoUri); + } + }); + + // Gallery launcher + galleryLauncher = registerForActivityResult( + new ActivityResultContracts.GetContent(), + uri -> { + if (uri != null) { + //imageView.setImageURI(uri); + viewModel.setSelectedImageUri(uri); + } + }); + } + + /** + * Check permissions and open camera + */ + private void checkPermissionAndOpenCamera() { + String[] requiredPermissions = getRequiredCameraPermissions(); + + if (hasPermissions(requiredPermissions)) { + openCamera(); + } else { + pendingAction = PENDING_CAMERA; + requestMultiplePermissionsLauncher.launch(requiredPermissions); + } + } + + /** + * Check permissions and open gallery + */ + private void checkPermissionAndOpenGallery() { + String[] requiredPermissions = getRequiredGalleryPermissions(); + + if (hasPermissions(requiredPermissions)) { + openGallery(); + } else { + pendingAction = PENDING_GALLERY; + requestMultiplePermissionsLauncher.launch(requiredPermissions); + } + } + + /** + * Get required permissions for camera + */ + private String[] getRequiredCameraPermissions() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // Android 13+ - Camera only (storage permission not needed for camera) + return new String[]{Manifest.permission.CAMERA}; + } else { + // Android 12 and below - Camera + storage for saving photo + return new String[]{ + Manifest.permission.CAMERA, + Manifest.permission.WRITE_EXTERNAL_STORAGE + }; + } + } + + /** + * Get required permissions for gallery + */ + private String[] getRequiredGalleryPermissions() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // Android 13+ uses READ_MEDIA_IMAGES + return new String[]{Manifest.permission.READ_MEDIA_IMAGES}; + } else { + // Android 12 and below use READ_EXTERNAL_STORAGE + return new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}; + } + } + + /** + * Check if all permissions are granted + */ + private boolean hasPermissions(String[] permissions) { + for (String permission : permissions) { + if (ContextCompat.checkSelfPermission(requireContext(), permission) + != PackageManager.PERMISSION_GRANTED) { + return false; + } + } + return true; + } + + private void openCamera() { + try { + File photoFile = createImageFile(); + photoUri = FileProvider.getUriForFile( + requireContext(), + requireContext().getPackageName() + ".provider", + photoFile + ); + + Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); + cameraLauncher.launch(cameraIntent); + + } catch (IOException e) { + e.printStackTrace(); + Toast.makeText(getContext(), "Error creating file", Toast.LENGTH_SHORT).show(); + } + } + + private void openGallery() { + galleryLauncher.launch("image/*"); + } + + private File createImageFile() throws IOException { + // Create an image file name + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); + String imageFileName = "JPEG_" + timeStamp + "_"; + + File storageDir = requireContext().getExternalFilesDir(null); + File image = File.createTempFile( + imageFileName, /* prefix */ + ".jpg", /* suffix */ + storageDir /* directory */ + ); + + return image; + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/fragments/ReportsFragment.java b/app/src/main/java/com/utopiaindustries/qc_android/fragments/ReportsFragment.java new file mode 100644 index 0000000..ab2d897 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/fragments/ReportsFragment.java @@ -0,0 +1,162 @@ +package com.utopiaindustries.qc_android.fragments; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.utopiaindustries.qc_android.R; +import com.utopiaindustries.qc_android.apiservice.ApiService; +import com.utopiaindustries.qc_android.apiservice.ApiServiceFactory; + +import java.util.ArrayList; +import java.util.Objects; + +public class ReportsFragment extends Fragment {//implements SelectReportListener { + + RecyclerView recyclerView; + ApiService apiService; + + //Store store; + //InspectionReportService inspectionReportService; + TextView emptyReportTextView; + //List alertlist = new ArrayList<>(); + SharedPreferences sharedPreferences; + SharedPreferences.Editor editor; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.fragment_reports, container, false); + + initializeViews(view); + // Inflate the layout for this fragment + return view; + } + + private void initializeViews(View view) { + + sharedPreferences = requireActivity().getSharedPreferences("login_prefs", Context.MODE_PRIVATE); + editor = sharedPreferences.edit(); + + //store = Store.getInstance(); + apiService = ApiServiceFactory.getApiService(); + //inspectionReportService = InspectionReportService.getInstance(); + recyclerView = view.findViewById(R.id.reports_recyclerview); + emptyReportTextView = view.findViewById(R.id.empty_report_text); + + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + + //Toast.makeText(getContext(), "Reports Fragment", Toast.LENGTH_LONG).show(); + + + } + + /*private void refreshReportData() { + SharedPreferences sharedPreferences = getContext().getSharedPreferences("login_prefs", Context.MODE_PRIVATE); + String username = sharedPreferences.getString("username", null); + apiService.fetchAllReports(username).enqueue( + new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful() && response.body() != null) { + + String appOpened = sharedPreferences.getString("appOpened", "false"); + //Log.e("appOpened", appOpened); + if (response.body().getAlerts() != null + && !response.body().getAlerts().isEmpty() && appOpened.equalsIgnoreCase("false")) { + alertlist = response.body().getAlerts(); + showAlertsDialog(alertlist, getActivity()); + + editor.putString("appOpened", "true"); + editor.apply(); + } + //ReportAdapter adapter = new ReportAdapter(response.body().getReports(), ); + + if (response.body().getReports() != null && !response.body().getReports().isEmpty()) + { + ReportAdapter adapter = new ReportAdapter(response.body().getReports()); + + recyclerView.setAdapter(adapter); + if (response.body().getReports().isEmpty()) + emptyReportTextView.setVisibility(View.VISIBLE); + } + + } else { + System.out.println("Error"); + emptyReportTextView.setVisibility(View.VISIBLE); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + System.out.println(t.getMessage()); + emptyReportTextView.setVisibility(View.VISIBLE); + } + } + ); + }*/ + + @Override + public void onResume() { + super.onResume(); + + //refreshReportData(); + } + + public void updateData() { + // Your logic to update fragment data + //System.out.println("Reports-Update"); + //refreshReportData(); + } + + /*private void showAlertsDialog(List alerts, Context context) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + View dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_alerts, null); + + RecyclerView recyclerView = dialogView.findViewById(R.id.recyclerAlerts); + AlertAdapter adapter = new AlertAdapter(alerts, context); + recyclerView.setLayoutManager(new LinearLayoutManager(context)); + recyclerView.setAdapter(adapter); + + builder.setView(dialogView); + + builder.setPositiveButton("Close", null); + + AlertDialog dialog = builder.create(); + dialog.setCancelable(false); + dialog.show(); + + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> { + LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); + if (layoutManager != null) { + int lastVisible = layoutManager.findLastCompletelyVisibleItemPosition(); + int totalItems = adapter.getItemCount(); + + if (recyclerView.canScrollVertically(1)) { + // User has not scrolled to bottom yet + Toast.makeText(context, "Please read all instructions before closing", Toast.LENGTH_LONG).show(); + } else { + // All good, close the dialog + dialog.dismiss(); + } + } + }); + }*/ +} \ No newline at end of file diff --git a/app/src/main/java/com/utopiaindustries/qc_android/helper/DateTimeUtils.java b/app/src/main/java/com/utopiaindustries/qc_android/helper/DateTimeUtils.java new file mode 100644 index 0000000..b4617df --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/helper/DateTimeUtils.java @@ -0,0 +1,22 @@ +package com.utopiaindustries.qc_android.helper; + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class DateTimeUtils { + + public static final String HTML5_DATETIME_INPUT_FORMAT_WITH_SECONDS = "yyyy-MM-dd'T'HH:mm:ss"; + + /** + * format localdatetime into a given format string + */ + @RequiresApi(api = Build.VERSION_CODES.O) + public static String getFormattedDateTimeString(LocalDateTime dateTime, String dateTimeFormat ) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern( dateTimeFormat ); + return dateTime.format( formatter ); + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/helper/Helper.java b/app/src/main/java/com/utopiaindustries/qc_android/helper/Helper.java new file mode 100644 index 0000000..e9d9d11 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/helper/Helper.java @@ -0,0 +1,459 @@ +package com.utopiaindustries.qc_android.helper; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.media.ExifInterface; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.text.TextUtils; +import android.util.Log; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; + +import androidx.preference.PreferenceManager; + +import com.bumptech.glide.Glide; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.net.HttpURLConnection; +import java.net.URL; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Consumer; + +public class Helper { + + public static final String project_file = "Quality-Control"; + + public static final String departmentId = "departId"; + public static final String departmentName = "departName"; + public static final String locationSiteId = "locationSiteId"; + public static final String locationSiteName = "locationSiteName"; + public static final String unitId = "unitId"; + public static final String unitName = "unitName"; + public static final String floorId = "floorId"; + public static final String floorName = "floorName"; + + public static final String logInUser = "LogInUser"; + + public static final String firstTimeApiCall = "isFirstTimeApiCall"; + + public static final String homeSite = "Sites"; + public static final String superVisor = "SuperVisor"; + public static final String recordType = "RecordType"; + + public static final String observationClass = "observationClass"; + public static final String observationSubClass = "observationSubClass"; + + public static final String hseFloors = "hseFloors"; + public static final String hseActivities = "hseActivities"; + + public static final String hseInjuryTypes = "hseInjuryTypes"; + public static final String hseKpis = "hseKpis"; + + public static final String hseDepartment = "hseDepartment"; + public static final String hseIncidentTypes = "hseIncidentTypes"; + public static final String hseInjuredBodyPart = "hseInjuredBodyPart"; + public static final String hsePtwType = "hsePtwType"; + + public static final String hseSafetyTrainingTopics = "hseSafetyTrainingTopics"; + public static final String hseBuildings = "hseBuildings"; + public static final String hseWorkingTeams = "hseWorkingTeams"; + + public static final String hseInjuries = "hseInjuries"; + + //Live url for images + //public final static String IMAGES_URL = "https://portal.utopiaindustries.pk/uind/images/"; + + //Test Url for images + public final static String IMAGES_URL = "http://192.168.90.22:8081/uind/images/"; + + public static boolean isValidEmail(CharSequence target) { + return !TextUtils.isEmpty(target) && android.util.Patterns.EMAIL_ADDRESS.matcher(target).matches(); + } + + public static boolean isNetworkConnected(Context context) { + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo(); + return activeNetwork != null && activeNetwork.isConnectedOrConnecting(); + } + + static public void setPreferenceObject(Context c, Object modal, String key) { + + SharedPreferences appSharedPrefs = PreferenceManager.getDefaultSharedPreferences(c.getApplicationContext()); + SharedPreferences.Editor prefsEditor = appSharedPrefs.edit(); + + Gson gson = new Gson(); + String jsonObject = gson.toJson(modal); + prefsEditor.putString(key, jsonObject); + prefsEditor.commit(); + } + + /*static public QualityControlResponse getPreferenceObjectJson(Context c, String key) { + + SharedPreferences appSharedPrefs = PreferenceManager.getDefaultSharedPreferences(c.getApplicationContext()); + + String json = appSharedPrefs.getString(key, ""); + Gson gson = new Gson(); + QualityControlResponse selectedUser = gson.fromJson(json, QualityControlResponse.class); + return selectedUser; + }*/ + + /*static public void saveArrayList(List list, String key, Context context){ + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = prefs.edit(); + Gson gson = new Gson(); + String json = gson.toJson(list); + editor.putString(key, json); + editor.apply(); + + }*/ + + /*static public List getArrayList(String key, Context context){ + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + // Check if the key exists + if (!prefs.contains(key)) { + return null; // Return null if the key doesn't exist + } + + Gson gson = new Gson(); + String json = prefs.getString(key, null); + Type type = new TypeToken>() {}.getType(); + return gson.fromJson(json, type); + }*/ + + static public void RemoveArrayList(String key, Context context) { + if (context != null) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + if (prefs.contains(key)) { // Check if the key exists + SharedPreferences.Editor editor = prefs.edit(); + editor.remove(key); // Remove the key-value pair + editor.apply(); // Apply changes + ///Log.e("SharedPreferences", "Key '" + key + "' removed successfully."); + } else { + Log.e("SharedPreferences", "Key '" + key + "' does not exist."); + } + } + + } + + //for department, site, unit, floor + public static void saveList(List list, String key, Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = prefs.edit(); + Gson gson = new Gson(); + String json = gson.toJson(list); // Convert the list to JSON + editor.putString(key, json); + editor.apply(); // Save to SharedPreferences + } + + public static List getList(String key, Context context, Class clazz) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + // Check if the key exists + if (!prefs.contains(key)) { + return new ArrayList<>(); // Return an empty list if the key doesn't exist + } + + Gson gson = new Gson(); + String json = prefs.getString(key, null); + Type type = TypeToken.getParameterized(ArrayList.class, clazz).getType(); // Use the provided class type + return gson.fromJson(json, type); // Convert JSON back to the list + } + + public static Uri correctImageRotationAndGetUri(Context context, String imagePath) { + try { + Bitmap bitmap = BitmapFactory.decodeFile(imagePath); + ExifInterface exif = new ExifInterface(imagePath); + int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); + Matrix matrix = new Matrix(); + + switch (orientation) { + case ExifInterface.ORIENTATION_ROTATE_90: matrix.postRotate(90); break; + case ExifInterface.ORIENTATION_ROTATE_180: matrix.postRotate(180); break; + case ExifInterface.ORIENTATION_ROTATE_270: matrix.postRotate(270); break; + } + + Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + + // Save to new file + File correctedFile = new File(context.getCacheDir(), "corrected_" + System.currentTimeMillis() + ".jpg"); + FileOutputStream fos = new FileOutputStream(correctedFile); + rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); + fos.flush(); + fos.close(); + + return Uri.fromFile(correctedFile); + } catch (Exception e) { + e.printStackTrace(); + return Uri.fromFile(new File(imagePath)); // fallback + } + } + + public static boolean isNullOrEmpty( String str ) { + return str == null || str.isEmpty(); + } + + public static void setupDropdown(AutoCompleteTextView textView, ArrayAdapter adapter) { + textView.setAdapter(adapter); + + // Always show dropdown when focused + textView.setOnFocusChangeListener((v, hasFocus) -> { + if (hasFocus) { + textView.post(textView::showDropDown); + } + }); + + // Show dropdown when clicked + textView.setOnClickListener(v -> textView.showDropDown()); + + // Optional: tweak dropdown position + textView.setDropDownVerticalOffset(10); + } + + public static void deleteFile(String filePath) { + File file = new File(filePath); + if (file.exists()) { + if (file.delete()) { + System.out.println("File deleted successfully"); + } else { + System.out.println("Failed to delete the file"); + } + } else { + System.out.println("File does not exist"); + } + } + + public static String formatDate(String inputDate) { + // Define the input and output date formats + SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US); + SimpleDateFormat outputFormat = new SimpleDateFormat("MMM dd, yyyy hh:mm a", Locale.US); + + try { + // Parse the input string into a Date object + Date date = inputFormat.parse(inputDate); + // Format the Date object into the desired output string + return outputFormat.format(date); + } catch (ParseException e) { + e.printStackTrace(); + return null; // Return null on parsing error + } + } + + public static String findTitleById(List list, int id) { + for (T item : list) { + try { + Method getIdMethod = item.getClass().getMethod("getId"); + Method getTitleMethod = item.getClass().getMethod("getTitle"); + + int itemId = (int) getIdMethod.invoke(item); + if (itemId == id) { + return (String) getTitleMethod.invoke(item); + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + return null; + } + + public static String findTitleByIdForSupervisor(List list, int id) { + for (T item : list) { + try { + Method getIdMethod = item.getClass().getMethod("getId"); + Method getTitleMethod = item.getClass().getMethod("getName"); + + int itemId = (int) getIdMethod.invoke(item); + if (itemId == id) { + return (String) getTitleMethod.invoke(item); + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + return null; + } + + public static void getImageBytesWithGlide(Context context, String url, Consumer callback) { + new Thread(() -> { + try { + // Load bitmap using Glide + Bitmap bitmap = Glide.with(context) + .asBitmap() + .load(url) + .submit() + .get(); + + // Convert bitmap to byte array + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + byte[] byteArray = stream.toByteArray(); + + // Return result on main thread + new Handler(Looper.getMainLooper()).post(() -> callback.accept(byteArray)); + + } catch (Exception e) { + e.printStackTrace(); + new Handler(Looper.getMainLooper()).post(() -> callback.accept(null)); + } + }).start(); + } + + public static String findTitlesByIds(List list, String idString) { + List titles = new ArrayList<>(); + + if (idString == null || idString.trim().isEmpty()) { + return ""; + } + + String[] idTokens = idString.split(","); + + for (String token : idTokens) { + try { + int targetId = Integer.parseInt(token.trim()); + + for (T item : list) { + Method getIdMethod = item.getClass().getMethod("getId"); + Method getTitleMethod = item.getClass().getMethod("getTitle"); + + int itemId = (int) getIdMethod.invoke(item); + + if (itemId == targetId) { + titles.add((String) getTitleMethod.invoke(item)); + break; + } + } + } catch (NumberFormatException e) { + Log.e("Invalid ID token: ",""+token); + } catch (Exception e) { + e.printStackTrace(); + } + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < titles.size(); i++) { + sb.append(titles.get(i)); + if (i < titles.size() - 1) { + sb.append(", "); + } + } + return sb.toString(); + } + + public interface MultiImageDownloadCallback { + void onSuccess(List images); + void onError(Exception e); + } + + public interface BeforeAfterImageCallback { + void onSuccess(List beforeImages, List afterImages); + void onError(Exception e); + } + + /*public static void downloadImagesAsync(List urlList, MultiImageDownloadCallback callback) { + ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.execute(() -> { + List imageBytesList = new ArrayList<>(); + try { + for (UploadedDocument urlString : urlList) { + try { + URL url = new URL(IMAGES_URL + urlString.getGeneratedFilename()); + Log.e("URL: ",""+url); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.connect(); + + InputStream inputStream = connection.getInputStream(); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer)) != -1) { + byteArrayOutputStream.write(buffer, 0, len); + } + + inputStream.close(); + connection.disconnect(); + + imageBytesList.add(byteArrayOutputStream.toByteArray()); + + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + new Handler(Looper.getMainLooper()).post(() -> callback.onSuccess(imageBytesList)); + + } catch (Exception e) { + new Handler(Looper.getMainLooper()).post(() -> callback.onError(e)); + } + }); + }*/ + + /*public static void downloadBeforeAfterImagesAsync(List documents, BeforeAfterImageCallback callback) { + ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.execute(() -> { + List beforeImages = new ArrayList<>(); + List afterImages = new ArrayList<>(); + + try { + for (UploadedDocument doc : documents) { + try { + URL url = new URL(IMAGES_URL + doc.getGeneratedFilename()); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.connect(); + + InputStream inputStream = connection.getInputStream(); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer)) != -1) { + byteArrayOutputStream.write(buffer, 0, len); + } + + inputStream.close(); + connection.disconnect(); + + byte[] imageBytes = byteArrayOutputStream.toByteArray(); + + if ("OBSERVATION_BEFORE".equalsIgnoreCase(doc.getType())) { + beforeImages.add(imageBytes); + } else if ("OBSERVATION_AFTER".equalsIgnoreCase(doc.getType())) { + afterImages.add(imageBytes); + } + + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + new Handler(Looper.getMainLooper()).post(() -> callback.onSuccess(beforeImages, afterImages)); + + } catch (Exception e) { + new Handler(Looper.getMainLooper()).post(() -> callback.onError(e)); + } + }); + }*/ +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/helper/Preference.java b/app/src/main/java/com/utopiaindustries/qc_android/helper/Preference.java new file mode 100644 index 0000000..6f02ea3 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/helper/Preference.java @@ -0,0 +1,82 @@ +package com.utopiaindustries.qc_android.helper; + +import android.content.Context; +import android.content.SharedPreferences; + +public class Preference { + + private static final int PREFERENCE_MODE_PRIVATE = 0; + private static String MY_STRING_PREF = "mystringpref"; + public static String LOGIN_FILE = "login_prefs"; + public static String TEMP_FILE = "temp_prefs"; + + + public static String KEY_PIN_CODE = "pin_code"; + + public static String KEY_CUSTOMER_ID = "cus_id"; + public static String KEY_CUSTOMER_NAME = "cus_name"; + + public static String KEY_USER_IS_MASTER = "user_is_master"; + + public static SharedPreferences getPrefs(String nameOfFile, Context context) { + + return context.getSharedPreferences(nameOfFile, Context.MODE_PRIVATE); + } + + public static boolean containsKey(String fileName, String key, Context context) { + return getPrefs(fileName, context).contains(key); + } + + public static String getMyStringPref(String fileName, String key, Context context) { + + return getPrefs(fileName,context).getString(key, "default"); + } + public static boolean getMyBooleanPref(String fileName, String key, Context context) { + + return getPrefs(fileName,context).getBoolean(key,false); + } + public static int getMyIntPref(String fileName, String key, Context context) { + + return getPrefs(fileName,context).getInt(key, 0); + } + + public static long getMyLongPref(String fileName, String key, Context context) { + + return getPrefs(fileName,context).getLong(key, 0); + } + + public static void setMyLongPref(String fileName, String key, Context context, long value) { + // perform validation etc.. + getPrefs(fileName,context).edit().putLong(key, value).apply(); + } + + public static void setMyStringPref(String fileName, String key, Context context, String value) { + // perform validation etc.. + getPrefs(fileName,context).edit().putString(key, value).apply(); + } + + public static void setMyBooleanPref(String fileName, String key, Context context, boolean value) { + // perform validation etc.. + getPrefs(fileName,context).edit().putBoolean(key, value).apply(); + } + + public static void setMyIntPref(String fileName, String key, Context context, int value) { + // perform validation etc.. + getPrefs(fileName,context).edit().putInt(key, value).apply(); + } + + + public static void remove(String fileName, String key, Context context) { + // perform validation etc.. + if (context != null) { + if (getPrefs(fileName,context).contains(key)) { + getPrefs(fileName,context).edit().remove(key).apply(); + } + } + + } + public static void removeAll(String fileName, Context context) { + // perform validation etc.. + getPrefs(fileName,context).edit().clear().apply(); + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/helper/PropertyReader.java b/app/src/main/java/com/utopiaindustries/qc_android/helper/PropertyReader.java new file mode 100644 index 0000000..55b7a2b --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/helper/PropertyReader.java @@ -0,0 +1,24 @@ +package com.utopiaindustries.qc_android.helper; + +import android.content.Context; +import android.content.res.Resources; + +import com.utopiaindustries.qc_android.R; + +import java.io.InputStream; +import java.util.Properties; + +public class PropertyReader { + + public static Properties getProperties( Context context ) { + Properties properties = new Properties(); + try { + Resources resources = context.getResources(); + InputStream inputStream = resources.openRawResource( R.raw.application ); + properties.load( inputStream ); + } catch ( Exception e ) { + e.printStackTrace(); + } + return properties; + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/helper/Release.java b/app/src/main/java/com/utopiaindustries/qc_android/helper/Release.java new file mode 100644 index 0000000..1c18f3a --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/helper/Release.java @@ -0,0 +1,69 @@ +package com.utopiaindustries.qc_android.helper; + + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import java.util.List; + +@JsonIgnoreProperties( ignoreUnknown = true ) +@JsonNaming( PropertyNamingStrategy.SnakeCaseStrategy.class ) +public class Release { + private String id; + private String tagName; + private String name; + private List assets; + + public Release() { + } + + public String getId() { + return id; + } + + public void setId( String id ) { + this.id = id; + } + + public String getTagName() { + return tagName; + } + + public void setTagName( String tagName ) { + this.tagName = tagName; + } + + public String getName() { + return name; + } + + public void setName( String name ) { + this.name = name; + } + + public List getAssets() { + return assets; + } + + public void setAssets( List assets ) { + this.assets = assets; + } + + public String getDownloadUrl() { + if ( this.assets == null || this.assets.isEmpty() ) { + return null; + } + return this.assets.get( 0 ).getBrowserDownloadUrl(); + } + + @Override + public String toString() { + return "Release{" + + "id='" + id + '\'' + + ", tagName='" + tagName + '\'' + + ", name='" + name + '\'' + + ", assets=" + assets + + '}'; + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/helper/ReleaseAsset.java b/app/src/main/java/com/utopiaindustries/qc_android/helper/ReleaseAsset.java new file mode 100644 index 0000000..16be36b --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/helper/ReleaseAsset.java @@ -0,0 +1,95 @@ +package com.utopiaindustries.qc_android.helper; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.utopiaindustries.qc_android.utils.jackson.ZonedDateTimeDeserializer; + +import java.time.ZonedDateTime; + + +@JsonIgnoreProperties( ignoreUnknown = true ) +@JsonNaming( PropertyNamingStrategy.SnakeCaseStrategy.class ) +public class ReleaseAsset { + private String id; + private String name; + private long size; + private long downloadCount; + @JsonDeserialize( using = ZonedDateTimeDeserializer.class ) + private ZonedDateTime createdAt; + private String uuid; + private String browserDownloadUrl; + + public ReleaseAsset() { + } + + public String getId() { + return id; + } + + public void setId( String id ) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName( String name ) { + this.name = name; + } + + public long getSize() { + return size; + } + + public void setSize( long size ) { + this.size = size; + } + + public long getDownloadCount() { + return downloadCount; + } + + public void setDownloadCount( long downloadCount ) { + this.downloadCount = downloadCount; + } + + public ZonedDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt( ZonedDateTime createdAt ) { + this.createdAt = createdAt; + } + + public String getUuid() { + return uuid; + } + + public void setUuid( String uuid ) { + this.uuid = uuid; + } + + public String getBrowserDownloadUrl() { + return browserDownloadUrl; + } + + public void setBrowserDownloadUrl( String browserDownloadUrl ) { + this.browserDownloadUrl = browserDownloadUrl; + } + + @Override + public String toString() { + return "ReleaseAsset{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", size=" + size + + ", downloadCount=" + downloadCount + + ", createdAt=" + createdAt + + ", uuid='" + uuid + '\'' + + ", browserDownloadUrl='" + browserDownloadUrl + '\'' + + '}'; + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/models/InspectionCheckPoint.java b/app/src/main/java/com/utopiaindustries/qc_android/models/InspectionCheckPoint.java new file mode 100644 index 0000000..f69a09d --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/models/InspectionCheckPoint.java @@ -0,0 +1,118 @@ +package com.utopiaindustries.qc_android.models; + + +import android.net.Uri; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class InspectionCheckPoint implements Serializable { + + private static final long serialVersionUID = 1L; + private long id; + private String title; + private String category; + + private boolean isOkChecked; + private boolean isNoChecked; + private String remarks; + private List images; + + public InspectionCheckPoint() { + } + + public InspectionCheckPoint(long id, String title, String category, boolean isOkChecked, boolean isNoChecked, String remarks, List images) { + this.id = id; + this.title = title; + this.category = category; + this.isOkChecked = isOkChecked; + this.isNoChecked = isNoChecked; + this.remarks = remarks; + this.images = images; + } + + 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 getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public boolean isOkChecked() { + return isOkChecked; + } + + public void setOkChecked(boolean okChecked) { + isOkChecked = okChecked; + } + + public List getImages() { + return images; + } + + public void setImages(List images) { + this.images = images; + } + + public void addImage(byte[] imageBytes) { + if (this.images == null) { + this.images = new ArrayList<>(); + } + this.images.add(imageBytes); + } + + public void removeImage(int position) { + if (this.images != null && position < this.images.size()) { + this.images.remove(position); + } + } + + public void clearAllImages() { + if (this.images != null) { + this.images.clear(); + } + } + + public String getRemarks() { + return remarks; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + public boolean isNoChecked() { + return isNoChecked; + } + + public void setNoChecked(boolean noChecked) { + isNoChecked = noChecked; + } + + @Override + public String toString() { + return "InspectionCheckPoint{" + + "id=" + id + + ", title='" + title + '\'' + + ", category='" + category + '\'' + + '}'; + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/utils/DatabaseExecutor.java b/app/src/main/java/com/utopiaindustries/qc_android/utils/DatabaseExecutor.java new file mode 100644 index 0000000..d60ae67 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/utils/DatabaseExecutor.java @@ -0,0 +1,19 @@ +package com.utopiaindustries.qc_android.utils; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class DatabaseExecutor { + + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); + + public static ExecutorService getExecutor() { + return executor; + } + + public static void shutdown() { + if (!executor.isShutdown()) { + executor.shutdown(); + } + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/utils/FileUtils.java b/app/src/main/java/com/utopiaindustries/qc_android/utils/FileUtils.java new file mode 100644 index 0000000..198d5bb --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/utils/FileUtils.java @@ -0,0 +1,98 @@ +package com.utopiaindustries.qc_android.utils; + +import android.content.Context; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class FileUtils { + + // Save model object as byte array file + public static String saveObjectToFile(Context context, Object model, String fileName) { + File file = new File(context.getFilesDir(), fileName); + ObjectOutputStream oos = null; + FileOutputStream fos = null; + + try { + fos = new FileOutputStream(file); + oos = new ObjectOutputStream(fos); + oos.writeObject(model); // model must implement Serializable + oos.flush(); + return file.getAbsolutePath(); + } catch (IOException e) { + e.printStackTrace(); + return null; // return null if saving failed + } finally { + try { + if (oos != null) oos.close(); + if (fos != null) fos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /*public static StorageManager loadStorageFromFile(String filePath) { + File file = new File(filePath); + if (!file.exists()) return null; + + try (FileInputStream fis = new FileInputStream(file); + ObjectInputStream ois = new ObjectInputStream(fis)) { + + return (StorageManager) ois.readObject(); + + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + return null; + } + }*/ + + private static final Gson gson = new GsonBuilder().create(); + + /*public static String saveObjectToFileWithGson(Context context, StorageManager storage, String fileName) { + try { + File dir = context.getFilesDir(); + File file = new File(dir, fileName); + + try (FileWriter writer = new FileWriter(file)) { + gson.toJson(storage, writer); // ✅ Save as JSON + } + + return file.getAbsolutePath(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + }*/ + + /*public static StorageManager loadStorageFromFileWithGson(String filePath) { + File file = new File(filePath); + if (!file.exists()) return null; + + try (FileReader reader = new FileReader(file)) { + return gson.fromJson(reader, StorageManager.class); // ✅ Deserialize safely + } catch (Exception e) { + e.printStackTrace(); + return null; + } + }*/ + + public static String getAppVersion(Context context) { + try { + return context.getPackageManager() + .getPackageInfo(context.getPackageName(), 0).versionName; + } catch (Exception e) { + e.printStackTrace(); + return "0.0"; // fallback + } + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/utils/FullScreenImageDialog.java b/app/src/main/java/com/utopiaindustries/qc_android/utils/FullScreenImageDialog.java new file mode 100644 index 0000000..a9b8126 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/utils/FullScreenImageDialog.java @@ -0,0 +1,46 @@ +package com.utopiaindustries.qc_android.utils; + +import android.app.Dialog; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.view.Window; +import android.view.WindowManager; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; + +import com.utopiaindustries.qc_android.R; + +public class FullScreenImageDialog extends DialogFragment { + private Bitmap imageBitmap; + + public FullScreenImageDialog(Bitmap imageBitmap) { + this.imageBitmap = imageBitmap; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + Dialog dialog = new Dialog(requireContext()); + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + dialog.setContentView(R.layout.dialog_fullscreen_image); + + /*ImageView imageView = dialog.findViewById(R.id.fullscreenImageView); + imageView.setImageBitmap(imageBitmap);*/ + + //imageView.setOnClickListener(v -> dismiss()); // Close on image click + ImageView imageView = dialog.findViewById(R.id.img_view); + imageView.setImageBitmap(imageBitmap); + + ImageView btnClose = dialog.findViewById(R.id.btn_close); + btnClose.setOnClickListener(v -> dismiss()); + + dialog.getWindow().setLayout( + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.MATCH_PARENT + ); + return dialog; + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/utils/NonSwipeableViewPager.java b/app/src/main/java/com/utopiaindustries/qc_android/utils/NonSwipeableViewPager.java new file mode 100644 index 0000000..1bdee7e --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/utils/NonSwipeableViewPager.java @@ -0,0 +1,33 @@ +package com.utopiaindustries.qc_android.utils; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; + +import androidx.viewpager.widget.ViewPager; + +/** + * Created by Development on 05-Oct-2021. + */ +public class NonSwipeableViewPager extends ViewPager +{ + public NonSwipeableViewPager(Context context) { + super(context); + } + + public NonSwipeableViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + // Never allow swiping to switch between pages + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + // Never allow swiping to switch between pages + return false; + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/utils/ProgressDialogFragment.java b/app/src/main/java/com/utopiaindustries/qc_android/utils/ProgressDialogFragment.java new file mode 100644 index 0000000..93da20a --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/utils/ProgressDialogFragment.java @@ -0,0 +1,37 @@ +package com.utopiaindustries.qc_android.utils; + +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; + +import com.utopiaindustries.qc_android.R; + +public class ProgressDialogFragment extends DialogFragment { + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.dialog_progress, container, false); + } + + @Override + public void onStart() { + super.onStart(); + if (getDialog() != null && getDialog().getWindow() != null) { + getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent); + } + } + + @Override + public void onCancel(@NonNull DialogInterface dialog) { + // Prevent the dialog from being canceled when touched outside + setCancelable(false); + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/utils/SelectListener.java b/app/src/main/java/com/utopiaindustries/qc_android/utils/SelectListener.java new file mode 100644 index 0000000..583ae43 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/utils/SelectListener.java @@ -0,0 +1,7 @@ +package com.utopiaindustries.qc_android.utils; + + +public interface SelectListener { + + //void onItemClicked(InspectionReportWrapper reportWrapper); +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/utils/jackson/LocalDateTimeDeserializer.java b/app/src/main/java/com/utopiaindustries/qc_android/utils/jackson/LocalDateTimeDeserializer.java new file mode 100644 index 0000000..fbb3f2c --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/utils/jackson/LocalDateTimeDeserializer.java @@ -0,0 +1,35 @@ +package com.utopiaindustries.qc_android.utils.jackson; + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.utopiaindustries.qc_android.helper.DateTimeUtils; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class LocalDateTimeDeserializer extends StdDeserializer { + + private static final long serialVersionUID = 1L; + + @RequiresApi(api = Build.VERSION_CODES.O) + public LocalDateTimeDeserializer() { + super( LocalDateTime.class ); + } + + @RequiresApi(api = Build.VERSION_CODES.O) + @Override + public LocalDateTime deserialize( JsonParser parser, DeserializationContext ctxt ) throws IOException, JsonProcessingException { + JsonNode node = parser.getCodec().readTree( parser ); + String dateStr = node.asText(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern( DateTimeUtils.HTML5_DATETIME_INPUT_FORMAT_WITH_SECONDS ); + return LocalDateTime.parse( dateStr, formatter ); + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/utils/jackson/LocalDateTimeSerializer.java b/app/src/main/java/com/utopiaindustries/qc_android/utils/jackson/LocalDateTimeSerializer.java new file mode 100644 index 0000000..3638c74 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/utils/jackson/LocalDateTimeSerializer.java @@ -0,0 +1,25 @@ +package com.utopiaindustries.qc_android.utils.jackson; + + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.utopiaindustries.qc_android.helper.DateTimeUtils; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@RequiresApi(api = Build.VERSION_CODES.O) +public class LocalDateTimeSerializer extends JsonSerializer { + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern( DateTimeUtils.HTML5_DATETIME_INPUT_FORMAT_WITH_SECONDS ); + + @Override + public void serialize( LocalDateTime value, JsonGenerator gen, SerializerProvider serializers ) throws IOException { + gen.writeString( value.format( FORMATTER ) ); + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/utils/jackson/ZonedDateTimeDeserializer.java b/app/src/main/java/com/utopiaindustries/qc_android/utils/jackson/ZonedDateTimeDeserializer.java new file mode 100644 index 0000000..59cd133 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/utils/jackson/ZonedDateTimeDeserializer.java @@ -0,0 +1,35 @@ +package com.utopiaindustries.qc_android.utils.jackson; + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +public class ZonedDateTimeDeserializer extends StdDeserializer { + + private static final long serialVersionUID = 1L; + + @RequiresApi(api = Build.VERSION_CODES.O) + public ZonedDateTimeDeserializer() { + super( LocalDateTime.class ); + } + + @RequiresApi(api = Build.VERSION_CODES.O) + @Override + public ZonedDateTime deserialize( JsonParser parser, DeserializationContext ctxt ) throws IOException, JsonProcessingException { + JsonNode node = parser.getCodec().readTree( parser ); + String dateStr = node.asText(); + DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME; + return ZonedDateTime.parse( dateStr, formatter ); + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/viewmodels/DataEntryViewModel.java b/app/src/main/java/com/utopiaindustries/qc_android/viewmodels/DataEntryViewModel.java new file mode 100644 index 0000000..f317b4c --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/viewmodels/DataEntryViewModel.java @@ -0,0 +1,90 @@ +package com.utopiaindustries.qc_android.viewmodels; + +import android.net.Uri; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +public class DataEntryViewModel extends ViewModel { + + // Personal Info + private MutableLiveData firstName = new MutableLiveData<>(); + private MutableLiveData lastName = new MutableLiveData<>(); + private MutableLiveData email = new MutableLiveData<>(); + + // Address Info + private MutableLiveData address = new MutableLiveData<>(); + private MutableLiveData city = new MutableLiveData<>(); + + // Employment Info + private MutableLiveData company = new MutableLiveData<>(); + private MutableLiveData position = new MutableLiveData<>(); + + private MutableLiveData selectedImageUri = new MutableLiveData<>(); + + // Getters and Setters + public LiveData getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName.setValue(firstName); + } + + public LiveData getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName.setValue(lastName); + } + + public LiveData getEmail() { + return email; + } + + public void setEmail(String email) { + this.email.setValue(email); + } + + public LiveData getAddress() { + return address; + } + + public void setAddress(String address) { + this.address.setValue(address); + } + + public LiveData getCity() { + return city; + } + + public void setCity(String city) { + this.city.setValue(city); + } + + public LiveData getCompany() { + return company; + } + + public void setCompany(String company) { + this.company.setValue(company); + } + + public LiveData getPosition() { + return position; + } + + public void setPosition(String position) { + this.position.setValue(position); + } + + public MutableLiveData getSelectedImageUri() { + return selectedImageUri; + } + + public void setSelectedImageUri(Uri uri) { + selectedImageUri.setValue(uri); + } +} diff --git a/app/src/main/java/com/utopiaindustries/qc_android/viewmodels/ServiceViewModel.java b/app/src/main/java/com/utopiaindustries/qc_android/viewmodels/ServiceViewModel.java new file mode 100644 index 0000000..7f143d3 --- /dev/null +++ b/app/src/main/java/com/utopiaindustries/qc_android/viewmodels/ServiceViewModel.java @@ -0,0 +1,278 @@ +package com.utopiaindustries.qc_android.viewmodels; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import com.utopiaindustries.qc_android.apiservice.ApiService; +import com.utopiaindustries.qc_android.apiservice.ApiServiceFactory; +import com.utopiaindustries.qc_android.db.CheckpointRepository; +import com.utopiaindustries.qc_android.models.InspectionCheckPoint; + +import java.util.List; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class ServiceViewModel extends ViewModel { + + /*private MutableLiveData employeeLiveData; + private MutableLiveData dailyWageLiveData; + private MutableLiveData userLiveData; + private MutableLiveData reportListLiveData; + private MutableLiveData userSaveLiveData; + + private final ExecutorService executorService; + private MutableLiveData userDraftLiveData;*/ + private ApiService apiService; + private MutableLiveData isLoading; + private MutableLiveData userLoginLiveData; + private MutableLiveData errorLiveData; + private MutableLiveData> checkpointsListLiveData; + CheckpointRepository repository; + + public ServiceViewModel() { + /* + userLiveData = new MutableLiveData<>(); + reportListLiveData = new MutableLiveData<>(); + employeeLiveData = new MutableLiveData<>(); + dailyWageLiveData = new MutableLiveData<>(); + userSaveLiveData = new MutableLiveData<>(); + userDraftLiveData = new MutableLiveData<>(); + + this.executorService = Executors.newFixedThreadPool(4);*/ + apiService = ApiServiceFactory.getApiService(); + isLoading = new MutableLiveData<>(); + userLoginLiveData = new MutableLiveData<>(); + errorLiveData = new MutableLiveData<>(); + checkpointsListLiveData = new MutableLiveData<>(); + } + + /*public LiveData getEmployeeLiveData() + { + return employeeLiveData; + }*/ + + /*public LiveData getDailyWageLiveData() + { + return dailyWageLiveData; + }*/ + + /*public LiveData getUserLiveData() { + return userLiveData; + }*/ + + /*public LiveData getReportListLiveData() { + return reportListLiveData; + }*/ + + /*public LiveData getUserSaveLiveData() { + return userSaveLiveData; + }*/ + + /*public LiveData getUserDraftLiveData() { + return userDraftLiveData; + }*/ + + public LiveData getLoadingState() { + + return isLoading; + } + + public LiveData getErrorMessage() { + return errorLiveData; + } + + public void isUserAuthenticated(String username, String password, String[] roles) { + isLoading.setValue(true); + apiService.isUserAuthenticated(username, password, roles).enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + isLoading.setValue(false); + //Log.e("onResponse-1: ", "Successful: "+response); + if (response.isSuccessful() && response.body() != null) { + //Log.e("onResponse-2: ", "Successful: "+response); + userLoginLiveData.setValue(response.body()); + } else { + userLoginLiveData.setValue(false); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + //Log.e("onResponse-2: ", "failed"+t.getMessage()); + isLoading.setValue(false); + errorLiveData.setValue(t.getMessage()); + } + }); + } + + /*public void sendUserDraftCount(String username, String dCount) { + isLoading.setValue(true); + apiService.sendDraftCount(username, dCount).enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + isLoading.setValue(false); + + if (response.isSuccessful() && response.body() != null) { + userDraftLiveData.setValue(response.body()); + } else { + errorLiveData.setValue(response.message()); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + isLoading.setValue(false); + errorLiveData.setValue(t.getMessage()); + } + }); + }*/ + + public void getCheckpointsData(Context context) { + repository = new CheckpointRepository(context); + isLoading.setValue(true); + apiService.fetchCheckPoints().enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + isLoading.setValue(false); + if (response.isSuccessful() && response.body() != null) { + //Log.e("onResponse: ", "Successful"); + checkpointsListLiveData.setValue(response.body()); + repository.insert( response.body() ); + } else { + errorLiveData.setValue(response.message()); + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + //Log.e("onResponse: ", "Fail"); + isLoading.setValue(false); + errorLiveData.setValue(t.getMessage()); + } + }); + } + + /*public void getDailyWageWorkerData(String visitorId) { + isLoading.setValue(true); + apiService.getDailyWageData(visitorId).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + isLoading.setValue(false); + if (response.isSuccessful() && response.body() != null) { + //Log.e("onResponse: ", "Successful"); + dailyWageLiveData.setValue(response.body()); + } else { + errorLiveData.setValue(response.message()); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + //Log.e("onResponse: ", "Fail"); + isLoading.setValue(false); + errorLiveData.setValue(t.getMessage()); + } + }); + }*/ + + /*public void getHSEData() { + isLoading.setValue(true); + apiService.getHseData().enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + + isLoading.setValue(false); + if (response.isSuccessful() && response.body() != null) { + //Log.e("onResponse-HseData: ", "Successful"); + userLiveData.setValue(response.body()); + } else { + errorLiveData.setValue(response.message()); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + //Log.e("onResponse: ", "Fail"); + isLoading.setValue(false); + errorLiveData.setValue(t.getMessage()); + } + }); + }*/ + + /*public void getHSEReportListData(String userName) { + isLoading.setValue(true); + apiService.getHseReportList(userName).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + + isLoading.setValue(false); + if (response.isSuccessful() && response.body() != null) { + //Log.e("onResponse-HseData: ", "Successful"); + reportListLiveData.setValue(response.body()); + } else { + errorLiveData.setValue(response.message()); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + //Log.e("onResponse: ", "Fail"); + isLoading.setValue(false); + errorLiveData.setValue(t.getMessage()); + } + }); + }*/ + + /*public void saveHSEData(HseReportRequest hseRequestModel) { + isLoading.setValue(true); + + // Execute the task in the background + executorService.execute(() -> { + try { + apiService.saveHseReport(hseRequestModel).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + isLoading.setValue(false); + if (response.isSuccessful() && response.body() != null) { + userSaveLiveData.setValue(response.body()); + } else { + errorLiveData.setValue(response.message()); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + isLoading.setValue(false); + errorLiveData.setValue(t.getMessage()); + } + }); + } catch (Exception e) { + errorLiveData.postValue(e.getMessage()); + isLoading.postValue(false); + } + }); + + }*/ + + public LiveData getLoginUser() { + return userLoginLiveData; + } + + public LiveData> getCheckpointsList() { + return checkpointsListLiveData; + } + + /*public LiveData getUser() { + return userLiveData; + }*/ + + public LiveData getError() { + return errorLiveData; + } +} diff --git a/app/src/main/res/anim/bs_list_layout_fade_in.xml b/app/src/main/res/anim/bs_list_layout_fade_in.xml new file mode 100644 index 0000000..f967280 --- /dev/null +++ b/app/src/main/res/anim/bs_list_layout_fade_in.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/anim/fade_in.xml b/app/src/main/res/anim/fade_in.xml new file mode 100644 index 0000000..e27221b --- /dev/null +++ b/app/src/main/res/anim/fade_in.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/fade_out.xml b/app/src/main/res/anim/fade_out.xml new file mode 100644 index 0000000..4ebd4e2 --- /dev/null +++ b/app/src/main/res/anim/fade_out.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/grow_from_bottom.xml b/app/src/main/res/anim/grow_from_bottom.xml new file mode 100644 index 0000000..be9e03a --- /dev/null +++ b/app/src/main/res/anim/grow_from_bottom.xml @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/grow_from_top.xml b/app/src/main/res/anim/grow_from_top.xml new file mode 100644 index 0000000..16c36e8 --- /dev/null +++ b/app/src/main/res/anim/grow_from_top.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/popup_hide.xml b/app/src/main/res/anim/popup_hide.xml new file mode 100644 index 0000000..e311d4b --- /dev/null +++ b/app/src/main/res/anim/popup_hide.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/anim/popup_show.xml b/app/src/main/res/anim/popup_show.xml new file mode 100644 index 0000000..d90ef50 --- /dev/null +++ b/app/src/main/res/anim/popup_show.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/anim/slide_down.xml b/app/src/main/res/anim/slide_down.xml new file mode 100644 index 0000000..be798a9 --- /dev/null +++ b/app/src/main/res/anim/slide_down.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in_bottom.xml b/app/src/main/res/anim/slide_in_bottom.xml new file mode 100644 index 0000000..ae4e011 --- /dev/null +++ b/app/src/main/res/anim/slide_in_bottom.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in_left.xml b/app/src/main/res/anim/slide_in_left.xml new file mode 100644 index 0000000..44e6eb9 --- /dev/null +++ b/app/src/main/res/anim/slide_in_left.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in_left_menu.xml b/app/src/main/res/anim/slide_in_left_menu.xml new file mode 100644 index 0000000..bc4f72c --- /dev/null +++ b/app/src/main/res/anim/slide_in_left_menu.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in_right.xml b/app/src/main/res/anim/slide_in_right.xml new file mode 100644 index 0000000..740308b --- /dev/null +++ b/app/src/main/res/anim/slide_in_right.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in_right_menu.xml b/app/src/main/res/anim/slide_in_right_menu.xml new file mode 100644 index 0000000..bb153b3 --- /dev/null +++ b/app/src/main/res/anim/slide_in_right_menu.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in_top.xml b/app/src/main/res/anim/slide_in_top.xml new file mode 100644 index 0000000..a608380 --- /dev/null +++ b/app/src/main/res/anim/slide_in_top.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_out_bottom.xml b/app/src/main/res/anim/slide_out_bottom.xml new file mode 100644 index 0000000..46b3bf1 --- /dev/null +++ b/app/src/main/res/anim/slide_out_bottom.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/app/src/main/res/anim/slide_out_left.xml b/app/src/main/res/anim/slide_out_left.xml new file mode 100644 index 0000000..4db9289 --- /dev/null +++ b/app/src/main/res/anim/slide_out_left.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_out_right.xml b/app/src/main/res/anim/slide_out_right.xml new file mode 100644 index 0000000..73e7bfc --- /dev/null +++ b/app/src/main/res/anim/slide_out_right.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_out_top.xml b/app/src/main/res/anim/slide_out_top.xml new file mode 100644 index 0000000..6d813cd --- /dev/null +++ b/app/src/main/res/anim/slide_out_top.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_up.xml b/app/src/main/res/anim/slide_up.xml new file mode 100644 index 0000000..57f2be5 --- /dev/null +++ b/app/src/main/res/anim/slide_up.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/color/tab_text_selector.xml b/app/src/main/res/color/tab_text_selector.xml new file mode 100644 index 0000000..5cfd419 --- /dev/null +++ b/app/src/main/res/color/tab_text_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/arrow_back.xml b/app/src/main/res/drawable/arrow_back.xml new file mode 100644 index 0000000..7b7c7d0 --- /dev/null +++ b/app/src/main/res/drawable/arrow_back.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/circle_background.xml b/app/src/main/res/drawable/circle_background.xml new file mode 100644 index 0000000..274e668 --- /dev/null +++ b/app/src/main/res/drawable/circle_background.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/custom_button.xml b/app/src/main/res/drawable/custom_button.xml new file mode 100644 index 0000000..151887f --- /dev/null +++ b/app/src/main/res/drawable/custom_button.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/drawable/hse_login.jpg b/app/src/main/res/drawable/hse_login.jpg new file mode 100644 index 0000000000000000000000000000000000000000..87cbe8fd90091f06cce6154a0a67d890c1c20a75 GIT binary patch literal 12940 zcmd6NcT`i`w{8Hb3P=e^GfEMpi1eNu3rz$Bqze%NDWUfg5)lEZIiP@mfHY}BM4Hsl zdy(FI?+Gh{9W^x-%?-L6H?GrNzfRACs>>33bFMynh zjDm@b)CvFr0A!R`-u~n8e=TI>S3Xiv)6mjgzj~nJCV-rbf`a@S#XnwOJsot_4!Fid z$$VGh5fzJ`6*aFrt7LFoCJo=?(k7t(AeLY1jmLXhIyUxO9Gn981cmMkOUua0$tx&6 zd8(?WuA%wN;H9CFv5BdfwTYoRXED zllwg{zo76}S$Rce)$i(>=9bpB_Kwc3?xEq4(XsJ~$*IMq<(1X7^^MIf+`-||@yRLv z?ED{GWB`hPhxOl){Rdo3SGdTpU8A^0{SPiOa^EYXV7f+mSAvT9ksh^`I}5L5Fb(VD zxXjWfT0SX#EbxuTARQaO^r8UnA87xE?Eehd`~N3o{~g%>z%>Q9K|yxac@#_l2;kBn zD^dvX|F7AUso!uqP|OnQdMgj0yEa>4y@9@itJ>9E(52?MtF2m5G_o+g87a_o3jY8) z9rYWCKswhu?oLXJkq@;?G{&E+B{$R>+k=wPp7im{I8Av22j2jYc+8R$ntaC5NV_Bo z+IhhuotiX)@IKrAff&kQIu27JXbNN@J^E>uC~(smV?XcWGnYlCr`;h@GRdsn?@(15 zQilL_s`?3Rx_IK@SK~Q&@9&Fi>pjoDX3U_5id)8FTE^x8_XSxM_*kIQ^bvREG~b{ zlSU6_=ng_56Yw`UU^2O>Rh=FQpkfUm!Xmf5GGY@AB){ici6!sztM@|D*5}4L)Tm$2 zt@RSrwMhVfbDZ&0qfYk`!!w1Xq+`#KjUUBatu=NZ`NXaKfsH(d=4+rA{=fn#Qk_YQ zg;GEIbJlsD7o3nS1U40su+PaY?xGXQdiS4O&NuR~*WcpZgkwv8nA$5kw-Gbld2-Wg zI0Vw)(&c9t+-s1*_grW@&fi4qh71eHmE zU*9rb8F!_F@m))p2m{T_(gMsx*O}J^a&{W6aFM+qWb(;kJd^~mnP+z7^uyrTa%~rt zxLvF~p2p|zn2-aJY<=Lv1yqaHy@yq8)8gf^)O*=pm!R|U`GvcmZTCjzmB;oIdiH)f zfIhvgC?ibC6($I~+E)u*o;sAZa7rBI7*1QJq2bh{#XoFEl;sK?8l;D_ID7_I86_DF z*fa^OpDf?bNN{7_JRW*N9m3o6TEA~TJ0Vs=lv-bJFWUB)$r3WMCquPtNdV9o=6Dox zB%$}R5{kQokpLD3B)}73g>IN-7%L_MWU!Blh2l1|E^SDFKdSz~!2MPv<&0nVw2rxX zzVp<)JJMyav_Oy2?GbpXIK#abWr35KvGm}b1afS;`;ipwf|*)n1g z8tr=yQ>QA#xKwSfGw?$<<}(Y=kClE z9DO~ZTWY0K4Jpw{C_0KRqGT0PjG)n?Z$IN4^5AeN4rcpY;1j5eI*~Ezo zyX8+Deag(lE3sA!26^Eink~k$a@cB*iq=c4%TMCdc+9(wv0o}PrDy9)=Y`^S_T;($nbdL_cglCBKu7VE)a~hU zLVtgx5PE1J^*g>OGtHy9Yb8LOhJ5*l!3X<;DQD*R&5J1FjOa?M@qUUSXxnC_y* z{Bd<3+#kOkgtIo1tt=ME3){_*J^AqWFw>AGwdY2CL$%CC4Sjml#Y6nPap;&c-w61Q z;~)#nPSD(tZ!di-o6pl*a22>R4}dE%Mi3wPA*NO+VAo>&g{+#G?z=HoE>tAp%sZsu zPjPDn#Tq+*l*2>}1wRDS(g1ej4naOL=UzV_CTQ4}d*uTC&CxdVX%�d0$?>Tu&W` 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

FoFbS|5IrLJsT+En~U*v&5~) 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml new file mode 100644 index 0000000..62eb4b0 --- /dev/null +++ b/app/src/main/res/drawable/ic_add.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_close.xml b/app/src/main/res/drawable/ic_close.xml new file mode 100644 index 0000000..bcc56a1 --- /dev/null +++ b/app/src/main/res/drawable/ic_close.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml new file mode 100644 index 0000000..883bcaa --- /dev/null +++ b/app/src/main/res/drawable/ic_delete.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_email.xml b/app/src/main/res/drawable/ic_email.xml new file mode 100644 index 0000000..4c104e9 --- /dev/null +++ b/app/src/main/res/drawable/ic_email.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_launcher.webp b/app/src/main/res/drawable/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..77e5dadee5a56a1007a98b0bc212c0ac155a0b71 GIT binary patch literal 1140 zcmV-)1dIDpNk&F&1ONb6MM6+kP&iCr1ONapN5Byf)rNw!4Vc!y?i~pcF#%jH#8|8x zp-2Fv$mthdNX4BE{V7k#C-?#90*aYsEHl%Lo4MPbfb14wW=6@-sXDHTa9VBKvYg-D z6{#fOz#@l8rn3T-Yk_Nba#U& zZV@MK>$AesGQ9t!&J#ApjrNGtMPccK@@e7*B<-2p&|Kwd`xK@6%%HPMH%35AwXiII z7SJ>LHhUZ!pCZ{_M9=Ec4CuiLmWy!vI%zRD#nZmA>7U_g+lEFeEhTsiwyk^FaKdRK zvTqfRh|s)+gdJhleB5vf|4NG!xXz@xI>I&m&1B>BbORk6!zI3>tWsLycH^M_W&p6v zDjf$(+D>k2HAbM*$?KvR0OalBrbZ(KI@z4WxHyeH0Fbr`>z@AD2%gXC2rE5=2*6_U ztA_^wAnTv`R5y@@4)lC{+bHrXn1oJlnu~0(`vJlf*2U`ebds@cCe2xhvfHp^Co|4q zsl7V{5VS(GJ??B#Z5vLMmzwxR1bURZ>Wm?ipYaMhf03N4hCS|NVXYfPl6#ovGR|Uf zf>GAjY77w{TkI+jGh6*hTKf9lr+1SK@5*Tm4pFqsI_Q()5K(TvlM*ur{YjddZ#Qoa z#BPt_nB`hqN2AF`(pWX-+;mr)t>D}bjq3kIf>In72ck-e_UHT!V(1Q*(jEKchsA`Cz3XgWc@T5Th8o3W zCZKsDz&_$806DYul$_&u#!yf#?})G!?*yTwTI5`LDTKwl9}yO6l@NtmNRG5p3KN7j zEd+#|p}WeVVD&~o&=2JaVk&!a5<#$&%vvTnFhj#J0mz)2%USq%yy~Flm=Le_ zLC_xj5`uF3b?FsI&$6FRnv9`f#nn_wiJ{O)r3*pXSyb7c4)k+Env)$0HP|G9q!l{F zgdoaKEsa$$6fQ~GVMwBXRE2iOJ8XHXZ>ec@)kvk!u$@+o_f$%ll^n&=o zo(Yvvpy%b=UzhU;d;V<<4ezYde?Cp-ejcKbD`*6cRj2ZxGGyk8kXeLTSeQkag_&8H zMVN(!S%g_wDuZO;cznN~DUc-)86x${KMfj1@#*t Gt)~LcJ3E~K literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_password.xml b/app/src/main/res/drawable/ic_password.xml new file mode 100644 index 0000000..1813549 --- /dev/null +++ b/app/src/main/res/drawable/ic_password.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/icon_update.xml b/app/src/main/res/drawable/icon_update.xml new file mode 100644 index 0000000..6be0972 --- /dev/null +++ b/app/src/main/res/drawable/icon_update.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/image_picker.xml b/app/src/main/res/drawable/image_picker.xml new file mode 100644 index 0000000..fd890ce --- /dev/null +++ b/app/src/main/res/drawable/image_picker.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/img_load.xml b/app/src/main/res/drawable/img_load.xml new file mode 100644 index 0000000..2102567 --- /dev/null +++ b/app/src/main/res/drawable/img_load.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/app/src/main/res/drawable/qc.png b/app/src/main/res/drawable/qc.png new file mode 100644 index 0000000000000000000000000000000000000000..b9d4e753bf0d73e4469070be49f1ff02340e90de GIT binary patch literal 4679 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 JPDHLkV1gEw6a@eP literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/rounded_btn_login.xml b/app/src/main/res/drawable/rounded_btn_login.xml new file mode 100644 index 0000000..b07de96 --- /dev/null +++ b/app/src/main/res/drawable/rounded_btn_login.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_white.xml b/app/src/main/res/drawable/rounded_white.xml new file mode 100644 index 0000000..6072ca7 --- /dev/null +++ b/app/src/main/res/drawable/rounded_white.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/tab_background_selector.xml b/app/src/main/res/drawable/tab_background_selector.xml new file mode 100644 index 0000000..14ae91f --- /dev/null +++ b/app/src/main/res/drawable/tab_background_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-sw800dp/activity_dashboard.xml b/app/src/main/res/layout-sw800dp/activity_dashboard.xml new file mode 100644 index 0000000..f3beb9e --- /dev/null +++ b/app/src/main/res/layout-sw800dp/activity_dashboard.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + +