Compare commits

...

42 Commits
1.6.2 ... 1.6.7

Author SHA1 Message Date
Johan von Forstner
c48f33e265 Release 1.6.7 2023-08-21 19:26:30 +02:00
johan12345
8ba4897026 Android Automotive: update donate link 2023-08-21 19:22:45 +02:00
Johan von Forstner
42916d71ca Onboarding: Do not select GoingElectric by default
instead make the user actively choose the data source
#291
2023-08-03 16:47:20 +02:00
Johan von Forstner
5ca7524e8b OpenChargeMap: Map "Tesla Model S/X" plug to Chargepoint.SUPERCHARGER
#6
2023-08-03 16:34:10 +02:00
Johan von Forstner
c37f72a26b reduce vertical padding around dialogs
#292
2023-08-02 20:46:16 +02:00
Johan von Forstner
6f0113c50d adjustments to data source selection screen for smaller screens 2023-08-02 19:58:05 +02:00
Johan von Forstner
f99ea7ca9e Fix formatting of Portuguese string 2023-08-02 16:21:02 +02:00
Johan von Forstner
788db0c10f fix popup menu theme in dark mode
see #287
2023-08-02 16:09:48 +02:00
Hosted Weblate
db2213a50f Translated using Weblate (Portuguese)
Currently translated at 100.0% (349 of 349 strings)

Co-authored-by: Celso Azevedo <mail@celsoazevedo.com>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/pt/
Translation: EVMap/Android
2023-08-02 16:08:45 +02:00
Johan von Forstner
ace4126035 more uppercase consistency 2023-08-02 15:45:47 +02:00
Johan von Forstner
d5d6e4f314 GoingElectric: nullability fix 2023-08-01 20:23:54 +02:00
Johan von Forstner
55999d15e6 upper/lower case consistency
as mentioned in comment in #287
2023-07-30 20:01:11 +02:00
Johan von Forstner
b61ca609d3 GoingElectric: fix "Tesla Supercharger CCS" filter
"Tesla Supercharger CCS" is a bit peculiar - it is available as a filter, but the API just returns "CCS" in the charging station details. So we cannot use it for filtering as it won't work in the local database. So we join them into a single filter option. If you want to find Tesla Superchargers with CCS, you can still do that using the network filter.
2023-07-27 10:16:22 +02:00
johan12345
b0afad2144 fix nullability issue in TeslaAvailabilityDetector 2023-07-19 20:59:47 +02:00
johan12345
94842954e3 Release 1.6.6 2023-07-16 11:23:10 +02:00
johan12345
1bee5f7e13 upgrade Coil to 2.4.0 2023-07-16 11:22:02 +02:00
johan12345
d636cde70e EnBwAvailabilityDetector: zoom in until no markers are grouped 2023-07-14 19:25:53 +02:00
johan12345
2a4497fe7a fix NPE in EnBwAvailabilityDetector 2023-07-14 19:23:57 +02:00
johan12345
10e3287d82 fix division by zero error 2023-07-14 18:48:26 +02:00
Johan von Forstner
a0f7a389c8 CI: fix file name 2023-07-13 21:52:45 +02:00
johan12345
1cabb8dccf Release 1.6.5 2023-07-13 21:35:08 +02:00
johan12345
85079bb888 fix SQL query for minConnectors filter 2023-07-13 21:20:05 +02:00
johan12345
10bfc21f54 add padding for privacy policy checkbox 2023-07-13 21:11:43 +02:00
johan12345
ae33fce637 add fossAutomotive variant to CI 2023-07-02 17:45:17 +02:00
johan12345
773d57b9a9 update explanations of variants
refs #281
2023-07-02 17:45:07 +02:00
Johan von Forstner
022f570322 add checkbox to accept privacy policy during onboarding 2023-07-01 22:01:35 +02:00
johan12345
a6c2b30325 Android Auto: Show the charger icon in the result row
fixes #284
2023-07-01 21:32:24 +02:00
johan12345
2210e65e5c Add reload button in detail view
fixes #287
2023-07-01 21:10:11 +02:00
johan12345
024e3cef35 add menu item to manually reload map content
#287
2023-07-01 19:50:31 +02:00
johan12345
687ef2ec0f add vehicle brand look-up table for Android Auto
fixes #289
2023-07-01 19:40:57 +02:00
johan12345
9e61dce7be enable fossAutomotive variant
refs #281
2023-07-01 12:16:41 +02:00
johan12345
aad7a320d0 make Android Auto app also available in foss build
The only Google dependency it references is the Car App Library, which is open source. GMS is not imported by the Android Auto app.
Of course it won't actually be usable on devices without the Android Auto app, which itself requires GMS.

preparation for #281
2023-07-01 12:16:41 +02:00
johan12345
96df684b80 remove unneeded overrideLibrary in manifest 2023-07-01 12:16:41 +02:00
Johan von Forstner
7903c027c7 AvailabilityDetector: Further increase max merging distance
to 60m
2023-06-22 17:10:48 +02:00
johan12345
06801c1898 Release 1.6.4 2023-06-17 17:59:30 +02:00
johan12345
c946b0fcd3 TeslaAvailabilityDetector: fix cases when number of chargepoints does not match 2023-06-17 17:38:33 +02:00
johan12345
dd4fcc7550 Run clustering on Dispatchers.Default, not Dispatchers.IO 2023-06-16 23:08:01 +02:00
johan12345
2ce82b961b Introduce clustering up to zoom level 15 in very crowded places (>500 chargers within view)
refs #285
2023-06-16 22:50:14 +02:00
johan12345
1be519b1ee Release 1.6.3 2023-06-14 22:02:07 +02:00
Hosted Weblate
01737f21d2 Translated using Weblate (Portuguese)
Currently translated at 100.0% (313 of 313 strings)

Co-authored-by: Celso Azevedo <mail@celsoazevedo.com>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/pt/
Translation: EVMap/Android
2023-06-14 21:07:41 +02:00
johan12345
17ce9f420b Tesla: CongestionPriceHistogram is nullable 2023-06-13 22:57:10 +02:00
johan12345
6eb90498eb GoingElectric: fix SQL implementation of network/barrierFree/chargeCards filters 2023-06-13 20:37:30 +02:00
95 changed files with 869 additions and 522 deletions

View File

@@ -76,3 +76,12 @@ jobs:
asset_path: app/build/outputs/apk/googleAutomotive/release/app-google-automotive-release.apk
asset_name: app-google-automotive-release.apk
asset_content_type: application/vnd.android.package-archive
- name: upload Foss Automotive artifact
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ github.token }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: app/build/outputs/apk/fossAutomotive/release/app-foss-automotive-release.apk
asset_name: app-foss-automotive-release.apk
asset_content_type: application/vnd.android.package-archive

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
buildvariant: [ FossNormal, GoogleNormal, GoogleAutomotive ]
buildvariant: [ FossNormal, FossAutomotive, GoogleNormal, GoogleAutomotive ]
steps:
- name: Check out code
uses: actions/checkout@v2

View File

