diff --git a/app/build.gradle b/app/build.gradle index fbe29d5..42f64ef 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "com.utopiaindustries.qualitychecker" minSdk 24 targetSdk 34 - versionCode 3 - versionName "1.2" + versionCode 5 + versionName "1.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 38a9d46..f2df0f5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -61,17 +61,17 @@ - + android:exported="false" />--> - - + --> { openDatabase(); database.beginTransaction(); @@ -49,6 +51,14 @@ public class ReportRepository { values.put(DatabaseHelper.REPORT_COLUMN_SYNCED, wrapper.isSynced()); database.insertWithOnConflict(DatabaseHelper.REPORT_TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE); database.setTransactionSuccessful(); + + // Count total rows after insert + /*Cursor cursor = database.rawQuery("SELECT COUNT(*) FROM " + DatabaseHelper.REPORT_TABLE_NAME, null); + if (cursor != null && cursor.moveToFirst()) { + int count = cursor.getInt(0); + Log.e("Total Rows Inserted", String.valueOf(count)); + cursor.close(); + }*/ } catch (Exception e) { e.printStackTrace(); } finally { @@ -58,6 +68,29 @@ public class ReportRepository { }); } + public List getAllSavedPaths() { + List paths = new ArrayList<>(); + openDatabase(); + Cursor cursor = null; + try { + cursor = database.query( + DatabaseHelper.REPORT_TABLE_NAME, + new String[]{DatabaseHelper.REPORT_COLUMN_CONTENT}, + null, null, null, null, null + ); + if (cursor != null && cursor.moveToFirst()) { + do { + paths.add(cursor.getString(0)); + } while (cursor.moveToNext()); + } + } finally { + if (cursor != null) cursor.close(); + close(); + } + return paths; + } + + public void insertOrReplaceUser(InspectionReportWrapper wrapper) { executeSafely(() -> { openDatabase(); diff --git a/app/src/main/java/com/utopiaindustries/qualitychecker/models/InspectionReport.java b/app/src/main/java/com/utopiaindustries/qualitychecker/models/InspectionReport.java index f8aaf4b..0ef2c9d 100644 --- a/app/src/main/java/com/utopiaindustries/qualitychecker/models/InspectionReport.java +++ b/app/src/main/java/com/utopiaindustries/qualitychecker/models/InspectionReport.java @@ -3,6 +3,7 @@ package com.utopiaindustries.qualitychecker.models; import androidx.annotation.NonNull; import java.io.Serializable; +import java.time.LocalDate; import java.util.List; public class InspectionReport implements Serializable { @@ -23,6 +24,7 @@ public class InspectionReport implements Serializable { private String productionRepresentative; private String qcRepresentative; private String floor; + private String auditDate; // wrapper @@ -179,7 +181,13 @@ public class InspectionReport implements Serializable { this.filePath = filePath; } + public String getAuditDate() { + return auditDate; + } + public void setAuditDate(String auditDate) { + this.auditDate = auditDate; + } @Override public String toString() { @@ -201,6 +209,7 @@ public class InspectionReport implements Serializable { ", floor='" + floor + '\'' + ", items=" + items + ", filePath='" + filePath + '\'' + + ", auditDate='" + auditDate + '\'' + ", wrapperId=" + wrapperId + '}'; } diff --git a/app/src/main/java/com/utopiaindustries/qualitychecker/ui/activities/HomeActivity.java b/app/src/main/java/com/utopiaindustries/qualitychecker/ui/activities/HomeActivity.java index a5a3bf9..2de7d56 100644 --- a/app/src/main/java/com/utopiaindustries/qualitychecker/ui/activities/HomeActivity.java +++ b/app/src/main/java/com/utopiaindustries/qualitychecker/ui/activities/HomeActivity.java @@ -43,17 +43,21 @@ import com.utopiaindustries.qualitychecker.db.InspectionCheckpointSkuRepository; import com.utopiaindustries.qualitychecker.db.InspectionLabelRepository; import com.utopiaindustries.qualitychecker.db.ItemRepository; import com.utopiaindustries.qualitychecker.db.ProductRepository; +import com.utopiaindustries.qualitychecker.db.ReportRepository; import com.utopiaindustries.qualitychecker.models.EmployeePhoto; import com.utopiaindustries.qualitychecker.models.InspectionReport; +import com.utopiaindustries.qualitychecker.models.InspectionReportWrapper; import com.utopiaindustries.qualitychecker.models.callback.SaveCheckpointCallback; import com.utopiaindustries.qualitychecker.models.callback.SaveDefectCallback; import com.utopiaindustries.qualitychecker.models.callback.SaveDimensionCallback; import com.utopiaindustries.qualitychecker.models.callback.SaveInspectionLabelCallBack; import com.utopiaindustries.qualitychecker.models.callback.SaveItemCallback; import com.utopiaindustries.qualitychecker.models.callback.SaveProductCallBack; +import com.utopiaindustries.qualitychecker.models.callback.SaveReportCallback; import com.utopiaindustries.qualitychecker.notification.NotificationHelper; import com.utopiaindustries.qualitychecker.receiver.NetworkReceiver; import com.utopiaindustries.qualitychecker.service.InspectionReportService; +import com.utopiaindustries.qualitychecker.service.NetworkService; import com.utopiaindustries.qualitychecker.store.Store; import com.utopiaindustries.qualitychecker.ui.activities.fragments.ReportsFragment; import com.utopiaindustries.qualitychecker.ui.adapter.PagerAdapter; @@ -63,6 +67,9 @@ import com.utopiaindustries.qualitychecker.utils.FileUtils; import com.utopiaindustries.qualitychecker.utils.PropertyReader; import com.utopiaindustries.qualitychecker.utils.Release; +import java.io.ByteArrayInputStream; +import java.io.ObjectInputStream; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Properties; @@ -73,7 +80,7 @@ import retrofit2.Response; public class HomeActivity extends AppCompatActivity implements View.OnClickListener { - private Button createReportBtn,refreshReportsBtn,logoutBtn,fetchProductBtn, appUpdateBtn; + private Button createReportBtn,refreshReportsBtn,logoutBtn,fetchProductBtn, appUpdateBtn, reportsUploadBtn; //private RecyclerView recyclerView; private ApiService apiService; private TextView usernameTitle;//, emptyReportTextView; @@ -115,6 +122,7 @@ public class HomeActivity extends AppCompatActivity implements View.OnClickListe logoutBtn = findViewById( R.id.logout_btn ); fetchProductBtn = findViewById( R.id.fetch_product_btn ); appUpdateBtn = findViewById(R.id.app_update_btn); + reportsUploadBtn = findViewById(R.id.reports_upload_btn); progressBar = findViewById(R.id.progressBar); //recyclerView = findViewById( R.id.reports_recyclerview ); usernameTitle = findViewById( R.id.username_title ); @@ -138,6 +146,7 @@ public class HomeActivity extends AppCompatActivity implements View.OnClickListe logoutBtn.setOnClickListener( this ); fetchProductBtn.setOnClickListener( this ); appUpdateBtn.setOnClickListener( this ); + reportsUploadBtn.setOnClickListener( this ); //recyclerView.setLayoutManager( new LinearLayoutManager( this ) ); @@ -228,6 +237,17 @@ public class HomeActivity extends AppCompatActivity implements View.OnClickListe } } + + if ( id == R.id.reports_upload_btn) { + + if (!FileUtils.isNetworkConnected(this)) { + Toast.makeText(this, "No Internet Connection Available", Toast.LENGTH_LONG).show(); + } + else { + pushDataToInternet(); + } + + } } private int generateRandomNumber(int min, int max) { @@ -480,21 +500,21 @@ private int generateRandomNumber(int min, int max) { @Override protected void onStart() { super.onStart(); - if (networkReceiver == null) { + /*if (networkReceiver == null) { networkReceiver = new NetworkReceiver(); } IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(networkReceiver, filter); - isReceiverRegistered = true; // Mark the receiver as registered + isReceiverRegistered = true; */ // Mark the receiver as registered } @Override protected void onStop() { super.onStop(); - if (isReceiverRegistered) { + /*if (isReceiverRegistered) { unregisterReceiver(networkReceiver); isReceiverRegistered = false; // Mark the receiver as unregistered - } + }*/ } /** @@ -555,4 +575,90 @@ private int generateRandomNumber(int min, int max) { } } ); } + + @SuppressLint("DefaultLocale") + private void pushDataToInternet() { + ReportRepository repository = new ReportRepository( this ); + List reportWrappers = repository.findAllUnsynced(); + InspectionReportService service = InspectionReportService.getInstance(); + try { + List reports = getReports( reportWrappers ); + int size = reports.size(); + if( size > 0 ) { + Toast.makeText(this, "Uploading Reports. Please wait", Toast.LENGTH_LONG).show(); + reportsUploadBtn.setEnabled(false); + final int[] uploadedCount = {0}; + + for ( InspectionReport report : reports ){ + service.saveReport(report, new SaveReportCallback() { + @Override + public void onSuccess() { + // update status + repository.updateSyncStatus( report.getWrapperId() ); + // remove file + FileUtils.deleteFile( report.getFilePath() ); + + uploadedCount[0]++; + + NotificationHelper.showNotification( + HomeActivity.this, + "Report Uploading", + "Uploaded " + uploadedCount[0] + " of " + size + " reports"); + + //Log.e("Reports-size: ",""+reports.size()); + if (uploadedCount[0] == size) { + Toast.makeText(HomeActivity.this, "All reports uploaded successfully", Toast.LENGTH_SHORT).show(); + reportsUploadBtn.setEnabled(true); + } + } + @Override + public void onFailure(Throwable throwable) { + uploadedCount[0]++; + // Even on failure, increment the counter to avoid locking the button forever + + NotificationHelper.showNotification( + HomeActivity.this, + "Report Uploading", + "Uploaded " + uploadedCount[0] + " of " + size + " reports (with some errors)"); + + + if (uploadedCount[0] == size) { + reportsUploadBtn.setEnabled(true); + Toast.makeText(HomeActivity.this, "Finished uploading with some errors", Toast.LENGTH_SHORT).show(); + } + } + }); + } + } + else { + Toast.makeText(this, "No Reports to upload", Toast.LENGTH_SHORT).show(); + } + } catch ( Exception e ) { + e.printStackTrace(); + } + } + + /* + * map wrapper to reports + * */ + 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() ); + 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/qualitychecker/ui/fragments/ThirdStepFragment.java b/app/src/main/java/com/utopiaindustries/qualitychecker/ui/fragments/ThirdStepFragment.java index 183d060..b887f76 100644 --- a/app/src/main/java/com/utopiaindustries/qualitychecker/ui/fragments/ThirdStepFragment.java +++ b/app/src/main/java/com/utopiaindustries/qualitychecker/ui/fragments/ThirdStepFragment.java @@ -5,6 +5,7 @@ import static android.app.Activity.RESULT_OK; import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; +import android.app.DatePickerDialog; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -25,6 +26,7 @@ import android.view.WindowManager; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; +import android.widget.DatePicker; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ImageView; @@ -67,9 +69,13 @@ import com.utopiaindustries.qualitychecker.utils.FileUtils; import com.utopiaindustries.qualitychecker.utils.ImageUriHolder; import java.io.File; +import java.text.SimpleDateFormat; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -104,6 +110,11 @@ public class ThirdStepFragment extends Fragment implements View.OnClickListener ArrayList sectionArrayList = new ArrayList<>(); private EditText etHandFeelNotOk, etHandFeelOk, etHandFeelPercent; + private ImageView imgCalendar; + private TextView txtDate; + private int year, month, day; + private Calendar calendar; + private LocalDate localDate; @Nullable @Override @@ -302,9 +313,36 @@ public class ThirdStepFragment extends Fragment implements View.OnClickListener } }); + imgCalendar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showDatePickerDialog(getActivity()); + } + }); + return view; } + private void showDatePickerDialog(Context context) { + DatePickerDialog datePickerDialog = new DatePickerDialog( + context, + new DatePickerDialog.OnDateSetListener() { + @Override + public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { + // Month is 0-indexed, so we add 1 + Calendar selectedCalendar = Calendar.getInstance(); + selectedCalendar.set(year, monthOfYear, dayOfMonth); + SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy", Locale.getDefault()); + String selectedDate = dateFormat.format(selectedCalendar.getTime()); + txtDate.setText(selectedDate); + store.getReport().setAuditDate(selectedDate); + } + }, + year, month, day + ); + datePickerDialog.show(); + } + public void onImagePickerResult(int requestCode, int resultCode, Intent data) { if (requestCode == CAMERA_REQUEST) { Uri selectedImageUri = ImageUriHolder.getInstance().getImageUri(); @@ -633,6 +671,7 @@ public class ThirdStepFragment extends Fragment implements View.OnClickListener sectionSpinner.setSelection(index); etHandFeelNotOk.setText(String.valueOf(store.getReport().getItems().get(0).getHandFeelNotOkay())); + txtDate.setText(store.getReport().getAuditDate()); } } @@ -714,6 +753,14 @@ public class ThirdStepFragment extends Fragment implements View.OnClickListener txtMinor = view.findViewById(R.id.txt_minor); etFloor = view.findViewById(R.id.et_floor); + calendar = Calendar.getInstance(); + year = calendar.get(Calendar.YEAR); + month = calendar.get(Calendar.MONTH); + day = calendar.get(Calendar.DAY_OF_MONTH); + + imgCalendar = view.findViewById(R.id.img_calendar); + txtDate = view.findViewById(R.id.txt_date); + etHandFeelNotOk = view.findViewById(R.id.et_hand_feel_not_ok); etHandFeelOk = view.findViewById(R.id.et_hand_feel_ok); etHandFeelPercent = view.findViewById(R.id.et_not_ok_percent); diff --git a/app/src/main/java/com/utopiaindustries/qualitychecker/utils/FileUtils.java b/app/src/main/java/com/utopiaindustries/qualitychecker/utils/FileUtils.java index c1fded8..1382ee9 100644 --- a/app/src/main/java/com/utopiaindustries/qualitychecker/utils/FileUtils.java +++ b/app/src/main/java/com/utopiaindustries/qualitychecker/utils/FileUtils.java @@ -207,6 +207,20 @@ public class FileUtils { }).start(); } + public static byte[] readFileToByteArray(String path) throws IOException { + File file = new File(path); + FileInputStream fis = new FileInputStream(file); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = fis.read(buffer)) != -1) { + bos.write(buffer, 0, bytesRead); + } + fis.close(); + return bos.toByteArray(); + } + + /** * is null or empty */ diff --git a/app/src/main/res/drawable/icon_calendar.xml b/app/src/main/res/drawable/icon_calendar.xml new file mode 100644 index 0000000..0f090ec --- /dev/null +++ b/app/src/main/res/drawable/icon_calendar.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 2305fc7..9fd6f0e 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -90,10 +90,10 @@ android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="wrap_content" - app:tabMaxWidth="0dp" + android:layout_below="@+id/layout_heading" app:tabGravity="fill" - app:tabMode="fixed" - android:layout_below="@+id/layout_heading" /> + app:tabMaxWidth="0dp" + app:tabMode="fixed" />