Compare commits

..

23 Commits

Author SHA1 Message Date
Baptiste BONNEAU 872cbea47c Update 'README.md'
2 years ago
Baptiste BONNEAU 6b5f6b0f1c Update 'README.md'
2 years ago
Baptiste BONNEAU ec29898d91 Update 'README.md'
2 years ago
Arthur VALIN 4b4f1de6b7 Improving edition map
2 years ago
Arthur VALIN d2f6389123 Merge remote-tracking branch 'origin/master'
2 years ago
Arthur VALIN 9cee2c6512 Implementing edition map into roadtrip detail
2 years ago
Baptiste BONNEAU 334ab83aae Update 'README.md'
2 years ago
Baptiste BONNEAU f2083a33eb Update 'README.md'
2 years ago
Baptiste BONNEAU 4e6ca95857 Update 'README.md'
2 years ago
Baptiiiiste b8e5a6e5a3 doc
2 years ago
Baptiiiiste aea9a3114e Logo
2 years ago
Baptiiiiste b89dfad74a Logo
2 years ago
Arthur VALIN fd33116210 Merge remote-tracking branch 'origin/master'
2 years ago
Arthur VALIN ab028cdebe Adding rate limiter to API client and clearing map after addition of a roadtrip
2 years ago
Baptiiiiste 2b7ccb39d8 deleting useless imports
2 years ago
Baptiiiiste fd20790e68 deleting useless file
2 years ago
Baptiiiiste fa2a580c63 fixing bugs and adding toasts for more informations
2 years ago
Baptiiiiste 3552beba2f deleting & displaying persisted data
2 years ago
Baptiiiiste 4942de0bd5 merge & displaying address informations on detail
2 years ago
Arthur VALIN 9b9c71474f Adding calls to reverse geocoding API using retrofit
2 years ago
Arthur VALIN e470e37101 Refactoring some dirty code with some more kotlin spirit
2 years ago
Arthur VALIN 8e1e373572 Adding Roadtrip addition dialog
2 years ago
Arthur VALIN 63100d9f98 Enabling multidex
2 years ago

@ -0,0 +1 @@
Geocaching

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

