From 03f12dbacf24b9add1fd79823d55f0753502360e Mon Sep 17 00:00:00 2001
From: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date: Mon, 1 Apr 2024 21:48:01 +0700
Subject: [PATCH] Squashed commit of the following:

commit 922c1801d45d8f12544e7ce4b029769fd539053f
Merge: 7e7808a 681fd98
Author: Nigel  Sahl <13521043@std.stei.itb.ac.id>
Date:   Mon Apr 1 13:53:23 2024 +0000

    Merge branch 'feature/gmail' into 'main'

    Feature/gmail

    See merge request NerbFox/if3210-2024-android-nos!13

commit 681fd98b8d4bf4efc1346ab44a1bda5f55db0df3
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Mon Apr 1 21:01:23 2024 +0700

    feat: intent gmail for send transaction

commit 7e7808a6a52f45fdfcc364ef0d5b11c0bd6c2013
Merge: 42a52dc 836f08e
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Mon Apr 1 10:16:33 2024 +0000

    Merge branch 'feature/save-transaction' into 'main'

    Feature/save transaction

    See merge request NerbFox/if3210-2024-android-nos!12

commit 836f08eb5752c6af82df0f3b0017192867fe8987
Merge: 6485434 42a52dc
Author: Muhammad Fadhil Amri <13521066@std.stei.itb.ac.id>
Date:   Mon Apr 1 10:13:51 2024 +0000

    Merge branch 'main' into 'feature/save-transaction'

    # Conflicts:
    #   app/src/main/res/values/strings.xml

commit 6485434bb71c5d1597871f44acb2ffe854dd5aa9
Author: fadhilAmri <13521066@std.stei.itb.ac.id>
Date:   Mon Apr 1 17:22:14 2024 +0700

    feat: Toast for download success

commit a56fbae0921ce1bf5dbb2e7e387ddaa63156d4d4
Author: fadhilAmri <13521066@std.stei.itb.ac.id>
Date:   Mon Apr 1 17:10:34 2024 +0700

    feat: create excel from list transactions and write it to internal storage

commit 42a52dcfb0d70b8e3ef61d532bcef3c3ede4e981
Merge: 7d6a913 13d8150
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Mon Apr 1 09:26:49 2024 +0000

    Merge branch 'feature/graph' into 'main'

    Feature/graph

    See merge request NerbFox/if3210-2024-android-nos!11

commit 13d81506df0eca80b2e05c3a907af09d1dbacdfe
Merge: 83e6e2b 7d6a913
Author: Nigel  Sahl <13521043@std.stei.itb.ac.id>
Date:   Mon Apr 1 08:42:03 2024 +0000

    Merge branch 'main' into 'feature/graph'

    # Conflicts:
    #   app/src/main/res/values/strings.xml

commit 83e6e2bf8bfe58fa7119c73016e2257a540284c9
Author: Nigel Sahl <93074692+NerbFox@users.noreply.github.com>
Date:   Mon Apr 1 15:50:08 2024 +0700

    update: use coroutine

commit 0e7d9460ad349b8ffa9dfdc7103613e211a5c485
Author: Nigel Sahl <93074692+NerbFox@users.noreply.github.com>
Date:   Mon Apr 1 15:49:56 2024 +0700

    update: add getSum function

commit 4947a3ada2df9668cade52654acb6769c342585c
Author: Nigel Sahl <93074692+NerbFox@users.noreply.github.com>
Date:   Mon Apr 1 14:27:50 2024 +0700

    update: res

commit 14d98284faa6fd31d6b0c9b05a0a3ef9aa7d72f0
Author: Nigel Sahl <93074692+NerbFox@users.noreply.github.com>
Date:   Mon Apr 1 14:27:36 2024 +0700

    update: rotating screen and graph

commit 7d6a91371c23c22bffc9b63a3b68c98f03b3686e
Merge: 07d4b55 8502ec8
Author: Muhammad Fadhil Amri <13521066@std.stei.itb.ac.id>
Date:   Mon Apr 1 05:43:58 2024 +0000

    Merge branch 'feature/scan' into 'main'

    Feature/scan

    See merge request NerbFox/if3210-2024-android-nos!10

commit 8502ec82d4e8ae856cf171150c66d9b33e7dec5b
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Mon Apr 1 12:51:05 2024 +0700

    feat: user input scan + map integration

commit e8c2c6e0655d82bc208ee4c78e344f9120a42a56
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Mon Apr 1 10:53:58 2024 +0700

    feat: insert transaction from scan

commit d3a260f0a8a80559169980fa65881c7e08513d49
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Sun Mar 31 23:13:09 2024 +0700

    feat: received scan response from server

commit 3c3d6e4dd39b525ec7cb9d0cf173ad0a1b5328b0
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Sun Mar 31 21:35:04 2024 +0700

    feat: gradle build caching

commit 28a2b448d33889b7f1bf26e34ab3f1ea2e5eebe7
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Sat Mar 30 16:46:20 2024 +0700

    feat: take & choose picture

commit 7ab56e5cb1ccdb8bad3d8b3daf072bf16200ad54
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Sat Mar 30 15:12:04 2024 +0700

    style: updated scan style

commit c3bb908a7f3da90985fcb863145e682c73210264
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Sat Mar 30 14:15:13 2024 +0700

    fix: camera permission

commit f4e95d708bc417ec08ced52ea5dd14a7035cfde3
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Sat Mar 30 13:50:30 2024 +0700

    feat: access camera preview

commit 07d4b55033511b13b6564d24a5ed3b982706fa18
Merge: 56ed163 440b3e6
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Mon Apr 1 03:57:10 2024 +0000

    Merge branch 'feature/location' into 'main'

    Feature/location

    See merge request NerbFox/if3210-2024-android-nos!9

commit 440b3e618846e7aad15b150284af1230cb8f2d9a
Merge: 1ca3577 56ed163
Author: Muhammad Fadhil Amri <13521066@std.stei.itb.ac.id>
Date:   Mon Apr 1 03:56:12 2024 +0000

    Merge branch 'main' into 'feature/location'

    # Conflicts:
    #   app/src/main/AndroidManifest.xml
    #   app/src/main/res/values/strings.xml

commit 1ca3577c0fe415dd4e83a2bb31298ed5506e64ec
Author: fadhilAmri <13521066@std.stei.itb.ac.id>
Date:   Mon Apr 1 11:02:33 2024 +0700

    fix: Back Button Navigation from Maps Activity to Main Activity

commit 341bc15557d461ad3189c286428d73c661ffeaf8
Author: fadhilAmri <13521066@std.stei.itb.ac.id>
Date:   Mon Apr 1 10:45:09 2024 +0700

    feat: show location in Google Maps

commit 454ea8debb0c3a7dd4dc366c9edd3ed2422cf345
Author: Nigel Sahl <93074692+NerbFox@users.noreply.github.com>
Date:   Mon Apr 1 10:41:02 2024 +0700

    update: graph display

commit 5b874e6a650e416a06168c67254a47f04ce1ed33
Author: fadhilAmri <13521066@std.stei.itb.ac.id>
Date:   Mon Apr 1 09:24:17 2024 +0700

    feat: intent to MapsActivity

commit 1584c00da104005b7ebb8f9b3650ff6b3cffdfb2
Author: fadhilAmri <13521066@std.stei.itb.ac.id>
Date:   Mon Apr 1 09:10:16 2024 +0700

    feat: location sensor

commit 2568756013761483160d2d1dbe2ab03935408dca
Author: Nigel Sahl <93074692+NerbFox@users.noreply.github.com>
Date:   Sun Mar 31 23:24:08 2024 +0700

    update: statistics fragment

commit 56ed163f75f77c54f8f516d58ce79e4c399132e0
Merge: ac9e0f1 2568756
Author: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date:   Sun Mar 31 16:15:14 2024 +0000

    Merge branch 'feature/graph' into 'main'

    update: statistics fragment

    See merge request NerbFox/if3210-2024-android-nos!8
---
 app/build.gradle.kts                          |  17 +-
 app/src/main/AndroidManifest.xml              |  26 +-
 .../java/com/example/nerbos/MapsActivity.kt   |  73 +++
 .../com/example/nerbos/data/TransactionDao.kt |   5 +
 .../nerbos/fragments/scan/ScanFragment.kt     | 475 ++++++++++++++++--
 .../fragments/statistic/StatisticFragment.kt  | 188 +++++--
 .../transaction/TransactionAdapter.kt         |   9 +
 .../transaction/TransactionFragment.kt        | 282 +++++++++--
 .../repository/TransactionRepository.kt       |   5 +
 .../example/nerbos/service/Authentication.kt  |   2 +-
 .../nerbos/viewmodel/TransactionViewModel.kt  |   5 +
 .../res/drawable/border_save_selection.xml    |  13 +
 app/src/main/res/drawable/button_save.png     | Bin 0 -> 6110 bytes
 app/src/main/res/drawable/button_send.png     | Bin 0 -> 6043 bytes
 app/src/main/res/drawable/ic_back.xml         |   5 +
 app/src/main/res/drawable/ic_circle.xml       |   5 +
 .../drawable/round_corner_save_selection.xml  |   6 +
 .../main/res/layout-land/activity_main.xml    |   6 +-
 .../res/layout-land/fragment_statistic.xml    | 126 +++++
 .../main/res/layout-land/fragment_user.xml    | 141 ++++++
 app/src/main/res/layout/activity_maps.xml     |  46 ++
 .../main/res/layout/add_scan_transaction.xml  |  67 +++
 app/src/main/res/layout/fragment_scan.xml     |  66 ++-
 .../main/res/layout/fragment_statistic.xml    | 118 ++++-
 .../main/res/layout/fragment_transaction.xml  | 110 +++-
 app/src/main/res/values/colors.xml            |  11 +
 app/src/main/res/values/strings.xml           |  19 +
 app/src/main/res/xml/provider_paths.xml       |   6 +
 build.gradle.kts                              |   1 +
 gradle.properties                             |   3 +-
 settings.gradle.kts                           |   1 +
 31 files changed, 1688 insertions(+), 149 deletions(-)
 create mode 100644 app/src/main/java/com/example/nerbos/MapsActivity.kt
 create mode 100644 app/src/main/res/drawable/border_save_selection.xml
 create mode 100644 app/src/main/res/drawable/button_save.png
 create mode 100644 app/src/main/res/drawable/button_send.png
 create mode 100644 app/src/main/res/drawable/ic_back.xml
 create mode 100644 app/src/main/res/drawable/ic_circle.xml
 create mode 100644 app/src/main/res/drawable/round_corner_save_selection.xml
 create mode 100644 app/src/main/res/layout-land/fragment_statistic.xml
 create mode 100644 app/src/main/res/layout-land/fragment_user.xml
 create mode 100644 app/src/main/res/layout/activity_maps.xml
 create mode 100644 app/src/main/res/layout/add_scan_transaction.xml
 create mode 100644 app/src/main/res/xml/provider_paths.xml

diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index c661268..4efe442 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -2,6 +2,7 @@ plugins {
     id("com.android.application")
     id("org.jetbrains.kotlin.android")
     id("kotlin-kapt")
+    id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
 }
 
 android {
@@ -47,8 +48,17 @@ dependencies {
     implementation("com.android.volley:volley:1.2.1")
     implementation("androidx.core:core-ktx:1.12.0")
     implementation("androidx.appcompat:appcompat:1.6.1")
+    implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")
     implementation("com.google.android.material:material:1.11.0")
     implementation("androidx.constraintlayout:constraintlayout:2.1.4")
+    implementation("com.google.android.gms:play-services-maps:18.2.0")
+    implementation("com.google.android.gms:play-services-location:21.2.0")
+    implementation("androidx.camera:camera-core:1.3.2")
+    implementation("androidx.camera:camera-camera2:1.3.2")
+    implementation("androidx.camera:camera-lifecycle:1.3.2")
+    implementation("androidx.camera:camera-view:1.3.2")
+    implementation("androidx.exifinterface:exifinterface:1.3.7")
+    implementation("com.squareup.okhttp3:okhttp:4.9.3")
     testImplementation("junit:junit:4.13.2")
     androidTestImplementation("androidx.test.ext:junit:1.1.5")
     androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
@@ -88,9 +98,12 @@ dependencies {
 
     // Kotlin components
     implementation ("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.20")
-    api ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
-    api ("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1")
+    api ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
+    api ("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
 
+    // POI: excel
+    implementation ("org.apache.poi:poi:5.0.0")
+    implementation ("org.apache.poi:poi-ooxml:5.2.3")
 
     //DataBinding
     kapt ("com.android.databinding:compiler:3.2.0-alpha10")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e52baae..221bdfd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,9 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-feature android:name="android.hardware.camera.any" />
+
     <application
         android:allowBackup="true"
         android:icon="@mipmap/ic_primary_logo"
@@ -12,6 +17,25 @@
         android:supportsRtl="true"
         android:theme="@style/Theme.NerbOS">
 
+        <provider
+            android:name="androidx.core.content.FileProvider"
+            android:authorities="${applicationId}.provider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/provider_paths" />
+        </provider>
+
+        <meta-data
+            android:name="com.google.android.geo.API_KEY"
+            android:value="AIzaSyBdVOQUHGOg7TS74kPCzoEtDDlGGZHpAEs" />
+
+        <activity
+            android:name=".MapsActivity"
+            android:exported="false"
+            android:label="@string/title_activity_maps" />
+
         <service android:name=".service.NetworkManagerService" />
 
         <service
diff --git a/app/src/main/java/com/example/nerbos/MapsActivity.kt b/app/src/main/java/com/example/nerbos/MapsActivity.kt
new file mode 100644
index 0000000..d4ccc07
--- /dev/null
+++ b/app/src/main/java/com/example/nerbos/MapsActivity.kt
@@ -0,0 +1,73 @@
+package com.example.nerbos
+
+import android.Manifest
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.graphics.Camera
+import android.location.Address
+import android.location.Geocoder
+import android.location.Location
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+import android.util.Log
+import android.widget.ImageView
+import android.widget.TextView
+import android.widget.Toast
+import androidx.core.app.ActivityCompat
+
+import com.google.android.gms.maps.CameraUpdateFactory
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.OnMapReadyCallback
+import com.google.android.gms.maps.SupportMapFragment
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.MarkerOptions
+import com.example.nerbos.databinding.ActivityMapsBinding
+import com.google.android.gms.location.FusedLocationProviderClient
+import com.google.android.gms.location.LocationServices
+import com.google.android.gms.maps.model.Marker
+import java.util.Locale
+
+class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
+
+    private lateinit var mMap: GoogleMap
+    private lateinit var binding: ActivityMapsBinding
+    private lateinit var location: Address
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        binding = ActivityMapsBinding.inflate(layoutInflater)
+        setContentView(binding.root)
+
+        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
+        val mapFragment = supportFragmentManager
+            .findFragmentById(R.id.map) as SupportMapFragment
+        mapFragment.getMapAsync(this)
+
+        val myIntent = intent
+        val locationName : String? = myIntent.getStringExtra("locationName")
+        val geocoder = Geocoder(this, Locale.getDefault())
+        val address = geocoder.getFromLocationName(locationName!!, 1)
+        if (address != null) {
+            location = address[0]
+        }
+
+        // Navigation
+        findViewById<ImageView>(R.id.backButton).setOnClickListener {
+            val mainIntent: Intent = Intent(this, MainActivity::class.java)
+            startActivity(mainIntent)
+            finish()
+        }
+
+    }
+
+    override fun onMapReady(googleMap: GoogleMap) {
+        mMap = googleMap
+        mMap.uiSettings.isZoomControlsEnabled = true
+        val currentLatLong : LatLng = LatLng(location.latitude,location.longitude)
+        val markerOptions = MarkerOptions().position(currentLatLong).title("Current Location")
+        mMap.addMarker(markerOptions)
+        mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(currentLatLong, 15f))
+    }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/nerbos/data/TransactionDao.kt b/app/src/main/java/com/example/nerbos/data/TransactionDao.kt
index ce1a9db..29683ae 100644
--- a/app/src/main/java/com/example/nerbos/data/TransactionDao.kt
+++ b/app/src/main/java/com/example/nerbos/data/TransactionDao.kt
@@ -8,6 +8,7 @@ import androidx.room.OnConflictStrategy
 import androidx.room.Query
 import androidx.room.Update
 import com.example.nerbos.model.Transaction
+import com.example.nerbos.model.TransactionCategory
 
 @Dao
 interface TransactionDao {
@@ -22,4 +23,8 @@ interface TransactionDao {
 
     @Delete
     suspend fun deleteTransation(transaction: Transaction)
+
+    @Query("SELECT SUM(nominal) FROM transaction_table WHERE userID = :userID AND category = :category")
+    fun getSum(userID: Int, category: TransactionCategory): LiveData<Float>
+
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/nerbos/fragments/scan/ScanFragment.kt b/app/src/main/java/com/example/nerbos/fragments/scan/ScanFragment.kt
index 7161de1..a47dea4 100644
--- a/app/src/main/java/com/example/nerbos/fragments/scan/ScanFragment.kt
+++ b/app/src/main/java/com/example/nerbos/fragments/scan/ScanFragment.kt
@@ -1,33 +1,86 @@
 package com.example.nerbos.fragments.scan
 
+import android.Manifest
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Matrix
+import android.location.Geocoder
 import android.os.Bundle
-import androidx.fragment.app.Fragment
+import android.os.Handler
+import android.os.Looper
+import android.provider.MediaStore
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.EditText
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.RadioGroup
+import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AlertDialog
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.ImageCaptureException
+import androidx.camera.core.Preview
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.view.PreviewView
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import androidx.core.net.toUri
+import androidx.exifinterface.media.ExifInterface
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
 import com.example.nerbos.R
+import com.example.nerbos.model.Transaction
+import com.example.nerbos.model.TransactionCategory
+import com.example.nerbos.service.Authentication
+import com.example.nerbos.viewmodel.TransactionViewModel
+import com.google.android.gms.location.FusedLocationProviderClient
+import com.google.android.gms.location.LocationServices
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import okhttp3.Call
+import okhttp3.Callback
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
+import okhttp3.MultipartBody
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.RequestBody.Companion.asRequestBody
+import okhttp3.Response
+import org.json.JSONException
+import org.json.JSONObject
+import java.io.File
+import java.io.IOException
+import java.util.Locale
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
 
-// 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.
- */
 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 lateinit var previewView: PreviewView
+    private lateinit var cameraExecutor: ExecutorService
+    private lateinit var imageCapture: ImageCapture
+    private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
+    private lateinit var geocoder: Geocoder
+    private var fragmentContext: Context? = null
+
+    private val requestCameraPermissionCode = Manifest.permission.CAMERA
+    private val uploadURL: String by lazy {
+        requireContext().getString(R.string.backend_api_scan)
+    }
+
+    override fun onAttach(context: Context) {
+        super.onAttach(context)
+        fragmentContext = context
+    }
+
+    override fun onDetach() {
+        super.onDetach()
+        fragmentContext = null
     }
 
     override fun onCreateView(
@@ -35,26 +88,370 @@ class ScanFragment : Fragment() {
         savedInstanceState: Bundle?
     ): View? {
         // Inflate the layout for this fragment
-        return inflater.inflate(R.layout.fragment_scan, container, false)
-    }
-
-    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 Scan.
-         */
-        // 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)
+        val view = inflater.inflate(R.layout.fragment_scan, container, false)
+        previewView = view.findViewById(R.id.previewView)
+        return view
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        cameraExecutor = Executors.newSingleThreadExecutor()
+        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(requireContext())
+        geocoder = Geocoder(requireContext(), Locale.getDefault())
+
+        if (allPermissionsGranted()) {
+            startCamera()
+        } else {
+            requestCameraPermission.launch(requestCameraPermissionCode)
+        }
+
+        view.findViewById<ImageButton>(R.id.captureButton).setOnClickListener {
+            takePicture()
+        }
+
+        view.findViewById<ImageButton>(R.id.galleryButton).setOnClickListener {
+            dispatchGalleryIntent()
+        }
+    }
+
+    private fun allPermissionsGranted() = ContextCompat.checkSelfPermission(
+        requireContext(),
+        Manifest.permission.CAMERA
+    ) == PackageManager.PERMISSION_GRANTED
+
+    private fun startCamera() {
+        val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
+
+        cameraProviderFuture.addListener({
+            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
+
+            val preview = Preview.Builder()
+                .build()
+                .also {
+                    it.setSurfaceProvider(previewView.surfaceProvider)
+                }
+
+            imageCapture = ImageCapture.Builder().build()
+
+            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
+
+            try {
+                cameraProvider.unbindAll()
+                cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
+            } catch (e: Exception) {
+                e.printStackTrace()
+            }
+
+        }, ContextCompat.getMainExecutor(requireContext()))
+    }
+
+    private fun takePicture() {
+        val imageCapture = imageCapture
+
+        // Create output file to hold the captured image
+        val externalFilesDirs = requireContext().getExternalFilesDirs(null)
+        val photoFile = File(externalFilesDirs.firstOrNull(), "${System.currentTimeMillis()}.jpg")
+
+        // Setup image capture metadata
+        val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
+
+        // Capture the image
+        imageCapture.takePicture(
+            outputOptions,
+            ContextCompat.getMainExecutor(requireContext()),
+            object : ImageCapture.OnImageSavedCallback {
+                override fun onError(exc: ImageCaptureException) {
+                    Toast.makeText(requireContext(), "Error capturing image: ${exc.message}", Toast.LENGTH_SHORT).show()
+                }
+
+                override fun onImageSaved(output: ImageCapture.OutputFileResults) {
+                    // Image captured successfully, read orientation metadata
+                    val savedUri = photoFile.toUri()
+                    val imageBitmap = BitmapFactory.decodeFile(savedUri.path)
+                    val rotatedBitmap = savedUri.path?.let {
+                        rotateImageIfRequired(imageBitmap,
+                            it
+                        )
+                    }
+                    showImagePopup(rotatedBitmap)
+                }
+            })
+    }
+
+    private fun rotateImageIfRequired(bitmap: Bitmap, path: String): Bitmap {
+        val ei = ExifInterface(path)
+        val orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
+
+        return when (orientation) {
+            ExifInterface.ORIENTATION_ROTATE_90 -> rotateImage(bitmap, 90f)
+            ExifInterface.ORIENTATION_ROTATE_180 -> rotateImage(bitmap, 180f)
+            ExifInterface.ORIENTATION_ROTATE_270 -> rotateImage(bitmap, 270f)
+            else -> bitmap
+        }
+    }
+
+    private fun rotateImage(source: Bitmap, angle: Float): Bitmap {
+        val matrix = Matrix()
+        matrix.postRotate(angle)
+        return Bitmap.createBitmap(source, 0, 0, source.width, source.height, matrix, true)
+    }
+
+    private fun dispatchGalleryIntent() {
+        val galleryIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
+        requestGallery.launch(galleryIntent)
+    }
+
+    private fun showImagePopup(imageBitmap: Bitmap?) {
+        if (imageBitmap != null) {
+            MaterialAlertDialogBuilder(requireContext())
+                .setTitle("Use this picture?")
+                .setPositiveButton("Yes") { dialog, _ ->
+                    val imageFile = File.createTempFile("image", ".jpg", requireContext().cacheDir)
+                    imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, imageFile.outputStream())
+                    uploadImageToServer(imageFile) { success, message ->
+                        if (success) {
+                            showToastOnUIThread(message.toString())
+                        } else {
+                            showToastOnUIThread(message.toString())
+                        }
+                    }
+                    dialog.dismiss()
+                }
+                .setNegativeButton("No") { dialog, _ ->
+                    showToastOnUIThread("Scan cancelled")
+                    dialog.dismiss()
+                }
+                .setView(ImageView(requireContext()).apply {
+                    setImageBitmap(imageBitmap)
+                    adjustViewBounds = true
+                })
+                .show()
+                .apply {
+                    getButton(AlertDialog.BUTTON_POSITIVE)?.setTextColor(ContextCompat.getColor(requireContext(), R.color.white))
+                    getButton(AlertDialog.BUTTON_NEGATIVE)?.setTextColor(ContextCompat.getColor(requireContext(), R.color.white))
+                }
+        } else {
+            Toast.makeText(requireContext(), "Failed to load image", Toast.LENGTH_SHORT).show()
+        }
+    }
+
+    @Suppress("DEPRECATION")
+    private fun showTransactionInputDialog(callback: (String, String, String) -> Unit) {
+        val dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.add_scan_transaction, null)
+        val nameInput = dialogView.findViewById<EditText>(R.id.nameInput)
+        val locationInput = dialogView.findViewById<EditText>(R.id.locationInput)
+        val transactionTypeRadioGroup = dialogView.findViewById<RadioGroup>(R.id.transactionTypeRadioGroup)
+        val mapButton = dialogView.findViewById<ImageButton>(R.id.mapButton)
+
+        transactionTypeRadioGroup.check(R.id.incomeRadioButton)
+        mapButton.setOnClickListener {
+            if (ActivityCompat.checkSelfPermission(
+                    requireContext(),
+                    Manifest.permission.ACCESS_FINE_LOCATION
+                ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
+                    requireContext(),
+                    Manifest.permission.ACCESS_COARSE_LOCATION
+                ) != PackageManager.PERMISSION_GRANTED
+            ) {
+                ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)
+                return@setOnClickListener
+            }
+            fusedLocationProviderClient.lastLocation.addOnSuccessListener (requireActivity()){
+                    location ->
+                if (location != null) {
+                    val address = geocoder.getFromLocation(location.latitude, location.longitude, 1)
+                    locationInput.setText(address!![0].getAddressLine(0))
+                } else {
+                    Toast.makeText(requireContext(), "Failed to get location", Toast.LENGTH_SHORT)
+                        .show()
+                }
+            }
+        }
+
+        val dialog = AlertDialog.Builder(requireContext())
+            .setTitle("Transaction Input")
+            .setView(dialogView)
+            .setPositiveButton("OK") { _, _ ->
+                val name = nameInput.text.toString()
+                val location = locationInput.text.toString()
+                val transactionType = when (transactionTypeRadioGroup.checkedRadioButtonId) {
+                    R.id.incomeRadioButton -> "Income"
+                    R.id.outcomeRadioButton -> "Outcome"
+                    else -> ""
+                }
+                if (name.isNotEmpty() && location.isNotEmpty() && transactionType.isNotEmpty()) {
+                    callback(name, location, transactionType)
+                } else {
+                    // Display error message if any field is empty
+                    Toast.makeText(requireContext(), "Please fill in all fields", Toast.LENGTH_SHORT).show()
+                    // Show the dialog again to allow the user to fill in missing fields
+                    showTransactionInputDialog(callback)
+                }
+            }
+            .setNegativeButton("Cancel") { dialog, _ ->
+                dialog.dismiss()
+            }
+            .create()
+
+        dialog.show()
+
+        dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.setTextColor(ContextCompat.getColor(requireContext(), R.color.white))
+        dialog.getButton(AlertDialog.BUTTON_NEGATIVE)?.setTextColor(ContextCompat.getColor(requireContext(), R.color.white))
+    }
+
+    private fun showTransactionConfirmationDialog(responseBody: String?, transactionName: String, selectedTransactionType: String, totalNominal: Float, location: String, callback: (Boolean) -> Unit) {
+        val builder = AlertDialog.Builder(requireContext())
+        builder.setTitle("Confirm Transaction Details")
+
+        // Parse the JSON response body to extract item details
+        val itemDetails = StringBuilder()
+        calculateTotalNominal(responseBody)?.let {
+            try {
+                val jsonObject = JSONObject(responseBody.toString())
+                val itemsArray = jsonObject.getJSONObject("items").getJSONArray("items")
+                for (i in 0 until itemsArray.length()) {
+                    val itemObject = itemsArray.getJSONObject(i)
+                    val name = itemObject.getString("name")
+                    val qty = itemObject.getInt("qty")
+                    val price = itemObject.getDouble("price").toFloat()
+                    itemDetails.append("$name: $qty x $price\n")
                 }
+            } catch (e: JSONException) {
+                e.printStackTrace()
             }
+        }
+
+        val transactionTypeMessage = if (selectedTransactionType == "Income") "income" else "outcome"
+        val message = "Transaction Name: $transactionName\n\n$itemDetails\nTotal Nominal: $totalNominal\n\nThis transaction will be added as an $transactionTypeMessage with location: $location.\n\nDo you want to proceed?"
+        builder.setMessage(message)
+
+        builder.setPositiveButton("Yes") { dialog, _ ->
+            callback(true)
+            dialog.dismiss()
+        }
+
+        builder.setNegativeButton("No") { dialog, _ ->
+            callback(false)
+            dialog.dismiss()
+        }
+
+        val dialog = builder.create()
+        dialog.show()
+
+        dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.setTextColor(ContextCompat.getColor(requireContext(), R.color.white))
+        dialog.getButton(AlertDialog.BUTTON_NEGATIVE)?.setTextColor(ContextCompat.getColor(requireContext(), R.color.white))
+    }
+
+    private fun uploadImageToServer(imageFile: File, callback: (Boolean, String?) -> Unit) {
+        showTransactionInputDialog { name, location, transactionType ->
+            val authentication = Authentication(requireContext())
+            val token = authentication.getToken()
+            val client = OkHttpClient()
+
+            val requestBody = MultipartBody.Builder()
+                .setType(MultipartBody.FORM)
+                .addFormDataPart("file", imageFile.name, imageFile.asRequestBody("image/*".toMediaTypeOrNull()))
+                .build()
+
+            val request = Request.Builder()
+                .url(uploadURL)
+                .header("Authorization", "Bearer $token")
+                .post(requestBody)
+                .build()
+
+            client.newCall(request).enqueue(object : Callback {
+                override fun onResponse(call: Call, response: Response) {
+                    if (response.isSuccessful) {
+                        // Parse the response body to extract transaction items and calculate total nominal
+                        val responseBody = response.body?.string()
+                        val totalNominal = calculateTotalNominal(responseBody)
+
+                        // Run on UI thread to show transaction confirmation dialog
+                        requireActivity().runOnUiThread {
+                            showTransactionConfirmationDialog(responseBody, name, transactionType, totalNominal ?: 0f, location) { confirmed ->
+                                if (confirmed) {
+                                    // Create a single transaction and add it to the database
+                                    totalNominal?.let { nominal ->
+                                        val transaction = Transaction(
+                                            userID = authentication.getNim(),
+                                            name = name,
+                                            category = if (transactionType == "Income") TransactionCategory.INCOME else TransactionCategory.OUTCOME,
+                                            nominal = nominal,
+                                            location = location
+                                        )
+                                        addTransactionToDatabase(transaction)
+                                        callback(true, "Transaction added successfully")
+                                    } ?: run {
+                                        callback(false, "Failed to calculate total nominal")
+                                    }
+                                } else {
+                                    callback(false, "Transaction cancelled")
+                                }
+                            }
+                        }
+                    } else {
+                        callback(false, "Failed to upload image. Please try again later.")
+                    }
+                }
+
+                override fun onFailure(call: Call, e: IOException) {
+                    showToastOnUIThread("Failed to upload image: ${e.message}")
+                    callback(false, e.message)
+                }
+            })
+        }
+    }
+
+    private fun calculateTotalNominal(responseBody: String?): Float? {
+        var totalNominal: Float? = null
+        try {
+            val jsonObject = JSONObject(responseBody.toString())
+            val itemsArray = jsonObject.getJSONObject("items").getJSONArray("items")
+            totalNominal = 0f
+            for (i in 0 until itemsArray.length()) {
+                val itemObject = itemsArray.getJSONObject(i)
+                val qty = itemObject.getInt("qty")
+                val price = itemObject.getDouble("price").toFloat()
+                totalNominal += qty * price
+            }
+        } catch (e: JSONException) {
+            e.printStackTrace()
+        }
+        return totalNominal
+    }
+
+    private fun addTransactionToDatabase(transaction: Transaction) {
+        val transactionViewModel = ViewModelProvider(requireActivity())[TransactionViewModel::class.java]
+        transactionViewModel.addTransaction(transaction)
+    }
+
+    private fun showToastOnUIThread(message: String) {
+        Handler(Looper.getMainLooper()).post {
+            Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
+        }
+    }
+
+    private val requestCameraPermission =
+        registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
+            if (isGranted) {
+                startCamera()
+            } else {
+                // Handle the case where the user denied the permission
+                Toast.makeText(requireContext(), "Camera permission denied", Toast.LENGTH_SHORT).show()
+            }
+        }
+
+    private val requestGallery = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+        if (result.resultCode == Activity.RESULT_OK) {
+            val imageUri = result.data?.data
+            val inputStream = requireContext().contentResolver.openInputStream(imageUri!!)
+            val imageBitmap = BitmapFactory.decodeStream(inputStream)
+            showImagePopup(imageBitmap)
+        }
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        cameraExecutor.shutdown()
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/nerbos/fragments/statistic/StatisticFragment.kt b/app/src/main/java/com/example/nerbos/fragments/statistic/StatisticFragment.kt
index 55768bf..df203cb 100644
--- a/app/src/main/java/com/example/nerbos/fragments/statistic/StatisticFragment.kt
+++ b/app/src/main/java/com/example/nerbos/fragments/statistic/StatisticFragment.kt
@@ -1,60 +1,182 @@
 package com.example.nerbos.fragments.statistic
 
