From b50037625f7e930f5e1218e605d564dd158b98ec Mon Sep 17 00:00:00 2001 From: "saad.siddiq" Date: Tue, 27 Jan 2026 18:06:15 +0500 Subject: [PATCH] Checkpoint Screen Design --- app/src/main/AndroidManifest.xml | 4 +- .../activities/DashboardActivity.java | 5 +- .../activities/QCTerryActivity.java | 73 ++++++ .../adapters/CheckPointAdapter.java | 3 +- .../qc_android/apiservice/RetrofitClient.java | 14 +- .../qc_android/db/DatabaseHelper.java | 36 +-- .../fragments/FragmentQcTerryTwo.java | 234 ++++++------------ .../qc_android/helper/Helper.java | 48 ++++ .../models/InspectionCheckPoint.java | 8 +- .../viewmodels/DataEntryViewModel.java | 16 ++ app/src/main/res/drawable/et_border.xml | 20 ++ .../res/layout-sw800dp/activity_login.xml | 4 +- .../main/res/layout-sw800dp/check_point.xml | 35 +-- .../custom_layout_for_image.xml | 6 +- .../layout-sw800dp/fragment_qc_terry_one.xml | 193 +++++++++------ .../layout-sw800dp/fragment_qc_terry_two.xml | 23 ++ app/src/main/res/xml/file_paths.xml | 2 +- 17 files changed, 419 insertions(+), 305 deletions(-) create mode 100644 app/src/main/res/drawable/et_border.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7aafaf6..227a17c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,7 +20,7 @@ tools:ignore="ScopedStorage" /> { if (Helper.isNetworkConnected(this)) { - + serviceViewModel.getCheckpointsData(this); } }); btnLogout.setOnClickListener(v -> {}); btnAppUpdate.setOnClickListener(v -> {}); btnReportUpload.setOnClickListener(v -> {}); - - - } public void initializeLayout() { 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 index bc58cd1..452ce48 100644 --- a/app/src/main/java/com/utopiaindustries/qc_android/activities/QCTerryActivity.java +++ b/app/src/main/java/com/utopiaindustries/qc_android/activities/QCTerryActivity.java @@ -1,26 +1,43 @@ package com.utopiaindustries.qc_android.activities; import android.os.Bundle; +import android.view.Window; +import android.view.WindowManager; 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.navigation.NavController; import androidx.navigation.Navigation; import com.utopiaindustries.qc_android.R; +import com.utopiaindustries.qc_android.db.CheckpointRepository; +import com.utopiaindustries.qc_android.models.InspectionCheckPoint; +import com.utopiaindustries.qc_android.viewmodels.DataEntryViewModel; + +import java.util.ArrayList; +import java.util.List; public class QCTerryActivity extends AppCompatActivity { private NavController navController; + private List checkPointList = new ArrayList<>(); + private DataEntryViewModel viewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_qcterry); + + 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); @@ -32,6 +49,9 @@ public class QCTerryActivity extends AppCompatActivity { private void initializeLayout() { + viewModel = new ViewModelProvider(this).get(DataEntryViewModel.class); + loadCheckPoints(); + // Setup Navigation navController = Navigation.findNavController(this, R.id.nav_host_fragment); } @@ -40,4 +60,57 @@ public class QCTerryActivity extends AppCompatActivity { public boolean onSupportNavigateUp() { return navController.navigateUp() || super.onSupportNavigateUp(); } + + private void loadCheckPoints() { + checkPointList.clear(); + + CheckpointRepository checkpointRepository = new CheckpointRepository(this); + checkPointList = checkpointRepository.findAll(); + + /*InspectionCheckPoint item1 = new InspectionCheckPoint(1, "Safety Helmet Check", "Safety"); + checkPointList.add(item1); + + InspectionCheckPoint item2 = new InspectionCheckPoint(2, "Fire Extinguisher Check", "Safety"); + checkPointList.add(item2); + + InspectionCheckPoint item3 = new InspectionCheckPoint(3, "Material Quality Inspection", "Quality"); + checkPointList.add(item3); + + InspectionCheckPoint item4 = new InspectionCheckPoint(4, "Weld Quality Check", "Quality"); + checkPointList.add(item4); + + InspectionCheckPoint item5 = new InspectionCheckPoint(5, "Electrical Panel Inspection", "Electrical"); + checkPointList.add(item5); + + InspectionCheckPoint item6 = new InspectionCheckPoint(6, "Wiring Safety Check", "Electrical"); + checkPointList.add(item6); + + InspectionCheckPoint item8 = new InspectionCheckPoint(8, "Machine Guarding Check", "Mechanical"); + checkPointList.add(item8); + + InspectionCheckPoint item9 = new InspectionCheckPoint(9, "Structural Integrity", "Civil"); + checkPointList.add(item9); + + for (int i = 11; i <= 15; i++) { + String[] categories = {"Safety", "Quality", "Electrical", "General"}; + String category = categories[(i - 11) % categories.length]; + + InspectionCheckPoint item = new InspectionCheckPoint(i, "Check Point " + i, category); + checkPointList.add(item); + }*/ + + viewModel.setCheckPointList(checkPointList); + } + + 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); + } } \ 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 index c68b217..0093df5 100644 --- a/app/src/main/java/com/utopiaindustries/qc_android/adapters/CheckPointAdapter.java +++ b/app/src/main/java/com/utopiaindustries/qc_android/adapters/CheckPointAdapter.java @@ -73,8 +73,7 @@ public class CheckPointAdapter extends RecyclerView.Adapter 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 @@ -246,6 +93,15 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On viewModel = new ViewModelProvider(requireActivity()).get(DataEntryViewModel.class); + checkPointList.addAll(viewModel.getCheckPointList().getValue()); + // Setup RecyclerView + recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); + adapter = new CheckPointAdapter(getActivity(), checkPointList, this); + recyclerView.setAdapter(adapter); + + // Initialize Activity Result Launchers + initializeLaunchers(); + // Load existing data /*if (viewModel.getAddress().getValue() != null) { edtAddress.setText(viewModel.getAddress().getValue()); @@ -254,6 +110,8 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On edtCity.setText(viewModel.getCity().getValue()); }*/ + + btnNext.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -272,6 +130,7 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On public void onImagePickerClicked(int position) { // Handle image picker click //openImagePicker(position); + alertForPictures(getActivity(), position); } @Override @@ -294,8 +153,12 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On @Override public void onRemarksChanged(int position, String remarks) { // Update remarks in the model + Log.e("Pos: ",""+position); + Log.e("Remarks: ",""+remarks); InspectionCheckPoint item = checkPointList.get(position); item.setRemarks(remarks); + viewModel.setCheckPointList(checkPointList); + Log.e("ViewModel: ",""+viewModel.getCheckPointList().getValue()); } @Override @@ -304,6 +167,7 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On InspectionCheckPoint item = checkPointList.get(position); item.setOkChecked(isOkChecked); item.setNoChecked(isNoChecked); + viewModel.setCheckPointList(checkPointList); } private void openImagePicker(int position) { @@ -313,7 +177,7 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On // adapter.addImageToCheckPoint(position, imageUri); } - public void alertForPictures(Context con) { + public void alertForPictures(Context con, int position) { ViewGroup viewGroup = requireActivity().findViewById(android.R.id.content); TextView dialogCameraBtn, dialogGalleryBtn; @@ -334,7 +198,7 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On public void onClick(View view) { alertDialog.dismiss(); - checkPermissionAndOpenCamera(); + checkPermissionAndOpenCamera(position); } }); @@ -343,7 +207,7 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On public void onClick(View view) { alertDialog.dismiss(); - checkPermissionAndOpenGallery(); + checkPermissionAndOpenGallery(position); } }); @@ -385,6 +249,25 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On if (result.getResultCode() == getActivity().RESULT_OK && photoUri != null) { //imageView.setImageURI(photoUri); viewModel.setSelectedImageUri(photoUri); + + Helper.uriToByteArrayAsync( + getContext(), + photoUri, + 100, + compressedImage -> { + requireActivity().runOnUiThread(() -> { + checkPointList.get(currentPosition).addImage(compressedImage); + adapter.notifyItemChanged(currentPosition); + viewModel.setCheckPointList(checkPointList); + currentPosition = -1; + }); + }, + error -> { + requireActivity().runOnUiThread(() -> { + Toast.makeText(getContext(), "Error compressing image: " + error.getMessage(), Toast.LENGTH_SHORT).show(); + }); + } + ); } }); @@ -395,6 +278,25 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On if (uri != null) { //imageView.setImageURI(uri); viewModel.setSelectedImageUri(uri); + + Helper.uriToByteArrayAsync( + getContext(), + uri, + 100, + compressedImage -> { + requireActivity().runOnUiThread(() -> { + checkPointList.get(currentPosition).addImage(compressedImage); + adapter.notifyItemChanged(currentPosition); + viewModel.setCheckPointList(checkPointList); + currentPosition = -1; + }); + }, + error -> { + requireActivity().runOnUiThread(() -> { + Toast.makeText(getContext(), "Error compressing image: " + error.getMessage(), Toast.LENGTH_SHORT).show(); + }); + } + ); } }); } @@ -402,8 +304,9 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On /** * Check permissions and open camera */ - private void checkPermissionAndOpenCamera() { + private void checkPermissionAndOpenCamera(int position) { String[] requiredPermissions = getRequiredCameraPermissions(); + currentPosition = position; if (hasPermissions(requiredPermissions)) { openCamera(); @@ -416,8 +319,9 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On /** * Check permissions and open gallery */ - private void checkPermissionAndOpenGallery() { + private void checkPermissionAndOpenGallery(int position) { String[] requiredPermissions = getRequiredGalleryPermissions(); + currentPosition = position; if (hasPermissions(requiredPermissions)) { openGallery(); @@ -499,9 +403,9 @@ public class FragmentQcTerryTwo extends Fragment implements CheckPointAdapter.On File storageDir = requireContext().getExternalFilesDir(null); File image = File.createTempFile( - imageFileName, /* prefix */ - ".jpg", /* suffix */ - storageDir /* directory */ + imageFileName, + ".jpg", + storageDir ); return image; 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 index e9d9d11..9a9da4b 100644 --- a/app/src/main/java/com/utopiaindustries/qc_android/helper/Helper.java +++ b/app/src/main/java/com/utopiaindustries/qc_android/helper/Helper.java @@ -25,6 +25,7 @@ import com.google.gson.reflect.TypeToken; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.lang.reflect.Type; @@ -456,4 +457,51 @@ public class Helper { } }); }*/ + + public static void uriToByteArrayAsync( + Context context, + Uri uri, + int targetSizeInKB, + Consumer onSuccess, + Consumer onError + ) { + new Thread(() -> { + try { + int targetSizeInBytes = targetSizeInKB * 1024; + + // Load the image as a Bitmap without scaling + Bitmap bitmap; + try (InputStream inputStream = context.getContentResolver().openInputStream(uri)) { + bitmap = BitmapFactory.decodeStream(inputStream); + } + + if (bitmap == null) { + throw new IOException("Failed to decode image from URI."); + } + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + int minQuality = 10; + int maxQuality = 100; + int quality = maxQuality; + + // Binary search for the best quality that meets the target size + while (minQuality <= maxQuality) { + byteArrayOutputStream.reset(); + bitmap.compress(Bitmap.CompressFormat.JPEG, quality, byteArrayOutputStream); + + int byteSize = byteArrayOutputStream.size(); + if (byteSize > targetSizeInBytes) { + maxQuality = quality - 1; + } else { + minQuality = quality + 1; + } + quality = (minQuality + maxQuality) / 2; + } + + onSuccess.accept(byteArrayOutputStream.toByteArray()); + } catch (IOException e) { + onError.accept(e); + } + }).start(); + } } 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 index f69a09d..66f5263 100644 --- a/app/src/main/java/com/utopiaindustries/qc_android/models/InspectionCheckPoint.java +++ b/app/src/main/java/com/utopiaindustries/qc_android/models/InspectionCheckPoint.java @@ -22,14 +22,14 @@ public class InspectionCheckPoint implements Serializable { public InspectionCheckPoint() { } - public InspectionCheckPoint(long id, String title, String category, boolean isOkChecked, boolean isNoChecked, String remarks, List images) { + public InspectionCheckPoint(long id, String title, String category) { this.id = id; this.title = title; this.category = category; this.isOkChecked = isOkChecked; this.isNoChecked = isNoChecked; this.remarks = remarks; - this.images = images; + this.images = (images != null) ? images : new ArrayList<>(); } public long getId() { @@ -113,6 +113,10 @@ public class InspectionCheckPoint implements Serializable { "id=" + id + ", title='" + title + '\'' + ", category='" + category + '\'' + + ", isOkChecked=" + isOkChecked + + ", isNoChecked=" + isNoChecked + + ", remarks='" + remarks + '\'' + + ", images=" + images + '}'; } } 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 index f317b4c..e410880 100644 --- a/app/src/main/java/com/utopiaindustries/qc_android/viewmodels/DataEntryViewModel.java +++ b/app/src/main/java/com/utopiaindustries/qc_android/viewmodels/DataEntryViewModel.java @@ -6,6 +6,11 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; +import com.utopiaindustries.qc_android.models.InspectionCheckPoint; + +import java.util.ArrayList; +import java.util.List; + public class DataEntryViewModel extends ViewModel { // Personal Info @@ -23,6 +28,9 @@ public class DataEntryViewModel extends ViewModel { private MutableLiveData selectedImageUri = new MutableLiveData<>(); + private MutableLiveData> checkPointList = + new MutableLiveData<>(new ArrayList<>()); + // Getters and Setters public LiveData getFirstName() { return firstName; @@ -87,4 +95,12 @@ public class DataEntryViewModel extends ViewModel { public void setSelectedImageUri(Uri uri) { selectedImageUri.setValue(uri); } + + public LiveData> getCheckPointList() { + return checkPointList; + } + + public void setCheckPointList(List list) { + checkPointList.setValue(list); + } } diff --git a/app/src/main/res/drawable/et_border.xml b/app/src/main/res/drawable/et_border.xml new file mode 100644 index 0000000..83309cc --- /dev/null +++ b/app/src/main/res/drawable/et_border.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-sw800dp/activity_login.xml b/app/src/main/res/layout-sw800dp/activity_login.xml index 29ba8ce..5ff9ebc 100644 --- a/app/src/main/res/layout-sw800dp/activity_login.xml +++ b/app/src/main/res/layout-sw800dp/activity_login.xml @@ -33,7 +33,7 @@ android:layout_marginBottom="20dp" android:padding="5dp" android:text="Login Now to Continue" - android:textSize="@dimen/_16sdp" /> + android:textSize="@dimen/_14sdp" /> + android:textSize="@dimen/_12sdp" /> + android:orientation="vertical"> + + - - @@ -47,10 +51,11 @@ + android:layout_height="wrap_content" + android:layout_margin="5dp" + android:background="@drawable/et_border" + android:hint="Remarks" + android:padding="10dp" /> diff --git a/app/src/main/res/layout-sw800dp/fragment_qc_terry_one.xml b/app/src/main/res/layout-sw800dp/fragment_qc_terry_one.xml index ca1fea1..8ca4088 100644 --- a/app/src/main/res/layout-sw800dp/fragment_qc_terry_one.xml +++ b/app/src/main/res/layout-sw800dp/fragment_qc_terry_one.xml @@ -1,109 +1,142 @@ - + android:layout_height="match_parent"> - - - + android:background="@color/theme_color" + android:minHeight="?attr/actionBarSize" + android:theme="?attr/actionBarTheme" > - - + android:text="QC - Terry" + android:layout_gravity="center" + android:gravity="center" + android:padding="5dp" + android:textColor="@color/white" + android:textSize="@dimen/_11sdp" /> + - + android:layout_height="match_parent" + android:layout_below="@+id/toolbar" + android:layout_above="@+id/btnNext" + android:layout_margin="5dp" + android:padding="10dp" + android:orientation="vertical"> - - - - - - - - - - - + android:layout_marginBottom="24dp" + android:text="Personal Information" + android:textSize="20sp" + android:textStyle="bold" /> -