@ -1,5 +1,61 @@
J'ai pas fait grand chose car j'ai eu masse problème avec le projet, il a fallut modifier plein de choses dans les settings graddle. <p align="center">
<img src="Documentation/banner.png" />
</p>
![Kotlin](https://img.shields.io/badge/Kotlin-7F52FF.svg?style=for-the-badge&logo=Kotlin&logoColor=white)
![Android Studio](https://img.shields.io/badge/Android%20Studio-3DDC84.svg?style=for-the-badge&logo=Android-Studio&logoColor=white)
![Android](https://img.shields.io/badge/Android-3DDC84.svg?style=for-the-badge&logo=Android&logoColor=white)</br>
**RoadTrip** est une application mobile Android qui permet aux utilisateurs de créer des itinéraires de voyage.
![](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/water.png)
## 🪶 Fonctionnalités
Lors de votre arrivée sur l'application, vous retrouverez une carte avec votre emplacement actuel. </br>Naviguez sur la carte pour découvrir vos environs.
Appuyez 2 fois sur la carte pour créer un point d'intérêt, ajoutez en plusieurs afin de construire un itinéraire.
Validez ensuite votre voyage en cliquant sur le bouton ✅ en bas à gauche de l'écran et donnez lui un nom.
Retrouvez vos voyages dans l'onglet "Roadtrips" en bas de l'écran.
Accédez aux informations de votre voyage en cliquant dessus dans la liste déroulante. </br>Vous retrouverez la liste des points d'intérêts que vous avez ajouté, ainsi que les adresses de chacun d'entre eux.
Vous pouvez supprimer un voyage à tout moment avec le bouton en bas de votre écran</br>Ou vous pouvez l'éditer en rajoutant des points sur la carte et en validant.
![](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/water.png)
## 💽 Lancer l'application
Une fois le dépot cloné, vous pourrez lancer l'application sur votre téléphone Android grâce aux outils fournis par Android Studio. Si vous n'avez pas de téléphone Android, vous pouvez utiliser un émulateur.
![](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/water.png)
## 🤖 Made by
Arthur VALIN : **Arthur.VALIN@etu.uca.fr**
<a href = "https://codefirst.iut.uca.fr/git/arthur.valin">
<img src ="https://codefirst.iut.uca.fr/git/avatars/041c57af1e1d1e855876d8abb5f1c143?size=870" height="50px">
</a>
<br/>
<br/>
Baptiste BONNEAU : **Baptiste.BONNEAU@etu.uca.fr**
<a href = "https://codefirst.iut.uca.fr/git/baptiste.bonneau">
<img src ="https://codefirst.iut.uca.fr/git/avatars/e47d41c01439fc439a23cf6843310b05?size=870" height="50px">
</a>
J'ai laissé des commentaires dans la vue

@ -16,6 +16,7 @@ android {
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled = true
} }
buildTypes { buildTypes {
@ -51,5 +52,11 @@ dependencies {
implementation "androidx.fragment:fragment-ktx:1.5.6" implementation "androidx.fragment:fragment-ktx:1.5.6"
implementation 'org.osmdroid:osmdroid-android:6.1.14' implementation 'org.osmdroid:osmdroid-android:6.1.14'
implementation 'com.github.MKergall:osmbonuspack:6.9.0' implementation 'com.github.MKergall:osmbonuspack:6.9.0'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:okhttp:4.7.2'
implementation 'com.squareup.okhttp3:logging-interceptor:4.7.2'
} }

@ -4,7 +4,6 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission <uses-permission
@ -17,9 +16,9 @@
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/app_logo"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/app_logo_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.Geocaching" android:theme="@style/Theme.Geocaching"
android:enableOnBackInvokedCallback="true" android:enableOnBackInvokedCallback="true"

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -1,14 +1,16 @@
package uca.baptistearthur.geocaching.converters package uca.baptistearthur.geocaching.converters
import android.util.Log
import androidx.room.TypeConverter import androidx.room.TypeConverter
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.internal.bind.util.ISO8601Utils.format
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import uca.baptistearthur.geocaching.model.Address
import uca.baptistearthur.geocaching.model.Place import uca.baptistearthur.geocaching.model.Place
import uca.baptistearthur.geocaching.model.RoadTripEntity import uca.baptistearthur.geocaching.model.RoadTripEntity
import java.lang.String.format
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
class Converters { class Converters {
@ -41,12 +43,10 @@ class Converters {
} }
fun Date.toFrenchFormat(): String { fun Date.toFrenchFormat(): String {
val day: String = if(this.date < 10) "0${this.date}" else "${this.date}" val day: String = if(this.date < 10) "0${this.date}" else "${this.date}"
val month: String = if(this.month < 10) "0${this.month}" else "${this.month}" val month: String = if(this.month < 10) "0${this.month+1}" else "${this.month+1}"
val year = "${this.year + 1900}" val year = "${this.year + 1900}"
val hours: String = if(this.hours < 10) "0${this.hours}" else "${this.hours}" val hours: String = if(this.hours < 10) "0${this.hours}" else "${this.hours}"
val minutes: String = if(this.minutes < 10) "0${this.minutes}" else "${this.minutes}" val minutes: String = if(this.minutes < 10) "0${this.minutes}" else "${this.minutes}"
return "$day/$month/$year - ${hours}h$minutes" return "$day/$month/$year - ${hours}h$minutes"}
}

@ -3,12 +3,12 @@ package uca.baptistearthur.geocaching.data
import android.content.Context import android.content.Context
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.Database
import androidx.room.TypeConverters import androidx.room.TypeConverters
import uca.baptistearthur.geocaching.converters.Converters import uca.baptistearthur.geocaching.converters.Converters
import uca.baptistearthur.geocaching.model.RoadTripEntity import uca.baptistearthur.geocaching.model.RoadTripEntity
import androidx.room.Database
@Database(entities = [RoadTripEntity::class], version=1) @Database(entities = arrayOf(RoadTripEntity::class), version=1, exportSchema = false)
@TypeConverters(Converters::class) @TypeConverters(Converters::class)
abstract class BDD : RoomDatabase(){ abstract class BDD : RoomDatabase(){
@ -16,16 +16,13 @@ abstract class BDD : RoomDatabase(){
companion object{ companion object{
private var INSTANCE: BDD ?= null private var INSTANCE: BDD ?= null
fun getInstance(context: Context): BDD =
fun getInstance(context: Context) =
INSTANCE ?: synchronized(this){ INSTANCE ?: synchronized(this){
val db = Room.databaseBuilder(context, BDD::class.java, "roadTripDB").build() val db = Room.databaseBuilder(context, BDD::class.java, "roadTripDB").build()
INSTANCE = db INSTANCE = db
INSTANCE!! INSTANCE!!
} }
} }
} }

@ -16,7 +16,7 @@ interface RoadTripDAO {
@Delete @Delete
suspend fun deleteRoadTrip(r: RoadTripEntity) suspend fun deleteRoadTrip(r: RoadTripEntity)
@Query("SELECT * FROM Roadtrip") @Query("SELECT * FROM Roadtrip ORDER BY date")
fun getAllRoadTrips(): Flow<MutableList<RoadTripEntity>> fun getAllRoadTrips(): Flow<MutableList<RoadTripEntity>>
@Query("SELECT * FROM Roadtrip WHERE id = :id") @Query("SELECT * FROM Roadtrip WHERE id = :id")

@ -1,5 +1,7 @@
package uca.baptistearthur.geocaching.data package uca.baptistearthur.geocaching.data
import org.osmdroid.util.GeoPoint
import uca.baptistearthur.geocaching.model.Address
import uca.baptistearthur.geocaching.model.Place import uca.baptistearthur.geocaching.model.Place
import uca.baptistearthur.geocaching.model.RoadTripEntity import uca.baptistearthur.geocaching.model.RoadTripEntity
import java.util.Date import java.util.Date

@ -0,0 +1,5 @@
package uca.baptistearthur.geocaching.model
data class Address(
val country: String,
val displayName: String)

@ -1,6 +1,18 @@
package uca.baptistearthur.geocaching.model package uca.baptistearthur.geocaching.model
import android.util.Log
import kotlinx.coroutines.*
import org.osmdroid.util.GeoPoint
import uca.baptistearthur.geocaching.network.AddressNetwork
data class Place( class Place(private val lat: Double,
val latitude: Double, private val lon: Double,
val longitude: Double var address: Address = Address("unknown", "unknown"))
) : GeoPoint(lat, lon){
suspend fun initAddress() {
AddressNetwork.retrofit.let {
CoroutineScope(Dispatchers.IO).async {
address = it.getAddress(lat, lon)
}.await()
}
}
}

@ -1,9 +1,11 @@
package uca.baptistearthur.geocaching.model package uca.baptistearthur.geocaching.model
import android.provider.Telephony.Mms.Addr
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.google.gson.Gson import com.google.gson.Gson
import org.osmdroid.util.GeoPoint
import java.util.Date import java.util.Date
@Entity(tableName = "Roadtrip") @Entity(tableName = "Roadtrip")
@ -13,7 +15,6 @@ class RoadTripEntity(
@ColumnInfo(name="date") val date: Date, @ColumnInfo(name="date") val date: Date,
@ColumnInfo(name="places") val places: MutableList<Place> @ColumnInfo(name="places") val places: MutableList<Place>
){ ){
fun addPlaceToRoadTripList(place: Place) = places.add(place) fun addPlaceToRoadTripList(place: Place) = places.add(place)
fun addPlaceToRoadTripList(latitude: Double, longitude: Double) = places.add(Place(latitude, longitude)) fun addPlaceToRoadTripList(latitude: Double, longitude: Double) = places.add(Place(latitude, longitude))
fun toJSON(): String = Gson().toJson(this) fun toJSON(): String = Gson().toJson(this)

@ -0,0 +1,15 @@
package uca.baptistearthur.geocaching.network
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Headers
import retrofit2.http.Query
import uca.baptistearthur.geocaching.model.Address
interface AddressAPI {
@GET("/v1/reverse")
suspend fun getAddress(
@Query("lat") lat: Double,
@Query("lon") lon: Double
): Address
}

@ -0,0 +1,22 @@
package uca.baptistearthur.geocaching.network
import android.util.Log
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import uca.baptistearthur.geocaching.model.Address
import java.lang.reflect.Type
class AddressDeserializer : JsonDeserializer<Address> {
override fun deserialize(
json: JsonElement?,
typeOfT: Type?,
context: JsonDeserializationContext?
): Address = json?.asJsonObject!!.let{
Address(
it.get("address").asJsonObject.get("country").asString,
it.get("display_name").asString
)
}
}

@ -0,0 +1,64 @@
package uca.baptistearthur.geocaching.network
import com.google.gson.GsonBuilder
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Converter
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import uca.baptistearthur.geocaching.model.Address
import java.lang.reflect.Type
object AddressNetwork {
private val API_Info = object {
val base_url = "https://forward-reverse-geocoding.p.rapidapi.com/v1/reverse/"
val key = "19516a9900mshce10de76f99976bp10f192jsn8c8d82222baa"
}
private val rateLimiter = object {
val maxRequestsPerSecond = 2
val intervalInMillis = (1000.0 / maxRequestsPerSecond).toLong()
var lastRequestTime: Long = 0
}
val rateLimitInterceptor = Interceptor { chain ->
val now = System.currentTimeMillis()
val elapsed = now - rateLimiter.lastRequestTime
if (elapsed < rateLimiter.intervalInMillis) {
Thread.sleep(rateLimiter.intervalInMillis - elapsed)
}
rateLimiter.lastRequestTime = System.currentTimeMillis()
chain.proceed(chain.request())
}
private val interceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
private val gson = GsonBuilder().apply {
registerTypeAdapter(Address::class.java, AddressDeserializer())
}.create()
private val client = OkHttpClient.Builder()
.addInterceptor { chain ->
val request = chain.request().newBuilder()
.addHeader("X-RapidAPI-Key", API_Info.key)
.build()
chain.proceed(request)
}
.addInterceptor(interceptor)
.addInterceptor(rateLimitInterceptor)
.build()
val retrofit: AddressAPI by lazy {
Retrofit.Builder()
.baseUrl(API_Info.base_url)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(AddressAPI::class.java)
}
}

@ -1,9 +1,11 @@
package uca.baptistearthur.geocaching.recyclerview package uca.baptistearthur.geocaching.recyclerview
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.drawable.Drawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.osmdroid.util.GeoPoint
import uca.baptistearthur.geocaching.R import uca.baptistearthur.geocaching.R
import uca.baptistearthur.geocaching.model.Place import uca.baptistearthur.geocaching.model.Place
@ -16,8 +18,9 @@ class PlacesAdapter (val places: List<Place>) : RecyclerView.Adapter<PlacesViewH
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: PlacesViewHolder, position: Int) { override fun onBindViewHolder(holder: PlacesViewHolder, position: Int) {
holder.placeName.text = "Ici sera le nom" holder.placeAddress.text = places[position].address.displayName
holder.placeAddress.text = "Ici sera l'adresse"
} }
override fun getItemCount(): Int = places.size override fun getItemCount(): Int = places.size
} }

@ -2,13 +2,13 @@ package uca.baptistearthur.geocaching.recyclerview
import android.view.View import android.view.View
import android.widget.Button import android.widget.Button
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView.ViewHolder import androidx.recyclerview.widget.RecyclerView.ViewHolder
import uca.baptistearthur.geocaching.R import uca.baptistearthur.geocaching.R
class PlacesViewHolder(val cellule: View): ViewHolder(cellule) { class PlacesViewHolder(val cellule: View): ViewHolder(cellule) {
var placeName: TextView = cellule.findViewById(R.id.txtPlaceName)
var placeAddress: TextView = cellule.findViewById(R.id.txtPlaceAddress) var placeAddress: TextView = cellule.findViewById(R.id.txtPlaceAddress)
} }

@ -17,9 +17,9 @@ class RoadTripAdapter(val voyages: List<RoadTripEntity>, val navController: NavC
@SuppressLint("SetTextI18n", "ClickableViewAccessibility") @SuppressLint("SetTextI18n", "ClickableViewAccessibility")
override fun onBindViewHolder(holder: RoadTripViewHolder, position: Int) { override fun onBindViewHolder(holder: RoadTripViewHolder, position: Int) {
holder.roadTripAccessButton.text = "> " + voyages[position].name holder.roadTripAccessButton.text = "> " + if (voyages[position].name.length > 20) voyages[position].name.substring(0, 20) + "..." else voyages[position].name
holder.clickedRoadTrip = voyages[position] holder.clickedRoadTrip = voyages[position]
} }
override fun getItemCount(): Int = voyages.size override fun getItemCount(): Int = voyages.size
} }

@ -1,15 +0,0 @@
package uca.baptistearthur.geocaching.services
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import uca.baptistearthur.geocaching.R
class FragmentService {
fun loadFragment(fragment: Fragment, supportFragmentManager: FragmentManager){
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, fragment)
transaction.commit()
}
}

@ -2,26 +2,16 @@ package uca.baptistearthur.geocaching.ui.activity
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.ui.NavigationUI import androidx.navigation.ui.NavigationUI
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import kotlinx.coroutines.*
import uca.baptistearthur.geocaching.R import uca.baptistearthur.geocaching.R
import uca.baptistearthur.geocaching.application.RTApplication
import uca.baptistearthur.geocaching.ui.fragment.Map
import uca.baptistearthur.geocaching.viewModels.RoadTripViewModel
import uca.baptistearthur.geocaching.viewModels.RoadTripViewModelFactory
class MainWindow: AppCompatActivity() { class MainWindow: AppCompatActivity() {
// private val roadTripViewModel: RoadTripViewModel by viewModels<RoadTripViewModel> {
// RoadTripViewModelFactory((this.application as RTApplication).db.roadTripDAO())
// }
@SuppressLint("MissingInflatedId") @SuppressLint("MissingInflatedId")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {

@ -0,0 +1,47 @@
package uca.baptistearthur.geocaching.ui.fragment
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import org.osmdroid.bonuspack.routing.OSRMRoadManager
import org.osmdroid.util.BoundingBox
import org.osmdroid.util.GeoPoint
import uca.baptistearthur.geocaching.model.RoadTripEntity
import uca.baptistearthur.geocaching.ui.overlay.EditMarkerOverlay
import kotlin.math.ln
import kotlin.math.roundToInt
import kotlin.math.sqrt
class EditRoadtripMap : Map() {
var roadTrip: RoadTripEntity? = null
set(value) {
roadTrip?:
value?.let {
addEditMarkerOverlay(value)
getMapParams(value).let{
map.controller.setCenter(it.first)
map.controller.setZoom(it.second)
}
field = value
}
}
private fun addEditMarkerOverlay(roadTrip: RoadTripEntity){
val editMarkerOverlay = EditMarkerOverlay(OSRMRoadManager(context, userAgent), roadTrip)
editMarkerOverlay.addPlaces(roadTrip.places, map)
map.overlays.add(editMarkerOverlay);
}
private fun getMapParams(roadTrip: RoadTripEntity): Pair<GeoPoint, Double>{
val boundingBox = BoundingBox.fromGeoPoints(roadTrip.places)
val maxDistance = 7000000.0
val logZoom = (defaultZoomLevel - minimumZoomLevel) * ln(boundingBox.diagonalLengthInMeters) / ln(maxDistance)
val zoomLevel = (defaultZoomLevel - logZoom).coerceIn(minimumZoomLevel, defaultZoomLevel)
return Pair(boundingBox.centerWithDateLine, zoomLevel)
}
}

@ -1,101 +1,53 @@
package uca.baptistearthur.geocaching.ui.fragment package uca.baptistearthur.geocaching.ui.fragment
import android.content.Context
import android.content.pm.PackageManager
import android.location.LocationManager
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import org.osmdroid.config.Configuration import org.osmdroid.config.Configuration
import org.osmdroid.util.GeoPoint import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.ScaleBarOverlay import org.osmdroid.views.overlay.ScaleBarOverlay
import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.location.Location
import android.location.LocationListener
import android.util.Log import android.util.Log
import android.widget.ProgressBar
import androidx.activity.result.contract.ActivityResultContracts
import org.osmdroid.bonuspack.routing.OSRMRoadManager
import org.osmdroid.bonuspack.routing.RoadManager
import org.osmdroid.config.IConfigurationProvider
import org.osmdroid.library.BuildConfig
import org.osmdroid.tileprovider.tilesource.TileSourceFactory import org.osmdroid.tileprovider.tilesource.TileSourceFactory
import org.osmdroid.tileprovider.util.StorageUtils.getStorage
import org.osmdroid.views.overlay.compass.CompassOverlay import org.osmdroid.views.overlay.compass.CompassOverlay
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay
import uca.baptistearthur.geocaching.R import uca.baptistearthur.geocaching.R
import uca.baptistearthur.geocaching.ui.overlay.AddMarkerOverlay
import uca.baptistearthur.geocaching.ui.overlay.RecenterOverlay
class Map : Fragment() {
private lateinit var map : MapView
private lateinit var spinner: ProgressBar
private lateinit var locationManager: LocationManager
private val userAgent = "RoadTrip"
open class Map : Fragment() {
protected lateinit var map : MapView
companion object{
const val minimumZoomLevel = 4.0
const val defaultZoomLevel = 21.0
const val userAgent = "RoadTrip"
val defaultPoint = GeoPoint(48.8583, 2.2944) 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)
spinner.visibility=View.GONE;
isMapCentered=true;
}
map.invalidate()
}
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Configuration.getInstance().userAgentValue = userAgent; Configuration.getInstance().userAgentValue = userAgent;
} }
protected fun configureMap() = map.apply {
val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) setTileSource(TileSourceFactory.MAPNIK)
{ map.controller.setCenter(defaultPoint) } minZoomLevel = minimumZoomLevel
controller.apply {
private fun configureMap(view: View){ setCenter(defaultPoint)
Log.d("GeoMap", "MAP CONFIGURE") setZoom(defaultZoomLevel)
map = view.findViewById(R.id.mapView) }
map.setTileSource(TileSourceFactory.MAPNIK); addMapOverlays(mapView = this)
spinner = view.findViewById(R.id.mapLoading);
spinner.visibility=View.VISIBLE;
map.minZoomLevel = 4.0
map.controller.setZoom(21.0);
if (ContextCompat.checkSelfPermission(requireActivity(), ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissionLauncher.launch(ACCESS_FINE_LOCATION)
} }
map.controller.setCenter(defaultPoint)
open fun addMapOverlays(mapView: MapView){
// Compass Overlay
val compassOverlay = CompassOverlay(context, InternalCompassOrientationProvider(context), map); val compassOverlay = CompassOverlay(context, InternalCompassOrientationProvider(context), map);
compassOverlay.enableCompass(); compassOverlay.enableCompass();
map.getOverlays().add(compassOverlay); map.overlays.add(compassOverlay);
// Scale Bar Overlay
val scaleBarOverlay = ScaleBarOverlay(map) val scaleBarOverlay = ScaleBarOverlay(map)
scaleBarOverlay.setAlignRight(true) scaleBarOverlay.setAlignRight(true)
map.overlays.add(scaleBarOverlay) map.overlays.add(scaleBarOverlay)
val myLocation = MyLocationNewOverlay(GpsMyLocationProvider(context), map)
myLocation.enableMyLocation()
map.overlays.add(myLocation)
val recenter = RecenterOverlay(GpsMyLocationProvider(context), map)
recenter.enableMyLocation()
map.overlays.add(recenter);
val addMarker = AddMarkerOverlay(OSRMRoadManager(context, userAgent))
map.overlays.add(addMarker);
} }
override fun onCreateView( override fun onCreateView(
@ -103,30 +55,19 @@ class Map : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
Log.d("GeoMap", "MAP ON CREATE VIEW") Log.d("GeoMap", "MAP ON CREATE VIEW")
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_map, container, false) val view = inflater.inflate(R.layout.fragment_map, container, false)
configureMap(view) map = view.findViewById(R.id.mapView)
configureMap()
return view return view
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
Log.d("GeoMap", "MAP RESUME")
if (ContextCompat.checkSelfPermission(requireActivity(), ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
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 map.onResume() //needed for compass, my location overlays, v6.0.0 and up
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
Log.d("GeoMap", "MAP PAUSE")
if (ContextCompat.checkSelfPermission(requireActivity(), ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
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 map.onPause() //needed for compass, my location overlays, v6.0.0 and up
} }
} }

@ -0,0 +1,103 @@
package uca.baptistearthur.geocaching.ui.fragment
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 org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView
import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.location.LocationListener
import android.util.Log
import android.widget.ProgressBar
import androidx.activity.result.contract.ActivityResultContracts
import org.osmdroid.bonuspack.routing.OSRMRoadManager
import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay
import uca.baptistearthur.geocaching.R
import uca.baptistearthur.geocaching.ui.overlay.AddMarkerOverlay
import uca.baptistearthur.geocaching.ui.overlay.MarkerOverlay
import uca.baptistearthur.geocaching.ui.overlay.NewRoadtripOverlay
import uca.baptistearthur.geocaching.ui.overlay.RecenterOverlay
class MyLocationMap : Map() {
private lateinit var spinner: ProgressBar
private lateinit var locationManager: LocationManager
private var isMapCentered = false;
private val locationListener = LocationListener { location ->
val geoPoint = GeoPoint(location.latitude, location.longitude)
if(!isMapCentered){
map.controller.setCenter(geoPoint)
spinner.visibility=View.GONE;
isMapCentered=true;
}
map.invalidate()
}
private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()){}
private fun displaySpinner(view: View){
spinner = view.findViewById(R.id.mapLoading)
spinner.visibility=View.VISIBLE
if (ContextCompat.checkSelfPermission(requireActivity(), ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissionLauncher.launch(ACCESS_FINE_LOCATION)
}
}
override fun addMapOverlays(mapView: MapView){
super.addMapOverlays(mapView)
Log.d("GeoMap", "MyLocationOverlay")
// Recenter Overlay
val recenter = RecenterOverlay(GpsMyLocationProvider(context), map)
recenter.enableMyLocation()
map.overlays.add(recenter);
// My Location Overlay
val myLocation = MyLocationNewOverlay(GpsMyLocationProvider(context), map)
myLocation.enableMyLocation()
map.overlays.add(myLocation)
// Add Marker Overlay
val addMarker = AddMarkerOverlay(OSRMRoadManager(context, userAgent))
map.overlays.add(addMarker);
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.d("GeoMap", "MAP ON CREATE VIEW")
val view = inflater.inflate(R.layout.fragment_map, container, false)
map = view.findViewById(R.id.mapView)
configureMap()
displaySpinner(view)
return view
}
override fun onResume() {
super.onResume()
Log.d("GeoMap", "MAP RESUME")
if (ContextCompat.checkSelfPermission(requireActivity(), ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
locationManager = requireActivity().getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, locationListener)
}
}
override fun onPause() {
super.onPause()
Log.d("GeoMap", "MAP PAUSE")
if (ContextCompat.checkSelfPermission(requireActivity(), ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
locationManager = requireActivity().getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.removeUpdates(locationListener)
isMapCentered=false;
}
}
}

@ -1,85 +1,49 @@
package uca.baptistearthur.geocaching.ui.fragment package uca.baptistearthur.geocaching.ui.fragment
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button import android.widget.Toast
import android.widget.EditText
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import uca.baptistearthur.geocaching.R import uca.baptistearthur.geocaching.R
import uca.baptistearthur.geocaching.application.RTApplication import uca.baptistearthur.geocaching.application.RTApplication
import uca.baptistearthur.geocaching.data.Stub import uca.baptistearthur.geocaching.model.Place
import uca.baptistearthur.geocaching.model.RoadTripEntity import uca.baptistearthur.geocaching.model.RoadTripEntity
import uca.baptistearthur.geocaching.recyclerview.RoadTripAdapter import uca.baptistearthur.geocaching.recyclerview.RoadTripAdapter
import uca.baptistearthur.geocaching.ui.activity.MainWindow
import uca.baptistearthur.geocaching.viewModels.RoadTripViewModel import uca.baptistearthur.geocaching.viewModels.RoadTripViewModel
import uca.baptistearthur.geocaching.viewModels.RoadTripViewModelFactory import uca.baptistearthur.geocaching.viewModels.RoadTripViewModelFactory
import java.util.* import java.util.*
// 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 [RoadTripEntity.newInstance] factory method to
* create an instance of this fragment.
*/
class RoadTripFragment : Fragment() { class RoadTripFragment : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
private var roadTripRecyclerView : RecyclerView? = null private var roadTripRecyclerView : RecyclerView? = null
private var model = Stub().load()
// private val roadTripViewModel: RoadTripViewModel by viewModels<RoadTripViewModel> {
// RoadTripViewModelFactory((MainWindow().application as RTApplication).db.roadTripDAO()) // MainWindow().application ????? bof bof
// }
override fun onCreate(savedInstanceState: Bundle?) { private val roadTripViewModel: RoadTripViewModel by viewModels<RoadTripViewModel> {
super.onCreate(savedInstanceState) RoadTripViewModelFactory((requireActivity().application as RTApplication).db.roadTripDAO())
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
} }
@SuppressLint("MissingInflatedId")
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_roadtrip, container, false) val view = inflater.inflate(R.layout.fragment_roadtrip, container, false)
roadTripRecyclerView = view?.findViewById(R.id.recyclerViewRoadTripList) roadTripRecyclerView = view?.findViewById(R.id.recyclerViewRoadTripList)
roadTripRecyclerView?.adapter = RoadTripAdapter(model, findNavController())
roadTripViewModel.getAllRoadTrips().observe(viewLifecycleOwner, { roadTrips ->
if(roadTrips.isEmpty()){
Toast.makeText(
context,
R.string.noRoadTripFound,
Toast.LENGTH_SHORT
).show()
}else roadTripRecyclerView?.adapter = RoadTripAdapter(roadTrips, findNavController())
})
roadTripRecyclerView?.layoutManager = LinearLayoutManager(context) roadTripRecyclerView?.layoutManager = LinearLayoutManager(context)
return view return view
} }
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) =
RoadTripFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
} }

