commit eb02e85eb95d2b28457f1858fdbd38629c89933f Author: saad.siddiq Date: Tue Jan 27 10:08:35 2026 +0500 Initial commit 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 0000000..87cbe8f Binary files /dev/null and b/app/src/main/res/drawable/hse_login.jpg differ 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 0000000..77e5dad Binary files /dev/null and b/app/src/main/res/drawable/ic_launcher.webp differ 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 0000000..b9d4e75 Binary files /dev/null and b/app/src/main/res/drawable/qc.png differ 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 @@ + + + + + + + + + + + + + +