Compare commits

..

25 Commits
1.8.2 ... 1.9.0

Author SHA1 Message Date
johan12345
2df5710910 Release 1.9.0 2024-05-20 22:04:09 +02:00
johan12345
a513a8d6d4 fix source sets for releaseAutomotivePackageName build type 2024-05-20 22:04:09 +02:00
johan12345
a55e4df62d hide Tesla referral link
referral program has ended
2024-05-20 21:08:18 +02:00
johan12345
d540faa179 update car app library to 1.7.0-alpha02 2024-05-20 20:39:29 +02:00
johan12345
3d69d3e50c update AGP 2024-05-20 00:18:39 +02:00
johan12345
fce27f0c19 add SVG for Play Store icon 2024-05-20 00:18:07 +02:00
johan12345
da3b9643bc add build type with different package name for fossAutomotive
needed for Faurecia Aptoide
2024-05-20 00:16:21 +02:00
johan12345
b690d9744d update Play Store icon according to new specs
https://developer.android.com/distribute/google-play/resources/icon-design-specifications
2024-05-20 00:16:21 +02:00
johan12345
70387ec350 fix docs in README 2024-05-19 17:55:47 +02:00
johan12345
5a331df232 add Jawg Maps sponsor logo 2024-05-19 17:53:20 +02:00
johan12345
5dc5e1f43f Migrate Mapbox -> MapLibre
Use Jawg Maps for basemap, ArcGIS for satellite maps

fixes #141
refs #169, #197

hide traffic checkbox if traffic is not supported by map
2024-05-19 17:53:20 +02:00
Hosted Weblate
360e7767bd Translated using Weblate (Portuguese)
Currently translated at 100.0% (366 of 366 strings)

