From d9c4aa6b4c1c36392508e69f57743d9df4b4f99d Mon Sep 17 00:00:00 2001 From: Agsha Athalla Nurkareem <agshaathalla@gmail.com> Date: Sun, 31 Mar 2024 00:42:59 +0700 Subject: [PATCH] feat: twibbon page --- .../java/com/example/bondoman/ScanPage.kt | 11 ++ .../java/com/example/bondoman/TwibbonPage.kt | 187 ++++++++++++++++++ .../res/layout/fragment_header_twibbon.xml | 20 ++ .../main/res/layout/fragment_scan_page.xml | 32 ++- .../main/res/layout/fragment_twibbon_page.xml | 102 ++++++++++ 5 files changed, 349 insertions(+), 3 deletions(-) create mode 100644 bondoman/app/src/main/java/com/example/bondoman/TwibbonPage.kt create mode 100644 bondoman/app/src/main/res/layout/fragment_header_twibbon.xml create mode 100644 bondoman/app/src/main/res/layout/fragment_twibbon_page.xml diff --git a/bondoman/app/src/main/java/com/example/bondoman/ScanPage.kt b/bondoman/app/src/main/java/com/example/bondoman/ScanPage.kt index 5342507..98c5cdc 100644 --- a/bondoman/app/src/main/java/com/example/bondoman/ScanPage.kt +++ b/bondoman/app/src/main/java/com/example/bondoman/ScanPage.kt @@ -81,6 +81,17 @@ class ScanPage : Fragment() { binding.selectButton.setOnClickListener { selectPhoto() } + + binding.twibbonText.setOnClickListener { + val twibbonPage = TwibbonPage() + val headerTwibbon = HeaderScan() + val mainActivity = activity as? MainActivity + if (mainActivity != null) { + mainActivity.replaceFragment(twibbonPage, headerTwibbon) + } else { + Log.e("ScanPage", "Activity is null. Cannot cast to MainActivity.") + } + } } private fun selectPhoto() { diff --git a/bondoman/app/src/main/java/com/example/bondoman/TwibbonPage.kt b/bondoman/app/src/main/java/com/example/bondoman/TwibbonPage.kt new file mode 100644 index 0000000..12dda81 --- /dev/null +++ b/bondoman/app/src/main/java/com/example/bondoman/TwibbonPage.kt @@ -0,0 +1,187 @@ +package com.example.bondoman + +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Matrix +import android.os.Build +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.camera.core.CameraSelector +import androidx.camera.core.ImageCapture +import androidx.camera.core.ImageCaptureException +import androidx.camera.core.ImageProxy +import androidx.camera.core.Preview +import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import com.example.bondoman.databinding.FragmentTwibbonPageBinding + +class TwibbonPage : Fragment() { + + private var _binding: FragmentTwibbonPageBinding? = null + private val binding get() = _binding!! + private var imageCapture: ImageCapture? = null + private var cameraOn = false + private var currentCameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + _binding = FragmentTwibbonPageBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.twibbonText.visibility = View.GONE + if (allPermissionsGranted()) { + startCamera() + cameraOn = true + } else { + ActivityCompat.requestPermissions( + requireActivity(), REQUIRED_PERMISSIONS, 10 + ) + } + + binding.flipCamera.setOnClickListener { + currentCameraSelector = if (currentCameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) { + CameraSelector.DEFAULT_FRONT_CAMERA + } else { + CameraSelector.DEFAULT_BACK_CAMERA + } + startCamera() + } + + + binding.backButton.setOnClickListener { + val scanPage = ScanPage() + val headerTwibbon = HeaderScan() + val mainActivity = activity as? MainActivity + if (mainActivity != null) { + mainActivity.replaceFragment(scanPage, headerTwibbon) + } else { + Log.e("ScanPage", "Activity is null. Cannot cast to MainActivity.") + } + } + + binding.shutterButton.setOnClickListener { + if (cameraOn){ + takePhoto() + } else { + cameraOn = true + binding.hasilFoto.visibility = View.GONE + binding.previewView.visibility = View.VISIBLE + binding.twibbonText.visibility = View.GONE + binding.flipCamera.visibility = View.VISIBLE + Log.d("Camera", "visibility a: ${binding.hasilFoto.visibility}") + } + } + + } + + override fun onRequestPermissionsResult( + requestCode: Int, permissions: Array<String>, grantResults: + IntArray) { + if (requestCode == 10) { + if (allPermissionsGranted()) { + startCamera() + } else { + Log.d("Permission", "Permissions not granted by the user.") + Toast.makeText(requireContext(), "Permissions not granted by the user.", Toast.LENGTH_SHORT).show() + } + } + } + + private fun takePhoto() { + val imageCapture = imageCapture ?: return + + imageCapture.takePicture( + ContextCompat.getMainExecutor(requireContext()), + object : ImageCapture.OnImageCapturedCallback() { + override fun onError(exception: ImageCaptureException) { + Log.e("Camera", "Photo capture failed: ${exception.message}", exception) + } + + override fun onCaptureSuccess(image: ImageProxy) { + val buffer = image.planes[0].buffer + val bytes = ByteArray(buffer.remaining()) + buffer.get(bytes) + val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) + binding.hasilFoto.setImageBitmap(bitmap) + binding.hasilFoto.visibility = View.VISIBLE + binding.previewView.visibility = View.GONE + binding.twibbonText.visibility = View.VISIBLE + binding.flipCamera.visibility = View.GONE + Log.d("Camera", "visibility b: ${binding.hasilFoto.visibility}") + + if(currentCameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA){ + val rotationMat = Matrix().apply { postRotate(270f) } + val flipMat = Matrix().apply { postScale(-1f, 1f) } + val rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, rotationMat, true) + val flippedBitmap = Bitmap.createBitmap(rotatedBitmap, 0, 0, rotatedBitmap.width, rotatedBitmap.height, flipMat, true) + binding.hasilFoto.setImageBitmap(flippedBitmap) + } else { + val rotationMat = Matrix().apply { postRotate(90f) } + val rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, rotationMat, true) + binding.hasilFoto.setImageBitmap(rotatedBitmap) + } + + image.close() + cameraOn = false + } + } + ) + + } + + private fun startCamera() { + val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext()) + + cameraProviderFuture.addListener(Runnable { + val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() + + val preview = Preview.Builder().build().also { + it.setSurfaceProvider(binding.previewView.surfaceProvider) + } + + imageCapture = ImageCapture.Builder().build() + + try { + cameraProvider.unbindAll() + cameraProvider.bindToLifecycle( + this, currentCameraSelector, preview, imageCapture) + + } catch(exc: Exception) { + Log.e("Camera", "Use case binding failed", exc) + } + + }, ContextCompat.getMainExecutor(requireContext())) + } + + private fun allPermissionsGranted()= REQUIRED_PERMISSIONS.all { + ContextCompat.checkSelfPermission( + requireContext(), it + ) == PackageManager.PERMISSION_GRANTED + } + + override fun onDestroyView() { + super.onDestroyView() + } + + companion object { + private val REQUIRED_PERMISSIONS = + mutableListOf ( + android.Manifest.permission.CAMERA + ).apply { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { + add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) + } + }.toTypedArray() + } +} \ No newline at end of file diff --git a/bondoman/app/src/main/res/layout/fragment_header_twibbon.xml b/bondoman/app/src/main/res/layout/fragment_header_twibbon.xml new file mode 100644 index 0000000..3c6db3e --- /dev/null +++ b/bondoman/app/src/main/res/layout/fragment_header_twibbon.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".HeaderScan"> + + <TextView + android:id="@+id/header_scan" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/twibbon_header" + android:fontFamily="@font/font_poppins" + android:textColor="@color/secondary" + style="bold" + android:layout_gravity="center_horizontal" + android:layout_marginTop="70sp" + android:layout_marginBottom="10dp" + android:textSize="32sp"/> +</FrameLayout> \ No newline at end of file diff --git a/bondoman/app/src/main/res/layout/fragment_scan_page.xml b/bondoman/app/src/main/res/layout/fragment_scan_page.xml index 7965f78..baf92b3 100644 --- a/bondoman/app/src/main/res/layout/fragment_scan_page.xml +++ b/bondoman/app/src/main/res/layout/fragment_scan_page.xml @@ -36,22 +36,48 @@ android:id="@+id/shutter_button" android:layout_width="64dp" android:layout_height="64dp" - android:layout_marginTop="50dp" + android:layout_marginTop="40dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/layout_scan" android:background="@drawable/shutter_button" + android:contentDescription="@string/scan_take_picture" /> <Button android:id="@+id/select_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="40dp" - android:text=" select photos " + android:layout_marginTop="20dp" + android:text="@string/select_photos" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/shutter_button" android:background="@drawable/select_button"/> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/twibbon_text" + app:layout_constraintTop_toBottomOf="@id/select_button" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + android:layout_marginTop="18dp" + android:clickable="true"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/scan_to_twibbon_1" + android:fontFamily="@font/font_poppins" + android:textColor="@color/black" + android:textSize="14sp"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/scan_to_twibbon_2" + android:fontFamily="@font/font_poppins" + android:textColor="#0000EE" + android:textSize="14sp"/> + </LinearLayout> + </androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/bondoman/app/src/main/res/layout/fragment_twibbon_page.xml b/bondoman/app/src/main/res/layout/fragment_twibbon_page.xml new file mode 100644 index 0000000..65cf347 --- /dev/null +++ b/bondoman/app/src/main/res/layout/fragment_twibbon_page.xml @@ -0,0 +1,102 @@ +<?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:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/frameLayout" + android:background="@color/primary" + android:pointerIcon="none" + tools:context=".TwibbonPage"> + + <include + android:id="@+id/header_layout" + layout="@layout/fragment_header_twibbon" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:layout_constraintTop_toTopOf="parent" + /> + + <ImageButton + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/back_button" + android:src="@drawable/arrow_back" + android:background="@android:color/transparent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + android:layout_margin="20dp" + android:contentDescription="@string/twibbon_back" + /> + + <FrameLayout + android:id="@+id/layout_scan" + android:layout_width="270dp" + android:layout_height="270dp" + app:layout_constraintTop_toBottomOf="@id/header_layout" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + android:layout_marginTop="40dp" + android:orientation="vertical" + android:layout_gravity="center" + > + <androidx.camera.view.PreviewView + android:id="@+id/previewView" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + <ImageView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/hasil_Foto" + android:src="@drawable/twibbon_transparent" + android:scaleType="centerCrop" + android:contentDescription="@string/twibbon_frame"/> + + <ImageView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/twibbon_border" + android:src="@drawable/twibbon_transparent" + android:scaleType="centerCrop" + android:contentDescription="@string/twibbon_frame"/> + + </FrameLayout> + + <Button + android:id="@+id/shutter_button" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_marginTop="40dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/layout_scan" + android:background="@drawable/shutter_button" + android:contentDescription="@string/twibbon_take_picture" + /> + + <ImageButton + android:id="@+id/flip_camera" + android:layout_width="50dp" + android:layout_height="50dp" + android:layout_marginStart="16dp" + android:layout_marginTop="46dp" + android:background="@android:color/transparent" + android:contentDescription="@string/twibbon_flip_camera" + android:src="@drawable/change_camera" + app:layout_constraintStart_toEndOf="@id/shutter_button" + app:layout_constraintTop_toBottomOf="@id/layout_scan" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@id/twibbon_text" + app:layout_constraintTop_toBottomOf="@id/shutter_button" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + android:layout_marginTop="20dp" + android:text="@string/twibbon_after_take" + android:fontFamily="@font/font_poppins" + android:textColor="@color/secondary"/> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file -- GitLab