@ -6,38 +6,74 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment
import android.util.Log import android.util.Log
import android.widget.Button
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.fragment.app.*
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import uca.baptistearthur.geocaching.R import uca.baptistearthur.geocaching.R
import uca.baptistearthur.geocaching.application.RTApplication
import uca.baptistearthur.geocaching.converters.Converters import uca.baptistearthur.geocaching.converters.Converters
import uca.baptistearthur.geocaching.converters.toFrenchFormat import uca.baptistearthur.geocaching.converters.toFrenchFormat
import uca.baptistearthur.geocaching.model.RoadTripEntity
import uca.baptistearthur.geocaching.recyclerview.PlacesAdapter import uca.baptistearthur.geocaching.recyclerview.PlacesAdapter
import uca.baptistearthur.geocaching.viewModels.RoadTripViewModel
import uca.baptistearthur.geocaching.viewModels.RoadTripViewModelFactory
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
class RoadtripDetail : Fragment() { class RoadtripDetail : Fragment() {
private var placesRecyclerView : RecyclerView? = null private var placesRecyclerView : RecyclerView? = null
private val map: EditRoadtripMap = EditRoadtripMap()
private lateinit var roadTrip: RoadTripEntity;
private val roadTripViewModel: RoadTripViewModel by viewModels {
RoadTripViewModelFactory((requireActivity().application as RTApplication).db.roadTripDAO())
}
@SuppressLint("ResourceType")
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Log.d("GeoMap", "MAP ON CREATE VIEW")
// Inflate the layout for this fragment // Inflate the layout for this fragment
val view = inflater.inflate(R.layout.roadtrip_detail, container, false) val view = inflater.inflate(R.layout.roadtrip_detail, container, false)
roadTrip = Converters().toRoadTripEntity(arguments?.getString("roadTrip"))
var roadTripJSON = arguments?.getString("roadTrip") childFragmentManager.beginTransaction()
val roadTrip = Converters().toRoadTripEntity(roadTripJSON) .add(R.id.roadTripDetailMapView, map)
.commit()
placesRecyclerView = view?.findViewById(R.id.recyclerViewPlacesList) placesRecyclerView = view?.findViewById(R.id.recyclerViewPlacesList)
placesRecyclerView?.adapter = PlacesAdapter(roadTrip.places) placesRecyclerView?.adapter = PlacesAdapter(roadTrip.places)
placesRecyclerView?.layoutManager = LinearLayoutManager(context) placesRecyclerView?.layoutManager = LinearLayoutManager(context)
view?.findViewById<TextView>(R.id.roadTripDetailTitle)?.text = roadTrip.name view?.findViewById<TextView>(R.id.roadTripDetailTitle)?.text = roadTrip.name
view?.findViewById<TextView>(R.id.roadTripDetailDate)?.text = roadTrip.date.toFrenchFormat() view?.findViewById<TextView>(R.id.roadTripDetailDate)?.text = roadTrip.date.toFrenchFormat()
view?.findViewById<Button>(R.id.btnDeleteRoadTrip)?.setOnClickListener {
try{
roadTripViewModel.deleteRoadTrip(roadTrip)
}catch (e: Exception){
Toast.makeText(
context,
R.string.roadTripDeleteError,
Toast.LENGTH_SHORT
).show()
}finally {
findNavController().popBackStack()
Toast.makeText(
context,
R.string.roadTripDeleteConfirmation,
Toast.LENGTH_SHORT
).show()
}
}
return view return view
} }
override fun onStart() {
super.onStart()
map.roadTrip=roadTrip
}
} }