@@ -43,13 +43,27 @@ EVMap uses and put them into the app in the form of a resource file called `apik
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` variant only uses Mapbox data and should run on most Android devices, even without
- The `foss` variants only use Mapbox 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).
- `fossAutomotive` can be installed directly on
[Android Automotive OS (AAOS)](https://source.android.com/docs/automotive/start/what_automotive)
headunits without Google services.
It does not provide the usual smartphone UI, and requires an implementation of the
[AOSP template app host](https://source.android.com/docs/automotive/hmi/aosp_host)
to be installed. If you are an OEM and would like to distribute EVMap to your AAOS vehicles,
please [get in touch](mailto:evmap@vonforst.net).
- The `google` variants also include access to Google Maps data.
- `googleNormal` is intended to run on smartphones and tablets, and also includes the Android
Auto app for use on the car display.
- `googleAutomotive` variant is intended to be installed directly on car infotainment systems
using the Google-flavored Android Automotive OS. It does not provide the usual smartphone UI.
- `googleAutomotive` can be installed directly on car infotainment systems running the
Google-flavored Android Automotive OS (Google Automotive Services /
["Google built-in"](https://built-in.google/cars/)).
It does not provide the usual smartphone UI, and requires the
[Google Automotive App Host](https://play.google.com/store/apps/details?id=com.google.android.apps.automotive.templates.host)
to run, which should be preinstalled on those cars and can be updated through the Play Store.
We also have a special [documentation page](doc/android_auto.md) on how to test the Android Auto
app.

View File

@@ -21,8 +21,8 @@ android {
minSdkVersion 21
targetSdkVersion 33
// NOTE: always increase versionCode by 2 since automotive flavor uses versionCode + 1
versionCode 184
versionName "1.6.2"
versionCode 194
versionName "1.6.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resConfigs supportedLocales.split(',')
@@ -73,13 +73,6 @@ android {
minSdkVersion 29
}
}
variantFilter { variant ->
def names = variant.flavors*.name
// Android Automotive OS app is always based on Google variant
if (names.contains("automotive") && !names.contains("google")) {
setIgnore(true)
}
}
compileOptions {
coreLibraryDesugaringEnabled true
@@ -193,7 +186,7 @@ dependencies {
implementation 'com.squareup.moshi:moshi-kotlin:1.15.0'
implementation 'com.squareup.moshi:moshi-adapters:1.15.0'
implementation 'com.markomilos.jsonapi:jsonapi-retrofit:1.1.0'
implementation 'io.coil-kt:coil:1.1.0'
implementation 'io.coil-kt:coil:2.4.0'
implementation 'com.github.ev-map:StfalconImageViewer:5082ebd392'
implementation "com.mikepenz:aboutlibraries-core:$about_libs_version"
implementation "com.mikepenz:aboutlibraries:$about_libs_version"
@@ -205,9 +198,9 @@ dependencies {
// Android Auto
def carAppVersion = '1.3.0-rc01'
googleImplementation "androidx.car.app:app:$carAppVersion"
googleNormalImplementation "androidx.car.app:app-projected:$carAppVersion"
googleAutomotiveImplementation "androidx.car.app:app-automotive:$carAppVersion"
implementation "androidx.car.app:app:$carAppVersion"
normalImplementation "androidx.car.app:app-projected:$carAppVersion"
automotiveImplementation "androidx.car.app:app-automotive:$carAppVersion"
// AnyMaps
def anyMapsVersion = '8f1226e1c5'

View File

@@ -1,16 +0,0 @@
package net.vonforst.evmap.fragment
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
class OnboardingViewPagerAdapter(fragment: Fragment) :
FragmentStateAdapter(fragment) {
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment = when (position) {
0 -> WelcomeFragment()
1 -> IconsFragment()
2 -> DataSourceSelectFragment()
else -> throw IllegalArgumentException()
}
}

View File

@@ -1,53 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="androidx.car.app.MAP_TEMPLATES" />
<uses-permission android:name="com.google.android.gms.permission.CAR_FUEL" />
<uses-permission android:name="com.google.android.gms.permission.CAR_SPEED" />
<uses-sdk tools:overrideLibrary="androidx.car.app,androidx.car.app.projected" />
<queries>
<package android:name="com.google.android.projection.gearhead" />
<package android:name="com.google.android.apps.automotive.templates.host" />
</queries>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/google_maps_key" />
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<meta-data
android:name="androidx.car.app.theme"
android:resource="@style/CarAppTheme" />
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1" />
<service
android:name=".auto.CarAppService"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:exported="true">
<intent-filter>
<action
android:name="androidx.car.app.CarAppService"
android:category="androidx.car.app.category.POI" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="net.vonforst.evmap" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</service>
</application>
</manifest>

View File

@@ -1,69 +0,0 @@
package net.vonforst.evmap.fragment
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.DecelerateInterpolator
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import net.vonforst.evmap.databinding.FragmentOnboardingAndroidAutoBinding
class OnboardingViewPagerAdapter(fragment: Fragment) :
FragmentStateAdapter(fragment) {
override fun getItemCount(): Int = 4
override fun createFragment(position: Int): Fragment = when (position) {
0 -> WelcomeFragment()
1 -> IconsFragment()
2 -> AndroidAutoFragment()
3 -> DataSourceSelectFragment()
else -> throw IllegalArgumentException()
}
}
class AndroidAutoFragment : OnboardingPageFragment() {
private lateinit var binding: FragmentOnboardingAndroidAutoBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentOnboardingAndroidAutoBinding.inflate(inflater, container, false)
binding.btnGetStarted.setOnClickListener {
parent.goToNext()
}
binding.imgAndroidAuto.alpha = 0f
return binding.root
}
@SuppressLint("Recycle")
override fun onResume() {
super.onResume()
val animators =
listOf(
ObjectAnimator.ofFloat(binding.imgAndroidAuto, "translationY", -20f, 0f).apply {
interpolator = DecelerateInterpolator()
},
ObjectAnimator.ofFloat(binding.imgAndroidAuto, "alpha", 0f, 1f).apply {
interpolator = DecelerateInterpolator()
}
)
AnimatorSet().apply {
playTogether(animators)
start()
}
}
override fun onPause() {
super.onPause()
binding.imgAndroidAuto.alpha = 0f
}
}

View File

@@ -1,39 +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="auto_location_service">EVMap läuft unter Android Auto und nutzt dafür deinen Standort.</string>
<string name="auto_no_chargers_found">Keine Ladestationen in der Nähe gefunden</string>
<string name="auto_no_favorites_found">Keine Favoriten gefunden</string>
<string name="open_in_app">In App öffnen</string>
<string name="opened_on_phone">Auf dem Telefon geöffnet</string>
<string name="auto_location_permission_needed">Um EVMap auf Android Auto zu nutzen, braucht die App Zugriff auf deinen Standort.</string>
<string name="auto_vehicle_data_permission_needed">Für diese Funktion benötigt EVMap Zugriff auf Daten deines Fahrzeugs.</string>
<string name="grant_on_phone">Auf Telefon zulassen</string>
<string name="auto_chargers_closeby">In der Nähe</string>
<string name="auto_favorites">Favoriten</string>
<string name="auto_chargers_near_location">Nahe %s</string>
<string name="auto_fault_report_date">⚠️ Störungsmeldung (%s)</string>
<string name="auto_no_refresh_possible">Weitere Aktualisierung nicht möglich. Bitte zurück gehen und neu starten.</string>
<string name="auto_prices">Preise</string>
<string name="auto_vehicle_data">Fahrzeugdaten</string>
<string name="auto_charging_level">Ladezustand</string>
<string name="auto_no_data">Nicht verfügbar</string>
<string name="auto_range">Reichweite</string>
<string name="auto_speed">Geschwindigkeit</string>
<string name="auto_heading">Fahrtrichtung</string>
<string name="auto_settings">Einstellungen</string>
<string name="welcome_android_auto">Android Auto-Unterstützung</string>
<string name="welcome_android_auto_detail">Auf unterstützen Autos kannst du EVMap auch mit Android Auto nutzen. Öffne dazu einfach die EVMap-App aus dem Menü von Android Auto.</string>
<string name="sounds_cool">klingt cool</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap konnte das Fahrzeugmodell nicht erkennen.</string>
<string name="auto_chargeprice_vehicle_unknown">Keins der in der App ausgewählten Fahrzeuge passt zu diesem Fahrzeug (%1$s %2$s).</string>
<string name="auto_chargeprice_vehicle_ambiguous">Mehrere der in der App ausgewählten Fahrzeuge passen zu diesem Fahrzeug (%1$s %2$s).</string>
<string name="auto_chargers_ahead">Nur Ladestationen in Fahrtrichtung</string>
<string name="settings_android_auto_chargeprice_range">Ladebereich für Preisvergleich</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="selecting_all">alle Einträge ausgewählt</string>
<string name="selecting_none">alle Einträge abgewählt</string>
<string name="loading">Lade…</string>
<string name="auto_multipage_goto">Seite %d</string>
<string name="auto_multipage">(%d/%d)</string>
</resources>

View File

@@ -3,38 +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="auto_location_service">EVMap fonctionne sur Android Auto et utilise votre position.</string>
<string name="open_in_app">Ouvrir dans l\'application</string>
<string name="opened_on_phone">Ouvert sur le téléphone</string>
<string name="auto_location_permission_needed">Pour exécuter EVMap sur Android Auto, vous devez autoriser l\'accès à votre emplacement.</string>
<string name="grant_on_phone">Grant au téléphone</string>
<string name="auto_prices">Prix</string>
<string name="auto_vehicle_data">Données sur le véhicule</string>
<string name="auto_range">Autonomie</string>
<string name="auto_speed">Vitesse</string>
<string name="welcome_android_auto">Prise en charge dAndroid Auto</string>
<string name="sounds_cool">ça a l\'air cool</string>
<string name="auto_chargeprice_vehicle_unknown">Aucun des véhicules sélectionnés dans l\'application ne correspond à ce véhicule (%1$s %2$s).</string>
<string name="auto_chargeprice_vehicle_ambiguous">Plusieurs véhicules sélectionnés dans l\'application correspondent à ce véhicule (%1$s %2$s).</string>
<string name="selecting_all">tous les éléments sélectionnés</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="auto_chargeprice_vehicle_unavailable">EVMap n\'a pas pu déterminer le modèle de votre véhicule.</string>
<string name="auto_no_chargers_found">Aucun chargeur à proximité n\'a été trouvé</string>
<string name="auto_no_favorites_found">Pas de favoris trouvés</string>
<string name="auto_charging_level">Niveau de charge</string>
<string name="auto_chargers_closeby">Chargeurs à proximité</string>
<string name="auto_chargers_near_location">Près de %s</string>
<string name="auto_fault_report_date">⚠️ Rapport d\'anomalie (%s)</string>
<string name="auto_no_data">Indisponible</string>
<string name="auto_settings">Paramètres</string>
<string name="selecting_none">désélectionner tous les éléments</string>
<string name="auto_vehicle_data_permission_needed">Pour cette fonction, EVMap doit avoir accès aux données de votre véhicule.</string>
<string name="auto_heading">Direction</string>
<string name="auto_favorites">Favoris</string>
<string name="auto_no_refresh_possible">D\'autres mises à jour ne sont pas possibles. Veuillez revenir en arrière et redémarrer.</string>
<string name="settings_android_auto_chargeprice_range">Plage de charge pour la comparaison des prix</string>
<string name="welcome_android_auto_detail">Vous pouvez également utiliser EVMap à partir d\'Android Auto sur les voitures prises en charge. Il suffit de sélectionner l\'application EVMap dans le menu Android Auto.</string>
<string name="loading">Chargement…</string>
<string name="auto_multipage_goto">Page %d</string>
<string name="auto_multipage">(%d/%d)</string>
</resources>

View File

@@ -3,39 +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="auto_favorites">Favoritter</string>
<string name="auto_charging_level">Ladingsnivå</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap kunne ikke fastsette kjøretøymodellen.</string>
<string name="selecting_none">fravalgte alle elementer</string>
<string name="grant_on_phone">Innvilg på mobilenheten</string>
<string name="auto_chargers_closeby">Ladere i nærheten</string>
<string name="auto_prices">Pris</string>
<string name="auto_no_chargers_found">Ingen ladere i nærheten</string>
<string name="auto_no_favorites_found">Fant ikke noen favoritter</string>
<string name="open_in_app">Åpne i programmet</string>
<string name="auto_location_service">EVMap kjører på Android Auto og bruker posisjonen din.</string>
<string name="auto_heading">Fartsretning</string>
<string name="opened_on_phone">Åpnet på mobilenheten</string>
<string name="auto_location_permission_needed">Innvilg posisjonstilgang for å bruke EVMap på Android Auto.</string>
<string name="auto_chargers_near_location">Nær %s</string>
<string name="auto_fault_report_date">⚠️ Feilrapport (%s)</string>
<string name="auto_vehicle_data">Kjøretøydata</string>
<string name="auto_no_data">Utilgjengelig</string>
<string name="auto_speed">Hastighet</string>
<string name="auto_settings">Innstillinger</string>
<string name="auto_chargeprice_vehicle_unknown">Ingen av kjøretøyene valgt i programmet samsvarer med dette kjøretøyet (%1$s %2$s).</string>
<string name="welcome_android_auto">Android Auto-støtte</string>
<string name="auto_chargeprice_vehicle_ambiguous">Flere kjøretøy valgt i programmet samsvarer med dette kjøretøyet (%1$s %2$s).</string>
<string name="auto_vehicle_data_permission_needed">EvMap trenger tilgang til kjøretøydata for å bruke denne funksjonen.</string>
<string name="auto_no_refresh_possible">Videre oppdateringer er ikke mulig. Gå tilbake og start på ny.</string>
<string name="auto_range">Rekkevidde</string>
<string name="welcome_android_auto_detail">Du kan også bruke EVMap inne i Android Auto på bilder som støtter dette ved å velge det i Android Auto-menyen.</string>
<string name="settings_android_auto_chargeprice_range">Prissammenligning for laderekkevidde fordelt på pris</string>
<string name="data_sources_hint">I innstillingene kan du også bytte mellom Google Maps og OpenStreetMap (Mapbox) for kartdata.</string>
<string name="selecting_all">valgte alle elementene</string>
<string name="sounds_cool">den er grei</string>
<string name="auto_chargers_ahead">Kun ladere i kjøreretningen</string>
<string name="loading">Laster inn …</string>
<string name="auto_multipage_goto">Side %d</string>
<string name="auto_multipage">(%d/%d)</string>
</resources>

View File

@@ -1,41 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="auto_chargeprice_vehicle_ambiguous">Meerdere voertuigen geselecteerd in de app komen overeen met dit voertuig (%1$s %2$s).</string>
<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="auto_location_service">EVMap draait op Android Auto en gebruikt jouw locatie.</string>
<string name="auto_no_chargers_found">Geen laadpunten gevonden in de omgeving</string>
<string name="auto_no_favorites_found">Geen favorieten gevonden</string>
<string name="open_in_app">Open in de app</string>
<string name="opened_on_phone">Geopend op de telefoon</string>
<string name="auto_location_permission_needed">Om EVMap op Android Auto te gebruiken, moet je toegang geven tot je locatie.</string>
<string name="auto_vehicle_data_permission_needed">Voor deze functie heeft EVMap toegang nodig tot de gegevens van je voertuig.</string>
<string name="grant_on_phone">Geef toestemming op telefoon</string>
<string name="auto_chargers_closeby">Oplaadpunten in de buurt</string>
<string name="auto_favorites">Favorieten</string>
<string name="auto_chargers_near_location">Nabij %s</string>
<string name="auto_fault_report_date">⚠️ Foutrapport (%s)</string>
<string name="auto_no_refresh_possible">Verdere updates zijn niet mogelijk. Ga terug en herbegin.</string>
<string name="auto_prices">Prijzen</string>
<string name="auto_vehicle_data">Voertuiggegevens</string>
<string name="auto_charging_level">Laadniveau (SoC)</string>
<string name="auto_no_data">Niet beschikbaar</string>
<string name="auto_range">Reikwijdte</string>
<string name="auto_speed">Snelheid</string>
<string name="auto_heading">Richting</string>
<string name="auto_settings">Instellingen</string>
<string name="welcome_android_auto">Android Auto support</string>
<string name="welcome_android_auto_detail">Je kan EVMap ook gebruiken in Android Auto op ondersteunde voertuigen. Selecteer gewoon de EVMap app in het Android Auto menu.</string>
<string name="sounds_cool">klinkt cool</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap kon je voertuigtype niet bepalen.</string>
<string name="auto_chargers_ahead">Alleen laadpunten in rijrichting</string>
<string name="settings_android_auto_chargeprice_range">Laadbereik voor prijsvergelijking</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="selecting_all">alle items geselecteerd</string>
<string name="selecting_none">alle items gedeselecteerd</string>
<string name="loading">Laden…</string>
<string name="auto_multipage_goto">Pagina %d</string>
<string name="auto_multipage">(%d/%d)</string>
<string name="auto_chargeprice_vehicle_unknown">Geen enkel voertuig geselecteerd in de app komt overeen met dit voertuig (%1$s %2$s).</string>
</resources>

View File

@@ -1,41 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="auto_no_chargers_found">Não foram encontrados carregadores próximo de si</string>
<string name="auto_no_favorites_found">Nenhum favorito encontrado</string>
<string name="opened_on_phone">Aberto no telefone</string>
<string name="auto_location_permission_needed">Para usar o EVMap no Android Auto, permita o acesso à sua localização.</string>
<string name="open_in_app">Abrir na app</string>
<string name="auto_vehicle_data_permission_needed">Para esta funcionalidade, o EVMap precisa de aceder aos dados do seu veículo.</string>
<string name="auto_chargers_closeby">Carregadores próximos</string>
<string name="grant_on_phone">Conceda permissões no telefone</string>
<string name="auto_chargers_near_location">Perto de %s</string>
<string name="auto_favorites">Favoritos</string>
<string name="auto_chargeprice_vehicle_ambiguous">Vários veículos selecionados na app correspondem a este veículo (%1$s %2$s).</string>
<string name="selecting_none">todos os items desmarcados</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="selecting_all">todos os items selecionados</string>
<string name="loading">Carregando…</string>
<string name="auto_multipage_goto">Página %d</string>
<string name="auto_multipage">(%d/%d)</string>
<string name="settings_android_auto_chargeprice_range">Escala de carregamento para comparação de preços</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.
\n
\nA Google cobra 15% de cada doação.</string>
<string name="auto_location_service">O EVMap está a funcionar no Android Auto e usando a sua localização.</string>
<string name="auto_fault_report_date">⚠️ Problemas (%s)</string>
<string name="auto_no_refresh_possible">Não é possível atualizar. Por favor volte atrás e reinicie.</string>
<string name="auto_prices">Preços</string>
<string name="auto_vehicle_data">Dados do veículo</string>
<string name="auto_charging_level">Nível de carregamento</string>
<string name="auto_no_data">Não disponível</string>
<string name="auto_speed">Velocidade</string>
<string name="auto_heading">Direção</string>
<string name="auto_settings">Definições</string>
<string name="welcome_android_auto">Suporte para Android Auto</string>
<string name="auto_range">Alcance</string>
<string name="welcome_android_auto_detail">Também pode usar o EVMap no Android Auto em carros compatíveis. Basta selecionar a app EVMap no menu do Android Auto.</string>
<string name="auto_chargeprice_vehicle_unavailable">O EVMap não pôde determinar o modelo do seu veículo.</string>
<string name="auto_chargeprice_vehicle_unknown">Nenhum dos veículos selecionados na app corresponde a este veículo (%1$s %2$s).</string>
<string name="auto_chargers_ahead">Apenas carregadores na direção do destino</string>
<string name="sounds_cool">Continuar</string>
<string name="data_sources_hint">Também pode mudar entre o Google Maps e OpenStreetMap (Mapbox) nas definições da app.</string>
</resources>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="gauge_active">#00e676</color>
<color name="gauge_middle">#087f23</color>
<color name="gauge_inactive">#9e9e9e</color>
<color name="charger_100kw_dark">#FBC02D</color>
</resources>

View File

@@ -1,39 +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="auto_location_service">EVMap is running on Android Auto and using your location.</string>
<string name="auto_no_chargers_found">No nearby chargers found</string>
<string name="auto_no_favorites_found">No favorites found</string>
<string name="open_in_app">Open in app</string>
<string name="opened_on_phone">Opened on phone</string>
<string name="auto_location_permission_needed">To run EVMap on Android Auto, you need to grant access to your location.</string>
<string name="auto_vehicle_data_permission_needed">For this feature, EVMap needs access to your vehicle data.</string>
<string name="grant_on_phone">Grant on phone</string>
<string name="auto_chargers_closeby">Nearby chargers</string>
<string name="auto_favorites">Favorites</string>
<string name="auto_chargers_near_location">Near %s</string>
<string name="auto_fault_report_date">⚠️ Fault report (%s)</string>
<string name="auto_no_refresh_possible">Further updates not possible. Please go back and restart.</string>
<string name="auto_prices">Pricing</string>
<string name="auto_vehicle_data">Vehicle data</string>
<string name="auto_charging_level">Charging level</string>
<string name="auto_no_data">Unavailable</string>
<string name="auto_range">Range</string>
<string name="auto_speed">Speed</string>
<string name="auto_heading">Heading</string>
<string name="auto_settings">Settings</string>
<string name="welcome_android_auto">Android Auto support</string>
<string name="welcome_android_auto_detail">You can also use EVMap from within Android Auto on supported cars. Simply select the EVMap app in the Android Auto menu.</string>
<string name="sounds_cool">sounds cool</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap could not determine your vehicle model.</string>
<string name="auto_chargeprice_vehicle_unknown">None of the vehicles selected in the app matches this vehicle (%1$s %2$s).</string>
<string name="auto_chargeprice_vehicle_ambiguous">Multiple vehicles selected in the app match this vehicle (%1$s %2$s).</string>
<string name="auto_chargers_ahead">Only chargers along driving direction</string>
<string name="settings_android_auto_chargeprice_range">Charging range for price comparison</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="selecting_all">selected all items</string>
<string name="selecting_none">deselected all items</string>
<string name="loading">Loading…</string>
<string name="auto_multipage_goto">Page %d</string>
<string name="auto_multipage">(%d/%d)</string>
</resources>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CarAppTheme">
<item name="carColorPrimary">@color/colorPrimary</item>
<item name="carColorPrimaryDark">@color/colorPrimaryDark</item>
<item name="carColorSecondary">@color/colorSecondary</item>
<item name="carColorSecondaryDark">@color/colorSecondaryDark</item>
</style>
</resources>

View File

@@ -1,7 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:fragment="net.vonforst.evmap.fragment.preference.AndroidAutoSettingsFragment"
android:title="@string/settings_android_auto"
android:icon="@drawable/ic_android_auto" />
<PreferenceScreen>
</PreferenceScreen>

View File

@@ -6,6 +6,10 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="androidx.car.app.MAP_TEMPLATES" />
<uses-permission android:name="com.google.android.gms.permission.CAR_FUEL" />
<uses-permission android:name="com.google.android.gms.permission.CAR_SPEED" />
<queries>
<intent>
@@ -19,6 +23,9 @@
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
<package android:name="com.google.android.projection.gearhead" />
<package android:name="com.google.android.apps.automotive.templates.host" />
</queries>
<application
@@ -302,6 +309,38 @@
android:value="androidx.startup"
tools:node="remove" />
</provider>
<!-- Configuration for Android Auto app -->
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<meta-data
android:name="androidx.car.app.theme"
android:resource="@style/CarAppTheme" />
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1" />
<service
android:name=".auto.CarAppService"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:exported="true">
<intent-filter>
<action
android:name="androidx.car.app.CarAppService"
android:category="androidx.car.app.category.POI" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="net.vonforst.evmap" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</service>
</application>
</manifest>

View File

@@ -107,6 +107,10 @@ class MapsActivity : AppCompatActivity(),
navGraph.setStartDestination(R.id.onboarding)
navController.graph = navGraph
return
} else if (!prefs.privacyAccepted) {
navGraph.setStartDestination(R.id.onboarding)
navController.graph = navGraph
return
} else {
navGraph.setStartDestination(R.id.map)
navController.setGraph(navGraph, MapFragmentArgs(appStart = true).toBundle())

View File

@@ -12,7 +12,7 @@ import retrofit2.http.Path
import retrofit2.http.Query
private const val coordRange = 0.005 // range of latitude and longitude for loading the map
private const val maxDistance = 40 // max distance between reported positions in meters
private const val maxDistance = 60 // max distance between reported positions in meters
interface EnBwApi {
@GET("chargestations?grouping=false")
@@ -105,16 +105,18 @@ class EnBwAvailabilityDetector(client: OkHttpClient, baseUrl: String? = null) :
var markers =
api.getMarkers(lng - coordRange, lng + coordRange, lat - coordRange, lat + coordRange)
markers = markers.flatMap {
if (it.grouped) {
api.getMarkers(
it.viewPort.lowerLeftLon,
it.viewPort.upperRightLon,
it.viewPort.lowerLeftLat,
it.viewPort.upperRightLat
)
} else {
listOf(it)
while (markers.any { it.grouped }) {
markers = markers.flatMap {
if (it.grouped) {
api.getMarkers(
it.viewPort.lowerLeftLon,
it.viewPort.upperRightLon,
it.viewPort.lowerLeftLat,
it.viewPort.upperRightLat
)
} else {
listOf(it)
}
}
}
@@ -132,22 +134,26 @@ class EnBwAvailabilityDetector(client: OkHttpClient, baseUrl: String? = null) :
throw AvailabilityDetectorException("no candidates found")
}
// combine related stations
markers = markers.filter { marker ->
distanceBetween(
marker.lat,
marker.lon,
nearest.lat,
nearest.lon
) < maxDistance
if (nearest.numberOfChargePoints < location.totalChargepoints) {
// combine related stations
markers = markers.filter { marker ->
distanceBetween(
marker.lat,
marker.lon,
nearest.lat,
nearest.lon
) < maxDistance
}.filter {
// only include stations from same operator
it.operator == nearest.operator && it.stationId != null
}
} else {
markers = listOf(nearest)
}
val details = markers.filter {
// only include stations from same operator
it.operator == nearest.operator && it.stationId != null
}.map {
val details = markers.mapNotNull { it.stationId }.map {
// load details
api.getLocation(it.stationId!!)
api.getLocation(it)
}
val connectorStatus = details.flatMap { it.chargePoints }.flatMap { cp ->

View File

@@ -12,7 +12,7 @@ import retrofit2.http.Path
import java.util.*
private const val coordRange = 0.005 // range of latitude and longitude for loading the map
private const val maxDistance = 40 // max distance between reported positions in meters
private const val maxDistance = 60 // max distance between reported positions in meters
interface NewMotionApi {
@GET("markers/{lngMin}/{lngMax}/{latMin}/{latMax}/{zoom}")
@@ -28,7 +28,7 @@ interface NewMotionApi {
suspend fun getLocation(@Path("id") id: Long): NMLocation
@JsonClass(generateAdapter = true)
data class NMMarker(val coordinates: NMCoordinates, val locationUid: Long)
data class NMMarker(val coordinates: NMCoordinates, val locationUid: Long, val evseCount: Int)
@JsonClass(generateAdapter = true)
data class NMCoordinates(val latitude: Double, val longitude: Double)
@@ -111,14 +111,18 @@ class NewMotionAvailabilityDetector(client: OkHttpClient, baseUrl: String? = nul
throw AvailabilityDetectorException("no candidates found")
}
// combine related stations
markers = markers.filter { marker ->
distanceBetween(
marker.coordinates.latitude,
marker.coordinates.longitude,
nearest.coordinates.latitude,
nearest.coordinates.longitude
) < maxDistance
if (nearest.evseCount < location.totalChargepoints) {
// combine related stations
markers = markers.filter { marker ->
distanceBetween(
marker.coordinates.latitude,
marker.coordinates.longitude,
nearest.coordinates.latitude,
nearest.coordinates.longitude
) < maxDistance
}
} else {
markers = listOf(nearest)
}
// load details

View File

@@ -235,7 +235,7 @@ interface TeslaGraphQlApi {
data class GetNearbyChargingSitesResponse(val data: GetNearbyChargingSitesResponseData)
@JsonClass(generateAdapter = true)
data class GetNearbyChargingSitesResponseData(val charging: GetNearbyChargingSitesResponseDataCharging)
data class GetNearbyChargingSitesResponseData(val charging: GetNearbyChargingSitesResponseDataCharging?)
@JsonClass(generateAdapter = true)
data class GetNearbyChargingSitesResponseDataCharging(val nearbySites: GetNearbyChargingSitesResponseDataChargingNearbySites)
@@ -281,7 +281,7 @@ interface TeslaGraphQlApi {
val siteDynamic: SiteDynamic,
val siteStatic: SiteStatic,
val pricing: Pricing,
val congestionPriceHistogram: CongestionPriceHistogram,
val congestionPriceHistogram: CongestionPriceHistogram?,
)
@JsonClass(generateAdapter = true)
@@ -518,7 +518,8 @@ class TeslaAvailabilityDetector(
val results = api.getNearbyChargingSites(
req,
req.operationName
).data.charging.nearbySites.sitesAndDistances
).data.charging?.nearbySites?.sitesAndDistances
?: throw AvailabilityDetectorException("no candidates found.")
val result =
results.minByOrNull { it.haversineDistanceMiles.value }
?: throw AvailabilityDetectorException("no candidates found.")
@@ -540,6 +541,9 @@ class TeslaAvailabilityDetector(
Chargepoint.CCS_UNKNOWN
) && it.power != null && it.power <= 150
}
if (scV2CCSConnectors.sumOf { it.count } != 0 && scV2CCSConnectors.sumOf { it.count } != scV2Connectors.sumOf { it.count }) {
throw AvailabilityDetectorException("number of V2 connectors does not match number of V2 CCS connectors")
}
val scV3Connectors = location.chargepoints.filter {
it.type in listOf(
Chargepoint.CCS_TYPE_2,
@@ -550,36 +554,49 @@ class TeslaAvailabilityDetector(
"charger has unknown connectors"
)
val statusSorted = details.siteDynamic.chargerDetails.sortedBy { it.charger.labelLetter }
.sortedBy { it.charger.labelNumber }
var statusSorted = details.siteDynamic.chargerDetails.sortedBy { it.charger.labelLetter }
.sortedBy { it.charger.labelNumber }.map { it.availability }
if (statusSorted.size != scV2Connectors.sumOf { it.count } + scV3Connectors.sumOf { it.count }) {
// apparently some connectors are missing in Tesla data
// If we have just one type of charger, we can still match
val numMissing =
scV2Connectors.sumOf { it.count } + scV3Connectors.sumOf { it.count } - statusSorted.size
if (scV2Connectors.isEmpty() || scV3Connectors.isEmpty() && numMissing > 0) {
statusSorted =
statusSorted + List(numMissing) { TeslaGraphQlApi.ChargerAvailability.UNKNOWN }
} else {
throw AvailabilityDetectorException("Tesla API chargepoints do not match data source")
}
}
val statusMap = emptyMap<Chargepoint, List<ChargepointStatus>>().toMutableMap()
var i = 0
for (connector in scV2Connectors) {
statusMap[connector] =
statusSorted.subList(i, i + connector.count).map { it.availability.toStatus() }
statusSorted.subList(i, i + connector.count).map { it.toStatus() }
i += connector.count
}
if (scV2CCSConnectors.isNotEmpty()) {
i = 0
for (connector in scV2CCSConnectors) {
statusMap[connector] =
statusSorted.subList(i, i + connector.count).map { it.availability.toStatus() }
statusSorted.subList(i, i + connector.count).map { it.toStatus() }
i += connector.count
}
}
for (connector in scV3Connectors) {
statusMap[connector] =
statusSorted.subList(i, i + connector.count).map { it.availability.toStatus() }
statusSorted.subList(i, i + connector.count).map { it.toStatus() }
i += connector.count
}
val indexOfMidnight =
details.congestionPriceHistogram.dataAttributes.indexOfFirst { it.label == "12AM" }
val congestionHistogram = indexOfMidnight.takeIf { it >= 0 }?.let { index ->
val data = details.congestionPriceHistogram.data.toMutableList()
Collections.rotate(data, -index)
data
val congestionHistogram = details.congestionPriceHistogram?.let { cph ->
val indexOfMidnight = cph.dataAttributes.indexOfFirst { it.label == "12AM" }
indexOfMidnight.takeIf { it >= 0 }?.let { index ->
val data = cph.data.toMutableList()
Collections.rotate(data, -index)
data
}
}
return ChargeLocationStatus(

View File

@@ -153,6 +153,10 @@ class GoingElectricApiWrapper(
// no connectors chosen
return Resource.success(ChargepointList.empty())
}
if (connectorsVal != null && connectorsVal.values.contains("CCS")) {
// see note about Tesla Supercharger CCS filter in getFilters below
connectorsVal.values.add("Tesla Supercharger CCS")
}
val connectors = formatMultipleChoice(connectorsVal)
val chargeCardsVal = filters?.getMultipleChoiceValue("chargecards")
@@ -247,6 +251,10 @@ class GoingElectricApiWrapper(
// no connectors chosen
return Resource.success(ChargepointList.empty())
}
if (connectorsVal != null && connectorsVal.values.contains("CCS")) {
// see note about Tesla Supercharger CCS filter in getFilters below
connectorsVal.values.add("Tesla Supercharger CCS")
}
val connectors = formatMultipleChoice(connectorsVal)
val chargeCardsVal = filters?.getMultipleChoiceValue("chargecards")
@@ -324,6 +332,8 @@ class GoingElectricApiWrapper(
val freeparking = filters?.getBooleanValue("freeparking")
val open247 = filters?.getBooleanValue("open_247")
val barrierfree = filters?.getBooleanValue("barrierfree")
val networks = filters?.getMultipleChoiceValue("networks")
val chargecards = filters?.getMultipleChoiceValue("chargecards")
return chargers.filter { it ->
// apply filters which GoingElectric does not support natively
@@ -356,7 +366,13 @@ class GoingElectricApiWrapper(
?: GEOpeningHours(twentyfourSeven = true)
)
}
if (barrierfree == true) {
if (barrierfree == true
&& (networks == null || networks.all || it.network !in networks.values)
&& (chargecards == null || chargecards.all)
) {
/* barrierfree, networks and chargecards are combined with OR - so we can only
* be sure that the charger is barrierFree if the other filters are not active
* or the charger does not match the other filters */
inferred = inferred.copy(barrierFree = true)
}
inferred
@@ -426,9 +442,18 @@ class GoingElectricApiWrapper(
val networks = refData.networks
val chargeCards = refData.chargecards
val plugMap = plugs.associateWith { plug ->
nameForPlugType(sp, GEChargepoint.convertTypeFromGE(plug))
}
/*
"Tesla Supercharger CCS" is a bit peculiar - it is available as a filter, but the API
just returns "CCS" in the charging station details. So we cannot use it for filtering as
it won't work in the local database. So we join them into a single filter option.
If you want to find Tesla Superchargers with CCS, you can still do that using the network
filter.
*/
val plugMap = plugs
.filter { it != "Tesla Supercharger CCS" }
.associateWith { plug ->
nameForPlugType(sp, GEChargepoint.convertTypeFromGE(plug))
}
val networkMap = networks.associateWith { it }
val chargecardMap = chargeCards.associate { it.id.toString() to it.name }
val categoryMap = mapOf(
@@ -522,9 +547,6 @@ class GoingElectricApiWrapper(
if (filters.getBooleanValue("open_247") == true) {
result.append(" AND twentyfourSeven IS 1")
}
if (filters.getBooleanValue("barrierfree") == true) {
result.append(" AND barrierFree IS 1")
}
if (filters.getBooleanValue("exclude_faults") == true) {
result.append(" AND fault_report_description IS NULL AND fault_report_created IS NULL")
}
@@ -552,25 +574,34 @@ class GoingElectricApiWrapper(
requiresChargepointQuery = true
}
// networks, chargecards and barrierFree filters are combined with OR in the GE API
val networks = filters.getMultipleChoiceValue("networks")
if (networks != null && !networks.all) {
val networksList = if (networks.values.size == 0) {
""
} else {
networks.values.joinToString(",") { DatabaseUtils.sqlEscapeString(it) }
}
result.append(" AND network IN (${networksList})")
}
val chargecards = filters.getMultipleChoiceValue("chargecards")
if (chargecards != null && !chargecards.all) {
val chargecardsList = if (chargecards.values.size == 0) {
""
} else {
chargecards.values.joinToString(",")
val barrierFree = filters.getBooleanValue("barrierfree")
if ((networks != null && !networks.all) || barrierFree == true || (chargecards != null && !chargecards.all)) {
val queries = mutableListOf<String>()
if (networks != null && !networks.all) {
val networksList = if (networks.values.size == 0) {
""
} else {
networks.values.joinToString(",") { DatabaseUtils.sqlEscapeString(it) }
}
queries.add("network IN (${networksList})")
}
result.append(" AND json_extract(cc.value, '$.id') IN (${chargecardsList})")
requiresChargeCardQuery = true
if (barrierFree == true) {
queries.add("barrierFree IS 1")
}
if (chargecards != null && !chargecards.all) {
val chargecardsList = if (chargecards.values.size == 0) {
""
} else {
chargecards.values.joinToString(",")
}
queries.add("json_extract(cc.value, '$.id') IN (${chargecardsList})")
requiresChargeCardQuery = true
}
result.append(" AND (${queries.joinToString(" OR ")})")
}
val categories = filters.getMultipleChoiceValue("categories")
@@ -581,7 +612,7 @@ class GoingElectricApiWrapper(
val minConnectors = filters.getSliderValue("min_connectors")
if (minConnectors != null && minConnectors > 1) {
result.append(" GROUP BY ChargeLocation.id HAVING COUNT(1) >= ${minConnectors}")
result.append(" GROUP BY ChargeLocation.id HAVING SUM(json_extract(cp.value, '$.count')) >= ${minConnectors}")
requiresChargepointQuery = true
}

View File

@@ -5,7 +5,22 @@ import androidx.room.PrimaryKey
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import kotlinx.parcelize.Parcelize
import net.vonforst.evmap.model.*
import net.vonforst.evmap.model.Address
import net.vonforst.evmap.model.ChargeCard
import net.vonforst.evmap.model.ChargeCardId
import net.vonforst.evmap.model.ChargeLocation
import net.vonforst.evmap.model.ChargeLocationCluster
import net.vonforst.evmap.model.Chargepoint
import net.vonforst.evmap.model.ChargepointListItem
import net.vonforst.evmap.model.ChargepriceData
import net.vonforst.evmap.model.ChargerPhoto
import net.vonforst.evmap.model.Coordinate
import net.vonforst.evmap.model.Cost
import net.vonforst.evmap.model.FaultReport
import net.vonforst.evmap.model.Hours
import net.vonforst.evmap.model.OpeningHours
import net.vonforst.evmap.model.OpeningHoursDays
import net.vonforst.evmap.model.ReferenceData
import java.time.Instant
import java.time.LocalTime
@@ -35,7 +50,7 @@ sealed class GEChargepointListItem {
@JsonClass(generateAdapter = true)
data class GEChargeLocation(
@Json(name = "ge_id") val id: Long,
val name: String,
val name: String?,
val coordinates: GECoordinate,
val address: GEAddress,
val chargepoints: List<GEChargepoint>,
@@ -57,7 +72,7 @@ data class GEChargeLocation(
override fun convert(apikey: String, isDetailed: Boolean) = ChargeLocation(
id,
"goingelectric",
name,
name ?: "Charging station",
coordinates.convert(),
address.convert(),
chargepoints.map { it.convert() },

View File

@@ -375,7 +375,7 @@ class OpenChargeMapApiWrapper(
val minConnectors = filters.getSliderValue("min_connectors")
if (minConnectors != null && minConnectors > 1) {
result.append(" GROUP BY ChargeLocation.id HAVING COUNT(1) >= ${minConnectors}")
result.append(" GROUP BY ChargeLocation.id HAVING SUM(json_extract(cp.value, '$.count')) >= ${minConnectors}")
requiresChargepointQuery = true
}

View File

@@ -6,7 +6,15 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import kotlinx.parcelize.Parcelize
import net.vonforst.evmap.max
import net.vonforst.evmap.model.*
import net.vonforst.evmap.model.Address
import net.vonforst.evmap.model.ChargeLocation
import net.vonforst.evmap.model.Chargepoint
import net.vonforst.evmap.model.ChargepriceData
import net.vonforst.evmap.model.ChargerPhoto
import net.vonforst.evmap.model.Coordinate
import net.vonforst.evmap.model.Cost
import net.vonforst.evmap.model.FaultReport
import net.vonforst.evmap.model.ReferenceData
import java.time.Instant
import java.time.ZonedDateTime
@@ -165,7 +173,8 @@ data class OCMConnection(
17L -> Chargepoint.CEE_ROT
28L -> Chargepoint.SCHUKO
8L -> Chargepoint.TESLA_ROADSTER_HPC
27L -> Chargepoint.SUPERCHARGER
27L -> Chargepoint.SUPERCHARGER // Tesla North American plug (NACS)
30L -> Chargepoint.SUPERCHARGER // European Tesla Model S/X Supercharger plug (DC on Type 2)
25L -> Chargepoint.TYPE_2_SOCKET
1036L -> Chargepoint.TYPE_2_PLUG
1L -> Chargepoint.TYPE_1

View File

@@ -5,6 +5,11 @@ package net.vonforst.evmap.auto
* and human-readable vehicle models as listed by Chargeprice in their vehicle database.
*/
private val brands = mapOf(
"Saic" to "MG", // Seen on MG 4
"Google" to "Hyundai" // useful for debugging on the DHU. Delete in case there's ever a Google car ;)
)
private val models = mapOf(
"Audi" to mapOf(
"516 (G4x)" to "e-tron"
@@ -19,4 +24,11 @@ fun getVehicleModel(manufacturer: String?, model: String?) =
models[manufacturer]?.get(model) ?: model
} else {
null
}
fun getVehicleBrand(manufacturer: String?) =
if (manufacturer != null) {
brands[manufacturer] ?: manufacturer
} else {
null
}

View File

@@ -207,7 +207,7 @@ class ChargepriceScreen(ctx: CarContext, val charger: ChargeLocation) : Screen(c
private fun loadPrices(model: Model?) {
val dataAdapter = ChargepriceApi.getDataAdapter(charger)
val manufacturer = model?.manufacturer?.value
val manufacturer = getVehicleBrand(model?.manufacturer?.value)
val modelName = getVehicleModel(model?.manufacturer?.value, model?.name?.value)
lifecycleScope.launch {
try {
@@ -345,7 +345,7 @@ class ChargepriceScreen(ctx: CarContext, val charger: ChargeLocation) : Screen(c
} else if (vehicles.size > 1) {
if (manufacturer != null) {
vehicles = vehicles.filter {
it.brand == manufacturer
it.brand.lowercase() == getVehicleBrand(manufacturer)?.lowercase()
}
if (vehicles.isEmpty()) {
throw VehicleUnknownException()

View File

@@ -4,6 +4,7 @@ import android.content.pm.PackageManager
import android.location.Location
import android.os.Handler
import android.os.Looper
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.Spanned
import androidx.car.app.CarContext
@@ -35,6 +36,7 @@ import net.vonforst.evmap.model.FilterWithValue
import net.vonforst.evmap.storage.AppDatabase
import net.vonforst.evmap.storage.ChargeLocationsRepository
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.ui.ChargerIconGenerator
import net.vonforst.evmap.ui.availabilityText
import net.vonforst.evmap.ui.getMarkerTint
import net.vonforst.evmap.utils.bearingBetween
@@ -73,6 +75,7 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
private var lastDistanceUpdateTime: Instant? = null
private var lastChargersUpdateTime: Instant? = null
private var chargers: List<ChargeLocation>? = null
private var isFavorite: List<Boolean>? = null
private var loadingError = false
private var locationError = false
private var prefs = PreferenceDataSource(ctx)
@@ -114,6 +117,9 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
private var searchLocation: LatLng? = null
private val iconGen =
ChargerIconGenerator(carContext, null, height = 96)
init {
lifecycle.addObserver(this)
marker = MARKER
@@ -154,8 +160,8 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
val builder = ItemList.Builder()
// only show the city if not all chargers are in the same city
val showCity = chargerList.map { it.address?.city }.distinct().size > 1
chargerList.forEach { charger ->
builder.addItem(formatCharger(charger, showCity))
chargerList.forEachIndexed { i, charger ->
builder.addItem(formatCharger(charger, showCity, isFavorite?.get(i) ?: false))
}
builder.setNoItemsMessage(
carContext.getString(
@@ -231,10 +237,12 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
if (!supportsRefresh) {
screenManager.pushForResult(DummyReturnScreen(carContext)) {
chargers = null
isFavorite = null
loadChargers()
}
} else {
chargers = null
isFavorite = null
loadChargers()
}
} else {
@@ -245,6 +253,7 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
)
) {
chargers = null
isFavorite = null
loadChargers()
}
session.mapScreen = null
@@ -277,13 +286,18 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
}.build()
}
private fun formatCharger(charger: ChargeLocation, showCity: Boolean): Row {
val markerTint = if ((charger.maxPower ?: 0.0) > 100) {
private fun formatCharger(
charger: ChargeLocation,
showCity: Boolean,
isFavorite: Boolean
): Row {
val markerTint = getMarkerTint(charger)
val backgroundTint = if ((charger.maxPower ?: 0.0) > 100) {
R.color.charger_100kw_dark // slightly darker color for better contrast
} else {
getMarkerTint(charger)
markerTint
}
val color = ContextCompat.getColor(carContext, markerTint)
val color = ContextCompat.getColor(carContext, backgroundTint)
val place =
Place.Builder(CarLocation.create(charger.coordinates.lat, charger.coordinates.lng))
.setMarker(
@@ -293,16 +307,32 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
)
.build()
val icon = iconGen.getBitmap(
markerTint,
fault = charger.faultReport != null,
multi = charger.isMulti(),
fav = isFavorite
)
val iconSpan =
CarIconSpan.create(CarIcon.Builder(IconCompat.createWithBitmap(icon)).build())
return Row.Builder().apply {
// only show the city if not all chargers are in the same city (-> showCity == true)
// and the city is not already contained in the charger name
val title = SpannableStringBuilder().apply {
append(" ", iconSpan, SpannableString.SPAN_INCLUSIVE_EXCLUSIVE)
append(" ")
append(charger.name)
}
if (showCity && charger.address?.city != null && charger.address.city !in charger.name) {
setTitle(
CarText.Builder("${charger.name} · ${charger.address.city}")
.addVariant(charger.name)
.build())
val titleWithCity = SpannableStringBuilder().apply {
append("", iconSpan, SpannableString.SPAN_INCLUSIVE_EXCLUSIVE)
append(" ")
append("${charger.name} · ${charger.address.city}")
}
setTitle(CarText.Builder(titleWithCity).addVariant(title).build())
} else {
setTitle(charger.name)
setTitle(title)
}
val text = SpannableStringBuilder()
@@ -416,15 +446,19 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
val filters = repo.getFiltersAsync(carContext.stringProvider())
filtersWithValue = filtersWithValue(filters, filterValues)
val apiId = repo.api.value!!.id
// load chargers
if (filterStatus == FILTERS_FAVORITES) {
chargers =
val chargers =
db.favoritesDao().getAllFavoritesAsync().map { it.charger }.sortedBy {
distanceBetween(
location.latitude, location.longitude,
it.coordinates.lat, it.coordinates.lng
)
}
this@MapScreen.chargers = chargers
isFavorite = List(chargers.size) { true }
} else {
// try multiple search radii until we have enough chargers
var chargers: List<ChargeLocation>? = null
@@ -453,7 +487,11 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
break
}
}
val isFavorite = chargers?.map {
db.favoritesDao().findFavorite(it.id, apiId) != null
}
this@MapScreen.chargers = chargers
this@MapScreen.isFavorite = isFavorite
}
updateCoroutine = null

View File

@@ -646,7 +646,7 @@ class AboutScreen(ctx: CarContext) : Screen(ctx) {
.setOnClickListener(ParkedOnlyOnClickListener.create {
if (BuildConfig.FLAVOR_automotive == "automotive") {
// we can't open the donation page on the phone in this case
openUrl(carContext, carContext.getString(R.string.paypal_link))
openUrl(carContext, carContext.getString(R.string.donate_link))
} else {
val intent = Intent(carContext, MapsActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

View File

@@ -84,7 +84,7 @@ class VehicleDataScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx
return GridTemplate.Builder().apply {
setTitle(
if (model != null && model.manufacturer.value != null && model.name.value != null) {
"${model.manufacturer.value} ${
"${getVehicleBrand(model.manufacturer.value)} ${
getVehicleModel(
model.manufacturer.value,
model.name.value

View File

@@ -6,7 +6,6 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.graphics.Color
import android.location.Geocoder
import android.os.Bundle
import android.text.method.KeyListener
import android.view.*
@@ -194,6 +193,9 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
searchResultIcon = null
}
binding.detailAppBar.toolbar.popupTheme =
com.google.android.material.R.style.ThemeOverlay_AppCompat_DayNight
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _, insets ->
@@ -251,8 +253,11 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
binding.detailAppBar.toolbar.inflateMenu(R.menu.detail)
favToggle = binding.detailAppBar.toolbar.menu.findItem(R.id.menu_fav)
binding.detailAppBar.toolbar.menu.findItem(R.id.menu_edit).title =
getString(R.string.edit_at_datasource, vm.apiName)
vm.apiName.observe(viewLifecycleOwner) {
binding.detailAppBar.toolbar.menu.findItem(R.id.menu_edit).title =
getString(R.string.edit_at_datasource, it)
}
binding.detailView.topPart.doOnNextLayout {
bottomSheetBehavior.peekHeight = binding.detailView.topPart.bottom
@@ -433,6 +438,12 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
}
true
}
R.id.menu_reload -> {
vm.reloadChargerDetails()
true
}
else -> false
}
}
@@ -867,7 +878,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
if (BuildConfig.FLAVOR.contains("google") && mapFragment!!.priority[0] == MapFragment.GOOGLE) {
// Google Maps: icons can be generated in background thread
lifecycleScope.launch {
withContext(Dispatchers.IO) {
withContext(Dispatchers.Default) {
chargerIconGenerator.preloadCache()
}
}
@@ -1335,8 +1346,13 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return false
override fun onMenuItemSelected(menuItem: MenuItem) = when (menuItem.itemId) {
R.id.menu_reload -> {
vm.reloadChargepoints(true)
true
}
else -> false
}
override fun getRootView(): View {

View File

@@ -6,13 +6,17 @@ import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.AnimatedVectorDrawable
import android.os.Bundle
import android.text.Html
import android.text.method.LinkMovementMethod
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.DecelerateInterpolator
import android.widget.ImageView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import net.vonforst.evmap.R
import net.vonforst.evmap.databinding.*
@@ -22,12 +26,14 @@ import net.vonforst.evmap.storage.PreferenceDataSource
class OnboardingFragment : Fragment() {
private lateinit var binding: FragmentOnboardingBinding
private lateinit var adapter: OnboardingViewPagerAdapter
private lateinit var prefs: PreferenceDataSource
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
prefs = PreferenceDataSource(requireContext())
binding = FragmentOnboardingBinding.inflate(inflater)
adapter = OnboardingViewPagerAdapter(this)
@@ -67,6 +73,13 @@ class OnboardingFragment : Fragment() {
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (prefs.welcomeDialogShown) {
// skip to last page for selecting data source or accepting the privacy policy
binding.viewPager.currentItem = adapter.itemCount - 1
}
}
fun goToNext() {
if (binding.viewPager.currentItem == adapter.itemCount - 1) {
findNavController().navigate(R.id.action_onboarding_to_map)
@@ -76,6 +89,19 @@ class OnboardingFragment : Fragment() {
}
}
class OnboardingViewPagerAdapter(fragment: Fragment) :
FragmentStateAdapter(fragment) {
override fun getItemCount(): Int = 4
override fun createFragment(position: Int): Fragment = when (position) {
0 -> WelcomeFragment()
1 -> IconsFragment()
2 -> AndroidAutoFragment()
3 -> DataSourceSelectFragment()
else -> throw IllegalArgumentException()
}
}
abstract class OnboardingPageFragment : Fragment() {
lateinit var parent: OnboardingFragment
@@ -182,7 +208,9 @@ class DataSourceSelectFragment : OnboardingPageFragment() {
binding.rgDataSource.rbGoingElectric,
binding.rgDataSource.textView27,
binding.rgDataSource.rbOpenChargeMap,
binding.rgDataSource.textView28
binding.rgDataSource.textView28,
binding.dataSourceHint,
binding.cbAcceptPrivacy
)
override fun onCreateView(
@@ -196,6 +224,10 @@ class DataSourceSelectFragment : OnboardingPageFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.cbAcceptPrivacy.text =
Html.fromHtml(getString(R.string.accept_privacy, getString(R.string.privacy_link)))
binding.cbAcceptPrivacy.linksClickable = true
binding.cbAcceptPrivacy.movementMethod = LinkMovementMethod.getInstance()
binding.btnGetStarted.visibility = View.INVISIBLE
for (rb in listOf(
@@ -211,8 +243,24 @@ class DataSourceSelectFragment : OnboardingPageFragment() {
}
}
}
if (prefs.dataSourceSet) {
when (prefs.dataSource) {
"goingelectric" -> binding.rgDataSource.rbGoingElectric.isChecked = true
"openchargemap" -> binding.rgDataSource.rbOpenChargeMap.isChecked = true
}
}
binding.btnGetStarted.setOnClickListener {
if (!binding.cbAcceptPrivacy.isChecked) {
binding.cbAcceptPrivacy.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.delete_red
)
)
return@setOnClickListener
}
val result = if (binding.rgDataSource.rbGoingElectric.isChecked) {
"goingelectric"
} else if (binding.rgDataSource.rbOpenChargeMap.isChecked) {
@@ -221,6 +269,7 @@ class DataSourceSelectFragment : OnboardingPageFragment() {
return@setOnClickListener
}
prefs.dataSource = result
prefs.privacyAccepted = true
prefs.filterStatus = FILTERS_DISABLED
prefs.dataSourceSet = true
prefs.welcomeDialogShown = true
@@ -254,4 +303,47 @@ class DataSourceSelectFragment : OnboardingPageFragment() {
super.onPause()
animatedItems.forEach { it.alpha = 0f }
}
}
class AndroidAutoFragment : OnboardingPageFragment() {
private lateinit var binding: FragmentOnboardingAndroidAutoBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentOnboardingAndroidAutoBinding.inflate(inflater, container, false)
binding.btnGetStarted.setOnClickListener {
parent.goToNext()
}
binding.imgAndroidAuto.alpha = 0f
return binding.root
}
@SuppressLint("Recycle")
override fun onResume() {
super.onResume()
val animators =
listOf(
ObjectAnimator.ofFloat(binding.imgAndroidAuto, "translationY", -20f, 0f).apply {
interpolator = DecelerateInterpolator()
},
ObjectAnimator.ofFloat(binding.imgAndroidAuto, "alpha", 0f, 1f).apply {
interpolator = DecelerateInterpolator()
}
)
AnimatorSet().apply {
playTogether(animators)
start()
}
}
override fun onPause() {
super.onPause()
binding.imgAndroidAuto.alpha = 0f
}
}

View File

@@ -142,7 +142,8 @@ class ChargeLocationsRepository(
fun getChargepoints(
bounds: LatLngBounds,
zoom: Float,
filters: FilterValues?
filters: FilterValues?,
overrideCache: Boolean = false
): LiveData<Resource<List<ChargepointListItem>>> {
val api = api.value!!
@@ -200,7 +201,11 @@ class ChargeLocationsRepository(
}
}
}
return CacheLiveData(dbResult, apiResult, savedRegionResult).distinctUntilChanged()
return if (overrideCache) {
apiResult
} else {
CacheLiveData(dbResult, apiResult, savedRegionResult).distinctUntilChanged()
}
}
fun getChargepointsRadius(
@@ -290,10 +295,14 @@ class ChargeLocationsRepository(
chargers: List<ChargeLocation>,
zoom: Float
): List<ChargepointListItem> {
/* in very crowded places (good example: central London on OpenChargeMap without filters)
we have to cluster even at pretty high zoom levels to make sure the map does not get
laggy. Otherwise, only cluster at zoom levels <= 11. */
val useClustering = chargers.size > 500 || zoom <= 11f
val clusterDistance = getClusterDistance(zoom)
val chargersClustered = if (clusterDistance != null) {
Dispatchers.IO.run {
val chargersClustered = if (useClustering && clusterDistance != null) {
Dispatchers.Default.run {
cluster(chargers, zoom, clusterDistance)
}
} else chargers
@@ -301,7 +310,8 @@ class ChargeLocationsRepository(
}
fun getChargepointDetail(
id: Long
id: Long,
overrideCache: Boolean = false
): LiveData<Resource<ChargeLocation>> {
val dbResult = chargeLocationsDao.getChargeLocationById(
id,
@@ -317,7 +327,11 @@ class ChargeLocationsRepository(
chargeLocationsDao.insert(result.data!!)
}
}
return PreferCacheLiveData(dbResult, apiResult, cacheSoftLimit)
return if (overrideCache) {
apiResult
} else {
PreferCacheLiveData(dbResult, apiResult, cacheSoftLimit)
}
}
fun getFilters(sp: StringProvider) = MediatorLiveData<List<Filter<FilterValue>>>().apply {

View File

@@ -11,7 +11,6 @@ import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import co.anbora.labs.spatia.builder.SpatiaRoom
import co.anbora.labs.spatia.geometry.GeometryConverters
import kotlinx.coroutines.runBlocking
import net.vonforst.evmap.api.goingelectric.GEChargeCard
import net.vonforst.evmap.api.goingelectric.GEChargepoint
import net.vonforst.evmap.api.openchargemap.OCMConnectionType
@@ -35,7 +34,7 @@ import net.vonforst.evmap.model.*
OCMCountry::class,
OCMOperator::class,
SavedRegion::class
], version = 21
], version = 22
)
@TypeConverters(Converters::class, GeometryConverters::class)
abstract class AppDatabase : RoomDatabase() {
@@ -75,7 +74,8 @@ abstract class AppDatabase : RoomDatabase() {
MIGRATION_2, MIGRATION_3, MIGRATION_4, MIGRATION_5, MIGRATION_6,
MIGRATION_7, MIGRATION_8, MIGRATION_9, MIGRATION_10, MIGRATION_11,
MIGRATION_12, MIGRATION_13, MIGRATION_14, MIGRATION_15, MIGRATION_16,
MIGRATION_17, MIGRATION_18, MIGRATION_19, MIGRATION_20, MIGRATION_21
MIGRATION_17, MIGRATION_18, MIGRATION_19, MIGRATION_20, MIGRATION_21,
MIGRATION_22
)
.addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
@@ -452,6 +452,13 @@ abstract class AppDatabase : RoomDatabase() {
db.execSQL("DELETE FROM savedregion")
}
}
private val MIGRATION_22 = object : Migration(21, 22) {
override fun migrate(db: SupportSQLiteDatabase) {
// clear cache with this update
db.execSQL("DELETE FROM savedregion")
}
}
}
/**

View File

@@ -266,6 +266,12 @@ class PreferenceDataSource(val context: Context) {
set(value) {
sp.edit().putFloat("current_map_zoom", value).apply()
}
var privacyAccepted: Boolean
get() = sp.getBoolean("privacy_accepted", false)
set(value) {
sp.edit().putBoolean("privacy_accepted", value).apply()
}
}
fun SharedPreferences.getLatLng(key: String): LatLng? =

View File

@@ -151,8 +151,9 @@ class BarGraphView(context: Context, attrs: AttributeSet) : View(context, attrs)
legendPaint.textAlign = Paint.Align.CENTER
data.entries.forEachIndexed { i, (t, v) ->
val divisor = maxValue.toFloat().takeIf { it > 0f } ?: 1f
val height =
zeroHeight + (graphBounds.height() - zeroHeight) * v.toFloat() / maxValue.toFloat()
zeroHeight + (graphBounds.height() - zeroHeight) * v.toFloat() / divisor
val left = graphBounds.left + (barWidth + barMargin) * i
if (left + barWidth > graphBounds.right) return@forEachIndexed

View File

@@ -53,6 +53,7 @@ internal fun getClusterDistance(zoom: Float): Int? {
return when (zoom) {
in 0.0..7.0 -> 100
in 7.0..11.0 -> 75
in 11.0..15.0 -> 75
else -> null
}
}
@@ -162,9 +163,13 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
val chargerSparse: MutableLiveData<ChargeLocation?> by lazy {
state.getLiveData("chargerSparse")
}
private val triggerChargerDetailsRefresh = MutableLiveData(false)
val chargerDetails: LiveData<Resource<ChargeLocation>> = chargerSparse.switchMap { charger ->
charger?.id?.let {
repo.getChargepointDetail(it)
triggerChargerDetailsRefresh.value = false
triggerChargerDetailsRefresh.switchMap { overrideCache ->
charger?.id?.let {
repo.getChargepointDetail(it, overrideCache)
}
}
}.apply {
observeForever { chargerDetail ->
@@ -206,7 +211,7 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
val location: MutableLiveData<LatLng> by lazy {
MutableLiveData<LatLng>()
}
private val triggerAvailabilityRefresh = MutableLiveData<Boolean>(true)
private val triggerAvailabilityRefresh = MutableLiveData(true)
val availability: LiveData<Resource<ChargeLocationStatus>> by lazy {
chargerSparse.switchMap { charger ->
charger?.let {
@@ -479,10 +484,10 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
}
}
fun reloadChargepoints() {
fun reloadChargepoints(overrideCache: Boolean = false) {
val pos = mapPosition.value ?: return
val filters = filtersWithValue.value ?: return
chargepointLoader(pos to filters)
chargepointLoader(Triple(pos, filters, overrideCache))
}
private val miniMarkerThreshold = 13f
@@ -514,11 +519,10 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
throttleLatest(
500L,
viewModelScope
) { data: Pair<MapPosition, FilterValues> ->
) { data: Triple<MapPosition, FilterValues, Boolean> ->
chargepoints.value = Resource.loading(chargepoints.value?.data)
val mapPosition = data.first
val filters = data.second
val (mapPosition, filters, overrideCache) = data
val bounds = extendBounds(mapPosition.bounds)
if (filterStatus.value == FILTERS_FAVORITES) {
@@ -541,7 +545,7 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
return@throttleLatest
}
val result = repo.getChargepoints(bounds, mapPosition.zoom, filters)
val result = repo.getChargepoints(bounds, mapPosition.zoom, filters, overrideCache)
chargepointsInternal?.let { chargepoints.removeSource(it) }
chargepointsInternal = result
chargepoints.addSource(result) {
@@ -602,6 +606,10 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
triggerAvailabilityRefresh.value = true
}
fun reloadChargerDetails() {
triggerChargerDetailsRefresh.value = true
}
fun loadChargerById(chargerId: Long) {
chargerSparse.value = null
repo.getChargepointDetail(chargerId).observeForever { response ->

View File

@@ -2,7 +2,8 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools">
<ScrollView
android:id="@+id/scroll"
@@ -43,11 +44,39 @@
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="28dp"
android:layout_marginBottom="8dp"
android:breakStrategy="balanced"
android:text="@string/data_sources_description"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
android:textColor="?android:textColorSecondary"
app:layout_constraintBottom_toTopOf="@+id/dataSourceHint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/scroll" />
<TextView
android:id="@+id/dataSourceHint"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="16dp"
android:breakStrategy="balanced"
android:text="@string/data_sources_hint"
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
android:textColor="?android:textColorSecondary"
app:layout_constraintBottom_toTopOf="@+id/cb_accept_privacy"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/scroll" />
<CheckBox
android:id="@+id/cb_accept_privacy"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="16dp"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
tools:text="@string/accept_privacy"
app:layout_constraintBottom_toTopOf="@+id/btnGetStarted"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/scroll" />
@@ -58,7 +87,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginBottom="24dp"
android:layout_marginBottom="16dp"
android:text="@string/lets_go"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/scroll" />

View File

@@ -3,6 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
@@ -16,8 +17,8 @@
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="56dp"
app:layout_constraintBottom_toTopOf="@+id/btnGetStarted"
android:layout_marginBottom="32dp"
app:layout_constraintBottom_toTopOf="@+id/dataSourceHint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
@@ -54,6 +55,37 @@
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/dataSourceHint"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="32dp"
android:gravity="center"
android:text="@string/data_sources_hint"
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
android:textColor="?android:textColorSecondary"
android:breakStrategy="balanced"
app:layout_constraintBottom_toTopOf="@+id/cb_accept_privacy"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
<CheckBox
android:id="@+id/cb_accept_privacy"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="32dp"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
tools:text="@string/accept_privacy"
app:layout_constraintBottom_toTopOf="@+id/btnGetStarted"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/btnGetStarted"
style="@style/Widget.Material3.Button"

View File

@@ -27,7 +27,8 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/dialogTitle"
app:layout_constraintBottom_toTopOf="@id/btnCancel">
app:layout_constraintBottom_toTopOf="@id/btnCancel"
android:requiresFadingEdge="vertical">
<LinearLayout
android:layout_width="match_parent"

View File

@@ -3,7 +3,9 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
xmlns:tools="http://schemas.android.com/tools"
android:fillViewport="true"
android:requiresFadingEdge="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@@ -16,7 +18,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="32dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toTopOf="@+id/dataSourceHint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
@@ -27,6 +29,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="16dp"
android:gravity="center"
@@ -35,7 +38,9 @@
app:layout_constraintBottom_toTopOf="@+id/welcomeText2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
<TextView
android:id="@+id/welcomeText2"
@@ -71,16 +76,31 @@
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="32dp"
android:layout_marginBottom="24dp"
android:breakStrategy="balanced"
android:gravity="center"
android:text="@string/data_sources_hint"
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
android:textColor="?android:textColorSecondary"
android:breakStrategy="balanced"
app:layout_constraintBottom_toTopOf="@+id/btnGetStarted"
app:layout_constraintBottom_toTopOf="@+id/cb_accept_privacy"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
<CheckBox
android:id="@+id/cb_accept_privacy"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="24dp"
android:paddingStart="16dp"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
app:layout_constraintBottom_toTopOf="@+id/btnGetStarted"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
tools:text="@string/accept_privacy" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@@ -2,12 +2,6 @@
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_edit"
android:icon="@drawable/ic_edit"
android:title="@string/edit_at_datasource"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_share"
android:icon="@drawable/ic_share"
@@ -19,4 +13,13 @@
android:icon="@drawable/ic_fav_no"
android:title="@string/fav_add"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_edit"
android:icon="@drawable/ic_edit"
android:title="@string/edit_at_datasource" />
<item
android:id="@+id/menu_reload"
android:title="@string/reload" />
</menu>

View File

@@ -8,4 +8,8 @@
android:icon="@drawable/ic_filter"
app:showAsAction="ifRoom"
app:actionLayout="@layout/action_filter" />
<item
android:id="@+id/menu_reload"
android:title="@string/reload" />
</menu>

View File

@@ -113,7 +113,7 @@
<string name="goingelectric_forum">Forenthread bei GoingElectric.de</string>
<string name="contact">Kontakt</string>
<string name="menu_report_new_charger">Ladesäule melden</string>
<string name="edit_at_datasource">bei %s bearbeiten</string>
<string name="edit_at_datasource">Bei %s bearbeiten</string>
<string name="categories">Kategorien</string>
<string name="category_car_dealership">Autohaus</string>
<string name="category_service_on_motorway">Autobahnraststätte</string>
@@ -325,4 +325,40 @@
<string name="settings_cache_clear">Cache leeren</string>
<string name="settings_cache_clear_summary">Löscht alle gespeicherten Ladestationen außer Favoriten</string>
<string name="settings_cache_count_summary">%d Ladestationen gespeichert, %.1f MB</string>
<string name="auto_location_service">EVMap läuft unter Android Auto und nutzt dafür deinen Standort.</string>
<string name="auto_no_chargers_found">Keine Ladestationen in der Nähe gefunden</string>
<string name="auto_no_favorites_found">Keine Favoriten gefunden</string>
<string name="open_in_app">In App öffnen</string>
<string name="opened_on_phone">Auf dem Telefon geöffnet</string>
<string name="auto_location_permission_needed">Um EVMap auf Android Auto zu nutzen, braucht die App Zugriff auf deinen Standort.</string>
<string name="auto_vehicle_data_permission_needed">Für diese Funktion benötigt EVMap Zugriff auf Daten deines Fahrzeugs.</string>
<string name="grant_on_phone">Auf Telefon zulassen</string>
<string name="auto_chargers_closeby">In der Nähe</string>
<string name="auto_favorites">Favoriten</string>
<string name="auto_chargers_near_location">Nahe %s</string>
<string name="auto_fault_report_date">⚠️ Störungsmeldung (%s)</string>
<string name="auto_no_refresh_possible">Weitere Aktualisierung nicht möglich. Bitte zurück gehen und neu starten.</string>
<string name="auto_prices">Preise</string>
<string name="auto_vehicle_data">Fahrzeugdaten</string>
<string name="auto_charging_level">Ladezustand</string>
<string name="auto_no_data">Nicht verfügbar</string>
<string name="auto_range">Reichweite</string>
<string name="auto_speed">Geschwindigkeit</string>
<string name="auto_heading">Fahrtrichtung</string>
<string name="auto_settings">Einstellungen</string>
<string name="welcome_android_auto">Android Auto-Unterstützung</string>
<string name="welcome_android_auto_detail">Auf unterstützen Autos kannst du EVMap auch mit Android Auto nutzen. Öffne dazu einfach die EVMap-App aus dem Menü von Android Auto.</string>
<string name="sounds_cool">Klingt cool</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap konnte das Fahrzeugmodell nicht erkennen.</string>
<string name="auto_chargeprice_vehicle_unknown">Keins der in der App ausgewählten Fahrzeuge passt zu diesem Fahrzeug (%1$s %2$s).</string>
<string name="auto_chargeprice_vehicle_ambiguous">Mehrere der in der App ausgewählten Fahrzeuge passen zu diesem Fahrzeug (%1$s %2$s).</string>
<string name="auto_chargers_ahead">Nur Ladestationen in Fahrtrichtung</string>
<string name="settings_android_auto_chargeprice_range">Ladebereich für Preisvergleich</string>
<string name="selecting_all">alle Einträge ausgewählt</string>
<string name="selecting_none">alle Einträge abgewählt</string>
<string name="loading">Lade…</string>
<string name="auto_multipage_goto">Seite %d</string>
<string name="auto_multipage">(%d/%d)</string>
<string name="reload">Neu laden</string>
<string name="accept_privacy"><![CDATA[Ich habe die <a href=\"%s\">Datenschutzerklärung</a> von EVMap gelesen und bin damit einverstanden.]]></string>
</resources>

View File

@@ -55,7 +55,7 @@
<string name="filter_exclude_faults">Exclure les chargeurs avec des défauts signalés</string>
<string name="charge_cards">Méthodes de paiement</string>
<string name="goingelectric_forum">Fil de discussion du forum sur GoingElectric.de</string>
<string name="edit_at_datasource">modifier à %s</string>
<string name="edit_at_datasource">Modifier à %s</string>
<string name="categories">Catégories</string>
<string name="category_car_dealership">Concessionnaire automobile</string>
<string name="category_public_authorities">Pouvoirs publics</string>
@@ -298,4 +298,37 @@
</plurals>
<string name="developer_options">Paramètres développeur</string>
<string name="prediction_time_colon">%s :</string>
<string name="auto_location_service">EVMap fonctionne sur Android Auto et utilise votre position.</string>
<string name="open_in_app">Ouvrir dans l\'application</string>
<string name="opened_on_phone">Ouvert sur le téléphone</string>
<string name="auto_location_permission_needed">Pour exécuter EVMap sur Android Auto, vous devez autoriser l\'accès à votre emplacement.</string>
<string name="grant_on_phone">Grant au téléphone</string>
<string name="auto_prices">Prix</string>
<string name="auto_vehicle_data">Données sur le véhicule</string>
<string name="auto_range">Autonomie</string>
<string name="auto_speed">Vitesse</string>
<string name="welcome_android_auto">Prise en charge dAndroid Auto</string>
<string name="sounds_cool">Ça a l\'air cool</string>
<string name="auto_chargeprice_vehicle_unknown">Aucun des véhicules sélectionnés dans l\'application ne correspond à ce véhicule (%1$s %2$s).</string>
<string name="auto_chargeprice_vehicle_ambiguous">Plusieurs véhicules sélectionnés dans l\'application correspondent à ce véhicule (%1$s %2$s).</string>
<string name="selecting_all">tous les éléments sélectionnés</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap n\'a pas pu déterminer le modèle de votre véhicule.</string>
<string name="auto_no_chargers_found">Aucun chargeur à proximité n\'a été trouvé</string>
<string name="auto_no_favorites_found">Pas de favoris trouvés</string>
<string name="auto_charging_level">Niveau de charge</string>
<string name="auto_chargers_closeby">Chargeurs à proximité</string>
<string name="auto_chargers_near_location">Près de %s</string>
<string name="auto_fault_report_date">⚠️ Rapport d\'anomalie (%s)</string>
<string name="auto_no_data">Indisponible</string>
<string name="auto_settings">Paramètres</string>
<string name="selecting_none">désélectionner tous les éléments</string>
<string name="auto_vehicle_data_permission_needed">Pour cette fonction, EVMap doit avoir accès aux données de votre véhicule.</string>
<string name="auto_heading">Direction</string>
<string name="auto_favorites">Favoris</string>
<string name="auto_no_refresh_possible">D\'autres mises à jour ne sont pas possibles. Veuillez revenir en arrière et redémarrer.</string>
<string name="settings_android_auto_chargeprice_range">Plage de charge pour la comparaison des prix</string>
<string name="welcome_android_auto_detail">Vous pouvez également utiliser EVMap à partir d\'Android Auto sur les voitures prises en charge. Il suffit de sélectionner l\'application EVMap dans le menu Android Auto.</string>
<string name="loading">Chargement…</string>
<string name="auto_multipage_goto">Page %d</string>
<string name="auto_multipage">(%d/%d)</string>
</resources>

View File

@@ -127,7 +127,7 @@
<string name="category_amusement_park">Fornøyelsespark</string>
<string name="category_cinema">Kino</string>
<string name="category_parking_multi">Parkeringshus</string>
<string name="edit_at_datasource">rediger på %s</string>
<string name="edit_at_datasource">Rediger på %s</string>
<string name="category_camping">Campingplass</string>
<string name="category_service_on_motorway">Rasteplass (på motorvei)</string>
<string name="category_shopping_mall">Kjøpesenter</string>
@@ -297,4 +297,38 @@
<string name="developer_mode_enabled">Utviklermodus påslått</string>
<string name="menu_reset">Tilbakestill filterinnstillinger</string>
<string name="charger_website">Nettside</string>
<string name="auto_favorites">Favoritter</string>
<string name="auto_charging_level">Ladingsnivå</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap kunne ikke fastsette kjøretøymodellen.</string>
<string name="selecting_none">fravalgte alle elementer</string>
<string name="grant_on_phone">Innvilg på mobilenheten</string>
<string name="auto_chargers_closeby">Ladere i nærheten</string>
<string name="auto_prices">Pris</string>
<string name="auto_no_chargers_found">Ingen ladere i nærheten</string>
<string name="auto_no_favorites_found">Fant ikke noen favoritter</string>
<string name="open_in_app">Åpne i programmet</string>
<string name="auto_location_service">EVMap kjører på Android Auto og bruker posisjonen din.</string>
<string name="auto_heading">Fartsretning</string>
<string name="opened_on_phone">Åpnet på mobilenheten</string>
<string name="auto_location_permission_needed">Innvilg posisjonstilgang for å bruke EVMap på Android Auto.</string>
<string name="auto_chargers_near_location">Nær %s</string>
<string name="auto_fault_report_date">⚠️ Feilrapport (%s)</string>
<string name="auto_vehicle_data">Kjøretøydata</string>
<string name="auto_no_data">Utilgjengelig</string>
<string name="auto_speed">Hastighet</string>
<string name="auto_settings">Innstillinger</string>
<string name="auto_chargeprice_vehicle_unknown">Ingen av kjøretøyene valgt i programmet samsvarer med dette kjøretøyet (%1$s %2$s).</string>
<string name="welcome_android_auto">Android Auto-støtte</string>
<string name="auto_chargeprice_vehicle_ambiguous">Flere kjøretøy valgt i programmet samsvarer med dette kjøretøyet (%1$s %2$s).</string>
<string name="auto_vehicle_data_permission_needed">EvMap trenger tilgang til kjøretøydata for å bruke denne funksjonen.</string>
<string name="auto_no_refresh_possible">Videre oppdateringer er ikke mulig. Gå tilbake og start på ny.</string>
<string name="auto_range">Rekkevidde</string>
<string name="welcome_android_auto_detail">Du kan også bruke EVMap inne i Android Auto på bilder som støtter dette ved å velge det i Android Auto-menyen.</string>
<string name="settings_android_auto_chargeprice_range">Prissammenligning for laderekkevidde fordelt på pris</string>
<string name="selecting_all">valgte alle elementene</string>
<string name="sounds_cool">Den er grei</string>
<string name="auto_chargers_ahead">Kun ladere i kjøreretningen</string>
<string name="loading">Laster inn …</string>
<string name="auto_multipage_goto">Side %d</string>
<string name="auto_multipage">(%d/%d)</string>
</resources>

View File

@@ -287,7 +287,7 @@
<string name="show_less">minder…</string>
<string name="map_type">Kaarttype</string>
<string name="map_details">Kaartdetails</string>
<string name="edit_at_datasource">aanpassen op %s</string>
<string name="edit_at_datasource">Aanpassen op %s</string>
<string name="charge_cards">Betaalmethoden</string>
<string name="pref_map_provider">Kaartaanbieder</string>
<string name="twitter">Twitter</string>
@@ -295,4 +295,38 @@
<string name="and_n_others">en %d andere</string>
<string name="category_camping">Kampeerplaats</string>
<string name="category_public_authorities">Publieke instanties</string>
<string name="auto_chargeprice_vehicle_ambiguous">Meerdere voertuigen geselecteerd in de app komen overeen met dit voertuig (%1$s %2$s).</string>
<string name="auto_location_service">EVMap draait op Android Auto en gebruikt jouw locatie.</string>
<string name="auto_no_chargers_found">Geen laadpunten gevonden in de omgeving</string>
<string name="auto_no_favorites_found">Geen favorieten gevonden</string>
<string name="open_in_app">Open in de app</string>
<string name="opened_on_phone">Geopend op de telefoon</string>
<string name="auto_location_permission_needed">Om EVMap op Android Auto te gebruiken, moet je toegang geven tot je locatie.</string>
<string name="auto_vehicle_data_permission_needed">Voor deze functie heeft EVMap toegang nodig tot de gegevens van je voertuig.</string>
<string name="grant_on_phone">Geef toestemming op telefoon</string>
<string name="auto_chargers_closeby">Oplaadpunten in de buurt</string>
<string name="auto_favorites">Favorieten</string>
<string name="auto_chargers_near_location">Nabij %s</string>
<string name="auto_fault_report_date">⚠️ Foutrapport (%s)</string>
<string name="auto_no_refresh_possible">Verdere updates zijn niet mogelijk. Ga terug en herbegin.</string>
<string name="auto_prices">Prijzen</string>
<string name="auto_vehicle_data">Voertuiggegevens</string>
<string name="auto_charging_level">Laadniveau (SoC)</string>
<string name="auto_no_data">Niet beschikbaar</string>
<string name="auto_range">Reikwijdte</string>
<string name="auto_speed">Snelheid</string>
<string name="auto_heading">Richting</string>
<string name="auto_settings">Instellingen</string>
<string name="welcome_android_auto">Android Auto support</string>
<string name="welcome_android_auto_detail">Je kan EVMap ook gebruiken in Android Auto op ondersteunde voertuigen. Selecteer gewoon de EVMap app in het Android Auto menu.</string>
<string name="sounds_cool">Klinkt cool</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap kon je voertuigtype niet bepalen.</string>
<string name="auto_chargers_ahead">Alleen laadpunten in rijrichting</string>
<string name="settings_android_auto_chargeprice_range">Laadbereik voor prijsvergelijking</string>
<string name="selecting_all">alle items geselecteerd</string>
<string name="selecting_none">alle items gedeselecteerd</string>
<string name="loading">Laden…</string>
<string name="auto_multipage_goto">Pagina %d</string>
<string name="auto_multipage">(%d/%d)</string>
<string name="auto_chargeprice_vehicle_unknown">Geen enkel voertuig geselecteerd in de app komt overeen met dit voertuig (%1$s %2$s).</string>
</resources>

View File

@@ -76,7 +76,7 @@
<string name="category_public_authorities">Autoridades públicas</string>
<string name="category_private_charger">Carregador privado</string>
<string name="category_rest_area">Área de descanso</string>
<string name="edit_at_datasource">editado em %s</string>
<string name="edit_at_datasource">Editado em %s</string>
<string name="categories">Categorias</string>
<string name="category_service_on_motorway">Área de serviço (autoestrada)</string>
<string name="category_service_off_motorway">Área de serviço (fora da autoestrada)</string>
@@ -97,7 +97,7 @@
<string name="save_as_profile">Guardar como perfil</string>
<string name="filterprofiles_empty_state">Não existem filtros guardados</string>
<string name="welcome_2">Cada cor corresponde a potência máxima do carregador</string>
<string name="welcome_to_evmap">Bem-vindo ao EVMap</string>
<string name="welcome_to_evmap">Bem-vindo(a) ao EVMap</string>
<string name="pref_darkmode_always_off">Sempre desligado</string>
<string name="welcome_2_title">Escolha a potência</string>
<string name="navigate">Navegar</string>
@@ -330,4 +330,40 @@
<string name="settings_cache_count_summary">%d carregadores na base de dados, %.1f MB</string>
<string name="settings_caching">Caching (base de dados local)</string>
<string name="settings_cache_clear_summary">Elimina todos os carregadores guardados na base de dados local, com a exceção dos seus favoritos</string>
<string name="auto_no_chargers_found">Não foram encontrados carregadores próximo de si</string>
<string name="auto_no_favorites_found">Nenhum favorito encontrado</string>
<string name="opened_on_phone">Aberto no telefone</string>
<string name="auto_location_permission_needed">Para usar o EVMap no Android Auto, permita o acesso à sua localização.</string>
<string name="open_in_app">Abrir na app</string>
<string name="auto_vehicle_data_permission_needed">Para esta funcionalidade, o EVMap precisa de aceder aos dados do seu veículo.</string>
<string name="auto_chargers_closeby">Carregadores próximos</string>
<string name="grant_on_phone">Conceda permissões no telefone</string>
<string name="auto_chargers_near_location">Perto de %s</string>
<string name="auto_favorites">Favoritos</string>
<string name="auto_chargeprice_vehicle_ambiguous">Vários veículos selecionados na app correspondem a este veículo (%1$s %2$s).</string>
<string name="selecting_none">todos os items desmarcados</string>
<string name="selecting_all">todos os items selecionados</string>
<string name="loading">Carregando…</string>
<string name="auto_multipage_goto">Página %d</string>
<string name="auto_multipage">(%d/%d)</string>
<string name="settings_android_auto_chargeprice_range">Escala de carregamento para comparação de preços</string>
<string name="auto_location_service">O EVMap está a funcionar no Android Auto e usando a sua localização.</string>
<string name="auto_fault_report_date">⚠️ Problemas (%s)</string>
<string name="auto_no_refresh_possible">Não é possível atualizar. Por favor volte atrás e reinicie.</string>
<string name="auto_prices">Preços</string>
<string name="auto_vehicle_data">Dados do veículo</string>
<string name="auto_charging_level">Nível de carregamento</string>
<string name="auto_no_data">Não disponível</string>
<string name="auto_speed">Velocidade</string>
<string name="auto_heading">Direção</string>
<string name="auto_settings">Definições</string>
<string name="welcome_android_auto">Suporte para Android Auto</string>
<string name="auto_range">Alcance</string>
<string name="welcome_android_auto_detail">Também pode usar o EVMap no Android Auto em carros compatíveis. Basta selecionar a app EVMap no menu do Android Auto.</string>
<string name="auto_chargeprice_vehicle_unavailable">O EVMap não pôde determinar o modelo do seu veículo.</string>
<string name="auto_chargeprice_vehicle_unknown">Nenhum dos veículos selecionados na app corresponde a este veículo (%1$s %2$s).</string>
<string name="auto_chargers_ahead">Apenas carregadores na direção do destino</string>
<string name="sounds_cool">Continuar</string>
<string name="reload">Recarregar</string>
<string name="accept_privacy"><![CDATA[Li e aceito a <a href=\"%s\">política de privacidade</a> do EVMap.]]></string>
</resources>

View File

@@ -112,7 +112,7 @@
<string name="goingelectric_forum">Forum conversatii pe GoingElectric.de</string>
<string name="contact">Contact</string>
<string name="menu_report_new_charger">Statie noua</string>
<string name="edit_at_datasource">modificat la %s</string>
<string name="edit_at_datasource">Modificat la %s</string>
<string name="categories">Categorii</string>
<string name="category_car_dealership">Reprezentanta auto</string>
<string name="category_service_on_motorway">Zona servicii (autostrada)</string>

View File

@@ -30,4 +30,8 @@
<color name="background">#FFFFFF</color>
<color name="pager_unselected">#1F000000</color>
<color name="logo_tint_night">@null</color>
<color name="gauge_active">#00e676</color>
<color name="gauge_middle">#087f23</color>
<color name="gauge_inactive">#9e9e9e</color>
<color name="charger_100kw_dark">#FBC02D</color>
</resources>

View File

@@ -32,5 +32,6 @@
</string>
<string name="hide_on_scroll_fab_behavior">net.vonforst.evmap.ui.HideOnScrollFabBehavior</string>
<string name="paypal_link" translatable="false">https://paypal.me/johan98</string>
<string name="donate_link" translatable="false">https://ev-map.app/donate/</string>
<string name="copyright_summary">©20202023 Johan von Forstner and contributors</string>
</resources>

View File

@@ -113,7 +113,7 @@
<string name="goingelectric_forum">Forum thread at GoingElectric.de</string>
<string name="contact">Contact</string>
<string name="menu_report_new_charger">New charger</string>
<string name="edit_at_datasource">edit at %s</string>
<string name="edit_at_datasource">Edit at %s</string>
<string name="categories">Categories</string>
<string name="category_car_dealership">Car Dealership</string>
<string name="category_service_on_motorway">Service area (on motorway)</string>
@@ -325,4 +325,40 @@
<string name="settings_cache_clear">Clear cache</string>
<string name="settings_cache_clear_summary">Deletes all cached chargers except favorites</string>
<string name="settings_cache_count_summary">%d chargers cached, %.1f MB</string>
<string name="auto_location_service">EVMap is running on Android Auto and using your location.</string>
<string name="auto_no_chargers_found">No nearby chargers found</string>
<string name="auto_no_favorites_found">No favorites found</string>
<string name="open_in_app">Open in app</string>
<string name="opened_on_phone">Opened on phone</string>
<string name="auto_location_permission_needed">To run EVMap on Android Auto, you need to grant access to your location.</string>
<string name="auto_vehicle_data_permission_needed">For this feature, EVMap needs access to your vehicle data.</string>
<string name="grant_on_phone">Grant on phone</string>
<string name="auto_chargers_closeby">Nearby chargers</string>
<string name="auto_favorites">Favorites</string>
<string name="auto_chargers_near_location">Near %s</string>
<string name="auto_fault_report_date">⚠️ Fault report (%s)</string>
<string name="auto_no_refresh_possible">Further updates not possible. Please go back and restart.</string>
<string name="auto_prices">Pricing</string>
<string name="auto_vehicle_data">Vehicle data</string>
<string name="auto_charging_level">Charging level</string>
<string name="auto_no_data">Unavailable</string>
<string name="auto_range">Range</string>
<string name="auto_speed">Speed</string>
<string name="auto_heading">Heading</string>
<string name="auto_settings">Settings</string>
<string name="welcome_android_auto">Android Auto support</string>
<string name="welcome_android_auto_detail">You can also use EVMap from within Android Auto on supported cars. Simply select the EVMap app in the Android Auto menu.</string>
<string name="sounds_cool">Sounds cool</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap could not determine your vehicle model.</string>
<string name="auto_chargeprice_vehicle_unknown">None of the vehicles selected in the app matches this vehicle (%1$s %2$s).</string>
<string name="auto_chargeprice_vehicle_ambiguous">Multiple vehicles selected in the app match this vehicle (%1$s %2$s).</string>
<string name="auto_chargers_ahead">Only chargers along driving direction</string>
<string name="settings_android_auto_chargeprice_range">Charging range for price comparison</string>
<string name="selecting_all">selected all items</string>
<string name="selecting_none">deselected all items</string>
<string name="loading">Loading…</string>
<string name="auto_multipage_goto">Page %d</string>
<string name="auto_multipage">(%d/%d)</string>
<string name="reload">Reload</string>
<string name="accept_privacy"><![CDATA[I have read and accepted EVMap\'s <a href=\"%s\">privacy policy</a>.]]></string>
</resources>

View File

@@ -73,6 +73,20 @@
<!-- this is necessary to make sure the dialog gets "pushed up" when the keyboard appears -->
<item name="android:windowTranslucentStatus">false</item>
<item name="dialogCornerRadius">28dp</item>
<item name="alertDialogStyle">@style/MaterialAlertDialog.App</item>
</style>
<style name="MaterialAlertDialog.App" parent="MaterialAlertDialog.Material3">
<!-- reduce insets from 80dp to 24dp -->
<item name="backgroundInsetTop">24dp</item>
<item name="backgroundInsetBottom">24dp</item>
</style>
<style name="CarAppTheme">
<item name="carColorPrimary">@color/colorPrimary</item>
<item name="carColorPrimaryDark">@color/colorPrimaryDark</item>
<item name="carColorSecondary">@color/colorSecondary</item>
<item name="carColorSecondaryDark">@color/colorSecondaryDark</item>
</style>
</resources>

View File

@@ -13,6 +13,10 @@
android:fragment="net.vonforst.evmap.fragment.preference.ChargepriceSettingsFragment"
android:title="@string/settings_chargeprice"
android:icon="@drawable/ic_chargeprice" />
<Preference
android:fragment="net.vonforst.evmap.fragment.preference.AndroidAutoSettingsFragment"
android:title="@string/settings_android_auto"
android:icon="@drawable/ic_android_auto" />
<Preference
android:key="developer_options"
android:fragment="net.vonforst.evmap.fragment.preference.DeveloperSettingsFragment"

View File

@@ -2,9 +2,8 @@ Testing EVMap on Android Auto
=============================
In addition to the Android app on the phone, EVMap is also available as an Android Auto app built
using the [Android for Cars App Library](https://developer.android.com/training/cars/apps). The
Android Auto app is only available in the `google` build flavor of the app, and thus its code is
located in the `app/src/google/java` directory under the `net.vonforst.evmap.auto` package.
using the [Android for Cars App Library](https://developer.android.com/training/cars/apps). Its code
is located under the `net.vonforst.evmap.auto` package.
This page contains instructions on how to test the Android Auto app using the Desktop Head Unit
(DHU).

View File

@@ -0,0 +1,3 @@
Fehler behoben:
- Fehler im Caching-Algorithmus im Zusammenspiel mit bestimmten Filtern behoben
- Abstürze behoben

View File

@@ -0,0 +1,5 @@
Verbesserungen:
- Clustering an Orten mit extrem hoher Ladestationsdichte verstärkt
Fehler behoben:
- Abstürze behoben

View File

@@ -0,0 +1,7 @@
Verbesserungen:
- Buttons zum manuellen Neuladen der Karteninhalte bzw. Ladestationsdetails
- Die App für Android Auto ist nun auch mit der F-Droid-Version von EVMap nutzbar (falls Android Auto installiert ist)
- Android Auto: Ladestations-Icons werden mit angezeigt
Fehler behoben:
- Weitere Korrekturen beim Caching (Ladestationen wurden bei bestimmten Filtern und Zoomstufen nicht angezeigt)

View File

@@ -0,0 +1,2 @@
Fehler behoben:
- Abstürze behoben

View File

@@ -0,0 +1,5 @@
Fehler behoben:
- GoingElectric: Filteroption "CCS" erschien doppelt
- Korrekturen für kleine Bildschirme
- Farbe eines Menüs im dunklen Design korrigiert
- Abstürze behoben

View File

@@ -0,0 +1,3 @@
Bugfixes:
- Fixed error in caching algorithm when some filters are active
- Fixed crashes

View File

@@ -0,0 +1,5 @@
Improvements:
- Increased clustering in places with extremely high charger density
Bugfixes:
- Fixed crashes

View File

@@ -0,0 +1,7 @@
Improvements:
- Buttons to manually reload map contents / charger details
- Android Auto app is now also available with the F-Droid version of EVMap
- Android Auto: Show charger icons
Bugfixes:
- Additional corrections for caching function (chargers were not shown with certain filters and zoom levels)

View File

@@ -0,0 +1,2 @@
Bugfixes:
- Fixed crashes

View File

@@ -0,0 +1,5 @@
Bugfixes:
- GoingElectric: filter option "CCS" appeared twice
- Improvements for small displays
- Fixed color of a menu in dark mode
- Fixed crashes