diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a1fd2442e6d4e8cc20603bd6cc8a0461dc9c6e94..5ab29eb77c06d72ba595ade50586e2ac01386fe2 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 { @@ -50,6 +51,8 @@ dependencies { 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") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e52baae0b52c0d7b23950ee6472a45ee56941c00..be7d3d82f7d11567fd5de6b97e21e8e5d6f85500 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ <?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" /> @@ -12,6 +13,15 @@ android:supportsRtl="true" android:theme="@style/Theme.NerbOS"> + <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 0000000000000000000000000000000000000000..d4ccc07f8f2b97f2bbf2647fb5aab32968e20445 --- /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/fragments/transaction/TransactionAdapter.kt b/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionAdapter.kt index a2a9c178c266735d43c209a65dc6e3c8eaaef612..ae9cd893897417f02840a5358385352c0f221772 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,10 @@ class TransactionAdapter() : RecyclerView.Adapter<TransactionAdapter.Transaction holder.itemView.setOnClickListener{ onItemClick?.invoke(transactionList[position]) } + + holder.location.setOnClickListener { + onLocationClick?.invoke(holder.location.text.toString()) + } } @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 14b0558317ff5d3a1d459d7ec05c5061e134296f..3a82a2c30bcd5f0fcc1dee02f6209844a1aca870 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,7 +1,13 @@ package com.example.nerbos.fragments.transaction +import android.Manifest import android.app.AlertDialog import android.app.Dialog +import android.content.Intent +import android.content.pm.PackageManager +import android.location.Address +import android.location.Geocoder +import android.location.Location import android.os.Bundle import android.text.TextUtils import android.util.Log @@ -17,17 +23,24 @@ import android.widget.RadioButton import android.widget.RadioGroup import android.widget.TextView import android.widget.Toast +import androidx.core.app.ActivityCompat 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.google.android.gms.location.FusedLocationProviderClient +import com.google.android.gms.location.LocationServices +import com.google.android.gms.maps.CameraUpdateFactory +import com.google.android.gms.maps.model.LatLng +import java.util.Locale // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER @@ -45,6 +58,10 @@ class TransactionFragment : Fragment() { 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) @@ -52,6 +69,8 @@ class TransactionFragment : Fragment() { param1 = it.getString(ARG_PARAM1) param2 = it.getString(ARG_PARAM2) } + fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(requireContext()) + geocoder = Geocoder(requireContext(), Locale.getDefault()) } override fun onCreateView( @@ -67,6 +86,7 @@ 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] @@ -82,6 +102,12 @@ class TransactionFragment : Fragment() { return view } + 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 +128,14 @@ class TransactionFragment : Fragment() { dialog.dismiss() } + dialog.findViewById<EditText>(R.id.locationInput).setOnClickListener { + // TODO: Buat percabangan kalau ada internet + if (true){ + 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 @@ -214,6 +248,36 @@ class TransactionFragment : Fragment() { } } + 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(android.Manifest.permission.ACCESS_FINE_LOCATION), permissionCode) + return + } + fusedLocationProviderClient.lastLocation.addOnSuccessListener (requireActivity()){ + location -> + currentLocation = location + } + } + + 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) ) } 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 0000000000000000000000000000000000000000..15e0dcca3b0376164ca26f1b1648fe223aa976f8 --- /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/layout/activity_maps.xml b/app/src/main/res/layout/activity_maps.xml new file mode 100644 index 0000000000000000000000000000000000000000..827f664a41139e37d9f993860a71e7afeac921b6 --- /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/values/strings.xml b/app/src/main/res/values/strings.xml index 343b4c377de55b935141ff6ae6d1579d790ea1b1..b51f00d92c3c5c419c3fcef21dbcad43b7184dca 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -46,6 +46,8 @@ <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> diff --git a/build.gradle.kts b/build.gradle.kts index 98de0e48fee24c2754decc4211bfc712e0299e5f..1764a12d184f94f866991078a28422563e8d4935 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