Co-authored-by: Celso Azevedo <mail@celsoazevedo.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/pt/
Translation: EVMap/Android
2024-05-08 22:27:16 +02:00
Jean-Baptiste
5f0c9fd31d Enable automatic per app language (#340)
* Enable automatic per app language

* fix getAppLocale

---------

Co-authored-by: johan12345 <johan.forstner@gmail.com>
2024-05-08 22:19:09 +02:00
johan12345
536c884f23 fix crash introduced by 00b26d224f 2024-05-03 20:34:09 +02:00
johan12345
7daf5a0adb fix jumping position of slider in ChargepriceFragment
fixes #328
2024-04-28 19:30:57 +02:00
johan12345
862f2b06d8 remove broken resourcePlaceholders plugin, hardcode targetPackage in shortcuts.xml 2024-04-28 19:18:32 +02:00
johan12345
198a9ecc48 Fix snackbar action button color
fixes #337
2024-04-28 19:05:28 +02:00
johan12345
2762a32105 improve look of text input dialog
fixes #336
2024-04-28 18:52:41 +02:00
johan12345
8a83a80e75 Block ability to update filter profile name with nothing
fixes #335
2024-04-28 18:29:10 +02:00
johan12345
75e8569964 dismiss popupMenu when fragment is destroyed
fixes #331
2024-04-27 13:32:17 +02:00
johan12345
00b26d224f fix #329: Layer button reappears after screen rotation 2024-04-27 12:43:34 +02:00
Jean-Baptiste
836f42b299 Fix color of layer button (#334)
* Fix color of layer button

* keep the layers icon gray

---------

Co-authored-by: johan12345 <johan.forstner@gmail.com>
2024-04-27 12:24:19 +02:00
johan12345
3de994f09d update copyright in LICENSE 2024-04-26 22:41:39 +02:00
Jean-Baptiste
d78eda9d97 Update copyright (#333) 2024-04-26 22:40:45 +02:00
johan12345
ed4be05aed fix tests 2024-04-24 21:53:26 +02:00
63 changed files with 397 additions and 164 deletions

View File

@@ -1,6 +1,8 @@
<resources>
<string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">ci</string>
<string name="mapbox_key" translatable="false">ci</string>
<string name="jawg_key" translatable="false">ci</string>
<string name="arcgis_key" translatable="false">ci</string>
<string name="goingelectric_key" translatable="false">ci</string>
<string name="chargeprice_key" translatable="false">ci</string>
<string name="openchargemap_key" translatable="false">ci</string>

View File

@@ -30,6 +30,8 @@ jobs:
OPENCHARGEMAP_API_KEY: ${{ secrets.OPENCHARGEMAP_API_KEY }}
CHARGEPRICE_API_KEY: ${{ secrets.CHARGEPRICE_API_KEY }}
MAPBOX_API_KEY: ${{ secrets.MAPBOX_API_KEY }}
JAWG_API_KEY: ${{ secrets.JAWG_API_KEY }}
ARCGIS_API_KEY: ${{ secrets.ARCGIS_API_KEY }}
GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }}
FRONYX_API_KEY: ${{ secrets.FRONYX_API_KEY }}
ACRA_CRASHREPORT_CREDENTIALS: ${{ secrets.ACRA_CRASHREPORT_CREDENTIALS }}

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020-2023 Johan von Forstner and contributors
Copyright (c) 2020-2024 Johan von Forstner and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -24,7 +24,8 @@ Features
- Android Auto & Android Automotive OS integration
- No ads, fully open source
- Compatible with Android 5.0 and above
- Can use Google Maps or Mapbox (OpenStreetMap) as map backends - the version available on F-Droid only uses Mapbox.
- Can use Google Maps or OpenStreetMap as map backends - the version available on F-Droid only uses
OSM.
Screenshots
-----------
@@ -42,12 +43,14 @@ EVMap uses and put them into the app in the form of a resource file called `apik
`app/src/main/res/values`. You can find more information on which API keys are necessary for which
features and how they can be obtained in our [documentation page](doc/api_keys.md).
There are three different build flavors, `googleNormal`, `fossNormal` and `googleAutomotive`.
- The `foss` variants only use Mapbox data and should run on most Android devices, even without
There are four different build flavors, `googleNormal`, `fossNormal`, `googleAutomotive`, and
`fossAutomotive`.
- The `foss` variants only use OSM data and should run on most Android devices, even without
Google Play Services.
- `fossNormal` is intended to run on smartphones and tablets, and also includes the Android
Auto app for use on the car display (however for that to work, the Android Auto app is
necessary, which in turn does require Google Play Services).
Auto app for use on the car display (however Android Auto may not work if the app is not
installed from Google Play, see https://github.com/ev-map/EVMap/issues/319).
- `fossAutomotive` can be installed directly on
[Android Automotive OS (AAOS)](https://source.android.com/docs/automotive/start/what_automotive)
headunits without Google services.
@@ -75,5 +78,12 @@ You can use our [Weblate page](https://hosted.weblate.org/projects/evmap/) to he
into new languages.
<a href="https://hosted.weblate.org/engage/evmap/">
<img src="https://hosted.weblate.org/widgets/evmap/-/open-graph.png" width="500" alt="Translation status" />
<img src="https://hosted.weblate.org/widgets/evmap/-/open-graph.png" width="400" alt="Translation status" />
</a>
Sponsors
--------
<a href="https://www.jawg.io"><img src="https://www.jawg.io/static/Blue@10x-9cdc4596e4e59acbd9ead55e9c28613e.png" alt="JawgMaps" height="58"/></a><br>
Since mid 2024, **JawgMaps** provides their OpenStreetMap vector map tiles service to EVMap for
free, i.e. the background map displayed in the app if OpenStreetMap is selected as the data source.

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<defs>
<style>
.cls-1 {
fill: #00e676;
}
.cls-1, .cls-2, .cls-3, .cls-4, .cls-5, .cls-6, .cls-7, .cls-8 {
stroke-width: 0px;
}
.cls-2 {
fill: rgba(255, 255, 255, .2);
}
.cls-3 {
fill: #ffb300;
}
.cls-4 {
fill: #000;
isolation: isolate;
opacity: .45;
}
.cls-5 {
fill: #fff;
}
.cls-6 {
fill: #90a4ae;
}
.cls-7 {
fill: #546e7a;
}
.cls-8 {
fill: rgba(62, 39, 35, .2);
}
</style>
</defs>
<g id="Layer_1" data-name="Layer 1">
<g>
<rect class="cls-5" width="512" height="512" />
<g>
<g>
<path class="cls-3"
d="M159.42,338.98l-6.43-56.15-9.81,1.01,6.43,56.15,9.81-1.01ZM194.26,334.92l-6.43-56.15-9.81,1.01,6.43,56.15,9.81-1.01Z" />
<path class="cls-6"
d="M212.53,411.37c-3.04,3.72-5.41,6.09-5.75,6.43-8.79,7.1-15.9,9.13-21.65,6.43-10.15-5.07-9.47-24.02-9.13-26.05l7.1.34c-.34,5.41.68,16.91,5.41,19.28,2.71,1.35,7.44-.34,13.53-5.41h0s19.62-19.62,15.56-35.18c-4.74-18.6,16.91-45.33,24.02-54.46l1.01-1.01,5.75,4.4-1.01,1.35c-21.99,27.06-24.35,40.93-22.66,48.03,3.38,13.53-5.75,28.08-12.18,35.85Z" />
<path class="cls-6"
d="M137.78,338.3l2.71,23,21.31,14.21,28.75-3.04,17.59-18.6-2.71-23-67.65,7.44Z" />
<path class="cls-7"
d="M190.21,372.47l-28.75,3.04,6.09,25.37,22.66-2.71v-25.71h0ZM210.84,311.58l2.37,20.97-82.53,9.47-2.37-20.97,82.53-9.47Z" />
</g>
<g>
<g>
<path class="cls-1"
d="M275.45,80.22c-59.19,0-107.23,48.03-107.23,107.23,0,80.84,90.31,123.12,101.14,238.47.34,3.38,3.04,5.75,6.43,5.75s6.09-2.37,6.43-5.75c10.82-115.34,101.14-157.63,101.14-238.47-.68-59.53-48.71-107.23-107.9-107.23Z" />
<path class="cls-2"
d="M275.45,82.58c58.86,0,106.55,47.36,107.23,105.87v-1.01c0-59.19-48.03-107.23-107.23-107.23s-107.23,47.69-107.23,107.23v1.01c.68-58.52,48.37-105.87,107.23-105.87h0Z" />
<path class="cls-8"
d="M281.87,423.21c-.34,3.38-3.04,5.75-6.43,5.75s-6.09-2.37-6.43-5.75c-10.49-115.01-100.12-157.29-100.8-237.12v1.69c0,80.84,90.31,123.12,101.14,238.47.34,3.38,3.04,5.75,6.43,5.75s6.09-2.37,6.43-5.75c10.82-115.34,101.14-157.63,101.14-238.47v-1.69c-1.35,79.83-90.99,122.11-101.48,237.12h0Z" />
</g>
<path class="cls-4"
d="M250.75,135.01v64.94h17.59v53.11l41.27-71.03h-23.68l23.68-47.36c.34.34-58.86.34-58.86.34Z" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -8,10 +8,8 @@ plugins {
id("kotlin-kapt")
id("androidx.navigation.safeargs.kotlin")
id("com.mikepenz.aboutlibraries.plugin")
id("pt.jcosta.resourceplaceholders")
}
val supportedLocales = "en,de,fr,nb-rNO,nl,pt,ro,cs"
android {
defaultConfig {
@@ -20,12 +18,10 @@ android {
minSdk = 21
targetSdk = 34
// NOTE: always increase versionCode by 2 since automotive flavor uses versionCode + 1
versionCode = 212
versionName = "1.8.2"
versionCode = 214
versionName = "1.9.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
resourceConfigurations += supportedLocales.split(",")
buildConfigField("String", "supportedLocales", "\"$supportedLocales\"")
}
signingConfigs {
@@ -50,12 +46,21 @@ android {
)
signingConfig = signingConfigs.getByName("release")
}
create("releaseAutomotivePackageName") {
// Faurecia Aptoide requires the automotive variant to use a separate package name
initWith(getByName("release"))
applicationIdSuffix = ".automotive"
}
debug {
applicationIdSuffix = ".debug"
isDebuggable = true
}
}
sourceSets {
getByName("releaseAutomotivePackageName").setRoot("src/release")
}
flavorDimensions += listOf("dependencies", "automotive")
productFlavors {
create("foss") {
@@ -101,6 +106,9 @@ android {
disable += listOf("NullSafeMutableLiveData")
warning += listOf("MissingTranslation")
}
androidResources {
generateLocaleConfig = true
}
testOptions {
unitTests {
@@ -108,9 +116,6 @@ android {
}
}
resourcePlaceholders {
files("xml/shortcuts.xml")
}
namespace = "net.vonforst.evmap"
// add API keys from environment variable if not set in apikeys.xml
@@ -150,6 +155,28 @@ android {
if (mapboxKey != null) {
resValue("string", "mapbox_key", mapboxKey)
}
var jawgKey =
System.getenv("JAWG_API_KEY") ?: project.findProperty("JAWG_API_KEY")?.toString()
if (jawgKey == null && project.hasProperty("JAWG_API_KEY_ENCRYPTED")) {
jawgKey = decode(
project.findProperty("JAWG_API_KEY_ENCRYPTED").toString(),
"FmK.d,-f*p+rD+WK!eds"
)
}
if (jawgKey != null) {
resValue("string", "jawg_key", jawgKey)
}
var arcgisKey =
System.getenv("ARCGIS_API_KEY") ?: project.findProperty("ARCGIS_API_KEY")?.toString()
if (arcgisKey == null && project.hasProperty("ARCGIS_API_KEY_ENCRYPTED")) {
arcgisKey = decode(
project.findProperty("ARCGIS_API_KEY_ENCRYPTED").toString(),
"FmK.d,-f*p+rD+WK!eds"
)
}
if (arcgisKey != null) {
resValue("string", "arcgis_key", jawgKey)
}
var chargepriceKey =
System.getenv("CHARGEPRICE_API_KEY") ?: project.findProperty("CHARGEPRICE_API_KEY")
?.toString()
@@ -200,6 +227,22 @@ android {
}
}
androidComponents {
beforeVariants { variantBuilder ->
if (variantBuilder.buildType == "releaseAutomotivePackageName"
&& !variantBuilder.productFlavors.containsAll(
listOf(
"automotive" to "automotive",
"dependencies" to "foss"
)
)
) {
// releaseAutomotivePackageName type is only needed for fossAutomotive
variantBuilder.enable = false
}
}
}
configurations {
create("googleNormalImplementation") {}
create("googleAutomotiveImplementation") {}
@@ -259,34 +302,17 @@ dependencies {
implementation("com.github.romandanylyk:PageIndicatorView:b1bad589b5")
// Android Auto
val carAppVersion = "1.4.0-rc02"
val carAppVersion = "1.7.0-alpha02"
implementation("androidx.car.app:app:$carAppVersion")
normalImplementation("androidx.car.app:app-projected:$carAppVersion")
automotiveImplementation("androidx.car.app:app-automotive:$carAppVersion")
// AnyMaps
val anyMapsVersion = "4854581f72"
val anyMapsVersion = "c087b3e7c2"
implementation("com.github.ev-map.AnyMaps:anymaps-base:$anyMapsVersion")
googleImplementation("com.github.ev-map.AnyMaps:anymaps-google:$anyMapsVersion")
googleImplementation("com.google.android.gms:play-services-maps:18.2.0")
implementation("com.github.ev-map.AnyMaps:anymaps-mapbox:$anyMapsVersion") {
exclude(group = "com.mapbox.mapboxsdk", module = "mapbox-android-accounts")
exclude(group = "com.mapbox.mapboxsdk", module = "mapbox-android-telemetry")
exclude(group = "com.google.android.gms", module = "play-services-location")
exclude(group = "com.mapbox.mapboxsdk", module = "mapbox-android-core")
}
// original version of mapbox-android-core
googleImplementation("com.mapbox.mapboxsdk:mapbox-android-core:2.0.1")
// patched version that removes build-time dependency on GMS (-> no Google location services)
fossImplementation("com.github.ev-map:mapbox-events-android:a21c324501")
implementation("com.mapbox.mapboxsdk:mapbox-android-sdk") {
exclude(group = "com.mapbox.mapboxsdk", module = "mapbox-android-accounts")
exclude(group = "com.mapbox.mapboxsdk", module = "mapbox-android-telemetry")
version {
strictly("9.1.0-SNAPSHOT")
}
}
implementation("com.github.ev-map.AnyMaps:anymaps-maplibre:$anyMapsVersion")
// Google Places
googleImplementation("com.google.android.libraries.places:places:3.3.0")

View File

@@ -2,5 +2,5 @@
<resources>
<string name="donations_info" formatted="false">Pomohla vám EVMap? Podpořte její vývoj zasláním finančního daru vývojáři.</string>
<string name="donate_paypal">Přispět pomocí PayPalu</string>
<string name="data_sources_hint">Mapová data v aplikaci poskytuje služba OpenStreetMap (Mapbox).</string>
<string name="data_sources_hint">Mapová data v aplikaci poskytuje služba OpenStreetMap.</string>
</resources>

View File

@@ -2,5 +2,5 @@
<resources>
<string name="donations_info" formatted="false">Findest du EVMap nützlich? Unterstütze die Weiterentwicklung der App mit einer Spende an den Entwickler.</string>
<string name="donate_paypal">Mit PayPal spenden</string>
<string name="data_sources_hint">Die Kartendaten für die App stammen von OpenStreetMap (Mapbox).</string>
<string name="data_sources_hint">Die Kartendaten für die App stammen von OpenStreetMap.</string>
</resources>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="donations_info" formatted="false">Trouvez-vous EVMap utile \? Soutenez son développement en envoyant un don au développeur.</string>
<string name="data_sources_hint">Les données cartographiques de l\'application sont fournies par OpenStreetMap (Mapbox).</string>
<string name="data_sources_hint">Les données cartographiques de l\'application sont fournies par OpenStreetMap.</string>
<string name="donate_paypal">Faire un don avec PayPal</string>
</resources>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="donate_paypal">Doner med PayPal</string>
<string name="data_sources_hint">Kartdata i programmet tilbys av OpenStreetMap (Mapbox).</string>
<string name="data_sources_hint">Kartdata i programmet tilbys av OpenStreetMap.</string>
<string name="donations_info" formatted="false">Synes du EVMap er nyttig\? Støtt utviklingen ved å sende en slant til utvikleren.</string>
</resources>

View File

@@ -2,5 +2,5 @@
<resources>
<string name="donations_info" formatted="false">Vond je EVMap nuttig\? Je kan de ontwikkeling ondersteunen door een donatie te sturen naar de ontwikkelaar.</string>
<string name="donate_paypal">Doneer via PayPal</string>
<string name="data_sources_hint">De kaartgegevens zijn afkomstig van OpenStreetMap (Mapbox).</string>
<string name="data_sources_hint">De kaartgegevens zijn afkomstig van OpenStreetMap.</string>
</resources>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="data_sources_hint">Os dados do mapa são fornecidos pelo OpenStreetMap (Mapbox).</string>
<string name="data_sources_hint">Os dados do mapa são fornecidos pelo OpenStreetMap.</string>
<string name="donate_paypal">Doar com o PayPal</string>
<string name="donations_info" formatted="false">Acha que o EVMap é útil\? Apoie a manutenção e desenvolvimento com uma doação para o desenvolvedor da app.</string>
</resources>

View File

@@ -2,5 +2,5 @@
<resources>
<string name="donations_info" formatted="false">Crezi ca EVMap este util? Sprijina dezvoltarea printr-o donatie pentru dezvoltator.</string>
<string name="donate_paypal">Doneaza cu PayPal</string>
<string name="data_sources_hint">Hartile din aplicatie sunt furnizate de OpenStreetMap (Mapbox).</string>
<string name="data_sources_hint">Hartile din aplicatie sunt furnizate de OpenStreetMap.</string>
</resources>

View File

@@ -2,5 +2,5 @@
<resources>
<string name="donations_info" formatted="false">Do you find EVMap useful? Support its development by sending a donation to the developer.</string>
<string name="donate_paypal">Donate with PayPal</string>
<string name="data_sources_hint">Map data in the app is provided by OpenStreetMap (Mapbox).</string>
<string name="data_sources_hint">Map data in the app is provided by OpenStreetMap.</string>
</resources>

View File

@@ -3,5 +3,5 @@
<string name="donations_info" formatted="false">Pomohla vám EVMap? Podpořte její vývoj posláním finančního daru vývojáři.
\n
\nGoogle si z každého daru strhne 15 %.</string>
<string name="data_sources_hint">V nastavení můžete také pro mapová data přepínat mezi službami Mapy Google a OpenStreetMap (Mapbox).</string>
<string name="data_sources_hint">V nastavení můžete také pro mapová data přepínat mezi službami Mapy Google a OpenStreetMap.</string>
</resources>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="donations_info" formatted="false">Findest du EVMap nützlich? Unterstütze die Weiterentwicklung der App mit einer Spende an den Entwickler.\n\nGoogle zieht von der Spende 15% Gebühren ab.</string>
<string name="data_sources_hint">In den Einstellungen kannst du auch zwischen Google Maps und OpenStreetMap (Mapbox) für die Kartendaten wechseln.</string>
<string name="data_sources_hint">In den Einstellungen kannst du auch zwischen Google Maps und OpenStreetMap für die Kartendaten wechseln.</string>
</resources>

View File

@@ -3,5 +3,5 @@
<string name="donations_info" formatted="false">Trouvez-vous EVMap utile \? Soutenez son développement en envoyant un don au développeur.
\n
\nGoogle prend 15% sur chaque don.</string>
<string name="data_sources_hint">Dans les paramètres, vous pouvez également choisir entre Google Maps et OpenStreetMap (Mapbox) pour les données cartographiques.</string>
<string name="data_sources_hint">Dans les paramètres, vous pouvez également choisir entre Google Maps et OpenStreetMap pour les données cartographiques.</string>
</resources>

View File

@@ -3,5 +3,5 @@
<string name="donations_info" formatted="false">Synes du EVMap er nyttig\? Støtt utviklingen ved å sende penger til utvikleren.
\n
\nGoogle tar 15% av alle donasjoner.</string>
<string name="data_sources_hint">I innstillingene kan du også bytte mellom Google Maps og OpenStreetMap (Mapbox) for kartdata.</string>
<string name="data_sources_hint">I innstillingene kan du også bytte mellom Google Maps og OpenStreetMap for kartdata.</string>
</resources>

View File

@@ -3,5 +3,5 @@
<string name="donations_info" formatted="false">Vind je EVMap nuttig\? Je kan de ontwikkeling steunen via een donatie aan de ontwikkelaar.
\n
\nGoogle houdt 15% in van elke donatie.</string>
<string name="data_sources_hint">In de instellingen kan je ook wisselen tussen Google Maps en OpenStreetMap (Mapbox) voor de kaartgegevens.</string>
<string name="data_sources_hint">In de instellingen kan je ook wisselen tussen Google Maps en OpenStreetMap voor de kaartgegevens.</string>
</resources>

View File

@@ -3,5 +3,5 @@
<string name="donations_info" formatted="false">Acha que o EVMap é útil\? Apoie a manutenção e desenvolvimento com uma doação para o desenvolvedor da app.
\n
\nA Google cobra 15% de cada doação.</string>
<string name="data_sources_hint">Também pode mudar entre o Google Maps e OpenStreetMap (Mapbox) nas definições da app.</string>
<string name="data_sources_hint">Também pode mudar entre o Google Maps e OpenStreetMap nas definições da app.</string>
</resources>

View File

@@ -2,7 +2,7 @@
<resources>
<string-array name="pref_map_provider_names">
<item>@string/pref_provider_google_maps</item>
<item>@string/pref_provider_osm_mapbox</item>
<item>@string/pref_provider_osm</item>
</string-array>
<string-array name="pref_map_provider_values" translatable="false">
<item>google</item>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="donations_info" formatted="false">Do you find EVMap useful? Support its development by sending a donation to the developer.\n\nGoogle takes 15% off every donation.</string>
<string name="data_sources_hint">In the settings you can also switch between Google Maps and OpenStreetMap (Mapbox) for the map data.</string>
<string name="data_sources_hint">In the settings you can also switch between Google Maps and OpenStreetMap for the map data.</string>
</resources>

View File

@@ -41,13 +41,20 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:localeConfig="@xml/locales_config">
android:theme="@style/AppTheme">
<meta-data
android:name="com.mapbox.ACCESS_TOKEN"
android:value="@string/mapbox_key" />
<meta-data
android:name="io.jawg.ACCESS_TOKEN"
android:value="@string/jawg_key" />
<meta-data
android:name="com.arcgis.ACCESS_TOKEN"
android:value="@string/arcgis_key" />
<activity
android:name=".MapsActivity"
android:label="@string/app_name"

View File

@@ -113,8 +113,7 @@ class MapboxAutocompleteProvider(val context: Context) : AutocompleteProvider {
override fun getAttributionString(): Int = R.string.powered_by_mapbox
override fun getAttributionImage(dark: Boolean): Int =
if (dark) com.mapbox.mapboxsdk.R.drawable.mapbox_logo_icon else R.drawable.mapbox_logo
override fun getAttributionImage(dark: Boolean): Int = R.drawable.mapbox_logo
}
private fun BoundingBox.toLatLngBounds(): LatLngBounds {

View File

@@ -1,7 +1,12 @@
package net.vonforst.evmap.fragment
import android.os.Bundle
import android.view.*
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.MenuProvider
import androidx.databinding.DataBindingUtil
@@ -108,31 +113,19 @@ class FilterFragment : Fragment(), MenuProvider {
}
}
private fun saveProfile(error: Boolean = false) {
showEditTextDialog(requireContext()) { dialog, input ->
private fun saveProfile() {
showEditTextDialog(requireContext(), { dialog, input ->
vm.filterProfile.value?.let { profile ->
input.setText(profile.name)
}
if (error) {
input.error = getString(R.string.required)
}
dialog.setTitle(R.string.save_as_profile)
.setMessage(R.string.save_profile_enter_name)
.setPositiveButton(R.string.ok) { _, _ ->
if (input.text.isBlank()) {
saveProfile(true)
} else {
lifecycleScope.launch {
vm.saveAsProfile(input.text.toString())
findNavController().popBackStack()
}
}
}
.setNegativeButton(R.string.cancel) { _, _ ->
}
}
}, {
lifecycleScope.launch {
vm.saveAsProfile(it)
findNavController().popBackStack()
}
})
}
}

View File

@@ -183,20 +183,16 @@ class FilterProfilesFragment : Fragment() {
adapter = FilterProfilesAdapter(touchHelper, onDelete = { fp ->
delete(fp)
}, onRename = { fp ->
showEditTextDialog(requireContext()) { dialog, input ->
showEditTextDialog(requireContext(), { dialog, input ->
input.setText(fp.name)
dialog.setTitle(R.string.rename)
.setMessage(R.string.save_profile_enter_name)
.setPositiveButton(R.string.ok) { _, _ ->
lifecycleScope.launch {
vm.update(fp.copy(name = input.text.toString()))
}
}
.setNegativeButton(R.string.cancel) { _, _ ->
}
}
}, {
lifecycleScope.launch {
vm.update(fp.copy(name = it))
}
})
})
binding.filterProfilesList.apply {
this.adapter = this@FilterProfilesFragment.adapter

View File

@@ -44,7 +44,6 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
@@ -110,6 +109,7 @@ import net.vonforst.evmap.shouldUseImperialUnits
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.ui.ChargerIconGenerator
import net.vonforst.evmap.ui.ClusterIconGenerator
import net.vonforst.evmap.ui.HideOnScrollFabBehavior
import net.vonforst.evmap.ui.MarkerAnimator
import net.vonforst.evmap.ui.chargerZ
import net.vonforst.evmap.ui.clusterZ
@@ -155,6 +155,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
private var connectionErrorSnackbar: Snackbar? = null
private var previousChargepointIds: Set<Long>? = null
private var mapTopPadding: Int = 0
private var popupMenu: PopupMenu? = null
private lateinit var clusterIconGenerator: ClusterIconGenerator
private lateinit var chargerIconGenerator: ChargerIconGenerator
@@ -224,12 +225,12 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
mapFragment = MapFragment()
mapFragment!!.priority = arrayOf(
when (provider) {
"mapbox" -> MapFragment.MAPBOX
"mapbox" -> MapFragment.MAPLIBRE
"google" -> MapFragment.GOOGLE
else -> null
},
MapFragment.GOOGLE,
MapFragment.MAPBOX
MapFragment.MAPLIBRE
)
childFragmentManager
.beginTransaction()
@@ -274,7 +275,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
// set map padding so that compass is not obstructed by toolbar
mapTopPadding = systemWindowInsetTop + (48 * density).toInt() + (16 * density).toInt()
// if we actually use map.setPadding here, Mapbox will re-trigger onApplyWindowInsets
// if we actually use map.setPadding here, MapLibre will re-trigger onApplyWindowInsets
// and cause an infinite loop. So we rely on onMapReady being called later than
// onApplyWindowInsets.
@@ -729,6 +730,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
displaySearchResult(place, moveCamera = true)
}
vm.layersMenuOpen.observe(viewLifecycleOwner) { open ->
HideOnScrollFabBehavior.from(binding.fabLayers)?.hidden = open
binding.fabLayers.visibility = if (open) View.INVISIBLE else View.VISIBLE
binding.layersSheet.visibility = if (open) View.VISIBLE else View.INVISIBLE
updateBackPressedCallback()
@@ -1050,6 +1052,9 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
val context = this.context ?: return
chargerIconGenerator = ChargerIconGenerator(context, map.bitmapDescriptorFactory)
vm.mapTrafficSupported.value =
mapFragment?.let { AnyMap.Feature.TRAFFIC_LAYER in it.supportedFeatures } ?: false
if (BuildConfig.FLAVOR.contains("google") && mapFragment!!.priority[0] == MapFragment.GOOGLE) {
// Google Maps: icons can be generated in background thread
lifecycleScope.launch {
@@ -1058,7 +1063,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
}
}
} else {
// Mapbox: needs to be run on main thread
// MapLibre: needs to be run on main thread
chargerIconGenerator.preloadCache()
}
@@ -1399,14 +1404,13 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
MenuCompat.setGroupDividerEnabled(popup.menu, true)
popup.setForceShowIcon(true)
popup.setOnMenuItemClickListener {
val navController = requireView().findNavController()
when (it.itemId) {
R.id.menu_edit_filters -> {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
lifecycleScope.launch {
vm.copyFiltersToCustom()
navController.safeNavigate(
findNavController().safeNavigate(
MapFragmentDirections.actionMapToFilterFragment()
)
}
@@ -1416,7 +1420,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
R.id.menu_manage_filter_profiles -> {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
navController.safeNavigate(
findNavController().safeNavigate(
MapFragmentDirections.actionMapToFilterProfilesFragment()
)
true
@@ -1496,6 +1500,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
}
}
popup.setTouchModal(false)
popupMenu = popup
popup.show()
}
@@ -1579,5 +1584,8 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
override fun onDestroy() {
super.onDestroy()
/* if we don't dismiss the popup menu, it will be recreated in some cases
(split-screen mode) and then have references to a destroyed fragment. */
popupMenu?.dismiss()
}
}

View File

@@ -41,7 +41,7 @@ class UiSettingsFragment : BaseSettingsFragment() {
override fun onResume() {
super.onResume()
langPref.value = getAppLocale()
langPref.value = getAppLocale(requireContext())
immediateNavPref.isVisible = isGoogleMapsInstalled()
}

View File

@@ -26,9 +26,13 @@ import androidx.viewpager2.widget.ViewPager2
import coil.load
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.slider.RangeSlider
import net.vonforst.evmap.*
import net.vonforst.evmap.R
import net.vonforst.evmap.api.availability.ChargepointStatus
import net.vonforst.evmap.api.iconForPlugType
import net.vonforst.evmap.isDarkMode
import net.vonforst.evmap.kmPerMile
import net.vonforst.evmap.meterPerFt
import net.vonforst.evmap.shouldUseImperialUnits
import java.time.Instant
import kotlin.math.ceil
import kotlin.math.floor
@@ -69,7 +73,7 @@ fun invisibleUnlessAnimated(view: View, oldValue: Boolean, newValue: Boolean) {
if (oldValue == newValue) {
if (!newValue && view.visibility == View.VISIBLE && view.alpha == 1f) {
// view is initially invisible
view.visibility = View.GONE
view.visibility = View.INVISIBLE
} else {
return
}

View File

@@ -1,10 +1,12 @@
package net.vonforst.evmap.ui
import android.content.Context
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import net.vonforst.evmap.BuildConfig
import net.vonforst.evmap.R
import net.vonforst.evmap.storage.PreferenceDataSource
fun updateNightMode(prefs: PreferenceDataSource) {
AppCompatDelegate.setDefaultNightMode(
when (prefs.darkmode) {
@@ -25,13 +27,14 @@ fun updateAppLocale(language: String) {
)
}
fun getAppLocale(): String? {
fun getAppLocale(context: Context): String? {
val locales = AppCompatDelegate.getApplicationLocales()
return if (locales.isEmpty) {
"default"
} else {
val arr = Array(locales.size()) { locales.get(it)!!.toLanguageTag() }
LocaleListCompat.forLanguageTags(BuildConfig.supportedLocales).getFirstMatch(arr)
?.toLanguageTag()
val choices =
context.resources.getStringArray(R.array.pref_language_values).joinToString(",")
LocaleListCompat.forLanguageTags(choices).getFirstMatch(arr)?.toLanguageTag()
}
}
}

View File

@@ -10,50 +10,60 @@ import android.view.ViewGroup
import android.view.WindowManager
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.FrameLayout
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputLayout
import net.vonforst.evmap.R
import kotlin.math.roundToInt
private fun dialogEditText(ctx: Context): Pair<View, EditText> {
val container = FrameLayout(ctx)
container.setPadding(
(16 * ctx.resources.displayMetrics.density).toInt(), 0,
(16 * ctx.resources.displayMetrics.density).toInt(), 0
)
val input = EditText(ctx)
input.isSingleLine = true
container.addView(input)
return container to input
private fun dialogEditText(ctx: Context): Pair<TextInputLayout, EditText> {
val view = LayoutInflater.from(ctx).inflate(R.layout.dialog_textinput, null)
return view as TextInputLayout to view.findViewById(R.id.input)
}
fun showEditTextDialog(
ctx: Context,
customize: (MaterialAlertDialogBuilder, EditText) -> Unit
customize: (MaterialAlertDialogBuilder, EditText) -> Unit,
okAction: (String) -> Unit
): AlertDialog {
val (container, input) = dialogEditText(ctx)
val dialogBuilder = MaterialAlertDialogBuilder(ctx)
.setView(container)
.setPositiveButton(R.string.ok) { _, _ -> }
.setNegativeButton(R.string.cancel) { _, _ -> }
customize(dialogBuilder, input)
val dialog = dialogBuilder.show()
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
val okButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE)
// focus and show keyboard
input.requestFocus()
input.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
val text = input.text
val button = dialog.getButton(DialogInterface.BUTTON_POSITIVE)
if (text != null && button != null) {
button.performClick()
if (text != null && okButton != null) {
okButton.performClick()
return@setOnEditorActionListener true
}
}
false
}
okButton?.setOnClickListener {
if (input.text.isBlank()) {
container.isErrorEnabled = true
container.error = ctx.getString(R.string.required)
} else {
container.isErrorEnabled = false
okAction(input.text.toString())
dialog.dismiss()
}
}
return dialog
}

View File

@@ -12,6 +12,13 @@ import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike
class HideOnScrollFabBehavior(context: Context, attrs: AttributeSet) :
FloatingActionButton.Behavior(context, attrs) {
var hidden: Boolean = false
companion object {
fun from(view: View): HideOnScrollFabBehavior? {
return ((view.layoutParams as? CoordinatorLayout.LayoutParams)?.behavior as? HideOnScrollFabBehavior)
}
}
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
@@ -61,13 +68,13 @@ class HideOnScrollFabBehavior(context: Context, attrs: AttributeSet) :
child: FloatingActionButton,
dependency: View
): Boolean {
val behavior = BottomSheetBehaviorGoogleMapsLike.from<View>(dependency)
val behavior = BottomSheetBehaviorGoogleMapsLike.from(dependency)
when (behavior.state) {
BottomSheetBehaviorGoogleMapsLike.STATE_SETTLING -> {
}
BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN -> {
child.show()
if (!hidden) child.show()
}
else -> {
child.hide()
@@ -103,7 +110,7 @@ class HideOnScrollFabBehavior(context: Context, attrs: AttributeSet) :
child.hide()
} else if (dyConsumed < 0 && child.visibility != View.VISIBLE) {
// User scrolled up and the FAB is currently not visible -> show the FAB
child.show()
if (!hidden) child.show()
}
}
}

View File

@@ -3,7 +3,16 @@ package net.vonforst.evmap.viewmodel
import android.app.Application
import android.graphics.Point
import android.os.Parcelable
import androidx.lifecycle.*
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.liveData
import androidx.lifecycle.map
import androidx.lifecycle.switchMap
import androidx.lifecycle.viewModelScope
import com.car2go.maps.AnyMap
import com.car2go.maps.Projection
import com.car2go.maps.model.LatLng
@@ -24,7 +33,17 @@ import net.vonforst.evmap.api.openchargemap.OCMConnection
import net.vonforst.evmap.api.openchargemap.OCMReferenceData
import net.vonforst.evmap.api.stringProvider
import net.vonforst.evmap.autocomplete.PlaceWithBounds
import net.vonforst.evmap.model.*
import net.vonforst.evmap.model.ChargeLocation
import net.vonforst.evmap.model.Chargepoint
import net.vonforst.evmap.model.ChargepointListItem
import net.vonforst.evmap.model.FILTERS_DISABLED
import net.vonforst.evmap.model.FILTERS_FAVORITES
import net.vonforst.evmap.model.Favorite
import net.vonforst.evmap.model.FavoriteWithDetail
import net.vonforst.evmap.model.FilterValue
import net.vonforst.evmap.model.FilterValues
import net.vonforst.evmap.model.getMultipleChoiceValue
import net.vonforst.evmap.model.getSliderValue
import net.vonforst.evmap.storage.AppDatabase
import net.vonforst.evmap.storage.ChargeLocationsRepository
import net.vonforst.evmap.storage.EncryptedPreferenceDataStore
@@ -282,6 +301,12 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
}
}
val mapTrafficSupported: MutableLiveData<Boolean> by lazy {
MutableLiveData<Boolean>().apply {
value = false
}
}
val mapTrafficEnabled: MutableLiveData<Boolean> by lazy {
MutableLiveData<Boolean>().apply {
value = prefs.mapTrafficEnabled

View File

@@ -158,7 +158,6 @@
android:textColor="@android:color/white"
app:backgroundTintAvailability="@{BindingAdaptersKt.flatten(filteredAvailability.data.status.values())}"
app:invisibleUnless="@{filteredAvailability.data != null &amp;&amp; !expanded}"
app:invisibleUnlessAnimated="@{filteredAvailability.data != null &amp;&amp; !expanded}"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/txtName"
tools:backgroundTint="@color/available"

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.textfield.TextInputLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>

View File

@@ -53,6 +53,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/referral_tesla"
android:visibility="gone"
app:icon="@drawable/ic_tesla" />
<Button

View File

@@ -238,7 +238,8 @@
android:layout_gravity="top|end"
android:layout_marginEnd="20dp"
android:layout_marginTop="@dimen/layers_fab_top_padding"
app:tint="?android:colorControlNormal"
app:tint="?colorControlNormal"
app:backgroundTint="?android:colorBackground"
app:borderWidth="0dp"
app:srcCompat="@drawable/ic_layers"
app:layout_behavior="@string/hide_on_scroll_fab_behavior"
@@ -261,4 +262,4 @@
app:vm="@{vm}" />
</androidx.cardview.widget.CardView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
</layout>

View File

@@ -80,6 +80,7 @@
android:layout_marginEnd="16dp"
android:text="@string/map_details"
android:textAppearance="@style/TextAppearance.Material3.TitleSmall"
app:goneUnless="@{vm.mapTrafficSupported}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
@@ -94,6 +95,7 @@
android:layout_marginEnd="16dp"
android:text="@string/map_traffic"
android:checked="@={vm.mapTrafficEnabled}"
app:goneUnless="@{vm.mapTrafficSupported}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView23" />

View File

@@ -0,0 +1 @@
unqualifiedResLocale=en-US

View File

@@ -271,6 +271,7 @@
<string name="pref_chargeprice_currency_sek">Schwedische Krone (SEK)</string>
<string name="pref_chargeprice_currency_usd">US-Dollar (USD)</string>
<string name="pref_provider_google_maps">Google Maps</string>
<string name="pref_provider_osm">OpenStreetMap</string>
<string name="pref_provider_osm_mapbox">OpenStreetMap (Mapbox)</string>
<string name="about_contributors">Mitwirkende</string>
<string name="about_contributors_text">Dank an alle Mitwirkenden für ihre Beiträge von Code und Übersetzungen für EVMap:</string>

View File

@@ -380,4 +380,7 @@
<string name="status_faulted">Fora de serviço</string>
<string name="status_since">%1$s desde %2$s</string>
<string name="status_unknown">Estado Desconhecido</string>
<string name="pref_chargeprice_native_integration">Comparação de preços no EVMap</string>
<string name="pref_chargeprice_native_integration_on">Os preços serão exibidos diretamente no EVMap</string>
<string name="pref_chargeprice_native_integration_off">O botão de comparação de preços abrirá a app ou site do Chargeprice</string>
</resources>

View File

@@ -46,6 +46,6 @@
<string name="referral_maingau" translatable="false">Maingau</string>
<string name="referral_ewieeinfach" translatable="false">E wie einfach</string>
<string name="referral_eprimo" translatable="false">eprimo</string>
<string name="copyright_summary">©20202023 Johan von Forstner and contributors</string>
<string name="copyright_summary">©20202024 Johan von Forstner and contributors</string>
<string name="acra_backend_url" translatable="false">https://acra.muc.vonforst.net/report</string>
</resources>
</resources>

View File

@@ -271,6 +271,7 @@
<string name="pref_chargeprice_currency_sek">Swedish krona (SEK)</string>
<string name="pref_chargeprice_currency_usd">US dollar (USD)</string>
<string name="pref_provider_google_maps">Google Maps</string>
<string name="pref_provider_osm">OpenStreetMap</string>
<string name="pref_provider_osm_mapbox">OpenStreetMap (Mapbox)</string>
<string name="about_contributors">Contributors</string>
<string name="about_contributors_text">Thanks to all contributors for their coding and translation contributions to EVMap:</string>

View File

@@ -15,6 +15,7 @@
<item name="preferenceTheme">@style/AppTheme.Preference</item>
<item name="alertDialogTheme">@style/AppTheme.AlertDialog</item>
<item name="materialAlertDialogTheme">@style/AppTheme.AlertDialog</item>
<item name="snackbarButtonStyle">@style/Button.TextButton.Snackbar.App</item>
</style>
<style name="AppTheme.Preference" parent="@style/PreferenceThemeOverlay">
@@ -82,6 +83,10 @@
<item name="backgroundInsetBottom">24dp</item>
</style>
<style name="Button.TextButton.Snackbar.App" parent="Widget.Material3.Button.TextButton.Snackbar">
<item name="android:textColor">@color/colorPrimary</item>
</style>
<style name="CarAppTheme">
<item name="carColorPrimary">@color/colorPrimary</item>
<item name="carColorPrimaryDark">@color/colorPrimaryDark</item>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en" />
<locale android:name="de" />
<locale android:name="fr" />
<locale android:name="nb-NO" />
</locale-config>

View File

@@ -9,7 +9,7 @@
(e.g. in the debug version). -->
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="${applicationId}"
android:targetPackage="net.vonforst.evmap"
android:targetClass="net.vonforst.evmap.MapsActivity">
<extra
android:name="favorites"

View File

@@ -67,7 +67,7 @@ class NewMotionAvailabilityDetectorTest {
fun apiTest() {
for (chargepoint in listOf(2105L, 18284L)) {
val charger = runBlocking { api.getChargepointDetail(chargepoint).body()!! }
.chargelocations[0].convert("", true) as ChargeLocation
.chargelocations!![0].convert("", true) as ChargeLocation
println(charger)
runBlocking {

View File

@@ -60,7 +60,7 @@ class ChargepriceApiTest {
fun apiTest() {
for (chargepoint in listOf(2105L, 18284L)) {
val charger = runBlocking { ge.getChargepointDetail(chargepoint).body()!! }
.chargelocations[0].convert("", true) as ChargeLocation
.chargelocations!![0].convert("", true) as ChargeLocation
println(charger)
runBlocking {

View File

@@ -63,8 +63,8 @@ class GoingElectricApiTest {
val body = response.body()!!
assertEquals("ok", body.status)
assertEquals(null, body.startkey)
assertEquals(1, body.chargelocations.size)
val charger = body.chargelocations[0] as GEChargeLocation
assertEquals(1, body.chargelocations!!.size)
val charger = body.chargelocations!![0] as GEChargeLocation
assertEquals(2105, charger.id)
}
@@ -75,8 +75,8 @@ class GoingElectricApiTest {
val body = response.body()!!
assertEquals("ok", body.status)
assertEquals(null, body.startkey)
assertEquals(1, body.chargelocations.size)
val charger = body.chargelocations[0] as GEChargeLocation
assertEquals(1, body.chargelocations!!.size)
val charger = body.chargelocations!![0] as GEChargeLocation
assertEquals(34210, charger.id)
assertEquals(LocalTime.MIN, charger.openinghours!!.days!!.monday.start)
assertEquals(LocalTime.MAX, charger.openinghours!!.days!!.monday.end)
@@ -92,8 +92,8 @@ class GoingElectricApiTest {
val body = response.body()!!
assertEquals("ok", body.status)
assertEquals(null, body.startkey)
assertEquals(2, body.chargelocations.size)
val charger = body.chargelocations[0] as GEChargeLocation
assertEquals(2, body.chargelocations!!.size)
val charger = body.chargelocations!![0] as GEChargeLocation
assertEquals(41161, charger.id)
}
@@ -106,7 +106,7 @@ class GoingElectricApiTest {
val body = response.body()!!
assertEquals("ok", body.status)
assertEquals(null, body.startkey)
assertEquals(0, body.chargelocations.size)
assertEquals(0, body.chargelocations!!.size)
}
@Test
@@ -118,8 +118,8 @@ class GoingElectricApiTest {
val body = response.body()!!
assertEquals("ok", body.status)
assertEquals(2, body.startkey)
assertEquals(2, body.chargelocations.size)
val charger = body.chargelocations[0] as GEChargeLocation
assertEquals(2, body.chargelocations!!.size)
val charger = body.chargelocations!![0] as GEChargeLocation
assertEquals(41161, charger.id)
}
}

View File

@@ -10,11 +10,10 @@ buildscript {
gradlePluginPortal()
}
dependencies {
classpath("com.android.tools.build:gradle:8.2.2")
classpath("com.android.tools.build:gradle:8.3.2")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
classpath("com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$aboutLibsVersion")
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$navVersion")
classpath("pt.jcosta.resourceplaceholders:plugin:0.7")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -27,9 +26,6 @@ allprojects {
mavenCentral()
//noinspection JcenterRepositoryObsolete
maven { setUrl("https://jitpack.io") }
maven {
setUrl("https://raw.githubusercontent.com/ev-map/mapbox-gl-native-android/mvn")
}
}
}

View File

@@ -17,6 +17,12 @@ be put into the app in the form of a resource file called `apikeys.xml` under
<string name="mapbox_key" translatable="false">
insert your Mapbox key here
</string>
<string name="jawg_key" translatable="false">
insert your Jawg Maps key here
</string>
<string name="arcgis_key" translatable="false">
insert your ArcGIS Maps key here
</string>
<string name="goingelectric_key" translatable="false">
insert your GoingElectric key here
</string>
@@ -52,10 +58,12 @@ Map providers
The different Map SDKs are wrapped by our [fork](https://github.com/ev-map/AnyMaps) of the
[AnyMaps](https://github.com/sharenowTech/AnyMaps) library to provide a common API. The `google`
build flavor of the app includes both Google Maps and Mapbox and allows the user to switch between
the two, while the `foss` flavor only includes the Mapbox SDK.
build flavor of the app includes both Google Maps and OpenStreetMap (vector tiles from
[Jawg Maps](https://www.jawg.io/en/) through [MapLibre](https://maplibre.org/)) and allows the user
to switch between the two, while the `foss` flavor only includes OSM.
> ⚠️ When testing the app using the Android Emulator, we recommend using Google Maps and not Mapbox, as the latter has
> ⚠️ When testing the app using the Android Emulator, we recommend using Google Maps and not
> OSM/MapLibre, as the latter has
[issues displaying the markers](https://github.com/mapbox/mapbox-gl-native/issues/10829). It works fine on real Android devices.
### Google Maps
@@ -77,9 +85,39 @@ the two, while the `foss` flavor only includes the Mapbox SDK.
</details>
### Jawg Maps
[Dynamic Maps](https://www.jawg.io/docs/apidocs/maps/)
<details>
<summary>How to obtain an API key</summary>
1. [Sign up](https://www.jawg.io/lab) for a Jawg account
2. Under [Access Tokens](https://www.jawg.io/lab/access-tokens), copy your default access token or
create a new one. Do not restrict it to a specific origin (this setting is not compatible with
Android apps).
</details>
### ArcGIS
[World Imagery basemap](https://www.arcgis.com/home/item.html?id=10df2279f9684e4a9f6a7f08febac2a9)
*We use this for the satellite map, as [Jawg's](https://blog.jawg.io/satellite-imaging/) satellite
style does not have global coverage.*
<details>
<summary>How to obtain an API key</summary>
1. [Sign up](https://developers.arcgis.com/dashboard/) for an ArcGIS developer account
2. In the dashboard, copy your default API key or create a new one. It has to have access to the
"Basemaps" service.
</details>
### Mapbox
[Maps SDK for Android](https://docs.mapbox.com/android/maps)
[Geocoding API](https://docs.mapbox.com/api/search/geocoding/)
*previously we also used Mapbox's Maps SDK, but this has now been switched to Jawg Maps.*
<details>
<summary>How to obtain an API key</summary>
@@ -91,7 +129,6 @@ the two, while the `foss` flavor only includes the Mapbox SDK.
</details>
Charging station databases
--------------------------

View File

@@ -5,7 +5,7 @@ Funkce:
- Zobrazuje všechny nabíjecí stanice z komunitou spravovaných databází GoingElectric.de a Open Charge Map.
- Informace o dostupnosti v reálném čase (pouze v Evropě)
- Integrované srovnání cen pomocí Chargeprice.app (pouze v Evropě)
- Mapové podklady z OpenStreetMap (Mapbox)
- Mapové podklady z OpenStreetMap
- Vyhledávání míst
- Pokročilé možnosti filtrování, včetně uložených profilů filtrů
- Seznam oblíbených, také s informacemi o dostupnosti

View File

@@ -0,0 +1,7 @@
Änderungen:
- OpenStreetMap-Karten: Wechsel der Datenquelle von Mapbox zu Jawg Maps
Fehler behoben:
- App-Shortcuts repariert
- Anzeigefehler behoben
- Abstürze behoben

View File

@@ -5,7 +5,7 @@ Funktionen:
- Anzeige der Stromtankstellen aus den Stromtankstellenverzeichnissen von GoingElectric.de und Open Charge Map
- Echtzeit-Verfügbarkeitsanzeige für viele Ladesäulen (nur in Europa)
- Integrierter Preisvergleich für die jeweilige Ladesäule mit Chargeprice.app (nur in Europa)
- Kartendaten von OpenStreetMap (Mapbox)
- Kartendaten von OpenStreetMap
- Suche nach Orten
- Erweiterte Filterfunktionen, Filterprofile speichern
- Favoritenliste, auch mit Anzeige der Verfügbarkeit

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,7 @@
Changes:
- OpenStreetMap maps: Change data source from Mapbox to Jawg Maps
Bugfixes:
- Fixed app shortcuts
- Fixed display errors
- Fixed crashes

View File

@@ -5,7 +5,7 @@ Features:
- Shows all charging stations from the community-maintained GoingElectric.de and Open Charge Map directories
- Realtime availability information (only in Europe)
- Integrated price comparison using Chargeprice.app (only in Europe)
- Map data from OpenStreetMap (Mapbox)
- Map data from OpenStreetMap
- Search for places
- Advanced filtering options, including saved filter profiles
- Favorites list, also with availability information

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -5,7 +5,7 @@ Caractéristiques :
- Affiche toutes les stations de recharge des répertoires GoingElectric.de et Open Charge Map gérés par la communauté.
- Informations sur la disponibilité en temps réel (uniquement en Europe)
- Comparaison des prix intégrée grâce à Chargeprice.app (uniquement en Europe)
- Données cartographiques provenant d'OpenStreetMap (Mapbox)
- Données cartographiques provenant d'OpenStreetMap
- Recherche de lieux
- Options de filtrage avancées, y compris les profils de filtrage enregistrés
- Liste de favoris, avec également des informations sur la disponibilité

View File

@@ -5,7 +5,7 @@ Du finner info om ladestasjoner i hele verden og sanntidsinfo for mange av dem s
- Materiell design
- Sanntidsinfo (kun i Europa)
- Integrert sammenligningsinfo ved bruk av Chargeprice.app (kun i Europa)
- Kartdata fra OpenStreetMap (Mapbox)
- Kartdata fra OpenStreetMap
- Søk etter steder
- Avanserte filtreringsvalg, inkludert lagrede filterprofiler
- Favorittliste, som også har tilgjengelighetsinfo

View File

@@ -5,7 +5,7 @@ Kenmerken:
- Toont alle laadpunten van de GoingElectric.de en Open Charge Map databanken
- Real-time status (enkel in Europa)
- Geïntegreerde prijsvergelijking via Chargeprice.app (enkel in Europe)
- Kaartgegevens van OpenStreetMap (Mapbox)
- Kaartgegevens van OpenStreetMap
- Zoek naar locaties
- Geavanceerde filtermogelijkheden, inclusief bewaarde filterprofielen
- Lijst van favorieten, met statusinformatie

View File

@@ -5,7 +5,7 @@ Destaques:
- Mostra todas as estações de carregamento dos diretórios GoingElectric.de e Open Charge Map mantidos pela comunidade
- Informação de disponibilidade em tempo real (apenas na Europa)
- Comparação de preços integrada usando o Chargeprice.app (apenas na Europa)
- Informação do mapa via OpenStreetMap (Mapbox)
- Informação do mapa via OpenStreetMap
- Pesquise lugares
- Opções de filtragem avançadas, incluindo filtros de pesquisa que podem ser guardados
- Lista de favoritos, também com informações de disponibilidade