@ -1,91 +1,8 @@
package uca.baptistearthur.geocaching.ui.overlay package uca.baptistearthur.geocaching.ui.overlay
import android.content.Context
import android.util.Log
import android.view.MotionEvent
import androidx.core.content.ContextCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.osmdroid.bonuspack.routing.RoadManager import org.osmdroid.bonuspack.routing.RoadManager
import org.osmdroid.util.GeoPoint import uca.baptistearthur.geocaching.model.RoadTripEntity
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.Overlay
import org.osmdroid.views.overlay.Polyline
import uca.baptistearthur.geocaching.R
class AddMarkerOverlay(val roadManager: RoadManager) : Overlay() {
private var locations: MutableSet<PlaceMarker> = mutableSetOf()
private lateinit var roadOverlay: Polyline
private var newRoadtripOverlayVisible = false;
override fun onDoubleTap(e: MotionEvent?, mapView: MapView?): Boolean {
Log.d("GeoMap", "Longpress")
val proj = mapView?.projection;
if(proj!=null){
val loc = proj.fromPixels(e?.x?.toInt()!!, e.y.toInt() ) as GeoPoint
val marker = PlaceMarker(mapView, this)
marker.position = loc
if(locations.isNotEmpty()) locations.last().setDefaultIcon()
locations.add(marker)
locations.forEach{ it.closeInfoWindow()}
computeIcons(mapView.context)
mapView.overlays.add(marker)
computeRoad(mapView)
computeNewRoadtripOverlay(mapView);
mapView.invalidate()
}
return true;
}
fun computeIcons(context: Context){
if(locations.isNotEmpty()) {
val flagIcon = ContextCompat.getDrawable(context, R.drawable.roadtrip_marker)!!
locations.last().icon = flagIcon
locations.first().icon = flagIcon
}
}
fun computeRoad(mapView: MapView) {
mapView.overlays.remove(mapView.overlays.find { it is Polyline})
if (locations.size > 1) {
CoroutineScope(Dispatchers.IO).launch {
val road = roadManager.getRoad(ArrayList(locations.map{it -> it.position}))
withContext(Dispatchers.Main) {
roadOverlay = RoadManager.buildRoadOverlay(road)
mapView.overlays.add(roadOverlay)
}
}
}
}
fun computeNewRoadtripOverlay(mapView: MapView){
if (locations.size > 1) {
if(!newRoadtripOverlayVisible) {
mapView.overlays.add(NewRoadtripOverlay(locations))
newRoadtripOverlayVisible = true
}
}else{
Log.d("GeoRoad", "TRY DELETE ROADTRIP OVERLAY" + locations.size)
Log.d("GeoRoad", ""+mapView.overlays.size)
mapView.overlays.remove(mapView.overlays.find { it is NewRoadtripOverlay})
newRoadtripOverlayVisible=false
}
}
fun removeMarker(placeMarker: PlaceMarker) = locations.remove(placeMarker);
fun getMarkerLabel(placeMarker: PlaceMarker) =
when (placeMarker) {
locations.first() -> {
"Start"
}
locations.last() -> {
"Finish"
}
else -> {
"Step " + locations.indexOf(placeMarker);
}
}
class AddMarkerOverlay(roadManager: RoadManager) : MarkerOverlay<NewRoadtripOverlay>(roadManager) {
override fun createNewConfirmationOverlay(): NewRoadtripOverlay = NewRoadtripOverlay(places)
} }