+import android.graphics.Color
+import android.graphics.Typeface
 import android.os.Bundle
 import androidx.fragment.app.Fragment
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.lifecycleScope
 import com.example.nerbos.R
+import com.example.nerbos.databinding.FragmentStatisticBinding
+import com.example.nerbos.model.TransactionCategory
+import com.example.nerbos.viewmodel.TransactionViewModel
+import com.example.nerbos.service.Authentication
+import com.github.mikephil.charting.animation.Easing
+import com.github.mikephil.charting.charts.PieChart
+import com.github.mikephil.charting.data.PieData
+import com.github.mikephil.charting.data.PieDataSet
+import com.github.mikephil.charting.data.PieEntry
+import com.github.mikephil.charting.formatter.PercentFormatter
+import com.github.mikephil.charting.utils.MPPointF
+import kotlinx.coroutines.launch
+import java.text.NumberFormat
+import java.util.Locale
 
-// 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 [StatisticFragment.newInstance] factory method to
- * create an instance of this fragment.
- */
 class StatisticFragment : Fragment() {
-    // TODO: Rename and change types of parameters
-    private var param1: String? = null
-    private var param2: String? = null
+
+    private lateinit var transactionViewModel: TransactionViewModel
+    private lateinit var authentication: Authentication
+    private lateinit var pieChart: PieChart
+    private lateinit var binding: FragmentStatisticBinding
+    private val liveDataReady = MutableLiveData<Boolean>()
+    private var sumIncome: Float = 0.0f
+    private var sumOutcome : Float = 0.0f
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         arguments?.let {
-            param1 = it.getString(ARG_PARAM1)
-            param2 = it.getString(ARG_PARAM2)
+
         }
     }
 
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
-    ): View? {
+    ): View {
         // Inflate the layout for this fragment
-        return inflater.inflate(R.layout.fragment_statistic, container, false)
+        binding = FragmentStatisticBinding.inflate(inflater, container, false)
+        pieChart = binding.pieChart
+
+        // Create an authentication object
+        authentication = Authentication(requireContext())
+        // Set the view model for the transaction and set the user id
+        transactionViewModel = ViewModelProvider(this)[TransactionViewModel::class.java]
+
+        // Get the data from the database
+        getData()
+
+        // Set up the pie chart
+        pieChartSetup()
+
+        // Display the pie chart
+        displayPieChart()
+
+        return binding.root
     }
 
