diff --git a/app/build.gradle b/app/build.gradle
index 16f2993..cc18ac3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
+ id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
android {
@@ -40,4 +41,7 @@ dependencies {
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
+ implementation "androidx.fragment:fragment-ktx:1.5.5"
+ implementation 'org.osmdroid:osmdroid-android:6.1.14'
+
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a2340f2..7804d9a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,14 @@
+ xmlns:tools="http://schemas.android.com/tools" >
+
+
+
+
+
+
+
+
-
-
+ tools:targetApi="31" >
+
-
-
+
+
-
-
\ No newline at end of file
diff --git a/app/src/main/java/uca/baptistearthur/geocaching/List.kt b/app/src/main/java/uca/baptistearthur/geocaching/List.kt
new file mode 100644
index 0000000..d082f99
--- /dev/null
+++ b/app/src/main/java/uca/baptistearthur/geocaching/List.kt
@@ -0,0 +1,59 @@
+package uca.baptistearthur.geocaching
+
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+
+// 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 [List.newInstance] factory method to
+ * create an instance of this fragment.
+ */
+class List : 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)
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_list, 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 List.
+ */
+ // TODO: Rename and change types and number of parameters
+ @JvmStatic
+ fun newInstance(param1: String, param2: String) =
+ List().apply {
+ arguments = Bundle().apply {
+ putString(ARG_PARAM1, param1)
+ putString(ARG_PARAM2, param2)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/uca/baptistearthur/geocaching/MainWindow.kt b/app/src/main/java/uca/baptistearthur/geocaching/MainWindow.kt
index 1730b46..0774e7a 100644
--- a/app/src/main/java/uca/baptistearthur/geocaching/MainWindow.kt
+++ b/app/src/main/java/uca/baptistearthur/geocaching/MainWindow.kt
@@ -2,12 +2,41 @@ package uca.baptistearthur.geocaching
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import com.google.android.material.bottomnavigation.BottomNavigationView
+import org.osmdroid.config.Configuration
class MainWindow: AppCompatActivity() {
-
override fun onCreate(savedInstanceState: Bundle?) {
+
super.onCreate(savedInstanceState)
setContentView(R.layout.main_window)
+
+ loadFragment(Map())
+ val navigation = findViewById(R.id.bottom_navigation)
+ navigation.selectedItemId= R.id.map
+ navigation.setOnItemSelectedListener {
+ when (it.itemId) {
+ R.id.profile -> {
+ loadFragment(Profile())
+ true
+ }
+ R.id.map -> {
+ loadFragment(Map())
+ true
+ }
+ R.id.list -> {
+ loadFragment(List())
+ true
+ }
+ else -> false
+ }
+ }
+ }
+ private fun loadFragment(fragment: Fragment){
+ val transaction = supportFragmentManager.beginTransaction()
+ transaction.replace(R.id.fragment_container, fragment)
+ transaction.commit()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/uca/baptistearthur/geocaching/Map.kt b/app/src/main/java/uca/baptistearthur/geocaching/Map.kt
new file mode 100644
index 0000000..5d66bc5
--- /dev/null
+++ b/app/src/main/java/uca/baptistearthur/geocaching/Map.kt
@@ -0,0 +1,106 @@
+package uca.baptistearthur.geocaching
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.location.LocationManager
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import org.osmdroid.config.Configuration
+import org.osmdroid.util.GeoPoint
+import org.osmdroid.views.MapView
+import org.osmdroid.views.overlay.ScaleBarOverlay
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.hardware.SensorManager
+import android.location.Location
+import android.location.LocationListener
+import androidx.activity.result.contract.ActivityResultContracts
+import org.osmdroid.views.overlay.compass.CompassOverlay
+import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
+import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider
+import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay
+
+
+class Map : Fragment() {
+ private lateinit var map : MapView
+ val defaultPoint = GeoPoint(48.8583, 2.2944)
+ var isMapCentered = false;
+ val locationListener = object : LocationListener {
+ override fun onLocationChanged(location: Location) {
+ val geoPoint = GeoPoint(location.latitude, location.longitude)
+ if(!isMapCentered){
+ map.controller.setCenter(geoPoint)
+ isMapCentered=true;
+ }
+ map.invalidate()
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ Configuration.getInstance().userAgentValue = "Geocaching"
+
+ }
+
+ val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission())
+ { map.controller.setCenter(defaultPoint); }
+
+ private fun configureMap(view: View){
+ map = view.findViewById(R.id.mapView)
+ map.controller.setZoom(20.0);
+ if (ContextCompat.checkSelfPermission(requireActivity(), ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissionLauncher.launch(ACCESS_FINE_LOCATION)
+ }
+ map.controller.setCenter(defaultPoint)
+
+ val compassOverlay = CompassOverlay(context, InternalCompassOrientationProvider(context), map);
+ compassOverlay.enableCompass();
+ map.getOverlays().add(compassOverlay);
+
+ val scaleBarOverlay = ScaleBarOverlay(map)
+ scaleBarOverlay.setAlignRight(true)
+ map.overlays.add(scaleBarOverlay)
+
+ val myLocation = MyLocationNewOverlay(GpsMyLocationProvider(context), map)
+ myLocation.enableFollowLocation()
+ myLocation.enableMyLocation()
+ map.overlays.add(myLocation)
+
+ val recenter = RecenterOverlay(GpsMyLocationProvider(context), map)
+ recenter.enableMyLocation()
+ map.overlays.add(recenter);
+
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ // Inflate the layout for this fragment
+ val view = inflater.inflate(R.layout.fragment_map, container, false)
+ configureMap(view)
+ return view
+ }
+
+ override fun onResume() {
+ super.onResume()
+ if (ContextCompat.checkSelfPermission(requireActivity(), ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
+ val locationManager = requireActivity().getSystemService(Context.LOCATION_SERVICE) as LocationManager
+ locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, locationListener)
+ }
+ map.onResume() //needed for compass, my location overlays, v6.0.0 and up
+ }
+
+ override fun onPause() {
+ super.onPause()
+ if (ContextCompat.checkSelfPermission(requireActivity(), ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
+ val locationManager = requireActivity().getSystemService(Context.LOCATION_SERVICE) as LocationManager
+ locationManager.removeUpdates(locationListener)
+ isMapCentered=false;
+ }
+ map.onPause() //needed for compass, my location overlays, v6.0.0 and up
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/uca/baptistearthur/geocaching/Profile.kt b/app/src/main/java/uca/baptistearthur/geocaching/Profile.kt
new file mode 100644
index 0000000..5cca43b
--- /dev/null
+++ b/app/src/main/java/uca/baptistearthur/geocaching/Profile.kt
@@ -0,0 +1,59 @@
+package uca.baptistearthur.geocaching
+
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+
+// 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 [Profile.newInstance] factory method to
+ * create an instance of this fragment.
+ */
+class Profile : 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)
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_profile, 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 Profile.
+ */
+ // TODO: Rename and change types and number of parameters
+ @JvmStatic
+ fun newInstance(param1: String, param2: String) =
+ Profile().apply {
+ arguments = Bundle().apply {
+ putString(ARG_PARAM1, param1)
+ putString(ARG_PARAM2, param2)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/uca/baptistearthur/geocaching/RecenterOverlay.kt b/app/src/main/java/uca/baptistearthur/geocaching/RecenterOverlay.kt
new file mode 100644
index 0000000..47aae20
--- /dev/null
+++ b/app/src/main/java/uca/baptistearthur/geocaching/RecenterOverlay.kt
@@ -0,0 +1,93 @@
+package uca.baptistearthur.geocaching
+
+import android.graphics.*
+import android.graphics.drawable.shapes.Shape
+import android.location.Location
+import android.view.GestureDetector
+import android.view.MotionEvent
+import androidx.appcompat.widget.AppCompatDrawableManager
+import androidx.core.content.ContextCompat
+import org.osmdroid.util.GeoPoint
+import org.osmdroid.views.MapView
+import org.osmdroid.views.overlay.Overlay
+import org.osmdroid.views.overlay.mylocation.IMyLocationConsumer
+import org.osmdroid.views.overlay.mylocation.IMyLocationProvider
+
+
+class RecenterOverlay(val myLocationProvider: IMyLocationProvider, val mapView: MapView) : Overlay(), GestureDetector.OnGestureListener, IMyLocationConsumer {
+
+ private val gestureDetector: GestureDetector = GestureDetector(mapView.context, this)
+ private var circleRectF=RectF();
+
+
+ override fun draw(canvas: Canvas, mapView: MapView, shadow: Boolean) {
+
+ val circleSize = 300f
+ val circlePadding = 20f
+ val circleX = canvas.width - circleSize - circlePadding
+ val circleY = canvas.height - circleSize - circlePadding
+ circleRectF=RectF(circleX, circleY, circleX + circleSize, circleY + circleSize)
+
+ val paint = Paint().apply {
+ color = Color.WHITE
+ style = Paint.Style.FILL
+ }
+ canvas.drawCircle(
+ circleX + circleSize / 2,
+ circleY + circleSize / 2,
+ circleSize / 2,
+ paint
+ )
+
+ val iconSize = 180
+ val icon = ContextCompat.getDrawable(mapView.context, R.drawable.center)
+
+ val iconX = (circleX + circleSize / 2 - iconSize / 2).toInt()
+ val iconY = (circleY + circleSize / 2 - iconSize / 2).toInt()
+
+ icon?.setBounds(iconX, iconY, iconX + iconSize, iconY + iconSize)
+ icon?.draw(canvas)
+ }
+
+ fun enableMyLocation() {
+ myLocationProvider.startLocationProvider(this);
+ }
+
+ fun disableMyLocation(){
+ myLocationProvider.stopLocationProvider();
+ }
+
+ override fun onSingleTapConfirmed(e: MotionEvent?, mapView: MapView?) =
+ myLocationProvider.lastKnownLocation?.let {
+ if (e != null && circleRectF.contains(e.x, e.y)) {
+ mapView?.controller?.setCenter(GeoPoint(it.latitude, it.longitude))
+ mapView?.controller?.setZoom(20.0);
+
+ }
+ true
+ } ?: false
+
+
+ override fun onResume() {
+ super.onResume()
+ enableMyLocation()
+ }
+
+ override fun onPause(){
+ disableMyLocation()
+ super.onPause()
+ }
+
+ override fun onDetach(mapView: MapView?) {
+ disableMyLocation()
+ super.onDetach(mapView)
+ }
+
+ override fun onDown(p0: MotionEvent) = true
+ override fun onShowPress(p0: MotionEvent) = Unit
+ override fun onSingleTapUp(p0: MotionEvent) = true
+ override fun onScroll(p0: MotionEvent, p1: MotionEvent, p2: Float, p3: Float) = true;
+ override fun onLongPress(p0: MotionEvent) = Unit
+ override fun onFling(p0: MotionEvent, p1: MotionEvent, p2: Float, p3: Float) = true
+ override fun onLocationChanged(location: Location?, source: IMyLocationProvider?) = Unit
+}
diff --git a/app/src/main/res/drawable/center.xml b/app/src/main/res/drawable/center.xml
new file mode 100644
index 0000000..e215d44
--- /dev/null
+++ b/app/src/main/res/drawable/center.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/list.xml b/app/src/main/res/drawable/list.xml
new file mode 100644
index 0000000..206e694
--- /dev/null
+++ b/app/src/main/res/drawable/list.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/map.xml b/app/src/main/res/drawable/map.xml
new file mode 100644
index 0000000..ffd9dd9
--- /dev/null
+++ b/app/src/main/res/drawable/map.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/profile.xml b/app/src/main/res/drawable/profile.xml
new file mode 100644
index 0000000..d36b73c
--- /dev/null
+++ b/app/src/main/res/drawable/profile.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout/fragment_list.xml b/app/src/main/res/layout/fragment_list.xml
new file mode 100644
index 0000000..a037be3
--- /dev/null
+++ b/app/src/main/res/layout/fragment_list.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_map.xml b/app/src/main/res/layout/fragment_map.xml
new file mode 100644
index 0000000..6ebeadb
--- /dev/null
+++ b/app/src/main/res/layout/fragment_map.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml
new file mode 100644
index 0000000..491bbff
--- /dev/null
+++ b/app/src/main/res/layout/fragment_profile.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/main_window.xml b/app/src/main/res/layout/main_window.xml
index 1480d71..318c14e 100644
--- a/app/src/main/res/layout/main_window.xml
+++ b/app/src/main/res/layout/main_window.xml
@@ -1,66 +1,35 @@
-
-
-
-
-
-
-
-
-
-
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:background="@color/main_turquoise_700"
+ android:gravity="center_vertical"
+ android:padding="20dp"
+ android:text="@string/app_name"
+ android:textColor="@color/main_turquoise_50"
+ android:textSize="20sp"
+ android:textStyle="bold" />
-
-
-
-
-
-
+ android:layout_height="0dp"
+ android:layout_weight="8"/>
-
-
-
+
-
diff --git a/app/src/main/res/menu/bottom_tabs.xml b/app/src/main/res/menu/bottom_tabs.xml
new file mode 100644
index 0000000..d8469db
--- /dev/null
+++ b/app/src/main/res/menu/bottom_tabs.xml
@@ -0,0 +1,18 @@
+
+
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
index 4c291af..ef4f8e8 100644
--- a/app/src/main/res/values-night/themes.xml
+++ b/app/src/main/res/values-night/themes.xml
@@ -1,9 +1,9 @@
-