@ -0,0 +1,59 @@
package uca.baptistearthur.geocaching.ui.overlay
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.Log
import android.view.MotionEvent
import androidx.core.content.ContextCompat
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.Overlay
import uca.baptistearthur.geocaching.R
abstract class ConfirmationOverlay(val points: MutableCollection<PlaceMarker>) : Overlay() {
private var circleRectF=RectF()
override fun draw(canvas: Canvas, mapView: MapView, shadow: Boolean) {
val circleSize = 300f
val circlePadding = 20f
val circleY = canvas.height - circleSize - circlePadding
circleRectF= RectF(circlePadding, circleY, circlePadding + circleSize, circleY + circleSize)
val paint = Paint().apply {
color = Color.WHITE
style = Paint.Style.FILL
}
canvas.drawCircle(
circleSize / 2 + circlePadding,
circleY + circleSize / 2,
circleSize / 2,
paint
)
val iconSize = 180
val icon = ContextCompat.getDrawable(mapView.context, R.drawable.check)
val iconX = (circleSize / 2 - iconSize / 2 + circlePadding).toInt()
val iconY = (circleY + circleSize / 2 - iconSize / 2).toInt()
icon?.setBounds(iconX, iconY, iconX + iconSize, iconY + iconSize)
icon?.draw(canvas)
}
override fun onSingleTapConfirmed(e: MotionEvent?, mapView: MapView?) =
if (e != null && circleRectF.contains(e.x, e.y)) {
mapView?.let{
confirm(it)
}
true
}else{
false
}
abstract fun confirm(mapView: MapView)
}

@ -0,0 +1,17 @@
package uca.baptistearthur.geocaching.ui.overlay
import android.graphics.Canvas
import org.osmdroid.bonuspack.routing.RoadManager
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView
import uca.baptistearthur.geocaching.model.RoadTripEntity
class EditMarkerOverlay(roadManager: RoadManager, var roadTrip: RoadTripEntity) : MarkerOverlay<EditRoadtripOverlay>(roadManager) {
fun addPlaces(geopoints: Collection<GeoPoint>, mapView: MapView)=
geopoints.forEach {
addMarkerAtGeopoint(it, mapView)
}
override fun createNewConfirmationOverlay(): EditRoadtripOverlay = EditRoadtripOverlay(places, roadTrip)
}

