diff --git a/app/src/main/java/com/pbd/psi/ui/scan/ScanFragment.kt b/app/src/main/java/com/pbd/psi/ui/scan/ScanFragment.kt index f5166de942227f98b01871c1b63621def478f40a..f34f6ec8913a7ca3854fa2bae9eb437ad3d4ec77 100644 --- a/app/src/main/java/com/pbd/psi/ui/scan/ScanFragment.kt +++ b/app/src/main/java/com/pbd/psi/ui/scan/ScanFragment.kt @@ -1,60 +1,162 @@ package com.pbd.psi.ui.scan +import android.Manifest +import android.app.Activity +import android.app.AlertDialog +import android.content.Intent +import android.graphics.BitmapFactory +import android.graphics.drawable.BitmapDrawable +import android.net.Uri import android.os.Bundle -import androidx.fragment.app.Fragment +import android.os.Handler +import android.os.Looper +import android.provider.MediaStore +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import com.pbd.psi.R - -// TODO: Rename parameter arguments, choose names that match -// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER -private const val ARG_PARAM1 = "param1" -private const val ARG_PARAM2 = "param2" - -/** - * A simple [Fragment] subclass. - * Use the [ScanFragment.newInstance] factory method to - * create an instance of this fragment. - */ +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.camera.core.CameraSelector +import androidx.camera.core.ImageCapture +import androidx.camera.core.Preview +import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import com.pbd.psi.databinding.FragmentScanBinding + class ScanFragment : Fragment() { - // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { - param1 = it.getString(ARG_PARAM1) - param2 = it.getString(ARG_PARAM2) + + private var _binding: FragmentScanBinding? = null + private val binding get() = _binding!! + + private var imageCapture: ImageCapture? = null + + private var previewFrozen: Boolean = false + private var cameraProvider: ProcessCameraProvider? = null + + private val filePickerLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode == Activity.RESULT_OK) { + val selectedImageUri = result.data?.data + selectedImageUri?.let { uri -> + loadSelectedImage(uri) + } } } override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, + inflater: LayoutInflater, + container: ViewGroup?, savedInstanceState: Bundle? - ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_scan, container, false) + ): View { + _binding = FragmentScanBinding.inflate(inflater, container, false) + val root: View = binding.root + + val permissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted -> + if (isGranted) { + startCamera() + } + } + + permissionLauncher.launch(Manifest.permission.CAMERA) + + imageCapture = ImageCapture.Builder().build() + + binding.scanButton?.setOnClickListener { + previewMask() + } + + binding.uploadImage?.setOnClickListener{ + openFileExplorer() + } + + return root + } + + private fun startCamera() { + val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext()) + cameraProviderFuture.addListener({ + + cameraProvider = cameraProviderFuture.get() + + val preview = Preview.Builder().build().also { + mPreview -> + if (!previewFrozen) { + mPreview.setSurfaceProvider(binding.scanView?.surfaceProvider) + } else { + mPreview.setSurfaceProvider(null) + } + } + + imageCapture = ImageCapture.Builder().build() + + val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA + + try { + cameraProvider?.unbindAll() + cameraProvider?.bindToLifecycle(this, cameraSelector, preview!!) + } catch (e: Exception) { + Log.d("CameraX", "startCamera Failed:", e) + } + }, ContextCompat.getMainExecutor(requireContext())) + } + + private fun previewMask() { + binding.scanButton?.isEnabled = false + previewFrozen = !previewFrozen + startCamera() + + Handler(Looper.getMainLooper()).postDelayed({ + // TO DO: Add modal + val builder: AlertDialog.Builder = AlertDialog.Builder(context) + builder + .setMessage("Do you want to retake the picture?") + .setPositiveButton("Yes") { dialog, which -> + dialog.dismiss() + binding.scanButton?.isEnabled = true + previewFrozen = !previewFrozen + startCamera() + } + .setNegativeButton("No") { dialog, which -> + // TO DO : send a froze preview to the server as img form data + dialog.dismiss() + } + + val dialog: AlertDialog = builder.create() + dialog.show() + }, 1000) + } + + private fun openFileExplorer() { + val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) + filePickerLauncher.launch(intent) } - companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment ScanFragment. - */ - // TODO: Rename and change types and number of parameters - @JvmStatic - fun newInstance(param1: String, param2: String) = - ScanFragment().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) + private fun loadSelectedImage(uri: Uri) { + try { + requireContext().contentResolver.openInputStream(uri)?.use { inputStream -> + val bitmap = BitmapFactory.decodeStream(inputStream) + if (bitmap != null) { + val drawable = BitmapDrawable(resources, bitmap) + binding.scanView?.background = drawable + } else { + Toast.makeText(requireContext(), "Failed to decode image", Toast.LENGTH_SHORT).show() + Log.e("ScanFragment", "Failed to decode image") } } + } catch (e: Exception) { + Toast.makeText(requireContext(), "Failed to load image", Toast.LENGTH_SHORT).show() + Log.e("ScanFragment", "Failed to load image", e) + } + } + + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } } \ No newline at end of file diff --git a/app/src/main/java/com/pbd/psi/ui/scan/ScanViewModel.kt b/app/src/main/java/com/pbd/psi/ui/scan/ScanViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..3fb51289d9b5c101507e565037bb6080cd060eeb --- /dev/null +++ b/app/src/main/java/com/pbd/psi/ui/scan/ScanViewModel.kt @@ -0,0 +1,13 @@ +package com.pbd.psi.ui.twibbon + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel + +class ScanViewModel : ViewModel() { + + private val _text = MutableLiveData<String>().apply { + value = "This is Scan Fragment" + } + val text: LiveData<String> = _text +} \ No newline at end of file diff --git a/app/src/main/res/drawable/scan_button.png b/app/src/main/res/drawable/scan_button.png new file mode 100644 index 0000000000000000000000000000000000000000..f3a65e62f1681264fc21b5a5ac2a2bfce2b3548d Binary files /dev/null and b/app/src/main/res/drawable/scan_button.png differ diff --git a/app/src/main/res/layout-land/fragment_twibbon.xml b/app/src/main/res/layout-land/fragment_twibbon.xml index c00eeb41d7243591caf0e239ae8ef5061cc79e31..4e4ec756a535c32519fd005ddfd3792af40a0d2f 100644 --- a/app/src/main/res/layout-land/fragment_twibbon.xml +++ b/app/src/main/res/layout-land/fragment_twibbon.xml @@ -64,7 +64,7 @@ android:background="@drawable/capture_button" android:layout_marginTop="10dp" android:layout_marginLeft="40dp" - android:layout_width="142dip" + android:layout_width="159dip" android:layout_height="52dip" android:contentDescription="@string/capture" app:layout_constraintEnd_toStartOf="@id/changeButton" @@ -78,7 +78,7 @@ android:background="@drawable/change_button" android:layout_marginTop="10dp" android:layout_marginRight="40dp" - android:layout_width="142dip" + android:layout_width="159dip" android:layout_height="52dip" android:contentDescription="@string/change" app:layout_constraintEnd_toEndOf="parent" diff --git a/app/src/main/res/layout/activity_scan.xml b/app/src/main/res/layout/activity_scan.xml new file mode 100644 index 0000000000000000000000000000000000000000..40df5896cc70ce54ccf7fadc1927e1a251111386 --- /dev/null +++ b/app/src/main/res/layout/activity_scan.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/payment" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingTop="0dp" + tools:context=".ScanActivity"> + + <FrameLayout + android:id="@+id/placeholderPayment" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginStart="0dp" + android:layout_marginEnd="0dp" + android:background="?android:attr/windowBackground" + app:itemBackground="@color/primaryDarkest" + app:itemIconTint="@color/white" + app:itemTextColor="@color/white" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent"> + </FrameLayout> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_scan.xml b/app/src/main/res/layout/dialog_scan.xml new file mode 100644 index 0000000000000000000000000000000000000000..30de4c256c33bfa8409dfb9690f99b5f1736255a --- /dev/null +++ b/app/src/main/res/layout/dialog_scan.xml @@ -0,0 +1,41 @@ +<!-- res/layout/dialog_retake_image.xml --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="16dp" + android:id="@+id/dialog_scan"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Do you want to retake the image?" + android:textSize="18sp" + android:textColor="@android:color/black" + android:layout_gravity="center_horizontal"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="16dp"> + + <Button + android:id="@+id/btnRetake" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Retake" + android:textColor="@android:color/white" + android:background="@color/primaryDarkest"/> + + <Button + android:id="@+id/btnUse" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Use" + android:textColor="@android:color/white" + android:background="@color/primaryDarkest"/> + </LinearLayout> +</LinearLayout> diff --git a/app/src/main/res/layout/fragment_scan.xml b/app/src/main/res/layout/fragment_scan.xml index bab875d893ec10839f491695a4f5470e410a7dbe..4c64d8b9e6a4fc27928dbf6257423abc60061c28 100644 --- a/app/src/main/res/layout/fragment_scan.xml +++ b/app/src/main/res/layout/fragment_scan.xml @@ -52,6 +52,19 @@ android:layout_width="50dip" android:layout_height="50dip" android:contentDescription="@string/upload_image" + app:layout_constraintStart_toEndOf="@id/scan_button" + app:layout_constraintTop_toBottomOf="@+id/guideline"/> + + <ImageButton + android:id="@+id/scan_button" + android:layout_marginVertical="20dp" + android:layout_marginStart="10dp" + android:fontFamily="@font/montserrat" + android:gravity="center_horizontal" + android:background="@drawable/scan_button" + android:layout_width="50dip" + android:layout_height="50dip" + android:contentDescription="@string/upload_image" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/guideline"/> @@ -78,7 +91,13 @@ app:layout_constraintBottom_toTopOf="@+id/guideline" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/scan_header" /> + app:layout_constraintTop_toBottomOf="@+id/scan_header"> + + <androidx.camera.view.PreviewView + android:id="@+id/scan_view" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + </FrameLayout> </androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_twibbon.xml b/app/src/main/res/layout/fragment_twibbon.xml index 975295f0aeb71d866a51e1093b8794dcc8c5cf9e..3adadb30167fda16cf229e0544dcacb334505e89 100644 --- a/app/src/main/res/layout/fragment_twibbon.xml +++ b/app/src/main/res/layout/fragment_twibbon.xml @@ -66,7 +66,7 @@ android:gravity="center_horizontal" android:background="@drawable/capture_button" android:layout_marginTop="10dp" - android:layout_width="142dip" + android:layout_width="159dip" android:layout_height="52dip" android:contentDescription="@string/capture" app:layout_constraintEnd_toStartOf="@id/changeButton" @@ -79,7 +79,7 @@ android:gravity="center_horizontal" android:background="@drawable/change_button" android:layout_marginTop="10dp" - android:layout_width="142dip" + android:layout_width="159dip" android:layout_height="52dip" android:contentDescription="@string/change" app:layout_constraintEnd_toEndOf="parent"