-    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 Statistic.
-         */
-        // TODO: Rename and change types and number of parameters
-        @JvmStatic
-        fun newInstance(param1: String, param2: String) =
-            StatisticFragment().apply {
-                arguments = Bundle().apply {
-                    putString(ARG_PARAM1, param1)
-                    putString(ARG_PARAM2, param2)
+    private fun getData() {
+        lifecycleScope.launch {
+            // Get the sum of income and outcome
+            val nim = authentication.getNim()
+            val income = transactionViewModel.getSums(nim, TransactionCategory.INCOME)
+            val outcome = transactionViewModel.getSums(nim, TransactionCategory.OUTCOME)
+
+            // Use MediatorLiveData to observe both income and outcome
+            val result = MediatorLiveData<Float>().apply {
+                addSource(income) { value -> setValue(value) }
+                addSource(outcome) { value -> setValue(value) }
+            }
+
+            // Observing the result
+            result.observe(viewLifecycleOwner) {
+                if (income.value != null && outcome.value != null) {
+                    sumIncome = income.value!!
+                    sumOutcome = outcome.value!!
+                    liveDataReady.postValue(true)
                 }
             }
+        }
     }
+
+    // Display a pie chart of the income and outcome
+    private fun pieChartSetup() {
+        // Set the pie chart: percentage, description, extra offsets
+        pieChart.setUsePercentValues(true)
+        pieChart.description.isEnabled = false
+        pieChart.setExtraOffsets(5f, 10f, 5f, 5f)
+
+        // Set the pie chart: drag deceleration, hole, hole color
+        pieChart.setDragDecelerationFrictionCoef(0.95f)
+        pieChart.isDrawHoleEnabled = true
+        pieChart.setHoleColor(requireContext().getColor(R.color.primary_bg))
+
+        // Set the pie chart: transparent circle, hole color, hole radius, draw center text
+        pieChart.setTransparentCircleColor(requireContext().getColor(R.color.primary_bg))
+        pieChart.setTransparentCircleAlpha(110)
+        pieChart.holeRadius = 42f
+        pieChart.transparentCircleRadius = 48f
+        pieChart.setDrawCenterText(true)
+
+        // Set the pie chart: legend and entry label
+        pieChart.legend.isEnabled = false
+        pieChart.setEntryLabelColor(Color.BLACK)
+        pieChart.setEntryLabelTextSize(12f)
+
+        // Set the pie chart: rotation and highlight
+        pieChart.setRotationAngle(0f)
+        pieChart.isRotationEnabled = true
+        pieChart.isHighlightPerTapEnabled = true
+    }
+
+    private fun setPieData(){
+        // Set the pie chart: animation
+        pieChart.animateY(1400, Easing.EaseInOutQuad)
+
+        // Set the pie chart: data
+        val entries: ArrayList<PieEntry> = ArrayList()
+        entries.add(PieEntry(sumIncome, resources.getString(R.string.income)))
+        entries.add(PieEntry(sumOutcome, resources.getString(R.string.outcome)))
+
+        // Create a pie data set
+        val dataSet = PieDataSet(entries, resources.getString(R.string.pie_chart_title))
+
+        // Set the pie data set properties
+        dataSet.setDrawIcons(false)
+        dataSet.sliceSpace = 3f
+        dataSet.iconsOffset = MPPointF(0f, 40f)
+        dataSet.selectionShift = 5f
+
+        // Set the pie data set: colors
+        val colors: ArrayList<Int> = ArrayList()
+        colors.add(requireContext().getColor(R.color.purple_200))
+        colors.add(requireContext().getColor(R.color.red))
+        dataSet.colors = colors
+
+        // Set the pie data set: text size, text color, text typeface
+        val data = PieData(dataSet)
+        data.setValueFormatter(PercentFormatter())
+        data.setValueTextSize(15f)
+        data.setValueTypeface(Typeface.DEFAULT_BOLD)
+        data.setValueTextColor(Color.BLACK)
+        pieChart.setData(data)
+        pieChart.highlightValues(null)
+    }
+
+    private fun displayPieChart() {
+        // wait until the income and outcome are calculated
+        // Observe the LiveData object to update the pie chart when the data is ready
+        liveDataReady.observe(viewLifecycleOwner) { dataReady ->
+            if (dataReady) {
+                // Set the pie data
+                setPieData()
+
+                // Invalidate the pie chart (refresh)
+                pieChart.invalidate()
+
+                // Reset the sum of income and outcome
+                val incomeNumber = binding.totalIncomeNumber
+                val outcomeNumber = binding.totalOutcomeNumber
+
+                // Update the text views with the formatted values
+                incomeNumber.text = NumberFormat.getCurrencyInstance(Locale(resources.getString(R.string.language_code), resources.getString(R.string.country_code))).format(sumIncome)
+                outcomeNumber.text = NumberFormat.getCurrencyInstance(Locale(resources.getString(R.string.language_code), resources.getString(R.string.country_code))).format(sumOutcome)
+            }
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionAdapter.kt b/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionAdapter.kt
index a2a9c17..5962918 100644
--- a/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionAdapter.kt
+++ b/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionAdapter.kt
@@ -19,6 +19,7 @@ class TransactionAdapter() : RecyclerView.Adapter<TransactionAdapter.Transaction
     // Create ViewHolder Class
     private var transactionList : List<Transaction> = emptyList<Transaction>()
     var onItemClick: ((Transaction) -> Unit)? = null
+    var onLocationClick: ((String) -> Unit)? = null
     class TransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
         val name: TextView = itemView.findViewById(R.id.transactionName)
         val date: TextView = itemView.findViewById(R.id.transactionDate)
@@ -54,6 +55,14 @@ class TransactionAdapter() : RecyclerView.Adapter<TransactionAdapter.Transaction
         holder.itemView.setOnClickListener{
             onItemClick?.invoke(transactionList[position])
         }
+
+        holder.location.setOnClickListener {
+            onLocationClick?.invoke(holder.location.text.toString())
+        }
+    }
+
+    fun getTransactionList() : List<Transaction>{
+        return this.transactionList
     }
     @SuppressLint("NotifyDataSetChanged")
     fun setData(transactions: List<Transaction>){
diff --git a/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionFragment.kt b/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionFragment.kt
index 14b0558..238b866 100644
--- a/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionFragment.kt
+++ b/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionFragment.kt
@@ -1,14 +1,18 @@
 package com.example.nerbos.fragments.transaction
 
-import android.app.AlertDialog
+import android.Manifest
 import android.app.Dialog
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.location.Geocoder
+import android.location.Location
 import android.os.Bundle
 import android.text.TextUtils
-import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import android.view.Window
 import android.view.WindowManager
 import android.widget.Button
 import android.widget.EditText
@@ -17,41 +21,44 @@ import android.widget.RadioButton
 import android.widget.RadioGroup
 import android.widget.TextView
 import android.widget.Toast
+import androidx.core.app.ActivityCompat
+import androidx.core.content.FileProvider
 import androidx.fragment.app.Fragment
-import androidx.lifecycle.Observer
 import androidx.lifecycle.ViewModelProvider
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
+import com.example.nerbos.MapsActivity
 import com.example.nerbos.R
 import com.example.nerbos.model.Transaction
 import com.example.nerbos.model.TransactionCategory
-import com.example.nerbos.viewmodel.TransactionViewModel
 import com.example.nerbos.service.Authentication
 import com.example.nerbos.service.Utils
+import com.example.nerbos.viewmodel.TransactionViewModel
+import com.google.android.gms.location.FusedLocationProviderClient
+import com.google.android.gms.location.LocationServices
+import org.apache.poi.hssf.usermodel.HSSFWorkbook
+import org.apache.poi.ss.usermodel.Cell
+import org.apache.poi.ss.usermodel.Row
+import org.apache.poi.ss.usermodel.Sheet
+import org.apache.poi.ss.usermodel.Workbook
+import org.apache.poi.xssf.usermodel.XSSFWorkbook
+import java.io.File
+import java.io.FileOutputStream
+import java.util.Locale
 
-// 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 [Transaction.newInstance] factory method to
- * create an instance of this fragment.
- */
 class TransactionFragment : Fragment() {
-    // TODO: Rename and change types of parameters
-    private var param1: String? = null
-    private var param2: String? = null
+
     private  lateinit var transactionViewModel: TransactionViewModel
     private lateinit var authentication: Authentication
+    private var currentLocation: Location? = null
+    private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
+    private lateinit var geocoder: Geocoder
+    private val permissionCode = 1
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        arguments?.let {
-            param1 = it.getString(ARG_PARAM1)
-            param2 = it.getString(ARG_PARAM2)
-        }
+        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(requireContext())
+        geocoder = Geocoder(requireContext(), Locale.getDefault())
     }
 
     override fun onCreateView(
@@ -67,21 +74,190 @@ class TransactionFragment : Fragment() {
         recyclerView.layoutManager = LinearLayoutManager(context)
         recyclerView.adapter = transactionAdapter
         transactionAdapter.onItemClick = { transaction -> showModifyTransactionDialog(transaction)}
+        transactionAdapter.onLocationClick = {location -> showLocationOnMap(location)}
 
         // Transaction View Model
         transactionViewModel = ViewModelProvider(this)[TransactionViewModel::class.java]
 
         transactionViewModel.setReadDataUserId(authentication.getNim())
-        transactionViewModel.readAllData!!.observe(viewLifecycleOwner, Observer { transactions ->
-            transactionAdapter.setData(transactions) })
+        transactionViewModel.readAllData!!.observe(viewLifecycleOwner) { transactions ->
+            transactionAdapter.setData(transactions)
+        }
 
+        // Add Transaction Button
         view.findViewById<ImageView>(R.id.addTransactionButton).setOnClickListener {
             showAddTransactionDialog()
         }
 
+        // Save Transaction
+        val xlsSave = view.findViewById<TextView>(R.id.xlsSave)
+        val xlsxSave = view.findViewById<TextView>(R.id.xlsxSave)
+
+        view.findViewById<ImageView>(R.id.saveTransactionsButton).setOnClickListener{
+
+            if (xlsSave.visibility == View.VISIBLE){
+                xlsSave.visibility = View.GONE
+                xlsxSave.visibility = View.GONE
+            } else if(xlsSave.visibility == View.GONE){
+                xlsSave.visibility = View.VISIBLE
+                xlsxSave.visibility = View.VISIBLE
+            }
+        }
+
+        xlsSave.setOnClickListener {
+            val workbook: Workbook = createExcelFile(transactionAdapter.getTransactionList(), "xls")
+            saveWorkBook(workbook, "xls")
+            xlsSave.visibility = View.GONE
+            xlsxSave.visibility = View.GONE
+            Toast.makeText(requireContext(), "Transaction Report.xls is downloaded", Toast.LENGTH_LONG).show()
+        }
+
+        xlsxSave.setOnClickListener {
+            val workbook: Workbook = createExcelFile(transactionAdapter.getTransactionList(), "xlsx")
+            saveWorkBook(workbook, "xlsx")
+            xlsSave.visibility = View.GONE
+            xlsxSave.visibility = View.GONE
+            Toast.makeText(requireContext(), "Transaction Report.xlsx is downloaded", Toast.LENGTH_LONG).show()
+        }
+
+        // Send Transaction
+        val xlsSend = view.findViewById<TextView>(R.id.xlsSend)
+        val xlsxSend = view.findViewById<TextView>(R.id.xlsxSend)
+
+        view.findViewById<ImageView>(R.id.sendTransactionsButton).setOnClickListener{
+            if (xlsSend.visibility == View.VISIBLE){
+                xlsSend.visibility = View.GONE
+                xlsxSend.visibility = View.GONE
+            } else if(xlsSend.visibility == View.GONE){
+                xlsSend.visibility = View.VISIBLE
+                xlsxSend.visibility = View.VISIBLE
+            }
+        }
+
+        xlsSend.setOnClickListener {
+            val subject = getString(R.string.email_subject)
+            val message = getString(R.string.email_message)
+            val workbook: Workbook = createExcelFile(transactionAdapter.getTransactionList(), "xls")
+            sendWorkbook(authentication.getEmail(), subject, message, workbook, "xls")
+            xlsSend.visibility = View.GONE
+            xlsxSend.visibility = View.GONE
+        }
+
+        xlsxSend.setOnClickListener {
+            val subject = getString(R.string.email_subject)
+            val message = getString(R.string.email_message)
+            val workbook: Workbook = createExcelFile(transactionAdapter.getTransactionList(), "xlsx")
+            sendWorkbook(authentication.getEmail(), subject, message, workbook, "xlsx")
+            xlsSend.visibility = View.GONE
+            xlsxSend.visibility = View.GONE
+        }
+
         return view
     }
 
+    private fun createExcelFile(transactions : List<Transaction>, extension: String) : Workbook{
+        val workbook : Workbook
+        val sheet : Sheet
+        if (extension == "xls"){
+            workbook  = HSSFWorkbook()
+            sheet = workbook.createSheet("My Transactions")
+        } else { // "xlsx"
+            workbook = XSSFWorkbook()
+            sheet = workbook.createSheet("My Transactions")
+        }
+
+        // Setup Header
+        val header : Row = sheet.createRow(0)
+        var cell : Cell = header.createCell(0)
+        cell.setCellValue("Date")
+        cell = header.createCell(1)
+        cell.setCellValue("Category")
+        cell = header.createCell(2)
+        cell.setCellValue("Nominal")
+        cell = header.createCell(3)
+        cell.setCellValue("Name")
+        cell = header.createCell(4)
+        cell.setCellValue("Location")
+
+        // Fill the cell for transaction data
+        for (i in transactions.indices){
+            val row : Row= sheet.createRow(i+1)
+            cell = row.createCell(0)
+            cell.setCellValue(transactions[i].date.toString())
+            cell = row.createCell(1)
+            cell.setCellValue(transactions[i].category.toString())
+            cell = row.createCell(2)
+            cell.setCellValue(transactions[i].nominal.toDouble())
+            cell = row.createCell(3)
+            cell.setCellValue(transactions[i].name)
+            cell = row.createCell(4)
+            cell.setCellValue(transactions[i].location)
+        }
+
+        return workbook
+    }
+    private fun saveWorkBook(workbook: Workbook, extension: String) {
+        val appDirectory = requireContext().filesDir
+
+        val filepath = File(appDirectory , "Transaction Report.$extension")
+
+        try{
+            if(!filepath.exists()){
+                filepath.createNewFile()
+            }
+            val fileOutputStream = FileOutputStream(filepath)
+            workbook.write(fileOutputStream)
+            fileOutputStream.flush()
+            fileOutputStream.close()
+
+        }catch (e:Exception){
+            e.printStackTrace()
+        }
+    }
+
+    private fun sendWorkbook(email: String, subject: String, message: String, workbook: Workbook, extension: String) {
+        val file = File(requireContext().cacheDir, "Transaction Report.$extension")
+
+        try {
+            val fileOutputStream = FileOutputStream(file)
+            workbook.write(fileOutputStream)
+            fileOutputStream.flush()
+            fileOutputStream.close()
+
+            val intent = Intent(Intent.ACTION_SEND)
+            val uri = FileProvider.getUriForFile(requireContext(), requireContext().packageName + ".provider", file)
+            intent.type = "message/rfc822"
+            intent.putExtra(Intent.EXTRA_EMAIL, arrayOf(email))
+            intent.putExtra(Intent.EXTRA_SUBJECT, subject)
+            intent.putExtra(Intent.EXTRA_TEXT, message)
+            intent.putExtra(Intent.EXTRA_STREAM, uri)
+            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+
+            // Grant URI permission to the chosen email application
+            val chooserIntent = Intent.createChooser(intent, "Send Email")
+            val resInfoList: List<ResolveInfo> = requireContext().packageManager.queryIntentActivities(chooserIntent, PackageManager.MATCH_DEFAULT_ONLY)
+            for (resolveInfo in resInfoList) {
+                val packageName = resolveInfo.activityInfo.packageName
+                requireContext().grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
+            }
+
+            try {
+                startActivity(chooserIntent)
+            } catch (e: ActivityNotFoundException) {
+                Toast.makeText(requireContext(), "No email client installed", Toast.LENGTH_SHORT).show()
+            }
+        } catch (e: Exception) {
+            e.printStackTrace()
+            Toast.makeText(requireContext(), "Failed to send file", Toast.LENGTH_SHORT).show()
+        }
+    }
+
+    private fun showLocationOnMap(location: String) {
+        val mapIntent = Intent(requireActivity(), MapsActivity::class.java)
+        mapIntent.putExtra("locationName", location)
+        startActivity(mapIntent)
+    }
+
     private fun showAddTransactionDialog() {
         val dialog = Dialog(requireContext())
 
@@ -102,6 +278,12 @@ class TransactionFragment : Fragment() {
             dialog.dismiss()
         }
 
+        dialog.findViewById<EditText>(R.id.locationInput).setOnClickListener {
+            // TODO: Buat percabangan kalau ada internet
+            val etLocation= dialog.findViewById<EditText>(R.id.locationInput)
+            etLocation.setText(getAddressName())
+        }
+
         dialog.show()
         dialog.window!!.setBackgroundDrawableResource(R.drawable.round_corner_transparent)
         dialog.window!!.attributes = layoutParams
@@ -190,7 +372,7 @@ class TransactionFragment : Fragment() {
         val categoryString : String = radioButton.text.toString()
 
 
-        val userID: Int = authentication.getNim();
+        val userID: Int = authentication.getNim()
         val name : String = view.findViewById<EditText>(R.id.nameInput).text.toString()
         var category : TransactionCategory = TransactionCategory.INCOME
         if (categoryString === this.getString(R.string.income)){
@@ -214,28 +396,38 @@ class TransactionFragment : Fragment() {
         }
     }
 
-    private fun inputCheck(name: String,  nominal:Float, location:String): Boolean {
-        return !(TextUtils.isEmpty(name) || TextUtils.isEmpty(location) || (nominal<0) )
+    private fun setLocation(){
+        if (ActivityCompat.checkSelfPermission(
+                requireContext(),
+                Manifest.permission.ACCESS_FINE_LOCATION
+            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
+                requireContext(),
+                Manifest.permission.ACCESS_COARSE_LOCATION
+            ) != PackageManager.PERMISSION_GRANTED
+        ) {
+            ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), permissionCode)
+            return
+        }
+        fusedLocationProviderClient.lastLocation.addOnSuccessListener (requireActivity()){
+                location ->
+            currentLocation = location
+        }
     }
 
-    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 Transaction.
-         */
-        // TODO: Rename and change types and number of parameters
-        @JvmStatic
-        fun newInstance(param1: String, param2: String) =
-            TransactionFragment().apply {
-                arguments = Bundle().apply {
-                    putString(ARG_PARAM1, param1)
-                    putString(ARG_PARAM2, param2)
-                }
-            }
+    @Suppress("DEPRECATION")
+    private fun getAddressName() : String{
+        // Hanya set otomatis jika mendapatkan permission, jika tidak input lokasi string
+        setLocation()
+        return if (currentLocation!=null){
+            // Geocode to get the address string
+            val address = geocoder.getFromLocation(currentLocation!!.latitude, currentLocation!!.longitude, 1)
+            address!![0].getAddressLine(0)
+        } else {
+            ""
+        }
     }
 
+    private fun inputCheck(name: String,  nominal:Float, location:String): Boolean {
+        return !(TextUtils.isEmpty(name) || TextUtils.isEmpty(location) || (nominal<0) )
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/nerbos/repository/TransactionRepository.kt b/app/src/main/java/com/example/nerbos/repository/TransactionRepository.kt
index 167b34a..378a6f2 100644
--- a/app/src/main/java/com/example/nerbos/repository/TransactionRepository.kt
+++ b/app/src/main/java/com/example/nerbos/repository/TransactionRepository.kt
@@ -3,6 +3,7 @@ package com.example.nerbos.repository
 import androidx.lifecycle.LiveData
 import com.example.nerbos.model.Transaction
 import com.example.nerbos.data.TransactionDao
+import com.example.nerbos.model.TransactionCategory
 import com.example.nerbos.service.Authentication
 
 class TransactionRepository(private val transactionDao: TransactionDao) {
@@ -22,4 +23,8 @@ class TransactionRepository(private val transactionDao: TransactionDao) {
     fun setReadAllDataUserId(userID: Int){
         readAllData = transactionDao.readAllData(userID)
     }
+
+    fun getSum(userID: Int, category: TransactionCategory): LiveData<Float>{
+        return transactionDao.getSum(userID, category)
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/nerbos/service/Authentication.kt b/app/src/main/java/com/example/nerbos/service/Authentication.kt
index 62cde48..ca362b7 100644
--- a/app/src/main/java/com/example/nerbos/service/Authentication.kt
+++ b/app/src/main/java/com/example/nerbos/service/Authentication.kt
@@ -84,7 +84,7 @@ class Authentication(private val context: Context) {
         return email.toInt()
     }
 
-    private fun getToken(): String {
+    internal fun getToken(): String {
         val sharedPreferences : SharedPreferences =
             context.getSharedPreferences(context.getString(R.string.preferences), Context.MODE_PRIVATE)
         return sharedPreferences.getString(context.getString(R.string.token), "") ?: ""
diff --git a/app/src/main/java/com/example/nerbos/viewmodel/TransactionViewModel.kt b/app/src/main/java/com/example/nerbos/viewmodel/TransactionViewModel.kt
index 5008192..ed27c0a 100644
--- a/app/src/main/java/com/example/nerbos/viewmodel/TransactionViewModel.kt
+++ b/app/src/main/java/com/example/nerbos/viewmodel/TransactionViewModel.kt
@@ -6,6 +6,7 @@ import androidx.lifecycle.LiveData
 import androidx.lifecycle.viewModelScope
 import com.example.nerbos.model.Transaction
 import com.example.nerbos.data.TransactionDatabase
+import com.example.nerbos.model.TransactionCategory
 import com.example.nerbos.repository.TransactionRepository
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -41,4 +42,8 @@ class TransactionViewModel(application: Application): AndroidViewModel(applicati
         repository.setReadAllDataUserId(userId)
         readAllData = repository.readAllData
     }
+
+    fun getSums(userId: Int, category: TransactionCategory): LiveData<Float>{
+        return repository.getSum(userId, category)
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/res/drawable/border_save_selection.xml b/app/src/main/res/drawable/border_save_selection.xml
new file mode 100644
index 0000000..8983d53
--- /dev/null
+++ b/app/src/main/res/drawable/border_save_selection.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/base_bg" />
+        </shape>
+    </item>
+    <item android:bottom="2dp">
+        <shape android:shape="rectangle">
+            <solid android:color="@color/white"/>
+        </shape>
+    </item>
+</layer-list>
diff --git a/app/src/main/res/drawable/button_save.png b/app/src/main/res/drawable/button_save.png
new file mode 100644
index 0000000000000000000000000000000000000000..14956f537e47b091899be3f8065e3bd6994f2570
GIT binary patch
literal 6110
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

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable/button_send.png b/app/src/main/res/drawable/button_send.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a201a3dbd4d4953bb3cce8089a952608fa62835
GIT binary patch
literal 6043
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
V5zce>;imuq002ovPDHLkV1oIzxPSlv

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable/ic_back.xml b/app/src/main/res/drawable/ic_back.xml
new file mode 100644
index 0000000..15e0dcc
--- /dev/null
+++ b/app/src/main/res/drawable/ic_back.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M11.67,3.87L9.9,2.1 0,12l9.9,9.9 1.77,-1.77L3.54,12z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/ic_circle.xml b/app/src/main/res/drawable/ic_circle.xml
new file mode 100644
index 0000000..f974c60
--- /dev/null
+++ b/app/src/main/res/drawable/ic_circle.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="15dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="15dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/round_corner_save_selection.xml b/app/src/main/res/drawable/round_corner_save_selection.xml
new file mode 100644
index 0000000..8fc1858
--- /dev/null
+++ b/app/src/main/res/drawable/round_corner_save_selection.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/white" />
+    <corners android:bottomLeftRadius="10dp" android:bottomRightRadius="10dp" />
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml
index 8a9966c..f2c30fb 100644
--- a/app/src/main/res/layout-land/activity_main.xml
+++ b/app/src/main/res/layout-land/activity_main.xml
@@ -11,7 +11,6 @@
         android:id="@+id/appBarLayout"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:background="@color/primary_bg"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent">
@@ -45,16 +44,13 @@
         android:layout_width="78dp"
         android:layout_height="0dp"
 
-        android:background="@color/primary_bg"
+        android:background="@color/navbar_bg"
         app:elevation="1dp"
         app:itemBackground="@color/navbar_bg"
         app:itemIconTint="@color/navbar_icon"
-        app:itemTextColor="@color/navbar_icon"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintHorizontal_bias="1.0"
         app:layout_constraintTop_toBottomOf="@+id/appBarLayout"
-        app:layout_constraintVertical_bias="1.0"
         app:menu="@menu/bottom_navbar">
 
     </com.google.android.material.navigation.NavigationView>
diff --git a/app/src/main/res/layout-land/fragment_statistic.xml b/app/src/main/res/layout-land/fragment_statistic.xml
new file mode 100644
index 0000000..b810818
--- /dev/null
+++ b/app/src/main/res/layout-land/fragment_statistic.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+    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:fillViewport="true"
+    android:background="@color/primary_bg"
+    tools:context=".fragments.statistic.StatisticFragment">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <TextView
+            android:id="@+id/titleChart"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="44dp"
+            android:gravity="center"
+            android:padding="10dp"
+            android:text="@string/pie_chart_title"
+            android:textAlignment="center"
+            android:textColor="@color/chart_title"
+            android:textSize="24sp"
+            android:textStyle="bold"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintStart_toEndOf="@+id/pieChart"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <com.github.mikephil.charting.charts.PieChart
+            android:id="@+id/pieChart"
+            android:layout_width="300dp"
+            android:layout_height="300dp"
+            android:layout_marginStart="52dp"
+            android:layout_weight="1"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="0.5" />
+
+        <TextView
+            android:id="@+id/totalIncome"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content"
+            android:layout_margin="3dp"
+            android:layout_marginStart="8dp"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:padding="4dp"
+            android:text="@string/income"
+            android:textAlignment="center"
+            android:textColor="@color/chart_title"
+            app:drawableLeftCompat="@drawable/ic_circle"
+            app:drawableTint="@color/purple_200"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@+id/totalOutcome"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintStart_toEndOf="@+id/pieChart"
+            app:layout_constraintTop_toBottomOf="@+id/titleChart"
+            app:layout_constraintVertical_bias="0.2" />
+
+        <TextView
+            android:id="@+id/totalOutcome"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content"
+            android:layout_margin="3dp"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:padding="4dp"
+            android:text="@string/outcome"
+            android:textAlignment="center"
+            android:textColor="@color/chart_title"
+            app:drawableLeftCompat="@drawable/ic_circle"
+            app:drawableTint="@color/red"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintStart_toEndOf="@+id/totalIncome"
+            app:layout_constraintTop_toBottomOf="@+id/titleChart"
+            app:layout_constraintVertical_bias="0.2" />
+
+        <TextView
+            android:id="@+id/totalIncomeNumber"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="3dp"
+            android:layout_marginStart="20dp"
+            android:layout_marginTop="16dp"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:padding="4dp"
+            android:text="@string/income"
+            android:textAlignment="center"
+            android:textColor="@color/chart_title"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@+id/totalOutcomeNumber"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintStart_toEndOf="@+id/pieChart"
+            app:layout_constraintTop_toBottomOf="@+id/totalIncome"
+            app:layout_constraintVertical_bias="0.2" />
+
+        <TextView
+            android:id="@+id/totalOutcomeNumber"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="3dp"
+            android:layout_marginTop="24dp"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:padding="4dp"
+            android:text="@string/outcome"
+            android:textAlignment="center"
+            android:textColor="@color/chart_title"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintStart_toEndOf="@+id/totalIncomeNumber"
+            app:layout_constraintTop_toBottomOf="@+id/totalOutcome"
+            app:layout_constraintVertical_bias="0.200" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/fragment_user.xml b/app/src/main/res/layout-land/fragment_user.xml
new file mode 100644
index 0000000..05f64b5
--- /dev/null
+++ b/app/src/main/res/layout-land/fragment_user.xml
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout 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"
+    tools:context=".fragments.user.UserFragment"
+    android:background="@color/primary_bg">
+
+    <!-- TODO: Update blank fragment layout -->
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/primary_bg">
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/logoutCard"
+            android:layout_width="400dp"
+            android:layout_height="310dp"
+            app:cardBackgroundColor="@color/login_card_bg"
+            app:cardCornerRadius="20dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <androidx.constraintlayout.widget.ConstraintLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:padding="4dp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintHorizontal_bias="0.5"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintVertical_bias="0.5">
+
+                <TextView
+                    android:id="@+id/logoutTitle"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="15dp"
+                    android:gravity="center_horizontal"
+                    android:text="@string/welcome"
+                    android:textColor="@color/white"
+                    android:textSize="40sp"
+                    android:visibility="visible"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+
+                <TextView
+                    android:id="@+id/logoutDescription"
+                    android:layout_width="341dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="5dp"
+                    android:gravity="center_horizontal"
+                    android:text="@string/app_name_full"
+                    android:textColor="@color/text_secondary"
+                    android:textSize="20sp"
+                    android:visibility="visible"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toBottomOf="@+id/logoutTitle" />
+
+                <androidx.cardview.widget.CardView
+                    android:id="@+id/cardView4"
+                    android:layout_width="330dp"
+                    android:layout_height="50dp"
+                    android:layout_gravity="center_horizontal"
+                    app:cardBackgroundColor="@color/login_input_field"
+                    app:cardCornerRadius="20dp"
+                    app:layout_constraintBottom_toTopOf="@+id/cardView5"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintHorizontal_bias="0.548"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toBottomOf="@+id/logoutDescription"
+                    app:layout_constraintVertical_bias="0.361">
+
+                    <TextView
+                        android:id="@+id/textViewUser"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center"
+                        android:text=""
+                        android:textColor="@color/white"
+                        android:textSize="20sp" />
+
+                </androidx.cardview.widget.CardView>
+
+                <androidx.cardview.widget.CardView
+                    android:id="@+id/cardView5"
+                    android:layout_width="330dp"
+                    android:layout_height="50dp"
+                    android:layout_gravity="center_horizontal"
+                    android:layout_marginTop="75dp"
+                    app:cardBackgroundColor="@color/login_input_field"
+                    app:cardCornerRadius="20dp"
+                    app:layout_constraintBottom_toTopOf="@+id/button"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintHorizontal_bias="0.548"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toBottomOf="@+id/logoutDescription"
+                    app:layout_constraintVertical_bias="0.0">
+
+                    <TextView
+                        android:id="@+id/textViewEmail"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center"
+                        android:text=""
+                        android:textColor="@color/white"
+                        android:textSize="20sp" />
+
+                </androidx.cardview.widget.CardView>
+
+                <Button
+                    android:id="@+id/button"
+                    android:layout_width="130dp"
+                    android:layout_height="55dp"
+                    android:layout_gravity="center_horizontal"
+                    android:layout_marginTop="135dp"
+                    android:labelFor="@id/button"
+                    android:text="@string/logout_title"
+                    android:textColor="@color/white"
+                    android:textSize="20sp"
+                    app:backgroundTint="@color/dark_red"
+                    app:layout_constraintBottom_toBottomOf="parent"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintHorizontal_bias="0.497"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toBottomOf="@+id/logoutDescription"
+                    app:layout_constraintVertical_bias="0.1" />
+
+            </androidx.constraintlayout.widget.ConstraintLayout>
+
+        </androidx.cardview.widget.CardView>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_maps.xml b/app/src/main/res/layout/activity_maps.xml
new file mode 100644
index 0000000..827f664
--- /dev/null
+++ b/app/src/main/res/layout/activity_maps.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MapsActivity"
+    >
+
+    <fragment
+        xmlns:map="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/map"
+        android:name="com.google.android.gms.maps.SupportMapFragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        map:layout_constraintTop_toTopOf="parent"
+    />
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="24sp"
+        android:textColor="@color/white"
+        android:background="@color/base_bg"
+        android:text="@string/maps"
+        android:paddingTop = "5dp"
+        android:paddingStart="40dp"
+        android:paddingEnd="10dp"
+        android:paddingBottom="10dp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toEndOf="@id/backButton"
+        />
+
+    <ImageView
+        android:id="@+id/backButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="10dp"
+        android:paddingBottom="5dp"
+        android:src="@drawable/ic_back"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        />
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout/add_scan_transaction.xml b/app/src/main/res/layout/add_scan_transaction.xml
new file mode 100644
index 0000000..5208078
--- /dev/null
+++ b/app/src/main/res/layout/add_scan_transaction.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout 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="wrap_content"
+    android:orientation="vertical"
+    android:padding="16dp">
+
+    <EditText
+        android:id="@+id/nameInput"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:hint="@string/name"
+        android:inputType="text"
+        android:autofillHints="Transaction Name" />
+
+    <RadioGroup
+        android:id="@+id/transactionTypeRadioGroup"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:orientation="horizontal">
+
+        <RadioButton
+            android:id="@+id/incomeRadioButton"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/income" />
+
+        <RadioButton
+            android:id="@+id/outcomeRadioButton"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/outcome" />
+
+    </RadioGroup>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="horizontal">
+
+        <EditText
+            android:id="@+id/locationInput"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:autofillHints="Location Name"
+            android:hint="@string/location"
+            android:inputType="text"
+            android:layout_marginEnd="20dp"
+            tools:ignore="NestedWeights" />
+
+        <ImageButton
+            android:id="@+id/mapButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:contentDescription="@string/maps"
+            android:background="@color/transparent"
+            app:srcCompat="@android:drawable/ic_dialog_map" />
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_scan.xml b/app/src/main/res/layout/fragment_scan.xml
index 193a738..3d5bb31 100644
--- a/app/src/main/res/layout/fragment_scan.xml
+++ b/app/src/main/res/layout/fragment_scan.xml
@@ -3,14 +3,66 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:background="@color/primary_bg"
     tools:context=".fragments.scan.ScanFragment">
 
-    <!-- TODO: Update blank fragment layout -->
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="Scan Fragment"
-        android:textSize="26dp"
-        android:layout_gravity="center" />
+    <LinearLayout
+        android:id="@+id/scanVerticalLayout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        tools:ignore="UselessParent">
+
+        <Space
+            android:layout_width="match_parent"
+            android:layout_height="50dp" />
+
+        <androidx.camera.view.PreviewView
+            android:id="@+id/previewView"
+            android:layout_width="300dp"
+            android:layout_height="400dp"
+            android:layout_gravity="center" />
+
+        <LinearLayout
+            android:id="@+id/scanHorizontalLayout"
+            android:layout_width="300dp"
+            android:layout_height="160dp"
+            android:layout_gravity="center"
+            android:orientation="horizontal">
+
+            <ImageButton
+                android:id="@+id/captureButton"
+                android:layout_width="75dp"
+                android:layout_height="75dp"
+                android:layout_gravity="center"
+                android:background="@drawable/round_corner_button_weak"
+                android:contentDescription="@string/capture_button"
+                android:scaleType="fitCenter"
+                android:src="@android:drawable/ic_menu_camera"
+                tools:ignore="RedundantDescriptionCheck" />
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1" />
+
+            <ImageButton
+                android:id="@+id/galleryButton"
+                android:layout_width="75dp"
+                android:layout_height="75dp"
+                android:layout_gravity="center"
+                android:background="@drawable/round_corner_button_weak"
+                android:contentDescription="@string/gallery_button"
+                android:scaleType="fitCenter"
+                android:src="@android:drawable/ic_menu_gallery"
+                tools:ignore="RedundantDescriptionCheck" />
+
+        </LinearLayout>
+
+        <Space
+            android:layout_width="match_parent"
+            android:layout_height="125dp" />
+
+    </LinearLayout>
 
 </FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_statistic.xml b/app/src/main/res/layout/fragment_statistic.xml
index c90a5c1..ba705c8 100644
--- a/app/src/main/res/layout/fragment_statistic.xml
+++ b/app/src/main/res/layout/fragment_statistic.xml
@@ -1,16 +1,120 @@
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout
+    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:fillViewport="true"
+    android:background="@color/primary_bg"
     tools:context=".fragments.statistic.StatisticFragment">
 
-    <!-- TODO: Update blank fragment layout -->
-    <TextView
-        android:layout_width="wrap_content"
+    <RelativeLayout
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:text="Statistic Fragment"
-        android:textSize="26dp"
-        android:layout_gravity="center" />
+        android:layout_marginTop="0dp"
+        android:layout_gravity="center"
+        tools:ignore="UselessParent">
+
+        <TextView
+            android:id="@+id/titleChart"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:padding="10dp"
+            android:text="@string/pie_chart_title"
+            android:textAlignment="center"
+            android:textColor="@color/chart_title"
+            android:textSize="25sp"
+            android:textStyle="bold" />
+
+
+        <!--Ui component for our pie chart-->
+        <com.github.mikephil.charting.charts.PieChart
+            android:id="@+id/pieChart"
+            android:layout_width="300dp"
+            android:layout_height="300dp"
+            android:layout_below="@+id/titleChart"
+            android:layout_alignParentEnd="false"
+            android:layout_centerHorizontal="true"
+            android:layout_marginTop="20dp"
+            android:layout_marginBottom="5dp" />
+
+        <LinearLayout
+            android:id="@+id/totalIncomeOutcome"
+            android:layout_width="300dp"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/pieChart"
+            android:layout_marginTop="25dp"
+            android:orientation="horizontal"
+            android:layout_centerHorizontal="true"
+            android:weightSum="2">
+
+            <TextView
+                android:id="@+id/totalIncome"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_margin="3dp"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:padding="4dp"
+                android:text="@string/income"
+                android:textAlignment="center"
+                android:textColor="@color/chart_title"
+                app:drawableTint="@color/purple_200"
+                app:drawableLeftCompat="@drawable/ic_circle" />
+
+            <TextView
+                android:id="@+id/totalOutcome"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_margin="3dp"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:padding="4dp"
+                android:text="@string/outcome"
+                android:textAlignment="center"
+                android:textColor="@color/chart_title"
+                app:drawableLeftCompat="@drawable/ic_circle"
+                app:drawableTint="@color/red" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/totalIncomeOutcomeNumber"
+            android:layout_width="300dp"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/totalIncomeOutcome"
+            android:layout_marginTop="15dp"
+            android:orientation="horizontal"
+            android:layout_centerHorizontal="true"
+            android:weightSum="2">
+
+            <TextView
+                android:id="@+id/totalIncomeNumber"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_margin="3dp"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:padding="4dp"
+                android:text="@string/income"
+                android:textAlignment="center"
+                android:textColor="@color/chart_title" />
+
+            <TextView
+                android:id="@+id/totalOutcomeNumber"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_margin="3dp"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:padding="4dp"
+                android:text="@string/outcome"
+                android:textAlignment="center"
+                android:textColor="@color/chart_title" />
+
+        </LinearLayout>
+
+    </RelativeLayout>
 
 </FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_transaction.xml b/app/src/main/res/layout/fragment_transaction.xml
index 690063b..10f6f89 100644
--- a/app/src/main/res/layout/fragment_transaction.xml
+++ b/app/src/main/res/layout/fragment_transaction.xml
@@ -13,20 +13,40 @@
         android:background="@color/base_bg"
         android:padding="20dp"
         >
+        <ImageView
+            android:id="@+id/saveTransactionsButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/button_save"
 
+            android:layout_marginStart="10dp"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            android:contentDescription="@string/save_transactions_button" />
+
+        <ImageView
+            android:id="@+id/sendTransactionsButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/button_send"
+            android:layout_marginEnd="10dp"
+
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            android:contentDescription="@string/send_transactions_button" />
 
         <TextView
             android:id="@+id/myTransaction"
             android:layout_width="0sp"
             android:layout_height="wrap_content"
-            android:text="My Transaction"
+            android:text="@string/my_transaction"
             android:textColor="@color/white"
             android:textSize="20sp"
             android:textStyle="bold"
             android:layout_marginStart="10dp"
-            android:layout_marginTop="8dp"
+            android:layout_marginTop="40dp"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/saveTransactionsButton"
         />
 
         <ImageView
@@ -34,12 +54,10 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:background="@drawable/plus"
-            app:layout_constraintTop_toTopOf="parent"
+            android:layout_marginTop="30dp"
+            app:layout_constraintTop_toBottomOf="@id/sendTransactionsButton"
             app:layout_constraintEnd_toEndOf="parent"
-            > </ImageView>
-
-
-
+            android:contentDescription="@string/add_transaction_button"/>
 
         <androidx.recyclerview.widget.RecyclerView
             android:layout_width="match_parent"
@@ -49,5 +67,81 @@
             app:layout_constraintTop_toBottomOf="@+id/myTransaction"
         />
 
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_marginStart="28dp"
+            android:background="@drawable/round_corner_save_selection"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/saveTransactionsButton" >
+
+            <TextView
+                android:id="@+id/xlsSave"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/xls"
+                android:background="@drawable/border_save_selection"
+                android:textSize="18sp"
+                android:textColor="@color/base_bg"
+                android:paddingTop="2dp"
+                android:paddingStart="30dp"
+                android:paddingBottom="4dp"
+                android:paddingEnd="30dp"
+                android:visibility="gone"
+                />
+
+            <TextView
+                android:id="@+id/xlsxSave"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="18sp"
+                android:textColor="@color/base_bg"
+                android:text="@string/xlsx"
+                android:paddingBottom="3dp"
+                android:paddingStart="26dp"
+                android:paddingEnd="30dp"
+                android:visibility="gone"
+                />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_marginStart="290dp"
+            android:background="@drawable/round_corner_save_selection"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/sendTransactionsButton" >
+
+            <TextView
+                android:id="@+id/xlsSend"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/xls"
+                android:background="@drawable/border_save_selection"
+                android:textSize="18sp"
+                android:textColor="@color/base_bg"
+                android:paddingTop="2dp"
+                android:paddingStart="30dp"
+                android:paddingBottom="4dp"
+                android:paddingEnd="30dp"
+                android:visibility="gone"
+                />
+
+            <TextView
+                android:id="@+id/xlsxSend"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="18sp"
+                android:textColor="@color/base_bg"
+                android:text="@string/xlsx"
+                android:paddingBottom="3dp"
+                android:paddingStart="26dp"
+                android:paddingEnd="30dp"
+                android:visibility="gone"
+                />
+        </LinearLayout>
+
     </androidx.constraintlayout.widget.ConstraintLayout>
 </FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 20f56ac..e702056 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -20,4 +20,15 @@
     <color name="strong_blue_button">#0DA6C2</color>
     <color name="weak_blue_button">#800DA6C2</color>
     <color name="transparent">#00FFFFFF</color>
+
+    <color name="purple_200">#0F9D58</color>
+    <color name="purple_500">#0F9D58</color>
+    <color name="purple_700">#0F9D58</color>
+    <color name="teal_200">#FF03DAC5</color>
+    <color name="teal_700">#FF018786</color>
+    <color name="grey">#aaa</color>
+    <color name="button_color">#1E573B</color>
+    <color name="yellow">#FFEB3B</color>
+    <color name="chart_title">#EAE9F6</color>
+
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 93156b4..4850983 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -23,6 +23,7 @@
 
     <string name="backend_api_login">https://pbd-backend-2024.vercel.app/api/auth/login</string>
     <string name="backend_api_token">https://pbd-backend-2024.vercel.app/api/auth/token</string>
+    <string name="backend_api_scan">https://pbd-backend-2024.vercel.app/api/bill/upload</string>
 
     <string name="ok">OK</string>
     <string name="welcome">Welcome!</string>
@@ -46,7 +47,25 @@
     <string name="delete_success">Transaction successfully deleted</string>
     <string name="income">Income</string>
     <string name="outcome">Outcome</string>
+    <string name="title_activity_maps">MapsActivity</string>
+    <string name="maps">Maps</string>
     <string name="network_sensing_string">No Internet Connection Please check your connection and try again.</string>
     <string name="close_button">Close Button</string>
+    <string name="pie_chart">Pie Chart</string>
+    <string name="my_transaction">My Transaction</string>
+    <string name="add_transaction_button">Add Transaction Button</string>
+    <string name="send">Send</string>
+    <string name="save_transactions_button">Save Transactions Button</string>
+    <string name="send_transactions_button">Send Transactions Button</string>
+    <string name="xls">.xls</string>
+    <string name="xlsx">.xlsx</string>
+    <string name="pie_chart_title">Income vs Outcome Chart</string>
+    <string name="language_code">id</string>
+    <string name="country_code">ID</string>
+    <string name="capture_button">Capture Button</string>
+    <string name="gallery_button">Gallery Button</string>
+
+    <string name="email_subject">NerbOS - Transaction Report</string>
+    <string name="email_message">Here is your transaction report</string>
 
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml
new file mode 100644
index 0000000..660be41
--- /dev/null
+++ b/app/src/main/res/xml/provider_paths.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths>
+    <cache-path
+        name="cache"
+        path="/" />
+</paths>
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 98de0e4..1764a12 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,4 +2,5 @@
 plugins {
     id("com.android.application") version "8.3.1" apply false
     id("org.jetbrains.kotlin.android") version "1.9.22" apply false
+    id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin") version "2.0.1" apply false
 }
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 3c5031e..2e7e7ed 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -20,4 +20,5 @@ kotlin.code.style=official
 # Enables namespacing of each library's R class so that its R class includes only the
 # resources declared in the library itself and none from the library's dependencies,
 # thereby reducing the size of the R class for that library
-android.nonTransitiveRClass=true
\ No newline at end of file
+android.nonTransitiveRClass=true
+org.gradle.configuration-cache=true
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index f4f7ca0..f677740 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -9,6 +9,7 @@ dependencyResolutionManagement {
     repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
     repositories {
         google()
+        maven { url = uri("https://jitpack.io") }
         mavenCentral()
     }
 }
-- 
GitLab