@ -0,0 +1,80 @@
package uca.baptistearthur.geocaching.ui.overlay
import android.app.AlertDialog
import android.content.Context
import android.text.InputFilter
import android.util.Log
import android.widget.EditText
import android.widget.Toast
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView
import uca.baptistearthur.geocaching.R
import uca.baptistearthur.geocaching.application.RTApplication
import uca.baptistearthur.geocaching.model.Place
import uca.baptistearthur.geocaching.model.RoadTripEntity
import uca.baptistearthur.geocaching.viewModels.RoadTripViewModel
import uca.baptistearthur.geocaching.viewModels.RoadTripViewModelFactory
import java.util.*
class EditRoadtripOverlay(points: MutableCollection<PlaceMarker>, val roadTripEntity: RoadTripEntity) : ConfirmationOverlay(points) {
fun getRoadTripViewModelFromOverlay(overlayContext: Context): RoadTripViewModel {
val roadTripDao = (overlayContext.applicationContext as RTApplication).db.roadTripDAO()
val viewModelFactory = RoadTripViewModelFactory(roadTripDao)
return ViewModelProvider(overlayContext as ViewModelStoreOwner, viewModelFactory).get(
RoadTripViewModel::class.java)
}
private fun onValidation(mapView: MapView, input: String) {
val places: MutableList<Place> =
points.map { Place(it.position.latitude, it.position.longitude) }.toMutableList()
CoroutineScope(Dispatchers.Main).launch {
places.filter { it.address.displayName === "unknown" }.forEach { it.initAddress() }
val newRoadTrip = RoadTripEntity(
id = roadTripEntity.id,
name = input,
date = roadTripEntity.date,
places = places
)
getRoadTripViewModelFromOverlay(mapView.context).deleteRoadTrip(roadTripEntity)
getRoadTripViewModelFromOverlay(mapView.context).insertRoadTrip(newRoadTrip)
Toast.makeText(
mapView.context,
R.string.changesSaved,
Toast.LENGTH_SHORT
).show()
}
}
override fun confirm(mapView: MapView) {
val input = EditText(mapView.context)
input.setText(roadTripEntity.name)
input.filters = arrayOf<InputFilter>(InputFilter.LengthFilter(50))
val dialog = AlertDialog.Builder(mapView.context)
.setTitle(R.string.newRoadtripDialog)
.setView(input)
.setPositiveButton(R.string.confirm) { _, _ ->
val userInput = input.text.toString()
if (userInput.isNotBlank()) {
onValidation(mapView, input.text.toString())
} else {
Toast.makeText(
mapView.context,
R.string.emptyTextError,
Toast.LENGTH_SHORT
).show()
}
}
.setNegativeButton(R.string.cancel) { dialog, _ ->
dialog.cancel()
}
.create()
dialog.show()
}
}

@ -0,0 +1,95 @@
package uca.baptistearthur.geocaching.ui.overlay
import android.content.Context
import android.view.MotionEvent
import androidx.core.content.ContextCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.osmdroid.bonuspack.routing.RoadManager
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.Overlay
import org.osmdroid.views.overlay.Polyline
import uca.baptistearthur.geocaching.R
abstract class MarkerOverlay<T : ConfirmationOverlay>(val roadManager: RoadManager) : Overlay() {
protected var places: MutableSet<PlaceMarker> = mutableSetOf()
private lateinit var roadOverlay: Polyline
private var confirmationOverlayIsVisible = false;
protected fun addMarkerAtGeopoint(geoPoint: GeoPoint, mapView: MapView){
val marker = PlaceMarker(mapView, this)
marker.position = geoPoint
if(places.isNotEmpty()) places.last().setDefaultIcon()
places.add(marker)
places.forEach{ it.closeInfoWindow()}
computeIcons(mapView.context)
mapView.overlays.add(marker)
computeRoad(mapView)
computeConfirmationOverlay(mapView);
mapView.invalidate()
}
override fun onDoubleTap(e: MotionEvent?, mapView: MapView?): Boolean {
val proj = mapView?.projection;
if(proj!=null){
val geoPoint = proj.fromPixels(e?.x?.toInt()!!, e.y.toInt() ) as GeoPoint
addMarkerAtGeopoint(geoPoint, mapView)
}
return true;
}
fun computeIcons(context: Context) {
if (places.isNotEmpty()) {
val flagIcon = ContextCompat.getDrawable(context, R.drawable.roadtrip_marker)!!
places.last().icon = flagIcon
places.first().icon = flagIcon
}
}
fun computeRoad(mapView: MapView) {
mapView.overlays.removeAll{ it is Polyline }
if (places.size > 1) {
CoroutineScope(Dispatchers.IO).launch {
val road = roadManager.getRoad(ArrayList(places.map{it.position}))
withContext(Dispatchers.Main) {
roadOverlay = RoadManager.buildRoadOverlay(road)
mapView.overlays.add(roadOverlay)
}
}
}
}
abstract fun createNewConfirmationOverlay(): T
fun computeConfirmationOverlay(mapView: MapView){
if (places.size > 1) {
if(!confirmationOverlayIsVisible) {
mapView.overlays.add(createNewConfirmationOverlay())
confirmationOverlayIsVisible = true
}
}else{
mapView.overlays.removeAll{ it is ConfirmationOverlay }
confirmationOverlayIsVisible=false
}
}
fun removeMarker(placeMarker: PlaceMarker) = places.remove(placeMarker);
fun getMarkerLabel(placeMarker: PlaceMarker) =
when (placeMarker) {
places.first() -> {
"Start"
}
places.last() -> {
"Finish"
}
else -> {
"Step " + places.indexOf(placeMarker);
}
}
}

@ -1,58 +1,88 @@
package uca.baptistearthur.geocaching.ui.overlay package uca.baptistearthur.geocaching.ui.overlay
import android.graphics.Canvas import android.app.AlertDialog
import android.graphics.Color import android.content.Context
import android.graphics.Paint import android.text.InputFilter
import android.graphics.RectF
import android.util.Log import android.util.Log
import android.view.MotionEvent import android.widget.EditText
import androidx.core.content.ContextCompat import android.widget.Toast
import org.osmdroid.util.GeoPoint import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.osmdroid.views.MapView import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.Overlay import org.osmdroid.views.overlay.Polyline
import uca.baptistearthur.geocaching.R import uca.baptistearthur.geocaching.R
import uca.baptistearthur.geocaching.application.RTApplication
import uca.baptistearthur.geocaching.model.Address
import uca.baptistearthur.geocaching.model.Place
import uca.baptistearthur.geocaching.model.RoadTripEntity
import uca.baptistearthur.geocaching.viewModels.RoadTripViewModel
import uca.baptistearthur.geocaching.viewModels.RoadTripViewModelFactory
import java.util.*
class NewRoadtripOverlay(val points: Collection<PlaceMarker>) : Overlay() { class NewRoadtripOverlay(points: MutableCollection<PlaceMarker>) : ConfirmationOverlay(points) {
private var circleRectF=RectF() fun getRoadTripViewModelFromOverlay(overlayContext: Context): RoadTripViewModel {
val roadTripDao = (overlayContext.applicationContext as RTApplication).db.roadTripDAO()
override fun draw(canvas: Canvas, mapView: MapView, shadow: Boolean) { val viewModelFactory = RoadTripViewModelFactory(roadTripDao)
return ViewModelProvider(overlayContext as ViewModelStoreOwner, viewModelFactory).get(RoadTripViewModel::class.java)
val circleSize = 300f }
val circlePadding = 20f
val circleY = canvas.height - circleSize - circlePadding
circleRectF= RectF(circlePadding, circleY, circlePadding + circleSize, circleY + circleSize)
val paint = Paint().apply { private fun clearMap(mapView: MapView){
color = Color.WHITE mapView.overlays.removeAll { it is PlaceMarker || it is Polyline || it is NewRoadtripOverlay }
style = Paint.Style.FILL
} }
canvas.drawCircle( private fun onValidation(mapView: MapView, input: String){
circleSize / 2 + circlePadding, val places: MutableList<Place> = points.map { Place(it.position.latitude, it.position.longitude) }.toMutableList()
circleY + circleSize / 2, CoroutineScope(Dispatchers.Main).launch {
circleSize / 2, places.forEach{
paint it.initAddress()
Log.d("GeoMap", it.address.displayName)
}
val newRoadTrip = RoadTripEntity(
id = 0, // auto-generated ID
name = input,
date = Date(),
places = places
) )
getRoadTripViewModelFromOverlay(mapView.context).insertRoadTrip(newRoadTrip);
val iconSize = 180 Toast.makeText(
val icon = ContextCompat.getDrawable(mapView.context, R.drawable.check) mapView.context,
R.string.roadtripAdded,
val iconX = (circleSize / 2 - iconSize / 2 + circlePadding).toInt() Toast.LENGTH_SHORT
val iconY = (circleY + circleSize / 2 - iconSize / 2).toInt() ).show()
points.clear()
icon?.setBounds(iconX, iconY, iconX + iconSize, iconY + iconSize) }
icon?.draw(canvas) clearMap(mapView)
} }
override fun onSingleTapConfirmed(e: MotionEvent?, mapView: MapView?) = override fun confirm(mapView: MapView){
if (e != null && circleRectF.contains(e.x, e.y)) { val input = EditText(mapView.context)
// TODO: Sauvegarder le trajet (dans la variable points). input.filters = arrayOf<InputFilter>(InputFilter.LengthFilter(50))
Log.d("GeoRoad", "CONFIRM : "+points.size)
true val dialog = AlertDialog.Builder(mapView.context)
.setTitle(R.string.newRoadtripDialog)
.setView(input)
.setPositiveButton(R.string.confirm) { _, _ ->
val userInput = input.text.toString()
if (userInput.isNotBlank()) {
onValidation(mapView, input.text.toString())
} else { } else {
false Toast.makeText(
mapView.context,
R.string.emptyTextError,
Toast.LENGTH_SHORT
).show()
}
}
.setNegativeButton(R.string.cancel) { dialog, _ ->
dialog.cancel()
}
.create()
dialog.show()
} }
} }

