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