@ -1,18 +1,10 @@
package uca.baptistearthur.geocaching.ui.overlay package uca.baptistearthur.geocaching.ui.overlay
import android.graphics.drawable.Drawable
import android.util.Log
import android.view.MotionEvent import android.view.MotionEvent
import androidx.core.content.ContextCompat
import org.osmdroid.bonuspack.routing.OSRMRoadManager
import org.osmdroid.bonuspack.routing.RoadManager
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.Marker import org.osmdroid.views.overlay.Marker
import org.osmdroid.views.overlay.infowindow.InfoWindow
import uca.baptistearthur.geocaching.R
class PlaceMarker(val mapView: MapView, val parent : AddMarkerOverlay) : Marker(mapView) { class PlaceMarker(val mapView: MapView, val parent : MarkerOverlay<*>) : Marker(mapView) {
override fun getTitle() = parent.getMarkerLabel(this) override fun getTitle() = parent.getMarkerLabel(this)
override fun onLongPress(e: MotionEvent?, mapView: MapView?): Boolean { override fun onLongPress(e: MotionEvent?, mapView: MapView?): Boolean {
@ -22,7 +14,7 @@ class PlaceMarker(val mapView: MapView, val parent : AddMarkerOverlay) : Marker(
mapView.overlays.remove(this) mapView.overlays.remove(this)
parent.computeIcons(mapView.context) parent.computeIcons(mapView.context)
parent.computeRoad(mapView) parent.computeRoad(mapView)
parent.computeNewRoadtripOverlay(mapView) parent.computeConfirmationOverlay(mapView)
mapView.invalidate() mapView.invalidate()
return true return true
} }

@ -1,6 +1,7 @@
package uca.baptistearthur.geocaching.viewModels package uca.baptistearthur.geocaching.viewModels
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
//import androidx.lifecycle.asLiveData //import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -9,9 +10,9 @@ import uca.baptistearthur.geocaching.model.RoadTripEntity
class RoadTripViewModel(val dao: RoadTripDAO): ViewModel() { class RoadTripViewModel(val dao: RoadTripDAO): ViewModel() {
fun getRoadTripById(id: Int) = dao.getRoadTripById(id)//.asLiveData() fun getRoadTripById(id: Int) = dao.getRoadTripById(id).asLiveData()
fun getAllRoadTrips() = dao.getAllRoadTrips()//.asLiveData() fun getAllRoadTrips() = dao.getAllRoadTrips().asLiveData()
fun insertRoadTrip(r: RoadTripEntity){ fun insertRoadTrip(r: RoadTripEntity){
viewModelScope.launch { viewModelScope.launch {

@ -1,30 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF">
<group android:scaleX="0.4698"
android:scaleY="0.4698"
android:translateX="6.3624"
android:translateY="6.3624">
<path
android:fillColor="@android:color/white"
android:pathData="M22,16v-2l-8.5,-5V3.5C13.5,2.67 12.83,2 12,2s-1.5,0.67 -1.5,1.5V9L2,14v2l8.5,-2.5V19L8,20.5L8,22l4,-1l4,1l0,-1.5L13.5,19v-5.5L22,16z"/>
</group>
</vector>

@ -1,18 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="91dp"
android:height="91dp"
android:viewportWidth="91"
android:viewportHeight="91">
<path
android:fillColor="#FF000000"
android:pathData="M67.305,36.442v-8.055c0,-0.939 -0.762,-1.701 -1.7,-1.701H54.342v-5.524c0,-0.938 -0.761,-1.7 -1.699,-1.7h-12.75c-0.939,0 -1.701,0.762 -1.701,1.7v5.524H26.93c-0.939,0 -1.7,0.762 -1.7,1.701v8.055c0,0.938 0.761,1.699 1.7,1.699h0.488v34.021c0,0.938 0.761,1.7 1.699,1.7h29.481c3.595,0 6.52,-2.924 6.52,-6.518V38.142h0.486C66.543,38.142 67.305,37.381 67.305,36.442zM41.592,22.862h9.35v3.824h-9.35V22.862zM61.719,67.345c0,1.719 -1.4,3.117 -3.12,3.117h-27.78v-32.32l30.9,0.002V67.345zM63.904,34.742H28.629v-4.655h11.264h12.75h11.262V34.742z"/>
<path
android:fillColor="#FF000000"
android:pathData="M36.066,44.962h3.4v19.975h-3.4z"/>
<path
android:fillColor="#FF000000"
android:pathData="M44.566,44.962h3.4v19.975h-3.4z"/>
<path
android:fillColor="#FF000000"
android:pathData="M53.066,44.962h3.4v19.975h-3.4z"/>
</vector>

@ -3,7 +3,7 @@
<item android:gravity="bottom"> <item android:gravity="bottom">
<shape> <shape>
<size android:height="1dp" /> <size android:height="1dp" />
<solid android:color="@color/black" /> <solid android:color="@color/main_turquoise_200" />
</shape> </shape>
</item> </item>
</layer-list> </layer-list>

@ -1,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="800dp"
android:height="800dp"
android:viewportWidth="330"
android:viewportHeight="330">
<path
android:pathData="M250.61,154.39l-150,-150c-5.86,-5.86 -15.35,-5.86 -21.21,0c-5.86,5.86 -5.86,15.35 0,21.21l139.39,139.39L79.39,304.39c-5.86,5.86 -5.86,15.35 0,21.21C82.32,328.54 86.16,330 90,330s7.68,-1.46 10.61,-4.39l150,-150c2.81,-2.81 4.39,-6.63 4.39,-10.61C255,161.02 253.42,157.2 250.61,154.39z"
android:fillColor="#000000"/>
</vector>

@ -6,12 +6,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="45dp" android:layout_height="45dp"
android:layout_gravity="start" android:layout_gravity="start"
android:drawableStart="@drawable/right_arrow"
android:drawableLeft="@drawable/right_arrow"
android:gravity="left" android:gravity="left"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="20sp" android:textSize="20sp"
android:text="> TMP name"
tools:ignore="RtlHardcoded" tools:ignore="RtlHardcoded"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:layout_marginLeft="10dp" android:layout_marginLeft="10dp"

@ -9,45 +9,26 @@
android:background="@drawable/corner_radius" android:background="@drawable/corner_radius"
> >
<ImageView <ImageView
android:id="@+id/imageView" android:id="@+id/placeImageView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:layout_marginLeft="10dp" android:layout_marginLeft="7dp"
android:layout_margin="10dp" android:layout_margin="7dp"
android:src="@drawable/center" /> android:src="@drawable/center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:id="@+id/txtPlaceName"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="TMP/ ici c'est le nom"
android:layout_weight="1"
android:textSize="17dp"
android:textColor="@color/black"
/>
<TextView <TextView
android:id="@+id/txtPlaceAddress" android:id="@+id/txtPlaceAddress"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:text="TMP/ ici c'est l'adresse"
android:layout_weight="1" android:layout_weight="1"
android:textSize="13dp" android:textSize="15dp"
android:textColor="@color/black"
android:layout_marginRight="2dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_gravity="center"
/> />
</LinearLayout>
</LinearLayout> </LinearLayout>

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.fragment.Map"> tools:context=".ui.fragment.MyLocationMap">
<org.osmdroid.views.MapView <org.osmdroid.views.MapView
android:id="@+id/mapView" android:id="@+id/mapView"

@ -10,7 +10,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/roadtrip_title" android:text="@string/roadtrip_title"
android:background="@color/main_turquoise_200" android:background="@color/main_turquoise_500"
android:textColor="@color/main_turquoise_50" android:textColor="@color/main_turquoise_50"
android:padding="10dp" android:padding="10dp"
android:textSize="20sp"/> android:textSize="20sp"/>

@ -12,7 +12,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="TMP/ Roadtrip Name" android:text="TMP/ Roadtrip Name"
android:background="@color/main_turquoise_200" android:background="@color/main_turquoise_500"
android:textColor="@color/main_turquoise_50" android:textColor="@color/main_turquoise_50"
android:paddingTop="10dp" android:paddingTop="10dp"
android:paddingLeft="10dp" android:paddingLeft="10dp"
@ -25,13 +25,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="TMP/ Date" android:text="TMP/ Date"
android:background="@color/main_turquoise_200" android:background="@color/main_turquoise_500"
android:textColor="@color/main_turquoise_50" android:textColor="@color/main_turquoise_50"
android:textSize="15sp" android:textSize="15sp"
android:paddingLeft="10dp" android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingBottom="5dp"/> android:paddingBottom="5dp"/>
<org.osmdroid.views.MapView <FrameLayout
android:id="@+id/roadTripDetailMapView" android:id="@+id/roadTripDetailMapView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -61,7 +62,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/btnDeleteRoadTrip" android:text="@string/btnDeleteRoadTrip"
android:backgroundTint="@color/main_turquoise_500" android:backgroundTint="@color/main_turquoise_200"
android:layout_margin="10dp"/> android:layout_margin="10dp"/>
</LinearLayout> </LinearLayout>

@ -3,11 +3,11 @@
<item <item
android:id="@+id/map" android:id="@+id/map"
android:enabled="true" android:enabled="true"
android:title="@string/carte" android:title="@string/map"
android:icon="@drawable/map"/> android:icon="@drawable/map"/>
<item <item
android:id="@+id/roadTrip" android:id="@+id/roadTrip"
android:enabled="true" android:enabled="true"
android:title="@string/voyages" android:title="@string/travels"
android:icon="@drawable/road_trip" /> android:icon="@drawable/road_trip" />
</menu> </menu>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/app_logo_background"/>
<foreground android:drawable="@drawable/app_logo_foreground"/>
</adaptive-icon>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/app_logo_background"/>
<foreground android:drawable="@drawable/app_logo_foreground"/>
</adaptive-icon>

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

@ -24,7 +24,16 @@
tools:layout="@layout/roadtrip_detail" /> tools:layout="@layout/roadtrip_detail" />
<fragment <fragment
android:id="@+id/map" android:id="@+id/map"
android:name="uca.baptistearthur.geocaching.ui.fragment.Map" android:name="uca.baptistearthur.geocaching.ui.fragment.MyLocationMap"
android:label="fragment_map" android:label="fragment_map"
tools:layout="@layout/fragment_map" /> tools:layout="@layout/fragment_map" />
<fragment android:id="@+id/placeholder" >
<action
android:id="@+id/action_placeholder_to_map2"
app:destination="@id/roadTripDetailMapView" />
</fragment>
<fragment
android:id="@+id/roadTripDetailMapView"
android:name="uca.baptistearthur.geocaching.ui.fragment.EditRoadtripMap"
android:label="Map" />
</navigation> </navigation>

@ -0,0 +1,18 @@
<resources>
<string name="app_name">RoadTrip</string>
<string name="map">Carte</string>
<string name="travels">Voyages</string>
<string name="roadtrip_title">Mes RoadTrips:</string>
<string name="add_roadtrip_button">+</string>
<string name="placesList">Vos lieux à visiter:</string>
<string name="btnDeleteRoadTrip">Supprimer le voyage</string>
<string name="confirm">Valider</string>
<string name="cancel">Annuler</string>
<string name="emptyTextError">Le texte ne peux pas être vide</string>
<string name="newRoadtripDialog">Entrez le nom de votre voyage</string>
<string name="roadTripDeleteConfirmation">Le voyage a bien été supprimé</string>
<string name="roadTripDeleteError">Une erreur est survenue lors de la suppresion du voyage</string>
<string name="noRoadTripFound">Aucun voyage n\'a été trouvé, utilisez la carte</string>
<string name="roadtripAdded">Roadtrip ajouté</string>
<string name="changesSaved">Changements sauvegardés</string>
</resources>

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="app_logo_background">#52796F</color>
</resources>

@ -1,13 +1,18 @@
<resources> <resources>
<string name="app_name">RoadTrip</string> <string name="app_name">RoadTrip</string>
<string name="carte">Carte</string> <string name="map">Map</string>
<string name="voyages">Voyages</string> <string name="travels">Travels</string>
<string name="roadtrip_title">Mes RoadTrips:</string> <string name="roadtrip_title">My RoadTrips:</string>
<string name="add_roadtrip_button">+</string> <string name="add_roadtrip_button">+</string>
<string name="textaddNewRoadTrip">Entrez le nom du nouveau voyage</string> <string name="placesList">Your places to visit:</string>
<string name="placesList">Lieux à visiter sur votre chemin:</string> <string name="btnDeleteRoadTrip">Delete travel</string>
<string name="btnDeleteRoadTrip">Supprimer le voyage</string> <string name="confirm">Confirm</string>
<string name="cancel">Cancel</string>
<!-- TODO: Remove or change this placeholder text --> <string name="emptyTextError">The text cannot be empty</string>
<string name="hello_blank_fragment">Hello blank fragment</string> <string name="newRoadtripDialog">Enter the name of your travel</string>
<string name="roadTripDeleteConfirmation">The road trip has been successfully deleted</string>
<string name="roadTripDeleteError">An error occurred while deleting the road trip.</string>
<string name="noRoadTripFound">No trip was found, add one with the map.</string>
<string name="roadtripAdded">Roadtrip added</string>
<string name="changesSaved">Changes saved</string>
</resources> </resources>

@ -2,7 +2,7 @@
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="Theme.Geocaching" parent="Theme.MaterialComponents.DayNight.NoActionBar"> <style name="Theme.Geocaching" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Primary brand color. --> <!-- Primary brand color. -->
<item name="colorPrimary">@color/main_turquoise_50</item> <item name="colorPrimary">@color/main_turquoise_200</item>
<item name="colorPrimaryVariant">@color/main_turquoise_500</item> <item name="colorPrimaryVariant">@color/main_turquoise_500</item>
<item name="colorOnPrimary">@color/white</item> <item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. --> <!-- Secondary brand color. -->

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id 'com.android.application' version '7.3.0' apply false id 'com.android.application' version '7.4.1' apply false
id 'com.android.library' version '7.3.0' apply false id 'com.android.library' version '7.4.1' apply false
id 'org.jetbrains.kotlin.android' version '1.8.20' apply false id 'org.jetbrains.kotlin.android' version '1.8.20' apply false
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.1' apply false // id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.1' apply false
} }
Loading…
Cancel
Save